Why doesn't CMake put the `-L` flag in appropriate places? - c++

I'm trying to build a shared library with cmake on linux. The portion of code responsible for setting this up is:
if (BUILD_SHARED)
set(BUILD_SHARED_LIBS ON)
set(CMAKE_VERBOSE_MAKEFILE ON)
# shared library
add_library(semsim SHARED "${SEMSIM_HEADERS}" "${SEMSIM_SOURCES}")
#Note: not sure if this command does anything. It was supposed to be to link static libraries into my shared one
# target_link_options(semsim PUBLIC -Wl,--whole-archive)
#
message(STATUS "LIBBZ_INCLUDE_DIR ${LIBBZ_INCLUDE_DIR}")
target_include_directories(semsim PUBLIC # make public so include dirs propagate into test binaries
${CMAKE_SOURCE_DIR}
${LIBXML2_INCLUDE_DIR}
${LIBSBML_DEPS_INCLUDE_DIR}
${LIBSBML_INCLUDE_DIR}
${ZLIB_INCLUDE_DIR}
${ZIPPER_INCLUDE_DIR}
${LIBCOMBINE_INCLUDE_DIR}
${BZ2_INCLUDE_DIR}
${REDLAND_INCLUDE_DIR}
${RASQAL_INCLUDE_DIR}
${RAPTOR2_INCLUDE_DIR}
)
target_link_libraries(semsim PUBLIC # make public so libraries propagate into test binaries
${LIBCOMBINE_STATIC_LIBRARY}
${ZIPPER_STATIC_LIBRARY}
${ZLIB_STATIC_LIBRARY}
${LIBSBML_STATIC_LIB}
${LIBBZ_STATIC_LIBRARY}
${REDLAND_LIBRARY}
${RASQAL_LIBRARY}
${RAPTOR2_LIBRARY}
${LIBXML2_STATIC_LIBRARY}
dl
${BZ2_STATIC_LIBRARY}
curl xslt
)
endif ()
This produces the following command for execution:
/usr/bin/c++ -fPIC -std=c++14 -g -shared -Wl,-soname,libsemsim.so -o libsemsim.so CMakeFiles/semsim.dir/CurlGet.cpp.o CMakeFiles/semsim.dir/RDFNode.cpp.o CMakeFiles/semsim.dir/Subject.cpp.o CMakeFiles/semsim.dir/Predicate.cpp.o CMakeFiles/semsim.dir/Resource.cpp.o CMakeFiles/semsim.dir/Triple.cpp.o CMakeFiles/semsim.dir/SemsimUtils.cpp.o CMakeFiles/semsim.dir/MetaID.cpp.o CMakeFiles/semsim.dir/XmlAssistant.cpp.o CMakeFiles/semsim.dir/Reader.cpp.o CMakeFiles/semsim.dir/Editor.cpp.o CMakeFiles/semsim.dir/Writer.cpp.o CMakeFiles/semsim.dir/RDF.cpp.o CMakeFiles/semsim.dir/Participant.cpp.o CMakeFiles/semsim.dir/PhysicalEntity.cpp.o CMakeFiles/semsim.dir/PhysicalPhenomenon.cpp.o CMakeFiles/semsim.dir/PhysicalProcess.cpp.o CMakeFiles/semsim.dir/PhysicalPropertyResource.cpp.o CMakeFiles/semsim.dir/PhysicalForce.cpp.o CMakeFiles/semsim.dir/Query.cpp.o CMakeFiles/semsim.dir/SemsimCombineArchive.cpp.o CMakeFiles/semsim.dir/Triples.cpp.o ../../third_party/libCombine-0.2.3/INSTALL/lib/libCombine-static.a ../../third_party/zipper/INSTALL/lib/libZipper-static.a ../../third_party/zlib-1.2.11/INSTALL/lib/libz.a /usr/local/lib/libxml2.a -ldl ../../third_party/libsbml-dependencies/INSTALL/lib/libbz2.a -lcurl -lxslt
If I let CMake try to build with this command I get the following error:
/usr/bin/ld: /usr/local/lib/libxml2.a(uri.o): relocation R_X86_64_PC32 against symbol `xmlFree' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Bad value
However, I've noticed that the command produced by CMake is lacking -L flags. So I changed the command to :
/usr/bin/c++ -fPIC -std=c++14 -g -shared -o libsemsim.so CMakeFiles/semsim.dir/CurlGet.cpp.o CMakeFiles/semsim.dir/RDFNode.cpp.o CMakeFiles/semsim.dir/Subject.cpp.o CMakeFiles/semsim.dir/Predicate.cpp.o CMakeFiles/semsim.dir/Resource.cpp.o CMakeFiles/semsim.dir/Triple.cpp.o CMakeFiles/semsim.dir/SemsimUtils.cpp.o CMakeFiles/semsim.dir/MetaID.cpp.o CMakeFiles/semsim.dir/XmlAssistant.cpp.o CMakeFiles/semsim.dir/Reader.cpp.o CMakeFiles/semsim.dir/Editor.cpp.o CMakeFiles/semsim.dir/Writer.cpp.o CMakeFiles/semsim.dir/RDF.cpp.o CMakeFiles/semsim.dir/Participant.cpp.o CMakeFiles/semsim.dir/PhysicalEntity.cpp.o CMakeFiles/semsim.dir/PhysicalPhenomenon.cpp.o CMakeFiles/semsim.dir/PhysicalProcess.cpp.o CMakeFiles/semsim.dir/PhysicalPropertyResource.cpp.o CMakeFiles/semsim.dir/PhysicalForce.cpp.o CMakeFiles/semsim.dir/Query.cpp.o CMakeFiles/semsim.dir/SemsimCombineArchive.cpp.o CMakeFiles/semsim.dir/Triples.cpp.o -L ../../third_party/libCombine-0.2.3/INSTALL/lib/libCombine-static.a -L ../../third_party/
zipper/INSTALL/lib/libZipper-static.a -L ../../third_party/zlib-1.2.11/INSTALL/lib/libz.a -L /usr/local/lib/libxml2.a -l
dl -L ../../third_party/libsbml-dependencies/INSTALL/lib/libbz2.a -lcurl -lxslt
ran it in a shell and it worked. Could anybody suggest why CMake is not using the -L flag in the appropriate places?

Related

`ld` undefined reference error, but libraries are linked to by CMake and symbols exist

I have a CMake file like this:
cmake_minimum_required(VERSION 3.12)
project(cpp-service VERSION 0.1.0)
add_compile_definitions(OPENVDB_7_ABI_COMPATIBLE)
list(APPEND CMAKE_MODULE_PATH "/usr/local/lib64/cmake/OpenVDB/")
find_package(OpenVDB REQUIRED)
### https://stackoverflow.com/a/69290761/3405291
list(APPEND CMAKE_MODULE_PATH "deps/tbb/cmake/")
find_package(TBB REQUIRED)
add_executable(${PROJECT_NAME}
src/main.cpp
)
target_link_libraries(${PROJECT_NAME} PUBLIC
OpenVDB::openvdb
TBB::tbb
)
All the libraries are linked-to by CMake. The symbols are all available. But, linker cannot link find symbols at all. With many errors like this:
...
[ 92%] Building CXX object ...
[ 96%] Building CXX object ...
[100%] Linking CXX executable cpp-service
/usr/lib64/gcc/x86_64-suse-linux/7/../../../../x86_64-suse-linux/bin/ld: CMakeFiles/cpp-service.dir/src/main.cpp.o: in function `hollowing::mesh_to_grid(hollowing::Contour const&, openvdb::v7_2::math::Transform const&, float, float, int)':
/home/m3/repos/cpp-service/src/hollowing.h:268: undefined reference to `openvdb::v7_2::initialize()'
/usr/lib64/gcc/x86_64-suse-linux/7/../../../../x86_64-suse-linux/bin/ld: CMakeFiles/cpp-service.dir/src/main.cpp.o: in function `tbb::task_group_context::task_group_context(tbb::task_group_context::kind_type, unsigned long)':
/home/m3/repos/cpp-service/deps/tbb/include/tbb/task.h:499: undefined reference to `tbb::task_group_context::init()'
...
Guess
One guess is that I'm having this problem:
...It seems that gcc now send the linker flag --as-needed to ld. This has the effect of discarding any specified libraries that do not have symbols that are required for linking.
Tried
I tried this, but it had no effect. The same link errors are thrown:
target_link_options(${PROJECT_NAME} PUBLIC
"LINKER:-no-as-needed"
)
Question
I ran out of options debugging this problem. Does anybody have any suggestion to try out?
CMake commands
export CC=/usr/bin/gcc-11
export CXX=/usr/bin/g++-11
mkdir build
cd build
cmake ..
cmake --build . --verbose >> log.txt 2>&1 # Save log to file.
Compile commands
CMake shows the compile logs are like:
[ 3%] Building CXX object CMakeFiles/cpp-service.dir/src/main.cpp.o
/usr/bin/g++-11 -I/home/m3/repos/cpp-service/deps/tbb/include -I/.../more/include/paths/.../... -g -std=c++17 -MD -MT CMakeFiles/cpp-service.dir/src/main.cpp.o -MF CMakeFiles/cpp-service.dir/src/main.cpp.o.d -o CMakeFiles/cpp-service.dir/src/main.cpp.o -c /home/m3/repos/cpp-service/src/main.cpp
Link command
CMake log shows the link command is:
[100%] Linking CXX executable cpp-service
/usr/local/bin/cmake -E cmake_link_script CMakeFiles/cpp-service.dir/link.txt --verbose=1
/usr/bin/g++-11 -g CMakeFiles/cpp-service.dir/src/main.cpp.o CMakeFiles/cpp-service.dir/src/***.cpp.o CMakeFiles/cpp-service.dir/src/***more***object***files***.cpp.o -o cpp-service
The errors are thrown exactly right after the above link command. Errors like:
undefined reference to `openvdb::v7_2::initialize()'
Symbols
Symbols are defined inside the linked libraries:
nm /usr/local/lib64/libopenvdb.so | less
The above command shows the initialize symbol is available:
Fix 🔧
The cause of the linker errors was this statement:
if(LINUX)
The fix was to replace it with this:
if(UNIX AND NOT APPLE)
This commit fixes the problem:
Reference: https://stackoverflow.com/a/40152725/3405291
Strangely, CMake wasn't complaining about anything and just throwing random linker errors.

