CMake target_link_libraries not preserving order - c++

I am using CMake 3.8.2 (shipped with JetBrains CLion) and linking several static libraries of a custom project.
As I am required to preserve static symbols (legacy), I am including my own program parts with target_link_libraries(${TARGET} -Wl,--whole-archive ${MY_LIBRARY} -Wl,--no-whole-archive).
This works most of the time, but at some point the linker command will be like this:
/usr/bin/c++ -g CMakeFiles/my_exe.dir/my_exe.cpp.o -o my_exe libmy_other_lib.a -Wl,--whole-archive -Wl,--no-whole-archive
This is rather useless, as the library is added beforehand and then the -Wl,--whole-archive -Wl,--no-whole-archive occurs.
Note, that I need to add something like -Wl,--no-whole-archive after linking my_other_lib.a because I do not want to use this option for external dependencies.
Any thoughs on this?

Try passing these flags as a single argument to target_link_libraries, and not as a list:
target_link_libraries(${TARGET} "-Wl,--whole-archive ${MY_LIBRARY} -Wl,--no-whole-archive")

Related

Using --whole-archive linker option with CMake and libraries with other library dependencies

I've got a project that used to be a giant set of source files that all got compiled and then linked as one executable. As a first step in making the project more modular, I am breaking up the build into several smaller chunks and making them static libraries. There's a hierarchy, so Exe1 will link against static libs Lib2A and Lib2B. Lib2A will depend on static Lib3A, lib3B, lib3C, etc. The numbers here show their layer in the hierarchy.
The problem is that I need to use --whole-archive when linking or else some symbols from the underlying libraries are not found.
When I add the below for the linking of Exe1:
target_link_libraries(Exe1 -Wl,--whole-archive Lib2A Lib2B -Wl,--no-whole-archive)
I end up with an actual link stage command like:
g++ -o Exe1 -Wl,--whole-archive libLib2A.a libLib2B.a -Wl,--no-whole-archive libLib3A.a libLib3B.a libLib3C.a
Inevitably, symbols from some of the layer 3 static libraries get lost and I get missing symbol errors.
I expected that because Lib2A has Lib3* libraries as dependencies, that they would also be "inside" the --whole-archive part of the linker command, but they show up outside.
I've tried many different combinations (e.g. putting the --whole-archive stuff at lower layers), but haven't come across an approach that works using CMake. What am I doing wrong?
Thanks
For 3.12 and newer versions of CMake, I would use object libraries.
The workaround I found for versions earlier than that was to create an intermediate static library that used some property magic to place all linkage dependencies inside the --whole-archive section. For me, the top-level static library was called 'source'. It contained actually nothing itself, but had linkage dependencies on a bunch of other static libraries. I created 'source-combined' as follows:
add_library(source-combined STATIC "")
set_target_properties(source-combined PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(source-combined PUBLIC
-Wl,--whole-archive
$<TARGET_PROPERTY:source,INTERFACE_LINK_LIBRARIES>
-Wl,--no-whole-archive
)
Now when I create an executable or a shared library by linking against this souce-combined library, I get the --whole-archive and --no-whole-archive as bookends around the entire set of static libraries that were the link dependencies of 'source'. It took forever to stumble across this technique, so I'm sharing it.
The following worked for me. Consider two libraries:
my_platform
my_clib
We want the whole archive of my_clib, and my_platform links to it.
add_library(my_platform INTERFACE) # this could also be a regular library
add_library(my_clib STATIC)
target_sources(my_clib
PRIVATE
gcc_newlib_nano.c
gcc_newlib_nano_cpp.cc
)
# Link my_clib and any other libs
target_link_libraries(my_platform
INTERFACE
my_clib
)
# Ensure the whole archive is linked
target_link_options(my_platform
INTERFACE
-Wl,--whole-archive ${CMAKE_CURRENT_BINARY_DIR}/libmy_clib.a -Wl,--no-whole-archive
)
As an alternative to the above answer, I needed to get something quick and dirty to see if the effort to add whole archive target flags (or convert the code base to object libraries...) was the right solution. By following the CMake Source Code for the default link command, I modified my project's command to:
set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> -Wl,--start-group -Wl,--whole-archive <LINK_LIBRARIES> -Wl,--no-whole-archive -Wl,--end-group")
It worked a treat! While not the greatest solution, it will at least get some results quickly.
If you need to use the linker option --whole-archive, then you definably should use object libraries:
# Lib2A/CMakeLists.txt
add_library(Lib2A OBJECT ${Lib2A_SRC})
# Lib2B/CMakeLists.txt
add_library(Lib2B OBJECT ${Lib2B_SRC})
It is portable and does not require use the linker option --whole-archive.

linux linking to a .so , but still getting undefined reference

I am trying to create an executable that uses code from both static libraries and a shared library:
The static libs are several boost .a , pthread and libbus.a. The shared lib is a libwrap.so.
Note that the libwrap , uses code from libbus and libbus uses code from pthread. Finally, the executable uses code from libwrap and from boost.
Since the order of libraries included in the linker matters I am trying to find the "winning" sequence.
The linking stage is the following (pasted in multiple lines for convenience):
$ /usr/bin/c++
-Wall -Wextra
-fPIC
-fvisibility=hidden -fno-strict-aliasing -Wno-long-long
-m64
-rdynamic
-D_UNICODE -DUNICODE
CMakeFiles/Wrapper_Test.dir/test.cpp.o
/usr/local/lib/libboost_log.a
/usr/local/lib/libboost_system.a
/usr/local/lib/libboost_filesystem.a
/usr/local/lib/libboost_date_time.a
/usr/local/lib/libboost_thread.a
/usr/local/lib/libboost_log_setup.a
/usr/local/lib/libboost_chrono.a
-pthread
/home/nass/dev/Data_Parser/trunk/external/lib/linux64_gcc_release/libbus.a
-L/home/nass/dev/Data_Parser_build/lib #this is where the libwrap.so is located
-Wl,-rpath,/home/nass/dev/Data_Parser_build/lib
-lwrap #the shared lib
-o ../../../bin/Wrapper_Test
The link error is
CMakeFiles/Wrapper_Test.dir/test.cpp.o: In function `main':
test.cpp:(.text+0x2e): undefined reference to `wrapperNamespace::GetWrapper()'
collect2: error: ld returned 1 exit status
The GetWrapper() is located in libwrap.so of course, and I can verify it is a symbol that can be found in there:
$ nm -Ca ../../../lib/libwrap.so | grep GetWrapper
00000000000423d6 t wrapperNamespace::GetWrapper()
However, the linker cannot find it. what am I doing wrong here?
EDIT:
The linking command above is generated by the following CMakeLists.txt file:
set(TARGET_NAME Wrapper_Test)
#set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/bin)
#set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
#set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})
# INCLUDE INTERNAL FOLDER
include_directories(${CMAKE_SOURCE_DIR}/include/Wrapper)
add_executable(${TARGET_NAME} test.cpp)
add_boost_lib(${TARGET_NAME} log system filesystem date_time thread log_setup chrono)
setup_libbus(${TARGET_NAME}) #the libbus.a
target_link_libraries(${TARGET_NAME} -L../../../lib -lwrap)
set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER test)
I would start by looking at the cmake file which generates these lines.
It should be simple to add shared libaries to your path, for example:
find_library(
LIBWrapper
NAMES wrap
PATHS /home/nass/dev/Data_Parser_build/lib
)
And then link them to your test file, for example
add_executable(test src/test.cpp)
target_link_libraries(test ${LIBWrapper})
Similar should work for static libraries. This has the advantage that you don't have to deal with all the compiler/platform specific details which CMake supposedly handles for you, and which can be fairly complex & obscure.
If your library is generated dynamically, i.e. before cmake configuration time, you could just pass the appropriate linking flags to target_link_libraries:
target_link_libraries(test -L/home/nass/dev/Data_Parser_build/lib -lwrap)
I have used this suggestion in a few projects (e.g. https://github.com/caskorg/cask/blob/master/CMakeLists.txt) which dynamically generate the library, then link against it. If this doesn't work I suspect something else is wrong.
You should use -Wl,-Bstatic in front of static libs and -Wl,-Bdynamic for the dynamic ones. You also need to use -l for libraries and -L for library paths.
Something like:
$ /usr/bin/c++ test.cpp.o \
-L/usr/local/lib \
-Wl,-Bstatic \
-lboost_log \
-lsystem \
-L/home/nass/dev/Data_Parser_build/lib \
-Wl-Bdynamic \
-Wl,-rpath,/home/nass/dev/Data_Parser_build/lib \
-lwrap \
-o ../../../bin/Wrapper_Test
looks better. Many things depend on the version of your compiler/linker/OS.

gcc does not link properly against gsoap

I'm migrating my C++ application build system to cmake. One dependency is gsoap++, which generates a SOAP service and client. To link against it, I specify
set(CMAKE_CXX_LINK_FLAGS "-lgsoap++ ... [other flags]")
in the root CMakeLists.txt. The generated sources are created with
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src/generated/c++/ws)
set(GEN_WS_SOURCES ws/Bar.h ws/Bar_USCOREBinding.nsmap ws/soapBar_USCOREBindingProxy.h ws/soapBar_USCOREBindingProxy.cpp ws/soapBar_USCOREBindingService.h ws/soapBar_USCOREBindingService.cpp ws/soapH.h ws/soapC.cpp ws/soapStub.h)
add_custom_command(OUTPUT ${GEN_WS_SOURCES}
COMMAND wsdl2h -o ${CMAKE_BINARY_DIR}/src/generated/c++/ws/Bar.h -t wsdl/typemap.dat wsdl/bar.wsdl
COMMAND soapcpp2 -Lwxi -I/usr/include/gsoap ${CMAKE_BINARY_DIR}/src/generated/c++/ws/Bar.h -d ${CMAKE_BINARY_DIR}/src/generated/c++/ws
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
add_library(GEN_WS ${GEN_WS_SOURCES})
Everything seems to work properly, the sources get created. But when linking the application, I get lots of errors like
undefined reference to `soap_end'
undefined reference to `soap_sender_fault'
more undefined references to `soap_sender_fault' follow
If I compile with make VERBOSE=1, I see that -lgsoap++ is passed to /usr/bin/c++ when linking. Other libraries I link against don't throw any errors, but for some reason, the linker does not seem to find gsoap's functions. The library is present and there is no error that the linker can't find it.
The old, custom build script worked as expected; it also just passed -lgsoap++ to the linker. The only real difference I see is that my cmake build groups the sources into static libraries before attempting to link everything together. But I don't see how this can lead to all the symbols from the library not being found.
What's the problem here? Why can't the linker find gsoap's symbols?
Host OS is debian.
Edit: This is the complete linker command executed by cmake:
/usr/bin/c++ -w -Wall -pedantic -fdata-sections -ffunction-sections -std=c++0x
-lgsoap++ -lsoci_core -lsoci_sqlite3 -lmicrohttpd -lfetcp -lfeisc -lFedmIscCore
-lboost_system -lboost_thread -lboost_program_options -lboost_date_time
-lboost_unit_test_framework CMakeFiles/bar.dir/main.cpp.o
CMakeFiles/bar.dir/mainapp.cpp.o -o bar -rdynamic ../../libs/c++/libLIB_JSONXX.a
../../libs/c++/libLIB_CONFIG.a ../../libs/c++/libLIB_DB.a ../../libs/c++/libLIB_OBID.a
../../libs/c++/libLIB_HELPER.a ../../generated/c++/libGEN_WS.a
../../generated/c++/libGEN_DB.a ../../generated/c++/libGEN_CONFIG.a
libMAIN_CONTROLLERS.a libMAIN_HARDWARE.a libMAIN_HELPER.a libMAIN_MODEL.a
libMAIN_NETWORK.a libMAIN_SYSTEM.a ../../generated/c++/libGEN_DB.a
../../libs/c++/libLIB_DB.a ../../generated/c++/libGEN_CONFIG.a
../../libs/c++/libLIB_JSONXX.a ../../libs/c++/libLIB_CONFIG.a
../../libs/c++/libLIB_HELPER.a ../../libs/c++/libLIB_OBID.a
../../generated/c++/libGEN_WS.a
I'm not sure if it will directly solve your problem, but this:
set(CMAKE_CXX_LINK_FLAGS "-lgsoap++ ... [other flags]")
Should be:
set(CMAKE_CXX_LINK_FLAGS "[other flags]")
target_link_library(GEN_WS gsoap++) # after add_library of course

How to compile cpp source to full static binary and except for: libc, libpthread, libdl, libstdc++, and libm

I want to compile a binary linux to full static, But I always fail with this configuration:
CFLAGS="--static" CPPFLAGS="-I/home/alan/arm/arm-none-linux-gnueabi/libc/usr/include" LDFLAGS="-L/home/alan/arm/arm-none-linux-gnueabi/libc//usr/lib" LIBS="-lcrypt -ldl -lpthread -lm -lc -lstdc++" CC=arm-none-linux-gnueabi-gcc AR=arm-none-linux-gnueabi-ar CXX=arm-none-linux-gnueabi-g++ ./configure --host=arm-none-linux-gnueabi target=arm-none-linux-gnueabi --prefix=/home/alan/armbin/test --without-pcre --without-zlib --without-bzip2 --without-openssl --disable-ipv6 --enable-static
But I always get warning dlopen, gethostbyname, etc. If I not except libc, libm, etc to shared. I get warning approximately as below:
warning: Using 'gethostbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
Thank you very much.
Use -pthread instead of -lpthread.
Source.
Your link command should look like this:
g++ objectFiles $(CFLAGS) -o executable -Wl,-Bstatic -L/path/to/static/lib1/ -ls1 -L/path/to/static/lib2 -ls2 -Wl,-Bdynamic
You only have to explicitly call the static libraries libs1.a, libs2.a . The shared system libraries you are referring to (libc.so, libpthreads.so, libm.so, libdl.so, libstdc++.so etc.) should be found implicitly by your linker and are affected by -Wl, -Bdynamic. You don't have to pass them explicitly. Try and do a
"ldd executable" to see the dynamic dependencies.

how to do static linking of libwinpthread-1.dll in mingw?

I use mingw from here: http://sourceforge.net/projects/mingwbuilds/files/host-windows/releases/4.7.2/32-bit/threads-posix/sjlj/x32-4.7.2-release-posix-sjlj-rev2.7z/download
And I've sucessfully managed to link statically libstdc++-6.dll and libgcc_s_sjlj-1.dll by using -static-libgcc -static-libstdc++ parameters, but I cannot find a command for doing the same with libwinpthread-1.dll.
If your toolchain includes the static winpthreads, adding the option
-static
Will pull in static versions of all libraries it can.
Alternatively, you can remove libwinpthread.dll.a and the DLL itself from the toolchain directories. This might mess up programs linking with libstdc++ and libgcc DLLs though, so be careful.
A third option is to use -Wl,-Bdynamic and -Wl,-Bstatic to select which version you want linked in (which is what -static internally does when ld is called). An example:
gcc -o someexec someobject.o -Wl,-Bdynamic -lsomelibIwantshared -Wl,-Bstatic -lsomelibIwantstatic
If you run your link command with -v added, you should see these options appearing in the ld/collect2 invocation when you use -static-libgcc and -static-libstdc++.
Try this:
-static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic
Notice the -lstdc++ before -lpthread. It worked for me.
Make sure to add this to the very end of your g++ command line.
You should probably check command line options documentation for GCC.
These's no '-static-something' command, only standard libraries (libgcc and libstdc++) can be set to static linking with one command. For other libraries, you first switch to static linking with "-static" and then list the libraries to include with separate commands, ie "-lpthread".
To statically link winpthread even if threading isn't used in the program, pass the -Bstatic and --whole-archive parameters to the linker:
g++ -o hello.exe hello.cpp -Wl,-Bstatic,--whole-archive -lwinpthread -Wl,--no-whole-archive
Note the following:
The "whole archive" option should be disabled immediately afterwards.
You don't need to do this hack if your program actually uses symbols from the library (i.e. you use <thread> from C++11), in which case the library won't get dropped when you statically link it.
This hack is intended for MinGW-w64, to fix the libwinpthread-1.dll dependencies.
For anyone working in CMake, this solution is readily implemented in your CMakeLists.txt file as follows...
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
Apparently, CMake does some weird stuff with how the -Wl compiler flags are handled, making the -Wl,-Bstatic -lstdc++ -lwinpthread -Wl,-Bdynamic solution not work, with only two other options seemingly left: the bad compiler flag -static and the ugly compiler flag -Wl,--whole-archive.
Meanwhile, the good option that actually works in CMake, yet seems rather undocumented, is to directly use the linker flags. Hence, in CMake, this seems to be the best way to statically link to all the mingw-w64 C++ dependencies:
target_link_libraries (MyVeryAwesomeApp -static gcc stdc++ winpthread -dynamic)
It should be noted that even if there isn't a library explicitly following -dynamic, it should still be applied in order to ensure that the standard, implicitly linked libraries get linked correctly.
I circumvented this problem by using win32 variant of the mingw toolchain instead of the posix variant. With the win32 variant, -static-libgcc -static-libstdc++ is sufficient for an independent build.
If you are using toolchains from MSys2 (tested with gcc version 12) ,the only way to use winpthread statically is standard -static (forces all library to be static) or simply delete/rename the libwinpthread.dll.a && libpthread.dll.a imp libs.
Other methods like "Bstatic", "-l:libwinpthread.a" will no longer work (due to dependency hell, especially in CMake). The one with whole archive still works but it overrides the program properties.
You may also want static link libgcc and libstdc++ if the dependency comes from them.
Just link with -l:libwinpthread.a
Just add -static to your CFLAGS.
For example: ./configure CFLAGS="-static".
This will link all static libraries to your executable file.