From 12d4031f52a8c09014a840b2cf263478602618d0 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Wed, 3 Jul 2019 13:32:24 -0400 Subject: [PATCH] Add alias_target() function --- data/syntax-highlighting/vim/syntax/meson.vim | 1 + docs/markdown/Reference-manual.md | 14 +++++++++ docs/markdown/snippets/alias_target.md | 12 ++++++++ mesonbuild/ast/interpreter.py | 1 + mesonbuild/backend/ninjabackend.py | 30 ++++++++++++------- mesonbuild/backend/vs2010backend.py | 7 ++++- mesonbuild/build.py | 4 +++ mesonbuild/interpreter.py | 26 ++++++++++++---- run_unittests.py | 13 ++++++++ test cases/unit/62 alias target/main.c | 3 ++ test cases/unit/62 alias target/meson.build | 15 ++++++++++ 11 files changed, 109 insertions(+), 17 deletions(-) create mode 100644 docs/markdown/snippets/alias_target.md create mode 100644 test cases/unit/62 alias target/main.c create mode 100644 test cases/unit/62 alias target/meson.build diff --git a/data/syntax-highlighting/vim/syntax/meson.vim b/data/syntax-highlighting/vim/syntax/meson.vim index 85acf4301..94936c80b 100644 --- a/data/syntax-highlighting/vim/syntax/meson.vim +++ b/data/syntax-highlighting/vim/syntax/meson.vim @@ -69,6 +69,7 @@ syn keyword mesonBuiltin \ add_project_arguments \ add_project_link_arguments \ add_test_setup + \ alias_target \ assert \ benchmark \ both_libraries diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 3793ce358..195c451d5 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -119,6 +119,20 @@ Note that all these options are also available while running the `meson test` script for running tests instead of `ninja test` or `msbuild RUN_TESTS.vcxproj`, etc depending on the backend. +### alias_target + +``` meson +runtarget alias_target(target_name, dep1, ...) +``` + +Since *0.52.0* + +This function creates a new top-level target. Like all top-level targets, this +integrates with the selected backend. For instance, with Ninja you can +run it as `ninja target_name`. This is a dummy target that does not execute any +command, but ensures that all dependencies are built. Dependencies can be any +build target (e.g. return value of executable(), custom_target(), etc) + ### assert() ``` meson diff --git a/docs/markdown/snippets/alias_target.md b/docs/markdown/snippets/alias_target.md new file mode 100644 index 000000000..129730d03 --- /dev/null +++ b/docs/markdown/snippets/alias_target.md @@ -0,0 +1,12 @@ +## alias_target + +``` meson +runtarget alias_target(target_name, dep1, ...) +``` + +This function creates a new top-level target. Like all top-level targets, this +integrates with the selected backend. For instance, with Ninja you can +run it as `ninja target_name`. This is a dummy target that does not execute any +command, but ensures that all dependencies are built. Dependencies can be any +build target (e.g. return value of executable(), custom_target(), etc) + diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py index 535471066..0e490abdf 100644 --- a/mesonbuild/ast/interpreter.py +++ b/mesonbuild/ast/interpreter.py @@ -117,6 +117,7 @@ class AstInterpreter(interpreterbase.InterpreterBase): 'add_test_setup': self.func_do_nothing, 'find_library': self.func_do_nothing, 'subdir_done': self.func_do_nothing, + 'alias_target': self.func_do_nothing, }) def func_do_nothing(self, node, args, kwargs): diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index b830e377e..b57a783ce 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -702,6 +702,13 @@ int dummy; self.add_build(elem) self.processed_targets[target.get_id()] = True + def build_run_target_name(self, target): + if target.subproject != '': + subproject_prefix = '{}@@'.format(target.subproject) + else: + subproject_prefix = '' + return '{}{}'.format(subproject_prefix, target.name) + def generate_run_target(self, target): cmd = self.environment.get_build_command() + ['--internal', 'commandrunner'] deps = self.unwrap_dep_list(target) @@ -718,12 +725,6 @@ int dummy; arg_strings.append(os.path.join(self.environment.get_build_dir(), relfname)) else: raise AssertionError('Unreachable code in generate_run_target: ' + str(i)) - if target.subproject != '': - subproject_prefix = '{}@@'.format(target.subproject) - else: - subproject_prefix = '' - target_name = 'meson-{}{}'.format(subproject_prefix, target.name) - elem = NinjaBuildElement(self.all_outputs, target_name, 'CUSTOM_COMMAND', []) cmd += [self.environment.get_source_dir(), self.environment.get_build_dir(), target.subdir] + self.environment.get_build_command() @@ -756,14 +757,21 @@ int dummy; cmd.append(target.command) cmd += arg_strings + if texe: + target_name = 'meson-{}'.format(self.build_run_target_name(target)) + elem = NinjaBuildElement(self.all_outputs, target_name, 'CUSTOM_COMMAND', []) + elem.add_item('COMMAND', cmd) + elem.add_item('description', 'Running external command %s.' % target.name) + elem.add_item('pool', 'console') + # Alias that runs the target defined above with the name the user specified + self.create_target_alias(target_name) + else: + target_name = self.build_run_target_name(target) + elem = NinjaBuildElement(self.all_outputs, target_name, 'phony', []) + elem.add_dep(deps) cmd = self.replace_paths(target, cmd) - elem.add_item('COMMAND', cmd) - elem.add_item('description', 'Running external command %s.' % target.name) - elem.add_item('pool', 'console') self.add_build(elem) - # Alias that runs the target defined above with the name the user specified - self.create_target_alias(target_name) self.processed_targets[target.get_id()] = True def generate_coverage_command(self, elem, outputs): diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 86a7f8307..0ada94138 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -516,7 +516,12 @@ class Vs2010Backend(backends.Backend): def gen_run_target_vcxproj(self, target, ofname, guid): root = self.create_basic_crap(target, guid) - cmd_raw = [target.command] + target.args + if not target.command: + # FIXME: This is an alias target that doesn't run any command, there + # is probably a better way than running a this dummy command. + cmd_raw = python_command + ['-c', 'exit'] + else: + cmd_raw = [target.command] + target.args cmd = python_command + \ [os.path.join(self.environment.get_script_dir(), 'commandrunner.py'), self.environment.get_build_dir(), diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 55b162974..2e23a59bc 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2205,6 +2205,10 @@ class RunTarget(Target): def type_suffix(self): return "@run" +class AliasTarget(RunTarget): + def __init__(self, name, dependencies, subdir, subproject): + super().__init__(name, '', [], dependencies, subdir, subproject) + class Jar(BuildTarget): known_kwargs = known_jar_kwargs diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index f5bb4e541..7b7c100e5 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -863,10 +863,9 @@ class CustomTargetHolder(TargetHolder): return IncludeDirsHolder(build.IncludeDirs('', [], False, [os.path.join('@BUILD_ROOT@', self.interpreter.backend.get_target_dir(self.held_object))])) -class RunTargetHolder(InterpreterObject, ObjectHolder): - def __init__(self, name, command, args, dependencies, subdir, subproject): - InterpreterObject.__init__(self) - ObjectHolder.__init__(self, build.RunTarget(name, command, args, dependencies, subdir, subproject)) +class RunTargetHolder(TargetHolder): + def __init__(self, target, interp): + super().__init__(target, interp) def __repr__(self): r = '<{} {}: {}>' @@ -2103,6 +2102,7 @@ class Interpreter(InterpreterBase): 'add_project_link_arguments': self.func_add_project_link_arguments, 'add_test_setup': self.func_add_test_setup, 'add_languages': self.func_add_languages, + 'alias_target': self.func_alias_target, 'assert': self.func_assert, 'benchmark': self.func_benchmark, 'build_target': self.func_build_target, @@ -3300,7 +3300,23 @@ This will become a hard error in the future.''' % kwargs['input'], location=self raise InterpreterException('Depends items must be build targets.') cleaned_deps.append(d) command, *cmd_args = cleaned_args - tg = RunTargetHolder(name, command, cmd_args, cleaned_deps, self.subdir, self.subproject) + tg = RunTargetHolder(build.RunTarget(name, command, cmd_args, cleaned_deps, self.subdir, self.subproject), self) + self.add_target(name, tg.held_object) + return tg + + @FeatureNew('alias_target', '0.52.0') + @noKwargs + def func_alias_target(self, node, args, kwargs): + if len(args) < 2: + raise InvalidCode('alias_target takes at least 2 arguments.') + name = args[0] + if not isinstance(name, str): + raise InterpreterException('First argument must be a string.') + deps = listify(args[1:], unholder=True) + for d in deps: + if not isinstance(d, (build.BuildTarget, build.CustomTarget)): + raise InterpreterException('Depends items must be build targets.') + tg = RunTargetHolder(build.AliasTarget(name, deps, self.subdir, self.subproject), self) self.add_target(name, tg.held_object) return tg diff --git a/run_unittests.py b/run_unittests.py index d9281cfca..c5a6d4f22 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -3747,6 +3747,19 @@ recommended as it is not supported on some platforms''') testdir = os.path.join(self.unit_test_dir, '61 cmake parser') self.init(testdir, extra_args=['-Dcmake_prefix_path=' + os.path.join(testdir, 'prefix')]) + def test_alias_target(self): + if self.backend is Backend.vs: + # FIXME: This unit test is broken with vs backend, needs investigation + raise unittest.SkipTest('Skipping alias_target test with {} backend'.format(self.backend.name)) + testdir = os.path.join(self.unit_test_dir, '62 alias target') + self.init(testdir) + self.build() + self.assertPathDoesNotExist(os.path.join(self.builddir, 'prog' + exe_suffix)) + self.assertPathDoesNotExist(os.path.join(self.builddir, 'hello.txt')) + self.run_target('build-all') + self.assertPathExists(os.path.join(self.builddir, 'prog' + exe_suffix)) + self.assertPathExists(os.path.join(self.builddir, 'hello.txt')) + class FailureTests(BasePlatformTests): ''' Tests that test failure conditions. Build files here should be dynamically diff --git a/test cases/unit/62 alias target/main.c b/test cases/unit/62 alias target/main.c new file mode 100644 index 000000000..0fb4389f7 --- /dev/null +++ b/test cases/unit/62 alias target/main.c @@ -0,0 +1,3 @@ +int main(int argc, char *argv[]) { + return 0; +} diff --git a/test cases/unit/62 alias target/meson.build b/test cases/unit/62 alias target/meson.build new file mode 100644 index 000000000..6934cc79b --- /dev/null +++ b/test cases/unit/62 alias target/meson.build @@ -0,0 +1,15 @@ +project('alias target', 'c') + +python3 = import('python').find_installation() + +exe_target = executable('prog', 'main.c', + build_by_default : false) + +custom_target = custom_target('custom-target', + output : 'hello.txt', + command : [python3, '-c', 'print("hello")'], + capture : true, + build_by_default : false +) + +alias_target('build-all', [exe_target, custom_target])