SCons库和子库
我有一个基于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] )
对于需要它们的任何其他库,这将是一个很好的方法将生成的库添加到命令行,除了因为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方式这样做?
您应该允许构建找到共享库。
在SCons
文档中查找LIBPATH
和RPATH
变量; 这些是设置搜索路径的“Scons-y”方式,以便任何生成的-l
选项可以正确地找到库。
提到上面的内容,这里是你应该看到gcc
根据SCons的设置做的事情(如果没有,你可能必须手动完成)。
-l
选项总是查找共享库,前提是您还要为编译器提供库的位置。 有两次需要:在编译时( -L
选项)和运行时( -rpath
生成的链接器选项)。
LIBPATH
SCons设置应该为编译时搜索路径生成类似于-L/some/directory/path
path的内容。
RPATH
SCons设置应生成一个链接器选项以嵌入搜索路径; 例如-Wl,-rpath -Wl,\$ORIGIN/../lib
将嵌入一个搜索路径,该路径相对于可执行文件进行搜索,以便将可执行文件置于bin
搜索安装的并行lib
目录中。
这是组织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'] )
除了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类。