summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xCHANGES.txt5
-rwxr-xr-xRELEASE.txt3
-rw-r--r--SCons/Action.py22
-rw-r--r--SCons/Action.xml35
-rw-r--r--SCons/Tool/ninja/Methods.py4
-rw-r--r--SCons/Tool/ninja/NinjaState.py11
-rw-r--r--SCons/Tool/ninja/Utils.py4
-rw-r--r--test/Actions/subst_shell_env-fixture/SConstruct24
-rw-r--r--test/Actions/subst_shell_env.py4
9 files changed, 87 insertions, 25 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 519ced9..60e934f 100755
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -99,9 +99,12 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
- Added user configurable setting of ninja depfile format via NINJA_DEPFILE_PARSE_FORMAT.
Now setting NINJA_DEPFILE_PARSE_FORMAT to [msvc,gcc,clang] can force the ninja expected
format. Compiler tools will also configure the variable automatically.
- - Added SHELL_ENV_GENERATOR construction variables. This variable allows the user to Define
+ - Added SHELL_ENV_GENERATOR construction variable. This variable allows the user to Define
a function which will be called to generate or alter the execution environment which will
be used in the shell command of some Action.
+ - Updated SHELL_ENV_GENERATOR construction variable to SHELL_ENV_GENERATORS. This variable
+ is now an iterable which will contain functions which each are called and each can customize
+ the execution environment.
- Updated ninja scons daemon scripts to output errors to stderr as well as the daemon log.
- Fix typo in ninja scons daemon startup which causes ConnectionRefusedError to not retry
to connect to the server during start up.
diff --git a/RELEASE.txt b/RELEASE.txt
index 5bfee35..38fbbd7 100755
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -19,6 +19,9 @@ NEW FUNCTIONALITY
- Added SHELL_ENV_GENERATOR construction variables. This variable allows the user to Define
a function which will be called to generate or alter the execution environment which will
be used in the shell command of some Action.
+- Updated SHELL_ENV_GENERATOR construction variable to SHELL_ENV_GENERATORS. This variable
+ is now an iterable which will contain functions which each are called and each can customize
+ the execution environment.
- Added MSVC_USE_SETTINGS variable to pass a dictionary to configure the msvc compiler
system environment as an alternative to bypassing Visual Studio autodetection entirely.
diff --git a/SCons/Action.py b/SCons/Action.py
index 0849178..4dd0543 100644
--- a/SCons/Action.py
+++ b/SCons/Action.py
@@ -732,7 +732,7 @@ def _string_from_cmd_list(cmd_list):
default_ENV = None
-def get_default_ENV(env, target=None, source=None):
+def get_default_ENV(env):
"""
A fiddlin' little function that has an 'import SCons.Environment' which
can't be moved to the top level without creating an import loop. Since
@@ -755,6 +755,23 @@ def get_default_ENV(env, target=None, source=None):
return default_ENV
+def _resolve_shell_env(env, target, source):
+ ENV = get_default_ENV(env)
+ shell_gen = env.get('SHELL_ENV_GENERATORS')
+ if shell_gen is not None:
+ ENV = ENV.copy()
+ try:
+ shell_gens = iter(shell_gen)
+ except:
+ raise SCons.Errors.UserError("SHELL_ENV_GENERATORS must be iteratable.")
+ else:
+ for generator in shell_gens:
+ ENV = generator(env, target, source, ENV)
+ if not isinstance(ENV, dict):
+ raise SCons.Errors.UserError("SHELL_ENV_GENERATORS function: {generator} must return a dict.")
+ return ENV
+
+
def _subproc(scons_env, cmd, error='ignore', **kw):
"""Wrapper for subprocess which pulls from construction env.
@@ -924,10 +941,9 @@ class CommandAction(_ActionAction):
escape = env.get('ESCAPE', lambda x: x)
- ENV = env.get('SHELL_ENV_GENERATOR', get_default_ENV)(env, target, source)
+ ENV = _resolve_shell_env(env, target, source)
# Ensure that the ENV values are all strings:
-
for key, value in ENV.items():
if not is_String(value):
if is_List(value):
diff --git a/SCons/Action.xml b/SCons/Action.xml
index 2c18d55..8994adb 100644
--- a/SCons/Action.xml
+++ b/SCons/Action.xml
@@ -200,18 +200,32 @@ in which the command should be executed.
</summary>
</cvar>
-<cvar name="SHELL_ENV_GENERATOR">
+<cvar name="SHELL_ENV_GENERATORS">
<summary>
<para>
-A function to generate or alter the environment dictionary which will be used
-when executing the &cv-link-SPAWN; function. This primarily give the
-user a chance to customize the execution environment for particular Actions.
-It must return a dictionary containing the environment variables as
-keys and the values as values.
+Must be an iterable containing functions where each function generates or
+alters the environment dictionary which will be used
+when executing the &cv-link-SPAWN; function. The functions will initially
+be passed a reference of the current execution environment (e.g. env['ENV']),
+and each called while iterating the list. Each function must return a dictionary
+which will then be passed to the next function iterated. The return dictionary
+should contain keys which represent the environment variables and their respective
+values.
+
+This primary purpose of this construction variable is to give the user the ability
+to substitute execution environment variables based on env, targets, and sources.
+If desired, the user can completly customize the execution environment for particular
+targets.
</para>
<example_commands>
-def custom_shell_env(env, target, source):
+def custom_shell_env(env, target, source, shell_env):
+ # customize shell_env if desired
+ if str(target[0]) == 'special_target'is:
+ shell_env['SPECIAL_VAR'] = env.subst('SOME_VAR', target=target, source=source)
+ return shell_env
+
+env["SHELL_ENV_GENERATORS"] = [custom_shell_env]
</example_commands>
<para>
@@ -223,10 +237,15 @@ execution environment can be derived from.
<varname>target</varname>
The list of targets associated with this action.
</para>
- <para>
+ <para>
<varname>source</varname>
The list of sources associated with this action.
</para>
+ <para>
+ <varname>shell_env</varname>
+The current shell_env after iterating other SHELL_ENV_GENERATORS functions. This can be compared
+to the passed env['ENV'] to detect any changes.
+ </para>
</summary>
</cvar>
diff --git a/SCons/Tool/ninja/Methods.py b/SCons/Tool/ninja/Methods.py
index 2be576f..c0afab8 100644
--- a/SCons/Tool/ninja/Methods.py
+++ b/SCons/Tool/ninja/Methods.py
@@ -81,7 +81,7 @@ def get_generic_shell_command(env, node, action, targets, sources, executor=None
"GENERATED_CMD",
{
"cmd": generate_command(env, node, action, targets, sources, executor=executor),
- "env": get_command_env(env),
+ "env": get_command_env(env, targets, sources),
},
# Since this function is a rule mapping provider, it must return a list of dependencies,
# and usually this would be the path to a tool, such as a compiler, used for this rule.
@@ -266,7 +266,7 @@ def gen_get_response_file_command(env, rule, tool, tool_is_dynamic=False, custom
variables = {"rspc": rsp_content, rule: cmd}
if use_command_env:
- variables["env"] = get_command_env(env)
+ variables["env"] = get_command_env(env, targets, sources)
for key, value in custom_env.items():
variables["env"] += env.subst(
diff --git a/SCons/Tool/ninja/NinjaState.py b/SCons/Tool/ninja/NinjaState.py
index 63ea3a1..ac10b5e 100644
--- a/SCons/Tool/ninja/NinjaState.py
+++ b/SCons/Tool/ninja/NinjaState.py
@@ -809,6 +809,15 @@ class SConsToNinjaTranslator:
# Remove all preceding and proceeding whitespace
cmdline = cmdline.strip()
+ env = node.env if node.env else self.env
+ executor = node.get_executor()
+ if executor is not None:
+ targets = executor.get_all_targets()
+ else:
+ if hasattr(node, "target_peers"):
+ targets = node.target_peers
+ else:
+ targets = [node]
# Make sure we didn't generate an empty cmdline
if cmdline:
@@ -817,7 +826,7 @@ class SConsToNinjaTranslator:
"rule": get_rule(node, "GENERATED_CMD"),
"variables": {
"cmd": cmdline,
- "env": get_command_env(node.env if node.env else self.env),
+ "env": get_command_env(env, targets, node.sources),
},
"implicit": dependencies,
}
diff --git a/SCons/Tool/ninja/Utils.py b/SCons/Tool/ninja/Utils.py
index 888218d..2bd2263 100644
--- a/SCons/Tool/ninja/Utils.py
+++ b/SCons/Tool/ninja/Utils.py
@@ -259,7 +259,7 @@ def ninja_noop(*_args, **_kwargs):
return None
-def get_command_env(env):
+def get_command_env(env, target, source):
"""
Return a string that sets the environment for any environment variables that
differ between the OS environment and the SCons command ENV.
@@ -275,7 +275,7 @@ def get_command_env(env):
# os.environ or differ from it. We assume if it's a new or
# differing key from the process environment then it's
# important to pass down to commands in the Ninja file.
- ENV = get_default_ENV(env)
+ ENV = SCons.Action._resolve_shell_env(env, target, source)
scons_specified_env = {
key: value
for key, value in ENV.items()
diff --git a/test/Actions/subst_shell_env-fixture/SConstruct b/test/Actions/subst_shell_env-fixture/SConstruct
index 6e48add..5ba822e 100644
--- a/test/Actions/subst_shell_env-fixture/SConstruct
+++ b/test/Actions/subst_shell_env-fixture/SConstruct
@@ -1,24 +1,36 @@
import sys
-def custom_environment_expansion(env, target, source):
- ENV = env['ENV'].copy()
- ENV['EXPANDED_SHELL_VAR'] = env.subst(env['ENV']['EXPANDED_SHELL_VAR'], target=target, source=source)
+def custom_environment_expansion1(env, target, source, shell_env):
+ ENV = shell_env.copy()
+ ENV['EXPANDED_SHELL_VAR1'] = env.subst(env['ENV']['EXPANDED_SHELL_VAR1'], target=target, source=source)
+ return ENV
+
+def custom_environment_expansion2(env, target, source, shell_env):
+ ENV = shell_env.copy()
+ ENV['EXPANDED_SHELL_VAR2'] = env.subst(env['ENV']['EXPANDED_SHELL_VAR2'], target=target, source=source)
return ENV
def expand_this_generator(env, target, source, for_signature):
return "I_got_expanded_to_" + str(target[0])
+def expand_that_generator(env, target, source, for_signature):
+ return str(target[0]) + "_is_from_expansion"
+
env = Environment(tools=['textfile'])
-env['SHELL_ENV_GENERATOR'] = custom_environment_expansion
+env['SHELL_ENV_GENERATORS'] = [custom_environment_expansion1, custom_environment_expansion2]
env['EXPAND_THIS'] = expand_this_generator
-env['ENV']['EXPANDED_SHELL_VAR'] = "$EXPAND_THIS"
+env['EXPAND_THAT'] = expand_that_generator
+
+env['ENV']['EXPANDED_SHELL_VAR1'] = "$EXPAND_THIS"
+env['ENV']['EXPANDED_SHELL_VAR2'] = "$EXPAND_THAT"
env['ENV']['NON_EXPANDED_SHELL_VAR'] = "$EXPAND_THIS"
env.Textfile('expand_script.py', [
'import os',
- 'print(os.environ["EXPANDED_SHELL_VAR"])',
+ 'print(os.environ["EXPANDED_SHELL_VAR1"])',
+ 'print(os.environ["EXPANDED_SHELL_VAR2"])',
'print(os.environ["NON_EXPANDED_SHELL_VAR"])',
])
env.Command('out.txt', 'expand_script.py', fr'{sys.executable} $SOURCE > $TARGET')
diff --git a/test/Actions/subst_shell_env.py b/test/Actions/subst_shell_env.py
index 9f5c5db..d26f0ae 100644
--- a/test/Actions/subst_shell_env.py
+++ b/test/Actions/subst_shell_env.py
@@ -36,8 +36,8 @@ test = TestSCons.TestSCons()
test.dir_fixture('subst_shell_env-fixture')
test.run(arguments = ['-Q'])
-test.must_match('out.txt', f"I_got_expanded_to_out.txt{os.linesep}$EXPAND_THIS{os.linesep}")
-test.must_match('out2.txt', f"I_got_expanded_to_out2.txt{os.linesep}$EXPAND_THIS{os.linesep}")
+test.must_match('out.txt', f"I_got_expanded_to_out.txt{os.linesep}out.txt_is_from_expansion{os.linesep}$EXPAND_THIS{os.linesep}")
+test.must_match('out2.txt', f"I_got_expanded_to_out2.txt{os.linesep}out2.txt_is_from_expansion{os.linesep}$EXPAND_THIS{os.linesep}")
test.pass_test()