CMake include 3rd party project - c++

My background is in regular makefiles, I'm trying to convert our project to a CMake setup.
I get stuck when I try to include g3log as a subproject.
I added the following lines in my CMakeLists.txt.
Note, that ../../external/g3log_src is the top level directory of the third party product.
target_link_libraries( myproject LINK_PUBLIC ${Boost_LIBRARIES} g3log)
set(DG3_SHARED_LIB OFF)
include_directories("../../external/g3log_src/include")
add_subdirectory (../../external/g3log_src LIBRARY_OUTPUT_PATH/g3log)
However, it would not actually build g3log and so I cannot link it:
ld: library not found for -lg3log
There is a line
project (g3log)
included in the g3log CMakeLists.txt
I guess I'm missing something fundamental and easy here, but I'm lacking pointers on how to tackle the problem. Any help is appreciated.

When link with the 3d-party library, you should use library name, not a project name. In case of g3log, the library name is g3logger.
Also, building 3d-party project should prepend using it:
add_subdirectory (../../external/g3log_src g3log)
...
target_link_libraries( myproject LINK_PUBLIC ${Boost_LIBRARIES} g3logger)
This is because linking should be performed with target name, which is known to CMake only after processing 3d-party project.
Otherwise, you just pass -lg3logger option to the linker, but the linker cannot find this library because it hasn't built at the moment.

Related

How do I properly link my libraries in my project using CMake?

I'm currently learning CMake and I'm trying to create my first test project. I'm able to get a simple project up and running in visual studio via CMake. However, I'm having trouble trying to add a library. I've read some guides and things but I keep getting errors. Basically, I'm trying to link SDL libraries (a game programming library) in my sample project. I've placed these libraries in a top level, 'ThirdParty' folder. Here is what my CmakeLists.txt file looks like in my top level directory:
cmake_minimum_required(VERSION 2.8.11)
project(Hello)
#Find necessary header files
find_path(SDL_INCLUDE_DIR SDL.h HINTS ${CMAKE_SOURCE_DIR}/ThirdParty/SDL2/include/)
#Find necessary library files
find_library(SDL_LIB_DIR SDL2 HINTS ${CMAKE_SOURCE_DIR}/ThirdParty/SDL2/lib/x86)
find_library(SDLMAIN_LIB_DIR SDLmain HINTS ${CMAKE_SOURCE_DIR}/ThirdParty/SDL2/lib/x86)
#Add/Link files to project
include_directories(${SDL_INCLUDE_DIR})
target_link_libraries(Test PUBLIC ${SDL_LIB_DIR})
target_link_libraries(Test PUBLIC ${SDLMAIN_LIB_DIR})
add_executable(Test "${CMAKE_SOURCE_DIR}/Source/Main.cpp")
I'm not 100 percent sure of the HINTS parameter, but I saw it used on another thread. Anyway, here's the error I keep getting:
CMake Error: The following variables are used in this project, but they are set to NOTFOUND.
Please set them or make sure they are set and tested correctly in the CMake files:
SDLMAIN_LIB_DIR
linked by target "Test" in directory C:/Users/Jason/Desktop/Test
What am I doing wrong and how do I properly link libraries in CMake?
In cmake, you first create the executable, and then you link it to a library
You have to understand how finding libraries and packages works in CMake. Typically the way it works is that you use find_library or find_package, and then cmake will set some variables that you can use to link to/use the library.
I'm not familiar with SDL, but by googling a little bit about it, I would say this is how it should look like:
find_file(SDL2_INCLUDE_DIR NAME SDL.h HINTS SDL2)
find_library(SDL2_LIBRARY NAME SDL2)
add_executable(MyExec main.cpp)
target_include_directories(MyExec ${SDL2_INCLUDE_DIR})
target_link_libraries(MyExec ${SDL2_LIBRARY})
That find_library will set the variables SDL2_INCLUDE_DIR and SDL2_LIBRARY, which you can use to link to SDL and add its includes to your project.

Convert from Xcode to CMake

