Action.py (SCons-4.3.0) | : | Action.py (SCons-4.4.0) | ||
---|---|---|---|---|
skipping to change at line 739 | skipping to change at line 739 | |||
if not default_ENV: | if not default_ENV: | |||
import SCons.Environment | import SCons.Environment | |||
# This is a hideously expensive way to get a default shell | # This is a hideously expensive way to get a default shell | |||
# environment. What it really should do is run the platform | # environment. What it really should do is run the platform | |||
# setup to get the default ENV. Fortunately, it's incredibly | # setup to get the default ENV. Fortunately, it's incredibly | |||
# rare for an Environment not to have a shell environment, so | # rare for an Environment not to have a shell environment, so | |||
# we're not going to worry about it overmuch. | # we're not going to worry about it overmuch. | |||
default_ENV = SCons.Environment.Environment()['ENV'] | default_ENV = SCons.Environment.Environment()['ENV'] | |||
return default_ENV | return default_ENV | |||
def _resolve_shell_env(env, target, source): | ||||
""" | ||||
First get default environment. | ||||
Then if SHELL_ENV_GENERATORS is set and is iterable, | ||||
call each callable in that list to allow it to alter | ||||
the created execution environment. | ||||
""" | ||||
ENV = get_default_ENV(env) | ||||
shell_gen = env.get('SHELL_ENV_GENERATORS') | ||||
if shell_gen: | ||||
try: | ||||
shell_gens = iter(shell_gen) | ||||
except TypeError: | ||||
raise SCons.Errors.UserError("SHELL_ENV_GENERATORS must be iteratabl | ||||
e.") | ||||
else: | ||||
ENV = ENV.copy() | ||||
for generator in shell_gens: | ||||
ENV = generator(env, target, source, ENV) | ||||
if not isinstance(ENV, dict): | ||||
raise SCons.Errors.UserError(f"SHELL_ENV_GENERATORS function | ||||
: {generator} must return a dict.") | ||||
return ENV | ||||
def _subproc(scons_env, cmd, error='ignore', **kw): | def _subproc(scons_env, cmd, error='ignore', **kw): | |||
"""Wrapper for subprocess which pulls from construction env. | """Wrapper for subprocess which pulls from construction env. | |||
Use for calls to subprocess which need to interpolate values from | Use for calls to subprocess which need to interpolate values from | |||
an SCons construction environment into the environment passed to | an SCons construction environment into the environment passed to | |||
subprocess. Adds an an error-handling argument. Adds ability | subprocess. Adds an an error-handling argument. Adds ability | |||
to specify std{in,out,err} with "'devnull'" tag. | to specify std{in,out,err} with "'devnull'" tag. | |||
""" | """ | |||
# TODO: just uses subprocess.DEVNULL now, we can drop the "devnull" | # TODO: just uses subprocess.DEVNULL now, we can drop the "devnull" | |||
# string now - it is a holdover from Py2, which didn't have DEVNULL. | # string now - it is a holdover from Py2, which didn't have DEVNULL. | |||
skipping to change at line 783 | skipping to change at line 805 | |||
# produce something reasonable for just about everything else: | # produce something reasonable for just about everything else: | |||
new_env[key] = str(value) | new_env[key] = str(value) | |||
kw['env'] = new_env | kw['env'] = new_env | |||
try: | try: | |||
pobj = subprocess.Popen(cmd, **kw) | pobj = subprocess.Popen(cmd, **kw) | |||
except EnvironmentError as e: | except EnvironmentError as e: | |||
if error == 'raise': raise | if error == 'raise': raise | |||
# return a dummy Popen instance that only returns error | # return a dummy Popen instance that only returns error | |||
class dummyPopen: | class dummyPopen: | |||
def __init__(self, e): self.exception = e | def __init__(self, e): | |||
def communicate(self, input=None): return ('', '') | self.exception = e | |||
def wait(self): return -self.exception.errno | # Add the following two to enable using the return value as a contex | |||
t manager | ||||
# for example | ||||
# with Action._subproc(...) as po: | ||||
# logic here which uses po | ||||
def __enter__(self): | ||||
return self | ||||
def __exit__(self, *args): | ||||
pass | ||||
def communicate(self, input=None): | ||||
return ('', '') | ||||
def wait(self): | ||||
return -self.exception.errno | ||||
stdin = None | stdin = None | |||
class f: | class f: | |||
def read(self): return '' | def read(self): return '' | |||
def readline(self): return '' | def readline(self): return '' | |||
def __iter__(self): return iter(()) | def __iter__(self): return iter(()) | |||
stdout = stderr = f() | stdout = stderr = f() | |||
pobj = dummyPopen(e) | pobj = dummyPopen(e) | |||
finally: | finally: | |||
# clean up open file handles stored in parent's kw | # clean up open file handles stored in parent's kw | |||
for k, v in kw.items(): | for k, v in kw.items(): | |||
skipping to change at line 815 | skipping to change at line 853 | |||
# single item it should be the command string to execute; if a | # single item it should be the command string to execute; if a | |||
# list then it should be the words of the command string to | # list then it should be the words of the command string to | |||
# execute. Only a single command should be executed by this | # execute. Only a single command should be executed by this | |||
# object; lists of commands should be handled by embedding | # object; lists of commands should be handled by embedding | |||
# these objects in a ListAction object (which the Action() | # these objects in a ListAction object (which the Action() | |||
# factory above does). cmd will be passed to | # factory above does). cmd will be passed to | |||
# Environment.subst_list() for substituting environment | # Environment.subst_list() for substituting environment | |||
# variables. | # variables. | |||
if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.Comman dAction') | if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.Comman dAction') | |||
_ActionAction.__init__(self, **kw) | super().__init__(**kw) | |||
if is_List(cmd): | if is_List(cmd): | |||
if [c for c in cmd if is_List(c)]: | if [c for c in cmd if is_List(c)]: | |||
raise TypeError("CommandAction should be given only " | raise TypeError("CommandAction should be given only " | |||
"a single command") | "a single command") | |||
self.cmd_list = cmd | self.cmd_list = cmd | |||
def __str__(self): | def __str__(self): | |||
if is_List(self.cmd_list): | if is_List(self.cmd_list): | |||
return ' '.join(map(str, self.cmd_list)) | return ' '.join(map(str, self.cmd_list)) | |||
return str(self.cmd_list) | return str(self.cmd_list) | |||
skipping to change at line 891 | skipping to change at line 929 | |||
try: | try: | |||
spawn = env['SPAWN'] | spawn = env['SPAWN'] | |||
except KeyError: | except KeyError: | |||
raise SCons.Errors.UserError('Missing SPAWN construction variable.') | raise SCons.Errors.UserError('Missing SPAWN construction variable.') | |||
else: | else: | |||
if is_String(spawn): | if is_String(spawn): | |||
spawn = env.subst(spawn, raw=1, conv=lambda x: x) | spawn = env.subst(spawn, raw=1, conv=lambda x: x) | |||
escape = env.get('ESCAPE', lambda x: x) | escape = env.get('ESCAPE', lambda x: x) | |||
ENV = get_default_ENV(env) | ENV = _resolve_shell_env(env, target, source) | |||
# Ensure that the ENV values are all strings: | # Ensure that the ENV values are all strings: | |||
for key, value in ENV.items(): | for key, value in ENV.items(): | |||
if not is_String(value): | if not is_String(value): | |||
if is_List(value): | if is_List(value): | |||
# If the value is a list, then we assume it is a | # If the value is a list, then we assume it is a | |||
# path list, because that's a pretty common list-like | # path list, because that's a pretty common list-like | |||
# value to stick in an environment variable: | # value to stick in an environment variable: | |||
value = flatten_sequence(value) | value = flatten_sequence(value) | |||
ENV[key] = os.pathsep.join(map(str, value)) | ENV[key] = os.pathsep.join(map(str, value)) | |||
skipping to change at line 1195 | skipping to change at line 1233 | |||
try: | try: | |||
self.funccontents = _callable_contents(execfunction) | self.funccontents = _callable_contents(execfunction) | |||
except AttributeError: | except AttributeError: | |||
try: | try: | |||
# See if execfunction will do the heavy lifting for us. | # See if execfunction will do the heavy lifting for us. | |||
self.gc = execfunction.get_contents | self.gc = execfunction.get_contents | |||
except AttributeError: | except AttributeError: | |||
# This is weird, just do the best we can. | # This is weird, just do the best we can. | |||
self.funccontents = _object_contents(execfunction) | self.funccontents = _object_contents(execfunction) | |||
_ActionAction.__init__(self, **kw) | super().__init__(**kw) | |||
def function_name(self): | def function_name(self): | |||
try: | try: | |||
return self.execfunction.__name__ | return self.execfunction.__name__ | |||
except AttributeError: | except AttributeError: | |||
try: | try: | |||
return self.execfunction.__class__.__name__ | return self.execfunction.__class__.__name__ | |||
except AttributeError: | except AttributeError: | |||
return "unknown_python_function" | return "unknown_python_function" | |||
End of changes. 5 change blocks. | ||||
6 lines changed or deleted | 47 lines changed or added |