SCons代码生成和VariantDir

9

我希望SCons能够在我的src/目录下为我生成一些源文件,然后像其他源文件一样在我的构建目录build/variantX中进行构建。

这是我的SCons文件:

import SCons

def my_builder(env, target, source):
    # do stuff
    pass

env = Environment()
env.VariantDir('build/variant1/', 'src', duplicate=0)
env.Command('src/foobar.cc', 'src/foobar.input', action=my_builder)
env.Program('bin/test', [
    'build/variant1/foobar.cc',
    'build/variant1/test.cc',
    ])

这里出现了以下错误信息:

源文件 src/foobar.cc 未找到,需要被目标文件 build/variant1/foobar.o 使用

考虑到我确实提供了一个构建 src/foobar.cc 的命令,因此我认为这个错误信息不正确。
我尝试了几种解决方法:
  • 如果我在程序中将 Program 中的 build/variant1/foobar.cc 替换成 src/foobar.cc,则可以正常工作,但是显然 foobar.o 将会生成在 src/ 而不是 build/variant1

  • 如果我将命令中的 src/foobar.cc 替换成 build/variant1/foobar.cc,则也可以正常工作,但我希望代码生成在 src/ 中;(还有因为像 include 目录中的相对路径之类的东西将无法工作,除非 duplicate=1

  • 如果使用 duplicate=1,那么就会出现类似的错误信息,但这次是指明变体目录的:

    源文件 build/variant1/foobar.cc 未找到,需要被目标文件 build/variant1/foobar.o 使用

有解决这个问题的方法吗?这是 SCons 的限制/错误,还是我对此有根本性的误解?

你有没有找到这个问题的答案?我也遇到了非常类似的情况。 - jfritz42
3个回答

3
我建议在Command()和Program()调用之间创建一个明确的依赖关系,如下所示:
target1 = env.Command('src/foobar.cc', 'src/foobar.input', action=my_builder)
target2 = env.Program('bin/test', [
                      'build/variant1/foobar.cc',
                      'build/variant1/test.cc',
                      ])
Depends(target2, target1)
# This should work too
# Depends(target2, "src/foobar.cc")

或者,您可以在Program()的源中将目标指定为Command()的一部分,如下所示:

target1 = env.Command('src/foobar.cc', 'src/foobar.input', action=my_builder)
env.Program('bin/test', [
            target1,
            'build/variant1/test.cc',
            ])

我还没有测试过这个,所以不确定它如何与调用VariantDir()一起工作。

这里提供了有关使用SCons生成源代码的额外信息。


嗨,Brady,感谢你的帮助。你的第一个想法确实很好,但似乎没有任何效果。第二个想法似乎与我的第一个解决方法基本相同:它可以工作,但是它会在源目录而不是变体目录中构建。这个链接看起来很有趣,我现在正在浏览它。 - UncleZeiv
@UncleZeiv,我真的很惊讶Depends()函数没起作用。试着使用实际文件名,我会更新答案。 - Brady
“--tree=all”告诉我这将导致“bin/test”依赖于“正确”的“src/foobar.cc”(反过来又依赖于“src/foobar.input”),但是“build/variant1/foobar.o”仍然依赖于一个“悬空”的“src/foobar.cc”,它本身并没有任何依赖关系,而且(通过对Scons源代码的开放式调试)似乎甚至没有任何构建器与之相关联。我真的开始认为这是一个Scons的错误。 - UncleZeiv
我认为VariantDir在依赖关系树中从根本上破坏了某些东西。我设法想出一个非常简单的破碎测试案例,完全不涉及代码生成。如果您有兴趣,请查看这里:http://scons.tigris.org/issues/show_bug.cgi?id=2908 - UncleZeiv
@UncleZeiv 另一种直接调用VariantDir()函数的替代方法是创建分层构建,通过使用SConscript()函数并使用variant_dir参数来实现。这更加简单,不会产生VariantDir()调用的奇怪副作用。不过,我想这取决于你的项目结构。 - Brady

1

我知道已经有一段时间了,但我遇到了同样的问题。通过对“测试用例”和解决方案进行微小修改(见下文),代码如下:

import SCons

env = Environment()
env.VariantDir('build/variant1/', 'src', duplicate=0)
env.Command('src/foobar.cc', 'src/foobar.input', action="cp src/foobar.input src/foobar.cc", shell=True )
env.Depends("build/variant1/foobar.cc", "src/foobar.cc")
env.Program('bin/test', [
'build/variant1/foobar.cc',
])

添加 'env.Depends' 到 'variantdir-source' 到 'generated-source' 是关键。不知道为什么需要这样做。我会称其为一个错误,但我想它是特色(基于您收到的错误反馈..)。
干杯,

这对我解决了问题。看起来是一个 bug,scons 应该能够找出变体版本的文件是与生成的源相关的目标。 - dmoody256

0

顺便提一下,我找到了另一个解决方案,可以在使用SConscript(variant_dir = ...)时解决这个问题。在我的项目中,我没有单独的“src”目录,但我有分层的SConscript文件,并且理想情况下,我不想在每个地方都命名构建目录,因为它取决于正在构建的目标。

关键是(a)直接依赖于File而不是字符串文件名,这似乎会混淆依赖树[这解决了依赖性问题,如上所述],以及(b)明确创建Object并使用导致它在build目录中生成的名称,而不是源目录[解决.o-in-src /问题]。

这是我演示这个工作的内容,

SConstruct:

env = Environment()
Export('env')
SConscript('src/SConscript', variant_dir='build/variant1', duplicate=False)

src/SConscript:

Import('env')
s = env.Command('#/src/foobar.cc', 'foobar.input', action="cp $SOURCES $TARGETS", shell=True )
o = Object(target="foobar.o", source=s)
env.Program('#/bin/test', [o])

编程让我感到,

$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
cp src/foobar.input src/foobar.cc
g++ -o build/variant1/foobar.o -c src/foobar.cc
g++ -o bin/test build/variant1/foobar.o
scons: done building targets.

主要的缺点是您必须从树的根目录“root”目标源文件,而不是提供相对名称,以便将其生成在源目录而不是build/variant目录中。
如果有更优雅的方法,请提出建议!我目前将其包装在一个Builder中,可以自动处理上述问题,但仍需要提供绝对文件名,这很不方便。

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