我正在尝试使用scons构建一个模块化系统来编译多个共享库,其中一些共享库依赖于其他库。虽然我可以通过一些不同的解决方法进行编译,但每个都有一些缺点,如“尝试解决方案”部分所述。
.
├── SConstruct
└── src
├── libA
│ ├── a.cc
│ ├── a.hh
│ └── SConscript
├── libB
│ ├── b.cc
│ ├── b.hh
│ └── SConscript
└── SConscript
此处,b.cc
包含a.hh
,这是两个库之间的依赖关系。
每个文件的内容如下所示。
# In SConstruct
VariantDir('build', 'src', duplicate=False)
SConscript('build/SConscript')
# In src/SConscript
env = Environment()
SConscript(['libA/SConscript', 'libB/SConscript'],
exports='env')
# In src/libA/SConscript
Import('env')
env.Append(CPPPATH=['.'])
env.SharedLibrary('a.cc')
# In src/libB/SConscript
Import('env')
env.Append(CPPPATH=['.'])
env.SharedLibrary('b.cc')
在这里,我遇到了一个问题。当我运行scons时,我得到以下结果。
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o build/libA/a.os -c -fPIC -Ibuild/libA -Isrc/libA -Ibuild/libA -Isrc/libA src/libA/a.cc
g++ -o build/libA/liba.so -shared build/libA/a.os
g++ -o build/libB/b.os -c -fPIC -Ibuild/libB -Isrc/libB -Ibuild/libB -Isrc/libB src/libB/b.cc
src/libB/b.cc:3:16: fatal error: a.hh: No such file or directory
#include "a.hh"
^
compilation terminated.
scons: *** [build/libB/b.os] Error 1
scons: building terminated because of errors.
我从this older question了解到,-Ibuild/libA
和-Isrc/libA
的重复是有意义的,并且是有意的。但是,-Isrc/libA
在编译a.cc
时会传递两次,而在编译b.cc
时则不会传递。
这似乎是由于将字符串传递给CPPPATH
而不是Dir()
节点引起的。然后使用当前SConscript的路径扩展字符串,而不是使用当前的SConscript进行扩展。为避免这种情况,我在CPPPATH=['.']
和CPPPATH=[Dir('.')]
中将src/libA/SConscript
修改为src/libB/SConscript
。这不起作用,因为CPPPATH中只包含build/libA
,而不是src/libA
,如下所示。
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o build/libA/a.os -c -fPIC -Ibuild/libA -Ibuild/libB src/libA/a.cc
g++ -o build/libA/liba.so -shared build/libA/a.os
g++ -o build/libB/b.os -c -fPIC -Ibuild/libA -Ibuild/libB src/libB/b.cc
src/libB/b.cc:3:16: fatal error: a.hh: No such file or directory
#include "a.hh"
^
compilation terminated.
scons: *** [build/libB/b.os] Error 1
scons: building terminated because of errors.
其次,我用duplicate=False
对此进行了测试。结合第一个测试,使用Dir('.')
,这成功编译了库。但是,这并不理想,因为任何调试符号都指向build
目录中的源文件。对于大型项目,在编译之后,我想删除build
目录以节省空间,这会使调试变得更难,因为gdb无法再找到源文件。
接下来,我尝试使用abolute路径。也就是说,在libA中,我添加了CPPPATH=['#/src/libA']
,而在libB中,我添加了CPPPATH=['#/src/libB']
。这已成功编译duplicate=False
。但是,这可能是一个可能包含在许多不同的顶级SConstructs中的库。通过将路径硬编码到库的SConscript中,它限制了该库的使用。
最后,我尝试根本不使用VariantDir
。除了使用Dir('.')
作为每个库的路径之外,这还可以,但会将所有中间文件保留在src
目录中。这会使目录结构变得混乱,并且使维护多个构建(例如调试/发布)变得非常棘手。
是否存在可避免这些问题的设计用法?
答案 0 :(得分:1)
您声明“通过将路径硬编码到库的SConscript中,它会限制此库的使用。”。但是替代方案是什么?在构建描述中的某个时刻,您必须具体了解应该搜索哪些路径的隐式依赖项(如头或库)。如果你随后移动你的文件夹,那么构建将会中断...独立于你的构建工具选择。
使用SCons“#”路径,从顶级SConstruct开始指定,工作正常,是您的问题的一个解决方案。
另一种选择是使用相对路径,从当前的SConstruct向上或向下开始。因此,对于src/libB/SConscript
,您可以使用:
Import('env')
env.AppendUnique(CPPPATH=['.', '../libA'])
env.SharedLibrary('b.cc')
这至少可以让您同时将“libA
”和“libB
”移动到新的位置。但我认为你不能做得更好......