aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2020-11-14 16:43:00 +0200
committerJussi Pakkanen <jpakkane@gmail.com>2020-12-03 22:58:07 +0200
commitc21b04ba087189472c60ce6e84987b74e9b7fccd (patch)
tree71a5c7a660cf638e344c26f63b929902689a195e
parent6e39dcad2fbd8d1c739e262b0e7b7d901cf1ce08 (diff)
downloadmeson-c21b04ba087189472c60ce6e84987b74e9b7fccd.zip
meson-c21b04ba087189472c60ce6e84987b74e9b7fccd.tar.gz
meson-c21b04ba087189472c60ce6e84987b74e9b7fccd.tar.bz2
Add prelinking support for static libraries.
-rw-r--r--docs/markdown/Reference-manual.md4
-rw-r--r--docs/markdown/snippets/prelink.md6
-rw-r--r--mesonbuild/backend/ninjabackend.py21
-rw-r--r--mesonbuild/build.py22
-rw-r--r--mesonbuild/compilers/compilers.py2
-rw-r--r--mesonbuild/compilers/mixins/gnu.py3
-rwxr-xr-xrun_unittests.py26
-rw-r--r--test cases/unit/87 prelinking/file1.c14
-rw-r--r--test cases/unit/87 prelinking/file2.c9
-rw-r--r--test cases/unit/87 prelinking/file3.c9
-rw-r--r--test cases/unit/87 prelinking/file4.c9
-rw-r--r--test cases/unit/87 prelinking/main.c10
-rw-r--r--test cases/unit/87 prelinking/meson.build8
-rw-r--r--test cases/unit/87 prelinking/private_header.h11
-rw-r--r--test cases/unit/87 prelinking/public_header.h3
15 files changed, 155 insertions, 2 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index aeaeccb..f490f5f 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -1574,6 +1574,10 @@ has one argument the others don't have:
option has no effect on Windows and OS X since it doesn't make
sense on Windows and PIC cannot be disabled on OS X.
+- `prelink` *since0.57.0*: if `true` the object files in the target
+ will be prelinked, meaning that it will contain only one prelinked
+ object file rather than the individual object files.
+
### subdir()
``` meson
diff --git a/docs/markdown/snippets/prelink.md b/docs/markdown/snippets/prelink.md
new file mode 100644
index 0000000..26f5bbb
--- /dev/null
+++ b/docs/markdown/snippets/prelink.md
@@ -0,0 +1,6 @@
+## Add support for prelinked static libraries
+
+The static library gains a new `prelink` keyword argument that can be
+used to prelink object files in that target. This is currently only
+supported for the GNU toolchain, patches to add it to other compilers
+are most welcome.
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index b03954a..0fe8a2f 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -840,7 +840,11 @@ int dummy;
for src in self.generate_unity_files(target, unity_src):
obj_list.append(self.generate_single_compile(target, src, True, unity_deps + header_deps))
linker, stdlib_args = self.determine_linker_and_stdlib_args(target)
- elem = self.generate_link(target, outname, obj_list, linker, pch_objects, stdlib_args=stdlib_args)
+ if isinstance(target, build.StaticLibrary) and target.prelink:
+ final_obj_list = self.generate_prelink(target, obj_list)
+ else:
+ final_obj_list = obj_list
+ elem = self.generate_link(target, outname, final_obj_list, linker, pch_objects, stdlib_args=stdlib_args)
self.generate_shlib_aliases(target, self.get_target_dir(target))
self.add_build(elem)
@@ -2683,6 +2687,21 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
return guessed_dependencies + absolute_libs
+ def generate_prelink(self, target, obj_list):
+ assert(isinstance(target, build.StaticLibrary))
+ prelink_name = os.path.join(self.get_target_private_dir(target), target.name + '-prelink.o')
+ elem = NinjaBuildElement(self.all_outputs, [prelink_name], 'CUSTOM_COMMAND', obj_list)
+
+ prelinker = target.get_prelinker()
+ cmd = prelinker.exelist[:]
+ cmd += prelinker.get_prelink_args(prelink_name, obj_list)
+
+ cmd = self.replace_paths(target, cmd)
+ elem.add_item('COMMAND', cmd)
+ elem.add_item('description', 'Prelinking {}.'.format(prelink_name))
+ self.add_build(elem)
+ return [prelink_name]
+
def generate_link(self, target, outname, obj_list, linker, extra_args=None, stdlib_args=None):
extra_args = extra_args if extra_args is not None else []
stdlib_args = stdlib_args if stdlib_args is not None else []
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 36d4e19..5b7a679 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -102,7 +102,7 @@ known_build_target_kwargs = (
known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic', 'pie'}
known_shlib_kwargs = known_build_target_kwargs | {'version', 'soversion', 'vs_module_defs', 'darwin_versions'}
known_shmod_kwargs = known_build_target_kwargs | {'vs_module_defs'}
-known_stlib_kwargs = known_build_target_kwargs | {'pic'}
+known_stlib_kwargs = known_build_target_kwargs | {'pic', 'prelink'}
known_jar_kwargs = known_exe_kwargs | {'main_class'}
@lru_cache(maxsize=None)
@@ -1280,6 +1280,23 @@ You probably should put it in link_with instead.''')
return langs
+ def get_prelinker(self):
+ all_compilers = self.environment.coredata.compilers[self.for_machine]
+ if self.link_language:
+ comp = all_compilers[self.link_language]
+ return comp
+ for l in clink_langs:
+ if l in self.compilers:
+ try:
+ prelinker = all_compilers[l]
+ except KeyError:
+ raise MesonException(
+ 'Could not get a prelinker linker for build target {!r}. '
+ 'Requires a compiler for language "{}", but that is not '
+ 'a project language.'.format(self.name, l))
+ return prelinker
+ raise MesonException('Could not determine prelinker for {!r}.'.format(self.name))
+
def get_clink_dynamic_linker_and_stdlibs(self):
'''
We use the order of languages in `clink_langs` to determine which
@@ -1674,6 +1691,9 @@ class StaticLibrary(BuildTarget):
self.suffix = 'a'
self.filename = self.prefix + self.name + '.' + self.suffix
self.outputs = [self.filename]
+ self.prelink = kwargs.get('prelink', False)
+ if not isinstance(self.prelink, bool):
+ raise InvalidArguments('Prelink keyword argument must be a boolean.')
def get_link_deps_mapping(self, prefix, environment):
return {}
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index 5c9e1ae..2900e19 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -1200,6 +1200,8 @@ class Compiler(metaclass=abc.ABCMeta):
# TODO: using a TypeDict here would improve this
raise EnvironmentError('{} does not implement get_feature_args'.format(self.id))
+ def get_prelink_args(self, prelink_name: str, obj_list: T.List[str]) -> T.List[str]:
+ raise EnvironmentException('{} does not know how to do prelinking.'.format(self.id))
def get_args_from_envvars(lang: str,
for_machine: MachineChoice,
diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py
index bb1fc66..4024cbe 100644
--- a/mesonbuild/compilers/mixins/gnu.py
+++ b/mesonbuild/compilers/mixins/gnu.py
@@ -394,3 +394,6 @@ class GnuCompiler(GnuLikeCompiler):
# GCC only warns about unknown or ignored attributes, so force an
# error.
return ['-Werror=attributes']
+
+ def get_prelink_args(self, prelink_name: str, obj_list: T.List[str]) -> T.List[str]:
+ return ['-r', '-o', prelink_name] + obj_list
diff --git a/run_unittests.py b/run_unittests.py
index 9560319..a461df5 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -7389,6 +7389,32 @@ class LinuxlikeTests(BasePlatformTests):
content = f.read()
self.assertNotIn('-lfoo', content)
+ def test_prelinking(self):
+ # Prelinking currently only works on recently new GNU toolchains.
+ # Skip everything else. When support for other toolchains is added,
+ # remove limitations as necessary.
+ if is_osx():
+ raise unittest.SkipTest('Prelinking not supported on Darwin.')
+ if 'clang' in os.environ.get('CC', 'dummy'):
+ raise unittest.SkipTest('Prelinking not supported with Clang.')
+ gccver = subprocess.check_output(['cc', '--version'])
+ if b'7.5.0' in gccver:
+ raise unittest.SkipTest('GCC on Bionic is too old to be supported.')
+ testdir = os.path.join(self.unit_test_dir, '87 prelinking')
+ self.init(testdir)
+ self.build()
+ outlib = os.path.join(self.builddir, 'libprelinked.a')
+ ar = shutil.which('ar')
+ self.assertTrue(os.path.exists(outlib))
+ self.assertTrue(ar is not None)
+ p = subprocess.run([ar, 't', outlib],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.DEVNULL,
+ universal_newlines=True, timeout=1)
+ obj_files = p.stdout.strip().split('\n')
+ self.assertEqual(len(obj_files), 1)
+ self.assertTrue(obj_files[0].endswith('-prelink.o'))
+
class BaseLinuxCrossTests(BasePlatformTests):
# Don't pass --libdir when cross-compiling. We have tests that
# check whether meson auto-detects it correctly.
diff --git a/test cases/unit/87 prelinking/file1.c b/test cases/unit/87 prelinking/file1.c
new file mode 100644
index 0000000..9f0e265
--- /dev/null
+++ b/test cases/unit/87 prelinking/file1.c
@@ -0,0 +1,14 @@
+#include<public_header.h>
+#include<private_header.h>
+
+int public_func() {
+ return round1_a();
+}
+
+int round1_a() {
+ return round1_b();
+}
+
+int round2_a() {
+ return round2_b();
+}
diff --git a/test cases/unit/87 prelinking/file2.c b/test cases/unit/87 prelinking/file2.c
new file mode 100644
index 0000000..ce3b115
--- /dev/null
+++ b/test cases/unit/87 prelinking/file2.c
@@ -0,0 +1,9 @@
+#include<private_header.h>
+
+int round1_b() {
+ return round1_c();
+}
+
+int round2_b() {
+ return round2_c();
+}
diff --git a/test cases/unit/87 prelinking/file3.c b/test cases/unit/87 prelinking/file3.c
new file mode 100644
index 0000000..85052be
--- /dev/null
+++ b/test cases/unit/87 prelinking/file3.c
@@ -0,0 +1,9 @@
+#include<private_header.h>
+
+int round1_c() {
+ return round1_d();
+}
+
+int round2_c() {
+ return round2_d();
+}
diff --git a/test cases/unit/87 prelinking/file4.c b/test cases/unit/87 prelinking/file4.c
new file mode 100644
index 0000000..622364e
--- /dev/null
+++ b/test cases/unit/87 prelinking/file4.c
@@ -0,0 +1,9 @@
+#include<private_header.h>
+
+int round1_d() {
+ return round2_a();
+}
+
+int round2_d() {
+ return 42;
+}
diff --git a/test cases/unit/87 prelinking/main.c b/test cases/unit/87 prelinking/main.c
new file mode 100644
index 0000000..09a2e5c
--- /dev/null
+++ b/test cases/unit/87 prelinking/main.c
@@ -0,0 +1,10 @@
+#include<public_header.h>
+#include<stdio.h>
+
+int main(int argc, char **argv) {
+ if(public_func() != 42) {
+ printf("Something failed.\n");
+ return 1;
+ }
+ return 0;
+}
diff --git a/test cases/unit/87 prelinking/meson.build b/test cases/unit/87 prelinking/meson.build
new file mode 100644
index 0000000..3dbf88e
--- /dev/null
+++ b/test cases/unit/87 prelinking/meson.build
@@ -0,0 +1,8 @@
+project('prelinking', 'c')
+
+liba = static_library('prelinked', 'file1.c', 'file2.c', 'file3.c', 'file4.c',
+ prelink: true)
+exe = executable('testprog', 'main.c',
+ link_with: liba)
+test('prelinked', exe)
+ \ No newline at end of file
diff --git a/test cases/unit/87 prelinking/private_header.h b/test cases/unit/87 prelinking/private_header.h
new file mode 100644
index 0000000..f24b621
--- /dev/null
+++ b/test cases/unit/87 prelinking/private_header.h
@@ -0,0 +1,11 @@
+#pragma once
+
+int round1_a();
+int round1_b();
+int round1_c();
+int round1_d();
+
+int round2_a();
+int round2_b();
+int round2_c();
+int round2_d();
diff --git a/test cases/unit/87 prelinking/public_header.h b/test cases/unit/87 prelinking/public_header.h
new file mode 100644
index 0000000..0cd6786
--- /dev/null
+++ b/test cases/unit/87 prelinking/public_header.h
@@ -0,0 +1,3 @@
+#pragma once
+
+int public_func();