diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2019-05-02 22:21:56 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-02 22:21:56 +0300 |
commit | 7059c47aad2ef28046ceb8566f0c1d2f98e03cb1 (patch) | |
tree | 01bdf4592080181fbd7c89f2350ae8573f8bb98a | |
parent | 70997ca969dab0de7a800af7f7c7d6c7e25cf4ac (diff) | |
parent | d74ab216db4e4eb7574813517b5a7edb44631e1c (diff) | |
download | meson-7059c47aad2ef28046ceb8566f0c1d2f98e03cb1.zip meson-7059c47aad2ef28046ceb8566f0c1d2f98e03cb1.tar.gz meson-7059c47aad2ef28046ceb8566f0c1d2f98e03cb1.tar.bz2 |
Merge pull request #5161 from TheQwertiest/feature/custom_target_link
Can link against custom_target[i]
11 files changed, 336 insertions, 12 deletions
diff --git a/docs/markdown/snippets/linkcustom.md b/docs/markdown/snippets/linkcustom.md index d6ee801..0cf45ad 100644 --- a/docs/markdown/snippets/linkcustom.md +++ b/docs/markdown/snippets/linkcustom.md @@ -1,6 +1,6 @@ ## Can link against custom targets -The output of `custom_target` can be used in `link_with` and +The output of `custom_target` and `custom_target[i]` can be used in `link_with` and `link_whole` keyword arguments. This is useful for integrating custom code generator steps, but note that there are many limitations: @@ -10,7 +10,8 @@ code generator steps, but note that there are many limitations: - The user is responsible for ensuring that the code produced by different toolchains are compatible. - - The custom target can only have one output file. + - `custom_target` may only be used when it has a single output file. + Use `custom_target[i]` when dealing with multiple output files. - The output file must have the correct file name extension. diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 4a4f7f4..e40bcbc 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -171,6 +171,8 @@ class Backend: mlog.warning('custom_target {!r} has more than one output! ' 'Using the first one.'.format(t.name)) filename = t.get_outputs()[0] + elif isinstance(t, build.CustomTargetIndex): + filename = t.get_outputs()[0] else: assert(isinstance(t, build.BuildTarget)) filename = t.get_filename() @@ -214,7 +216,7 @@ class Backend: return os.path.join(self.get_target_dir(target), link_lib) elif isinstance(target, build.StaticLibrary): return os.path.join(self.get_target_dir(target), target.get_filename()) - elif isinstance(target, build.CustomTarget): + elif isinstance(target, (build.CustomTarget, build.CustomTargetIndex)): if not target.is_linkable_target(): raise MesonException('Tried to link against custom target "%s", which is not linkable.' % target.name) return os.path.join(self.get_target_dir(target), target.get_filename()) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index d1bf1e5..afbd59b 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -249,9 +249,15 @@ class Vs2010Backend(backends.Backend): all_deps[d.get_id()] = d elif isinstance(target, build.BuildTarget): for ldep in target.link_targets: - all_deps[ldep.get_id()] = ldep + if isinstance(ldep, build.CustomTargetIndex): + all_deps[ldep.get_id()] = ldep.target + else: + all_deps[ldep.get_id()] = ldep for ldep in target.link_whole_targets: - all_deps[ldep.get_id()] = ldep + if isinstance(ldep, build.CustomTargetIndex): + all_deps[ldep.get_id()] = ldep.target + else: + all_deps[ldep.get_id()] = ldep for obj_id, objdep in self.get_obj_target_deps(target.objects): all_deps[obj_id] = objdep for gendep in target.get_generated_sources(): @@ -1111,7 +1117,11 @@ class Vs2010Backend(backends.Backend): # Add more libraries to be linked if needed for t in target.get_dependencies(): - lobj = self.build.targets[t.get_id()] + if isinstance(t, build.CustomTargetIndex): + # We don't need the actual project here, just the library name + lobj = t + else: + lobj = self.build.targets[t.get_id()] linkname = os.path.join(down, self.get_target_filename_for_linking(lobj)) if t in target.link_whole_targets: # /WHOLEARCHIVE:foo must go into AdditionalOptions diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 4c8e50b..1fad9e0 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -576,7 +576,7 @@ class BuildTarget(Target): if self.link_targets or self.link_whole_targets: extra = set() for t in itertools.chain(self.link_targets, self.link_whole_targets): - if isinstance(t, CustomTarget): + if isinstance(t, CustomTarget) or isinstance(t, CustomTargetIndex): continue # We can't know anything about these. for name, compiler in t.compilers.items(): if name in clink_langs: @@ -1066,7 +1066,7 @@ You probably should put it in link_with instead.''') def link(self, target): for t in listify(target, unholder=True): - if not isinstance(t, Target): + if not isinstance(t, (Target, CustomTargetIndex)): raise InvalidArguments('{!r} is not a target.'.format(t)) if not t.is_linkable_target(): raise InvalidArguments('Link target {!r} is not linkable.'.format(t)) @@ -1074,13 +1074,13 @@ You probably should put it in link_with instead.''') msg = "Can't link non-PIC static library {!r} into shared library {!r}. ".format(t.name, self.name) msg += "Use the 'pic' option to static_library to build with PIC." raise InvalidArguments(msg) - if not isinstance(t, CustomTarget) and self.is_cross != t.is_cross: + if not isinstance(t, (CustomTarget, CustomTargetIndex)) and self.is_cross != t.is_cross: raise InvalidArguments('Tried to mix cross built and native libraries in target {!r}'.format(self.name)) self.link_targets.append(t) def link_whole(self, target): for t in listify(target, unholder=True): - if isinstance(t, CustomTarget): + if isinstance(t, (CustomTarget, CustomTargetIndex)): if not t.is_linkable_target(): raise InvalidArguments('Custom target {!r} is not linkable.'.format(t)) if not t.get_filename().endswith('.a'): @@ -1091,7 +1091,7 @@ You probably should put it in link_with instead.''') msg = "Can't link non-PIC static library {!r} into shared library {!r}. ".format(t.name, self.name) msg += "Use the 'pic' option to static_library to build with PIC." raise InvalidArguments(msg) - if not isinstance(t, CustomTarget) and self.is_cross != t.is_cross: + if not isinstance(t, (CustomTarget, CustomTargetIndex)) and self.is_cross != t.is_cross: raise InvalidArguments('Tried to mix cross built and native libraries in target {!r}'.format(self.name)) self.link_whole_targets.append(t) @@ -1168,7 +1168,7 @@ You probably should put it in link_with instead.''') # Check if any of the internal libraries this target links to were # written in this language for link_target in itertools.chain(self.link_targets, self.link_whole_targets): - if isinstance(link_target, CustomTarget): + if isinstance(link_target, (CustomTarget, CustomTargetIndex)): continue for language in link_target.compilers: if language not in langs: @@ -2259,6 +2259,26 @@ class CustomTargetIndex: def get_subdir(self): return self.target.get_subdir() + def get_filename(self): + return self.output + + def get_id(self): + return self.target.get_id() + + def get_all_link_deps(self): + return self.target.get_all_link_deps() + + def get_link_deps_mapping(self, prefix, environment): + return self.target.get_link_deps_mapping(prefix, environment) + + def get_link_dep_subdirs(self): + return self.target.get_link_dep_subdirs() + + def is_linkable_target(self): + suf = os.path.splitext(self.output)[-1] + if suf == '.a' or suf == '.dll' or suf == '.lib' or suf == '.so': + return True + class ConfigureFile: def __init__(self, subdir, sourcename, targetname, configuration_data): diff --git a/test cases/common/216 link custom/meson.build b/test cases/common/216 link custom/meson.build index 5af27cd..c8d3a6d 100644 --- a/test cases/common/216 link custom/meson.build +++ b/test cases/common/216 link custom/meson.build @@ -16,6 +16,8 @@ clib = custom_target('linkcustom', '-o', '@OUTPUT@', '--private-dir', '@PRIVATE_DIR@'] + cc.cmd_array()) +# custom_target tests + exe = executable('prog', 'prog.c', link_with: clib) test('linkcustom', exe) @@ -33,3 +35,23 @@ d2 = declare_dependency(link_whole: clib) exe4 = executable('prog4', 'prog.c', dependencies: d2) test('linkwhole2', exe2) + +# custom_target[i] tests + +exe_i = executable('prog_i', 'prog.c', link_with: clib[0]) +test('linkcustom', exe_i) + +d_i = declare_dependency(link_with: clib[0]) + +exe2_i = executable('prog2_i', 'prog.c', dependencies: d_i) +test('linkcustom2_i', exe2_i) + +# Link whole tests + +exe3_i = executable('prog3_i', 'prog.c', link_whole: clib[0]) +test('linkwhole', exe) + +d2_i = declare_dependency(link_whole: clib[0]) + +exe4_i = executable('prog4_i', 'prog.c', dependencies: d2_i) +test('linkwhole2_i', exe2_i) diff --git a/test cases/common/217 link custom_i single from multiple/generate_conflicting_stlibs.py b/test cases/common/217 link custom_i single from multiple/generate_conflicting_stlibs.py new file mode 100644 index 0000000..42d6631 --- /dev/null +++ b/test cases/common/217 link custom_i single from multiple/generate_conflicting_stlibs.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 + +import shutil, sys, subprocess, argparse, pathlib + +parser = argparse.ArgumentParser() + +parser.add_argument('--private-dir', required=True) +parser.add_argument('-o', nargs='+', required=True) +parser.add_argument('cmparr', nargs='+') + +contents = [''' +int flob() { + return 0; +} +''', ''' +int flob() { + return 1; +} +'''] + +def generate_lib_gnulike(outfile, c_file, private_dir, compiler_array): + if shutil.which('ar'): + static_linker = 'ar' + elif shutil.which('llvm-ar'): + static_linker = 'llvm-ar' + elif shutil.which('gcc-ar'): + static_linker = 'gcc-ar' + else: + sys.exit('Could not detect a static linker.') + o_file = c_file.with_suffix('.o') + compile_cmd = compiler_array + ['-c', '-g', '-O2', '-o', str(o_file), str(c_file)] + subprocess.check_call(compile_cmd) + out_file = pathlib.Path(outfile) + if out_file.exists(): + out_file.unlink() + link_cmd = [static_linker, 'csr', outfile, str(o_file)] + subprocess.check_call(link_cmd) + return 0 + + +def generate_lib_msvc(outfile, c_file, private_dir, compiler_array): + static_linker = 'lib' + o_file = c_file.with_suffix('.obj') + compile_cmd = compiler_array + ['/MDd', + '/nologo', + '/ZI', + '/Ob0', + '/Od', + '/c', + '/Fo' + str(o_file), + str(c_file)] + subprocess.check_call(compile_cmd) + out_file = pathlib.Path(outfile) + if out_file.exists(): + out_file.unlink() + link_cmd = [static_linker, + '/nologo', + '/OUT:' + str(outfile), + str(o_file)] + subprocess.check_call(link_cmd) + return 0 + +def generate_lib(outfiles, private_dir, compiler_array): + private_dir = pathlib.Path(private_dir) + if not private_dir.exists(): + private_dir.mkdir() + + for i, content in enumerate(contents): + c_file = private_dir / ('flob_' + str(i + 1) + '.c') + c_file.write_text(content) + outfile = outfiles[i] + + cl_found = False + for cl_arg in compiler_array: + if (cl_arg.endswith('cl') or cl_arg.endswith('cl.exe')) and 'clang-cl' not in cl_arg: + ret = generate_lib_msvc(outfile, c_file, private_dir, compiler_array) + if ret > 0: + return ret + else: + cl_found = True + break + if not cl_found: + ret = generate_lib_gnulike(outfile, c_file, private_dir, compiler_array) + if ret > 0: + return ret + return 0 + +if __name__ == '__main__': + options = parser.parse_args() + sys.exit(generate_lib(options.o, options.private_dir, options.cmparr)) diff --git a/test cases/common/217 link custom_i single from multiple/meson.build b/test cases/common/217 link custom_i single from multiple/meson.build new file mode 100644 index 0000000..eee1fe1 --- /dev/null +++ b/test cases/common/217 link custom_i single from multiple/meson.build @@ -0,0 +1,37 @@ +project('linkcustom', 'c') + +# This would require passing the static linker to the build script or having +# it detect it by itself. I'm too lazy to implement it now and it is not +# really needed for testing that custom targets work. It is the responsibility +# of the custom target to produce things in the correct format. +assert(not meson.is_cross_build(), + 'MESON_SKIP_TEST cross checking not implemented.') + +cc = meson.get_compiler('c') +genprog = find_program('generate_conflicting_stlibs.py') + +clib = custom_target('linkcustom', + output: ['libflob_1.a', 'libflob_2.a'], + command: [genprog, + '-o', '@OUTPUT@', + '--private-dir', '@PRIVATE_DIR@'] + cc.cmd_array()) + +clib_2 = clib[1] + +exe = executable('prog', 'prog.c', link_with: clib_2) +test('linkcustom', exe) + +d = declare_dependency(link_with: clib_2) + +exe2 = executable('prog2', 'prog.c', dependencies: d) +test('linkcustom2', exe2) + +# Link whole tests + +exe3 = executable('prog3', 'prog.c', link_whole: clib_2) +test('linkwhole', exe) + +d2 = declare_dependency(link_whole: clib_2) + +exe4 = executable('prog4', 'prog.c', dependencies: d2) +test('linkwhole2', exe2) diff --git a/test cases/common/217 link custom_i single from multiple/prog.c b/test cases/common/217 link custom_i single from multiple/prog.c new file mode 100644 index 0000000..8013034 --- /dev/null +++ b/test cases/common/217 link custom_i single from multiple/prog.c @@ -0,0 +1,5 @@ +int flob(); + +int main(int argc, char **argv) { + return (flob() == 1 ? 0 : 1); +} diff --git a/test cases/common/218 link custom_i multiple from multiple/generate_stlibs.py b/test cases/common/218 link custom_i multiple from multiple/generate_stlibs.py new file mode 100644 index 0000000..5292006 --- /dev/null +++ b/test cases/common/218 link custom_i multiple from multiple/generate_stlibs.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 + +import shutil, sys, subprocess, argparse, pathlib + +parser = argparse.ArgumentParser() + +parser.add_argument('--private-dir', required=True) +parser.add_argument('-o', nargs='+', required=True) +parser.add_argument('cmparr', nargs='+') + +contents = ['''#include<stdio.h> + +void flob_1() { + printf("Now flobbing #1.\\n"); +} +''', '''#include<stdio.h> + +void flob_2() { + printf("Now flobbing #2.\\n"); +} +'''] + +def generate_lib_gnulike(outfile, c_file, private_dir, compiler_array): + if shutil.which('ar'): + static_linker = 'ar' + elif shutil.which('llvm-ar'): + static_linker = 'llvm-ar' + elif shutil.which('gcc-ar'): + static_linker = 'gcc-ar' + else: + sys.exit('Could not detect a static linker.') + o_file = c_file.with_suffix('.o') + compile_cmd = compiler_array + ['-c', '-g', '-O2', '-o', str(o_file), str(c_file)] + subprocess.check_call(compile_cmd) + out_file = pathlib.Path(outfile) + if out_file.exists(): + out_file.unlink() + link_cmd = [static_linker, 'csr', outfile, str(o_file)] + subprocess.check_call(link_cmd) + return 0 + + +def generate_lib_msvc(outfile, c_file, private_dir, compiler_array): + static_linker = 'lib' + o_file = c_file.with_suffix('.obj') + compile_cmd = compiler_array + ['/MDd', + '/nologo', + '/ZI', + '/Ob0', + '/Od', + '/c', + '/Fo' + str(o_file), + str(c_file)] + subprocess.check_call(compile_cmd) + out_file = pathlib.Path(outfile) + if out_file.exists(): + out_file.unlink() + link_cmd = [static_linker, + '/nologo', + '/OUT:' + str(outfile), + str(o_file)] + subprocess.check_call(link_cmd) + return 0 + +def generate_lib(outfiles, private_dir, compiler_array): + private_dir = pathlib.Path(private_dir) + if not private_dir.exists(): + private_dir.mkdir() + + for i, content in enumerate(contents): + c_file = private_dir / ('flob_' + str(i + 1) + '.c') + c_file.write_text(content) + outfile = outfiles[i] + + cl_found = False + for cl_arg in compiler_array: + if (cl_arg.endswith('cl') or cl_arg.endswith('cl.exe')) and 'clang-cl' not in cl_arg: + ret = generate_lib_msvc(outfile, c_file, private_dir, compiler_array) + if ret > 0: + return ret + else: + cl_found = True + break + if not cl_found: + ret = generate_lib_gnulike(outfile, c_file, private_dir, compiler_array) + if ret > 0: + return ret + return 0 + +if __name__ == '__main__': + options = parser.parse_args() + sys.exit(generate_lib(options.o, options.private_dir, options.cmparr)) diff --git a/test cases/common/218 link custom_i multiple from multiple/meson.build b/test cases/common/218 link custom_i multiple from multiple/meson.build new file mode 100644 index 0000000..e5236e5 --- /dev/null +++ b/test cases/common/218 link custom_i multiple from multiple/meson.build @@ -0,0 +1,37 @@ +project('linkcustom', 'c') + +# This would require passing the static linker to the build script or having +# it detect it by itself. I'm too lazy to implement it now and it is not +# really needed for testing that custom targets work. It is the responsibility +# of the custom target to produce things in the correct format. +assert(not meson.is_cross_build(), + 'MESON_SKIP_TEST cross checking not implemented.') + +cc = meson.get_compiler('c') +genprog = find_program('generate_stlibs.py') + +clib = custom_target('linkcustom', + output: ['libflob_1.a', 'libflob_2.a'], + command: [genprog, + '-o', '@OUTPUT@', + '--private-dir', '@PRIVATE_DIR@'] + cc.cmd_array()) + +clibs = [clib[0], clib[1]] + +exe = executable('prog', 'prog.c', link_with: clibs) +test('linkcustom', exe) + +d = declare_dependency(link_with: clibs) + +exe2 = executable('prog2', 'prog.c', dependencies: d) +test('linkcustom2', exe2) + +# Link whole tests + +exe3 = executable('prog3', 'prog.c', link_whole: clibs) +test('linkwhole', exe) + +d2 = declare_dependency(link_whole: clibs) + +exe4 = executable('prog4', 'prog.c', dependencies: d2) +test('linkwhole2', exe2) diff --git a/test cases/common/218 link custom_i multiple from multiple/prog.c b/test cases/common/218 link custom_i multiple from multiple/prog.c new file mode 100644 index 0000000..51effe6 --- /dev/null +++ b/test cases/common/218 link custom_i multiple from multiple/prog.c @@ -0,0 +1,8 @@ +void flob_1(); +void flob_2(); + +int main(int argc, char **argv) { + flob_1(); + flob_2(); + return 0; +} |