Move BinaryTable environment lookups to Environment

This means that all the env lookups are done once, at initial configure
time. This has all of the expected advantages.
pull/8159/head
Dylan Baker 4 years ago
parent 38c7a7590c
commit e7a5c75285
  1. 63
      mesonbuild/envconfig.py
  2. 26
      mesonbuild/environment.py
  3. 33
      run_unittests.py

@ -23,6 +23,9 @@ from pathlib import Path
_T = T.TypeVar('_T')
if T.TYPE_CHECKING:
from .environment import Environment
# These classes contains all the data pulled from configuration files (native
# and cross file currently), and also assists with the reading environment
@ -122,10 +125,10 @@ ENV_VAR_PROG_MAP: T.Mapping[str, str] = {
# Deprecated environment variables mapped from the new variable to the old one
# Deprecated in 0.54.0
DEPRECATED_ENV_PROG_MAP: T.Mapping[str, str] = {
'DC_LD': 'D_LD',
'FC_LD': 'F_LD',
'RUSTC_LD': 'RUST_LD',
'OBJCXX_LD': 'OBJCPP_LD',
'd_ld': 'D_LD',
'fortran_ld': 'F_LD',
'rust_ld': 'RUST_LD',
'objcpp_ld': 'OBJCPP_LD',
}
class CMakeSkipCompilerTest(Enum):
@ -394,17 +397,18 @@ class MachineInfo:
return self.is_windows() or self.is_cygwin()
class BinaryTable:
def __init__(
self,
binaries: T.Optional[T.Dict[str, T.Union[str, T.List[str]]]] = None,
):
self.binaries = binaries or {} # type: T.Dict[str, T.Union[str, T.List[str]]]
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))
self.binaries: T.Dict[str, T.List[str]] = {}
if binaries:
for name, command in binaries.items():
if not isinstance(command, (list, str)):
raise mesonlib.MesonException(
f'Invalid type {command!r} for entry {name!r} in cross file')
self.binaries[name] = mesonlib.listify(command)
@staticmethod
def detect_ccache() -> T.List[str]:
@ -426,42 +430,17 @@ class BinaryTable:
# Return value has to be a list of compiler 'choices'
return compiler, ccache
def lookup_entry(self,
for_machine: MachineChoice,
is_cross: bool,
name: str) -> T.Optional[T.List[str]]:
def lookup_entry(self, name: str) -> T.Optional[T.List[str]]:
"""Lookup binary in cross/native file and fallback to environment.
Returns command with args as list if found, Returns `None` if nothing is
found.
"""
# Try explicit map, don't fall back on env var
# Try explict map, then env vars
for _ in [()]: # a trick to get `break`
raw_command = self.binaries.get(name)
if raw_command is not None:
command = mesonlib.stringlistify(raw_command)
break # found
evar = ENV_VAR_PROG_MAP.get(name)
if evar is not None:
raw_command = get_env_var(for_machine, is_cross, evar)
if raw_command is None:
deprecated = DEPRECATED_ENV_PROG_MAP.get(evar)
if deprecated is not None:
raw_command = get_env_var(for_machine, is_cross, deprecated)
if raw_command is not None:
mlog.deprecation(
'The', deprecated, 'environment variable is deprecated in favor of',
evar, once=True)
if raw_command is not None:
command = split_args(raw_command)
break # found
command = None
# Do not return empty or blank string entries
if command is not None and (len(command) == 0 or len(command[0].strip()) == 0):
command = None
command = self.binaries.get(name)
if not command:
return None
elif not command[0].strip():
return None
return command
class CMakeVariables:

