aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Ericson <git@JohnEricson.me>2019-08-02 14:29:40 -0400
committerJussi Pakkanen <jpakkane@gmail.com>2020-03-23 17:51:36 +0200
commit3a4388e51dee5e7e58f1e5ad4e60fb73b4aacf08 (patch)
tree8f0637e630777cc4a5707c16a30c4d980b888371
parentf6b0425576640e1613f64503951c7c604b868947 (diff)
downloadmeson-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.md22
-rw-r--r--docs/markdown/snippets/env_vars_and_cross.md12
-rw-r--r--mesonbuild/backend/backends.py2
-rw-r--r--mesonbuild/cmake/executor.py2
-rw-r--r--mesonbuild/compilers/c.py2
-rw-r--r--mesonbuild/compilers/compilers.py62
-rw-r--r--mesonbuild/compilers/cpp.py2
-rw-r--r--mesonbuild/compilers/mixins/islinker.py12
-rw-r--r--mesonbuild/coredata.py47
-rw-r--r--mesonbuild/dependencies/base.py26
-rw-r--r--mesonbuild/envconfig.py114
-rw-r--r--mesonbuild/environment.py35
-rw-r--r--mesonbuild/interpreter.py3
-rw-r--r--mesonbuild/linkers.py22
-rw-r--r--mesonbuild/modules/python.py4
-rw-r--r--mesonbuild/modules/python3.py2
-rw-r--r--mesonbuild/modules/windows.py2
-rwxr-xr-xrun_unittests.py27
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')))