R
R
redcircle2018-06-22 16:41:05
C++ / C#
redcircle, 2018-06-22 16:41:05

How to include all dependencies from other static libraries in a CMake project into a static library?

It is required to build a static library using CMake in a platform-independent way, in which all dependencies from other static libraries are already included.
A simplified example of what CMakeLists.txt looks like now:
cmake_minimum_required(VERSION 3.5)
find_package(Boost COMPONENTS system filesystem thread REQUIRED)
add_library (mylib mysource.cpp)
The project creates a static mylib library, but it does not include the required boost implementation, i.e. when linking applications with mylib need to be explicitly told to link boost.
I would like to avoid this, that is, it is necessary that all dependencies on boost be already included in mylib.
I would like to do this using CMake (without additional scripts, only expanding CMakeLists.txt) and cross-platform.

Answer the question

In order to leave comments, you need to log in

3 answer(s)
4
4rtzel, 2018-06-23
@4rtzel

Although I would certainly advise using find_package/add_subdirectory to include this lib in the program, but if you still need to get a static lib, then you can try something like this (based on this answer https://stackoverflow.com/a/37941544 /9953527):
CMakeLists.txt:

cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
project(mylib_combined VERSION 0.1 LANGUAGES CXX)

# Нам нужны статические либы
set(Boost_USE_STATIC_LIBS ON) 
find_package(Boost COMPONENTS system filesystem REQUIRED) 

# Наша собственная либа.
# К сожалению мы не может использовать add_library OBJECT либу, т.к.
# CMake не умеет работать с IMPORTED OBJECT либами, т.е. он не умеет
# экстрактить (ar -x) существующею static либу, чтобы собрать её потом 
# в одну большую (ar -qcs).
add_library(mylib mylib.cpp) 
add_custom_command(
    TARGET mylib
    POST_BUILD
    # Экстрактим нашу свежесобранную либу. Не самое красивое решение, но
    # что поделать ¯\_(ツ)_/¯
    COMMAND ar -x $<TARGET_FILE:mylib>
    # Теперь наши буст либы
    COMMAND ar -x ${Boost_SYSTEM_LIBRARY}
    COMMAND ar -x ${Boost_FILESYSTEM_LIBRARY}
    # А теперь собираем всё обратно
    COMMAND ar -qcs lib${PROJECT_NAME}.a *.o
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

To work with this library from another CMake project, you need the add_library IMPORTED target. But if you still plan to use it in other CMake projects, then it's better to do something like this:
CMakeLists.txt:
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
project(mylib_combined VERSION 0.1 LANGUAGES CXX)

set(Boost_USE_STATIC_LIBS ON) 
find_package(Boost COMPONENTS system filesystem REQUIRED) 

add_library(mylib mylib.cpp) 

add_custom_target(mylib_packer
    COMMAND ar -x $<TARGET_FILE:mylib>
    COMMAND ar -x ${Boost_SYSTEM_LIBRARY}
    COMMAND ar -x ${Boost_FILESYSTEM_LIBRARY} 
    COMMAND ar -qcs lib${PROJECT_NAME}.a *.o
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    DEPENDS mylib
)

add_library(mylib_combined STATIC IMPORTED GLOBAL)
add_dependencies(mylib_combined mylib_packer)
set_target_properties(mylib_combined
    PROPERTIES
        IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/lib${PROJECT_NAME}.a
)

And you can use mylib_combined target.
Obviously, the code above will only work on unix systems. There are tools for Windows/Mac OS, but I can't suggest anything here. I can only advise you to move all the work with ar/libtool/lib into a separate .cmake file for further reuse and include it using include .
Useful links:
https://gitlab.kitware.com/cmake/community/wikis/d...
https://pabloariasal.github.io/2018/02/19/its-time...
https://cmake .org/pipermail/cmake/2014-August/0582...
https://cmake.org/gitweb?p=cmake.git;a=blob;f=Test...

S
Stanislav Makarov, 2019-11-09
@Nipheris

Lord, what did they advise for crutches))
As jcmvbkbc rightly pointed out , usually one stat. the library does not drag in itself another. Moreover, such an opportunity is to enable one stat. library into another one - it exists in few places, I know about support for such a feature only in Visual Studio. Well, i.e. this is something out of the ordinary, and not the usual workflow. Normally, you should specify when linking stat. library, all its dependencies. Therefore, the question is how to transfer and use this information about the transitive dependencies of a static library when linking with it.
pkg-config is generally quite a normal solution, but it is no longer quite "within CMake".
Within CMake, everything is done using target_link_libraries and exporting the simak config paired with the simak targets file. For example, here is a simple script:

cmake_minimum_required(VERSION 3.5)
find_package(Boost COMPONENTS system filesystem thread REQUIRED)
add_library (mylib mysource.cpp)
target_link_libraries(mylib
  PRIVATE
    Boost::system
    Boost::filesystem
    Boost::thread
)

install(TARGETS mylib EXPORT mylib_export)
export(EXPORT mylib_export FILE cmake/mylib-targets.cmake)

will generate in the cmake subdirectory of the build directory a file containing mylib-targets.cmake, among other things, the following interesting information:
# Create imported target mylib
add_library(mylib STATIC IMPORTED)

set_target_properties(mylib PROPERTIES
  INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:Boost::system>;\$<LINK_ONLY:Boost::filesystem>;\$<LINK_ONLY:Boost::thread>"
)

Thus, Simak informs consumers of the mylib library that when linking with it, boost targets must also be added. Moreover, thanks to LINK_ONLY, this will only affect the linking process, in general, the dependence on the boost libraries will be private, as indicated in the script. If you change PRIVATE to PUBLIC and thus make the boost dependency public, then LINK_ONLY will go away and there will be a normal enumeration of the target's interface dependencies:
# Create imported target mylib
add_library(mylib STATIC IMPORTED)

set_target_properties(mylib PROPERTIES
  INTERFACE_LINK_LIBRARIES "Boost::system;Boost::filesystem;Boost::thread"
)

You just have to add the generation and installation of a trivial config (where there will only be find_package for boost and include the generated one mylib-targets.cmake) and do it in consumers find_package(mylib). All this is cross-platform and is standard practice, do not do unnecessary perversions with the merging of static libraries.

J
jcmvbkbc, 2018-06-23
@jcmvbkbc

The project creates a static mylib library, but it does not include the required boost implementation, that is, when linking an application with mylib, you must explicitly indicate that you need to link boost.

That's what libraries usually do - no normal library drags part of another with it.
But in order to indicate that the library has dependencies, use pkg-config . Such a library installs its archive (*.a), its header files (*.h) and the description for pkg-config (*.pc) in the system. The library user calls pkg-config --libs <имя библиотеки>and receives a list of keys to link.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question