可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a hierarchical build system based on SCons. I have a root SConstruct that calls into a SConscript that builds a shared library and then into a different SConscript that builds an executable that depends on the shared library.
So here's my question: my understanding of shared libraries on linux is that when you want to do the final ld
link for the executable that will be using the shared lib, the shared lib has to be included on the executable's ld
command line as a source to reference it (unless it's in a standard location in which case the -l
option works).
So here's something like what my SCons files look like:
=== 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")
So the hitch here is this line:
env.Append( LIBS=[shared_lib] )
This would be a great way to add generated libraries to the command line for any other libs that need them, EXCEPT that because SCons is doing a two-pass run through the SConscripts (first to generate it's dependency tree, then to do the work), rootdir/foolib/libfoo.so
winds up on the command line for ALL products, EVEN libfoo.so
itself:
gcc -g -Wall -Werror -o libfoo.so foo.o morefoo.o libfoo.so
So how is this best done with SCons? For now I've resorted to this hack:
=== 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")
Is there a more SCons-y way of doing this?
回答1:
You should allow the shared libraries to be found by the build.
Look for the LIBPATH
and RPATH
variables in the SCons
documentation; these are the "Scons-y" way to set up search paths so that any generated -l
options find libraries properly.
Having mentioned the above, here's what you should see gcc
do based on the setup of SCons (and if it doesn't, you may have to do it manually).
The -l
option always finds shared libraries provided that you also give the compiler the location of the library. There are two times this is needed: at compile time (-L
option) and at runtime (-rpath
generated linker option).
The LIBPATH
SCons setup should generate something that looks like -L/some/directory/path
for the compile-time search path.
The RPATH
SCons setup should generate a linker option to embed a search path; e.g. -Wl,-rpath -Wl,\$ORIGIN/../lib
would embed a search path that searches relative to the executable so that executables placed in bin
search in the parallel lib
directory of the installation.
回答2:
Here is a better way to organize your SConsctruct/SConscript files. Usually with Hierarchical builds you should share the env with the rest of the sub-directories. Notice that I cloned the main env in the barexec directory as well, so that the foolib is only used to link that binary.
=== 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'] )
回答3:
Additional to Brady decision i use static/global variables to store targets name and path. It's allow me more control over build.
# 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)
Define targets:
from project import Project ... env.SharedLibrary(Project.libPath(Project.MYLIB1_NAME), source=['foo.c', 'morefoo.c'])
Application:
from project import Project ... env.Append(LIBPATH = [Project.LIB_PATH]) env.Append(LIBS = [Project.MYLIB1_NAME]) env.Program(Project.appPath(Project.MYAPP1_NAME), source=[...])
In my projects it works fine, scons automatically find depends of library without any additional commands. And if i want to change name of library i just change my Project class.