I have a C++ project that I initially developed using Xcode on my Mac and I would like to set up the very same project using CMake. The project makes use of some external libraries, including eigen and boost. Xcode has a rather long list of Build Settings, including paths to external libraries, specification of the compiler version, additional linker flags, compiler optimization level, etc... and I am wondering where all of this information goes in the CMakeLists.txt file. I've searched extensively for help on this but have found precious little. I am new to CMake and have never written make files before. If there were a utility that could convert my Xcode project into a CMake project, that would be ideal. But I would be very glad to know of a tutorial on this, or to have some specific guidance. Any help on this would be greatly appreciated.
Conversion Strategy
I am pretty sure that the easiest and fastest way to move to CMake is a mixture of looking at comparable projects that link the same dependencies, and maybe copying a few relative file paths to your source files from XCode.
A well-written CMakeLists.txt that uses Eigen and Boost can be very small (and platform-independent).
One reason for this is CMake has a very different way to use dependencies.
For well-known libraries such as Boost, CMake includes scripts to include headers and link libraries.
Minimal CMakeLists.txt
A minimal example may look like this:
find_package(Eigen3 REQUIRED)
find_package(Boost REQUIRED COMPONENTS system)
add_executable(
app
src/main.cpp
)
target_include_directories(
app
${Eigen3_INCLUDE_DIRS}
${Boost_INCLUDE_DIR}
)
target_link_libraries(
app
${Boost_LIBRARIES}
)
Example Projects
For Eigen, there is no such pre-installed script that can be called by find_package.
Here are two real-world example projects that link Eigen and Boost: https://github.com/ompl/ompl/blob/master/CMakeLists.txt
https://github.com/roboticslibrary/rl
Here is an example for a find script for Eigen.
As a piece of advice, some CMake projects are out there are convoluted and use old syntax, newer project files tend to use small letters in commands.
If it happens that directly target linking libraries doesn't work, you'll have to write your own Find*.cmake files by yourself.
Here's a spec of what you should cover to make it (this is just one approach):
Imagine you have a library XYZ:
Your file
FindXYZ.cmake should have as a result a set of variables to use:
XYZ_LIBRARIES
XYZ_INCLUDE_DIR
For example:
set(XYZ_LIBRARIES ${SOME_PATH_TO_LIBRARY1} ${SOME_PATH_TO_LIBRARY2})
find_path(XYZ_INCLUDE_DIR NAMES xyz/xyz.h HINTS ${PLACE_WHERE_INCLUDE_SHOULD_BE}
PATH_SUFFIXES include
)
From your main cmake maybe you 'use' it:
find_package(XYZ REQUIRED)
# Now we use both _INCLUDE_DIR and _LIBRARIES
include_directories(SYSTEM "${XYZ_INCLUDE_DIR}")
# TARGET_NAME is the current target, the 'client' of XYZ
# append XYZ to our list of libraries to link
target_link_libraries(${TARGET_NAME} ${XYZ_LIBRARIES})
I'm surely missing details but that's the general idea. Here are some cmake scripts that I know work in a big project:
Example with OpenSSL
Find:
https://github.com/highfidelity/hifi/blob/Android/cmake/modules/FindOpenSSL.cmake
Client (in this case is a library that indeed needs OpenSSL):
https://github.com/highfidelity/hifi/blob/Android/domain-server/CMakeLists.txt

Linking both third party precompiled dynamic and static libaries in Windows

