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

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})

Related

Archiving static dependencies in modern CMake project

I need to package a C++ library that links to some other static libraries, and I'd like to be able to ship the compiled files alone without the need to ship the transitive dependencies as well. To this effect, I'm following this guide regarding modern CMake techniques, and I've specified all the needed dependencies as PRIVATE, as they are not used in my library's exposed API.
The issue is that it seems that, despite having specified the dependencies as PRIVATE, the linker still does not include them in the output library, so if I try to link my library to an executable, the linker will complain about missing symbols (at least using MSVC). Is there a way to solve this?
I've already taken a look at this but I'm not sure how to integrate it in the existing INSTALL targets
Assuming that you are creating a static library:
You receive unresolved symbols because dependencies in static libraries are not resolved during their creation. Only when you link the static lib to an executable or shared library, the linker actually tries to resolve the required symbols (and will fail in your case).
So you need to combine your static libraries to a single one (as you already found out).
You should follow the approach of combining add_custom_command and add_custom_target that is outlined in the answers you linked to (https://stackoverflow.com/a/32888999/1228449).
Then use INSTALL( FILES ....) to add the combined library to your install commands, e.g.:
include(GNUInstallDirs)
INSTALL( FILES ${LIBNAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} )
EDIT:
Alternatively, create a shared library.
ADD_LIBRARY( mylibrary SHARED ...)
Then the required symbols of the privately used static libraries are resolved at link time of your library. Furthermore, only the symbols that your library actually uses, are integrated into your library (whereas the static library will contain everything).

Shared lib with link to static libs internally

I have a shared library called MyLib. I have linked MyLib to a static version of Boost and three other libraries.
MyLib does not expose any feature of those linked libraries. In other words, the public interface headers of MyLib does not mention or include Boost or any other library.
I have built MyLib and got .lib(.a) and .dll(.so) files.
Now, I have MyEXE which is a program that is linked to MyLib. Everything is smooth till now.
If I need some feature of Boost or the other three libraries to be used in MyEXE:
Will it be safe to link MyEXE to Boost or the other libraries? Will I face any ODR problem?
If it is OK to link to them, should I link to the exact same version of Boost and other libraries that were used with MyLIB?
Does the previous scenario seems bad? Should I follow a better scenario? (Distributing the source and keeping the link process to the end developer is not an option for me)

CMake: Recommended way for a Config-File Package to depend on a Find-Module Package

I've just been reading about CMake's Config-File Package "concept" which sounds very promising. What I like very much about it is that if I create a Config-File Package myself I can specify other packages on which it depends. My Question is: How can I create a Config-File package that is "relocatable" and depends on a Find-Module Package (e.g. boost)?
In more detail: Suppose I want to create a package named HyDi. The cmake documentation explains then very nicely how I can create the corresponding HydiConfig.cmake and HydiTargets.cmake files automatically. A very simple version of the CMakeLists.txt that does this is:
project(HyDi)
find_package(Boost COMPONENTS program_options)
add_library(HyDi foo.cpp foo.hpp)
target_include_directories(HyDi PUBLIC INTERFACE ${Boost_INCLUDE_DIRS})
target_link_libraries(HyDi ${Boost_LIBRARIES})
target_compile_options(HyDi INTERFACE PUBLIC "-std=c++11")
install(TARGETS HyDi EXPORT HyDiTarget
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)
install(FILES foo.hpp DESTINATION include)
configure_file(cmake/HyDiConfig.cmake
"${CMAKE_CURRENT_BINARY_DIR}/HyDi/HyDiConfig.cmake"
COPYONLY
)
set(ConfigPackageLocation lib/cmake/HyDi)
install(EXPORT HyDiTarget FILE HyDiTargets.cmake
NAMESPACE Upstream:: DESTINATION ${ConfigPackageLocation} )
install(FILES cmake/HyDiConfig.cmake DESTINATION ${ConfigPackageLocation})
The corresponding HydiConfig.cmake is:
include(CMakeFindDependencyMacro)
find_dependency(Boost COMPONENTS program_options)
include("${CMAKE_CURRENT_LIST_DIR}/HyDiTargets.cmake")
However if I install this library, the HyDiTargets.cmake file will contain the include path to the Boost Libraries hardcoded and is thus not relocatable.
Note that the cmake documentation gives an example of how not to include the boost libraries that is essentially my version. But they unfortunately don't explain how to do it better.
I understand that I could build boost using cmake and could then import boost as a Config-file package so that my HydiTargets.cmake would relocatable. But this approach doesn't work with every other library that provides a Findxxx.cmake file.
Actually CMake doing this correct, when inject a "hardcoded" path to boost libraries (and you doing it wrong). Because after your library gets compiled and installed, it should "links" w/ very particular boost version (when it was at the moment of your library compilation) -- i.e. its header files and static/dynamic libraries.
Consider scenario: after successfull installation of your library, someone installs a new version of boost library (or any other third party library you depending on) in parallel (yeah, boost and some other libs could coexists in the same install prefix). To make things looks like a real world example, assume it is ABI incompatible w/ the previous version. Now if that "lucky" developer wants to use your (already compiled and installed) library (using exported targets and provided HyDiConfig.cmake) he'll get a trouble:
your library already linked to a "previous" boost version (remember ABI incompatible w/ a newer one);
so when you somehow replace that "hardcoded" paths and would find a newer version (as you trying to do in HyDiConfig.cmake) your "lucky" customer would angry on you for that mess!
That is not only about boost… same policy for all third party libraries: they should remains the same as it was at the moment of compilation (or at least ABI compatible if we are talking about dynamic linking at run time, but this is a separate story).
Moreover, your user probably even do not use boost in his app (but have more than one version installed) -- why you should find smth? You already know (thanks to CMake and hardcoded paths) what boost version "required" for your library! So finding smth new is completely wrong in this case! It is "too late… Your library already compiled, linked and installed!
Another case: he wants to use some other (newer) version of boost… Depending on order of find_package(boost) and find_package(yourLib) results may vary… but he'll be angry on you anyway!

Linking a static library to a shared library in cmake

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)

