SCons对编译时生成的文件的依赖性

14
我正在尝试设置SCons以跟踪在构建过程中自动生成的文件的依赖关系,并正确处理多线程构建。 我正在构建的项目是CIM提供程序,由定义数据结构的MOF文件、从MOF文件生成的自动生成的源文件和头文件以及引用自动生成文件的手写源文件和头文件组成。为了使构建成功,必须在编译任何手写文件之前完成自动生成步骤,否则手写文件依赖的头文件将不存在并且构建将失败。由自动生成步骤创建的.cpp文件还必须添加到源列表中,并在最终构建中进行编译。
当运行单线程构建时,一切都正常,因为自动生成步骤始终在编译步骤之前完成,因此生成的头文件已经就位。但是,在运行多线程构建时,它会在自动生成步骤完成之前尝试编译手写文件,导致构建失败。我已经指定了显式依赖关系,但SCons没有遵循它。
这是我的SConscript文件的相关部分,我从cim_targets[]中删除了单个文件名,因为列表非常长,但总结一下,cim_targets[]是自动生成步骤的目标输出文件列表,provider_sources[]是自动化生成步骤完成后返回的源文件列表,sources[]是手写源文件列表,GenProvider()是在外部定义的Command builder,用于执行自动生成步骤,SharedLibrary()是在外部定义的builder,使用SCons库builder和一些扩展。
# Define directory paths for the CIM schema
cim_dir = 'cim-schema-2.26.0'

var_smis_dir   = Dir('.').abspath # src/lib/XXX in variant

cim_sources = [
    Glob(os.path.join(cim_dir, '*qualifiers*.mof')),
    Glob(os.path.join(cim_dir, 'Core')     + '/CIM_*.mof'),
    Glob(os.path.join(cim_dir, 'Device')   + '/CIM_*.mof'),
    Glob(os.path.join(cim_dir, 'Event')    + '/CIM_*.mof'),
    Glob(os.path.join(cim_dir, 'XXXXXX')   + '/XXX_*.mof'),
    Glob(os.path.join(cim_dir, 'Interop')  + '/CIM_*.mof'),
    Glob(os.path.join(cim_dir, 'Physical') + '/CIM_*.mof'),
    Glob(os.path.join(cim_dir, 'System')   + '/CIM_*.mof'),
]

cim_sources_flat = []
for cim in cim_sources:
    for src in cim:
        cim_sources_flat.append(src)

cim_targets = [
    ......
]

sources = [
    'driver.cpp',
    'device.cpp',
    'cim_static_data.cpp',
    'module.cpp',
    'diag_log.cpp',
    'profile_element.cpp',
]

staticlibs = [
    ......
    ]


dynamiclibs = [
    .....
    ]

var_cim_sources = this_env.Install(var_smis_dir, cim_sources_flat)

cim_mof = 'cimv226.mof'

cim_linux_mof = os.path.join(cim_dir, 'cimv226-gen-flat.mof')

var_cim_sources.extend(this_env.Command(cim_mof, cim_linux_mof, Copy('$TARGET', '$SOURCE')))

# first generate the provider infrastructure using cimple
provider_sources = this_env.GenProvider(cim_targets, var_cim_sources, name, var_smis_dir)

# make sure these files don't build until AFTER the provider files have been created
this_env.Depends(sources, provider_sources)

sources_full = provider_sources + sources

# now we can compile the provider
this_env.SharedLibrary(libname, source=sources_full, staticlibs=staticlibs, dynamiclibs=dynamiclibs, installpath=install_dir)

我尝试设置一个显式依赖项,以便手写源代码在生成所有源代码之前不会被编译(this_env.Depends(sources, provider_sources)),但是当运行多线程时,SCons会忽略此依赖关系,并尝试在自动生成步骤完成之前编译手写文件。


只要您的GenProvider()准确地列出其输出文件(目标),并且任何头文件都生成到CPPPATH上列出的路径中,那么scons将不会编译包含这些文件的任何源文件,直到它们被生成。 - bdbaddog
2个回答

3

您尝试过使用此处定义的SideEffect()函数吗:

SCons Wiki: SideEffect

我不确定它是否完全符合您的需求,但可能会有所帮助。


自动生成步骤确实会创建一个清单文件,因此我尝试将其用作 SideEffect 目标: 但不幸的是问题仍然存在,感谢你的建议。 - rdowell

-1

我通过将生成的文件作为Command的目标来解决了这个问题。例如,假设您有一个名为foo.c的文件:

#include <stdio.h>
#include "foo.h"
int main() {}

假设需要在构建foo.c之前生成foo.h,可以按照以下方式进行:

import time

def build_foo(target, source, env):
    print("Generating foo source files")
    time.sleep(5)
    with open("foo.h", "w") as f:
        f.write("")
    # Generate bar.so, which we need later for X, blah blah blah
    print("Done generating")

env = Environment()
env.Command(['foo.h', 'bar.so'], 'foo.in', build_foo)
env.Program('foo', 'foo.c')

构建输出看起来像这样:

$ scons -Q -j4
build_foo(["foo.h", "foo.so"], ["foo.in"])
Generating foo source files
Done generating
gcc -o foo.o -c foo.c
gcc -o foo foo.o

如果您省略了foo.h作为目标(即env.Command(['bar.so'],'foo.in',build_foo)),则构建输出如下所示:
$ scons -Q -j4
gcc -o foo.o -c foo.c
build_foo(["foo.so"], ["foo.in"])
Generating foo source files
foo.c:2:10: fatal error: foo.h: No such file or directory
 #include "foo.h"
          ^~~~~~~
compilation terminated.
scons: *** [foo.o] Error 1
Done generating

你在 Python 动作函数中添加了一个 sleep 吗? - bdbaddog
@bdbaddog 这仅用于说明目的 - 证明即使传递“-j4”,SCons仍将等待构建foo。 它是为了模拟需要很长时间的生成步骤。 为什么要踩我呢? - Gillespie
因为你不应该需要做任何特殊的事情来使其工作。如果构建器/命令指定了所有目标和源,并且任何适用的扫描程序都是正确的并提供了正确的搜索路径(例如CPPPATH),那么它应该可以正常工作。 - bdbaddog
@bdbaddog - 你在说什么?显然你不需要将头文件列为构建目标 - 在CMake中你不需要这样做。你只需说出你的目标依赖于另一个目标,而无需明确列出任何头文件。很明显,这对于普通的scons用户来说并不是显而易见的。因此,这个问题有11个赞和4个收藏。 - Gillespie
看一下使用“scons --tree=prune”命令并将Depends和Requires()注释掉的输出。很可能你会发现缺少依赖项。这很可能是由于CPPPATH中缺少某个目录所致。因此,SCons找不到头文件以添加到隐式依赖项列表中。你永远不应该从一个对象文件向一个(未生成的)头文件添加Depends()。 - bdbaddog
显示剩余2条评论

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