SCons库和子库

时间:2012-07-20 01:17:48

标签: c++ c build build-process 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] )

这是将生成的库添加到任何其他需要它们的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方式吗?

4 个答案:

答案 0 :(得分:3)

您应该允许构建找到共享库。

LIBPATH文档中查找RPATHSCons个变量;这些是设置搜索路径的“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}之前构建},导致链接器错误,因为指定的库路径尚不存在。