I have a project where I mix between static and shared libs. The shared libs are delivered. Each shared lib is built with its correspondence static lib. A shared lib can depend on other shared libs as well. For example, I have: sharedA, sharedB, sharedC, staticA, staticB and staticC. SharedA is built with staticA inside and similar for other shared. Let's say sharedC, besides its dependency on staticC, also depends on sharedB.
For the static libraries, if they need dependency on another static one, I link against the one's correspondence shared lib. For example:
staticB depends on staticA. Hence, I link staticB against sharedA. The reason for this is to avoid size increase if statically declare staticB's dependency on staticA.
This system has been working. However, problem arises when I build the static libs with hidden visibility. The linker links with no error. But more than one symbols of a specific class might end up in the final symbol map. Therefore, the program can pick up the undesired one, which causes weird behaviour.
I have been reading about cmake object library, and probably switching the static libs to object libs could help in this case. I change the static libs to object, and get undefined symbol errors. I define dependency of an object lib on other, but still could not get rid of all the errors. My questions are:
I am using target_link_libraries(obj2 PUBLIC obj) to declare dependency between 2 object libs. Can this be the reason for undefined symbol error I get? Because according to cmake documentation:
Object Libraries may "link" to other object libraries to get usage requirements, but since they do not have a link step nothing is done with their object files.
Maybe I should switch to target_link_libraries(obj2 $<TARGET_OBJECTS:obj>) so obj2 is built with object files from obj.c? Another thing with this is: obj needs to be a library (created with add_library). In the function where I define all the links, obj is a variable and could not be put after $<TARGET_OBJECTS:. I have been looking around but not able to find anything. Is there a way I can put variable name after $<TARGET_OBJECTS:, or I have to refractor somehow to make this possible?
Any other suggestion on how to do this would be helpful to me
Related
I'm building a shared library using cmake. Here are the steps that I take, starting with building a shared library A_shared using an object library A_obj.
add_library(A_obj OBJECT ${A_SRCS})
add_library(A_shared SHARED)
target_link_libraries(A_shared PUBLIC A_obj)
This process works. Now I wish to build another shared library that uses A_shared and its own sources. So I have:
add_library(B_obj OBJECT ${B_SRCS})
target_link_libraries(B_obj PUBLIC A_shared)
add_library(B_shared SHARED)
target_link_libraries(B_shared PUBLIC B_obj)
It seems to me that this should be valid, because I'm building objects B_obj which have dependence on a shared libraries A_shared, and then using these objects to construct shared library B_shared and at the same time transitively passing the dependence on A_shared using the call to target_link_libraries.
However, this results in undefined symbols when building in MSVC. When linking B_shared.dll, I get unresolved external dependencies on global variables that were defined in ${A_SRCS} and used in ${B_srcs}, and not anything else (like functions). Strangely, the object files B_obj compile fine.
If I instead link B_shared to A_obj, it works fine. But this gives me the impression that B_shared will actually contain the object files from A_obj, but all I want it only to link to A_shared.
If I link B_obj to A_obj, nothing changes and I still get unresolved dependencies.
With gcc, B_shared is successfully linked.
Therefore, my question is: am I doing the correct thing in cmake to achieve what I want? I'm wondering what I'm misunderstanding, because I've researched this extensively and I can't find the fault in my process, so I would greatly appreciate any clarification.
This comes from the behaviour of shared libraries on Windows in general: See this question. I have to manually export the symbols. The interaction with the object library wasn't creating issues.
I have two C++ modules: A and B, where B links to a set of static libs lib*.a (I use * here to mean a set of lib files) and A links to B.
I have CMakeLists.txt for B:
add_library(B STATIC B.cpp)
target_include_directories(B PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} "/path/to/headers/directory/for/lib*.a")
link_directories("Path/to/directory/contains/lib*.a")
target_link_libraries(B PRIVATE lib*)
and CMakeLists.txt for A:
add_library(A STATIC A.cpp)
target_include_directories(A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} "/path/to/headers/for/libB.a") # compiler outputs libB.a when target name is B
link_directories("Path/to/directory/contains/libB.a")
target_link_libraries(A PRIVATE B)
Everything works fine, until I try to make A a shared lib, then I got error from the linker saying cannot find -l*. I believe the reason is that when I set A to be shared, the compiler looks for shared lib, which is not available.
The thing I don't understand:
B is already a static lib, why the linker needs lib*?
why compiler looks for shared lib when I set A to be shared? I thought even a shared A can be linked to static libs
I do want to note that in A.cpp, I have #include B.hh at the top, and B.hh further includes the headers for lib*.a, that is why I have target_include_directories(B PUBLIC...)
Your initial setup was already not working, but you did not see the error until switching to a shared library.
The reason for this is that all your target_link_libraries dependencies on A and B were not actually processed at all so long as both were static libraries. This has to do with what a static library is: Just a bunch of object files packed together into a single file. A static library is not linked, hence any build dependencies that concern the linker (such as linking in additional static libraries as dependencies) will not get resolved. The build system will keep track of them, but if they are missing, you will not notice unless you actually pass those libraries to something that is actually being linked - such as an executable or, in your case now, a shared library.
Since it can be a bit embarassing to have invisible unresolved dependencies like this, idiomatic CMake will have you model things differently. You never pull in static dependencies directly, instead you use an approach like the following: At the lowest layer, you have a find_library call that finds any external pre-built dependencies on the system (the lib* in your example). You then wrap that up into an imported target so that you never have to touch the raw library path again. To smoothen things out, you can hide all of this inside a find package script, so that your code only ever sees a find_package call that provides a lib* target and don't worry about the internals.
You then model all of your library interdepencies through target dependencies only. Such an approach has several advantages that make it more robust: If CMake can't find the lib*, it will tell you right away when the find_package call fail, instead of waiting until you're trying to link like it does now. By only specifying target-based dependencies, you ensure that the build is actually consuming the libraries that it was configured with (which is very much not ensured with the link_directories based approach you're having now).
I downloaded a library (NAG c libraries to be precise) and both the static and dynamic versions of the library have the same name (libnagc_nag). At the beginning I was having troubles because I was trying to link to the static version in OS X and by default it uses the dynamic library, so I changed the name of the files to distinguish between them (libnagc_nag_s.a and libnagc_nag_d.dylib for OS X and libnagc_nag_d.so in linux).
my question is: Is giving them the same name to both a common practice? Even if so, Is there any problem or disadvantage on changing their names in this way?
Also you can provide a full library's file name to the linker (as of an object file)...
Yes, it's the convention to have
libNAME.a
and
libNAME.so.VERS
with the .a being the static archive. Why do you want to add a static library in to your program, though? You'll encounter maintenance difficulties down the track if you do so.
Adding -lnagc_nag_d to your LDFLAGS in the Makefile should be sufficient to get the dynamic library linked in.
Sorry about the vague question title, I just want to ascertain some things.
Static libraries don't link with other static libraries, right?
So when I write a slib: A, that uses functionality of another: B, all I have to provide are the headers of B to A, and only those, even if A actually uses functionality from B?
Yes?
As long as exe: X, which uses A,has B.lib specified as linker input?
So that at link time, the linker takes A.lib, which basically only knows that a function of B was called somewhere inside its code, and somehow fills that information from B.lib, making A.lib 'whole', before X gets linked with the now working parts of it?
The motivation behind these questions is to get rid of some linker warnings 4006 and discarded double definitions, and I think that should do the trick, if this is actually a valid way of doing it.
Yes, you have got it pretty much right. Executables (and DLLs) which depend on static libraries cannot be created without those dependencies being resolved, but static libraries that contain dependencies on other static libraries do not require those dependencies to be resolved at static library creation time. In fact, the process of creating a static library does not involve the linker at all.
Can't really add the Neils answer except to say that a static library is really just a whole bunch of object (.o) files collected into a single indexed file. So what works for a .o file will work for a static library.
Prior to today I had always believed that the order that objects and libraries were passed to g++ during the linking stage was unimportant. Then, today, I tried to link from c++ code to c code. I wrapped all the C headers in an extern "C" block but the linker still had difficulties finding symbols which I knew were in the C object archives.
Perplexed, I created a relatively simple example to isolate the linking error but much to my surprise, the simpler example linked without any problems.
After a little trial and error, I found that by emulating the linking pattern used in the simple example, I could get the main code to link OK. The pattern was object code first, object archives second eg:
g++ -o serverCpp serverCpp.o algoC.o libcrypto.a
Can anyone shed some light on why this might be so?. I've never seen this problem when linking ordinary c++ code.
The order you specify object files and libraries is VERY important in GCC - if you haven't been bitten by this before you have lead a charmed life. The linker searches symbols in the order that they appear, so if you have a source file that contains a call to a library function, you need to put it before the library, or the linker won't know that it has to resolve it. Complex use of libraries can mean that you have to specify the library more than once, which is a royal pain to get right.
The library order pass to gcc/g++ does actually matter. If A depends on B, A must be listed first. The reason is that it optimizes out symbols that aren't referenced, so if it sees library B first, and no one has referenced it at that point then it won't link in anything from it at all.
A static library is a collection of object files grouped into an archive. When linking against it, the linker only chooses the objects it needs to resolve any currently undefined symbols. Since the objects are linked in order given on the command line, objects from the library will only be included if the library comes after all the objects that depend on it.
So the link order is very important; if you're going to use static libraries, then you need to be careful to keep track of dependencies, and don't introduce cyclic dependencies between libraries.
You can use --start-group archives --end-group
and write the 2 dependent libraries instead of archives:
gcc main.o -L. -Wl,--start-group -lobj_A -lobj_b -Wl,--end-group