【问题标题】:scons construction environment inheritancescons构建环境继承
【发布时间】:2012-07-16 21:20:20
【问题描述】:

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

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

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

不过,我不确定如何在 scons 中执行此操作。有 Import()Export() 函数,但它们基本上是丑陋的全局变量 - 调用 SConstruct 文件对子 SConstruct 文件对 Export()ed 的全局变量所做的事情没有太多控制.是否有任何干净的方法本质上将子 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() ) 工作对我来说很重要,因为我不想依赖所有子 SConscript 来执行安全克隆()-该策略应该是父 SConscript/SConstruct 文件。

不过,必须将env 作为策略参数名称还是有点难看。

【问题讨论】:

    标签: c++ python c build scons


    【解决方案1】:

    我所知道的最好的方法是从你的 SConstruct 大师那里做到这一点:

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

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

    Import('env')
    

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

    env = env.Clone()
    

    很确定这就是它的全部内容。

    【讨论】:

    • 谢谢汤姆。补充一点:如果 'env' 不一定是第二个参数,则可以使用语法:exports='env'
    • 不幸的是,这仍然让子模块自行决定克隆,因此不能保证下一个模块会这样做。
    【解决方案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 的用户有好处,但它是邪恶的。

    【讨论】:

      【解决方案3】:

      更新中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 签名将匹配并且文件将从构建缓存中复制。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-19
        • 1970-01-01
        • 1970-01-01
        • 2019-07-08
        相关资源
        最近更新 更多