Merge pull request #9014 from bonzini/mixed-language-link

Use appropriate compiler for the source file for "links" tests with file argument
pull/9307/head
Jussi Pakkanen 3 years ago committed by GitHub
commit fa47d8dab0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      docs/markdown/Reference-manual.md
  2. 21
      docs/markdown/snippets/mixed_language_linker_tests.md
  3. 2
      mesonbuild/compilers/__init__.py
  4. 28
      mesonbuild/compilers/compilers.py
  5. 15
      mesonbuild/interpreter/compiler.py
  6. 4
      mesonbuild/mesonlib/universal.py
  7. 11
      test cases/unit/97 compiler.links file arg/meson.build
  8. 1
      test cases/unit/97 compiler.links file arg/test.c
  9. 14
      unittests/allplatformstests.py

@ -2514,7 +2514,10 @@ the following methods:
positional argument compiles and links, you can specify external
dependencies to use with `dependencies` keyword argument, `code` can
be either a string containing source code or a `file` object
pointing to the source code.
pointing to the source code. *Since 0.60.0*, if the `file` object's
suffix does not match the compiler object's language, the compiler
corresponding to the suffix is used to compile the source, while the
target of the `links` method is used to link the resulting object file.
- `run(code)`: attempts to compile and execute the given code fragment,
returns a run result object, you can specify external dependencies

@ -0,0 +1,21 @@
== Link tests can use sources for a different compiler ==
Usually, the `links` method of the compiler object uses a single program
invocation to do both compilation and linking. Starting with this version,
whenever the argument to `links` is a file, Meson will check if the file
suffix matches the compiler object's language. If they do not match,
as in the following case:
```
cxx = meson.get_compiler('cpp')
cxx.links(files('test.c'))
```
then Meson will separate compilation and linking. In the above example
`test.c` will be compiled with a C compiler and the resulting object file
will be linked with a C++ compiler. This makes it possible to detect
misconfigurations of the compilation environment, for example when the
C++ runtime is not compatible with the one expected by the C compiler.
For this reason, passing file arguments with an unrecognized suffix to
`links` will cause a warning.

