如何指定scons别名依赖?

0 投票
2 回答
3006 浏览
提问于 2025-04-18 14:21

我有一个别名,用来构建一些包,叫做 '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 个回答

1

显然,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.txttranscoderLib 之间并没有关系。

如果 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
4

你设置的依赖关系有点问题,如果我理解得没错的话。根据你在编辑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”来构建一个单独的包……这个依赖关系不会生效。你必须明确地将依赖关系添加到每个包,因为这就是你的构建结构的样子。

撰写回答