Undefined Reference in self-built shared library with cmake - c++

I am currently trying to utilize a self-built shared library. The Library FooBar utilizes the "Foo" Library to do some costly calculations. "Foo" however needs "Bar", the licensing library. It has been successfully cross-compiled when using the following CMakeLists.txt:
cmake_minimum_required(VERSION 3.12)
project(FooBar LANGUAGES CXX)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
find_library(Curl
NAMES curl)
find_library(Foo
NAMES foo)
find_library(Bar
NAMES bar)
file(GLOB OBJECT_FILES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/resources/*.o)
file(GLOB SOURCE_FILES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/*.cpp)
add_library(FooBar SHARED ${SOURCE_FILES} ${OBJECT_FILES})
target_link_libraries(FooBar PRIVATE Threads::Threads)
target_link_libraries(FooBar PRIVATE -L${Foo} -L${Bar} -L${Curl})
The compilation is successful without any errors but when I want to include it in the executable, "FooBar" does give me an undefined reference to an function in "Bar", the licensing library. I already checked the "Bar"-library, it contains the used function!
CMakeLists.txt of the executable:
cmake_minimum_required(VERSION 3.12)
project(FooBarExe)
add_subdirectory(FooBar)
add_executable(FooBarExe ${FooBarExe_SRC} ${FooBarExe_INC}) # FooBarExe is just the placeholder for its original name!
target_link_libraries(FooBarExe PRIVATE FooBar)
Error message:
"FooBarExe/FooBar/FooBar.so: undefined reference to 'function'
collect2: error: ld returned 1 exit status"
Does anyone have another idea to solve this issue? I already reordered the libraries in target_link_libraries, compiled it as a static library, included and linked both libraries to FooBarExe via set_target_properties and INTERFACE_LINK_LIBRARIES without any success...
Edit:
I tried the following suggested solutions:
Removing "-L" when adding Library
Added a Check after the find_library(Bar ...)

There was an interface change in the "Bar" Library, which went unnoticed by me. Had to replace the header file and the library. Found the change by looking deeper into the library, using the nm -gDC xxxx.so command.
Thank you all for your help, the error was done by me...

Related

How to properly link to libraries in CMake (using Boehm GC)?

I've been trying to figure this out for a while, but can't seem to get it working. I've already checked a bunch of posts.
I have a static library libRuntime.a, generated like so:
cmake_minimum_required(VERSION 3.15)
project(Runtime)
set(CMAKE_CXX_STANDARD 17)
add_library(Runtime library.cpp)
find_library(GC gc)
message(${GC})
target_link_libraries(Runtime PUBLIC ${GC})
library.cpp uses Boehm GC, which is why I'm also linking it with my Runtime target.
Now, I want to call functions from my libRuntime.a, so I have the following other CMake project:
cmake_minimum_required(VERSION 3.15)
project(test)
set(CMAKE_CXX_STANDARD 17)
add_executable(test main.cpp)
find_library(TESTLIB Runtime lib)
message(${TESTLIB})
target_link_libraries(test ${TESTLIB})
I have pasted library.h into the project, and also pasted libRuntime.a into a directory called lib, so the definitions are known and the library is found. Calling functions from my Runtime library now gives me:
/path.../Scheme-Compiler/Runtime/library.cpp:12: undefined reference to `GC_init'
/usr/bin/ld: ../lib/libRuntime.a(library.cpp.o): in function `alloc_atom':
Thanks in advance
Because you use a separate CMake invocation to create the executable, the properties of the Runtime CMake target from your first project are not known. Specifically, CMake will not know any of the Runtime library's dependencies (i.e. GC), so you have to list them explicitly when linking Runtime to your executable:
cmake_minimum_required(VERSION 3.15)
project(test)
set(CMAKE_CXX_STANDARD 17)
add_executable(test main.cpp)
find_library(TESTLIB Runtime lib)
message(${TESTLIB})
# Find GC library.
find_library(GC gc)
# Link GC here, along with the Runtime library.
target_link_libraries(test PRIVATE ${GC} ${TESTLIB})

How to build static library with bundled dependencies - CMake

I am currently using CMake to create a static library which utilizes a few of the static libraries from OpenCV 4 ( core imgcodecs video highgui imgproc ). My intention is to be able to bundle all of the required OpenCV static libraries into my own library so that I can distribute it as one library. Additionally, I want for the user of my library to not have to install OpenCV 4 on their system (but do not mind if the user has to do simple installs using apt-get install). I know there are tools for bundling static libraries (such as using ar for linux).
However, where I really am having the issue is with all the dependencies of OpenCV (such as libjpeg, libpng, etc). I don't necessarily mind if these libraries are bundled with mine or linked dynamically as they are relatively easy to install (can be installed with sudo apt-get install, whereas opencv4 needs to be built from source).
What is the best way to go about doing this?
This is my current CMakeLists.txt
It is currently working, but that is because I am using find_package(OpenCV REQUIRED) (which defeats the purpose of what I am trying to do). When I remove that line, the linker complains about not being able to find the OpenCV dependencies.
cmake_minimum_required(VERSION 2.8)
project(myproject)
set(CMAKE_CXX_STANDARD 14)
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
link_directories(${CMAKE_CURRENT_LIST_DIR}/lib)
find_package(OpenMP REQUIRED)
find_package(OpenCV REQUIRED)
set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(nlohmann_json)
list(APPEND LINKER_LIBS opencv_core opencv_highgui opencv_video opencv_imgcodecs libmxnet.so libncnn.a nlohmann_json::nlohmann_json)
file(GLOB SRC${CMAKE_CURRENT_LIST_DIR}/src/*.cpp${CMAKE_CURRENT_LIST_DIR}/main.cpp)
add_library(myproject ${SRC})
target_link_libraries(myproject ${LINKER_LIBS} ${OpenMP_CXX_FLAGS})
To elaborate on my question. I build my project which generates libmyproject.a. I then take this library and will eventually extract the symbols from the OpenCV libs (libopencv_core.a libopencv_highgui.a libopencv_imgcodecs.a libopencv_video.a) and add them to my lib (for the time being, I have not yet done this step, which is why in the below example I am linking libopencv_*). I then use my library in a new project, for which the CMakeLists.txt is shown below:
cmake_minimum_required(VERSION 2.8)
project(myproject-driver)
set(CMAKE_CXX_STANDARD 14)
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
link_directories(${CMAKE_CURRENT_LIST_DIR}/lib)
find_package(OpenMP REQUIRED)
add_executable(myproject-driver main.cpp)
target_link_libraries(myproject-driver myproject libncnn.a ${OpenMP_CXX_FLAGS} libmxnet.so libopencv_core.a libopencv_highgui.a libopencv_imgcodecs.a libopencv_video.a)
Building this generates the following errors:
Linking CXX executable myproject-driver
/usr/bin/ld: /home/nchafni/Cyrus/myproject/lib/libopencv_imgcodecs.a(grfmt_jpeg.cpp.o): undefined reference to symbol 'jpeg_default_qtables##LIBJPEG_8.0'
//usr/lib/x86_64-linux-gnu/libjpeg.so.8: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
How can I fix this. Is there some CMake command which will link all these dependencies for me? Do I need to manually track down each dependency of those libopencv_* libs and link those manually? Once again, this is assuming that the person using libmyproject.a can't use find_package(OpenCV REQUIRED) as it won't be defined as they have not installed OpenCV on their machine.
First of all, don't use the super old and outdated version 2.8 of CMake. CMake 3.x is so much more powerful and pretty straightforward to use.
Some tips for modern CMake.
Don't use file(GLOB), see here why that is.
Don't use directory wide instructions, rather use target instructions, e.g. target_include_directories vs. include_directories.
Don't use string variables like ${<PACKAGE_NAME>_LIBRARIES}, rather use targets, e.g. <Package_NAME>::lib
When using targets instead of string variables, all the properties (including LINK_INTERFACE) of that target will be populated to the library/executable when calling target_link_libraries, so no more include_directories,link_directories, etc.
myproject
cmake_minimum_required(VERSION 3.14)
project(myproject)
set(CMAKE_CXX_STANDARD 14)
find_package(OpenMP REQUIRED)
find_package(OpenCV REQUIRED)
set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(nlohmann_json)
set(SOURCES ...) # list all the source files here
add_library(myproject ${SOURCES})
target_include_directories(myproject PUBLIC # give it a scope
${CMAKE_CURRENT_LIST_DIR}/include
)
target_link_libraries(myproject PUBLIC # give it a scope
opencv_core # using the target, you will get all LINK_LIBRARIES
opencv_highgui
opencv_video
opencv_imgcodecs
libmxnet.so # where is this coming from?
libncnn.a # where is this coming from?
nlohmann_json::nlohmann_json
OpenMP::OpenMP_CXX ## linking against a target, CXX_FLAGS will be populated automatically
)
myprojec-driver
cmake_minimum_required(VERSION 3.14)
project(myproject-driver)
set(CMAKE_CXX_STANDARD 14)
add_executable(myproject-driver main.cpp)
target_link_libraries(myproject-driver PUBLIC # give it a scope
myproject # gets all dependencies through the LINK_INTERFACE
)

CMake - Can't link shared library (subdirectory)

I am using CLion and mingw-w64.
My executable's CMakeLists.txt:
cmake_minimum_required(VERSION 3.12)
project(test_exe)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "D:\\test")
add_subdirectory(test_lib)
include_directories(test_lib/include;test_lib/deps/include)
link_directories(test_lib/deps/lib)
add_executable(test_exe main.cpp)
target_link_libraries(test_exe test_lib)
test_lib's CMakeLists.txt:
cmake_minimum_required(VERSION 3.12)
project(test_lib)
set(CMAKE_CXX_STANDARD 17)
include_directories(include;deps/include)
link_directories(deps/lib)
file(GLOB_RECURSE LIB_SOURCES "include/*.*" "src/*.*")
add_library(test_lib SHARED ${LIB_SOURCES})
target_link_libraries(test_lib libfreetype.a libpugixml.dll.a)
The problem is that when I add library with add_library(test_lib SHARED ${LIB_SOURCES}) I get undefined reference errors but when I add the library with add_library(test_lib ${LIB_SOURCES}) it works perfectly.
An empty project links as expected (both SHARED and STATIC) but I wonder why this one is not working? Because of the libraries I link in the test_lib's CMakeLists.txt?
When you build a static library, there is no linking taking place. It doesn't check that you have all required libraries.
When you do the same for shared librarie,s then on some platforms (like Windows, or on Linux with -X defs, I think), you need to solve all references.
And you have a shared library.

CMake doesn't work with Google Protobuf

Unable to link protobuf library using CMake. My CMakeLists is
cmake_minimum_required(VERSION 3.6)
project(addressbook)
set(CMAKE_CXX_STANDARD 11)
set(PROJECT_NAME addressbook)
ADD_SUBDIRECTORY(proto)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
ADD_EXECUTABLE(main main.cpp)
TARGET_LINK_LIBRARIES(main proto ${PROTOBUF_LIBRARY})
and in proto subdirectory there is another CMakeLists.txt (that way it is done in github repo https://github.com/shaochuan/cmake-protobuf-example)
INCLUDE(FindProtobuf)
FIND_PACKAGE(Protobuf REQUIRED)
INCLUDE_DIRECTORIES(${PROTOBUF_INCLUDE_DIR})
PROTOBUF_GENERATE_CPP(PROTO_SRC PROTO_HEADER message.proto)
ADD_LIBRARY(proto ${PROTO_HEADER} ${PROTO_SRC})
But my IDE still outputs buch of lines like
CMakeFiles/main.dir/main.cpp.o: In function main':
/home/camille/ClionProjects/protobuf/main.cpp:42: undefined reference
togoogle::protobuf::internal::VerifyVersion(int, int, char const*)'
/home/camille/ClionProjects/protobuf/main.cpp:49: undefined reference
to tutorial::AddressBook::AddressBook()'
/home/camille/ClionProjects/protobuf/main.cpp:54: undefined reference
togoogle::protobuf::Message::ParseFromIstream(std::istream*)'
Where is my mistake? How do I make it work?
Your program fails to link because ${PROTOBUF_LIBRARY} is empty in the scope of your top-level CMakeLists.txt. This happens because calling add_subdirectory creates a child scope, and the Protobuf_XXX variables set by find_package(Protobuf REQUIRED) are only in that child scope.
A good way to fix this is to add the following to proto/CMakeLists.txt:
target_link_libraries(proto INTERFACE ${Protobuf_LIBRARIES})
This instructs targets that link to proto to also link to ${Protobuf_LIBRARIES}. Now you can simplify target_link_libraries in your top-level CMakeLists.txt:
target_link_libraries(addressbook proto)
On a side note, you could also use e.g.
target_link_libraries(${PROJECT_NAME} INTERFACE ... )
${PROJECT_NAME} resolves to whatever you have set in the project(...) statement in that CMakeLists.txt file.
Finally, note that this links to Protobuf_LIBRARIES instead of PROTOBUF_LIBRARY. Protobuf_LIBRARIES includes both the Protocol Buffers libraries and the dependent Pthreads library.
Watch out for variable name case: With CMake 3.6 and later, the FindProtobuf module input and output variables were all renamed from PROTOBUF_ to Protobuf_ (see release notes), so using Protobuf_ works with CMake 3.6, but fails with undefined reference with an earlier version.
To be on the safe side, either use the old style
target_link_libraries(${PROJECT_NAME} INTERFACE ${PROTOBUF_LIBRARIES}))
or force everyone to use at least CMake 3.6
cmake_minimum_required(VERSION 3.6)
Also, there is a resolved bug report in the Kitware cmake issue tracker with more information on how to diagnose such issues.
The variable you need to pass to target_link_libraries is Protobuf_LIBRARIES. See documentation.
CMakeLists.txt:
cmake_minimum_required(VERSION 3.12)
project(protobuf)
SET(CMAKE_CXX_FLAGS "-g -Wall -Werror -std=c++11")
set(CMAKE_CXX_STANDARD 11)
INCLUDE(FindProtobuf)
FIND_PACKAGE(Protobuf REQUIRED)
INCLUDE_DIRECTORIES(${PROTOBUF_INCLUDE_DIR})
PROTOBUF_GENERATE_CPP(PROTO_SRC PROTO_HEADER addressbook.proto)
ADD_LIBRARY(proto2 ${PROTO_HEADER} ${PROTO_SRC})
TARGET_LINK_LIBRARIES(proto2)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
add_executable(protobuf main.cpp)
TARGET_LINK_LIBRARIES(protobuf proto2 ${PROTOBUF_LIBRARY})

Unable to link an executable to shared library

I have a cmake project where I am trying to link the executable to the shared library. But it is not getting linked. After searching enough and not finding any useful solutions, I'm posting my question here, please let me know if there are any obvious mistakes as I am not familiar with cmake
Here is my CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
# Set Project Name
set(PROJECT_NAME myproj)
project(${PROJECT_NAME})
# Tell Cmake to invoke gcc with specific flags. Use c++11 standard.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# Include all headers
include_directories(${PROJECT_SOURCE_DIR}/../public/include)
# Driver Program
file(GLOB_RECURSE SOURCE_FILES
"${PROJECT_SOURCE_DIR}/src/*.cpp"
)
# Dont know what to do about this
#set( CMAKE_SKIP_BUILD_RPATH true)
link_directories(${CMAKE_SOURCE_DIR}/../relative/path/to/sharedlib) #contains libshared.so
# Generate Executable, trying to generate shared library to see if it generates # correct dependencies
# add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES})
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
#Link the libraries
target_link_libraries(${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/../relative/path/to/sharedlib/libshared.so)
Here is the steps I have tried and some observations.
If I generate a shared library instead of an executable, it is able to find the dependent shared library(i.e libshared.so)
ldd myproj.so
libshared.so => full/path/to/sharedlib/libshared.so (0x00007fa26c263000)
I did not fully understand what it meant, but it is able to find the shared library is what I guessed.
If I try to generate an executable instead of shared library, I see undefined reference errors to the functions within the library.
I tried just target_link_libraries(${PROJECT_NAME} shared) as well and it gave same error.
Any clues as to how can I get the shared library linked to my executable? Thanks