A problem with automake Mingw cross-compiling of a shared library linked to an installed static library

I am building a test hello-world executable module linking to a simple shared object library 'libshared' which, in turn, links to a static library 'libsodium' that was built in the 'depends' directory.
OS is Ubuntu 16.04.6 LTS.
When I build this for Linux the build is okay.
When I build this for Mingw this libtool error message is printed:
*** Warning: This system cannot link to static lib archive /home/ubuntu/repo/test-dll/depends/x86_64-w64-mingw32/share/../lib/libsodium.la.
*** I have the capability to make that library automatically link in when
*** you link to this library. But I can only do this if you have a
*** shared version of the library, which you do not appear to have.
and recipe for target 'libshared.la' failed.
The Makefile.am:
.PHONY: gen
.INTERMEDIATE: $
# shared lib
lib_LTLIBRARIES = libshared.la
libshared_la_SOURCES = \
src/example_dll.c
libshared_la_CPPFLAGS = -I$(top_srcdir)/src -I /src
libshared_la_CFLAGS = -DBUILDING_EXAMPLE_DLL -O2 -Wno-unused-parameter
libshared_la_LDFLAGS = -no-undefined
libshared_la_LIBADD = -lsodium
#libshared_la_LIBADD = $(prefix)/lib/libsodium.la
# executable
inst_PROGRAMS = testexe
instdir=$(prefix)/bin
testexe_SOURCES = \
src/example_exe.c
testexe_CFLAGS = -DBUILDING_EXAMPLE_DLL -O2 -Wno-unused-parameter
testexe_CPPFLAGS = -I$(top_srcdir)/src
testexe_LDADD = libshared.la
How I invoke configure and make (build_win.sh):
export HOST=x86_64-w64-mingw32
CXX=x86_64-w64-mingw32-g++-posix
CC=x86_64-w64-mingw32-gcc-posix
PREFIX="$(pwd)/depends/$HOST"
set -eu -o pipefail
set -x
cd depends/ && make HOST=$HOST V=1 NO_QT=1
cd ../
./autogen.sh
CONFIG_SITE=$PWD/depends/x86_64-w64-mingw32/share/config.site ./configure --prefix="${PREFIX}" --host=x86_64-w64-mingw32
CC="${CC} -g " CXX="${CXX} -g " make V=1
configure.ac params is trivial
The only way I managed to build it was if I manually moved libsodium.la into the root of the distribution and 'libsodium.a' in the '.libs' subdirectory and manually corrected libsodium.la parameters 'libdir to '' (was '/home/ubuntu/repo/test-dll/depends/x86_64-w64-mingw32/share/../lib/libsodium.a') and 'installed' to 'no' (was 'yes').
So my questions are:
Why is it trying to link libsodium as a shared library when it is in 'depends' directory and set as 'installed=yes' (I need to link it as static)?
Could I have correct automake settings for MINGW to link my shared library to static libs in 'depends' directory?
Just found a solution (or workaround) how to make this link on Mingw (and produce dll):
instead of libshared_la_LIBADD = -lsodium that causes libtool to search for the libsodium shared library, I just passed an option -Wl,-lsodium to the linker in libshared_la_CFLAGS.

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

