我有一个基于SCons的分层构建系统。我有一个根SConstruct,它调用一个构建共享库的SConscript,然后调用一个不同的SConscript构建一个依赖于共享库的可执行文件。
所以这是我的问题:我对linux上的共享库的理解是,当你想为将要使用共享库的可执行文件做最后的ld
链接时,共享库必须包含在可执行文件的ld
命令行作为引用它的源(除非它在标准位置,-l
选项有效)。
所以这就像我的SCons文件一样:
=== rootdir / SConstruct
env=DefaultEnvironment()
shared_lib = SConscript('foolib/SConscript')
env.Append( LIBS=[shared_lib] )
executable = SConscript('barexec/SConscript')
=== rootdir / foolib / SConscript
env=DefaultEnvironment()
env.Append(CPPPATH=Glob('inc'))
penv = env.Clone()
penv.Append(CPPPATH=Glob('internal/inc'))
lib = penv.SharedLibrary( 'foo', source=['foo.c', 'morefoo.c']
Return("lib")
=== rootdir / barexec / SConscript
env=DefaultEnvironment()
exe = env.Program( 'bar', source=['main.c', 'bar.c', 'rod.c'] )
Return("exe")
所以这里的问题是这一行:
env.Append( LIBS=[shared_lib] )
这是将生成的库添加到任何其他需要它们的lib的命令行的好方法,除了因为SCons正在通过SConscripts进行两遍运行(首先生成它的依赖树,然后执行工作),rootdir/foolib/libfoo.so
结束所有产品的命令行,即libfoo.so
本身:
gcc -g -Wall -Werror -o libfoo.so foo.o morefoo.o libfoo.so
那么如何用SCons做得最好?现在我已经使用了这个黑客:
=== rootdir / SConstruct
env=DefaultEnvironment()
shared_lib = SConscript('foolib/SConscript')
env['shared_lib'] = shared_lib
executable = SConscript('barexec/SConscript')
...
=== rootdir / barexec / SConscript
env=DefaultEnvironment()
exe = env.Program( 'bar', source=['main.c', 'bar.c', 'rod.c'] + env['shared_lib'] )
Return("exe")
有更多的SCons-y方式吗?
答案 0 :(得分:3)
您应该允许构建找到共享库。
在LIBPATH
文档中查找RPATH
和SCons
个变量;这些是设置搜索路径的“Scons-y”方式,以便任何生成的-l
选项都能正确找到库。
提到上面的内容后,这里是你应该根据SCons的设置看到gcc
所做的事情(如果没有,你可能需要手动完成)。< / p>
-l
选项始终可以找到共享库,前提是您还可以为编译器提供库的位置。有两次需要:在编译时(-L
选项)和运行时(-rpath
生成的链接器选项)。
LIBPATH
SCons设置应为编译时搜索路径生成类似-L/some/directory/path
的内容。
RPATH
SCons设置应生成嵌入搜索路径的链接器选项;例如-Wl,-rpath -Wl,\$ORIGIN/../lib
将嵌入一个搜索路径,该路径相对于可执行文件进行搜索,以便bin
中的可执行文件在安装的并行lib
目录中进行搜索。
答案 1 :(得分:2)
这是组织SConsctruct / SConscript文件的更好方法。通常使用Hierarchical构建,您应该与其他子目录共享env。请注意,我也克隆了barexec目录中的主要env,因此foolib仅用于链接该二进制文件。
=== rootdir / SConstruct
import os
env=DefaultEnvironment()
subdirs = [
'foolib',
'barexec'
]
# The exports attribute allows you to pass variables to the subdir SConscripts
for dir in subdirs:
SConscript( os.path.join(dir, 'SConscript'), exports = ['env'])
=== rootdir / foolib / SConscript
# inports the env created in the root SConstruct
#
# Any changes made to 'env' here will be reflected in
# the root/SConstruct and in the barexec/SConscript
#
Import('env')
# Adding this 'inc' dir to the include path for all users of this 'env'
env.Append(CPPPATH=Glob('inc'))
penv = env.Clone()
# Adding this include only for targets built with penv
penv.Append(CPPPATH=Glob('internal/inc'))
penv.SharedLibrary( 'foo', source=['foo.c', 'morefoo.c'])
=== rootdir / barexec / SConscript
Import('env')
clonedEnv = env.Clone()
# The foo lib will only be used for targets compiled with the clonedEnv env
# Notice that specifying '#' in a path means relative to the root SConstruct
# for each [item] in LIBS, you will get -llib on the compilation line
# for each [item] in LIBPATH, you will get -Lpath on the compilation line
clonedEnv.Append(LIBS=['foo'], LIBPATH=['#foolib'])
clonedEnv.Program( 'bar', source=['main.c', 'bar.c', 'rod.c'] )
答案 2 :(得分:2)
除了Brady决定之外,我还使用静态/全局变量来存储目标名称和路径。这让我可以更好地控制构建。
# site_scons/project.py
class Project:
APP1_NAME = "app1_name"
APP2_NAME = "app2_name"
MYLIB1_NAME = "mylib1_name"
# etc
APP_PATH = "#build/$BuildMode/bin" # BuildMode - commonly in my projects debug or release, `#` - root of project dir
LIB_PATH = "#build/$BuildMode/lib"
@staticmethod
def appPath(name) :
return os.path.join(APP_PATH, name)
@staticmethod
def libPath(name) :
return os.path.join(LIB_PATH, name)
定义目标:
from project import Project
...
env.SharedLibrary(Project.libPath(Project.MYLIB1_NAME), source=['foo.c', 'morefoo.c'])
应用:
from project import Project
...
env.Append(LIBPATH = [Project.LIB_PATH])
env.Append(LIBS = [Project.MYLIB1_NAME])
env.Program(Project.appPath(Project.MYAPP1_NAME), source=[...])
在我的项目中它工作正常,scons自动查找依赖于库而无需任何其他命令。如果我想更改库的名称,我只需更改我的Project类。
答案 3 :(得分:0)
布雷迪的答案没有解决的一个问题是,在使用变体目录构建源代码时如何获取正确的库路径。这是一种非常相似的方法,可构建两个不同的变体:
SConstruct
# Common environment for all build modes.
common = Environment(CCFLAGS=["-Wall"], CPPPATH=["#foolib/inc"])
# Build-mode specific environments.
debug = common.Clone()
debug.Append(CCFLAGS=["-O0"])
release = common.Clone()
release.Append(CCFLAGS=["-O"], CPPDEFINES=["NDEBUG"])
# Run all builds.
SConscript("SConscript", exports={"env": debug}, variant_dir="debug")
SConscript("SConscript", exports={"env": release}, variant_dir="release")
#
的值中的CPPPATH
成为相对于项目根目录而不是变量目录的包含路径。 SConscript
Import("env")
subdirs=["barexec", "foolib"]
senv = env.Clone(FOOLIBDIR=Dir("foolib"))
SConscript(dirs=subdirs, exports={"env": senv})
SConscript
才能在每个variant_dir
中构建子目录。Dir()
时使用函数FOOLIBDIR
,可以相对于此文件而不是使用文件的位置来解析库的变体构建目录。 foolib/SConscript
Import("env")
penv = env.Clone()
penv.Append(CPPPATH=["internal/inc"])
penv.SharedLibrary("foo", source=["foo.c", "morefoo.c"])
barexec/SConscript
Import("env")
clonedEnv = env.Clone()
clonedEnv.Append(LIBPATH=["$FOOLIBDIR"], LIBS=["foo"])
clonedEnv.Program("bar", source=["main.c", "bar.c", "rod.c"])
LIBPATH
中,以便SCons 和都可以找到正确的库。 "foo"
添加到LIBS
通知SCons barexec
依赖于必须首先构建的foolib
,并将库添加到链接器命令行。$FOOLIBDIR
仅应在LIBPATH
也添加到"foo"
的情况下才添加到LIBS
–否则,barexec
可能在{{1}之前构建},导致链接器错误,因为指定的库路径尚不存在。