Link static library against another static library - static-libraries

I am trying to link a static library [1] into another static library [2] with scons.
Unfortunately the emitted call to "ar" never contains any path to library [1].
According to this post How to merge two "ar" static libraries into one it should be possible to merge to archives into one.
Is the usual call to CheckLibWithHeader not sufficient here?
Best regards

Have you tried specifying the complete path to library [1] when referring to it with the SCons ar command?
Brady
Adding more info to my original answer:
Since you havent posted your SCons scripts, I'll assume its something like the one I present below:
Normally, the LIBPATH construction variable is used to specify paths to libraries, but that appears to only work with the Program() builder and is not used with the ar command. What needs to be done then is to specify the complete path for the library in question. Assuming I have the following directory structure:
# tree .
.
|-- SConstruct
|-- fileA.cc
|-- fileA.o
|-- libB
| `-- libmoduleB.a
|-- libmoduleA.a
`-- libmoduleC.a
Here is the SConscript that shows how to do so:
env = Environment()
env.Library(target = 'moduleA', source = 'fileA.cc')
env.Library(target = 'moduleC', source = ['libmoduleA.a', '#libB/libmoduleB.a'])
Or Instead of the relative dir '#libB', you could specify an absolute path. (the '#' in a path means its relative to the SConscript)
And, to make it portable, you should specify the moduleB library (and moduleA) like this:
libBname = "%smoduleB%s" % (env['LIBPREFIX'], env['LIBSUFFIX'])
libB = os.path.join(pathToLibB, libBname)
Here is the result:
# scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o fileA.o -c fileA.cc
ar rc libmoduleA.a fileA.o
ranlib libmoduleA.a
ar rc libmoduleC.a libmoduleA.a libB/libmoduleB.a
ranlib libmoduleC.a
scons: done building targets.

You'd need to create a builder which runs the commands in the other SO question you've linked to.
ar -x libabc.a
ar -x libxyz.a
ar -c libaz.a *.o
Though you might need a scanner to find the files contained in each static library (ar t libabc.a) and then use the output from that as the input to a normal static library builder.
ofiles = env.unArchive('libabc.a')
ofiles.extend(env.unArchive('libxyz.a'))
env.StaticLibrary('az',ofiles)
Something like the above should work.

Related

makefile how can I generate obj files in a subfolder?

