From af9d1f8f15a57a99af376bd6b30730ff9b9812b1 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Thu, 20 Apr 2017 14:40:19 -0700 Subject: [PATCH 1/6] Add a testcase for linking C and C++ static archives into a shared library This exercises a regression where the C rather than C++ linker is chosen, resulting in symbol errors. Test for #1653 --- test cases/common/146 C and CPP link/foo.c | 19 +++++++++++++ test cases/common/146 C and CPP link/foo.cpp | 24 ++++++++++++++++ test cases/common/146 C and CPP link/foo.h | 16 +++++++++++ test cases/common/146 C and CPP link/foo.hpp | 24 ++++++++++++++++ test cases/common/146 C and CPP link/foobar.c | 23 +++++++++++++++ test cases/common/146 C and CPP link/foobar.h | 16 +++++++++++ .../common/146 C and CPP link/meson.build | 28 +++++++++++++++++++ 7 files changed, 150 insertions(+) create mode 100644 test cases/common/146 C and CPP link/foo.c create mode 100644 test cases/common/146 C and CPP link/foo.cpp create mode 100644 test cases/common/146 C and CPP link/foo.h create mode 100644 test cases/common/146 C and CPP link/foo.hpp create mode 100644 test cases/common/146 C and CPP link/foobar.c create mode 100644 test cases/common/146 C and CPP link/foobar.h create mode 100644 test cases/common/146 C and CPP link/meson.build diff --git a/test cases/common/146 C and CPP link/foo.c b/test cases/common/146 C and CPP link/foo.c new file mode 100644 index 000000000..77c7e39ff --- /dev/null +++ b/test cases/common/146 C and CPP link/foo.c @@ -0,0 +1,19 @@ +/* Copyright © 2017 Dylan Baker + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "foo.h" + +int forty_two(void) { + return 42; +} diff --git a/test cases/common/146 C and CPP link/foo.cpp b/test cases/common/146 C and CPP link/foo.cpp new file mode 100644 index 000000000..639af8e32 --- /dev/null +++ b/test cases/common/146 C and CPP link/foo.cpp @@ -0,0 +1,24 @@ +/* Copyright © 2017 Dylan Baker + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +namespace { + std::vector numbers = boost::assign::list_of(61); +} + +extern "C" int six_one(void) { + return numbers[1]; +} diff --git a/test cases/common/146 C and CPP link/foo.h b/test cases/common/146 C and CPP link/foo.h new file mode 100644 index 000000000..1ed8ce9b3 --- /dev/null +++ b/test cases/common/146 C and CPP link/foo.h @@ -0,0 +1,16 @@ +/* Copyright © 2017 Dylan Baker + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +int forty_two(void); diff --git a/test cases/common/146 C and CPP link/foo.hpp b/test cases/common/146 C and CPP link/foo.hpp new file mode 100644 index 000000000..e47f01dce --- /dev/null +++ b/test cases/common/146 C and CPP link/foo.hpp @@ -0,0 +1,24 @@ +/* Copyright © 2017 Dylan Baker + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +int six_one(void); + +#ifdef __cplusplus +} +#endif diff --git a/test cases/common/146 C and CPP link/foobar.c b/test cases/common/146 C and CPP link/foobar.c new file mode 100644 index 000000000..bd6cb00a4 --- /dev/null +++ b/test cases/common/146 C and CPP link/foobar.c @@ -0,0 +1,23 @@ +/* Copyright © 2017 Dylan Baker + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "foo.h" +#include "foo.hpp" +#include "foobar.h" + +void mynumbers(int nums[]) { + nums[0] = forty_two(); + nums[1] = six_one(); +} diff --git a/test cases/common/146 C and CPP link/foobar.h b/test cases/common/146 C and CPP link/foobar.h new file mode 100644 index 000000000..6dcb09664 --- /dev/null +++ b/test cases/common/146 C and CPP link/foobar.h @@ -0,0 +1,16 @@ +/* Copyright © 2017 Dylan Baker + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +void mynumbers(int nums[]); diff --git a/test cases/common/146 C and CPP link/meson.build b/test cases/common/146 C and CPP link/meson.build new file mode 100644 index 000000000..bfee4b2c7 --- /dev/null +++ b/test cases/common/146 C and CPP link/meson.build @@ -0,0 +1,28 @@ +# Copyright © 2017 Dylan Baker +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +project('C and C++ static link test', ['c', 'cpp'], + default_options : ['cpp_std=c++03']) + +dep_boost = dependency('boost') + +libcppfoo = static_library('cppfoo', ['foo.cpp', 'foo.hpp'], + dependencies : dep_boost) +libcfoo = static_library('cfoo', ['foo.c', 'foo.h']) + +libfoo = shared_library( + 'foo', + ['foobar.c', 'foobar.h'], + link_with : [libcfoo, libcppfoo], +) From 723884a369efa6fb5b825d76c4f81ba124358e2d Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Fri, 21 Apr 2017 16:05:24 +0530 Subject: [PATCH 2/6] Expose the implementation language for external libraries Ideally, all dependency objects should support this, but it's a lot of work and isn't supported by all dependency types (like frameworks and pkg-config), so for now just enable it for external libraries. --- mesonbuild/dependencies.py | 18 ++++++++++++------ mesonbuild/interpreter.py | 9 +-------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py index 39bf2b1b9..04a22f985 100644 --- a/mesonbuild/dependencies.py +++ b/mesonbuild/dependencies.py @@ -51,6 +51,7 @@ class DependencyMethods(Enum): class Dependency: def __init__(self, type_name, kwargs): self.name = "null" + self.language = None self.is_found = False self.type_name = type_name method = DependencyMethods(kwargs.get('method', 'auto')) @@ -570,11 +571,12 @@ class ExternalProgram: return self.name class ExternalLibrary(Dependency): - # TODO: Add `lang` to the parent Dependency object so that dependencies can - # be expressed for languages other than C-like - def __init__(self, name, link_args=None, language=None, silent=False): + # TODO: Add `language` support to all Dependency objects so that languages + # can be exposed for dependencies that support that (i.e., not pkg-config) + def __init__(self, name, link_args, language, silent=False): super().__init__('external', {}) self.name = name + self.language = language self.is_found = False self.link_args = [] self.lang_args = [] @@ -582,9 +584,13 @@ class ExternalLibrary(Dependency): self.is_found = True if not isinstance(link_args, list): link_args = [link_args] - if language: - self.lang_args = {language: link_args} - else: + self.lang_args = {language: link_args} + # We special-case Vala for now till the Dependency object gets + # proper support for exposing the language it was written in. + # Without this, vala-specific link args will end up in the C link + # args list if you link to a Vala library. + # This hack use to be in CompilerHolder.find_library(). + if language != 'vala': self.link_args = link_args if not silent: if self.is_found: diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index ed089871a..0eb392d3b 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -982,14 +982,7 @@ class CompilerHolder(InterpreterObject): if required and not linkargs: l = self.compiler.language.capitalize() raise InterpreterException('{} library {!r} not found'.format(l, libname)) - # If this is set to None, the library and link arguments are for - # a C-like compiler. Otherwise, it's for some other language that has - # a find_library implementation. We do this because it's easier than - # maintaining a list of languages that can consume C libraries. - lang = None - if self.compiler.language == 'vala': - lang = 'vala' - lib = dependencies.ExternalLibrary(libname, linkargs, language=lang) + lib = dependencies.ExternalLibrary(libname, linkargs, self.compiler.language) return ExternalLibraryHolder(lib) def has_argument_method(self, args, kwargs): From 22c4cd6e251f237f31b0612097e33dec391683fb Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Fri, 21 Apr 2017 16:07:03 +0530 Subject: [PATCH 3/6] Use linked-libraries to decide what linker to use 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++ compiler for linking. The same is also applicable for objc/objc++ etc, so we can keep using clike_langs for the priority order. Closes https://github.com/mesonbuild/meson/issues/1653 --- mesonbuild/build.py | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index bcbe31880..a5ebc3435 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -868,6 +868,28 @@ You probably should put it in link_with instead.''') def get_aliases(self): return {} + def get_langs_used_by_deps(self): + ''' + 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++ + compiler for linking. The same is also applicable for objc/objc++, etc, + so we can keep using clike_langs for the priority order. + + See: https://github.com/mesonbuild/meson/issues/1653 + ''' + langs = [] + # Check if any of the external libraries were written in this language + for dep in self.external_deps: + if dep.language not in langs: + langs.append(dep.language) + # Check if any of the internal libraries this target links to were + # written in this language + for link_target in self.link_targets: + for language in link_target.compilers: + if language not in langs: + langs.append(language) + return langs + def get_clike_dynamic_linker(self): ''' We use the order of languages in `clike_langs` to determine which @@ -878,9 +900,20 @@ You probably should put it in link_with instead.''') that can link compiled C. We don't actually need to add an exception for Vala here because of that. ''' + # Populate list of all compilers, not just those being used to compile + # sources in this target + if self.is_cross: + all_compilers = self.environment.coredata.cross_compilers + else: + all_compilers = self.environment.coredata.compilers + # Languages used by dependencies + dep_langs = self.get_langs_used_by_deps() + # Pick a compiler based on the language priority-order for l in clike_langs: - if l in self.compilers: - return self.compilers[l] + if l in self.compilers or l in dep_langs: + return all_compilers[l] + m = 'Could not get a dynamic linker for build target {!r}' + raise AssertionError(m.format(self.name)) def get_using_msvc(self): ''' From 93f3a6670ff0ed125986a2e0885eba0c9141bc10 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Fri, 21 Apr 2017 23:31:57 +0530 Subject: [PATCH 4/6] tests/common/146: Use C++98 features instead of Boost This makes it work on MSVC 2010 and platforms where Boost is not available. --- test cases/common/146 C and CPP link/foo.cpp | 11 +++++++++-- test cases/common/146 C and CPP link/meson.build | 8 ++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/test cases/common/146 C and CPP link/foo.cpp b/test cases/common/146 C and CPP link/foo.cpp index 639af8e32..d8b4dbbff 100644 --- a/test cases/common/146 C and CPP link/foo.cpp +++ b/test cases/common/146 C and CPP link/foo.cpp @@ -13,10 +13,17 @@ * limitations under the License. */ #include -#include + +const int cnums[] = {0, 61}; + +template +std::vector makeVector(const T (&data)[N]) +{ + return std::vector(data, data+N); +} namespace { - std::vector numbers = boost::assign::list_of(61); + std::vector numbers = makeVector(cnums); } extern "C" int six_one(void) { diff --git a/test cases/common/146 C and CPP link/meson.build b/test cases/common/146 C and CPP link/meson.build index bfee4b2c7..6f68bacc3 100644 --- a/test cases/common/146 C and CPP link/meson.build +++ b/test cases/common/146 C and CPP link/meson.build @@ -12,13 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -project('C and C++ static link test', ['c', 'cpp'], - default_options : ['cpp_std=c++03']) +project('C and C++ static link test', ['c', 'cpp']) -dep_boost = dependency('boost') - -libcppfoo = static_library('cppfoo', ['foo.cpp', 'foo.hpp'], - dependencies : dep_boost) +libcppfoo = static_library('cppfoo', ['foo.cpp', 'foo.hpp']) libcfoo = static_library('cfoo', ['foo.c', 'foo.h']) libfoo = shared_library( From 0ebf79ec8b74c8cbb1ea96fff740a8dfd80340b1 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Fri, 21 Apr 2017 23:57:21 +0530 Subject: [PATCH 5/6] configure_file: Accept output of configure_file as input --- mesonbuild/interpreter.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 0eb392d3b..eaaea737a 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -2313,9 +2313,11 @@ class Interpreter(InterpreterBase): raise InterpreterException('Input must be a string or a file') if isinstance(inputfile, str): inputfile = os.path.join(self.subdir, inputfile) + ifile_abs = os.path.join(self.environment.source_dir, inputfile) else: + ifile_abs = inputfile.absolute_path(self.environment.source_dir, + self.environment.build_dir) inputfile = inputfile.relative_name() - ifile_abs = os.path.join(self.environment.source_dir, inputfile) elif 'command' in kwargs and '@INPUT@' in kwargs['command']: raise InterpreterException('@INPUT@ used as command argument, but no input file specified.') # Validate output From 35ffb1a7c262acbcd15e532855471b0cb38379b5 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 22 Apr 2017 01:00:07 +0530 Subject: [PATCH 6/6] tests/common/146: Also test against external C++ libs --- .../common/146 C and CPP link/meson.build | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/test cases/common/146 C and CPP link/meson.build b/test cases/common/146 C and CPP link/meson.build index 6f68bacc3..583bd54df 100644 --- a/test cases/common/146 C and CPP link/meson.build +++ b/test cases/common/146 C and CPP link/meson.build @@ -14,11 +14,45 @@ project('C and C++ static link test', ['c', 'cpp']) -libcppfoo = static_library('cppfoo', ['foo.cpp', 'foo.hpp']) -libcfoo = static_library('cfoo', ['foo.c', 'foo.h']) +libc = static_library('cfoo', ['foo.c', 'foo.h']) + +# Test that linking C libs to external static C++ libs uses the C++ linker +# Since we can't depend on the test system to provide this, we create one +# ourselves at configure time and then 'find' it with cxx.find_library(). +cxx = meson.get_compiler('cpp') + +if cxx.get_id() == 'msvc' + compile_cmd = ['/c', '@INPUT@', '/Fo@OUTPUT@'] + stlib_cmd = ['lib', '/OUT:@OUTPUT@', '@INPUT@'] +else + compile_cmd = ['-c', '-fPIC', '@INPUT@', '-o', '@OUTPUT@'] + stlib_cmd = ['ar', 'csr', '@OUTPUT@', '@INPUT@'] +endif + +foo_cpp_o = configure_file( + input : 'foo.cpp', + output : 'foo.cpp.o', + command : cxx.cmd_array() + compile_cmd) + +configure_file( + input : foo_cpp_o, + output : 'libstcppext.a', + command : stlib_cmd) + +libstcppext = cxx.find_library('stcppext', dirs : meson.current_build_dir()) + +libfooext = shared_library( + 'fooext', + ['foobar.c', 'foobar.h'], + link_with : libc, + dependencies : libstcppext, +) + +# Test that linking C libs to internal static C++ libs uses the C++ linker +libcpp = static_library('cppfoo', ['foo.cpp', 'foo.hpp']) libfoo = shared_library( 'foo', ['foobar.c', 'foobar.h'], - link_with : [libcfoo, libcppfoo], + link_with : [libc, libcpp], )