Merge pull request #1654 from dcbaker/c-cpp-link

Add a testcase for linking C and C++ static archives into a shared li…
pull/1660/head
Jussi Pakkanen 8 years ago committed by GitHub
commit 33b5ddf35e
  1. 37
      mesonbuild/build.py
  2. 18
      mesonbuild/dependencies.py
  3. 13
      mesonbuild/interpreter.py
  4. 19
      test cases/common/146 C and CPP link/foo.c
  5. 31
      test cases/common/146 C and CPP link/foo.cpp
  6. 16
      test cases/common/146 C and CPP link/foo.h
  7. 24
      test cases/common/146 C and CPP link/foo.hpp
  8. 23
      test cases/common/146 C and CPP link/foobar.c
  9. 16
      test cases/common/146 C and CPP link/foobar.h
  10. 58
      test cases/common/146 C and CPP link/meson.build

@ -868,6 +868,28 @@ You probably should put it in link_with instead.''')
def get_aliases(self): def get_aliases(self):
return {} 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): def get_clike_dynamic_linker(self):
''' '''
We use the order of languages in `clike_langs` to determine which 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 that can link compiled C. We don't actually need to add an exception
for Vala here because of that. 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: for l in clike_langs:
if l in self.compilers: if l in self.compilers or l in dep_langs:
return self.compilers[l] 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): def get_using_msvc(self):
''' '''

@ -51,6 +51,7 @@ class DependencyMethods(Enum):
class Dependency: class Dependency:
def __init__(self, type_name, kwargs): def __init__(self, type_name, kwargs):
self.name = "null" self.name = "null"
self.language = None
self.is_found = False self.is_found = False
self.type_name = type_name self.type_name = type_name
method = DependencyMethods(kwargs.get('method', 'auto')) method = DependencyMethods(kwargs.get('method', 'auto'))
@ -570,11 +571,12 @@ class ExternalProgram:
return self.name return self.name
class ExternalLibrary(Dependency): class ExternalLibrary(Dependency):
# TODO: Add `lang` to the parent Dependency object so that dependencies can # TODO: Add `language` support to all Dependency objects so that languages
# be expressed for languages other than C-like # can be exposed for dependencies that support that (i.e., not pkg-config)
def __init__(self, name, link_args=None, language=None, silent=False): def __init__(self, name, link_args, language, silent=False):
super().__init__('external', {}) super().__init__('external', {})
self.name = name self.name = name
self.language = language
self.is_found = False self.is_found = False
self.link_args = [] self.link_args = []
self.lang_args = [] self.lang_args = []
@ -582,9 +584,13 @@ class ExternalLibrary(Dependency):
self.is_found = True self.is_found = True
if not isinstance(link_args, list): if not isinstance(link_args, list):
link_args = [link_args] link_args = [link_args]
if language: self.lang_args = {language: link_args}
self.lang_args = {language: link_args} # We special-case Vala for now till the Dependency object gets
else: # 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 self.link_args = link_args
if not silent: if not silent:
if self.is_found: if self.is_found:

@ -982,14 +982,7 @@ class CompilerHolder(InterpreterObject):
if required and not linkargs: if required and not linkargs:
l = self.compiler.language.capitalize() l = self.compiler.language.capitalize()
raise InterpreterException('{} library {!r} not found'.format(l, libname)) raise InterpreterException('{} library {!r} not found'.format(l, libname))
# If this is set to None, the library and link arguments are for lib = dependencies.ExternalLibrary(libname, linkargs, self.compiler.language)
# 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)
return ExternalLibraryHolder(lib) return ExternalLibraryHolder(lib)
def has_argument_method(self, args, kwargs): def has_argument_method(self, args, kwargs):
@ -2320,9 +2313,11 @@ class Interpreter(InterpreterBase):
raise InterpreterException('Input must be a string or a file') raise InterpreterException('Input must be a string or a file')
if isinstance(inputfile, str): if isinstance(inputfile, str):
inputfile = os.path.join(self.subdir, inputfile) inputfile = os.path.join(self.subdir, inputfile)
ifile_abs = os.path.join(self.environment.source_dir, inputfile)
else: else:
ifile_abs = inputfile.absolute_path(self.environment.source_dir,
self.environment.build_dir)
inputfile = inputfile.relative_name() inputfile = inputfile.relative_name()
ifile_abs = os.path.join(self.environment.source_dir, inputfile)
elif 'command' in kwargs and '@INPUT@' in kwargs['command']: elif 'command' in kwargs and '@INPUT@' in kwargs['command']:
raise InterpreterException('@INPUT@ used as command argument, but no input file specified.') raise InterpreterException('@INPUT@ used as command argument, but no input file specified.')
# Validate output # Validate output

@ -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;
}

@ -0,0 +1,31 @@
/* 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 <vector>
const int cnums[] = {0, 61};
template<typename T, int N>
std::vector<T> makeVector(const T (&data)[N])
{
return std::vector<T>(data, data+N);
}
namespace {
std::vector<int> numbers = makeVector(cnums);
}
extern "C" int six_one(void) {
return numbers[1];
}

@ -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);

@ -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

@ -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();
}

@ -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[]);

@ -0,0 +1,58 @@
# 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'])
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 : [libc, libcpp],
)
Loading…
Cancel
Save