|
|
|
@ -19,6 +19,8 @@ from . import mesonlib |
|
|
|
|
from .mesonlib import EnvironmentException, MachineChoice, PerMachine |
|
|
|
|
from . import mlog |
|
|
|
|
|
|
|
|
|
_T = typing.TypeVar('_T') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# These classes contains all the data pulled from configuration files (native |
|
|
|
|
# and cross file currently), and also assists with the reading environment |
|
|
|
@ -69,7 +71,7 @@ CPU_FAMILES_64_BIT = [ |
|
|
|
|
|
|
|
|
|
class MesonConfigFile: |
|
|
|
|
@classmethod |
|
|
|
|
def from_config_parser(cls, parser: configparser.ConfigParser): |
|
|
|
|
def from_config_parser(cls, parser: configparser.ConfigParser) -> typing.Dict[str, typing.Dict[str, typing.Dict[str, str]]]: |
|
|
|
|
out = {} |
|
|
|
|
# This is a bit hackish at the moment. |
|
|
|
|
for s in parser.sections(): |
|
|
|
@ -106,55 +108,58 @@ class HasEnvVarFallback: |
|
|
|
|
that we deal with environment variables will become more structured, and |
|
|
|
|
this can be starting point. |
|
|
|
|
""" |
|
|
|
|
def __init__(self, fallback = True): |
|
|
|
|
def __init__(self, fallback: bool = True): |
|
|
|
|
self.fallback = fallback |
|
|
|
|
|
|
|
|
|
class Properties(HasEnvVarFallback): |
|
|
|
|
def __init__( |
|
|
|
|
self, |
|
|
|
|
properties: typing.Optional[typing.Dict[str, typing.Union[str, typing.List[str]]]] = None, |
|
|
|
|
fallback = True): |
|
|
|
|
fallback: bool = True): |
|
|
|
|
super().__init__(fallback) |
|
|
|
|
self.properties = properties or {} |
|
|
|
|
self.properties = properties or {} # type: typing.Dict[str, typing.Union[str, typing.List[str]]] |
|
|
|
|
|
|
|
|
|
def has_stdlib(self, language): |
|
|
|
|
def has_stdlib(self, language: str) -> bool: |
|
|
|
|
return language + '_stdlib' in self.properties |
|
|
|
|
|
|
|
|
|
def get_stdlib(self, language): |
|
|
|
|
# Some of get_stdlib, get_root, get_sys_root are wider than is actually |
|
|
|
|
# true, but without heterogenious dict annotations it's not practical to |
|
|
|
|
# narrow them |
|
|
|
|
def get_stdlib(self, language: str) -> typing.Union[str, typing.List[str]]: |
|
|
|
|
return self.properties[language + '_stdlib'] |
|
|
|
|
|
|
|
|
|
def get_root(self): |
|
|
|
|
def get_root(self) -> typing.Optional[typing.Union[str, typing.List[str]]]: |
|
|
|
|
return self.properties.get('root', None) |
|
|
|
|
|
|
|
|
|
def get_sys_root(self): |
|
|
|
|
def get_sys_root(self) -> typing.Optional[typing.Union[str, typing.List[str]]]: |
|
|
|
|
return self.properties.get('sys_root', None) |
|
|
|
|
|
|
|
|
|
def __eq__(self, other): |
|
|
|
|
def __eq__(self, other: typing.Any) -> typing.Union[bool, 'NotImplemented']: |
|
|
|
|
if isinstance(other, type(self)): |
|
|
|
|
return self.properties == other.properties |
|
|
|
|
return NotImplemented |
|
|
|
|
|
|
|
|
|
# TODO consider removing so Properties is less freeform |
|
|
|
|
def __getitem__(self, key): |
|
|
|
|
def __getitem__(self, key: str) -> typing.Any: |
|
|
|
|
return self.properties[key] |
|
|
|
|
|
|
|
|
|
# TODO consider removing so Properties is less freeform |
|
|
|
|
def __contains__(self, item): |
|
|
|
|
def __contains__(self, item: typing.Any) -> bool: |
|
|
|
|
return item in self.properties |
|
|
|
|
|
|
|
|
|
# TODO consider removing, for same reasons as above |
|
|
|
|
def get(self, key, default=None): |
|
|
|
|
def get(self, key: str, default: typing.Any = None) -> typing.Any: |
|
|
|
|
return self.properties.get(key, default) |
|
|
|
|
|
|
|
|
|
class MachineInfo: |
|
|
|
|
def __init__(self, system, cpu_family, cpu, endian): |
|
|
|
|
def __init__(self, system: str, cpu_family: str, cpu: str, endian: str): |
|
|
|
|
self.system = system |
|
|
|
|
self.cpu_family = cpu_family |
|
|
|
|
self.cpu = cpu |
|
|
|
|
self.endian = endian |
|
|
|
|
self.is_64_bit = cpu_family in CPU_FAMILES_64_BIT |
|
|
|
|
self.is_64_bit = cpu_family in CPU_FAMILES_64_BIT # type: bool |
|
|
|
|
|
|
|
|
|
def __eq__(self, other): |
|
|
|
|
def __eq__(self, other: typing.Any) -> typing.Union[bool, 'NotImplemented']: |
|
|
|
|
if self.__class__ is not other.__class__: |
|
|
|
|
return NotImplemented |
|
|
|
|
return \ |
|
|
|
@ -163,16 +168,16 @@ class MachineInfo: |
|
|
|
|
self.cpu == other.cpu and \ |
|
|
|
|
self.endian == other.endian |
|
|
|
|
|
|
|
|
|
def __ne__(self, other): |
|
|
|
|
def __ne__(self, other: typing.Any) -> typing.Union[bool, 'NotImplemented']: |
|
|
|
|
if self.__class__ is not other.__class__: |
|
|
|
|
return NotImplemented |
|
|
|
|
return not self.__eq__(other) |
|
|
|
|
|
|
|
|
|
def __repr__(self): |
|
|
|
|
def __repr__(self) -> str: |
|
|
|
|
return '<MachineInfo: {} {} ({})>'.format(self.system, self.cpu_family, self.cpu) |
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
|
def from_literal(literal): |
|
|
|
|
@classmethod |
|
|
|
|
def from_literal(cls, literal: typing.Dict[str, str]) -> 'MachineInfo': |
|
|
|
|
minimum_literal = {'cpu', 'cpu_family', 'endian', 'system'} |
|
|
|
|
if set(literal) < minimum_literal: |
|
|
|
|
raise EnvironmentException( |
|
|
|
@ -187,49 +192,45 @@ class MachineInfo: |
|
|
|
|
if endian not in ('little', 'big'): |
|
|
|
|
mlog.warning('Unknown endian %s' % endian) |
|
|
|
|
|
|
|
|
|
return MachineInfo( |
|
|
|
|
literal['system'], |
|
|
|
|
cpu_family, |
|
|
|
|
literal['cpu'], |
|
|
|
|
endian) |
|
|
|
|
return cls(literal['system'], cpu_family, literal['cpu'], endian) |
|
|
|
|
|
|
|
|
|
def is_windows(self): |
|
|
|
|
def is_windows(self) -> bool: |
|
|
|
|
""" |
|
|
|
|
Machine is windows? |
|
|
|
|
""" |
|
|
|
|
return self.system == 'windows' |
|
|
|
|
|
|
|
|
|
def is_cygwin(self): |
|
|
|
|
def is_cygwin(self) -> bool: |
|
|
|
|
""" |
|
|
|
|
Machine is cygwin? |
|
|
|
|
""" |
|
|
|
|
return self.system == 'cygwin' |
|
|
|
|
|
|
|
|
|
def is_linux(self): |
|
|
|
|
def is_linux(self) -> bool: |
|
|
|
|
""" |
|
|
|
|
Machine is linux? |
|
|
|
|
""" |
|
|
|
|
return self.system == 'linux' |
|
|
|
|
|
|
|
|
|
def is_darwin(self): |
|
|
|
|
def is_darwin(self) -> bool: |
|
|
|
|
""" |
|
|
|
|
Machine is Darwin (iOS/OS X)? |
|
|
|
|
""" |
|
|
|
|
return self.system in ('darwin', 'ios') |
|
|
|
|
|
|
|
|
|
def is_android(self): |
|
|
|
|
def is_android(self) -> bool: |
|
|
|
|
""" |
|
|
|
|
Machine is Android? |
|
|
|
|
""" |
|
|
|
|
return self.system == 'android' |
|
|
|
|
|
|
|
|
|
def is_haiku(self): |
|
|
|
|
def is_haiku(self) -> bool: |
|
|
|
|
""" |
|
|
|
|
Machine is Haiku? |
|
|
|
|
""" |
|
|
|
|
return self.system == 'haiku' |
|
|
|
|
|
|
|
|
|
def is_openbsd(self): |
|
|
|
|
def is_openbsd(self) -> bool: |
|
|
|
|
""" |
|
|
|
|
Machine is OpenBSD? |
|
|
|
|
""" |
|
|
|
@ -239,29 +240,28 @@ class MachineInfo: |
|
|
|
|
# static libraries, and executables. |
|
|
|
|
# Versioning is added to these names in the backends as-needed. |
|
|
|
|
|
|
|
|
|
def get_exe_suffix(self): |
|
|
|
|
def get_exe_suffix(self) -> str: |
|
|
|
|
if self.is_windows() or self.is_cygwin(): |
|
|
|
|
return 'exe' |
|
|
|
|
else: |
|
|
|
|
return '' |
|
|
|
|
|
|
|
|
|
def get_object_suffix(self): |
|
|
|
|
def get_object_suffix(self) -> str: |
|
|
|
|
if self.is_windows(): |
|
|
|
|
return 'obj' |
|
|
|
|
else: |
|
|
|
|
return 'o' |
|
|
|
|
|
|
|
|
|
def libdir_layout_is_win(self): |
|
|
|
|
return self.is_windows() \ |
|
|
|
|
or self.is_cygwin() |
|
|
|
|
def libdir_layout_is_win(self) -> bool: |
|
|
|
|
return self.is_windows() or self.is_cygwin() |
|
|
|
|
|
|
|
|
|
class PerMachineDefaultable(PerMachine): |
|
|
|
|
class PerMachineDefaultable(PerMachine[_T]): |
|
|
|
|
"""Extends `PerMachine` with the ability to default from `None`s. |
|
|
|
|
""" |
|
|
|
|
def __init__(self): |
|
|
|
|
def __init__(self) -> None: |
|
|
|
|
super().__init__(None, None, None) |
|
|
|
|
|
|
|
|
|
def default_missing(self): |
|
|
|
|
def default_missing(self) -> None: |
|
|
|
|
"""Default host to buid and target to host. |
|
|
|
|
|
|
|
|
|
This allows just specifying nothing in the native case, just host in the |
|
|
|
@ -273,7 +273,7 @@ class PerMachineDefaultable(PerMachine): |
|
|
|
|
if self.target is None: |
|
|
|
|
self.target = self.host |
|
|
|
|
|
|
|
|
|
def miss_defaulting(self): |
|
|
|
|
def miss_defaulting(self) -> None: |
|
|
|
|
"""Unset definition duplicated from their previous to None |
|
|
|
|
|
|
|
|
|
This is the inverse of ''default_missing''. By removing defaulted |
|
|
|
@ -285,18 +285,17 @@ class PerMachineDefaultable(PerMachine): |
|
|
|
|
if self.host == self.build: |
|
|
|
|
self.host = None |
|
|
|
|
|
|
|
|
|
class MachineInfos(PerMachineDefaultable): |
|
|
|
|
def matches_build_machine(self, machine: MachineChoice): |
|
|
|
|
class MachineInfos(PerMachineDefaultable[typing.Optional[MachineInfo]]): |
|
|
|
|
def matches_build_machine(self, machine: MachineChoice) -> bool: |
|
|
|
|
return self.build == self[machine] |
|
|
|
|
|
|
|
|
|
class BinaryTable(HasEnvVarFallback): |
|
|
|
|
def __init__( |
|
|
|
|
self, |
|
|
|
|
binaries: typing.Optional[typing.Dict[str, typing.Union[str, typing.List[str]]]] = None, |
|
|
|
|
|
|
|
|
|
fallback = True): |
|
|
|
|
fallback: bool = True): |
|
|
|
|
super().__init__(fallback) |
|
|
|
|
self.binaries = binaries or {} |
|
|
|
|
self.binaries = binaries or {} # type: typing.Dict[str, typing.Union[str, typing.List[str]]] |
|
|
|
|
for name, command in self.binaries.items(): |
|
|
|
|
if not isinstance(command, (list, str)): |
|
|
|
|
# TODO generalize message |
|
|
|
@ -325,10 +324,10 @@ class BinaryTable(HasEnvVarFallback): |
|
|
|
|
'cmake': 'CMAKE', |
|
|
|
|
'qmake': 'QMAKE', |
|
|
|
|
'pkgconfig': 'PKG_CONFIG', |
|
|
|
|
} |
|
|
|
|
} # type: typing.Dict[str, str] |
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
|
def detect_ccache(): |
|
|
|
|
def detect_ccache() -> typing.List[str]: |
|
|
|
|
try: |
|
|
|
|
subprocess.check_call(['ccache', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
|
|
|
|
except (OSError, subprocess.CalledProcessError): |
|
|
|
@ -336,14 +335,14 @@ class BinaryTable(HasEnvVarFallback): |
|
|
|
|
return ['ccache'] |
|
|
|
|
|
|
|
|
|
@classmethod |
|
|
|
|
def _warn_about_lang_pointing_to_cross(cls, compiler_exe, evar): |
|
|
|
|
def _warn_about_lang_pointing_to_cross(cls, compiler_exe: str, evar: str) -> None: |
|
|
|
|
evar_str = os.environ.get(evar, 'WHO_WOULD_CALL_THEIR_COMPILER_WITH_THIS_NAME') |
|
|
|
|
if evar_str == compiler_exe: |
|
|
|
|
mlog.warning('''Env var %s seems to point to the cross compiler. |
|
|
|
|
This is probably wrong, it should always point to the native compiler.''' % evar) |
|
|
|
|
|
|
|
|
|
@classmethod |
|
|
|
|
def parse_entry(cls, entry): |
|
|
|
|
def parse_entry(cls, entry: typing.Union[str, typing.List[str]]) -> typing.Tuple[typing.List[str], typing.List[str]]: |
|
|
|
|
compiler = mesonlib.stringlistify(entry) |
|
|
|
|
# Ensure ccache exists and remove it if it doesn't |
|
|
|
|
if compiler[0] == 'ccache': |
|
|
|
@ -354,8 +353,8 @@ This is probably wrong, it should always point to the native compiler.''' % evar |
|
|
|
|
# Return value has to be a list of compiler 'choices' |
|
|
|
|
return compiler, ccache |
|
|
|
|
|
|
|
|
|
def lookup_entry(self, name): |
|
|
|
|
"""Lookup binary |
|
|
|
|
def lookup_entry(self, name: str) -> typing.Optional[typing.List[str]]: |
|
|
|
|
"""Lookup binaryk |
|
|
|
|
|
|
|
|
|
Returns command with args as list if found, Returns `None` if nothing is |
|
|
|
|
found. |
|
|
|
@ -404,11 +403,12 @@ class Directories: |
|
|
|
|
self.sharedstatedir = sharedstatedir |
|
|
|
|
self.sysconfdir = sysconfdir |
|
|
|
|
|
|
|
|
|
def __contains__(self, key: str) -> str: |
|
|
|
|
def __contains__(self, key: str) -> bool: |
|
|
|
|
return hasattr(self, key) |
|
|
|
|
|
|
|
|
|
def __getitem__(self, key: str) -> str: |
|
|
|
|
return getattr(self, key) |
|
|
|
|
def __getitem__(self, key: str) -> typing.Optional[str]: |
|
|
|
|
# Mypy can't figure out what to do with getattr here, so we'll case for it |
|
|
|
|
return typing.cast(typing.Optional[str], getattr(self, key)) |
|
|
|
|
|
|
|
|
|
def __setitem__(self, key: str, value: typing.Optional[str]) -> None: |
|
|
|
|
setattr(self, key, value) |
|
|
|
|