Linking problems with cmake and cygwin

Here's the setup:
set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .dll.a .a)
set(MATLAB_MEXFILE_EXT mexw64)
find_file(MATLAB_ROOT matlab $ENV{ProgramW6432})
set(MATLAB_LIBRARIES_DIR ${MATLAB_ROOT}/extern/lib/win64/microsoft)
find_path(MATLAB_INCLUDE_DIR
NAMES mex.h
PATHS ${MATLAB_ROOT}
PATH_SUFFIXES extern/include
NO_DEFAULT_PATH
)
include_directories(${MATLAB_INCLUDE_DIR})
find_library(MATLAB_MEX_LIB mex ${MATLAB_LIBRARIES_DIR} NO_DEFAULT_PATH)
find_library(MATLAB_MX_LIB mx PATHS ${MATLAB_LIBRARIES_DIR} NO_DEFAULT_PATH)
find_library(MATLAB_MAT_LIB mat PATHS ${MATLAB_LIBRARIES_DIR} NO_DEFAULT_PATH)
set(MATLAB_LIBRARIES ${MATLAB_MEX_LIB} ${MATLAB_MX_LIB} ${MATLAB_MAT_LIB})
add_library(mytest SHARED mytest.cpp)
target_link_libraries(mytest ${MATLAB_LIBRARIES})
But when I run make I get errors of the type:
undefined reference to `_mexErrMsgIdAndTxt'
Clearly a linking issue. But why is it occuring? Here is the output of the make command:
/usr/bin/c++.exe -Dmytest_EXPORTS -I"/cygdrive/c/Program Files/matlab/extern/include" -o CMakeFiles/mytest.dir/mytest.cpp.o -c /home/hous/workspace/mytest.cpp
/usr/bin/c++.exe -shared -Wl,--enable-auto-import -o mytest.mexw64 -Wl,--out-implib,libmytest.dll.a -Wl,--major-image-version,0,--minor-image-version,0 CMakeFiles/mytest.dir/mytest.cpp.o "/cygdrive/c/Program Files/matlab/extern/lib/win64/microsoft/libmex.lib" "/cygdrive/c/Program Files/matlab/extern/lib/win64/microsoft/libmex.lib" "/cygdrive/c/Program Files/matlab/extern/lib/win64/microsoft/libmx.lib" "/cygdrive/c/Program Files/matlab/extern/lib/win64/microsoft/libmat.lib"
All seems to be correct?