3

我有一个基于 SCons 的分层构建系统。我有一个根 SConstruct,它调用一个构建共享库的 SConscript,然后调用另一个构建依赖于共享库的可执行文件的 SConscript。

所以这是我的问题:我对 linux 上的共享库的理解是,当您想要为ld将使用共享库的可执行文件进行最终链接时,必须将共享库ld作为参考源包含在可执行文件的命令行中它(除非它位于标准位置,在这种情况下该-l选项有效)。

所以这里是我的 SCons 文件的样子:

=== 根目录/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 呢?现在我已经求助于这个黑客:

=== 根目录/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

4 回答 4

3

这是组织 SConstruct/SConscript 文件的更好方法。通常对于分层构建,您应该与其余的子目录共享 env。请注意,我也克隆了 barexec 目录中的主环境,因此傻瓜仅用于链接该二进制文件。

=== 根目录/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'] )
于 2012-07-20T07:33:36.877 回答
3

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

在文档中查找LIBPATHRPATH变量;SCons这些是设置搜索路径的“Scons-y”方式,以便任何生成的-l选项都能正确找到库。

上面已经提到了,下面是您应该根据 SCons 的设置看到gcc的内容(如果没有,您可能必须手动进行)。

如果您还向编译器提供了库的位置,该-l选项始终会找到共享库。有两次需要这样做:编译时(-L选项)和运行时(-rpath生成的链接器选项)。

LIBPATHSCons 设置应该生成类似于-L/some/directory/path编译时搜索路径的内容。

RPATHSCons 设置应该生成一个链接器选项来嵌入搜索路径;例如-Wl,-rpath -Wl,\$ORIGIN/../lib,将嵌入一个相对于可执行文件进行搜索的搜索路径,以便可执行文件放置在安装bin的并行lib目录中进行搜索。

于 2012-07-20T03:26:48.313 回答
2

除了布雷迪决定,我使用静态/全局变量来存储目标名称和路径。它让我可以更好地控制构建。

# 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 自动查找依赖于库而无需任何其他命令。如果我想更改库的名称,我只需更改我的项目类。

于 2012-07-20T17:03:32.893 回答
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通知 SConsbarexec取决于foolib必须先构建哪个,并将库添加到链接器命令行。
  • $FOOLIBDIR应该只添加到LIBPATHwhen"foo"也添加到LIBS- 如果没有,barexec可能会在之前构建foolib,导致链接器错误,因为指定的库路径(尚)不存在。
于 2019-12-09T15:48:55.677 回答