使用 python 函数生成器时,Scons 会忽略依赖跟踪环境

Scons ignores the environment for dependency tracking when using python function builders

我在 scons 2.5.1 中遇到与通过环境将参数传递给基于 python 的构建器相关的问题。 当调用普通构建器时,如果传入的任何源文件或环境变量发生更改,结果似乎会被标记为脏。 使用 python 函数生成器(此处描述 http://scons.org/doc/1.2.0/HTML/scons-user/x3524.html)时,scons 似乎只关心源文件。

这里是一个最小的人工示例,说明它失败的地方。这是通过环境传递参数和使用shell将其写入目标文件的两种实现。一个实现只是一个命令字符串,另一个使用 python 子进程在 python 函数中调用它。我使用 scons 的参数 select 要使用的构建器。

#SConstruct
import subprocess

def echo_fun(env, source, target):
    subprocess.check_call('echo %s > %s' % (env['MESSAGE'], str(target[0])), shell= True)
    return None

env = Environment(BUILDERS = {'echo' : Builder(action='echo $MESSAGE > $TARGET'),
                              'echo_py': Builder(action=echo_fun),
                              })
build_fn = env.echo_py if ARGUMENTS.get('USE_PYTHON', False) else env.echo
build_fn(['test.file'], [], MESSAGE = ARGUMENTS.get('MSG', 'None'))

这是 运行 具有不同参数的 scons 脚本的结果:

PS C:\work\code\sconsissue> scons -Q MSG=Hello
echo Hello > test.file
PS C:\work\code\sconsissue> scons -Q MSG=Hello
scons: `.' is up to date.
PS C:\work\code\sconsissue> scons -Q MSG=HelloAgain
echo HelloAgain > test.file
PS C:\work\code\sconsissue> del .\test.file
PS C:\work\code\sconsissue> scons -Q MSG=Hello -Q USE_PYTHON=True
echo_fun(["test.file"], [])
PS C:\work\code\sconsissue> scons -Q MSG=Hello -Q USE_PYTHON=True
scons: `.' is up to date.
PS C:\work\code\sconsissue> scons -Q MSG=HelloAgain -Q USE_PYTHON=True
scons: `.' is up to date.

在使用普通构建器的情况下,它检测到当 MSG 更改时结果是脏的(当 MSG 保持不变时是干净的)但是在 python 命令版本中它认为它是最新的,即使味精已更改。

解决此问题的方法是将我的构建器脚本放在单独的 python 脚本中,然后使用环境依赖项作为命令行参数调用该 python 脚本,但它看起来很复杂。

这是预期的行为还是错误?

有没有比我上面描述的更简单的解决方法,我可以将构建函数保存在 SConstruct 文件中?

这是预期的行为,因为 SCons 无法知道函数(如所写)依赖于 MESSAGE。

但是,如果您阅读联机帮助页 http://scons.org/doc/production/HTML/scons-man.html

您会看到这个(在 "Action Objects" 下):

The variables may also be specified by a varlist= keyword parameter; if both are present, they are combined. This is necessary whenever you want a target to be rebuilt when a specific construction variable changes. This is not often needed for a string action, as the expanded variables will normally be part of the command line, but may be needed if a Python function action uses the value of a construction variable when generating the command line.

...
# Alternatively, use a keyword argument.
a = Action(build_it, varlist=['XXX'])

因此,如果您重写为:

#SConstruct
import subprocess

def echo_fun(env, source, target):
    subprocess.check_call('echo %s > %s' % (env['MESSAGE'], str(target[0])), shell= True)
    return None

env = Environment(BUILDERS = {'echo' : Builder(action='echo $MESSAGE > $TARGET'),
                              'echo_py': Builder(action=Action(echo_fun, varlist=['MESSAGE'])),
                              })
build_fn = env.echo_py if ARGUMENTS.get('USE_PYTHON', False) else env.echo
build_fn(['test.file'], [], MESSAGE = ARGUMENTS.get('MSG', 'None'))

它应该如您所愿。