Move some configuration classes out of environment.py

First of all, I'd like compilers and other modules that environment.py
currently imports to be able to take these without creating
hard-to-follow module cycles.

Second of all, environment.py's exact purpose seems a bit obscured.
Splitting the data types (and basic pure functions) from the more
complex logic that infers that data seems like a good way to separate
concerns.
pull/4991/head
John Ericson 6 years ago committed by Dylan Baker
parent 94fe01deec
commit 2622e9ec32
  1. 397
      mesonbuild/envconfig.py
  2. 442
      mesonbuild/environment.py
  3. 2
      mesonbuild/interpreter.py

@ -0,0 +1,397 @@
# Copyright 2012-2016 The Meson development team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import configparser, os, shlex, subprocess
import typing
from . import mesonlib
from .mesonlib import EnvironmentException, MachineChoice, PerMachine
from . import mlog
# These classes contains all the data pulled from configuration files (native
# and cross file currently), and also assists with the reading environment
# variables.
#
# At this time there isn't an ironclad difference between this an other sources
# of state like `coredata`. But one rough guide is much what is in `coredata` is
# the *output* of the configuration process: the final decisions after tests.
# This, on the other hand has *inputs*. The config files are parsed, but
# otherwise minimally transformed. When more complex fallbacks (environment
# detection) exist, they are defined elsewhere as functions that construct
# instances of these classes.
known_cpu_families = (
'aarch64',
'arc',
'arm',
'e2k',
'ia64',
'mips',
'mips64',
'parisc',
'ppc',
'ppc64',
'riscv32',
'riscv64',
'rl78',
'rx',
's390x',
'sparc',
'sparc64',
'x86',
'x86_64'
)
class MesonConfigFile:
@classmethod
def parse_datafile(cls, filename):
config = configparser.ConfigParser()
try:
with open(filename, 'r') as f:
config.read_file(f, filename)
except FileNotFoundError:
raise EnvironmentException('File not found: %s.' % filename)
return cls.from_config_parser(config)
@classmethod
def from_config_parser(cls, parser: configparser.ConfigParser):
out = {}
# This is a bit hackish at the moment.
for s in parser.sections():
section = {}
for entry in parser[s]:
value = parser[s][entry]
# Windows paths...
value = value.replace('\\', '\\\\')
if ' ' in entry or '\t' in entry or "'" in entry or '"' in entry:
raise EnvironmentException('Malformed variable name %s in cross file..' % entry)
try:
res = eval(value, {'__builtins__': None}, {'true': True, 'false': False})
except Exception:
raise EnvironmentException('Malformed value in cross file variable %s.' % entry)
for i in (res if isinstance(res, list) else [res]):
if not isinstance(i, (str, int, bool)):
raise EnvironmentException('Malformed value in cross file variable %s.' % entry)
section[entry] = res
out[s] = section
return out
class Properties:
def __init__(
self,
properties: typing.Optional[typing.Dict[str, typing.Union[str, typing.List[str]]]] = None):
self.properties = properties or {}
def has_stdlib(self, language):
return language + '_stdlib' in self.properties
def get_stdlib(self, language):
return self.properties[language + '_stdlib']
def get_root(self):
return self.properties.get('root', None)
def get_sys_root(self):
return self.properties.get('sys_root', None)
def __eq__(self, other):
if isinstance(other, type(self)):
return self.properties == other.properties
return NotImplemented
# TODO consider removing so Properties is less freeform
def __getitem__(self, key):
return self.properties[key]
# TODO consider removing so Properties is less freeform
def __contains__(self, item):
return item in self.properties
# TODO consider removing, for same reasons as above
def get(self, key, default=None):
return self.properties.get(key, default)
class MachineInfo:
def __init__(self, system, cpu_family, cpu, endian):
self.system = system
self.cpu_family = cpu_family
self.cpu = cpu
self.endian = endian
def __eq__(self, other):
if self.__class__ is not other.__class__:
return NotImplemented
return \
self.system == other.system and \
self.cpu_family == other.cpu_family and \
self.cpu == other.cpu and \
self.endian == other.endian
def __ne__(self, other):
if self.__class__ is not other.__class__:
return NotImplemented
return not self.__eq__(other)
def __repr__(self):
return '<MachineInfo: {} {} ({})>'.format(self.system, self.cpu_family, self.cpu)
@staticmethod
def from_literal(literal):
minimum_literal = {'cpu', 'cpu_family', 'endian', 'system'}
if set(literal) < minimum_literal:
raise EnvironmentException(
'Machine info is currently {}\n'.format(literal) +
'but is missing {}.'.format(minimum_literal - set(literal)))
cpu_family = literal['cpu_family']
if cpu_family not in known_cpu_families:
mlog.warning('Unknown CPU family %s, please report this at https://github.com/mesonbuild/meson/issues/new' % cpu_family)
endian = literal['endian']
if endian not in ('little', 'big'):
mlog.warning('Unknown endian %s' % endian)
return MachineInfo(
literal['system'],
cpu_family,
literal['cpu'],
endian)
def is_windows(self):
"""
Machine is windows?
"""
return self.system == 'windows'
def is_cygwin(self):
"""
Machine is cygwin?
"""
return self.system == 'cygwin'
def is_linux(self):
"""
Machine is linux?
"""
return self.system == 'linux'
def is_darwin(self):
"""
Machine is Darwin (iOS/OS X)?
"""
return self.system in ('darwin', 'ios')
def is_android(self):
"""
Machine is Android?
"""
return self.system == 'android'
def is_haiku(self):
"""
Machine is Haiku?
"""
return self.system == 'haiku'
def is_openbsd(self):
"""
Machine is OpenBSD?
"""
return self.system == 'openbsd'
# Various prefixes and suffixes for import libraries, shared libraries,
# static libraries, and executables.
# Versioning is added to these names in the backends as-needed.
def get_exe_suffix(self):
if self.is_windows() or self.is_cygwin():
return 'exe'
else:
return ''
def get_object_suffix(self):
if self.is_windows():
return 'obj'
else:
return 'o'
def libdir_layout_is_win(self):
return self.is_windows() \
or self.is_cygwin()
class PerMachineDefaultable(PerMachine):
"""Extends `PerMachine` with the ability to default from `None`s.
"""
def __init__(self):
super().__init__(None, None, None)
def default_missing(self):
"""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):
"""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):
def matches_build_machine(self, machine: MachineChoice):
return self.build == self[machine]
class BinaryTable:
def __init__(self, binaries = {}, fallback = True):
self.binaries = binaries
self.fallback = fallback
for name, command in self.binaries.items():
if not isinstance(command, (list, str)):
# TODO generalize message
raise mesonlib.MesonException(
'Invalid type {!r} for binary {!r} in cross file'
''.format(command, name))
# Map from language identifiers to environment variables.
evarMap = {
# Compilers
'c': 'CC',
'cpp': 'CXX',
'cs': 'CSC',
'd': 'DC',
'fortran': 'FC',
'objc': 'OBJC',
'objcpp': 'OBJCXX',
'rust': 'RUSTC',
'vala': 'VALAC',
# Binutils
'strip': 'STRIP',
'ar': 'AR',
'windres': 'WINDRES',
'cmake': 'CMAKE',
'qmake': 'QMAKE',
'pkgconfig': 'PKG_CONFIG',
}
@classmethod
def detect_ccache(cls):
try:
has_ccache = subprocess.call(['ccache', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except OSError:
has_ccache = 1
if has_ccache == 0:
cmdlist = ['ccache']
else:
cmdlist = []
return cmdlist
@classmethod
def _warn_about_lang_pointing_to_cross(cls, compiler_exe, evar):
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):
compiler = mesonlib.stringlistify(entry)
# Ensure ccache exists and remove it if it doesn't
if compiler[0] == 'ccache':
compiler = compiler[1:]
ccache = cls.detect_ccache()
else:
ccache = []
# Return value has to be a list of compiler 'choices'
return compiler, ccache
def lookup_entry(self, name):
"""Lookup binary
Returns command with args as list if found, Returns `None` if nothing is
found.
First tries looking in explicit map, then tries environment variable.
"""
# Try explict map, don't fall back on env var
command = self.binaries.get(name)
if command is not None:
command = mesonlib.stringlistify(command)
# Relies on there being no "" env var
evar = self.evarMap.get(name, "")
self._warn_about_lang_pointing_to_cross(command[0], evar)
elif self.fallback:
# Relies on there being no "" env var
evar = self.evarMap.get(name, "")
command = os.environ.get(evar)
if command is not None:
command = shlex.split(command)
return command
class Directories:
"""Data class that holds information about directories for native and cross
builds.
"""
def __init__(self, bindir: typing.Optional[str] = None, datadir: typing.Optional[str] = None,
includedir: typing.Optional[str] = None, infodir: typing.Optional[str] = None,
libdir: typing.Optional[str] = None, libexecdir: typing.Optional[str] = None,
localedir: typing.Optional[str] = None, localstatedir: typing.Optional[str] = None,
mandir: typing.Optional[str] = None, prefix: typing.Optional[str] = None,
sbindir: typing.Optional[str] = None, sharedstatedir: typing.Optional[str] = None,
sysconfdir: typing.Optional[str] = None):
self.bindir = bindir
self.datadir = datadir
self.includedir = includedir
self.infodir = infodir
self.libdir = libdir
self.libexecdir = libexecdir
self.localedir = localedir
self.localstatedir = localstatedir
self.mandir = mandir
self.prefix = prefix
self.sbindir = sbindir
self.sharedstatedir = sharedstatedir
self.sysconfdir = sysconfdir
def __contains__(self, key: str) -> str:
return hasattr(self, key)
def __getitem__(self, key: str) -> str:
return getattr(self, key)
def __setitem__(self, key: str, value: typing.Optional[str]) -> None:
setattr(self, key, value)
def __iter__(self) -> typing.Iterator[typing.Tuple[str, str]]:
return iter(self.__dict__.items())

@ -19,10 +19,14 @@ from . import coredata
from .linkers import ArLinker, ArmarLinker, VisualStudioLinker, DLinker, CcrxLinker
from . import mesonlib
from .mesonlib import (
MesonException, EnvironmentException, MachineChoice, PerMachine, Popen_safe
MesonException, EnvironmentException, MachineChoice, PerMachine, Popen_safe,
)
from . import mlog
from .envconfig import (
BinaryTable, Directories, MachineInfo, MachineInfos, MesonConfigFile,
PerMachineDefaultable, Properties, known_cpu_families,
)
from . import compilers
from .compilers import (
CompilerType,
@ -79,28 +83,6 @@ from .compilers import (
build_filename = 'meson.build'
known_cpu_families = (
'aarch64',
'arc',
'arm',
'e2k',
'ia64',
'mips',
'mips64',
'parisc',
'ppc',
'ppc64',
'riscv32',
'riscv64',
'rl78',
'rx',
's390x',
'sparc',
'sparc64',
'x86',
'x86_64'
)
def detect_gcovr(version='3.1', log=False):
gcovr_exe = 'gcovr'
try:
@ -299,6 +281,37 @@ def detect_msys2_arch():
return os.environ['MSYSTEM_CARCH']
return None
def detect_machine_info(compilers = None) -> MachineInfo:
"""Detect the machine we're running on
If compilers are not provided, we cannot know as much. None out those
fields to avoid accidentally depending on partial knowledge. The
underlying ''detect_*'' method can be called to explicitly use the
partial information.
"""
return MachineInfo(
detect_system(),
detect_cpu_family(compilers) if compilers is not None else None,
detect_cpu(compilers) if compilers is not None else None,
sys.byteorder)
# TODO make this compare two `MachineInfo`s purely. How important is the
# `detect_cpu_family({})` distinction? It is the one impediment to that.
def machine_info_can_run(machine_info: MachineInfo):
"""Whether we can run binaries for this machine on the current machine.
Can almost always run 32-bit binaries on 64-bit natively if the host
and build systems are the same. We don't pass any compilers to
detect_cpu_family() here because we always want to know the OS
architecture, not what the compiler environment tells us.
"""
if machine_info.system != detect_system():
return False
true_build_cpu_family = detect_cpu_family({})
return \
(machine_info.cpu_family == true_build_cpu_family) or \
((true_build_cpu_family == 'x86_64') and (machine_info.cpu_family == 'x86'))
def search_version(text):
# Usually of the type 4.1.4 but compiler output may contain
# stuff like this:
@ -356,7 +369,7 @@ class Environment:
self.machines = MachineInfos()
# Will be fully initialized later using compilers later.
self.machines.detect_build()
self.detect_build_machine()
# Similar to coredata.compilers and build.compilers, but lower level in
# that there is no meta data, only names/paths.
@ -1162,6 +1175,9 @@ 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
@ -1236,386 +1252,10 @@ class Environment:
value = self.properties[for_machine].get('needs_exe_wrapper', None)
if value is not None:
return value
return not self.machines[for_machine].can_run()
return not machine_info_can_run(self.machines[for_machine])
def get_exe_wrapper(self):
if not self.need_exe_wrapper():
from .dependencies import EmptyExternalProgram
return EmptyExternalProgram()
return self.exe_wrapper
class MesonConfigFile:
@classmethod
def parse_datafile(cls, filename):
config = configparser.ConfigParser()
try:
with open(filename, 'r') as f:
config.read_file(f, filename)
except FileNotFoundError:
raise EnvironmentException('File not found: %s.' % filename)
return cls.from_config_parser(config)
@classmethod
def from_config_parser(cls, parser: configparser.ConfigParser):
out = {}
# This is a bit hackish at the moment.
for s in parser.sections():
section = {}
for entry in parser[s]:
value = parser[s][entry]
# Windows paths...
value = value.replace('\\', '\\\\')
if ' ' in entry or '\t' in entry or "'" in entry or '"' in entry:
raise EnvironmentException('Malformed variable name %s in cross file..' % entry)
try:
res = eval(value, {'__builtins__': None}, {'true': True, 'false': False})
except Exception:
raise EnvironmentException('Malformed value in cross file variable %s.' % entry)
for i in (res if isinstance(res, list) else [res]):
if not isinstance(i, (str, int, bool)):
raise EnvironmentException('Malformed value in cross file variable %s.' % entry)
section[entry] = res
out[s] = section
return out
class Properties:
def __init__(
self,
properties: typing.Optional[typing.Dict[str, typing.Union[str, typing.List[str]]]] = None):
self.properties = properties or {}
def has_stdlib(self, language):
return language + '_stdlib' in self.properties
def get_stdlib(self, language):
return self.properties[language + '_stdlib']
def get_root(self):
return self.properties.get('root', None)
def get_sys_root(self):
return self.properties.get('sys_root', None)
def __eq__(self, other):
if isinstance(other, type(self)):
return self.properties == other.properties
return NotImplemented
# TODO consider removing so Properties is less freeform
def __getitem__(self, key):
return self.properties[key]
# TODO consider removing so Properties is less freeform
def __contains__(self, item):
return item in self.properties
# TODO consider removing, for same reasons as above
def get(self, key, default=None):
return self.properties.get(key, default)
class MachineInfo:
def __init__(self, system, cpu_family, cpu, endian):
self.system = system
self.cpu_family = cpu_family
self.cpu = cpu
self.endian = endian
def __eq__(self, other):
if self.__class__ is not other.__class__:
return NotImplemented
return \
self.system == other.system and \
self.cpu_family == other.cpu_family and \
self.cpu == other.cpu and \
self.endian == other.endian
def __ne__(self, other):
if self.__class__ is not other.__class__:
return NotImplemented
return not self.__eq__(other)
def __repr__(self):
return '<MachineInfo: {} {} ({})>'.format(self.system, self.cpu_family, self.cpu)
@staticmethod
def detect(compilers = None):
"""Detect the machine we're running on
If compilers are not provided, we cannot know as much. None out those
fields to avoid accidentally depending on partial knowledge. The
underlying ''detect_*'' method can be called to explicitly use the
partial information.
"""
return MachineInfo(
detect_system(),
detect_cpu_family(compilers) if compilers is not None else None,
detect_cpu(compilers) if compilers is not None else None,
sys.byteorder)
@staticmethod
def from_literal(literal):
minimum_literal = {'cpu', 'cpu_family', 'endian', 'system'}
if set(literal) < minimum_literal:
raise EnvironmentException(
'Machine info is currently {}\n'.format(literal) +
'but is missing {}.'.format(minimum_literal - set(literal)))
cpu_family = literal['cpu_family']
if cpu_family not in known_cpu_families:
mlog.warning('Unknown CPU family %s, please report this at https://github.com/mesonbuild/meson/issues/new' % cpu_family)
endian = literal['endian']
if endian not in ('little', 'big'):
mlog.warning('Unknown endian %s' % endian)
return MachineInfo(
literal['system'],
cpu_family,
literal['cpu'],
endian)
def is_windows(self):
"""
Machine is windows?
"""
return self.system == 'windows'
def is_cygwin(self):
"""
Machine is cygwin?
"""
return self.system == 'cygwin'
def is_linux(self):
"""
Machine is linux?
"""
return self.system == 'linux'
def is_darwin(self):
"""
Machine is Darwin (iOS/OS X)?
"""
return self.system in ('darwin', 'ios')
def is_android(self):
"""
Machine is Android?
"""
return self.system == 'android'
def is_haiku(self):
"""
Machine is Haiku?
"""
return self.system == 'haiku'
def is_openbsd(self):
"""
Machine is OpenBSD?
"""
return self.system == 'openbsd'
# Various prefixes and suffixes for import libraries, shared libraries,
# static libraries, and executables.
# Versioning is added to these names in the backends as-needed.
def get_exe_suffix(self):
if self.is_windows() or self.is_cygwin():
return 'exe'
else:
return ''
def get_object_suffix(self):
if self.is_windows():
return 'obj'
else:
return 'o'
def libdir_layout_is_win(self):
return self.is_windows() \
or self.is_cygwin()
# TODO make this compare two `MachineInfo`s purely. How important is the
# `detect_cpu_family({})` distinction? It is the one impediment to that.
def can_run(self):
"""Whether we can run binaries for this machine on the current machine.
Can almost always run 32-bit binaries on 64-bit natively if the host
and build systems are the same. We don't pass any compilers to
detect_cpu_family() here because we always want to know the OS
architecture, not what the compiler environment tells us.
"""
if self.system != detect_system():
return False
true_build_cpu_family = detect_cpu_family({})
return \
(self.cpu_family == true_build_cpu_family) or \
((true_build_cpu_family == 'x86_64') and (self.cpu_family == 'x86'))
class PerMachineDefaultable(PerMachine):
"""Extends `PerMachine` with the ability to default from `None`s.
"""
def __init__(self):
super().__init__(None, None, None)
def default_missing(self):
"""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):
"""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):
def detect_build(self, compilers = None):
self.build = MachineInfo.detect(compilers)
def matches_build_machine(self, machine: MachineChoice):
return self.build == self[machine]
class BinaryTable:
def __init__(self, binaries = {}, fallback = True):
self.binaries = binaries
self.fallback = fallback
for name, command in self.binaries.items():
if not isinstance(command, (list, str)):
# TODO generalize message
raise mesonlib.MesonException(
'Invalid type {!r} for binary {!r} in cross file'
''.format(command, name))
# Map from language identifiers to environment variables.
evarMap = {
# Compilers
'c': 'CC',
'cpp': 'CXX',
'cs': 'CSC',
'd': 'DC',
'fortran': 'FC',
'objc': 'OBJC',
'objcpp': 'OBJCXX',
'rust': 'RUSTC',
'vala': 'VALAC',
# Binutils
'strip': 'STRIP',
'ar': 'AR',
'windres': 'WINDRES',
'cmake': 'CMAKE',
'qmake': 'QMAKE',
'pkgconfig': 'PKG_CONFIG',
}
@classmethod
def detect_ccache(cls):
try:
has_ccache = subprocess.call(['ccache', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except OSError:
has_ccache = 1
if has_ccache == 0:
cmdlist = ['ccache']
else:
cmdlist = []
return cmdlist
@classmethod
def _warn_about_lang_pointing_to_cross(cls, compiler_exe, evar):
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):
compiler = mesonlib.stringlistify(entry)
# Ensure ccache exists and remove it if it doesn't
if compiler[0] == 'ccache':
compiler = compiler[1:]
ccache = cls.detect_ccache()
else:
ccache = []
# Return value has to be a list of compiler 'choices'
return compiler, ccache
def lookup_entry(self, name):
"""Lookup binary
Returns command with args as list if found, Returns `None` if nothing is
found.
First tries looking in explicit map, then tries environment variable.
"""
# Try explict map, don't fall back on env var
command = self.binaries.get(name)
if command is not None:
command = mesonlib.stringlistify(command)
# Relies on there being no "" env var
evar = self.evarMap.get(name, "")
self._warn_about_lang_pointing_to_cross(command[0], evar)
elif self.fallback:
# Relies on there being no "" env var
evar = self.evarMap.get(name, "")
command = os.environ.get(evar)
if command is not None:
command = shlex.split(command)
return command
class Directories:
"""Data class that holds information about directories for native and cross
builds.
"""
def __init__(self, bindir: typing.Optional[str] = None, datadir: typing.Optional[str] = None,
includedir: typing.Optional[str] = None, infodir: typing.Optional[str] = None,
libdir: typing.Optional[str] = None, libexecdir: typing.Optional[str] = None,
localedir: typing.Optional[str] = None, localstatedir: typing.Optional[str] = None,
mandir: typing.Optional[str] = None, prefix: typing.Optional[str] = None,
sbindir: typing.Optional[str] = None, sharedstatedir: typing.Optional[str] = None,
sysconfdir: typing.Optional[str] = None):
self.bindir = bindir
self.datadir = datadir
self.includedir = includedir
self.infodir = infodir
self.libdir = libdir
self.libexecdir = libexecdir
self.localedir = localedir
self.localstatedir = localstatedir
self.mandir = mandir
self.prefix = prefix
self.sbindir = sbindir
self.sharedstatedir = sharedstatedir
self.sysconfdir = sysconfdir
def __contains__(self, key: str) -> str:
return hasattr(self, key)
def __getitem__(self, key: str) -> str:
return getattr(self, key)
def __setitem__(self, key: str, value: typing.Optional[str]) -> None:
setattr(self, key, value)
def __iter__(self) -> typing.Iterator[typing.Tuple[str, str]]:
return iter(self.__dict__.items())

@ -2025,7 +2025,7 @@ class Interpreter(InterpreterBase):
# 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.machines.detect_build(self.coredata.compilers)
self.build.environment.detect_build_machine(self.coredata.compilers)
self.build.environment.machines.default_missing()
assert self.build.environment.machines.build.cpu is not None
assert self.build.environment.machines.host.cpu is not None

Loading…
Cancel
Save