From 06bfc2dab61c5bf79265a8db777b02732ee86ecf Mon Sep 17 00:00:00 2001 From: "Michael Hirsch, Ph.D" <10931741+scivision@users.noreply.github.com> Date: Thu, 2 May 2019 16:26:51 -0400 Subject: [PATCH] per-target manual specification of link_language --- docs/markdown/Reference-manual.md | 4 +++- docs/markdown/snippets/link_language.md | 10 ++++++++ mesonbuild/build.py | 23 ++++++++++++++----- mesonbuild/compilers/fortran.py | 4 +++- run_project_tests.py | 4 ++++ test cases/fortran/14 fortran links c/clib.c | 7 ++++++ .../fortran/14 fortran links c/f_call_c.f90 | 10 ++++++++ .../fortran/14 fortran links c/meson.build | 13 +++++++++++ 8 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 docs/markdown/snippets/link_language.md create mode 100644 test cases/fortran/14 fortran links c/clib.c create mode 100644 test cases/fortran/14 fortran links c/f_call_c.f90 create mode 100644 test cases/fortran/14 fortran links c/meson.build diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 056612dc9..d86d825d4 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -518,6 +518,8 @@ be passed to [shared and static libraries](#library). depends on such as a symbol visibility map. The purpose is to automatically trigger a re-link (but not a re-compile) of the target when this file changes. +- `link_language` since 0.51.0 makes the linker for this target + be for the specified language. This is helpful for multi-language targets. - `link_whole` links all contents of the given static libraries whether they are used by not, equivalent to the `-Wl,--whole-archive` argument flag of GCC, available since 0.40.0. @@ -568,7 +570,7 @@ be passed to [shared and static libraries](#library). the keyword argument for the default behaviour. - `override_options` takes an array of strings in the same format as `project`'s `default_options` overriding the values of these options - for this target only, since 0.40.0 + for this target only, since 0.40.0. - `gnu_symbol_visibility` specifies how symbols should be exported, see e.g [the GCC Wiki](https://gcc.gnu.org/wiki/Visibility) for more information. This value can either be an empty string or one of diff --git a/docs/markdown/snippets/link_language.md b/docs/markdown/snippets/link_language.md new file mode 100644 index 000000000..28ebe8b42 --- /dev/null +++ b/docs/markdown/snippets/link_language.md @@ -0,0 +1,10 @@ +## New target keyword argument: `link_language` +There may be situations for which the user wishes to manually specify the linking language. +For example, a C++ target may link C, Fortran, etc. and perhaps the automatic detection in Meson does not pick the desired compiler. +The user can manually choose the linker by language per-target like this example of a target where one wishes to link with the Fortran compiler: +```meson +executable(..., link_language : 'fortran') +``` + +A specific case this option fixes is where for example the main program is Fortran that calls C and/or C++ code. +The automatic language detection of Meson prioritizes C/C++, and so an compile-time error results like `undefined reference to main`, because the linker is C or C++ instead of Fortran, which is fixed by this per-target override. diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 1fad9e01b..603e0d0f9 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import List import copy, os, re from collections import OrderedDict import itertools, pathlib @@ -88,7 +89,7 @@ known_build_target_kwargs = ( rust_kwargs | cs_kwargs) -known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic', 'pie'} +known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic', 'link_language', 'pie'} known_shlib_kwargs = known_build_target_kwargs | {'version', 'soversion', 'vs_module_defs', 'darwin_versions'} known_shmod_kwargs = known_build_target_kwargs known_stlib_kwargs = known_build_target_kwargs | {'pic'} @@ -425,7 +426,7 @@ a hard error in the future.''' % name) self.option_overrides = self.parse_overrides(kwargs) - def parse_overrides(self, kwargs): + def parse_overrides(self, kwargs) -> dict: result = {} overrides = stringlistify(kwargs.get('override_options', [])) for o in overrides: @@ -437,7 +438,7 @@ a hard error in the future.''' % name) result[k] = v return result - def is_linkable_target(self): + def is_linkable_target(self) -> bool: return False class BuildTarget(Target): @@ -454,6 +455,7 @@ class BuildTarget(Target): self.objects = [] self.external_deps = [] self.include_dirs = [] + self.link_language = kwargs.get('link_language') self.link_targets = [] self.link_whole_targets = [] self.link_depends = [] @@ -571,6 +573,9 @@ class BuildTarget(Target): else: compilers = self.environment.coredata.compilers + # did user override clink_langs for this target? + link_langs = [self.link_language] if self.link_language else clink_langs + # If this library is linked against another library we need to consider # the languages of those libraries as well. if self.link_targets or self.link_whole_targets: @@ -579,7 +584,7 @@ class BuildTarget(Target): if isinstance(t, CustomTarget) or isinstance(t, CustomTargetIndex): continue # We can't know anything about these. for name, compiler in t.compilers.items(): - if name in clink_langs: + if name in link_langs: extra.add((name, compiler)) for name, compiler in sorted(extra, key=lambda p: sort_clink(p[0])): self.compilers[name] = compiler @@ -588,7 +593,7 @@ class BuildTarget(Target): # No source files or parent targets, target consists of only object # files of unknown origin. Just add the first clink compiler # that we have and hope that it can link these objects - for lang in clink_langs: + for lang in link_langs: if lang in compilers: self.compilers[lang] = compilers[lang] break @@ -1149,7 +1154,7 @@ You probably should put it in link_with instead.''') def get_aliases(self): return {} - def get_langs_used_by_deps(self): + def get_langs_used_by_deps(self) -> List[str]: ''' Sometimes you want to link to a C++ library that exports C API, which means the linker must link in the C++ stdlib, and we must use a C++ @@ -1159,6 +1164,11 @@ You probably should put it in link_with instead.''') See: https://github.com/mesonbuild/meson/issues/1653 ''' langs = [] + + # User specified link_language of target (for multi-language targets) + if self.link_language: + return [self.link_language] + # Check if any of the external libraries were written in this language for dep in self.external_deps: if dep.language is None: @@ -1173,6 +1183,7 @@ You probably should put it in link_with instead.''') for language in link_target.compilers: if language not in langs: langs.append(language) + return langs def get_clink_dynamic_linker_and_stdlibs(self): diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index dd54fd03f..86ebe0541 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -333,7 +333,6 @@ class GnuFortranCompiler(GnuCompiler, FortranCompiler): def language_stdlib_only_link_flags(self): return ['-lgfortran', '-lm'] - class ElbrusFortranCompiler(GnuFortranCompiler, ElbrusCompiler): def __init__(self, exelist, version, compiler_type, is_cross, exe_wrapper=None, defines=None, **kwargs): GnuFortranCompiler.__init__(self, exelist, version, compiler_type, is_cross, exe_wrapper, defines, **kwargs) @@ -427,6 +426,9 @@ class PGIFortranCompiler(PGICompiler, FortranCompiler): FortranCompiler.__init__(self, exelist, version, is_cross, exe_wrapper, **kwags) PGICompiler.__init__(self, compiler_type) + def language_stdlib_only_link_flags(self) -> List[str]: + return ['-lpgf90rtl', '-lpgf90', '-lpgf90_rpm1', '-lpgf902', + '-lpgf90rtl', '-lpgftnrtl', '-lrt'] class FlangFortranCompiler(ClangCompiler, FortranCompiler): def __init__(self, exelist, version, is_cross, exe_wrapper=None, **kwags): diff --git a/run_project_tests.py b/run_project_tests.py index c1d42fce1..29716aa22 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -505,6 +505,10 @@ def skippable(suite, test): if test.endswith('netcdf'): return True + # MSVC doesn't link with GFortran + if test.endswith('14 fortran links c'): + return True + # No frameworks test should be skipped on linux CI, as we expect all # prerequisites to be installed if mesonlib.is_linux(): diff --git a/test cases/fortran/14 fortran links c/clib.c b/test cases/fortran/14 fortran links c/clib.c new file mode 100644 index 000000000..81b2e0c17 --- /dev/null +++ b/test cases/fortran/14 fortran links c/clib.c @@ -0,0 +1,7 @@ +#include + +void hello(void){ + + printf("hello from C\n"); + +} diff --git a/test cases/fortran/14 fortran links c/f_call_c.f90 b/test cases/fortran/14 fortran links c/f_call_c.f90 new file mode 100644 index 000000000..af1e79c82 --- /dev/null +++ b/test cases/fortran/14 fortran links c/f_call_c.f90 @@ -0,0 +1,10 @@ +implicit none + +interface +subroutine hello() bind (c) +end subroutine hello +end interface + +call hello() + +end program diff --git a/test cases/fortran/14 fortran links c/meson.build b/test cases/fortran/14 fortran links c/meson.build new file mode 100644 index 000000000..163aec6eb --- /dev/null +++ b/test cases/fortran/14 fortran links c/meson.build @@ -0,0 +1,13 @@ +project('Fortran calling C', 'fortran', 'c') + +ccid = meson.get_compiler('c').get_id() +if ccid == 'msvc' or ccid == 'clang-cl' + error('MESON_SKIP_TEST: MSVC and GCC do not interoperate like this.') +endif + +c_lib = library('clib', 'clib.c') + +f_call_c = executable('f_call_c', 'f_call_c.f90', + link_with: c_lib, + link_language: 'fortran') +test('Fortran calling C', f_call_c)