获取scons以在重建时区分空源和不存在源



在构建我的程序时,区分不存在的文件和空文件是很重要的。然而,当源文件从其中一种状态改变为另一种状态时,scons似乎对它们进行了相同的处理,并且忽略了重建目标。


分步示例:

步骤0:

SConstruct

foo = Command('foo', [], 'echo $TARGET is not created here!')
bar = Command('bar', foo, 'touch $TARGET ; test -f $SOURCE')
Default(bar)

结果:

scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
echo foo is not created here!
foo is not created here!
touch bar ; test -f foo
scons: *** [bar] Error 1
scons: building terminated because of errors.

我的解释:

foo的命令创建文件失败,但没有引发错误,因此运行bar的命令。它检查foo是否存在并返回一个错误。构建失败(到目前为止一切如预期)。

步骤1:

SConstruct:

foo = Command('foo', [], 'touch $TARGET')
bar = Command('bar', foo, 'touch $TARGET ; test -f $SOURCE')
Default(bar)

结果:

scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
touch foo
touch bar ; test -f foo
scons: done building targets.

我的解释:

foo被重建,因为它已经改变了。这一次,它创建一个空文件。bar正在重建,因为它之前失败了。这次成功了。构建成功(仍然如预期的那样)。

步骤2:

SConstruct

foo = Command('foo', [], 'echo $TARGET is not created here!')
bar = Command('bar', foo, 'touch $TARGET ; test -f $SOURCE')
Default(bar)

结果:

scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
echo foo is not created here!
foo is not created here!
scons: `bar' is up to date.
scons: done building targets.

我的解释:重新构建

foo,因为它再次发生了更改(已恢复到以前的版本)。文件foo不再存在,因为scons在构建文件之前删除了文件,命令无法重新创建文件。bar没有重新构建,因为scons似乎没有检测到源文件中的更改。构建是成功的,而它不应该成功!


如何在最后一步强制scons重建bar?

该解决方案应该可以很好地扩展到"foo"生成许多文件的命令,其中的列表在SConscript中以编程方式生成,不能硬编码。

顺便说一下,scons现在可以区分空和不存在,这是最近的更改(commit 3b7f8b4ce0, github.com/SCons/scons/pull/3833)。

SCons确定文件已挂起的默认方式是比较文件的前一个版本和当前版本的MD5签名。一个不存在的文件的签名是从0字节的数据中计算出来的,就像一个空文件一样,所以SCons看不到它们之间的区别。这通常是可以的,特别是不创建目标文件并不是对SCons的完全合法使用。然而,我们可以通过提供不同的函数来决定文件是否不同,从而使其工作。

这样的函数在SCons中称为Decider。有三个是现成的。缺省情况下使用MD5协议。第二个使用时间戳。第三个使用MD5,但只在时间戳不同的情况下使用。

在这种情况下,时间戳可能可以工作,因为对于不存在的文件它是0。但是,当时间戳改变而文件内容没有改变时,它会产生误报。

相反,我们可以提供我们自己的决定器,它将做我们想做的事情:

from os import path
env = DefaultEnvironment()
decider_env = env.Clone()
def decide_if_changed(dependency, target, prev_ni, repo_node=None):
csig = dependency.get_csig() # it has to be called every time or the value won't be in `prev_ni` for the next check
return not path.isfile(dependency.abspath) or not hasattr(prev_ni, 'csig') or prev_ni.csig != csig
decider_env.Decider(decide_if_changed)
foo = env.Command('foo', [], 'echo $TARGET is not created here!')
bar = decider_env.Command('bar', foo, 'touch $TARGET ; test -f $SOURCE')
Default(bar)

这个自定义决定器类似于基于MD5的默认实现,除了如果文件不存在它也会报告更改。这应该涵盖问题中描述的问题。

将新的决策器分配给默认环境的克隆。这样我们就可以控制哪个目标使用它。在这种情况下,只有bar使用非默认的决定符。

如果你说"我怎样才能强迫SCons做xyz?",那么你对SCons的理解是不完整的。

SCons将只构建过期的目标。

除非. .

您使用AlwaysBuild(target)见:https://scons.org/doc/production/HTML/scons-man.html#f-AlwaysBuild

似乎你也不希望foo在(重新)构建之前被删除?

那么你应该使用Precious(target)参见:https://scons.org/doc/production/HTML/scons-man.html#f-Precious

也. .使用空源代码调用构建器是很糟糕的形式。

SCons怎么知道它是不是过时了?

对于您的示例,是什么原因导致foo被(重新)构建?

最新更新