CMake - Can't link shared library (subdirectory) - c++

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.

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
)

Link boost libraries statically using CMake in CLion

CLion 1.2, with bundled CMake 3.3.2 and MinGW-w64 4.8.4
I need to get a single DLL in a result of building that no need any other libraries to work. But can't link Boost libraries statically. I bootstrapped and built Boost with corresponding MinGW.
cmake_minimum_required(VERSION 3.3)
project(SampleProject)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(BOOST_ROOT "..\\lib\\boost_1_59_0")
set(Boost_USE_STATIC_LIBS ON)
set(BOOST_COMPONENTS_NEEDED filesystem )
find_package(Boost 1.59.0 REQUIRED COMPONENTS ${BOOST_COMPONENTS_NEEDED})
if(NOT Boost_FOUND)
message(FATAL_ERROR "Could not find boost!")
endif()
include_directories(${Boost_INCLUDE_DIR})
set(SOURCE_FILES main.cpp)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--kill-at -static-libgcc -static-libstdc++")
add_library(${CMAKE_PROJECT_NAME} SHARED ${SOURCE_FILES})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}\\..\\..\\output")
target_link_libraries(${CMAKE_PROJECT_NAME} ${Boost_LIBRARIES})
Build output:
O:/SampleProject/Cpp/lib/boost_1_59_0/stage/lib/libboost_filesystem-mgw48-mt-d-1_59.a(operations.o): In function error':
O:\SampleProject\Cpp\lib\boost_1_59_0/libs/filesystem/src/operations.cpp:286: undefined reference toboost::system::system_category()'
What else should I do to link with boost?
UPDATE: there is a list of built libraries
libboost_filesystem-mgw48-1_59.a
libboost_filesystem-mgw48-d-1_59.a
libboost_filesystem-mgw48-mt-1_59.a
libboost_filesystem-mgw48-mt-d-1_59.a
libboost_filesystem-mgw48-mt-s-1_59.a
libboost_filesystem-mgw48-mt-sd-1_59.a
libboost_filesystem-mgw48-s-1_59.a
libboost_filesystem-mgw48-sd-1_59.a
libboost_system-mgw48-1_59.a
libboost_system-mgw48-d-1_59.a
libboost_system-mgw48-mt-1_59.a
libboost_system-mgw48-mt-d-1_59.a
libboost_system-mgw48-mt-s-1_59.a
libboost_system-mgw48-mt-sd-1_59.a
libboost_system-mgw48-s-1_59.a
libboost_system-mgw48-sd-1_59.a
This looks like a linker error suggesting that you are not linking to Boost::system
You need to add system to BOOST_COMPONENTS_NEEDED. Change this line and see if it helps
set(BOOST_COMPONENTS_NEEDED system filesystem )

Static linking using cmake

I tried to static linking libstdc++-6 and libgcc_s_seh-1.I'm using Clion who use cmake. I'm using SFML but it's not necessary that it's dynamically linked.
Thanks
cmake_minimum_required(VERSION 2.8.4)
project(Game_Project)
set(EXECUTABLE_NAME "Game_Project")
# Enable debug symbols by default
if(CMAKE_BUILD_TYPE STREQUAL "")
set(CMAKE_BUILD_TYPE Debug)
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mwindows")
endif()
# (you can also set it on the command line: -D CMAKE_BUILD_TYPE=Release)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -s")
set(BUILD_SHARED_LIBS OFF)
set(CMAKE_EXE_LINKER_FLAGS "-static -static-libgcc -static-libstdc++")
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/SFML-2.2/cmake/Modules/" ${CMAKE_MODULE_PATH})
set(CMAKE_SOURCE_DIR src)
file(GLOB_RECURSE SRCS src/*.cpp)
#Find any version 2.X of SFML
#See the FindSFML.cmake file for additional details and instructions
set(SFML_ROOT "SFML-2.2")
find_package(SFML 2 REQUIRED system window graphics network audio)
include_directories(${CMAKE_SOURCE_DIR} include)
add_executable(${EXECUTABLE_NAME} ${SRCS})
if(SFML_FOUND)
include_directories(${SFML_INCLUDE_DIR})
target_link_libraries(${EXECUTABLE_NAME} ${SFML_LIBRARIES})
target_link_libraries(${EXECUTABLE_NAME} ${SFML_DEPENDENCIES})
endif()
install(TARGETS ${EXECUTABLE_NAME} DESTINATION bin)
If you link dynamically against SFML, which in turn links dynamically against libstdc++, your application will still require the so/dll files for libstdc++ because of SFML.
Think of the SFML.dll as a separate executable. That executable has a dynamic runtime dependency on libstdc++. You cannot get rid of that, because SFML has already been linked and there is no way to have it point to the part of libstdc++ that is statically linked to your executable instead.
The only way to get rid of the dependency is to make sure that all components link statically against the library in their linking phase.
The important thing to note here is that static libraries are of no concern for this. Static libraries never pass through the linker (think of them as a bunch of packed together object files), so it is the top-level executable or dynamic library pulling them in that determines how they link against the standard library.
So if you were to build SFML as a static library instead that is then pulled in by your executable, which is configured to statically link against libstdc++, the problem would disappear as well.

Link the static versions of the Boost libraries using CMake

I've got both the static and the dynamic versions of the boost libraries in /usr/lib. Now I'd like CMake to prefer the static versions during the linkage of my executable. What can I do?
In your CMakeLists.txt file:
set(Boost_USE_STATIC_LIBS ON)
find_package(Boost REQUIRED ...)
Where I have ..., you optionally put the names of the libraries you want to use, and then target_link_libraries(targetname ${Boost_LIBRARIES}) later below. If you have a fairly recent distribution of CMake, it should work exactly as advertised. I do it exactly this way in my own projects.
Here is a full example of CMAKEFILE,For example, include boost program options
cmake_minimum_required(VERSION 3.15)
project(your_project)
set(Boost_USE_STATIC_LIBS ON)
find_package(Boost 1.70 COMPONENTS program_options REQUIRED)
set(CMAKE_CXX_STANDARD 14)
add_executable(your_project main.cpp)
target_link_libraries(rconpp Boost::program_options)
references:
cmake documents about BOOST