Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/60.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ SCons图书馆和子图书馆_C++_C_Build_Build Process_Scons - Fatal编程技术网

C++ SCons图书馆和子图书馆

C++ SCons图书馆和子图书馆,c++,c,build,build-process,scons,C++,C,Build,Build Process,Scons,我有一个基于SCON的分层构建系统。我有一个根SConstruct,它调用一个用于构建共享库的SConscript,然后调用另一个用于构建依赖于共享库的可执行文件的SConscript 所以我的问题是:我对linux上共享库的理解是,当您想要为将使用共享库的可执行文件执行最后的ld链接时,共享库必须包含在可执行文件的ld命令行中,作为引用它的源代码(除非它位于标准位置,在这种情况下,-l选项起作用) 下面是我的SCons文件的样子: ==rootdir/SConstruct env=Defaul

我有一个基于SCON的分层构建系统。我有一个根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')
env=DefaultEnvironment()
shared_lib = SConscript('foolib/SConscript')
env['shared_lib'] = shared_lib
executable = SConscript('barexec/SConscript')
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/傻瓜/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")
env=DefaultEnvironment()
exe = env.Program( 'bar', source=['main.c', 'bar.c', 'rod.c'] )
Return("exe")
env=DefaultEnvironment()
exe = env.Program( 'bar', source=['main.c', 'bar.c', 'rod.c'] + env['shared_lib'] )
Return("exe")
# 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'])
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'] )
==rootdir/barexec/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")
env=DefaultEnvironment()
exe = env.Program( 'bar', source=['main.c', 'bar.c', 'rod.c'] )
Return("exe")
env=DefaultEnvironment()
exe = env.Program( 'bar', source=['main.c', 'bar.c', 'rod.c'] + env['shared_lib'] )
Return("exe")
# 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'])
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'] )
所以这里的挂接装置是这条线:

env.Append( LIBS=[shared_lib] )
这将是一种将生成的库添加到命令行的好方法,用于其他任何需要它们的lib,除了因为SCons正在通过SConscripts执行两次运行(首先生成它的依赖关系树,然后执行此工作),
rootdir/dublib/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.Append( LIBS=[shared_lib] )
executable = SConscript('barexec/SConscript')
env=DefaultEnvironment()
shared_lib = SConscript('foolib/SConscript')
env['shared_lib'] = shared_lib
executable = SConscript('barexec/SConscript')
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/barexec/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")
env=DefaultEnvironment()
exe = env.Program( 'bar', source=['main.c', 'bar.c', 'rod.c'] )
Return("exe")
env=DefaultEnvironment()
exe = env.Program( 'bar', source=['main.c', 'bar.c', 'rod.c'] + env['shared_lib'] )
Return("exe")
# 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'])
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'] )

是否有更多的SCons-y方法可以做到这一点?

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

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

上面已经提到,下面是您应该看到的基于SCON设置的
gcc
操作(如果没有,您可能需要手动操作)

-l
选项始终会查找共享库,前提是您还向编译器提供了库的位置。这需要两次:编译时(
-l
选项)和运行时(
-rpath
生成的链接器选项)

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


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

以下是一个比较好的方法r组织您的sconstruct/SConscript文件的方法。通常,对于分层构建,您应该与其他子目录共享env。请注意,我也在barexec目录中克隆了主env,因此傻瓜库仅用于链接该二进制文件

==rootdir/SConstruct

env=DefaultEnvironment()
shared_lib = SConscript('foolib/SConscript')
env.Append( LIBS=[shared_lib] )
executable = SConscript('barexec/SConscript')
env=DefaultEnvironment()
shared_lib = SConscript('foolib/SConscript')
env['shared_lib'] = shared_lib
executable = SConscript('barexec/SConscript')
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/傻瓜/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")
env=DefaultEnvironment()
exe = env.Program( 'bar', source=['main.c', 'bar.c', 'rod.c'] )
Return("exe")
env=DefaultEnvironment()
exe = env.Program( 'bar', source=['main.c', 'bar.c', 'rod.c'] + env['shared_lib'] )
Return("exe")
# 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'])
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'] )
==rootdir/barexec/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")
env=DefaultEnvironment()
exe = env.Program( 'bar', source=['main.c', 'bar.c', 'rod.c'] )
Return("exe")
env=DefaultEnvironment()
exe = env.Program( 'bar', source=['main.c', 'bar.c', 'rod.c'] + env['shared_lib'] )
Return("exe")
# 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'])
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'] )

