Allow setting <lang>_args before the compiler is detected

This is required to be able to pass compiler and linker arguments to the
methods that try to guess what linker to use.
pull/6065/head
Dylan Baker 5 years ago
parent 7460e4ccda
commit 87766b3727
  1. 179
      mesonbuild/compilers/compilers.py
  2. 21
      mesonbuild/coredata.py
  3. 1
      mesonbuild/environment.py

@ -795,106 +795,11 @@ class Compiler:
"""
return []
def use_preproc_flags(self) -> bool:
"""
Whether the compiler (or processes it spawns) cares about CPPFLAGS
"""
return self.get_language() in {'c', 'cpp', 'objc', 'objcpp'}
def use_ldflags(self) -> bool:
"""
Whether the compiler (or processes it spawns) cares about LDFLAGS
"""
return self.get_language() in languages_using_ldflags
def get_linker_args_from_envvars(self) -> typing.List[str]:
return self.linker.get_args_from_envvars()
def get_args_from_envvars(self) -> typing.Tuple[typing.List[str], typing.List[str]]:
"""
Returns a tuple of (compile_flags, link_flags) for the specified language
from the inherited environment
"""
def log_var(var, val: typing.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))
lang = self.get_language()
compiler_is_linker = self.linker is not None and self.linker.invoked_by_compiler()
if lang not in cflags_mapping:
return [], []
compile_flags = [] # type: typing.List[str]
link_flags = [] # type: typing.List[str]
env_compile_flags = os.environ.get(cflags_mapping[lang])
log_var(cflags_mapping[lang], env_compile_flags)
if env_compile_flags is not None:
compile_flags += split_args(env_compile_flags)
# Link flags (same for all languages)
if self.use_ldflags():
env_link_flags = self.get_linker_args_from_envvars()
else:
env_link_flags = []
log_var('LDFLAGS', env_link_flags)
link_flags += env_link_flags
if compiler_is_linker:
# 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
# too. This is also what Autotools does. However, we don't want to do
# this when the linker is stand-alone such as with MSVC C/C++, etc.
link_flags = compile_flags + link_flags
# Pre-processor flags for certain languages
if self.use_preproc_flags():
env_preproc_flags = os.environ.get('CPPFLAGS')
log_var('CPPFLAGS', env_preproc_flags)
if env_preproc_flags is not None:
compile_flags += split_args(env_preproc_flags)
return compile_flags, link_flags
def get_options(self):
opts = {} # build afresh every time
description = 'Extra arguments passed to the {}'.format(self.get_display_language())
opts.update({
self.language + '_args': coredata.UserArrayOption(
description + ' compiler',
[], split_args=True, user_input=True, allow_dups=True),
self.language + '_link_args': coredata.UserArrayOption(
description + ' linker',
[], split_args=True, user_input=True, allow_dups=True),
})
return opts
def get_and_default_options(self, properties: Properties):
"""
Take default values from env variables and/or config files.
"""
opts = self.get_options()
if properties.fallback:
# Get from env vars.
compile_args, link_args = self.get_args_from_envvars()
else:
compile_args = []
link_args = []
for k, o in opts.items():
if k in properties:
# Get from configuration files.
o.set_value(properties[k])
elif k == self.language + '_args':
o.set_value(compile_args)
elif k == self.language + '_link_args':
o.set_value(link_args)
return opts
def get_options(self) -> typing.Dict[str, coredata.UserOption]:
return {}
def get_option_compile_args(self, options):
return []
@ -1220,3 +1125,83 @@ def get_largefile_args(compiler):
# transitionary features and must be enabled by programs that use
# those features explicitly.
return []
def get_args_from_envvars(lang: str, use_linker_args: bool) -> typing.Tuple[typing.List[str], typing.List[str]]:
"""
Returns a tuple of (compile_flags, link_flags) for the specified language
from the inherited environment
"""
def log_var(var, val: typing.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: typing.List[str]
link_flags = [] # type: typing.List[str]
env_compile_flags = os.environ.get(cflags_mapping[lang])
log_var(cflags_mapping[lang], env_compile_flags)
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
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
# too. This is also what Autotools does. However, we don't want to do
# this when the linker is stand-alone such as with MSVC C/C++, etc.
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 env_preproc_flags is not None:
compile_flags += split_args(env_preproc_flags)
return compile_flags, link_flags
def get_global_options(lang: str, properties: Properties) -> typing.Dict[str, coredata.UserOption]:
"""Retreive options that apply to all compilers for a given language."""
description = 'Extra arguments passed to the {}'.format(lang)
opts = {
lang + '_args': coredata.UserArrayOption(
description + ' compiler',
[], split_args=True, user_input=True, allow_dups=True),
lang + '_link_args': coredata.UserArrayOption(
description + ' linker',
[], 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, True)
else:
compile_args = []
link_args = []
for k, o in opts.items():
if k in properties:
# Get from configuration files.
o.set_value(properties[k])
elif k == lang + '_args':
o.set_value(compile_args)
elif k == lang + '_link_args':
o.set_value(link_args)
return opts

@ -35,6 +35,8 @@ import shlex
if TYPE_CHECKING:
from . import dependencies
from .compilers import Compiler
from .environment import Environment
OptionDictType = Dict[str, 'UserOption[Any]']
@ -742,13 +744,28 @@ class CoreData:
self.set_options(options, subproject=subproject)
def process_new_compiler(self, lang: str, comp, env):
def add_lang_args(self, lang: str, for_machine: MachineChoice, env: 'Environment') -> None:
"""Add global language arguments that are needed before compiler/linker detection."""
from .compilers import compilers
optprefix = lang + '_'
for k, o in compilers.get_global_options(lang, 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
opt_prefix = for_machine.get_prefix()
if opt_prefix + k in env.cmd_line_options:
o.set_value(env.cmd_line_options[opt_prefix + k])
self.compiler_options[for_machine].setdefault(k, o)
def process_new_compiler(self, lang: str, comp: Type['Compiler'], env: 'Environment') -> None:
from . import compilers
self.compilers[comp.for_machine][lang] = comp
enabled_opts = []
optprefix = lang + '_'
for k, o in comp.get_and_default_options(env.properties[comp.for_machine]).items():
for k, o in comp.get_options().items():
if not k.startswith(optprefix):
raise MesonException('Internal error, %s has incorrect prefix.' % k)
# prefixed compiler options affect just this machine

@ -1538,6 +1538,7 @@ class Environment:
return comp
def detect_compiler_for(self, lang: str, for_machine: MachineChoice):
self.coredata.add_lang_args(lang, for_machine, self)
comp = self.compiler_from_language(lang, for_machine)
if comp is not None:
assert comp.for_machine == for_machine

Loading…
Cancel
Save