Archiving static dependencies in modern CMake project - c++

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

Related

How to use headers from library but do not link to it with CMake?

I have an executable and a dynamic library that both depend on a static libary. If more context helps, the executable embeds a Python interperter and the dynamic library is a Python extension module. The dynamic library should not link to the static library because the later is already linked by the executable. However, the dynamic library requires headers from the static library to compile, which is only supplied implicitly via target_link_libraries. In effect, What I need is a target_link_libraries that adds include paths and does nothing else.
Here is what I've tried:
Retrieve include directories from target property of the static library, but indirectly included headers are still missing.
Have the executable and the dynamic library both link to the static library. That can't be expected to work, and it doesn't.
Turn the static library into a dynamic library. That would work, but I would like to avoid touching build scripts for the static library and the executable as much as possible.
Make the dynamic library static and links it with executable. This is impossible because Python extensions must be dynamically loaded.
Usually find_package(Foo) defines packages' include directories in a variable FOO_INCLUDE_DIR.
You can simply add that to your project's include path with
include_directories("${FOO_INCLUDE_DIR}")
You will need a separate target that provides only the headers.
Both the static and the dynamic library will then depend on that header-only target, but not on each other.
This is typically achieved by defining an interface library target for the headers:
target_include_directories(MyHeaders INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
add_library(MyStaticLib STATIC a.cpp)
target_link_libraries(MyStaticLib PUBLIC MyHeaders)
add_library(MyDynamicLib SHARED b.cpp)
target_link_libraries(MySDynamicLib PUBLIC MyHeaders)
Note how for this to work you will need full control over the definition of all the library targets. If the static library is provided by a third-party build that is not under your control this will not work. In that case you should get into contact with the maintainer of the third-party library to figure out how they can support your use case.
The $<TARGET_PROPERTY> generator expression does the trick.
target_include_directories(
MyDynamicLib
PRIVATE
$<TARGET_PROPERTY:MyStaticLib,INCLUDE_DIRECTORIES>)
get_target_property is evaluated in the configuration phase and return what is written in CMakeLists.txt. The $<TARGET_PROPERTY> generator expression is evaluated in the generation phase and returns the actual value.

IMPORTED library that sets linker search directories only

The project I am working on has numerous external dependencies that I am now converting to proper IMPORTED targets with their IMPORTED_LOCATION or IMPORTED_IMPLIB set, along with their INTERFACE_INCLUDE_DIRECTORIES. This is much cleaner than just adding all directories to each target globally as it was before.
I'm running into a problem that there is now a library that defines the library that needs to be linked in the source file using a MSVC-specific #pragma. I tried adding an MODULE IMPORTED library, but that results in NAME-NOTFOUND.obj being linked, which is not what I want and not what the documentation seems to imply.
I also tried CMake 3.x's INTERFACE IMPORTED library type, which results in the #pragma-defined library being linked, but not found, because I found no way to specify the linker search directory for that target. I would prefer a solution that works for CMake 2.8.12.1.
Is there a way to solve this without naming the exact library file in the CMake script?

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

Link poco static library to target in cmake

I read a lot of post but I don't yet well understood how to link my target to a static version of a library.
My project depends on poco libraries and, while in linux they are stored in /usr/local/lib (both the static and shared versions) in my windows machine are in d:\libs\poco\lib and d:\libs\poco\bin (where I have an enviroment variable called POCO_DIR = D:\libs\poco)
So, how can I have to write the find_library() directive in cmake file?
Thanks in advance.
You never link with a Poco DLL on windows, not even when you use shared Poco libraries. Linking is always with entries in the %POCO_BASE%/lib. For shared builds, .lib is just a stub ("import library") that takes care of loading the DLL at runtime. See Linking Implicitly for details on how this works.
Poco static libraries can be distinguished from the import libraries for DLLs by the file name - static libs have "mt" ("mtd" for debug binaries, "md" and "mdd" when runtime library DLLs are used) appended to the name. So the import library for PocoFoundation.dll will be named PocoFoundation.lib, while the static library using static runtimes is named PocoFoundationmt.lib. Static library using dynamic runtimes is PocoFoundationmd.lib. See Use Run-Time Library for details.
As for CMake, I am not an expert, but for e.g. static Foundation and Net libraries should be something like this:
FIND_LIBRARY(Poco_LIBRARIES NAMES PocoFoundationmt PocoNetmt PATH_SUFFIXES ${POCO_DIR}/lib)
EDIT: If you define POCO_STATIC in your project, static linking will be automatic through library headers, see e.g. Foundation.h.

Why doesn't Libtool want to link with a static library?

I want to build a shared library that uses ZipArchive using GNU Autotools but I'm having this problem:
Warning: linker path does not have real file for library -lziparch.
I have the capability to make that library automatically link in when
you link to this library. But I can only do this if you have a
shared version of the library, which you do not appear to have
because I did check the linker path looking for a file starting
with libziparch and none of the candidates passed a file format test
using a file magic. Last file checked: /usr/local/ZipArchive/ZipArchive/libziparch.a
The inter-library dependencies that have been dropped here will be
automatically added whenever a program is linked with this library
or is declared to -dlopen it.
If I build a static library or if I use a shared library of ZipArchive it works but the problem is that the makefile that comes with ZipArchive source code only builds a static library.
How can I force Libtool to link with a static library?
Generally, static archives are created with non-pic object files and they cannot be put into shared libraries.
What this message is telling you though, is that when a program links to YOUR library using Libtool, that -lziparch will get added to the link. So you don't need to change anything unless you're building a module for an interpreted language. In that case, you will have to build ZipArchive as a shared library. In addition, this wouldn't work on a platform like MS Windows where shared libraries (DLLs) have to resolve all their symbols at link time.
All that said, if your ziparch static lib is PIC code, you can use the -whole-archive flag when linking it to your library. This would be the least portable solution though.