6

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

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

我试图组合的新布局有一个主构建环境,它与我们需要的所有必要的 CFLAGS 和构建定义一起放在源树根目录中。我希望将此构建环境传递给子 SConscript 文件,以便我知道构建树中的每个文件.c和文件都是使用相同的命令行构建的。.cpp

不过,我不确定如何在 scons 中执行此操作。有Import()and函数,但这些基本上是丑陋的全局变量——调用 SConstruct 文件对子 SConstruct 文件对被'edExport()的全局变量所做的事情没有太多控制。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" )

然后master_env主 SConstruct 中的 未受污染。这项Export( env=env.Clone() )工作对我来说很重要,因为我不想依赖所有子 SConscript 来执行安全克隆()-该策略应该是父 SConscript/SConstruct 文件。

env尽管如此,必须通过策略将其作为参数名称还是有点难看。

4

3 回答 3

8

我所知道的最好的方法是从你的大师 SConstruct 做这个:

env = Environment()

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

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

Import('env')

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

env = env.Clone()

很确定这就是它的全部。

于 2012-07-18T15:15:06.980 回答
1

我 grep 源代码(Ubuntu 12.04 中的 Scons 2.1.0)并找出使用其关键字Export更新字典的内容。global_exports

所以这段代码可能会运行:

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

文档中没有任何内容,因此它是一个功能。

Exportand使用SConstript框架魔法通过名称获取变量,即使它可能对还不了解 python 的用户有好处,但它是邪恶的。

于 2012-07-17T23:13:14.467 回答
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 不匹配。

此时,执行 ascons -c然后删除两个目录中的 .sconsign.dblite 文件(并删除构建缓存以防万一)。首先在 中dir1重建,完成后,在 'dir2' 中重建。您将获得正确的行为:MD5 签名将匹配并且文件将从构建缓存中复制。

根据 Tom 的解决方案,我最终放弃了 OP 的解决方案,并将保留父环境的责任转移到所有子目录中。不完全是我想要做的,但至少构建缓存现在按预期工作。

于 2014-10-02T19:39:26.587 回答