diff options
author | Daniel Moody <daniel.moody@mongodb.com> | 2022-04-14 20:49:51 (GMT) |
---|---|---|
committer | Daniel Moody <daniel.moody@mongodb.com> | 2022-04-14 20:49:51 (GMT) |
commit | 4bb4178b19294ddc0bc1ec801d28c0bb2509a4c5 (patch) | |
tree | c5181ed73738d5f4a63ce973478531ac2c8312cc | |
parent | a3602d5c9d9fea87c3335aa714c34b35bf698dd0 (diff) | |
download | SCons-4bb4178b19294ddc0bc1ec801d28c0bb2509a4c5.zip SCons-4bb4178b19294ddc0bc1ec801d28c0bb2509a4c5.tar.gz SCons-4bb4178b19294ddc0bc1ec801d28c0bb2509a4c5.tar.bz2 |
Add ninja alias option to label generated source files
-rw-r--r-- | SCons/Tool/ninja/NinjaState.py | 74 | ||||
-rw-r--r-- | SCons/Tool/ninja/__init__.py | 6 | ||||
-rw-r--r-- | SCons/Tool/ninja/ninja.xml | 14 | ||||
-rw-r--r-- | test/ninja/generated_sources_alias.py | 82 | ||||
-rw-r--r-- | test/ninja/ninja-fixture/gen_source.c | 9 | ||||
-rw-r--r-- | test/ninja/ninja_test_sconscripts/sconstruct_generated_sources_alias | 41 |
6 files changed, 199 insertions, 27 deletions
diff --git a/SCons/Tool/ninja/NinjaState.py b/SCons/Tool/ninja/NinjaState.py index fff60be..c9d8a89 100644 --- a/SCons/Tool/ninja/NinjaState.py +++ b/SCons/Tool/ninja/NinjaState.py @@ -360,26 +360,56 @@ class NinjaState: kwargs['pool'] = 'local_pool' ninja.rule(rule, **kwargs) - generated_source_files = sorted({ - output - # First find builds which have header files in their outputs. - for build in self.builds.values() - if self.has_generated_sources(build["outputs"]) - for output in build["outputs"] - # Collect only the header files from the builds with them - # in their output. We do this because is_generated_source - # returns True if it finds a header in any of the outputs, - # here we need to filter so we only have the headers and - # not the other outputs. - if self.is_generated_source(output) - }) - - if generated_source_files: - ninja.build( - outputs="_generated_sources", - rule="phony", - implicit=generated_source_files + # If the user supplied an alias to determine generated sources, use that, otherwise + # determine what the generated sources are dynamically. + generated_sources_alias = self.env.get('NINJA_GENERATED_SOURCE_ALIAS_NAME') + generated_sources_build = None + + if generated_sources_alias: + generated_sources_build = self.builds.get(generated_sources_alias) + if generated_sources_build is None or generated_sources_build["rule"] != 'phony': + raise Exception( + "ERROR: 'NINJA_GENERATED_SOURCE_ALIAS_NAME' set, but no matching Alias object found." + ) + + if generated_sources_alias and generated_sources_build: + generated_source_files = sorted( + [] if not generated_sources_build else generated_sources_build['implicit'] ) + def check_generated_source_deps(build): + return ( + build != generated_sources_build + and set(build["outputs"]).isdisjoint(generated_source_files) + ) + else: + generated_sources_build = None + generated_source_files = sorted({ + output + # First find builds which have header files in their outputs. + for build in self.builds.values() + if self.has_generated_sources(build["outputs"]) + for output in build["outputs"] + # Collect only the header files from the builds with them + # in their output. We do this because is_generated_source + # returns True if it finds a header in any of the outputs, + # here we need to filter so we only have the headers and + # not the other outputs. + if self.is_generated_source(output) + }) + + if generated_source_files: + generated_sources_alias = "_ninja_generated_sources" + ninja.build( + outputs=generated_sources_alias, + rule="phony", + implicit=generated_source_files + ) + def check_generated_source_deps(build): + return ( + not build["rule"] == "INSTALL" + and set(build["outputs"]).isdisjoint(generated_source_files) + and set(build.get("implicit", [])).isdisjoint(generated_source_files) + ) template_builders = [] scons_compiledb = False @@ -402,9 +432,7 @@ class NinjaState: # cycle. if ( generated_source_files - and not build["rule"] == "INSTALL" - and set(build["outputs"]).isdisjoint(generated_source_files) - and set(build.get("implicit", [])).isdisjoint(generated_source_files) + and check_generated_source_deps(build) ): # Make all non-generated source targets depend on # _generated_sources. We use order_only for generated @@ -413,7 +441,7 @@ class NinjaState: # sure that all of these sources are generated before # other builds. order_only = build.get("order_only", []) - order_only.append("_generated_sources") + order_only.append(generated_sources_alias) build["order_only"] = order_only if "order_only" in build: build["order_only"].sort() diff --git a/SCons/Tool/ninja/__init__.py b/SCons/Tool/ninja/__init__.py index 3c72ee4..3dde0fa 100644 --- a/SCons/Tool/ninja/__init__.py +++ b/SCons/Tool/ninja/__init__.py @@ -276,8 +276,8 @@ def generate(env): provider = gen_get_response_file_command(env, rule, tool) env.NinjaRuleMapping("${" + var + "}", provider) - # some of these construction vars could be generators, e.g. - # CommandGeneratorAction, so if the var is not a string, we + # some of these construction vars could be generators, e.g. + # CommandGeneratorAction, so if the var is not a string, we # can't parse the generated string. if isinstance(env.get(var), str): env.NinjaRuleMapping(env.get(var, None), provider) @@ -297,7 +297,7 @@ def generate(env): # requires that all generated sources are added as order_only # dependencies to any builds that *might* use them. # TODO: switch to using SCons to help determine this (Github Issue #3624) - env["NINJA_GENERATED_SOURCE_SUFFIXES"] = [".h", ".hpp"] + env["NINJA_GENERATED_SOURCE_SUFFIXES"] = env.get('NINJA_GENERATED_SOURCE_SUFFIXES', [".h", ".hpp"]) # Force ARCOM so use 's' flag on ar instead of separately running ranlib ninja_hack_arcom(env) diff --git a/SCons/Tool/ninja/ninja.xml b/SCons/Tool/ninja/ninja.xml index 51ff435..9bf4e10 100644 --- a/SCons/Tool/ninja/ninja.xml +++ b/SCons/Tool/ninja/ninja.xml @@ -65,6 +65,7 @@ See its __doc__ string for a discussion of the format. <item>NINJA_ENV_VAR_CACHE</item> <item>NINJA_FILE_NAME</item> <item>NINJA_GENERATED_SOURCE_SUFFIXES</item> + <item>NINJA_GENERATED_SOURCE_ALIAS_NAME</item> <item>NINJA_MSVC_DEPS_PREFIX</item> <item>NINJA_POOL</item> <item>NINJA_REGENERATE_DEPS</item> @@ -198,6 +199,17 @@ python -m pip install ninja </summary> </cvar> + <cvar name="NINJA_GENERATED_SOURCE_ALIAS_NAME"> + <summary> + <para> + A string matching the name of a user defined alias which represents a list of all generated sources. + This will prevent the auto-detection of generated sources from &cv-NINJA_GENERATED_SOURCE_SUFFIXES;. + Then all other source files will be made to depend on this in the &ninja; build file, forcing the + generated sources to be built first. + </para> + </summary> + </cvar> + <cvar name="NINJA_MSVC_DEPS_PREFIX"> <summary> <para> @@ -262,7 +274,7 @@ python -m pip install ninja </para> <para> - If not explicitly set, &SCons; will generate this dynamically from the + If not explicitly set, &SCons; will generate this dynamically from the execution environment stored in the current &consenv; (e.g. <literal>env['ENV']</literal>) where those values differ from the existing shell.. diff --git a/test/ninja/generated_sources_alias.py b/test/ninja/generated_sources_alias.py new file mode 100644 index 0000000..2c4ed36 --- /dev/null +++ b/test/ninja/generated_sources_alias.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +# +# Copyright The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +import os + +import TestSCons +from TestCmd import IS_WINDOWS + +test = TestSCons.TestSCons() + +try: + import ninja +except ImportError: + test.skip_test("Could not find module in python") + +_python_ = TestSCons._python_ +_exe = TestSCons._exe + +ninja_bin = os.path.abspath(os.path.join( + ninja.BIN_DIR, + 'ninja' + _exe)) + +test.dir_fixture('ninja-fixture') + +test.file_fixture('ninja_test_sconscripts/sconstruct_generated_sources_alias', 'SConstruct') + +for alias_value in [0, 1]: + + # Gen with source alias + test.run(arguments=['--disable-execute-ninja', f'USE_GEN_SOURCE_ALIAS={alias_value}'], stdout=None) + test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja']) + test.must_not_exist(test.workpath('gen_source' + _exe)) + + # run ninja independently + program = test.workpath('run_ninja_env.bat') if IS_WINDOWS else ninja_bin + test.run(program=program, stdout=None) + test.run(program=test.workpath('gen_source' + _exe), stdout="4") + + # generate simple build + test.run(arguments=[f'USE_GEN_SOURCE_ALIAS={alias_value}'], stdout=None) + test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja']) + test.must_contain_all(test.stdout(), 'Executing:') + test.must_contain_all(test.stdout(), 'ninja%(_exe)s -f' % locals()) + test.run(program=test.workpath('gen_source' + _exe), stdout="4") + + # clean build and ninja files + test.run(arguments=[f'USE_GEN_SOURCE_ALIAS={alias_value}', '-c'], stdout=None) + test.must_contain_all_lines(test.stdout(), [ + 'Removed generated_header1.htest', + 'Removed generated_header2.htest', + 'Removed generated_header2.c', + 'Removed build.ninja']) + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/ninja/ninja-fixture/gen_source.c b/test/ninja/ninja-fixture/gen_source.c new file mode 100644 index 0000000..38ac3fe --- /dev/null +++ b/test/ninja/ninja-fixture/gen_source.c @@ -0,0 +1,9 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "generated_header2.htest" + +int main(int argc, char *argv[]) +{ + printf("%d", func2()); +} diff --git a/test/ninja/ninja_test_sconscripts/sconstruct_generated_sources_alias b/test/ninja/ninja_test_sconscripts/sconstruct_generated_sources_alias new file mode 100644 index 0000000..3661bcc --- /dev/null +++ b/test/ninja/ninja_test_sconscripts/sconstruct_generated_sources_alias @@ -0,0 +1,41 @@ +import SCons + +SetOption('experimental','ninja') +DefaultEnvironment(tools=[]) + +vars = Variables() +vars.Add("USE_GEN_SOURCE_ALIAS") + +env = Environment(variables=vars) +my_gen_alias = 'my_ninja_gen_sources' + + +env['SCANNERS'][0].add_scanner('.htest', SCons.Tool.CScanner) +env.Command('out.txt', [], f'echo USE_GEN_SOURCE_ALIAS={env["USE_GEN_SOURCE_ALIAS"]}') + +if env['USE_GEN_SOURCE_ALIAS'] == "1": + env['NINJA_GENERATED_SOURCE_ALIAS_NAME'] = my_gen_alias +else: + env['NINJA_GENERATED_SOURCE_SUFFIXES'] = ['.htest'] + +env.Tool('ninja') + +env.Alias(my_gen_alias, env.Textfile('generated_header1.htest', [ + '#pragma once', + 'int func1(){return 4;};' +])) +alias = env.Alias(my_gen_alias, env.Textfile('generated_header2.htest', [ + '#pragma once', + '', + 'int func2();' +])) +env.Depends(alias, 'out.txt') + +my_gen_alias, env.Textfile('generated_header2.c', [ + '#include "generated_header1.htest"', + '#include "generated_header2.htest"', + '', + 'int func2(){return func1();}' +]) + +env.Program(target='gen_source', source=['gen_source.c', 'generated_header2.c'])
\ No newline at end of file |