summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Moody <daniel.moody@mongodb.com>2022-04-14 20:49:51 (GMT)
committerDaniel Moody <daniel.moody@mongodb.com>2022-04-14 20:49:51 (GMT)
commit4bb4178b19294ddc0bc1ec801d28c0bb2509a4c5 (patch)
treec5181ed73738d5f4a63ce973478531ac2c8312cc
parenta3602d5c9d9fea87c3335aa714c34b35bf698dd0 (diff)
downloadSCons-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.py74
-rw-r--r--SCons/Tool/ninja/__init__.py6
-rw-r--r--SCons/Tool/ninja/ninja.xml14
-rw-r--r--test/ninja/generated_sources_alias.py82
-rw-r--r--test/ninja/ninja-fixture/gen_source.c9
-rw-r--r--test/ninja/ninja_test_sconscripts/sconstruct_generated_sources_alias41
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