Combining several static libraries into one using CMake Combining several static libraries into one using CMake c c

Combining several static libraries into one using CMake


Given the most simple working example I can think of: 2 classes, a and b, where a depends on b . .

a.h

#ifndef A_H#define A_Hclass aclass{public:    int method(int x, int y);};#endif

a.cpp

#include "a.h"#include "b.h"int aclass::method(int x, int y) {    bclass b;    return x * b.method(x,y);}

b.h

#ifndef B_H#define B_Hclass bclass{public:    int method(int x, int y);};#endif

b.cpp

#include "b.h"int bclass::method(int x, int y) {    return x+y;}

main.cpp

#include "a.h"#include <iostream>int main(){    aclass a;    std::cout << a.method(3,4) << std::endl;    return 0;}

It is possible to compile these into separate static libs, and then combine the static libs using a custom target.

cmake_minimum_required(VERSION 2.8.7)add_library(b b.cpp b.h)add_library(a a.cpp a.h)add_executable(main main.cpp)set(C_LIB ${CMAKE_BINARY_DIR}/libcombi.a)add_custom_target(combined        COMMAND ar -x $<TARGET_FILE:a>        COMMAND ar -x $<TARGET_FILE:b>        COMMAND ar -qcs ${C_LIB} *.o        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}        DEPENDS a b        )add_library(c STATIC IMPORTED GLOBAL)add_dependencies(c combined)set_target_properties(c        PROPERTIES        IMPORTED_LOCATION ${C_LIB}        )target_link_libraries(main c)

It also works just fine using Apple's libtool version of the custom target . . .

add_custom_target(combined        COMMAND libtool -static -o ${C_LIB} $<TARGET_FILE:a> $<TARGET_FILE:b>        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}        DEPENDS a b        )

Still seams as though there should be a neater way . .


You can use this function to join any number of libraries.

function(combine_archives output_archive list_of_input_archives)    set(mri_file ${TEMP_DIR}/${output_archive}.mri)    set(FULL_OUTPUT_PATH ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/lib${output_archive}.a)    file(WRITE ${mri_file} "create ${FULL_OUTPUT_PATH}\n")    FOREACH(in_archive ${list_of_input_archives})        file(APPEND ${mri_file} "addlib ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/lib${in_archive}.a\n")    ENDFOREACH()    file(APPEND ${mri_file} "save\n")    file(APPEND ${mri_file} "end\n")    set(output_archive_dummy_file ${TEMP_DIR}/${output_archive}.dummy.cpp)    add_custom_command(OUTPUT ${output_archive_dummy_file}                       COMMAND touch ${output_archive_dummy_file}                       DEPENDS ${list_of_input_archives})    add_library(${output_archive} STATIC ${output_archive_dummy_file})    add_custom_command(TARGET ${output_archive}                       POST_BUILD                       COMMAND ar -M < ${mri_file})endfunction(combine_archives)

It has the benefits of using add_custom_command and not add_custom_target. This way, the library (and it's dependencies) are only built when needed and not every time.The drawback is the print of the generation of the dummy file.


This doesn't directly answer the question, but I found it useful:

https://cristianadam.eu/20190501/bundling-together-static-libraries-with-cmake/

Basic, define a CMake function that will collect all the static libs required by a target and combine them into a single static lib:

add_library(awesome_lib STATIC ...);bundle_static_library(awesome_lib awesome_lib_bundled)

Here's a copy & paste of the actual function:

function(bundle_static_library tgt_name bundled_tgt_name)  list(APPEND static_libs ${tgt_name})  function(_recursively_collect_dependencies input_target)    set(_input_link_libraries LINK_LIBRARIES)    get_target_property(_input_type ${input_target} TYPE)    if (${_input_type} STREQUAL "INTERFACE_LIBRARY")      set(_input_link_libraries INTERFACE_LINK_LIBRARIES)    endif()    get_target_property(public_dependencies ${input_target} ${_input_link_libraries})    foreach(dependency IN LISTS public_dependencies)      if(TARGET ${dependency})        get_target_property(alias ${dependency} ALIASED_TARGET)        if (TARGET ${alias})          set(dependency ${alias})        endif()        get_target_property(_type ${dependency} TYPE)        if (${_type} STREQUAL "STATIC_LIBRARY")          list(APPEND static_libs ${dependency})        endif()        get_property(library_already_added          GLOBAL PROPERTY _${tgt_name}_static_bundle_${dependency})        if (NOT library_already_added)          set_property(GLOBAL PROPERTY _${tgt_name}_static_bundle_${dependency} ON)          _recursively_collect_dependencies(${dependency})        endif()      endif()    endforeach()    set(static_libs ${static_libs} PARENT_SCOPE)  endfunction()  _recursively_collect_dependencies(${tgt_name})  list(REMOVE_DUPLICATES static_libs)  set(bundled_tgt_full_name     ${CMAKE_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${bundled_tgt_name}${CMAKE_STATIC_LIBRARY_SUFFIX})  if (CMAKE_CXX_COMPILER_ID MATCHES "^(Clang|GNU)$")    file(WRITE ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar.in      "CREATE ${bundled_tgt_full_name}\n" )            foreach(tgt IN LISTS static_libs)      file(APPEND ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar.in        "ADDLIB $<TARGET_FILE:${tgt}>\n")    endforeach()        file(APPEND ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar.in "SAVE\n")    file(APPEND ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar.in "END\n")    file(GENERATE      OUTPUT ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar      INPUT ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar.in)    set(ar_tool ${CMAKE_AR})    if (CMAKE_INTERPROCEDURAL_OPTIMIZATION)      set(ar_tool ${CMAKE_CXX_COMPILER_AR})    endif()    add_custom_command(      COMMAND ${ar_tool} -M < ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar      OUTPUT ${bundled_tgt_full_name}      COMMENT "Bundling ${bundled_tgt_name}"      VERBATIM)  elseif(MSVC)    find_program(lib_tool lib)    foreach(tgt IN LISTS static_libs)      list(APPEND static_libs_full_names $<TARGET_FILE:${tgt}>)    endforeach()    add_custom_command(      COMMAND ${lib_tool} /NOLOGO /OUT:${bundled_tgt_full_name} ${static_libs_full_names}      OUTPUT ${bundled_tgt_full_name}      COMMENT "Bundling ${bundled_tgt_name}"      VERBATIM)  else()    message(FATAL_ERROR "Unknown bundle scenario!")  endif()  add_custom_target(bundling_target ALL DEPENDS ${bundled_tgt_full_name})  add_dependencies(bundling_target ${tgt_name})  add_library(${bundled_tgt_name} STATIC IMPORTED)  set_target_properties(${bundled_tgt_name}     PROPERTIES       IMPORTED_LOCATION ${bundled_tgt_full_name}      INTERFACE_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:${tgt_name},INTERFACE_INCLUDE_DIRECTORIES>)  add_dependencies(${bundled_tgt_name} bundling_target)endfunction()