diff options
-rwxr-xr-x | CHANGES.txt | 5 | ||||
-rwxr-xr-x | RELEASE.txt | 3 | ||||
-rw-r--r-- | SCons/Action.py | 22 | ||||
-rw-r--r-- | SCons/Action.xml | 35 | ||||
-rw-r--r-- | SCons/Tool/ninja/Methods.py | 4 | ||||
-rw-r--r-- | SCons/Tool/ninja/NinjaState.py | 11 | ||||
-rw-r--r-- | SCons/Tool/ninja/Utils.py | 4 | ||||
-rw-r--r-- | test/Actions/subst_shell_env-fixture/SConstruct | 24 | ||||
-rw-r--r-- | test/Actions/subst_shell_env.py | 4 |
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() |