Split environment variable and command line cflags

They are supposed to have different behavior. The environment variables
apply to both the compiler and linker when the compiler acts as a
linker, but the command line ones do not.

Fixes #8345
pull/8609/head
Dylan Baker 4 years ago committed by Jussi Pakkanen
parent 692f673312
commit 2cd0723c42
  1. 11
      mesonbuild/compilers/compilers.py
  2. 11
      mesonbuild/coredata.py
  3. 23
      mesonbuild/environment.py
  4. 22
      run_unittests.py

@ -1234,18 +1234,21 @@ def get_global_options(lang: str,
description = f'Extra arguments passed to the {lang}'
argkey = OptionKey('args', lang=lang, machine=for_machine)
largkey = argkey.evolve('link_args')
envkey = argkey.evolve('env_args')
cargs = coredata.UserArrayOption(
description + ' compiler',
env.options.get(argkey, []), split_args=True, user_input=True, allow_dups=True)
# the compiler args always gets the environemtn variable arguments
cargs.extend_value(env.options.get(envkey, []))
largs = coredata.UserArrayOption(
description + ' linker',
env.options.get(largkey, []), split_args=True, user_input=True, allow_dups=True)
# This needs to be done here, so that if we have string values in the env
# options that we can safely combine them *after* they've been split
# The linker gets the compiler environment variable only if the comiler
# acts as the linker
if comp.INVOKES_LINKER:
largs.set_value(largs.value + cargs.value)
largs.extend_value(env.options.get(envkey, []))
opts: 'KeyedOptionDictType' = {argkey: cargs, largkey: largs}

@ -243,6 +243,11 @@ class UserArrayOption(UserOption[T.List[str]]):
', '.join(bad), ', '.join(self.choices)))
return newvalue
def extend_value(self, value: T.Union[str, T.List[str]]) -> None:
"""Extend the value with an additional value."""
new = self.validate_value(value)
self.set_value(self.value + new)
class UserFeatureOption(UserComboOption):
static_choices = ['enabled', 'disabled', 'auto']
@ -776,8 +781,10 @@ class CoreData:
for_machine: MachineChoice, env: 'Environment') -> None:
"""Add global language arguments that are needed before compiler/linker detection."""
from .compilers import compilers
options = compilers.get_global_options(lang, comp, for_machine, env)
self.add_compiler_options(options, lang, for_machine, env)
# These options are all new at this point, because the compiler is
# responsible for adding its own options, thus calling
# `self.options.update()`` is perfectly safe.
self.options.update(compilers.get_global_options(lang, comp, for_machine, env))
def process_new_compiler(self, lang: str, comp: 'Compiler', env: 'Environment') -> None:
from . import compilers

@ -798,7 +798,11 @@ class Environment:
env_opts: T.DefaultDict[OptionKey, T.List[str]] = collections.defaultdict(list)
for (evar, keyname), for_machine in itertools.product(opts, MachineChoice):
if self.is_cross_build():
for_machine = MachineChoice.BUILD
else:
for_machine = MachineChoice.HOST
for evar, keyname in opts:
p_env = _get_env_var(for_machine, self.is_cross_build(), evar)
if p_env is not None:
# these may contain duplicates, which must be removed, else
@ -834,6 +838,23 @@ class Environment:
env_opts[key].extend(p_list)
else:
key = OptionKey.from_string(keyname).evolve(machine=for_machine)
if evar in compilers.compilers.CFLAGS_MAPPING.values():
# If this is an environment variable, we have to
# store it separately until the compiler is
# instantiated, as we don't know whether the
# compiler will want to use these arguments at link
# time and compile time (instead of just at compile
# time) until we're instantiating that `Compiler`
# object. This is required so that passing
# `-Dc_args=` on the command line and `$CFLAGS`
# have subtely differen behavior. `$CFLAGS` will be
# added to the linker command line if the compiler
# acts as a linker driver, `-Dc_args` will not.
#
# We stil use the original key as the base here, as
# we want to inhert the machine and the compiler
# language
key = key.evolve('env_args')
env_opts[key].extend(p_list)
# Only store options that are not already in self.options,

@ -5634,6 +5634,28 @@ class AllPlatformTests(BasePlatformTests):
matches += 1
self.assertEqual(matches, 1)
def test_env_flags_to_linker(self) -> None:
# Compilers that act as drivers should add their compiler flags to the
# linker, those that do not shouldn't
with mock.patch.dict(os.environ, {'CFLAGS': '-DCFLAG', 'LDFLAGS': '-flto'}):
env = get_fake_env()
# Get the compiler so we know which compiler class to mock.
cc = env.detect_compiler_for('c', MachineChoice.HOST)
cc_type = type(cc)
# Test a compiler that acts as a linker
with mock.patch.object(cc_type, 'INVOKES_LINKER', True):
cc = env.detect_compiler_for('c', MachineChoice.HOST)
link_args = env.coredata.get_external_link_args(cc.for_machine, cc.language)
self.assertEqual(sorted(link_args), sorted(['-DCFLAG', '-flto']))
# And one that doesn't
with mock.patch.object(cc_type, 'INVOKES_LINKER', False):
cc = env.detect_compiler_for('c', MachineChoice.HOST)
link_args = env.coredata.get_external_link_args(cc.for_machine, cc.language)
self.assertEqual(sorted(link_args), sorted(['-flto']))
class FailureTests(BasePlatformTests):
'''
Tests that test failure conditions. Build files here should be dynamically

Loading…
Cancel
Save