Lately, I have been using cmake as a generator for my projects. I have successfully generated many vtk and other application projects. However, I now face a problem when trying to link both dynamic and static precompiled libraries. In particular, I have been given some dynamic precompiled third party dlls along with their respective .lib files. Furthermore, I am trying to link some static precompiled libraries (only .lib files) to my project so as to check the software licences.
Let say that my project is called test_example and I have some precompiled dynamic libraries in libs directory. The structure of my project directory is:
Test_example
-/include
-/libs
-/build
-CMakeLists.txt
The CMakeLists.txt for linking the dynamic libaries has the following content:
cmake_minimum_required(VERSION 2.8.9)
project (test_example)
set(CMAKE_BUILD_TYPE Release)
#For the shared libraries:
set (PROJECT_LINK_LIBS dynamic_1.dll dynamic_2.dll )
set (PROJECT_LINK_DIR ${test_example_SOURCE_DIR}/libs/)
set (PROJECT_INCLUDE_DIR ${test_example_SOURCE_DIR}/include/)
link_directories(${PROJECT_LINK_DIR})
include_directories(${PROJECT_INCLUDE_DIR})
add_executable(test_example test_example.cpp)
target_link_libraries(test_example ${PROJECT_LINK_LIBS})
When I generate the project with this cmake lists, I can successfully use methods from the precompiled dlls. However, I have not found a way to link against my static libraries, as well. Let say I have one static library which is called test_licence.lib. Should I drop it in the libs folder as well and simply refer to it like I do with the dynamic? When I do so and when opening my project solution in Visual Studio, I can see that both dynamic and static libraries have been added to Linker-->Input-->Additional DEpendencies. However, when I am trying to build the project, I have unresolved external dependencies which are methods from the static lib.
Does any of you have any idea what would be the most efficient way to accomplish that? Many thanks in advance!
There is a couple of issues here.
Unlike to what you may be used to from VS, CMake prefers absolute paths for linking, instead of setting a link directory and giving the relative path.
Also, you do not link against .dll files. Dlls are loaded at runtime, not at link time. Many dlls are shipped with import libraries (with a .lib ending), that handle the runtime loading automatically for you. These are the ones you should link against.
Also try not to hardcode libraries in CMake code. The problem here is that if something goes wrong, you end up with a cryptic linker error. You should use find_library instead, which will usually make CMake complain early if something is off.
A cleaner version of your CMake script would be something like
cmake_minimum_required(VERSION 2.8.9)
project (test_example)
# note setting the build type does nothing on a visual studio build
# and should probably be left to the user for other generators
set(CMAKE_BUILD_TYPE Release)
#For the shared libraries:
# this call will succeed if it finds a dynamic_1.lib file
find_library(DYNAMIC_LIB1 dynamic_1 HINTS ${test_example_SOURCE_DIR}/libs)
if(NOT DYNAMIC_LIB1)
message(FATAL_ERROR "Library dynamic_1 was not found!")
endif()
find_library(DYNAMIC_LIB2 dynamic_2 HINTS ${test_example_SOURCE_DIR}/libs)
if(NOT DYNAMIC_LIB2)
message(FATAL_ERROR "Library dynamic_2 was not found!")
endif()
# for the static libraries:
# basically the same; again this looks for a static_1.lib file
find_library(STATIC_LIB1 static1 HINTS ${test_example_SOURCE_DIR}/libs)
if(NOT STATIC_LIB1)
message(FATAL_ERROR "Library static_1 was not found!")
endif()
set (PROJECT_INCLUDE_DIR ${test_example_SOURCE_DIR}/include/)
include_directories(${PROJECT_INCLUDE_DIR})
add_executable(test_example test_example.cpp)
target_link_libraries(test_example ${DYNAMIC_LIB1} ${DYNAMIC_LIB2} ${STATIC_LIB1})
Double check in Visual Studio that all libraries were added as linker inputs, as expected. If you still get linker errors, it means that something is wrong with your third-party .lib file. Open a new question with the exact linker error that you get.
Here is how we are doing it:
Firstly, use add_library with final arguments STATIC IMPORTED. Then subsequently use set_property to set the IMPORTED_LOCATION property, which is a path to the built library. For example, we pull in gtest like so:
add_library(gtest UNKNOWN IMPORTED)
set_property(TARGET gtest PROPERTY IMPORTED_LOCATION ${binary_dir}/googlemock/gtest/${CMAKE_FIND_LIBRARY_PREFIXES}gtest.a)
Then, gtest is a known library in your build system and you can link with it normally later on by just doing
target_link_libraries(target-name gtest)
See also: Cmake imported libraries documenation

"No rule to make target" error in cmake when linking to shared library