@ -29,7 +29,7 @@ from .mesonlib import (
from . import mlog
from .envconfig import (
BinaryTable, MachineInfo,
BinaryTable, ENV_VAR_PROG_MAP, MachineInfo,
Properties, known_cpu_families, get_env_var_pair,
CMakeVariables,
)
@ -129,6 +129,7 @@ from .compilers import (
VisualStudioCCompiler,
VisualStudioCPPCompiler,
)
from mesonbuild import envconfig
if T.TYPE_CHECKING:
from configparser import ConfigParser
@ -654,6 +655,7 @@ class Environment:
# Take default value from env if not set in cross/native files or command line.
self.set_default_options_from_env()
self._set_default_binaries_from_env()
# Warn if the user is using two different ways of setting build-type
# options that override each other
@ -804,6 +806,21 @@ class Environment:
v = mesonlib.listify(self.options.get(key, []))
self.options.setdefault(key, v + p_list)
def _set_default_binaries_from_env(self) -> None:
"""Set default binaries from the environment.
For example, pkg-config can be set via PKG_CONFIG, or in the machine
file. We want to set the default to the env variable.
"""
opts = itertools.chain(envconfig.DEPRECATED_ENV_PROG_MAP.items(),
envconfig.ENV_VAR_PROG_MAP.items())
for (name, evar), for_machine in itertools.product(opts, MachineChoice):
p_env_pair = get_env_var_pair(for_machine, self.is_cross_build(), evar)
if p_env_pair is not None:
_, p_env = p_env_pair
self.binaries[for_machine].binaries.setdefault(name, mesonlib.split_args(p_env))
def create_new_coredata(self, options: 'argparse.Namespace') -> None:
# WARNING: Don't use any values from coredata in __init__. It gets
# re-initialized with project options by the interpreter during
@ -853,11 +870,8 @@ class Environment:
def is_library(self, fname):
return is_library(fname)
def lookup_binary_entry(self, for_machine: MachineChoice, name: str):
return self.binaries[for_machine].lookup_entry(
for_machine,
self.is_cross_build(),
name)
def lookup_binary_entry(self, for_machine: MachineChoice, name: str) -> T.List[str]:
return self.binaries[for_machine].lookup_entry(name)
@staticmethod
def get_gnu_compiler_defines(compiler):

@ -2637,17 +2637,16 @@ class AllPlatformTests(BasePlatformTests):
# something like `ccache gcc -pipe` or `distcc ccache gcc` works.
wrapper = os.path.join(testdir, 'compiler wrapper.py')
wrappercc = python_command + [wrapper] + cc.get_exelist() + ['-DSOME_ARG']
wrappercc_s = ''
for w in wrappercc:
wrappercc_s += quote_arg(w) + ' '
os.environ[evar] = wrappercc_s
wcc = getattr(env, 'detect_{}_compiler'.format(lang))(MachineChoice.HOST)
os.environ[evar] = ' '.join(quote_arg(w) for w in wrappercc)
# Check static linker too
wrapperlinker = python_command + [wrapper] + linker.get_exelist() + linker.get_always_args()
wrapperlinker_s = ''
for w in wrapperlinker:
wrapperlinker_s += quote_arg(w) + ' '
os.environ['AR'] = wrapperlinker_s
os.environ['AR'] = ' '.join(quote_arg(w) for w in wrapperlinker)
# Need a new env to re-run environment loading
env = get_fake_env(testdir, self.builddir, self.prefix)
wcc = getattr(env, 'detect_{}_compiler'.format(lang))(MachineChoice.HOST)
wlinker = env.detect_static_linker(wcc)
# Pop it so we don't use it for the next detection
evalue = os.environ.pop('AR')
@ -5692,9 +5691,9 @@ class WindowsTests(BasePlatformTests):
envvars = [mesonbuild.envconfig.ENV_VAR_PROG_MAP['{}_ld'.format(lang)]]
# Also test a deprecated variable if there is one.
if envvars[0] in mesonbuild.envconfig.DEPRECATED_ENV_PROG_MAP:
if f'{lang}_ld' in mesonbuild.envconfig.DEPRECATED_ENV_PROG_MAP:
envvars.append(
mesonbuild.envconfig.DEPRECATED_ENV_PROG_MAP[envvars[0]])
mesonbuild.envconfig.DEPRECATED_ENV_PROG_MAP[f'{lang}_ld'])
for envvar in envvars:
with mock.patch.dict(os.environ, {envvar: name}):
@ -7293,12 +7292,12 @@ class LinuxlikeTests(BasePlatformTests):
raise unittest.SkipTest('Solaris currently cannot override the linker.')
if not shutil.which(check):
raise unittest.SkipTest('Could not find {}.'.format(check))
envvars = [mesonbuild.envconfig.BinaryTable.ENV_VAR_PROG_MAP['{}_ld'.format(lang)]]
envvars = [mesonbuild.envconfig.ENV_VAR_PROG_MAP['{}_ld'.format(lang)]]
# Also test a deprecated variable if there is one.
if envvars[0] in mesonbuild.envconfig.BinaryTable.DEPRECATION_MAP:
if f'{lang}_ld' in mesonbuild.envconfig.DEPRECATED_ENV_PROG_MAP:
envvars.append(
mesonbuild.envconfig.BinaryTable.DEPRECATION_MAP[envvars[0]])
mesonbuild.envconfig.DEPRECATED_ENV_PROG_MAP[f'{lang}_ld'])
for envvar in envvars:
with mock.patch.dict(os.environ, {envvar: name}):
@ -8274,7 +8273,7 @@ class NativeFileTests(BasePlatformTests):
return 'gfortran', 'gcc'
self.helper_for_compiler('fortran', cb)
def _single_implementation_compiler(self, lang, binary, version_str, version):
def _single_implementation_compiler(self, lang: str, binary: str, version_str: str, version: str) -> None:
"""Helper for languages with a single (supported) implementation.
Builds a wrapper around the compiler to override the version.
@ -8283,7 +8282,7 @@ class NativeFileTests(BasePlatformTests):
env = get_fake_env()
getter = getattr(env, 'detect_{}_compiler'.format(lang))
getter = functools.partial(getter, MachineChoice.HOST)
env.binaries.host.binaries[lang] = wrapper
env.binaries.host.binaries[lang] = [wrapper]
compiler = getter()
self.assertEqual(compiler.version, version)
@ -8310,7 +8309,7 @@ class NativeFileTests(BasePlatformTests):
'swiftc', version='Swift 1.2345', outfile='stderr',
extra_args={'Xlinker': 'macosx_version. PROJECT:ld - 1.2.3'})
env = get_fake_env()
env.binaries.host.binaries['swift'] = wrapper
env.binaries.host.binaries['swift'] = [wrapper]
compiler = env.detect_swift_compiler(MachineChoice.HOST)
self.assertEqual(compiler.version, '1.2345')

Loading…
Cancel
Save