Skip to content

Commit

Permalink
Allow link_depends to take strings, Files or generated objects. Closes
Browse files Browse the repository at this point in the history
…mesonbuild#1172

Currently only strings can be passed to the link_depends argument of
executable and *library, which solves many cases, but not every one.
This patch allows generated sources and Files to be passed as well.

On the implementation side, it uses a helper method to keep the more
complex logic separated from the __init__ method. This also requires
that Targets set their link_depends paths as Files, and the backend is
responsible for converting to strings when it wants them.

This adds tests for the following cases:
- Using a file in a subdir
- Using a configure_file as an input
- Using a custom_target as an input

It does not support using a generator as an input, since currently that
would require calling the generator twice, once for the -Wl argument,
and once for the link_depends.

Also updates the docs.
  • Loading branch information
dcbaker committed May 1, 2017
1 parent b65b9fe commit 72fb966
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 13 deletions.
2 changes: 1 addition & 1 deletion docs/markdown/Reference-manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ Executable supports the following keyword arguments. Note that just like the pos
- `<languagename>_pch` precompiled header file to use for the given language
- `<languagename>_args` compiler flags to use for the given language; eg: `cpp_args` for C++
- `link_args` flags to use during linking. You can use unix-style flags here for all platforms.
- `link_depends` an extra file in the source tree that the link step depends on such as a symbol visibility map. The purpose is to automatically trigger a re-link (but not a re-compile) of the target when this file changes.
- `link_depends` files that the link step depends on such as a symbol visibility map. The purpose is to automatically trigger a re-link (but not a re-compile) of the target when one or more of these files changes.
- `include_directories` one or more objects created with the `include_directories` function
- `dependencies` one or more objects created with [`dependency`](#dependency) or [`find_library`](#compiler-object) (for external deps) or [`declare_dependency`](#declare_dependency) (for deps built by the project)
- `gui_app` when set to true flags this target as a GUI application on platforms where this makes a difference (e.g. Windows)
Expand Down
10 changes: 8 additions & 2 deletions mesonbuild/backend/ninjabackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -2317,8 +2317,8 @@ def generate_link(self, target, outfile, outname, obj_list, linker, extra_args=[
# current compiler.
commands = commands.to_native()
dep_targets = [self.get_dependency_filename(t) for t in dependencies]
dep_targets += [os.path.join(self.environment.source_dir,
target.subdir, t) for t in target.link_depends]
dep_targets.extend([self.get_dependency_filename(t)
for t in target.link_depends])
elem = NinjaBuildElement(self.all_outputs, outname, linker_rule, obj_list)
elem.add_dep(dep_targets + custom_target_libraries)
elem.add_item('LINK_ARGS', commands)
Expand All @@ -2336,6 +2336,12 @@ def determine_rpath_dirs(self, target):
def get_dependency_filename(self, t):
if isinstance(t, build.SharedLibrary):
return os.path.join(self.get_target_private_dir(t), self.get_target_filename(t) + '.symbols')
elif isinstance(t, mesonlib.File):
if t.is_built:
return t.relative_name()
else:
return t.absolute_path(self.environment.get_source_dir(),
self.environment.get_build_dir())
return self.get_target_filename(t)

def generate_shlib_aliases(self, target, outdir):
Expand Down
42 changes: 32 additions & 10 deletions mesonbuild/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,35 @@ def validate_sources(self):
assert(len(self.compilers) == 1)
return

def process_link_depends(self, sources, environment):
"""Process the link_depends keyword argument.
This is designed to handle strings, Files, and the output of Custom
Targets. Notably it doesn't handle generator() returned objects. Since
adding them as a link depends would inherently cuase them to be
generated twice, since you also need to pass them twice.
"""
if not isinstance(sources, list):
sources = [sources]
for s in sources:
if hasattr(s, 'held_object'):
s = s.held_object

if isinstance(s, File):
self.link_depends.append(s)
elif isinstance(s, str):
self.link_depends.append(
File.from_source_file(environment.source_dir, self.subdir, s))
# os.path.join(environment.source_dir, self.subdir, s))
elif hasattr(s, 'get_outputs'):
self.link_depends.extend(
[File.from_built_file(s.subdir, p) for p in s.get_outputs()])
# [os.path.join(s.subdir, p) for p in s.get_outputs()])
else:
raise InvalidArguments(
'Link_depends arguments must be strings, Files, '
'or a Custom Target, or lists thereof.')

def get_original_kwargs(self):
return self.kwargs

Expand Down Expand Up @@ -618,12 +647,7 @@ def process_kwargs(self, kwargs, environment):
for i in self.link_args:
if not isinstance(i, str):
raise InvalidArguments('Link_args arguments must be strings.')
self.link_depends = kwargs.get('link_depends', [])
if not isinstance(self.link_depends, list):
self.link_depends = [self.link_depends]
for i in self.link_depends:
if not isinstance(i, str):
raise InvalidArguments('Link_depends arguments must be strings.')
self.process_link_depends(kwargs.get('link_depends', []), environment)
# Target-specific include dirs must be added BEFORE include dirs from
# internal deps (added inside self.add_deps()) to override them.
inclist = kwargs.get('include_directories', [])
Expand Down Expand Up @@ -1260,13 +1284,11 @@ def process_kwargs(self, kwargs, environment):
self.vs_module_defs = File.from_absolute_file(path)
else:
self.vs_module_defs = File.from_source_file(environment.source_dir, self.subdir, path)
# link_depends can be an absolute path or relative to self.subdir
self.link_depends.append(path)
self.link_depends.append(self.vs_module_defs)
elif isinstance(path, File):
# When passing a generated file.
self.vs_module_defs = path
# link_depends can be an absolute path or relative to self.subdir
self.link_depends.append(path.absolute_path(environment.source_dir, environment.build_dir))
self.link_depends.append(path)
else:
raise InvalidArguments(
'Shared library vs_module_defs must be either a string, '
Expand Down
6 changes: 6 additions & 0 deletions test cases/linuxlike/3 linker script/bob.map.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
V1_0_0 {
global:
"@in@";
local:
*;
};
5 changes: 5 additions & 0 deletions test cases/linuxlike/3 linker script/copy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import shutil
import sys

if __name__ == '__main__':
shutil.copy(sys.argv[1], sys.argv[2])
48 changes: 48 additions & 0 deletions test cases/linuxlike/3 linker script/meson.build
Original file line number Diff line number Diff line change
@@ -1,8 +1,56 @@
project('linker script', 'c')

# Static map file
mapfile = 'bob.map'
vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile)

l = shared_library('bob', 'bob.c', link_args : vflag, link_depends : mapfile)
e = executable('prog', 'prog.c', link_with : l)
test('core', e)

# configure_file
conf = configuration_data()
conf.set('in', 'bobMcBob')
m = configure_file(
input : 'bob.map.in',
output : 'bob-conf.map',
configuration : conf,
)
vflag = '-Wl,--version-script,@0@'.format(m)

l = shared_library('bob-conf', 'bob.c', link_args : vflag, link_depends : m)
e = executable('prog-conf', 'prog.c', link_with : l)
test('core', e)

# custom_target
python = find_program('python3')
m = custom_target(
'bob-ct.map',
command : [python, '@INPUT0@', '@INPUT1@', 'bob-ct.map'],
input : ['copy.py', 'bob.map'],
output : 'bob-ct.map',
depend_files : 'bob.map',
)
vflag = '-Wl,--version-script,@0@'.format(m.full_path())

l = shared_library('bob-ct', ['bob.c', m], link_args : vflag, link_depends : m)
e = executable('prog-ct', 'prog.c', link_with : l)
test('core', e)

# File
mapfile = files('bob.map')
vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile[0])

l = shared_library('bob-files', 'bob.c', link_args : vflag, link_depends : mapfile)
e = executable('prog-files', 'prog.c', link_with : l)
test('core', e)

subdir('sub')

# With map file in subdir
mapfile = 'sub/foo.map'
vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile)

l = shared_library('bar', 'bob.c', link_args : vflag, link_depends : mapfile)
e = executable('prog-bar', 'prog.c', link_with : l)
test('core', e)
6 changes: 6 additions & 0 deletions test cases/linuxlike/3 linker script/sub/foo.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
V1_0_0 {
global:
"bobMcBob";
local:
*;
};
6 changes: 6 additions & 0 deletions test cases/linuxlike/3 linker script/sub/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
mapfile = 'foo.map'
vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile)

l = shared_library('foo', '../bob.c', link_args : vflag, link_depends : mapfile)
e = executable('prog-foo', '../prog.c', link_with : l)
test('core', e)

0 comments on commit 72fb966

Please sign in to comment.