diff options
author | John Ericson <git@JohnEricson.me> | 2019-08-02 14:29:40 -0400 |
---|---|---|
committer | Jussi Pakkanen <jpakkane@gmail.com> | 2020-03-23 17:51:36 +0200 |
commit | 3a4388e51dee5e7e58f1e5ad4e60fb73b4aacf08 (patch) | |
tree | 8f0637e630777cc4a5707c16a30c4d980b888371 | |
parent | f6b0425576640e1613f64503951c7c604b868947 (diff) | |
download | meson-3a4388e51dee5e7e58f1e5ad4e60fb73b4aacf08.zip meson-3a4388e51dee5e7e58f1e5ad4e60fb73b4aacf08.tar.gz meson-3a4388e51dee5e7e58f1e5ad4e60fb73b4aacf08.tar.bz2 |
Fix legacy env var support with cross
Fix #3969
-rw-r--r-- | docs/markdown/howtox.md | 22 | ||||
-rw-r--r-- | docs/markdown/snippets/env_vars_and_cross.md | 12 | ||||
-rw-r--r-- | mesonbuild/backend/backends.py | 2 | ||||
-rw-r--r-- | mesonbuild/cmake/executor.py | 2 | ||||
-rw-r--r-- | mesonbuild/compilers/c.py | 2 | ||||
-rw-r--r-- | mesonbuild/compilers/compilers.py | 62 | ||||
-rw-r--r-- | mesonbuild/compilers/cpp.py | 2 | ||||
-rw-r--r-- | mesonbuild/compilers/mixins/islinker.py | 12 | ||||
-rw-r--r-- | mesonbuild/coredata.py | 47 | ||||
-rw-r--r-- | mesonbuild/dependencies/base.py | 26 | ||||
-rw-r--r-- | mesonbuild/envconfig.py | 114 | ||||
-rw-r--r-- | mesonbuild/environment.py | 35 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 3 | ||||
-rw-r--r-- | mesonbuild/linkers.py | 22 | ||||
-rw-r--r-- | mesonbuild/modules/python.py | 4 | ||||
-rw-r--r-- | mesonbuild/modules/python3.py | 2 | ||||
-rw-r--r-- | mesonbuild/modules/windows.py | 2 | ||||
-rwxr-xr-x | run_unittests.py | 27 |
18 files changed, 234 insertions, 164 deletions
diff --git a/docs/markdown/howtox.md b/docs/markdown/howtox.md index 1a2add0..8231d3d 100644 --- a/docs/markdown/howtox.md +++ b/docs/markdown/howtox.md @@ -12,19 +12,15 @@ When first running Meson, set it in an environment variable. $ CC=mycc meson <options> ``` -Note that environment variables like `CC` _always_ refer to the native -compiler. That is, the compiler used to compile programs that run on -the current machine. The compiler used in cross compilation is set -with the cross file. - -This behaviour is different from e.g. Autotools, where cross -compilation is done by setting `CC` to point to the cross compiler -(such as `/usr/bin/arm-linux-gnueabihf-gcc`). The reason for this is -that Meson supports natively the case where you compile helper tools -(such as code generators) and use the results during the -build. Because of this Meson needs to know both the native and the -cross compiler. The former is set via the environment variables or -native-files and the latter via the cross file only. +Note that environment variables like `CC` only works in native builds. The `CC` +refers to the compiler for the host platform, that is the compiler used to +compile programs that run on the machine we will eventually install the project +on. The compiler used to build things that run on the machine we do the +building can be specified with `CC_FOR_BUILD`. You can use it in cross builds. + +Note that environment variables are never the idiomatic way to do anything with +Meson, however. It is better to use the native and cross files. And the tools +for the host platform in cross builds can only be specified with a cross file. There is a table of all environment variables supported [Here](Reference-tables.md#compiler-and-linker-selection-variables) diff --git a/docs/markdown/snippets/env_vars_and_cross.md b/docs/markdown/snippets/env_vars_and_cross.md new file mode 100644 index 0000000..61a63f3 --- /dev/null +++ b/docs/markdown/snippets/env_vars_and_cross.md @@ -0,0 +1,12 @@ +## Environment Variables with Cross Builds + +Previously in Meson, variables like `CC` effected both the host and build +platforms for native builds, but the just the build platform for cross builds. +Now `CC_FOR_BUILD` is used for the build platform in cross builds. + +This old behavior is inconsistent with the way Autotools works, which +undermines the purpose of distro-integration that is the only reason +environment variables are supported at all in Meson. The new behavior is not +quite the same, but doesn't conflict: meson doesn't always repond to an +environment when Autoconf would, but when it does it interprets it as Autotools +would. diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index e9ab9f4..ab922ba 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1044,7 +1044,7 @@ class Backend: subprocess.check_call(cmd, env=child_env) def create_install_data(self): - strip_bin = self.environment.binaries.host.lookup_entry('strip') + strip_bin = self.environment.lookup_binary_entry(MachineChoice.HOST, 'strip') if strip_bin is None: if self.environment.is_cross_build(): mlog.warning('Cross file does not specify strip binary, result will not be stripped.') diff --git a/mesonbuild/cmake/executor.py b/mesonbuild/cmake/executor.py index 7b9edf7..a41b293 100644 --- a/mesonbuild/cmake/executor.py +++ b/mesonbuild/cmake/executor.py @@ -66,7 +66,7 @@ class CMakeExecutor: # Create an iterator of options def search(): # Lookup in cross or machine file. - potential_cmakepath = environment.binaries[self.for_machine].lookup_entry('cmake') + potential_cmakepath = environment.lookup_binary_entry(self.for_machine, 'cmake') if potential_cmakepath is not None: mlog.debug('CMake binary for %s specified from cross file, native file, or env var as %s.', self.for_machine, potential_cmakepath) yield ExternalProgram.from_entry('cmake', potential_cmakepath) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 82640be..eba7131 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -17,6 +17,7 @@ import typing as T from .. import coredata from ..mesonlib import MachineChoice, MesonException, mlog, version_compare +from ..linkers import LinkerEnvVarsMixin from .c_function_attributes import C_FUNC_ATTRIBUTES from .mixins.clike import CLikeCompiler from .mixins.ccrx import CcrxCompiler @@ -29,7 +30,6 @@ from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler from .mixins.clang import ClangCompiler from .mixins.elbrus import ElbrusCompiler from .mixins.pgi import PGICompiler -from .mixins.islinker import LinkerEnvVarsMixin from .mixins.emscripten import EmscriptenMixin from .compilers import ( gnu_winlibs, diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index a0d752c..de8fb70 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -18,7 +18,10 @@ import itertools import typing as T from functools import lru_cache -from ..linkers import StaticLinker, GnuLikeDynamicLinkerMixin, SolarisDynamicLinker +from ..linkers import ( + GnuLikeDynamicLinkerMixin, LinkerEnvVarsMixin, SolarisDynamicLinker, + StaticLinker, +) from .. import coredata from .. import mlog from .. import mesonlib @@ -27,7 +30,7 @@ from ..mesonlib import ( Popen_safe, split_args ) from ..envconfig import ( - Properties, + Properties, get_env_var ) if T.TYPE_CHECKING: @@ -79,7 +82,9 @@ clink_suffixes += ('h', 'll', 's') all_suffixes = set(itertools.chain(*lang_suffixes.values(), clink_suffixes)) # Languages that should use LDFLAGS arguments when linking. -languages_using_ldflags = ('objcpp', 'cpp', 'objc', 'c', 'fortran', 'd', 'cuda') +languages_using_ldflags = {'objcpp', 'cpp', 'objc', 'c', 'fortran', 'd', 'cuda'} +# Languages that should use CPPFLAGS arguments when linking. +languages_using_cppflags = {'c', 'cpp', 'objc', 'objcpp'} soregex = re.compile(r'.*\.so(\.[0-9]+)?(\.[0-9]+)?(\.[0-9]+)?$') # Environment variables that each lang uses. @@ -855,8 +860,10 @@ class Compiler: """ return [] - def get_linker_args_from_envvars(self) -> T.List[str]: - return self.linker.get_args_from_envvars() + def get_linker_args_from_envvars(self, + for_machine: MachineChoice, + is_cross: bool) -> T.List[str]: + return self.linker.get_args_from_envvars(for_machine, is_cross) def get_options(self) -> T.Dict[str, coredata.UserOption]: return {} @@ -1208,37 +1215,27 @@ def get_largefile_args(compiler): return [] -def get_args_from_envvars(lang: str, use_linker_args: bool) -> T.Tuple[T.List[str], T.List[str]]: +def get_args_from_envvars(lang: str, + for_machine: MachineChoice, + is_cross: bool, + use_linker_args: bool) -> T.Tuple[T.List[str], T.List[str]]: """ Returns a tuple of (compile_flags, link_flags) for the specified language from the inherited environment """ - def log_var(var, val: T.Optional[str]): - if val: - mlog.log('Appending {} from environment: {!r}'.format(var, val)) - else: - mlog.debug('No {} in the environment, not changing global flags.'.format(var)) - if lang not in cflags_mapping: return [], [] compile_flags = [] # type: T.List[str] link_flags = [] # type: T.List[str] - env_compile_flags = os.environ.get(cflags_mapping[lang]) - log_var(cflags_mapping[lang], env_compile_flags) + env_compile_flags = get_env_var(for_machine, is_cross, cflags_mapping[lang]) if env_compile_flags is not None: compile_flags += split_args(env_compile_flags) # Link flags (same for all languages) if lang in languages_using_ldflags: - # This is duplicated between the linkers, but I'm not sure how else - # to handle this - env_link_flags = split_args(os.environ.get('LDFLAGS', '')) - else: - env_link_flags = [] - log_var('LDFLAGS', env_link_flags) - link_flags += env_link_flags + link_flags += LinkerEnvVarsMixin.get_args_from_envvars(for_machine, is_cross) if use_linker_args: # When the compiler is used as a wrapper around the linker (such as # with GCC and Clang), the compile flags can be needed while linking @@ -1247,16 +1244,18 @@ def get_args_from_envvars(lang: str, use_linker_args: bool) -> T.Tuple[T.List[st link_flags = compile_flags + link_flags # Pre-processor flags for certain languages - if lang in {'c', 'cpp', 'objc', 'objcpp'}: - env_preproc_flags = os.environ.get('CPPFLAGS') - log_var('CPPFLAGS', env_preproc_flags) + if lang in languages_using_cppflags: + env_preproc_flags = get_env_var(for_machine, is_cross, 'CPPFLAGS') if env_preproc_flags is not None: compile_flags += split_args(env_preproc_flags) return compile_flags, link_flags -def get_global_options(lang: str, comp: T.Type[Compiler], +def get_global_options(lang: str, + comp: T.Type[Compiler], + for_machine: MachineChoice, + is_cross: bool, properties: Properties) -> T.Dict[str, coredata.UserOption]: """Retreive options that apply to all compilers for a given language.""" description = 'Extra arguments passed to the {}'.format(lang) @@ -1269,13 +1268,12 @@ def get_global_options(lang: str, comp: T.Type[Compiler], [], split_args=True, user_input=True, allow_dups=True), } - if properties.fallback: - # Get from env vars. - # XXX: True here is a hack - compile_args, link_args = get_args_from_envvars(lang, comp.INVOKES_LINKER) - else: - compile_args = [] - link_args = [] + # Get from env vars. + compile_args, link_args = get_args_from_envvars( + lang, + for_machine, + is_cross, + comp.INVOKES_LINKER) for k, o in opts.items(): if k in properties: diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index ac0500a..94feeb9 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -21,6 +21,7 @@ from .. import coredata from .. import mlog from ..mesonlib import MesonException, MachineChoice, version_compare +from ..linkers import LinkerEnvVarsMixin from .compilers import ( gnu_winlibs, msvc_winlibs, @@ -37,7 +38,6 @@ from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler from .mixins.clang import ClangCompiler from .mixins.elbrus import ElbrusCompiler from .mixins.pgi import PGICompiler -from .mixins.islinker import LinkerEnvVarsMixin from .mixins.emscripten import EmscriptenMixin if T.TYPE_CHECKING: diff --git a/mesonbuild/compilers/mixins/islinker.py b/mesonbuild/compilers/mixins/islinker.py index 3092395..681c816 100644 --- a/mesonbuild/compilers/mixins/islinker.py +++ b/mesonbuild/compilers/mixins/islinker.py @@ -20,7 +20,6 @@ are both the linker and compiler in one binary. This module provides mixin classes for those cases. """ -import os import typing as T from ... import mesonlib @@ -30,17 +29,6 @@ if T.TYPE_CHECKING: from ...environment import Environment -class LinkerEnvVarsMixin: - - """Mixin reading LDFLAGS from the environment.""" - - def get_linker_args_from_envvars(self) -> T.List[str]: - flags = os.environ.get('LDFLAGS') - if not flags: - return [] - return mesonlib.split_args(flags) - - class BasicLinkerIsCompilerMixin: """Provides a baseline of methods that a linker would implement. diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 6f75534..f29f5f0 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -1,4 +1,4 @@ -# Copyright 2012-2019 The Meson development team +# Copyrighs 2012-2019 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. @@ -22,6 +22,7 @@ from .mesonlib import ( MesonException, MachineChoice, PerMachine, OrderedSet, default_libdir, default_libexecdir, default_prefix, split_args ) +from .envconfig import get_env_var_pair from .wrap import WrapMode import ast import argparse @@ -758,20 +759,29 @@ class CoreData: # Some options default to environment variables if they are # unset, set those now. These will either be overwritten # below, or they won't. These should only be set on the first run. - p_env = os.environ.get('PKG_CONFIG_PATH') - if p_env: - # PKG_CONFIG_PATH may contain duplicates, which must be - # removed, else a duplicates-in-array-option warning arises. - p_list = list(OrderedSet(p_env.split(':'))) - if env.first_invocation: - options['pkg_config_path'] = p_list - elif options.get('pkg_config_path', []) != p_list: - mlog.warning( - 'PKG_CONFIG_PATH environment variable has changed ' - 'between configurations, meson ignores this. ' - 'Use -Dpkg_config_path to change pkg-config search ' - 'path instead.' - ) + for for_machine in MachineChoice: + p_env_pair = get_env_var_pair(for_machine, self.is_cross_build(), 'PKG_CONFIG_PATH') + if p_env_pair is not None: + p_env_var, p_env = p_env_pair + + # PKG_CONFIG_PATH may contain duplicates, which must be + # removed, else a duplicates-in-array-option warning arises. + p_list = list(OrderedSet(p_env.split(':'))) + + key = 'pkg_config_path' + if for_machine == MachineChoice.BUILD: + key = 'build.' + key + + if env.first_invocation: + options[key] = p_list + elif options.get(key, []) != p_list: + mlog.warning( + p_env_var + + ' environment variable has changed ' + 'between configurations, meson ignores this. ' + 'Use -Dpkg_config_path to change pkg-config search ' + 'path instead.' + ) for k, v in env.cmd_line_options.items(): if subproject: @@ -794,7 +804,12 @@ class CoreData: from .compilers import compilers optprefix = lang + '_' - for k, o in compilers.get_global_options(lang, comp, env.properties[for_machine]).items(): + for k, o in compilers.get_global_options( + lang, + comp, + for_machine, + env.is_cross_build(), + env.properties[for_machine]).items(): if not k.startswith(optprefix): raise MesonException('Internal error, %s has incorrect prefix.' % k) # prefixed compiler options affect just this machine diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index f03af12..50f4179 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -31,6 +31,7 @@ from pathlib import Path, PurePath from .. import mlog from .. import mesonlib from ..compilers import clib_langs +from ..envconfig import get_env_var from ..environment import BinaryTable, Environment, MachineInfo from ..cmake import CMakeExecutor, CMakeTraceParser, CMakeException from ..mesonlib import MachineChoice, MesonException, OrderedSet, PerMachine @@ -352,7 +353,7 @@ class ExternalDependency(Dependency, HasNativeKwarg): # Create an iterator of options def search_tool(self, name, display_name, default_names): # Lookup in cross or machine file. - potential_path = self.env.binaries[self.for_machine].lookup_entry(name) + potential_path = self.env.lookup_binary_entry(self.for_machine, name) if potential_path is not None: mlog.debug('{} binary for {} specified from cross file, native file, ' 'or env var as {}'.format(display_name, self.for_machine, potential_path)) @@ -435,7 +436,7 @@ class ConfigToolDependency(ExternalDependency): if not isinstance(versions, list) and versions is not None: versions = listify(versions) - tool = self.env.binaries[self.for_machine].lookup_entry(self.tool_name) + tool = self.env.lookup_binary_entry(self.for_machine, self.tool_name) if tool is not None: tools = [tool] else: @@ -762,7 +763,10 @@ class PkgConfigDependency(ExternalDependency): # # Only prefix_libpaths are reordered here because there should not be # too many system_libpaths to cause library version issues. - pkg_config_path = os.environ.get('PKG_CONFIG_PATH') + pkg_config_path = get_env_var( + self.for_machine, + self.env.is_cross_build(), + 'PKG_CONFIG_PATH') if pkg_config_path: pkg_config_path = pkg_config_path.split(os.pathsep) else: @@ -1098,8 +1102,12 @@ class CMakeDependency(ExternalDependency): cm_args.append('-DCMAKE_MODULE_PATH=' + ';'.join(cm_path)) pref_path = self.env.coredata.builtins_per_machine[self.for_machine]['cmake_prefix_path'].value - if 'CMAKE_PREFIX_PATH' in os.environ: - env_pref_path = os.environ['CMAKE_PREFIX_PATH'].split(os.pathsep) + env_pref_path = get_env_var( + self.for_machine, + self.env.is_cross_build(), + 'CMAKE_PREFIX_PATH') + if env_pref_path is not None: + env_pref_path = env_pref_path.split(os.pathsep) env_pref_path = [x for x in env_pref_path if x] # Filter out empty strings if not pref_path: pref_path = [] @@ -1813,8 +1821,12 @@ class ExternalProgram: return ' '.join(self.command) @classmethod - def from_bin_list(cls, bt: BinaryTable, name): - command = bt.lookup_entry(name) + def from_bin_list(cls, env: Environment, for_machine: MachineChoice, name): + # There is a static `for_machine` for this class because the binary + # aways runs on the build platform. (It's host platform is our build + # platform.) But some external programs have a target platform, so this + # is what we are specifying here. + command = env.lookup_binary_entry(for_machine, name) if command is None: return NonExistingExternalProgram() return cls.from_entry(name, command) diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index 0ae1e6d..ac13a71 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -16,7 +16,7 @@ import configparser, os, subprocess import typing as T from . import mesonlib -from .mesonlib import EnvironmentException, split_args +from .mesonlib import EnvironmentException, MachineChoice, PerMachine, split_args from . import mlog _T = T.TypeVar('_T') @@ -108,27 +108,47 @@ class MesonConfigFile: out[s] = section return out -class HasEnvVarFallback: +def get_env_var_pair(for_machine: MachineChoice, + is_cross: bool, + var_name: str) -> T.Tuple[T.Optional[str], T.Optional[str]]: """ - A tiny class to indicate that this class contains data that can be - initialized from either a config file or environment file. The `fallback` - field says whether env vars should be used. Downstream logic (e.g. subclass - methods) can check it to decide what to do, since env vars are currently - lazily decoded. - - Frankly, this is a pretty silly class at the moment. The hope is the way - that we deal with environment variables will become more structured, and - this can be starting point. + Returns the exact env var and the value. """ - def __init__(self, fallback: bool = True): - self.fallback = fallback - -class Properties(HasEnvVarFallback): + candidates = PerMachine( + # The prefixed build version takes priority, but if we are native + # compiling we fall back on the unprefixed host version. This + # allows native builds to never need to worry about the 'BUILD_*' + # ones. + ([var_name + '_FOR_BUILD'] if is_cross else [var_name]), + # Always just the unprefixed host verions + ([] if is_cross else [var_name]), + )[for_machine] + for var in candidates: + value = os.environ.get(var) + if value is not None: + break + else: + formatted = ', '.join(['{!r}'.format(var) for var in candidates]) + mlog.debug('None of {} are defined in the environment, not changing global flags.'.format(formatted)) + return None + mlog.log('Using {!r} from environment with value: {!r}'.format(var, value)) + return var, value + +def get_env_var(for_machine: MachineChoice, + is_cross: bool, + var_name: str) -> T.Tuple[T.Optional[str], T.Optional[str]]: + ret = get_env_var_pair(for_machine, is_cross, var_name) + if ret is None: + return None + else: + var, value = ret + return value + +class Properties: def __init__( self, properties: T.Optional[T.Dict[str, T.Union[str, T.List[str]]]] = None, - fallback: bool = True): - super().__init__(fallback) + ): self.properties = properties or {} # type: T.Dict[str, T.Union[str, T.List[str]]] def has_stdlib(self, language: str) -> bool: @@ -290,12 +310,11 @@ class MachineInfo: def libdir_layout_is_win(self) -> bool: return self.is_windows() or self.is_cygwin() -class BinaryTable(HasEnvVarFallback): +class BinaryTable: def __init__( self, binaries: T.Optional[T.Dict[str, T.Union[str, T.List[str]]]] = None, - fallback: bool = True): - super().__init__(fallback) + ): 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)): @@ -355,13 +374,6 @@ class BinaryTable(HasEnvVarFallback): return ['ccache'] @classmethod - 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: T.Union[str, T.List[str]]) -> T.Tuple[T.List[str], T.List[str]]: compiler = mesonlib.stringlistify(entry) # Ensure ccache exists and remove it if it doesn't @@ -373,38 +385,42 @@ 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: str) -> T.Optional[T.List[str]]: + def lookup_entry(self, + for_machine: MachineChoice, + is_cross: bool, + 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 - 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 None: - deprecated = self.DEPRECATION_MAP.get(evar) - if deprecated: - command = os.environ.get(deprecated) - if command: - mlog.deprecation( - 'The', deprecated, 'environment variable is deprecated in favor of', - evar, once=True) - if command is not None: - command = split_args(command) + # 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 = self.evarMap.get(name) + if evar is not None: + raw_command = get_env_var(for_machine, is_cross, evar) + if raw_command is None: + deprecated = self.DEPRECATION_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): - return None + command = None return command class Directories: diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 323180b..61e4e5d 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -558,8 +558,8 @@ class Environment: if self.coredata.cross_files: config = MesonConfigFile.from_config_parser( coredata.load_configs(self.coredata.cross_files)) - properties.host = Properties(config.get('properties', {}), False) - binaries.host = BinaryTable(config.get('binaries', {}), False) + properties.host = Properties(config.get('properties', {})) + binaries.host = BinaryTable(config.get('binaries', {})) if 'host_machine' in config: machines.host = MachineInfo.from_literal(config['host_machine']) if 'target_machine' in config: @@ -573,12 +573,10 @@ class Environment: self.properties = properties.default_missing() self.paths = paths.default_missing() - exe_wrapper = self.binaries.host.lookup_entry('exe_wrapper') + exe_wrapper = self.lookup_binary_entry(MachineChoice.HOST, 'exe_wrapper') if exe_wrapper is not None: from .dependencies import ExternalProgram - self.exe_wrapper = ExternalProgram.from_bin_list( - self.binaries.host, - 'exe_wrapper') + self.exe_wrapper = ExternalProgram.from_bin_list(self, MachineChoice.HOST, 'exe_wrapper') else: self.exe_wrapper = None @@ -680,6 +678,12 @@ 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) + @staticmethod def get_gnu_compiler_defines(compiler): """ @@ -729,7 +733,7 @@ class Environment: The list of compilers is detected in the exact same way for C, C++, ObjC, ObjC++, Fortran, CS so consolidate it here. ''' - value = self.binaries[for_machine].lookup_entry(lang) + value = self.lookup_binary_entry(for_machine, lang) if value is not None: compilers, ccache = BinaryTable.parse_entry(value) # Return value has to be a list of compiler 'choices' @@ -772,7 +776,7 @@ class Environment: check_args += self.coredata.compiler_options[for_machine][comp_class.language + '_args'].value override = [] # type: T.List[str] - value = self.binaries[for_machine].lookup_entry(comp_class.language + '_ld') + value = self.lookup_binary_entry(for_machine, comp_class.language + '_ld') if value is not None: override = comp_class.use_linker_args(value[0]) check_args += override @@ -839,7 +843,7 @@ class Environment: check_args = comp_class.LINKER_PREFIX + ['--version'] + extra_args override = [] # type: T.List[str] - value = self.binaries[for_machine].lookup_entry(comp_class.language + '_ld') + value = self.lookup_binary_entry(for_machine, comp_class.language + '_ld') if value is not None: override = comp_class.use_linker_args(value[0]) check_args += override @@ -1347,7 +1351,7 @@ class Environment: self._handle_exceptions(popen_exceptions, compilers) def detect_java_compiler(self, for_machine): - exelist = self.binaries.host.lookup_entry('java') + exelist = self.lookup_binary_entry(for_machine, 'java') info = self.machines[for_machine] if exelist is None: # TODO support fallback @@ -1394,7 +1398,7 @@ class Environment: self._handle_exceptions(popen_exceptions, compilers) def detect_vala_compiler(self, for_machine): - exelist = self.binaries.host.lookup_entry('vala') + exelist = self.lookup_binary_entry(for_machine, 'vala') is_cross = not self.machines.matches_build_machine(for_machine) info = self.machines[for_machine] if exelist is None: @@ -1420,7 +1424,7 @@ class Environment: cc = self.detect_c_compiler(for_machine) is_link_exe = isinstance(cc.linker, VisualStudioLikeLinkerMixin) - override = self.binaries[for_machine].lookup_entry('rust_ld') + override = self.lookup_binary_entry(for_machine, 'rust_ld') for compiler in compilers: if isinstance(compiler, str): @@ -1596,7 +1600,7 @@ class Environment: self._handle_exceptions(popen_exceptions, compilers) def detect_swift_compiler(self, for_machine): - exelist = self.binaries.host.lookup_entry('swift') + exelist = self.lookup_binary_entry(for_machine, 'swift') is_cross = not self.machines.matches_build_machine(for_machine) info = self.machines[for_machine] if exelist is None: @@ -1656,16 +1660,13 @@ class Environment: return comp def detect_static_linker(self, compiler): - linker = self.binaries[compiler.for_machine].lookup_entry('ar') + linker = self.lookup_binary_entry(compiler.for_machine, 'ar') if linker is not None: linkers = [linker] else: - evar = 'AR' defaults = [[l] for l in self.default_static_linker] if isinstance(compiler, compilers.CudaCompiler): linkers = [self.cuda_static_linker] + defaults - elif evar in os.environ: - linkers = [split_args(os.environ[evar])] elif isinstance(compiler, compilers.VisualStudioLikeCompiler): linkers = [self.vs_static_linker, self.clang_cl_static_linker] elif isinstance(compiler, compilers.GnuCompiler): diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 970b709..09e213e 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -3136,13 +3136,12 @@ external dependencies (including libraries) must go to "dependencies".''') return success def program_from_file_for(self, for_machine, prognames, silent): - bins = self.environment.binaries[for_machine] for p in unholder(prognames): if isinstance(p, mesonlib.File): continue # Always points to a local (i.e. self generated) file. if not isinstance(p, str): raise InterpreterException('Executable name must be a string') - prog = ExternalProgram.from_bin_list(bins, p) + prog = ExternalProgram.from_bin_list(self.environment, for_machine, p) if prog.found(): return ExternalProgramHolder(prog) return None diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py index dc5b144..fa898d0 100644 --- a/mesonbuild/linkers.py +++ b/mesonbuild/linkers.py @@ -17,6 +17,7 @@ import os import typing as T from . import mesonlib +from .envconfig import get_env_var if T.TYPE_CHECKING: from .coredata import OptionDictType @@ -268,7 +269,20 @@ def evaluate_rpath(p: str, build_dir: str, from_dir: str) -> str: return os.path.relpath(os.path.join(build_dir, p), os.path.join(build_dir, from_dir)) -class DynamicLinker(metaclass=abc.ABCMeta): +class LinkerEnvVarsMixin(metaclass=abc.ABCMeta): + + """Mixin reading LDFLAGS from the environment.""" + + @staticmethod + def get_args_from_envvars(for_machine: mesonlib.MachineChoice, + is_cross: bool) -> T.List[str]: + raw_value = get_env_var(for_machine, is_cross, 'LDFLAGS') + if raw_value is not None: + return mesonlib.split_args(raw_value) + else: + return [] + +class DynamicLinker(LinkerEnvVarsMixin, metaclass=abc.ABCMeta): """Base class for dynamic linkers.""" @@ -327,12 +341,6 @@ class DynamicLinker(metaclass=abc.ABCMeta): # XXX: is use_ldflags a compiler or a linker attribute? - def get_args_from_envvars(self) -> T.List[str]: - flags = os.environ.get('LDFLAGS') - if not flags: - return [] - return mesonlib.split_args(flags) - def get_option_args(self, options: 'OptionDictType') -> T.List[str]: return [] diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index 3f971f7..a5c58a2 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -19,7 +19,7 @@ import typing as T from pathlib import Path from .. import mesonlib -from ..mesonlib import MesonException +from ..mesonlib import MachineChoice, MesonException from . import ExtensionModule from mesonbuild.modules import ModuleReturnValue from ..interpreterbase import ( @@ -506,7 +506,7 @@ class PythonModule(ExtensionModule): if len(args) > 1: raise InvalidArguments('find_installation takes zero or one positional argument.') - name_or_path = state.environment.binaries.host.lookup_entry('python') + name_or_path = state.environment.lookup_binary_entry(MachineChoice.HOST, 'python') if name_or_path is None and args: name_or_path = args[0] if not isinstance(name_or_path, str): diff --git a/mesonbuild/modules/python3.py b/mesonbuild/modules/python3.py index 46f15f0..97bd5ec 100644 --- a/mesonbuild/modules/python3.py +++ b/mesonbuild/modules/python3.py @@ -48,7 +48,7 @@ class Python3Module(ExtensionModule): @noKwargs def find_python(self, state, args, kwargs): - command = state.environment.binaries.host.lookup_entry('python3') + command = state.environment.lookup_binary_entry(mesonlib.MachineChoice.HOST, 'python3') if command is not None: py3 = dependencies.ExternalProgram.from_entry('python3', command) else: diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py index 8589adc..b3e4983 100644 --- a/mesonbuild/modules/windows.py +++ b/mesonbuild/modules/windows.py @@ -48,7 +48,7 @@ class WindowsModule(ExtensionModule): return self._rescomp # Will try cross / native file and then env var - rescomp = ExternalProgram.from_bin_list(state.environment.binaries[for_machine], 'windres') + rescomp = ExternalProgram.from_bin_list(state.environment, for_machine, 'windres') if not rescomp or not rescomp.found(): comp = self.detect_compiler(state.environment.coredata.compilers[for_machine]) diff --git a/run_unittests.py b/run_unittests.py index 962ab5d..d1c10f5 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -1454,6 +1454,7 @@ class BasePlatformTests(unittest.TestCase): # FIXME: Extract this from argv? self.backend = getattr(Backend, os.environ.get('MESON_UNIT_TEST_BACKEND', 'ninja')) self.meson_args = ['--backend=' + self.backend.name] + self.meson_native_file = None self.meson_cross_file = None self.meson_command = python_command + [get_meson_script()] self.setup_command = self.meson_command + self.meson_args @@ -1563,6 +1564,8 @@ class BasePlatformTests(unittest.TestCase): if default_args: args += ['--prefix', self.prefix, '--libdir', self.libdir] + if self.meson_native_file: + args += ['--native-file', self.meson_native_file] if self.meson_cross_file: args += ['--cross-file', self.meson_cross_file] self.privatedir = os.path.join(self.builddir, 'meson-private') @@ -6313,8 +6316,30 @@ class LinuxlikeTests(BasePlatformTests): def test_identity_cross(self): testdir = os.path.join(self.unit_test_dir, '61 identity cross') + + nativefile = tempfile.NamedTemporaryFile(mode='w') + nativefile.write('''[binaries] +c = ['{0}'] +'''.format(os.path.join(testdir, 'build_wrapper.py'))) + nativefile.flush() + self.meson_native_file = nativefile.name + + crossfile = tempfile.NamedTemporaryFile(mode='w') + crossfile.write('''[binaries] +c = ['{0}'] +'''.format(os.path.join(testdir, 'host_wrapper.py'))) + crossfile.flush() + self.meson_cross_file = crossfile.name + + # TODO should someday be explicit about build platform only here + self.init(testdir) + + def test_identity_cross_env(self): + testdir = os.path.join(self.unit_test_dir, '61 identity cross') + env = { + 'CC_FOR_BUILD': '"' + os.path.join(testdir, 'build_wrapper.py') + '"', + } crossfile = tempfile.NamedTemporaryFile(mode='w') - env = {'CC': '"' + os.path.join(testdir, 'build_wrapper.py') + '"'} crossfile.write('''[binaries] c = ['{0}'] '''.format(os.path.join(testdir, 'host_wrapper.py'))) |