每个源目录都需要sconscript文件吗

4 投票
2 回答
6510 浏览
提问于 2025-04-17 19:05

我正在使用scons来编译我的项目。我的项目源文件分布在不同的文件夹里。请问每个文件夹都需要一个sconscript文件来编译这些源文件吗?

我试着用一个sconscript文件来编译所有的文件夹,但所有的目标文件(对象文件)都只添加到了我的源文件夹里。

我使用了这个函数:

env.Library('libs',files_list)

如果files_list只包含文件名,那么目标文件会生成在变体目录里。

如果files_list包含文件的完整路径名,那么目标文件会生成在源文件夹里。

你能告诉我该怎么做吗?

2 个回答

2

首先回答你的第一个问题:不需要在每个源代码子目录里都有一个 SConscript 文件,这样才能编译该目录里的文件。其实所有的操作都可以通过一个 SConstruct 文件来完成。

不过,通常来说,在每个源代码子目录里放一个 SConscript 文件会让项目看起来更整洁、更有条理。一般情况下,根目录的 SConstruct 文件会设置一些整个项目都需要的公共内容,并负责调用各个源代码子目录。然后,每个子目录里的 SConscript 文件就专注于处理该子目录的具体内容。我个人比较喜欢这种方式,因为它更模块化。此外,这样做还可以让你用不同的环境来调用同一个子目录的 SConscript 文件,从而编译出同一段代码的不同版本,比如调试版和发布版。

所有这些都可以通过在 SConstruct 文件中创建一个环境,然后用 SConscript() 函数把这个环境传递给子目录来实现。下面是一个例子:

SConstruct

env = Environment()
env.Append(CPPPATH = '/some/dir/common/to/all')

SConscript('src/subdirA/SConscript',
           variant_dir = 'build/subdirA',
           duplicate = 0,
           exports = 'env')
SConscript('src/subdirB/SConscript',
           variant_dir = 'build/subdirB',
           duplicate = 0,
           exports = 'env')

src/subdirA/SConscript

Import('env')

# If you need to add specific things to the env, then you should clone it,
# else the changes will be seen in other subdirs: clonedEnv = env.Clone()
# No need to specify the path to the source files if all source files are in 
# the same dir as this SConscript.
env.Library(target='subdirA', source='fileA.cc')

src/subdirB/SConscript

Import('env')

# If you need to add specific things to the env, then you should clone it,
# else the changes will be seen in other subdirs: clonedEnv = env.Clone()
env.Library(target='subdirB', source='fileB.cc')

至于最后的问题,我不太明白你具体想要什么,不过使用我上面解释的选项,编译出来的目标文件总是会放在 VariantDir 里。

15

我准备了一个例子,展示如何用一个 SConstruct 脚本(没有其他的 SConscripts)来编译像你这样的项目,使用 SCons 的 VariantDir() 函数。我决定把这个放在单独的回答里,这样更容易阅读。

VariantDir() 函数的文档写得不太好,所以你提到的关于编译后的对象文件放置位置的问题并不好解决。这里的“窍门”是要在变体目录中引用所有的源文件,而不是在你实际的源文件目录中,下面会有示例。

这是我项目中源文件的结构:

$ tree .
.
├── SConstruct
├── src1
│   ├── class1.cc
│   └── class1.h
├── src2
│   ├── class2.cc
│   └── class2.h
└── srcMain
    └── main.cc

这是 SConstruct 文件:

env = Environment()

# Set the include paths
env.Append(CPPPATH = ['src1', 'src2'])

# Notice the source files are referred to in the build dir
# If you dont do this, the compiled objects will be in the src dirs
src1Sources = ['build/lib1/class1.cc']
src2Sources = ['build/lib2/class2.cc']
mainSources = ['build/mainApp/main.cc']

env.VariantDir(variant_dir = 'build/lib1', src_dir = 'src1', duplicate = 0)
env.VariantDir(variant_dir = 'build/lib2', src_dir = 'src2', duplicate = 0)
env.VariantDir(variant_dir = 'build/mainApp', src_dir = 'srcMain', duplicate = 0)

lib1 = env.Library(target = 'build/lib1/src1', source = src1Sources)
lib2 = env.Library(target = 'build/lib1/src2', source = src2Sources)
env.Program(target = 'build/mainApp/main', source = [mainSources, lib1, lib2])

这是编译输出:

$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o build/lib1/class1.o -c -Isrc1 -Isrc2 src1/class1.cc
ar rc build/lib1/libsrc1.a build/lib1/class1.o
ranlib build/lib1/libsrc1.a
g++ -o build/lib2/class2.o -c -Isrc1 -Isrc2 src2/class2.cc
ar rc build/lib1/libsrc2.a build/lib2/class2.o
ranlib build/lib1/libsrc2.a
g++ -o build/mainApp/main.o -c -Isrc1 -Isrc2 srcMain/main.cc
g++ -o build/mainApp/main build/mainApp/main.o build/lib1/libsrc1.a build/lib1/libsrc2.a
scons: done building targets.

这是编译后得到的项目结构:

$ tree .
.
├── build
│   ├── lib1
│   │   ├── class1.o
│   │   ├── libsrc1.a
│   │   └── libsrc2.a
│   ├── lib2
│   │   └── class2.o
│   └── mainApp
│       ├── main
│       └── main.o
├── SConstruct
├── src1
│   ├── class1.cc
│   └── class1.h
├── src2
│   ├── class2.cc
│   └── class2.h
└── srcMain
    └── main.cc

需要提到的是,使用 SConscript() 函数并指定 variant_dir 是更简单的方法,但如果你的需求不允许这样做,这个例子也能正常工作。关于 VariantDir() 函数的更多信息,可以查看 SCons 手册。在那里你还会发现以下内容:

请注意,VariantDir() 与附属的 SConscript 文件配合使用时效果最好。

撰写回答