aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/Reference-manual.md5
-rw-r--r--docs/markdown/snippets/mixed_language_linker_tests.md21
-rw-r--r--mesonbuild/compilers/__init__.py2
-rw-r--r--mesonbuild/compilers/compilers.py28
-rw-r--r--mesonbuild/interpreter/compiler.py15
-rw-r--r--mesonbuild/mesonlib/universal.py4
-rw-r--r--test cases/unit/97 compiler.links file arg/meson.build11
-rw-r--r--test cases/unit/97 compiler.links file arg/test.c1
-rw-r--r--unittests/allplatformstests.py14
9 files changed, 92 insertions, 9 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index 31ed77e..b3dbf65 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -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
diff --git a/docs/markdown/snippets/mixed_language_linker_tests.md b/docs/markdown/snippets/mixed_language_linker_tests.md
new file mode 100644
index 0000000..8b94edb
--- /dev/null
+++ b/docs/markdown/snippets/mixed_language_linker_tests.md
@@ -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.
diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py
index 98b0b5f..807a009 100644
--- a/mesonbuild/compilers/__init__.py
+++ b/mesonbuild/compilers/__init__.py
@@ -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,
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index 6896b76..dfa551d 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -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)
diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py
index 54f4bee..b76e7f8 100644
--- a/mesonbuild/interpreter/compiler.py
+++ b/mesonbuild/interpreter/compiler.py
@@ -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 ''
diff --git a/mesonbuild/mesonlib/universal.py b/mesonbuild/mesonlib/universal.py
index 1680541..f81c01a 100644
--- a/mesonbuild/mesonlib/universal.py
+++ b/mesonbuild/mesonlib/universal.py
@@ -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)
diff --git a/test cases/unit/97 compiler.links file arg/meson.build b/test cases/unit/97 compiler.links file arg/meson.build
new file mode 100644
index 0000000..c409dcb
--- /dev/null
+++ b/test cases/unit/97 compiler.links file arg/meson.build
@@ -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')))
diff --git a/test cases/unit/97 compiler.links file arg/test.c b/test cases/unit/97 compiler.links file arg/test.c
new file mode 100644
index 0000000..78f2de1
--- /dev/null
+++ b/test cases/unit/97 compiler.links file arg/test.c
@@ -0,0 +1 @@
+int main(void) { return 0; }
diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py
index 7edb96e..e6ddab3 100644
--- a/unittests/allplatformstests.py
+++ b/unittests/allplatformstests.py
@@ -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'])