@ -35,6 +35,7 @@ __all__ = [
'lang_suffixes',
'LANGUAGES_USING_LDFLAGS',
'sort_clink',
'SUFFIX_TO_LANG',
'compiler_from_language',
'detect_compiler_for',
@ -150,6 +151,7 @@ from .compilers import (
lang_suffixes,
LANGUAGES_USING_LDFLAGS,
sort_clink,
SUFFIX_TO_LANG,
)
from .detect import (
compiler_from_language,

@ -84,6 +84,8 @@ for _l in clink_langs + ('vala',):
clink_suffixes += lang_suffixes[_l]
clink_suffixes += ('h', 'll', 's')
all_suffixes = set(itertools.chain(*lang_suffixes.values(), clink_suffixes)) # type: T.Set[str]
SUFFIX_TO_LANG = dict(itertools.chain(*(
[(suffix, lang) for suffix in v] for lang, v in lang_suffixes.items()))) # type: T.Dict[str, str]
# Languages that should use LDFLAGS arguments when linking.
LANGUAGES_USING_LDFLAGS = {'objcpp', 'cpp', 'objc', 'c', 'fortran', 'd', 'cuda'} # type: T.Set[str]
@ -276,7 +278,7 @@ base_options: 'KeyedOptionDictType' = {
OptionKey('b_pch'): coredata.UserBooleanOption('Use precompiled headers', True),
OptionKey('b_lto'): coredata.UserBooleanOption('Use link time optimization', False),
OptionKey('b_lto'): coredata.UserBooleanOption('Use link time optimization', False),
OptionKey('b_lto_threads'): coredata.UserIntegerOption('Use multiple threads for Link Time Optimization', (None, None,0)),
OptionKey('b_lto_threads'): coredata.UserIntegerOption('Use multiple threads for Link Time Optimization', (None, None, 0)),
OptionKey('b_lto_mode'): coredata.UserComboOption('Select between different LTO modes.',
['default', 'thin'],
'default'),
@ -680,8 +682,8 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
raise EnvironmentException('Language %s does not support sizeof checks.' % self.get_display_language())
def alignment(self, typename: str, prefix: str, env: 'Environment', *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> int:
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> int:
raise EnvironmentException('Language %s does not support alignment checks.' % self.get_display_language())
def has_function(self, funcname: str, prefix: str, env: 'Environment', *,
@ -767,7 +769,7 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
no_ccache = False
if isinstance(code, str):
srcname = os.path.join(tmpdirname,
'testfile.' + self.default_suffix)
'testfile.' + self.default_suffix)
with open(srcname, 'w', encoding='utf-8') as ofile:
ofile.write(code)
# ccache would result in a cache miss
@ -775,8 +777,11 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
contents = code
else:
srcname = code.fname
with open(code.fname, encoding='utf-8') as f:
contents = f.read()
if not is_object(code.fname):
with open(code.fname, encoding='utf-8') as f:
contents = f.read()
else:
contents = '<binary>'
# Construct the compiler command-line
commands = self.compiler_args()
@ -791,7 +796,8 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
# extra_args must be last because it could contain '/link' to
# pass args to VisualStudio's linker. In that case everything
# in the command line after '/link' is given to the linker.
commands += extra_args
if extra_args:
commands += extra_args
# Generate full command-line with the exelist
command_list = self.get_exelist() + commands.to_native()
mlog.debug('Running compile:')
@ -1232,12 +1238,18 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
with self._build_wrapper(code, env, extra_args, dependencies, mode, disable_cache=disable_cache) as p:
return p.returncode == 0, p.cached
def links(self, code: 'mesonlib.FileOrString', env: 'Environment', *,
compiler: T.Optional['Compiler'] = None,
extra_args: T.Union[None, T.List[str], CompilerArgs, T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None,
mode: str = 'compile',
disable_cache: bool = False) -> T.Tuple[bool, bool]:
if compiler:
with compiler._build_wrapper(code, env, dependencies=dependencies, want_output=True) as r:
objfile = mesonlib.File.from_absolute_file(r.output_name)
return self.compiles(objfile, env, extra_args=extra_args,
dependencies=dependencies, mode='link', disable_cache=True)
return self.compiles(code, env, extra_args=extra_args,
dependencies=dependencies, mode='link', disable_cache=disable_cache)

@ -11,6 +11,7 @@ from .. import coredata
from .. import dependencies
from .. import mesonlib
from .. import mlog
from ..compilers import SUFFIX_TO_LANG
from ..compilers.compilers import CompileCheckMode
from ..interpreterbase import (ObjectHolder, noPosargs, noKwargs,
FeatureNew, disablerIfNotFound,
@ -454,13 +455,27 @@ class CompilerHolder(ObjectHolder['Compiler']):
@typed_kwargs('compiler.links', *_COMPILES_KWS)
def links_method(self, args: T.Tuple['mesonlib.FileOrString'], kwargs: 'CompileKW') -> bool:
code = args[0]
compiler = None
if isinstance(code, mesonlib.File):
code = mesonlib.File.from_absolute_file(
code.rel_to_builddir(self.environment.source_dir))
suffix = code.suffix
if suffix not in self.compiler.file_suffixes:
for_machine = self.compiler.for_machine
clist = self.interpreter.coredata.compilers[for_machine]
if suffix not in SUFFIX_TO_LANG:
# just pass it to the compiler driver
mlog.warning(f'Unknown suffix for test file {code}')
elif SUFFIX_TO_LANG[suffix] not in clist:
mlog.warning(f'Passed {SUFFIX_TO_LANG[suffix]} source to links method, not specified for {for_machine.get_lower_case_name()} machine.')
else:
compiler = clist[SUFFIX_TO_LANG[suffix]]
testname = kwargs['name']
extra_args = functools.partial(self._determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
deps, msg = self._determine_dependencies(kwargs['dependencies'])
result, cached = self.compiler.links(code, self.environment,
compiler=compiler,
extra_args=extra_args,
dependencies=deps)
cached_msg = mlog.blue('(cached)') if cached else ''

@ -421,6 +421,10 @@ class File(HoldableObject):
absdir = builddir
return os.path.join(absdir, self.relative_name())
@property
def suffix(self) -> str:
return os.path.splitext(self.fname)[1][1:].lower()
def endswith(self, ending: str) -> bool:
return self.fname.endswith(ending)

@ -0,0 +1,11 @@
project('test', ['c', 'cpp'])
cc = meson.get_compiler('c')
cxx = meson.get_compiler('cpp')
# used by run_unittests.py to grab the path to the C and C++ compilers
assert(cc.compiles(files('test.c')))
assert(cxx.compiles(files('test.c')))
assert(cc.links(files('test.c')))
assert(cxx.links(files('test.c')))

@ -1924,6 +1924,20 @@ class AllPlatformTests(BasePlatformTests):
'recommended as it is not supported on some platforms')
self.assertIn(msg, out)
def test_mixed_language_linker_check(self):
testdir = os.path.join(self.unit_test_dir, '97 compiler.links file arg')
self.init(testdir)
cmds = self.get_meson_log_compiler_checks()
self.assertEqual(len(cmds), 5)
# Path to the compilers, gleaned from cc.compiles tests
cc = cmds[0][0]
cxx = cmds[1][0]
# cc.links
self.assertEqual(cmds[2][0], cc)
# cxx.links with C source
self.assertEqual(cmds[3][0], cc)
self.assertEqual(cmds[4][0], cxx)
def test_ndebug_if_release_disabled(self):
testdir = os.path.join(self.unit_test_dir, '28 ndebug if-release')
self.init(testdir, extra_args=['--buildtype=release', '-Db_ndebug=if-release'])

Loading…
Cancel
Save