Creating CMakeLists file from existing Makefile

I want to use cmake to generate my build files for a C++ project. I have an existing Makefile.
I am having problems generating this Makefile using the standard cmake syntax.
How do I include standard C++ libraries like -lstdc++ -lpthread -lboost_thread-mt in the TARGET_LINK_LIBRARIES section of cmake? Or should these files be included in the ADD_DEPENDENCIES section.
(OR)
Is there a simple tool which generates a CMakeList.txt file from a Makefile
Unfortunately, there is no straightforward 1:1 conversion from Makefiles to CMakeLists. Since CMake is supposed to run on all platforms, it can not rely on platform specific assumptions like GNU make does, which complicates things in certain places.
In particular, CMake offers a very powerful and rather complex mechanism for using libraries: You call find_package with the name of your library, which will invoke a library search script from your cmake module path. This script (which is also written in CMake) will attempt to detect the location of the library's header and lib files and store them in a couple of CMake variables that can then be passed to the according CMake commands like include_directories and target_link_libraries.
There are two problems with this approach: First, you need a search script. Fortunately, CMake ships with search scripts for Pthreads, Boost and a couple of others, but if you are using a more exotic library, you might have to write the search script yourself, which is kind of an arcane experience at first...
The second major problem is that there is no standard way for a search script to return its results. While there are naming conventions for the used variables, those often don't apply. In practice that means you will have to check out a search script's source to know how to use it. Fortunately, the scripts that come with CMake are mostly very well documented.
The builtin scripts are located somewhere like <cmake-install-prefix>/share/cmake-2.8/Modules. For your question, look at the FindBoost.cmake and FindThreads.cmake files (CMake should automatically link with the standard library). Anycorn already gave some sample code for using the Boost script, everything else you need to know is in the CMake documentation or directly in the search script files.
Like this:
target_link_libraries(your-target-name pthread boost_thread-mt etc)
You should not use add_dependencies when you want to link libraries. Linking implies a dependency, but the dependency alone will not be sufficient when you need to link.
With Boost you really need to use package finder
set(Boost_ADDITIONAL_VERSIONS "1.46" "1.46.0" "1.46.1")
set(Boost_USE_MULTITHREADED ON) # for -mt
find_package(Boost COMPONENTS thread)
if(Boost_FOUND)
MESSAGE(STATUS "Found Boost: ${Boost_LIBRARY_DIRS}")
MESSAGE(STATUS "Found Boost libraries: ${Boost_LIBRARIES}")
set(LIBRARIES "${LIBRARIES};${Boost_LIBRARIES}")
else()
MESSAGE(FATAL_ERROR "Boost Thread NOT FOUND")
endif()
target_link_libraries(executable ${LIBRARIES})