如何指定scons别名依赖?
我有一个别名,用来构建一些包,叫做 'build_packages'
。不过,这个别名在构建之前需要先构建一个特定的转码库:'transcoderLib'
。'transcoderLib'
也是一个别名,单独构建时没有问题。我尝试这样指定我的别名依赖关系:
env.Alias('transcoderLib')
...
Default(env.Alias('build_packages'))
Depends('build_packages', 'transcoderLib')
但是当我构建 'build_packages'
时,'transcoderLib'
根本没有被构建,导致构建失败,因为 'build_packages'
依赖于它。为什么 scons 没有识别到这个依赖呢?
进一步的信息是:在我声明这两个别名的时候,它们都还没有任何目标,只是一些空的别名,稍后会由 SConscripts 填充。我希望 Depends('build_packages', 'transcoderLib')
能定义 'build_packages'
对 'transcoderLib'
的依赖关系,这样 'transcoderLib'
就会先被构建,然后再构建 'build_packages'
。
编辑:为了增加这个困惑,我发现当我使用 scons --tree=derived,prune
时,它会打印出 'build_packages'
的所有正确依赖关系!如果它能看到这些依赖,为什么不去构建它们呢?!!!这里是构建树本身(当然已经去掉了一些保密信息):
+-build_packages
+-Build\Config\Package_1
| +-Package_1_Dependencies...
+-Build\Config\Package_2
| +-Package_2_Dependencies...
...etc... for 16+ packages...
+-transcoderLib
+-Build\Transcoder.dll
| +-DLL dependencies...
+-Build\Transcoder.lib
| +-LIB dependencies
+-Build\Transcoder.exp
| +-EXP depdendencies
+-Lib\Python\Transcoder.pyd
+-[Build\Transcoder.dll]
+-[Build\Transcoder.lib]
+-[Build\Transcoder.exp]
编辑 3:为了提供一个更完整的例子,我准备了一个简单的 SConstruct,比较好地模拟了我遇到的情况(我删除了编辑 2 中的例子以避免混淆)。真正的问题是一个 Python 文件正在导入 'transcoderLib' 目标,所以 scons 甚至不知道它需要在构建包之前先构建这个库。我试图告诉 scons 这是必要的。这里是这个简单的例子:
import SCons
import shutil
import os
env = Environment(tools=['default', 'textfile'])
def package_builder(target, source, env):
cmdLine = 'python test.py'
return os.system(cmdLine)
def copy(env, target, source):
shutil.copy(source[0].path, target[0].path)
env.Append(BUILDERS = { 'copybuilder' : Builder(action = Action(copy, ' COPY $SOURCE -> $TARGET')) } )
env.Append(BUILDERS = { 'package' : Builder(action = Action(package_builder, ' PACKAGE $SOURCES -> $TARGET'), suffix='.zip' ) } )
env.Textfile(target='foo.txt', source='hello, world\n')
config = int(ARGUMENTS.get('config', 0))
if config == 1:
Default(env.Alias('build_packages'))
Depends(env.Alias('build_packages'), env.Alias('transcoderLib'))
env.Alias('transcoderLib', 'foo.txt')
copyTarget = env.copybuilder('bar.txt', 'foo.txt')
env.Alias('transcoderLib', copyTarget)
package = env.package('#Test', [])
env.Alias('build_packages', package)
当然,还有 test.py,这是一个非常简单的脚本,依赖于 bar.txt,也就是 'transcoderLib' 的目标:
import os
if not os.path.exists('bar.txt'):
raise Exception("bar.txt wasn't created yet!")
还有输出:
scons: warning: Support for pre-2.7.0 Python version (2.6.2) is deprecated.
If this will cause hardship, contact dev@scons.tigris.org.
File "C:\Python26\Scripts\scons.py", line 192, in
scons: Building targets ...
PACKAGE -> Test.zip
Traceback (most recent call last):
File "test.py", line 3, in
raise Exception("bar.txt wasn't created yet!")
Exception: bar.txt wasn't created yet!
scons: *** [Test.zip] Error 1
当然,如果我先运行 'scons transcoderLib',它会顺利构建 'bar.txt',而且我可以毫无错误地运行 'scons config=1'。对于那些想知道我为什么使用没有任何源或目标的构建器的人,这是为了让我的例子尽可能简单。这个 'package' 构建器应该只是运行 'test.py'(实际上,它还会构建包,但包的依赖关系与 'transcoderLib' 完全隔离)。
2 个回答
显然,env.Alias()
是用来设置依赖关系的,而不是等价关系。所以当你说 build_packages 依赖于 transcoderLib 时,你并不是在说 bar.txt 也依赖于 transcoderLib。
想想你简单示例生成的树状结构:
$ scons -n -Q --tree=all -f ex.sc config=1
scons: `build_packages' is up to date.
+-build_packages
+-bar.txt
| +-hello, world
+-transcoderLib
+-foo.txt
+-hello, world
正如你所看到的,build_packages
依赖于 bar.txt
,这是因为有调用 env.Alias()
。同时,build_packages
也依赖于 transcoderLib
,这是因为有 Depends()
的调用。但 bar.txt
和 transcoderLib
之间并没有关系。
如果 bar.txt
确实依赖于 transcoderLib
,那么这个关系需要明确表达出来:
Depends('bar.txt', env.Alias('transcoderLib'))
如果在你的示例中添加了那一行,生成的树状结构是:
$ scons -Q --tree=all -f ex1.sc config=1
scons: `build_packages' is up to date.
+-build_packages
+-bar.txt
| +-hello, world
| +-transcoderLib
| +-foo.txt
| +-hello, world
+-transcoderLib
+-foo.txt
+-hello, world
你设置的依赖关系有点问题,如果我理解得没错的话。根据你在编辑3中提供的SConstruct,我尝试做了一些修改,如下所示:
import SCons
import shutil
import os
env = Environment(tools=['default', 'textfile'])
def package_builder(target, source, env):
cmdLine = 'python test.py'
return os.system(cmdLine)
def copy(env, target, source):
shutil.copy(source[0].path, target[0].path)
env.Append(BUILDERS = { 'copybuilder' : Builder(action = Action(copy, ' COPY $SOURCE -> $TARGET')) } )
env.Append(BUILDERS = { 'package' : Builder(action = Action(package_builder, ' PACKAGE $SOURCES -> $TARGET'), suffix='.zip' ) } )
env.Textfile(target='foo.txt', source='hello, world\n')
#
# Here the changes start
#
# The following line is not needed, SCons picks up the
# dependency bar.txt -> foo.txt automatically
#tclib = env.Alias('transcoderLib', 'foo.txt')
copyTarget = env.copybuilder('bar.txt', 'foo.txt')
# Defining Alias for later reference...
# important: don't create new Alias()es all over
# the place, but pass a single reference around
tclib = env.Alias('transcoderLib', copyTarget)
# Main change: Your initial dependencies were wrong, it's
# the package (='python test.py') that depends on your
# transcoderLib (='bar.txt')...
package = env.package('#Test', [])
env.Depends(package, tclib)
# Create an Alias for the packages, you can pass
# a list of all your packages too here...
bp = env.Alias('build_packages', package)
# Finally, set the package Alias() as default target if
# requested by the config parameter...
config = int(ARGUMENTS.get('config', 0))
if config == 1:
env.Default(bp)
这样做可以确保先正确生成'bar.txt',然后再进行'package'步骤。需要注意的是,在更新时,'package'构建器会一次又一次被调用,因为指定的目标"#Test"从来没有被创建……所以它总是被认为是过时的。
希望这对你有帮助,或者至少能给你一些新的思路,即使这不是你一直在寻找的解决方案。
最后一点:简单地把一个必需的依赖关系添加到所有包的集合中,通过将其添加到一个包含你所有包的别名中,是行不通的。你的别名被视为一个不同的目标,所以如果你调用“scons package_1”来构建一个单独的包……这个依赖关系不会生效。你必须明确地将依赖关系添加到每个包,因为这就是你的构建结构的样子。