Scons构建环境继承

6

我正在重构一个基于scons的构建系统时遇到了一些问题。我们有一个包含多个不同输出对象(dll、可执行文件、测试可执行文件)的C/C++源代码树,并且源文件的布局有些异构(尽管大部分在“模块”目录中,其中包括src/inc/目录)。

目前设置中最大的问题之一是,我们希望所有这些构建产品都默认使用一致的编译器选项进行构建。我们当前的布局有一个主SConstruct文件,在子目录中调用许多子SConscript文件,然后构建较大构建产品的各个部分(例如.a)。默认情况下,scons中的SConscript()函数不会将当前构建环境对象传递或继承到被调用的SConstruct文件中。这意味着当前所有这些子SConscript文件都在使用自己不同的构建环境。

我正在尝试组合的新布局在源代码树根处放置了一个主构建环境,其中包含了我们需要的所有CFLAGS和构建定义。我想将此构建环境传递给子SConscript文件,以便我知道我们构建树中的每个.c.cpp文件都使用相同的命令行进行构建。

然而,我不确定如何在scons中实现这一点。有Import()Export()函数,但这些基本上是丑陋的全局变量——调用SConstruct文件对于Export()的全局变量没有太多控制权。是否有任何干净的方式将当前构建环境作为参数传递给子SConscript文件,而不让它必须修改它?也许像这样:

master_env = Environment()
master_env.Append( CXXFLAGS=['-Wall', '-Werror', '-g', '-fPIC', ... ] )

### add other stuff that we want everything to use

SConscript( 'somelibrary/SConstruct', inherited_environment=master_env.Clone() )

### master_env has now been used to build a 
### .dll in somelibrary/, but any variations
### made to somelibrary/SConstruct's inherited 
### env haven't contaminated master_env

我知道我可以做一些笨拙且有些恶心的事情,就像这样:

clobber_env = Environment()
master_env = Environment()
master_env.Append( CXXFLAGS=['-Wall', '-Werror', '-g', '-fPIC', ... ] )

call_somelibrary_sconstruct( master_env )

def call_somelibrary_sconstruct(env):
    param_env = env.Clone()
    Export( 'param_env' )
    SConstript( 'somelibrary/SConstruct' )

    # because we don't want any contamination of our global variable 
    # between SConscript calls. I'm not even sure if this is necessary
    # or does what I think it does because I'm not sure how this ugly
    # Export()'d global variable environment works with locals like 
    # param_env here.
    param_env = clobber_env
    Export( 'param_env' ) 

有没有一种优雅的方法来做这件事?

更新:

所以我已经尝试了一些东西,看起来只要我在主SConstruct文件中这样做:

def build_somelib( env ):
    Export( env=env.Clone() )
    somelib = SConscript( 'somelib/SConscript' )
    return somelib

master_env = Environment()
master_env.Append( CXXFLAGS=['-Wall', '-Werror', '-g', '-fPIC', ... ] )

build_somelib( master_env )

然后在 somelib/SConscript 中执行以下操作:

Import( 'env' )
env.Append( CXXFLAGS=['-weirdoption1', ... ] )
lib = env.StaticLibrary( 'somelib', source=['source1.cpp', 'source2.cpp', ...] )
Return( "lib" )

那么主SConstruct中的master_env将保持不受污染。对我来说,Export( env=env.Clone() )很重要,因为我不想依赖于所有子SConscripts进行安全的Clone()操作 - 这个策略应该由父SConscript/SConstruct文件来执行。

然而,按照政策要求将env作为参数名称有点丑陋。

3个回答

8
我知道的最好的方法是从您的主SConstruct中执行以下操作:

只需这样做:

env = Environment()

env.SConscript('src/SConscript', 'env')

然后在你的src/SConscript文件中:

Import('env')

然后您可以像在SConstruct文件中一样引用env变量。如果您不想在src/SConscript中改变SConstruct的env,请在Import之后放置以下代码:

env = env.Clone()

我相信这就是全部内容了。

1
谢谢Tom。还有一件事要补充:如果'env'不一定是第二个参数,可以使用语法:exports='env' - arr_sea
不幸的是,这仍然取决于子模块的克隆裁量权,因此下一个模块并不能保证会这样做。 - Evgen

2

我查找了源代码(Ubuntu 12.04中的Scons 2.1.0)并发现Export使用其关键字更新global_exports字典。

因此,这段代码可能会运行:

Export( param_env=env.Clone() )
SConscript( 'somelibrary/SConstruct' )
Export( param_env=clobber_env ) 

文档中没有提及,所以它是一个特性。

ExportSConstript使用帧魔法来按名称获取变量,即使对于不了解Python的用户也可能很好,但这是不好的。


1

更新中OP提出的解决方案存在一个严重问题(已经使用scons-2.3.3和scons-2.3.4进行了测试):

def build_somelib( env ):
    Export( env=env.Clone() )
    somelib = SConscript( 'somelib/SConscript' )
    return somelib

master_env = Environment()
master_env.Append( CXXFLAGS=['-Wall', '-Werror', '-g', '-fPIC', ... ] )

build_somelib( master_env )

使用上述方法并开启CacheDir(some_dir)(我还使用了VariantDir,在这种情况下可能会有所不同)。在dir1中,使用scons --cache-debug=log进行完整构建。构建完成后,在具有相同代码的dir2中,也使用scons --cache-debug=log进行完整构建。所有文件应从构建缓存中复制,但我发现绝大多数文件都没有被复制。大多数文件(但不是全部)在两个目录之间具有MD5签名不匹配。此问题也可以通过修改“dir1”中的文件并重新构建,然后将修改复制到“dir2”,并再次进行重建来触发。您将看到相同的MD5不匹配。

此时,请执行scons -c,然后删除两个目录中的.sconsign.dblite文件(并出于谨慎起见删除构建缓存)。首先在dir1中重新构建,然后在‘dir2’中重新构建。您将获得正确的行为:MD5签名将匹配,并且文件将从构建缓存中复制。

我最终放弃了OP的解决方案,转而按照汤姆的建议将保护父级环境的责任转移到了所有子目录。虽然不完全是我想做的,但构建缓存现在可以正常工作了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接