Add prelinking support for static libraries.

This commit is contained in:
Jussi Pakkanen 2020-11-14 16:43:00 +02:00
parent 6e39dcad2f
commit c21b04ba08
15 changed files with 155 additions and 2 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,3 @@
#pragma once
int public_func();