Add prelinking support for static libraries.

pull/8069/head
Jussi Pakkanen 4 years ago
parent 6e39dcad2f
commit c21b04ba08
  1. 4
      docs/markdown/Reference-manual.md
  2. 6
      docs/markdown/snippets/prelink.md
  3. 21
      mesonbuild/backend/ninjabackend.py
  4. 22
      mesonbuild/build.py
  5. 2
      mesonbuild/compilers/compilers.py
  6. 3
      mesonbuild/compilers/mixins/gnu.py
  7. 26
      run_unittests.py
  8. 14
      test cases/unit/87 prelinking/file1.c
  9. 9
      test cases/unit/87 prelinking/file2.c
  10. 9
      test cases/unit/87 prelinking/file3.c
  11. 9
      test cases/unit/87 prelinking/file4.c
  12. 10
      test cases/unit/87 prelinking/main.c
  13. 8
      test cases/unit/87 prelinking/meson.build
  14. 11
      test cases/unit/87 prelinking/private_header.h
  15. 3
      test cases/unit/87 prelinking/public_header.h

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

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

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

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

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

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

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

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

@ -0,0 +1,9 @@
#include<private_header.h>
int round1_b() {
return round1_c();
}
int round2_b() {
return round2_c();
}

@ -0,0 +1,9 @@
#include<private_header.h>
int round1_c() {
return round1_d();
}
int round2_c() {
return round2_d();
}

@ -0,0 +1,9 @@
#include<private_header.h>
int round1_d() {
return round2_a();
}
int round2_d() {
return 42;
}

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

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

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

@ -0,0 +1,3 @@
#pragma once
int public_func();
Loading…
Cancel
Save