Linking a static library to a shared library in cmake - c++

I have been working on a fun project (a game engine) for awhile now and figured i could make it more portable and easy to compile across platforms if I use cmake. Right now i have it set up like so, with a main executable and then a bunch of shared libraries that the executable is linked to. I've found all the material needed to produce the libraries and the executable, and linking those to the executable, but what of linking a dependency like a static library or another shared library to one of the libraries i produce? Here is a visual
Sentiment (name of project)
-Root (all the interfaces and components of the engine. main.cpp is here
-Sentiment_OGL4Renderer (the files for the Renderer library)
-Sentiment_SFMLGui (the files for the Gui library)
-Sentiment_TestGame (the code for a game)
now i want all of these, the executable and the shared libraries built and put into the bin folder in the top level directory. What i found suggested online for a setup like this was to make cmakelists.txt files in each folder, and then one in the root, for each project. What i have thus far is this.
#Sentiment
cmake_minimum_required(VERSION 2.6)
project(Sentiment)
set(RENDERER Sentiment_OGL4Renderer)
set(GUI Sentiment_SFMLGui)
set(GAME Test_Game)
add_definitions(-DBUILD_DLL)
list( APPEND CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS} -g -ftest-coverage -fprofile-arcs")
set(EXECUTABLE_OUTPUT_PATH "${Sentiment_SOURCE_DIR}/bin")
set(LIBRARY_OUTPUT_PATH "${EXECUTABLE_OUTPUT_PATH}")
link_directories("${LIBRARY_OUTPUT_PATH}")
add_subdirectory("${RENDERER}")
add_subdirectory("${GUI}")
add_subdirectory("${GAME}")
add_subdirectory(Root)
in root
project(Sentiment_exe)
link_directories("${Sentiment_SOURCE_DIR}/bin")
AUX_SOURCE_DIRECTORY(. new_source_list)
add_executable("${CMAKE_PROJECT_NAME}" ${new_source_list})
target_link_libraries("${CMAKE_PROJECT_NAME}" "${LIBRARY_OUTPUT_PATH}/${RENDERER}" "${LIBRARY_OUPUT_PATH}/${GUI}" "${LIBRARY_OUTPUT_PATH}/${GAME}" "${ADDITIONAL_DEPENDENCIES}")
in Sentiment_OGL4Renderer
project(Sentiment_OGL4-3Renderer)
include_directories(${Sentiment_SOURCE_DIR})
add_definitions(-DGLEW_STATIC)
add_library(Sentiment_OGL4-3Renderer SHARED Sentiment_OGL4Renderer.cpp GL/glew.cpp)
in Sentiment_SFMLGui
project(Sentiment_SFMLGui)
include_directories(${Sentiment_SOURCE_DIR})
add_library(Sentiment_SFMLGui SHARED Sentiment_SFMLGui.cpp)
in Sentiment_TestGame
project(Sentiment_Game)
include_directories(${Sentiment_SOURCE_DIR})
add_library(Sentiment_Game SHARED Game.cpp)
As you can tell there are a lot of third party libraries, and i tried various methods of linking, like with target_link_libraries, and i cannot for the life of me figure how to link an external library to the ones i've made. First off, the renderer uses GLEW but it needs no external dependency so ignore that. Then it needs OpenGL32.lib and Gdi32.lib from the windows sdk (only for windows). As for SFML, i've got the DLL's in the bin folder which need to be linked, (can easily get the .so's when working in linux and can distribute with the final product if I ever choose to do so). I need these all linked as dependencies to the libraries i create, but nothing seems to work. The project is all c++ and I am currently using mingw32 to compile it. I'm brand new to cmake so please be gentle if it is really simple.

To link external libraries, best practice is to use or create FindModule for given external library.
CMake comes with numerous modules that aid in finding various well-known libraries and packages.
The list of standard modules is in the official documentation
In case there is no standard module for your external library, you should write your own.
The OpenGL library has standard module FindOpenGL:
find_package (OpenGL)
if (OPENGL_FOUND)
include_directories(${OPENGL_INCLUDE_DIR})
target_link_libraries (Sentiment_OGL4-3Renderer ${OPENGL_gl_LIBRARY})
endif (OPENGL_FOUND)