除了布雷迪的决定之外,我还使用静态/全局变量来存储目标名称和路径。这使我能够更好地控制构建

# 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=[...])

在我的项目中,它工作得很好,SCON自动查找库的依赖项,而无需任何其他命令。如果我想更改库的名称,我只需更改我的项目类。

Brady的答案没有解决的一个问题是如何在使用变体目录进行源代码外构建时获得正确的库路径。下面是一个非常类似的方法这构建了两种不同的变体:

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
    的值中的
    #
    使include路径相对于项目根而不是variant dir
SConscript

Import("env")

subdirs=["barexec", "foolib"]
senv = env.Clone(FOOLIBDIR=Dir("foolib"))
SConscript(dirs=subdirs, exports={"env": senv})
Import("env")

penv = env.Clone()
penv.Append(CPPPATH=["internal/inc"])
penv.SharedLibrary("foo", source=["foo.c", "morefoo.c"])
Import("env")

clonedEnv = env.Clone()
clonedEnv.Append(LIBPATH=["$FOOLIBDIR"], LIBS=["foo"])
clonedEnv.Program("bar", source=["main.c", "bar.c", "rod.c"])
  • 在每个
    variant\u dir
    中构建子目录需要此根级别的
    SConscript
  • 通过在设置
    dougibdir
    时使用函数
    Dir()
    ,库的变量生成目录将相对于此文件而不是使用它的位置进行解析
傻瓜书/SConscript

Import("env")

subdirs=["barexec", "foolib"]
senv = env.Clone(FOOLIBDIR=Dir("foolib"))
SConscript(dirs=subdirs, exports={"env": senv})
Import("env")

penv = env.Clone()
penv.Append(CPPPATH=["internal/inc"])
penv.SharedLibrary("foo", source=["foo.c", "morefoo.c"])
Import("env")

clonedEnv = env.Clone()
clonedEnv.Append(LIBPATH=["$FOOLIBDIR"], LIBS=["foo"])
clonedEnv.Program("bar", source=["main.c", "bar.c", "rod.c"])
  • 在进行任何更改之前克隆环境非常重要,以避免影响其他目录
barexec/SConscript

Import("env")

subdirs=["barexec", "foolib"]
senv = env.Clone(FOOLIBDIR=Dir("foolib"))
SConscript(dirs=subdirs, exports={"env": senv})
Import("env")

penv = env.Clone()
penv.Append(CPPPATH=["internal/inc"])
penv.SharedLibrary("foo", source=["foo.c", "morefoo.c"])
Import("env")

clonedEnv = env.Clone()
clonedEnv.Append(LIBPATH=["$FOOLIBDIR"], LIBS=["foo"])
clonedEnv.Program("bar", source=["main.c", "bar.c", "rod.c"])
  • 库的variant build dir被添加到
    LIBPATH
    ,这样scon和链接器都可以找到正确的库
  • “foo”
    添加到
    LIBS
    会通知SCons
    barexec
    依赖于必须首先构建的
    dublib
    ,并将库添加到链接器命令行
  • $doubibdir
    只应在
    LIBPATH
    中添加
    “foo”
    也添加到
    LIBS
    时添加到
    LIBPATH中–如果没有,则可能在
    doubib
    之前构建
    barexec
    ,导致链接器错误,因为指定的库路径(尚未)不存在

这听起来是个好主意,但这是否会破坏SCons的依赖性检查?如果我将LIBS=foo添加到bar程序环境中,则SCons不一定知道gcc从何处获取libfoo.so(可能是我的项目,可能是/usr/lib或SCons知道的任何东西),并且不会检测libfoo.so和barexec之间的依赖关系。我必须用env.Depends()破解foo和bar之间的依赖关系,不是吗?@TedMiddleton,您必须告诉SCON可执行文件需要哪些库(通过LIBS构造环境),以及它们在哪里(通过LIBPATH构造环境),则依赖关系树是基于此创建的。SCON确实会知道这些变量,因为它有这些变量,随后会将这些变量传递给gcc或其他编译器。并且您不需要使用env.Depends()函数来破解任何依赖关系。尝试一下,您会看到:)在问了这个问题后,我发现在玩这个游戏时,DefaultEnvironment()ac