Fix legacy env var support with cross

Fix #3969
pull/6798/merge
John Ericson 6 years ago committed by Jussi Pakkanen
parent f6b0425576
commit 3a4388e51d
  1. 22
      docs/markdown/howtox.md
  2. 12
      docs/markdown/snippets/env_vars_and_cross.md
  3. 2
      mesonbuild/backend/backends.py
  4. 2
      mesonbuild/cmake/executor.py
  5. 2
      mesonbuild/compilers/c.py
  6. 62
      mesonbuild/compilers/compilers.py
  7. 2
      mesonbuild/compilers/cpp.py
  8. 12
      mesonbuild/compilers/mixins/islinker.py
  9. 47
      mesonbuild/coredata.py
  10. 26
      mesonbuild/dependencies/base.py
  11. 114
      mesonbuild/envconfig.py
  12. 35
      mesonbuild/environment.py
  13. 3
      mesonbuild/interpreter.py
  14. 22
      mesonbuild/linkers.py
  15. 4
      mesonbuild/modules/python.py
  16. 2
      mesonbuild/modules/python3.py
  17. 2
      mesonbuild/modules/windows.py
  18. 27
      run_unittests.py

@ -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)

@ -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.

@ -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.')

@ -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)

@ -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,

@ -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:

@ -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:

@ -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.

@ -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

@ -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)

@ -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)):
@ -354,13 +373,6 @@ class BinaryTable(HasEnvVarFallback):
return []
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)
@ -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:

@ -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):

@ -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

@ -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 []

@ -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):

@ -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:

@ -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])

@ -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')))

Loading…
Cancel
Save