Make `PerMachine` and `MachineChoice` have just `build` and `host`

Meson itself *almost* only cares about the build and host platforms. The
exception is it takes a `target_machine` in the cross file and exposes
it to the user; but it doesn't do anything else with it. It's therefore
overkill to put target in `PerMachine` and `MachineChoice`. Instead, we
make a `PerThreeMachine` only for the machine infos.

Additionally fix a few other things that were bugging me in the process:

 - Get rid of `MachineInfos` class. Since `envconfig.py` was created, it
   has no methods that couldn't just got on `PerMachine`

 - Make `default_missing` and `miss_defaulting` work functionally. That
   means we can just locally bind rather than bind as class vars the
   "unfrozen" configuration. This helps prevent bugs where one forgets
   to freeze a configuration.
pull/4910/head
John Ericson 6 years ago committed by Jussi Pakkanen
parent 38b347ecd0
commit 957d8e051c
  1. 2
      mesonbuild/coredata.py
  2. 8
      mesonbuild/dependencies/base.py
  3. 36
      mesonbuild/envconfig.py
  4. 66
      mesonbuild/environment.py
  5. 8
      mesonbuild/interpreter.py
  6. 97
      mesonbuild/mesonlib.py
  7. 2
      run_tests.py
  8. 2
      run_unittests.py

@ -243,7 +243,7 @@ class CoreData:
self.init_builtins()
self.backend_options = {}
self.user_options = {}
self.compiler_options = PerMachine({}, {}, {})
self.compiler_options = PerMachine({}, {})
self.base_options = {}
self.cross_files = self.__load_config_files(options.cross_file, 'cross')
self.compilers = OrderedDict()

@ -522,7 +522,7 @@ class ConfigToolDependency(ExternalDependency):
class PkgConfigDependency(ExternalDependency):
# The class's copy of the pkg-config path. Avoids having to search for it
# multiple times in the same Meson invocation.
class_pkgbin = PerMachine(None, None, None)
class_pkgbin = PerMachine(None, None)
# We cache all pkg-config subprocess invocations to avoid redundant calls
pkgbin_cache = {}
@ -957,9 +957,9 @@ class CMakeTarget:
class CMakeDependency(ExternalDependency):
# The class's copy of the CMake path. Avoids having to search for it
# multiple times in the same Meson invocation.
class_cmakebin = PerMachine(None, None, None)
class_cmakevers = PerMachine(None, None, None)
class_cmakeinfo = PerMachine(None, None, None)
class_cmakebin = PerMachine(None, None)
class_cmakevers = PerMachine(None, None)
class_cmakeinfo = PerMachine(None, None)
# We cache all pkg-config subprocess invocations to avoid redundant calls
cmake_cache = {}
# Version string for the minimum CMake version