I want to generate my obj files in a subfolder, I have tried this:
lib/*.o: source/*.cpp
clang++ $(CC_FLAGS) -c -Iinclude source/*.cpp
But it still generates the obj files in the project root and not in the lib/
The project tree that I'm trying to have:
Project/
source/(cpp files)
include/(header files)
lib/(obj files)
You don't show your current makefile, but my suspicion is that it's wrong. However as we can't see it, we'll leave that alone.
The compiler does not support writing multiple object files to a different directory. If you pass multiple source files along with the -c flag then it will write out multiple object files but only to the current directory... as you've discovered the -o flag can't be specified on compile lines which generate multiple output files.
You can change your recipe to look like this:
cd lib && clang++ $(CC_FLAGS) -c -I../include ../source/*.cpp
this will cause all the object files to be written to the lib directory because it's now the current directory.
However, putting this into a makefile is not simple, because make itself is designed to have a single recipe create a single target. However you have this problem with your existing makefile which you don't show, as well.

Link command line too long: how to use response files when linking in scons on windows

Like others I have a link line that exceeds the Windows cmd line limit. For most cases we have solved the problem by building intermediate archives (aka static libraries) with subsets of the object files and performed the final link with those archives. However using this strategy with Google Test this causes the tests not to be found, specifically the tests defined in the object files that were archived.
Update: This is why. I will probably use this workaround, but I would still like to understand how to make response files work under scons.
The LongCmdLinesOnWin32 fix is problematic. We have a cygwin environment and pathnames that include spaces, so some compiler absolute paths involve quotes. The script in LongCmdLinesOnWin32 first needs to be extended to handle both the embedded quotes and the spaces (otherwise it creates separate tokens of a single path name). More seriously, when using MS Visual Studio, the compiler command is just 'cl' i.e doesn't include the pathname. This is not available in the PATH environment--it appears to be dynamically set (somehow) and not visible when constructing the cmdline argument to the LongCmdLinesOnWin32 script. But I digress....
There seems to be a much simpler (and to my eyes suitable) solution: response files, which are also supported by gcc.
I wrote a little function to take the list of object names and print them to a text file, one to a line, something like:
"""
In place for generating response files
"""
def gen_response_file(filename,file_list):
with open(filename,"w") as f:
for obj_name in file_list:
f.write ('%s\n' %os.path.abspath(str(obj_name)).replace('\\','/'))
return filename
I then tried prepending the '#' character to the file name and added it to the list of options.
The command line echoed was:
link /nologo /MACHINE:x86 /DEBUG #E:\dev\pcoip_view_client\soft_test.rsp /OUT:blah_client\blah_client_tests.exe /LIBPATH:\\sterbkp03\qt\4.8.2\lib ....
If I simply named the file "soft_test" then scons would add the suffix ".obj" and the linker could not find it, so I tried adding the suffix '.rsp'. Now, the linker complains it cannot find the file, but it is present. I captured the output from scons and pasted it to a bat file. When I ran the bat file (from the VS 2008 command line env.) the link worked like a charm, so it seems like scons is somehow causing the problem with finding the file
I tried changing the path, using absolute (#C:\blah\soft_test.rsp), relative (#.\soft_test.rsp) and just #soft_test.rsp, none of them worked.
LINK : fatal error LNK1104: cannot open file '#E:\dev\swift.dev\blah_client\soft_test.rsp'
scons: *** [blah_client\blah_client_tests.exe] Error 1104
I'm using scons v2.1.0.r5357, VS 2008 and python 2.7 under Windows 7-64
My scons file looks like:
test_objects = tenv.Object(test_sources)
xx = gen_response_file('soft_test.rsp',test_objects)
tenv.Append( LINKFLAGS = [ '#%s' % os.path.abspath(xx)]) #
test_exe = tenv.Program(target = 'blah_client_tests', source = objects + moc_objects + qrc_objects )
Any suggestions greatly appreciated.
Update: I tried with gcc and there was no problem. My guess is that somehow the scons rules associated with Visual Studio tools is different enough to cause grief.
I tried to reproduce this in Linux using gcc, and came across a different problem, whose solution may help.
Originally, I used this SConscript:
import os
"""
In place for generating response files
"""
def gen_response_file(filename,file_list):
with open(filename,"w") as f:
for obj_name in file_list:
f.write ('%s\n' %os.path.abspath(str(obj_name)).replace('\\','/'))
return filename
env = Environment()
test_objects = env.Object(target = 'testClass', source = 'testClass.cc')
resp_file = gen_response_file('response_file.rsp', test_objects)
env.Append(LINKFLAGS = [ '#%s' % os.path.abspath(resp_file)])
env.Program(target = 'helloWorld', source = 'helloWorld.cc')
Here are the related source files I used:
# tree .
.
|-- SConstruct
|-- helloWorld.cc
|-- testClass.cc
`-- testClass.h
Where helloWorld.cc is the main program. helloWorld.cc includes testClass.h and links in testClass.o When I tried to compile this, the response file was correctly generated (only contains /some/path/testClass.o) and read by the compiler. The problem that I came across was that testClass.o was not compiled, since SCons doesnt appear to recognize the dependency with the objects listed in the response file. Here is the result:
# scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o helloWorld.o -c helloWorld.cc
g++ -o helloWorld #/some/path/response_file.rsp helloWorld.o
g++: /some/path/testClass.o: No such file or directory
scons: *** [helloWorld] Error 1
scons: building terminated because of errors.
This seems like a failure in SCons because it doesnt analyze the response file. To solve this problem, I had to use the Depends() function as in the following excerpt:
...
bin = env.Program(target = 'helloWorld', source = 'helloWorld.cc')
env.Depends(bin, test_objects)
This worked and gave me the following:
# scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o helloWorld.o -c helloWorld.cc
g++ -o testClass.o -c testClass.cc
g++ -o helloWorld #/some/path/response_file.rsp helloWorld.o
scons: done building targets.
I know this doesnt answer the original question about why the response files cant be found, but once you solve that, you will most likely run into the problem mentioned above, and have to use the Depends() function.

Header include path in files generated by `protoc`

When I call protoc like this
protoc --cpp_out=. path/to/test.proto
the files
path/to/test.pb.cc and
path/to/test.pb.h
are generated which is what I want. But, since the cc needs the h, the h is included like this
#include "path/to/test.pb.h"
which is not what I want. The background is that my build tool (scons) calls protoc from the project's root and not from the directory which includes the source files. I found no obvious option in the manpage or the help text.
So my next idea was to consider this as "correct" and adjust my build system, but: The two files are siblings in the directory tree, so when one includes the other, no path is needed. Even compiling by hand fails.
Can someone help me with that?
Doing find-replace on generated files is most likely easier
than reorganization of your build system (use sed command on Linux/unix).
What I ended up doing for my project is as follows:
Create a pb/ directory at the same level as your include/ and src/ directories.
Put your .proto files in there, and create a makefile. Write the following in it:
CXX = g++
CXXFLAGS = -O3
PROTOBF = $(shell find ./ -name '*.proto')
SOURCES = $(subst proto,pb.cc,$(PROTOBF))
OBJECTS = $(subst proto,pb.o,$(PROTOBF))
default: $(OBJECTS)
#echo -n
$(SOURCES): %.pb.cc : %.proto
protoc --cpp_out=. $<
$(OBJECTS): %.pb.o : %.pb.cc
$(CXX) $(CXXFLAGS) -c $< -o $#
Which will essentially generate and build the protobuffer files when invoked.
In your main makefile, simply add the following include path: -Ipb/.
And when including a protocol buffer header, use #include <whatever.pb.h>.
Add the object files generated in pb/ to your linking step. Myself I used:
PB_OBJS = $(shell find pb/ -name '*.pb.o')
And gave that to the linker along with the normal object files in obj/.
Then, you can probably call the pb/ makefile from the main makefile if you want to automate it. The important point is that protoc be called from the pb/ directory or the include will be messed up.
Sorry for the ugly makefiles. At least it works, and I hope this helps you...

Automake linking modules

I have a problem with automake/autoconf. I will show you the layout of my source tree first:
src
------arch
----------avr
--------------i2c.c
-------sys
-----------thread.c
Now my problem. It isn't that hard to fully compile arch/avr/i2c.c and sys/thread.c. But what i actualy want is to compile all my sub directories partialy (with gcc -c) and then link all objects from one subdir together (ld -r) and make a program of of those subdirectory object files when all subdirectories are compiled. Is this possible, and if so, how?
Greetz,
Michel
Automake has no support for ld -r, and I don't think libtool can do it either. The usual setup is to build one static archive per directory, and link the main program with these static archives.
You could have in arch/avr/Makefile.am
noinst_LIBRARIES = libavr.a
libavr_a_SOURCES = i2c.c ...
something similar in sys/, and src/Makefile.am would look like
SUBDIRS = arch sys .
bin_PROGRAMS = foo
foo_SOURCES = main
foo_LDADD = arch/avr/libavr.a sys/libsys.a

Using library paths in makefiles

I have written a makefile like this:
HEADER = -I./cygdrive/c/cpros/kajj/source4
LIBB = -L./cygdrive/c/cpros/kajj/source1 -L./cygdrive/c/cpros/kajj/source2
LIBRA = -larith -ldekk
target : game.o
gcc $(HEADER) $(LIBB) $< -o $# $(LIBRA)
game.o : game.c
gcc -c game.c
I have created my own static library and included the header file path and library path. When I execute my makefile it gives an error saying that
/usr/lib/gcc cannot find -larith -ldekk.
It is pointing to the lib/ directory but it is not over there: -ldekk and -larith are in source1 and source2 files respectively.
How to solve this error?
Instead of -L./cygdrive/c, use -L/cygdrive/c. The dot makes the library path relative from the current directory, i.e. it will look for a cygdrive subfolder of the current folder instead of drive C.
My revised Makefile libraries line is:
LIBS=-L/usr/lib/arm-linux-gnueabihf -lrtlsdr -lpthread -lm
This solved the issue in a Raspberry Pi 4 running the latest Raspbain as of Dec 30, 2019

Resources