Related

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

creating a static library and linking using Cmake

I have a application which need to use two libraries (pugixml and lua) and need to be an multi-platform build capable. I am trying to use Cmake for this. Since I have three different solution (two for creating static libraries and one solution which actually uses that libraries). So far what I do is run Cmake for those two libraries and copy those static libraries manually to the lib folder into application and run cmake again.
I need to reduce the three step and make it as a single step so that I can handle the multi-platform build in a single shot.
I need to know, should we need to create a dynamic link library or shared library or static library for these kind of operation?
And need help on how to do it. I tried creating the source folder of pugixml and copy the cpp/hpp/h file and wrote a cmakelists file like
set(HEADERS pugixml.hpp pugiconfig.hpp)
set(SOURCES ${HEADERS} pugixml.cpp)
add_library(pugixmlLib STATIC ${SOURCES})
target_link_libraries(pugixmlLib pugixml)
On the solution I could see my application project and pugixml project, but on my application project linker property I could find pugixml library.

C++ Shared library creation - linking against other shared libraries

I'm creating a C++ shared library which links against some Boost libraries (Boost version 1.55 on my local machine).
I can use my library on my machine, but I can't use it on another system with a different version of Boost (let's say 1.54) because of undefined references.
I am using CMake, here the CMakeLists.txt file:
cmake_minimum_required(VERSION 2.8)
project(my_library)
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
FILE(GLOB_RECURSE INCLUDE_FILES "include/*.hpp")
FILE(GLOB_RECURSE SOURCE_FILES "src/*.cpp")
add_library(${PROJECT_NAME} SHARED ${INCLUDE_FILES} ${SOURCE_FILES})
target_link_libraries(${PROJECT_NAME} -pthread -lboost_filesystem -lboost_regex -lboost_system)
I'm new to library creation and I'm struggling for days on that issue. I'm wondering if I must create a static library instead with Boost inside. But I would like my library to be the smallest as possible.
EDIT: When I check my lib dependencies I got this for Boost regex:
libboost_regex.so.1.55.0 => /usr/lib/x86_64-linux-gnu/libboost_regex.so.1.55.0 (0x00007fe228a27000)
Is it possible to update this to NOT link against a particular version of Boost?
When you link to a shared library, such as Boost, it is essential that the ABI of the library that is actually loaded in run time is compatible to the one that was used when compiling and linking. Boost does not maintain ABI compatibility between releases. This is why there is a strict dependency on a particular Boost version. There is no way to build an executable or library that will be version-agnostic to the Boost version it uses.
In Linux world it is normal to open source your code so that it can be compiled for each Linux distribution individually. This way, the Boost version used during compilation and run time will be the same - the one that the distro maintainers ship.
If open source model is not suitable for you, you can either do the package building yourself for different Linux distributions or attempt to hide the dependency somehow. One way to do the latter is to build your library as a shared object but link with static libraries of Boost. You must take a great care though to not expose Boost in any of your public interfaces and not export any Boost symbols from your library. This includes type info and consequently Boost exceptions cannot be thrown from your library. Basically, the fact that you're using Boost must be absolutely hidden from your library users. Otherwise there will be difficult to resolve conflicts between your Boost and the Boost that might be used by the user of your library.
Note that for some Boost libraries even static linking is not an option as there may be a requirement to link with shared libraries in some configurations. You should consult with the documentation of each Boost library you use to see if such constraints exist.
You should not create static library, as it just an archive of object files. Also static libraries are not created by compilers, it created using arching tools
ar cr libtemp.a obj/*.o
In contrary Shared libraries are supported by Compilers.
g++ -fPIC -shared *.o -o libtemp.so
And you can use tools like "nm", "ldd" and "objdump" to inspect into your library for symbols.
Read about linker and loaders, it will give a better idea about this topic.
Also standard practice for CMAKE is to use find_package instead of liking directlty.
find_package (Threads)
find_package(Boost,file_system,regex,system)
target_link_libraries (myapp ${CMAKE_THREAD_LIBS_INIT} ${BOOST_LIBS})

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)