@ -16,7 +16,7 @@ import configparser, os, shlex, subprocess
import typing
from . import mesonlib
from .mesonlib import EnvironmentException, MachineChoice, PerMachine
from .mesonlib import EnvironmentException
from . import mlog
_T = typing.TypeVar('_T')
@ -255,40 +255,6 @@ class MachineInfo:
def libdir_layout_is_win(self) -> bool:
return self.is_windows() or self.is_cygwin()
class PerMachineDefaultable(PerMachine[typing.Optional[_T]]):
"""Extends `PerMachine` with the ability to default from `None`s.
"""
def __init__(self) -> None:
super().__init__(None, None, None)
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
cross non-compiler case, and just target in the native-built
cross-compiler case.
"""
if self.host is None:
self.host = self.build
if self.target is None:
self.target = self.host
def miss_defaulting(self) -> None:
"""Unset definition duplicated from their previous to None
This is the inverse of ''default_missing''. By removing defaulted
machines, we can elaborate the original and then redefault them and thus
avoid repeating the elaboration explicitly.
"""
if self.target == self.host:
self.target = None
if self.host == self.build:
self.host = None
class MachineInfos(PerMachineDefaultable[MachineInfo]):
def matches_build_machine(self, machine: MachineChoice) -> bool:
return self.build == self[machine]
class BinaryTable(HasEnvVarFallback):
def __init__(
self,

@ -19,12 +19,13 @@ from .linkers import ArLinker, ArmarLinker, VisualStudioLinker, DLinker, CcrxLin
from . import mesonlib
from .mesonlib import (
MesonException, EnvironmentException, MachineChoice, Popen_safe,
PerMachineDefaultable, PerThreeMachineDefaultable
)
from . import mlog
from .envconfig import (
BinaryTable, Directories, MachineInfo, MachineInfos, MesonConfigFile,
PerMachineDefaultable, Properties, known_cpu_families,
BinaryTable, Directories, MachineInfo, MesonConfigFile,
Properties, known_cpu_families,
)
from . import compilers
from .compilers import (
@ -403,48 +404,62 @@ class Environment:
# Just create a fresh coredata in this case
self.create_new_coredata(options)
self.machines = MachineInfos()
# Will be fully initialized later using compilers later.
self.detect_build_machine()
## locally bind some unfrozen configuration
# Stores machine infos, the only *three* machine one because we have a
# target machine info on for the user (Meson never cares about the
# target machine.)
machines = PerThreeMachineDefaultable()
# Similar to coredata.compilers and build.compilers, but lower level in
# that there is no meta data, only names/paths.
self.binaries = PerMachineDefaultable()
binaries = PerMachineDefaultable()
# Misc other properties about each machine.
self.properties = PerMachineDefaultable()
# Just uses hard-coded defaults and environment variables. Might be
# overwritten by a native file.
self.binaries.build = BinaryTable()
self.properties.build = Properties()
properties = PerMachineDefaultable()
# Store paths for native and cross build files. There is no target
# machine information here because nothing is installed for the target
# architecture, just the build and host architectures
self.paths = PerMachineDefaultable()
paths = PerMachineDefaultable()
## Setup build machine defaults
# Will be fully initialized later using compilers later.
machines.build = detect_machine_info()
# Just uses hard-coded defaults and environment variables. Might be
# overwritten by a native file.
binaries.build = BinaryTable()
properties.build = Properties()
## Read in native file(s) to override build machine configuration
if self.coredata.config_files is not None:
config = MesonConfigFile.from_config_parser(
coredata.load_configs(self.coredata.config_files))
self.binaries.build = BinaryTable(config.get('binaries', {}))
self.paths.build = Directories(**config.get('paths', {}))
binaries.build = BinaryTable(config.get('binaries', {}))
paths.build = Directories(**config.get('paths', {}))
## Read in cross file(s) to override host machine configuration
if self.coredata.cross_files:
config = MesonConfigFile.from_config_parser(
coredata.load_configs(self.coredata.cross_files))
self.properties.host = Properties(config.get('properties', {}), False)
self.binaries.host = BinaryTable(config.get('binaries', {}), False)
properties.host = Properties(config.get('properties', {}), False)
binaries.host = BinaryTable(config.get('binaries', {}), False)
if 'host_machine' in config:
self.machines.host = MachineInfo.from_literal(config['host_machine'])
machines.host = MachineInfo.from_literal(config['host_machine'])
if 'target_machine' in config:
self.machines.target = MachineInfo.from_literal(config['target_machine'])
self.paths.host = Directories(**config.get('paths', {}))
machines.target = MachineInfo.from_literal(config['target_machine'])
paths.host = Directories(**config.get('paths', {}))
## "freeze" now initialized configuration, and "save" to the class.
self.machines.default_missing()
self.binaries.default_missing()
self.properties.default_missing()
self.paths.default_missing()
self.machines = machines.default_missing()
self.binaries = binaries.default_missing()
self.properties = properties.default_missing()
self.paths = paths.default_missing()
exe_wrapper = self.binaries.host.lookup_entry('exe_wrapper')
if exe_wrapper is not None:
@ -1233,9 +1248,6 @@ class Environment:
self._handle_exceptions(popen_exceptions, linkers, 'linker')
raise EnvironmentException('Unknown static linker "%s"' % ' '.join(linkers))
def detect_build_machine(self, compilers = None):
self.machines.build = detect_machine_info(compilers)
def get_source_dir(self):
return self.source_dir

@ -2047,12 +2047,12 @@ class Interpreter(InterpreterBase):
if not mock:
self.parse_project()
# Initialize machine descriptions. We can do a better job now because we
# Re-initialize machine descriptions. We can do a better job now because we
# have the compilers needed to gain more knowledge, so wipe out old
# inferrence and start over.
self.build.environment.machines.miss_defaulting()
self.build.environment.detect_build_machine(self.coredata.compilers)
self.build.environment.machines.default_missing()
machines = self.build.environment.machines.miss_defaulting()
machines.build = environment.detect_machine_info(self.coredata.compilers)
self.build.environment.machines = machines.default_missing()
assert self.build.environment.machines.build.cpu is not None
assert self.build.environment.machines.host.cpu is not None
assert self.build.environment.machines.target.cpu is not None

@ -313,37 +313,120 @@ class OrderedEnum(Enum):
return self.value < other.value
return NotImplemented
class MachineChoice(OrderedEnum):
"""Enum class representing one of the three possible values for binaries,
the build, host, and target machines.
"""Enum class representing one of the two abstract machine names used in
most places: the build, and host, machines.
"""
BUILD = 0
HOST = 1
TARGET = 2
class PerMachine(typing.Generic[_T]):
def __init__(self, build: _T, host: _T, target: _T):
def __init__(self, build: _T, host: _T):
self.build = build
self.host = host
self.target = target
def __getitem__(self, machine: MachineChoice) -> _T:
return {
MachineChoice.BUILD: self.build,
MachineChoice.HOST: self.host,
MachineChoice.TARGET: self.target
}[machine]
def __setitem__(self, machine: MachineChoice, val: _T) -> None:
key = {
MachineChoice.BUILD: 'build',
MachineChoice.HOST: 'host',
MachineChoice.TARGET: 'target'
}[machine]
setattr(self, key, val)
def miss_defaulting(self) -> "PerMachineDefaultable[typing.Optional[_T]]":
"""Unset definition duplicated from their previous to None
This is the inverse of ''default_missing''. By removing defaulted
machines, we can elaborate the original and then redefault them and thus
avoid repeating the elaboration explicitly.
"""
unfreeze = PerMachineDefaultable() # type: PerMachineDefaultable[typing.Optional[_T]]
unfreeze.build = self.build
unfreeze.host = self.host
if unfreeze.host == unfreeze.build:
unfreeze.host = None
return unfreeze
class PerThreeMachine(PerMachine[_T]):
"""Like `PerMachine` but includes `target` too.
It turns out just one thing do we need track the target machine. There's no
need to computer the `target` field so we don't bother overriding the
`__getitem__`/`__setitem__` methods.
"""
def __init__(self, build: _T, host: _T, target: _T):
super().__init__(build, host)
self.target = target
def miss_defaulting(self) -> "PerThreeMachineDefaultable[typing.Optional[_T]]":
"""Unset definition duplicated from their previous to None
This is the inverse of ''default_missing''. By removing defaulted
machines, we can elaborate the original and then redefault them and thus
avoid repeating the elaboration explicitly.
"""
unfreeze = PerThreeMachineDefaultable() # type: PerThreeMachineDefaultable[typing.Optional[_T]]
unfreeze.build = self.build
unfreeze.host = self.host
unfreeze.target = self.target
if unfreeze.target == unfreeze.host:
unfreeze.target = None
if unfreeze.host == unfreeze.build:
unfreeze.host = None
return unfreeze
def matches_build_machine(self, machine: MachineChoice) -> bool:
return self.build == self[machine]
class PerMachineDefaultable(PerMachine[typing.Optional[_T]]):
"""Extends `PerMachine` with the ability to default from `None`s.
"""
def __init__(self) -> None:
super().__init__(None, None)
def default_missing(self) -> "PerMachine[typing.Optional[_T]]":
"""Default host to buid
This allows just specifying nothing in the native case, and just host in the
cross non-compiler case.
"""
freeze = PerMachine(self.build, self.host)
if freeze.host is None:
freeze.host = freeze.build
return freeze
class PerThreeMachineDefaultable(PerMachineDefaultable, PerThreeMachine[typing.Optional[_T]]):
"""Extends `PerThreeMachine` with the ability to default from `None`s.
"""
def __init__(self) -> None:
PerThreeMachine.__init__(self, None, None, None)
def default_missing(self) -> "PerThreeMachine[typing.Optional[_T]]":
"""Default host to buid and target to host.
This allows just specifying nothing in the native case, just host in the
cross non-compiler case, and just target in the native-built
cross-compiler case.
"""
freeze = PerThreeMachine(self.build, self.host, self.target)
if freeze.host is None:
freeze.host = freeze.build
if freeze.target is None:
freeze.target = freeze.host
return freeze
def is_sunos() -> bool:
return platform.system().lower() == 'sunos'

@ -220,7 +220,7 @@ def clear_meson_configure_class_caches():
mesonbuild.compilers.CCompiler.find_library_cache = {}
mesonbuild.compilers.CCompiler.find_framework_cache = {}
mesonbuild.dependencies.PkgConfigDependency.pkgbin_cache = {}
mesonbuild.dependencies.PkgConfigDependency.class_pkgbin = mesonlib.PerMachine(None, None, None)
mesonbuild.dependencies.PkgConfigDependency.class_pkgbin = mesonlib.PerMachine(None, None)
def run_configure_inprocess(commandlist):
old_stdout = sys.stdout

@ -848,7 +848,7 @@ class InternalTests(unittest.TestCase):
PkgConfigDependency.check_pkgconfig = old_check
# Reset dependency class to ensure that in-process configure doesn't mess up
PkgConfigDependency.pkgbin_cache = {}
PkgConfigDependency.class_pkgbin = PerMachine(None, None, None)
PkgConfigDependency.class_pkgbin = PerMachine(None, None)
def test_version_compare(self):
comparefunc = mesonbuild.mesonlib.version_compare_many

Loading…
Cancel
Save