I build my shared library:
env.SharedLibrary(target,Split(sources))
Documentation says
"On Windows systems, the SharedLibrary builder method will always build an import (.lib) library in addition to the shared (.dll) library, adding a .lib library with the same basename". That is right but I need another directory for it, so my question:
Is it possible to set another target directory for import library?
I want .dll and .lib in different directories:
bin/target.dll
lib/target.lib
It is possible to do it in VS projects but I also need a decision for Scons.
Thanks.
UPD:
We have the following structure
/project
/bin
/lib
/include
/source
SConstruct
/library
lib.cpp
SConscript
/app
SConscript
main.cpp
app depends on library.
The following scripts are very simplified.
SConstruct
g_env = Environment()
...
g_target = 'Library_' + g_arch
if g_debug: g_target += 'd'
SConscript('library/SConscript')
SConscript('app/SConscript')
library/SConscript
sources = [ .. ]
env_lib = g_env.Clone()
...
env_lib.SharedLibrary('#../lib/' + g_target,sources)
app/SConscript
sources = [ .. ]
app_env = g_env.Clone()
app_env.Append(LIBPATH = Split('#../lib'))
app_env.Append(LIBS = Split(g_target))
app_env.Program('app',sources)
If I go to app dir and run
scons -u
I get all I need:
lib/Library.dll
lib/Library.lib
source/app/app.exe
But if I want just to rebuild Library running
scons -u
from library directory - just builds me .obj files, there is no final shared library.
I have no idea why it works so, I'm not quite familiar with it. But now we need to get final libraries in different directories (.lib in lib, .dll in bin) as I mentioned above.
The standard way of doing this, would be to use the Install() method (see chap 11 "Installing files in other directories" of our UserGuide):
Install('lib','bin/target.lib')
You should set no_import_lib in your call to SharedLibrary()
env_lib.SharedLibrary('#../lib/' + g_target,sources,no_import_lib=True)
Also, are you outputing a .exp file?
Just list the name of the lib in the list of target files.
env.SharedLibrary([target, 'lib/anyname.lib'], Split(sources))
SCons will recognize the target .lib file based on its suffix (LIBSUFFIX), and it will adapt the /IMPLIB argument of the linker automatically.
Related
I am building cgal with the following command:
git clone https://github.com/CGAL/cgal.git cgal;
cd cgal;
git checkout releases/CGAL-4.12-branch;
mkdir ../cgal_library;
cd ../cgal_library; cmake ../cgal -DCGAL_LIB=`pwd` -DCGAL_HEADER_ONLY=ON;
I then go into the cgal_library directory, and the only things present in the lib and include directories are:
include/
CGAL/version.h
// and there is no lib dir, no headers whatsoever
Next, I install without the -DCGAL_HEADER_ONLY=ON flag, and all I get is a couple of images, which I cannot get another library's configure script to recognize as a valid cgal library:
include/
CGAL/compiler_config.h version.h
lib/
libCGAL_Core.so libCGAL_Core.so.13.0.2 libCGAL_ImageIO.so.13
libCGAL.so libCGAL.so.13.0.2
libCGAL_Core.so.13 libCGAL_ImageIO.so
libCGAL_ImageIO.so.13.0.2 libCGAL.so.13
So all I get are these image files and core objects--very few in number. I read the documentation, and I gather that there should be something in the include folder, and that the lib folder should have things in it, other than a few small object files.
How should I be compiling CGAL?
You specify in the cmake options that you want CGAL in HEADER-ONLY, that is why you don't have any lib. In HEADER-ONLY, you don't have to build CGAL, jsute give the path to the CGAL root directory as CGAL_DIR when building your program. If you want to build CGAL and have libs, make -DCGAL_HEADER_ONLY=OFF.
How can I use HKDF library in my qt project? I found this library seems appropriate in qt (I checked with my source), but I couldn't include this header to project.
Adding a library to a Qt project is actually quite simple. In your qmake .pro file you need the following:
# This is the search location for the compiler to look for headers that accompany your library.
# For system libraries that typically resides under **/usr/include** or **/usr/local/include** if you used `make install`.
INCLUDEPATH+="/path/of/headers/for/library"
# This is the search location for the compiler/linker to look for the library itself.
# For system libraries this is usually somewhere under **/usr/lib** or **/usr/local/lib**
LIBS+= -L/path/of/library/itself
# This is the name of the library to include at link time
# without the **lib** prefix and the **.so** / **.a** / **.lib** / **.dll** extension.
LIBS+= -lMyLibraryName
# This is the full path of the library file itself
# *including* the aforementioned **lib** prefix and the **.so** / **.a** / **.lib** / **.dll** extension.
# This is used by qmake to look for changes to the library at build time,
# to make sure it is re-linked on change and other dependency related stuff
PRE_TARGETDEPS += /path/and/filename/of/library/itself/libMyLibraryName.lib
TIP: All the paths, unless they are specified as absolute paths (starting with '/') will be relative to the build directory. This might be the project directory, but in the case of shadow builds, it will be the shadow build directory. As a tip, simply prepend the following to your relative paths to make them relative to project directory: $$_PRO_FILE_PWD_/ so if for example your lib resides in /my/qt/project/libs/mylib you could make your project resilient to moving by using $$_PRO_FILE_PWD_/libs/mylib instead. Please note that the "project dir" is the location of the qmake .pro file.
I use CryptoPP HKDF implementation by https://www.cryptopp.com
Firstly, build static lib for valid architecture and for your platform (MacOS, Android, iOS, etc). CryptoPP Wiki has working manuals and scripts.
Then, just add 2 lines to your qmake *.pro file:
INCLUDEPATH += $$DEV_LIBS_PATH/cryptopp/$$ANDROID_ARCH/include
LIBS += -L$$DEV_LIBS_PATH/cryptopp/$$ANDROID_ARCH/lib -lcryptopp
as you can understand, I used qmake variables DEV_LIBS_PATH and ANDROID_ARCH which are just compose right path to relevant headers and static library libcryptopp.a.
I am trying to have omake build stuff in my bin directory. I tried
LIB = ../bin/mylib
.DEFAULT: $(OCamlLibrary $(LIB), $(FILES))
But only the .a and .cmxa end up in bin, all the .cmx .cmi and .o end up in src. What am I doing wrong?
Nothing wrong. OMake's built-in OCaml related rules compile each module and produce its objects (.cmo, .cmi, .cmx and .o) in the same directory of the source code.
$(OCamlLibrary $(LIB), $(FILES)) function link these module objects and produce library files $(LIB).cma, $(LIB).cmxa and $(LIB).a, just like you have seen.
If you want to change the destination directory of object files, you have to fix the build rules for OCaml. They are defined in OCaml.om found in OMake's library directory, but I have to say that this is very challenging.
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?
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.
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'] )
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.
One issue that Brady's answer doesn't address is how to get correct library paths when building out-of-source using variant dirs. Here's a very similar approach that builds two different variants:
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")
The # in the value for CPPPATH makes the include path relative to the project root instead of the variant dir.
SConscript
Import("env")
subdirs=["barexec", "foolib"]
senv = env.Clone(FOOLIBDIR=Dir("foolib"))
SConscript(dirs=subdirs, exports={"env": senv})
This root-level SConscript is required to build the subdirectories in each variant_dir.
By using the function Dir() when setting FOOLIBDIR, the library's variant build directory is resolved relative to this file rather than where it's used.
foolib/SConscript
Import("env")
penv = env.Clone()
penv.Append(CPPPATH=["internal/inc"])
penv.SharedLibrary("foo", source=["foo.c", "morefoo.c"])
It's important to clone the environment before making any changes to avoid affecting other directories.
barexec/SConscript
Import("env")
clonedEnv = env.Clone()
clonedEnv.Append(LIBPATH=["$FOOLIBDIR"], LIBS=["foo"])
clonedEnv.Program("bar", source=["main.c", "bar.c", "rod.c"])
The library's variant build dir is added to LIBPATH so both SCons and the linker can find the correct library.
Adding "foo" to LIBS informs SCons that barexec depends on foolib which must be built first, and adds the library to the linker command line.
$FOOLIBDIR should only be added to LIBPATH when "foo" is also added to LIBS – if not, barexec might be built before foolib, resulting in linker errors because the specified library path does not (yet) exist.
I'm trying to use SCons for building a piece of software that depends on a library that is available in sources that are installed in system. For example in /usr/share/somewhere/src. *.cpp in that directory should be built into static library and linked with my own code. Library sources have no SConscript among them.
Since library is in system directory I have no rights and don't want to put build artefacts somewhere under /usr. /tmp or .build in current working directory is OK. I suspect this can be done easily but I've got entangled by all these SConscripts and VariantDirs.
env = Environment()
my_things = env.SConscript('src/SConsctipt', variant_dir='.build/my_things')
sys_lib = env.SConscript(????)
result = env.Program('result', [my_things, sys_lib])
What is intended way to solve the problem with SCons?
You could use a Repository to do this. For example, in your SConstruct you could write:
sys_lib = env.SConscript("external.scons", variant_dir=".build/external")
Then in the external.scons file (which is in your source tree), you add the path to the external source tree and how to build the library therein.
env = Environment()
env.Repository("/usr/share/somewhere/src")
lib = env.Library("library_name", Glob("*.cpp"))
Return("lib")