问题
I have a library that only interacts in static scope with an application. This requires me to link the library with the --whole-archive
option to avoid the linker from "optimizing" out the library (this is done because the linker never actually sees my library being used).
The issue is that I haven't found a way to add this linker option for a specific library in scons.
env.Append(LIBS=['mylib']) #I don't have the linker option
env.Append(LINKFLAGS=['-Wl,--whole-archive','-lmylib']) #I don't add myself to the scons dependency tree, I also get added to the link line before the LIBPATH variable.
How do I support linker flags elegantly in scons?
回答1:
Reworded Question:
How do I link a static library into a shared library with the --whole-archive link flag, with scons, while maintaining dependencies.
I am restating your question just to make sure we both have a clear understanding of what you are trying to accomplish. Please comment if I am not properly capturing the intent of your question.
Why it is not working for you:
You cannot include the library in LIBS
, because you want to surround the library with the --whole-archive
flags. If you place this entry in the LINKFLAGS
it could work, but you lose your dependency tracking.
Answer:
When you move the library from an entry in LIBS
to an entry in LINKFLAGS
you lose the implicit dependency, but that's fine, just set an explicit dependency with Depends
.
Working Example:
File Layout
.
├── A.cpp
├── A.h
├── B.cpp
├── B.h
├── main.cpp
└── SConstruct
Files
A.cpp
#include "A.h"
int foo2(void) {
return 2;
}
A.h
#ifndef __A__
#define __A__
int foo2(void);
#endif
B.cpp
#include "B.h"
#include "A.h"
int bar(void) {
return 2 * foo2();
}
B.h
#ifndef __B__
#define __B__
int bar(void);
#endif
main.cpp
#include <iostream>
#include "B.h"
using namespace std;
int main() {
cout << "Sum of foo and bar = " << bar() << endl;
return 0;
}
SConstruct
import os
# Top level Build Environment
env = Environment()
# Library A
envA = env.Clone()
libA = envA.StaticLibrary('A.cpp')
# Library B
envB = env.Clone()
envB.Append(LINKFLAGS=['-Wl,--whole-archive,./libA.a,--no-whole-archive'])
libB = envB.SharedLibrary('B.cpp')
Depends(libB, libA)
# Test Program
envE = env.Clone()
envE.Append(LIBS=['B'],
LIBPATH=['.'],
RPATH=[os.path.abspath(os.curdir)])
envE.Program('example', 'main.cpp')
To Run:
Copy all the files to the same directory, and type scons
in that directory. This is the output you should see on linux if you build and ask for the dependency tree. (I'm using Fedora 21)
>> scons --version
SCons by Steven Knight et al.:
script: v2.3.4, 2014/09/27 12:51:43, by garyo on lubuntu
engine: v2.3.4, 2014/09/27 12:51:43, by garyo on lubuntu
engine path: ['/usr/lib/scons/SCons']
Copyright (c) 2001 - 2014 The SCons Foundation
>> scons --tree=prune
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o A.o -c A.cpp
g++ -o B.os -c -fPIC B.cpp
g++ -o main.o -c main.cpp
ar rc libA.a A.o
ranlib libA.a
g++ -o libB.so -Wl,--whole-archive,./libA.a,--no-whole-archive -shared B.os
g++ -o example -Wl,-rpath=/full/path/to/build/directory main.o -L. -lB
+-.
+-A.cpp
+-A.h
+-A.o
| +-A.cpp
| +-A.h
| +-/bin/g++
+-B.cpp
+-B.h
+-B.os
| +-B.cpp
| +-A.h
| +-B.h
| +-/bin/g++
+-SConstruct
+-example
| +-main.o
| | +-main.cpp
| | +-B.h
| | +-/bin/g++
| +-/bin/g++
| +-libB.so
| +-[B.os]
| +-libA.a
| +-[A.o]
| +-/bin/ar
| +-/bin/ranlib
+-[libA.a]
+-[libB.so]
+-main.cpp
+-[main.o]
scons: done building targets.
>> ./example
Sum of foo and bar = 4
What The Example Does:
- Builds a static library A.
- Builds a dynamic library B with the
--whole-archive
link flag on library A. - Builds an executable that links in library B and executes a function from library B that utilizes a function in library A.
- Properly tracks dependencies between targets.
Conclusion:
You can see from the dependency graph above that library A is correctly listed as a dependency of library B.
Enjoy!
回答2:
Scons still doesn't support link flags such as -Wl,--whole-archive
. Using the solution provided by Kenneth E. Bellock is has been working well for many years. The downside is that this is error prone. You have to be careful to manage your manual dependency list.
There is an alternate workaround. Add the following to your SConstruct file:
whole_archive = env.Command('-Wl,--whole-archive', [], '')
no_whole_archive = env.Command('-Wl,--no-whole-archive', [], '')
Now you can wrap the necessary libraries with whole-archive/no-whole-archive in any combination. Example:
so_libs = env.SharedLibrary('myso', whole_archive+['libfoo1.a']+no_whole_archive+['libbar.a']+whole_archive+['libfoo2.a']+no_whole_archive)
回答3:
You can add LIBS and LINKFLAGS to your linker statement.
env.SharedLibrary('target',sources,LIBS=['mylib'],LINKFLAGS=env['LINKFLAGS']+['-Wl,--whole-archive','-lmylib'])
For example.
来源:https://stackoverflow.com/questions/22122498/how-do-i-add-whole-archive-linker-option-in-scons