In Ubuntu, I have downloaded a third-party shared library, mylibrary.so, which I have placed in the directory /home/karnivaurus/Libraries. I have also placed the associated header file, myheader.h, in the directory /home/karnivaurus/Headers. I now want to link to this library in my C++ code, using CMake. Here is my CMakeLists.txt file:
cmake_minimum_required(VERSION 2.0.0)
project(DemoProject)
include_directories(/home/karnivaurus/Headers)
add_executable(demo demo.cpp)
target_link_libraries(demo /home/karnivaurus/Libraries/mylibrary)
However, this gives me the error message:
:-1: error: No rule to make target `/home/karnivaurus/Libraries/mylibrary', needed by `demo'. Stop.
What's going on?
While the other answer posted here is valid, it is out-dated. CMake now provides better solutions for using a pre-built external library in your code. In fact, CMake itself even discourages the use of link_directories() in its documentation.
The target_link_libraries() command takes very specific syntax for linking to an external library. A more modern solution is to create an IMPORTED CMake target for your external library:
add_library(MyExternalLib SHARED IMPORTED)
# Provide the full path to the library, so CMake knows where to find it.
set_target_properties(MyExternalLib PROPERTIES IMPORTED_LOCATION /home/karnivaurus/Libraries/mylibrary.so)
You can then use this imported CMake target later on in your code, and link it to your other targets:
target_link_libraries(demo PRIVATE MyExternalLib)
For other ways to use an external third-party library in your CMake code, see the responses here.
You may use a full path to the static library. To link w/ dynamic one, better to use link_directories() like this:
cmake_minimum_required(VERSION 2.0.0)
project(DemoProject)
include_directories(/home/karnivaurus/Headers)
link_directories(/home/karnivaurus/Libraries)
add_executable(demo demo.cpp)
target_link_libraries(demo mylibrary)
and make sure mylibrary has prefix lib and suffix .so in file name (i.e. full name is /home/karnivaurus/Libraries/libmylibrary.so).
To make you project more flexible, you'd better to write a finder module and avoid hardcode paths like /home/karnivaurus/*

How to set up a CMake project correctly?

While it's easy to find surface level information on how to use CMake, information how to actually use CMake properly seems to be extremely hard to find. How should in a decently sized CMake project (one executable, one or more static libraries used by that executable, one or more external dependencies used by the static libraries) the folders and CMakeList.txt files be ordered? What CMakeList.txt files should have what commands?
A good way to learn how to use CMake effectively is by looking at other projects. LLVM and its subprojects are a good example.
Generally, good coding practices convert to good CMake practices; you want modularity, a clear style and flexibility.
An example of this might be to have rules for building your executable inside the src directory, then use that target in the root project folder. Something like this:
-my_proj
|
----CMakeLists.txt //contains directives for top-level dependencies and linking, includes subfolders
----src
|
----CMakeLists.txt //contains definition of your main executable target
----internal_lib
|
----CMakeLists.txt //contains definition of your internal static libraries
my_proj/CMakeLists.txt
add_subdirectory(src)
find_package (Threads REQUIRED) #find pthreads package
target_link_libraries (my_exe my_lib ${CMAKE_THREAD_LIBS_INIT}) #link against pthreads and my_lib
my_proj/src/CMakeLists.txt
add_subdirectory(internal_lib)
add_executable(my_exe my_source1.cpp mysource2.cpp)
my_proj/src/internal_lib/CMakeLists.txt
add_library(my_lib my_lib_source1.cpp my_lib_source2.cpp)
I hope this tutorial is exactly what you need to starting with a CMake configuration for a simple project including one executable and several libraries - take a look! I find the CMake by Example anyway the best possibility to learn CMake the easy way:
Using CMake with executables
add_executable(myapp main.c)
Using CMake with static libraries
add_library(test STATIC test.c)
Using CMake with dynamic libraries
add_library(test SHARED test.c)
Linking libraries to executables with CMake
add_subdirectory(libtest_project)
add_executable(myapp main.c)
target_link_libraries(myapp test)