From ee8b3b12a067c52efcfb7c05c638023fbe0495c8 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Wed, 5 Oct 2016 15:58:45 +0530 Subject: [PATCH 1/3] Add cross-platform PIC support for static libraries With C/C++, on Windows you don't need to pass any arguments for a static library to be PIC. On UNIX platforms you need to pass -fPIC. Other languages such as D have compiler-specific PIC arguments required for PIC support in static libraries on UNIX platforms. This kwarg allows people to specify which static libraries should be built with PIC support. This is usually used for static libraries that will be linked into shared libraries. --- mesonbuild/backend/backends.py | 2 ++ mesonbuild/build.py | 28 ++++++++++++++----- .../common/62 exe static shared/meson.build | 5 ++-- test cases/common/62 exe static shared/prog.c | 7 ++++- .../common/62 exe static shared/shlib2.c | 7 +++++ .../62 exe static shared/subdir/exports.h | 12 ++++++++ .../62 exe static shared/subdir/shlib.c | 11 +------- 7 files changed, 52 insertions(+), 20 deletions(-) create mode 100644 test cases/common/62 exe static shared/shlib2.c create mode 100644 test cases/common/62 exe static shared/subdir/exports.h diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index d5af05696..16f7adab4 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -341,6 +341,8 @@ class Backend(): commands += compiler.get_werror_args() if isinstance(target, build.SharedLibrary): commands += compiler.get_pic_args() + if isinstance(target, build.StaticLibrary) and target.pic: + commands += compiler.get_pic_args() for dep in target.get_external_deps(): # Cflags required by external deps might have UNIX-specific flags, # so filter them out if needed diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 1ef183b2f..a532509f0 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -44,12 +44,19 @@ known_basic_kwargs = {'install' : True, 'native' : True, } -known_shlib_kwargs = known_basic_kwargs.copy() -known_shlib_kwargs.update({'version' : True, - 'soversion' : True, - 'name_prefix' : True, - 'name_suffix' : True, - 'vs_module_defs' : True}) +# These contain kwargs supported by both static and shared libraries. These are +# combined here because a library() call might be shared_library() or +# static_library() at runtime based on the configuration. +# FIXME: Find a way to pass that info down here so we can have proper target +# kwargs checking when specifically using shared_library() or static_library(). +known_lib_kwargs = known_basic_kwargs.copy() +known_lib_kwargs.update({'version' : True, # Only for shared libs + 'soversion' : True, # Only for shared libs + 'name_prefix' : True, + 'name_suffix' : True, + 'vs_module_defs' : True, # Only for shared libs + 'pic' : True, # Only for static libs + }) def compilers_are_msvc(compilers): """ @@ -516,6 +523,10 @@ class BuildTarget(): if not isinstance(name_suffix, str): raise InvalidArguments('Name suffix must be a string.') self.suffix = name_suffix + if isinstance(self, StaticLibrary): + self.pic = kwargs.get('pic', False) + if not isinstance(self.pic, bool): + raise InvalidArguments('Argument pic must be boolean') def get_subdir(self): return self.subdir @@ -831,6 +842,9 @@ class StaticLibrary(BuildTarget): def type_suffix(self): return "@sta" + def check_unknown_kwargs(self, kwargs): + self.check_unknown_kwargs_int(kwargs, known_lib_kwargs) + class SharedLibrary(BuildTarget): def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): self.soversion = None @@ -988,7 +1002,7 @@ class SharedLibrary(BuildTarget): self.link_depends.append(path) def check_unknown_kwargs(self, kwargs): - self.check_unknown_kwargs_int(kwargs, known_shlib_kwargs) + self.check_unknown_kwargs_int(kwargs, known_lib_kwargs) def get_import_filename(self): """ diff --git a/test cases/common/62 exe static shared/meson.build b/test cases/common/62 exe static shared/meson.build index 3c753910a..8631e68f1 100644 --- a/test cases/common/62 exe static shared/meson.build +++ b/test cases/common/62 exe static shared/meson.build @@ -1,6 +1,7 @@ project('statchain', 'c') subdir('subdir') -statlib = static_library('stat', 'stat.c', link_with : shlib) -exe = executable('prog', 'prog.c', link_with : statlib) +statlib = static_library('stat', 'stat.c', link_with : shlib, pic : true) +shlib2 = shared_library('shr2', 'shlib2.c', link_with : statlib) +exe = executable('prog', 'prog.c', link_with : shlib2) test('runtest', exe) diff --git a/test cases/common/62 exe static shared/prog.c b/test cases/common/62 exe static shared/prog.c index 4f82a6b16..26603b694 100644 --- a/test cases/common/62 exe static shared/prog.c +++ b/test cases/common/62 exe static shared/prog.c @@ -1,5 +1,10 @@ +int shlibfunc2(); int statlibfunc(); int main(int argc, char **argv) { - return statlibfunc() == 42 ? 0 : 1; + if (statlibfunc() != 42) + return 1; + if (shlibfunc2() != 24) + return 1; + return 0; } diff --git a/test cases/common/62 exe static shared/shlib2.c b/test cases/common/62 exe static shared/shlib2.c new file mode 100644 index 000000000..e26d11c73 --- /dev/null +++ b/test cases/common/62 exe static shared/shlib2.c @@ -0,0 +1,7 @@ +#include "subdir/exports.h" + +int statlibfunc(void); + +int DLL_PUBLIC shlibfunc2(void) { + return statlibfunc() - 18; +} diff --git a/test cases/common/62 exe static shared/subdir/exports.h b/test cases/common/62 exe static shared/subdir/exports.h new file mode 100644 index 000000000..c89ccb23b --- /dev/null +++ b/test cases/common/62 exe static shared/subdir/exports.h @@ -0,0 +1,12 @@ +#pragma once + +#if defined _WIN32 || defined __CYGWIN__ + #define DLL_PUBLIC __declspec(dllexport) +#else + #if defined __GNUC__ + #define DLL_PUBLIC __attribute__ ((visibility("default"))) + #else + #pragma message ("Compiler does not support symbol visibility.") + #define DLL_PUBLIC + #endif +#endif diff --git a/test cases/common/62 exe static shared/subdir/shlib.c b/test cases/common/62 exe static shared/subdir/shlib.c index d649c7d24..002c83f92 100644 --- a/test cases/common/62 exe static shared/subdir/shlib.c +++ b/test cases/common/62 exe static shared/subdir/shlib.c @@ -1,13 +1,4 @@ -#if defined _WIN32 || defined __CYGWIN__ - #define DLL_PUBLIC __declspec(dllexport) -#else - #if defined __GNUC__ - #define DLL_PUBLIC __attribute__ ((visibility("default"))) - #else - #pragma message ("Compiler does not support symbol visibility.") - #define DLL_PUBLIC - #endif -#endif +#include "exports.h" int DLL_PUBLIC shlibfunc() { return 42; From ce1393609f0f870ffa1b0634f561613b7b627f0b Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Fri, 7 Oct 2016 19:56:04 +0530 Subject: [PATCH 2/3] Error out if shared lib links to static lib without PIC This is only needed on Linux and BSD. So, we always unconditionally enable self.pic for Windows and OS X. --- mesonbuild/build.py | 14 +++++++++++--- .../failing/33 exe static shared/meson.build | 11 +++++++++++ test cases/failing/33 exe static shared/prog.c | 10 ++++++++++ test cases/failing/33 exe static shared/shlib2.c | 16 ++++++++++++++++ test cases/failing/33 exe static shared/stat.c | 3 +++ 5 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 test cases/failing/33 exe static shared/meson.build create mode 100644 test cases/failing/33 exe static shared/prog.c create mode 100644 test cases/failing/33 exe static shared/shlib2.c create mode 100644 test cases/failing/33 exe static shared/stat.c diff --git a/mesonbuild/build.py b/mesonbuild/build.py index a532509f0..3d41cda4a 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -524,9 +524,15 @@ class BuildTarget(): raise InvalidArguments('Name suffix must be a string.') self.suffix = name_suffix if isinstance(self, StaticLibrary): - self.pic = kwargs.get('pic', False) - if not isinstance(self.pic, bool): - raise InvalidArguments('Argument pic must be boolean') + # You can't disable PIC on OS X. The compiler ignores -fno-PIC. + # PIC is always on for Windows (all code is position-independent + # since library loading is done differently) + if for_darwin(self.is_cross, self.environment) or for_windows(self.is_cross, self.environment): + self.pic = True + else: + self.pic = kwargs.get('pic', False) + if not isinstance(self.pic, bool): + raise InvalidArguments('Argument pic must be boolean') def get_subdir(self): return self.subdir @@ -629,6 +635,8 @@ by calling get_variable() on the subproject object.''') t = t.held_object if not isinstance(t, (StaticLibrary, SharedLibrary)): raise InvalidArguments('Link target is not library.') + if isinstance(self, SharedLibrary) and isinstance(t, StaticLibrary) and not t.pic: + raise InvalidArguments("Can't link a non-PIC static library into a shared library") if self.is_cross != t.is_cross: raise InvalidArguments('Tried to mix cross built and native libraries in target %s.' % self.name) self.link_targets.append(t) diff --git a/test cases/failing/33 exe static shared/meson.build b/test cases/failing/33 exe static shared/meson.build new file mode 100644 index 000000000..b1027647d --- /dev/null +++ b/test cases/failing/33 exe static shared/meson.build @@ -0,0 +1,11 @@ +project('statchain', 'c') + +host_system = host_machine.system() +if host_system == 'windows' or host_system == 'darwin' + error('Test only fails on Linux and BSD') +endif + +statlib = static_library('stat', 'stat.c', pic : false) +shlib2 = shared_library('shr2', 'shlib2.c', link_with : statlib) +exe = executable('prog', 'prog.c', link_with : shlib2) +test('runtest', exe) diff --git a/test cases/failing/33 exe static shared/prog.c b/test cases/failing/33 exe static shared/prog.c new file mode 100644 index 000000000..26603b694 --- /dev/null +++ b/test cases/failing/33 exe static shared/prog.c @@ -0,0 +1,10 @@ +int shlibfunc2(); +int statlibfunc(); + +int main(int argc, char **argv) { + if (statlibfunc() != 42) + return 1; + if (shlibfunc2() != 24) + return 1; + return 0; +} diff --git a/test cases/failing/33 exe static shared/shlib2.c b/test cases/failing/33 exe static shared/shlib2.c new file mode 100644 index 000000000..5b68843dc --- /dev/null +++ b/test cases/failing/33 exe static shared/shlib2.c @@ -0,0 +1,16 @@ +#if defined _WIN32 || defined __CYGWIN__ + #define DLL_PUBLIC __declspec(dllexport) +#else + #if defined __GNUC__ + #define DLL_PUBLIC __attribute__ ((visibility("default"))) + #else + #pragma message ("Compiler does not support symbol visibility.") + #define DLL_PUBLIC + #endif +#endif + +int statlibfunc(void); + +int DLL_PUBLIC shlibfunc2(void) { + return 24; +} diff --git a/test cases/failing/33 exe static shared/stat.c b/test cases/failing/33 exe static shared/stat.c new file mode 100644 index 000000000..56ec66c67 --- /dev/null +++ b/test cases/failing/33 exe static shared/stat.c @@ -0,0 +1,3 @@ +int statlibfunc() { + return 42; +} From 67c9e520de4194b06fcfa2609f2036dfd9636217 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Fri, 7 Oct 2016 20:00:29 +0530 Subject: [PATCH 3/3] compilers: Don't pollute c_args with -fPIC on OS X It's always enabled and the compiler just ignores all PIC-related arguments. Relatedly, do the same thing for Clang as GCC. --- mesonbuild/compilers.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py index 19fb888c8..c81a5995f 100644 --- a/mesonbuild/compilers.py +++ b/mesonbuild/compilers.py @@ -1934,8 +1934,8 @@ class GnuCompiler: return defines[define] def get_pic_args(self): - if self.gcc_type == GCC_MINGW: - return [] # On Window gcc defaults to fpic being always on. + if self.gcc_type in (GCC_MINGW, GCC_OSX): + return [] # On Window and OS X, pic is always on. return ['-fPIC'] def get_buildtype_args(self, buildtype): @@ -2059,6 +2059,11 @@ class ClangCompiler(): self.base_options.append('b_lundef') self.base_options.append('b_asneeded') + def get_pic_args(self): + if self.clang_type in (CLANG_WIN, CLANG_OSX): + return [] # On Window and OS X, pic is always on. + return ['-fPIC'] + def get_buildtype_args(self, buildtype): return gnulike_buildtype_args[buildtype] @@ -2174,8 +2179,8 @@ class FortranCompiler(Compiler): return ' '.join(self.exelist) def get_pic_args(self): - if self.gcc_type == GCC_MINGW: - return [] # On Windows gcc defaults to fpic being always on. + if self.gcc_type in (GCC_MINGW, GCC_OSX): + return [] # On Window and OS X, pic is always on. return ['-fPIC'] def get_std_shared_lib_link_args(self):