programing

CMake를 사용하여 여러 정적 라이브러리를 하나로 결합

projobs 2022. 8. 10. 23:59
반응형

CMake를 사용하여 여러 정적 라이브러리를 하나로 결합

cmake 메일링 리스트에 기재되어 있는 과 같은 문제가 있습니다.프로젝트는 다수의 스태틱라이브러리에 의존합니다(모두 개별 서브모듈의 소스로부터 구축되어 각각 독자적인 CMake Lists).각 라이브러리의 빌드 프로세스를 설명하는 txt)를 사용하여 하나의 정적 라이브러리로 결합하여 소비자에게 공개하고 싶습니다.제 라이브러리의 의존성은 변경될 수 있으며, 이러한 변화로 인해 개발자들에게 부담을 주고 싶지 않습니다.가장 좋은 해결책은 모든 libs를 하나의 lib에 묶는 것입니다.

재미있게도target_link_libraries타깃 설정 시 명령어가 모든 통계 정보를 결합하지 않습니다.mylib이렇게 쓰니까...

target_link_libraries(mylib a b c d)

하지만, 이상하게도, 만약 내가 이 모든 것들을mylib실행 가능한 프로젝트의 하위 모듈을 투영하고 링크만 합니다.mylib최상위 실행 파일인 CMAkeLists에서 확인할 수 있습니다.txt, 라이브러리가 결합된 것 같습니다.즉, mylib는 27MB로 타깃을 빌드만 설정했을 때 3MB가 아닌mylib.

libs를 오브젝트 파일로 압축 해제하고 재결합(여기, 여기)하는 솔루션이 있습니다만, 위의 예에서 설명한 바와 같이 CMake가 libs를 자동으로 Marge할 수 있을 것 같으면 이는 매우 서투른 것으로 보입니다.내가 놓치고 있는 마법 명령어나 릴리스 라이브러리를 만들 때 추천할 만한 우아한 방법이 있나요?

내가 생각할 수 있는 가장 간단한 작업 예를 들어, 2개의 클래스,a그리고.b,어디에a에 의존하다b. .

a.h.

#ifndef A_H
#define A_H

class 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_H

class 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;
}

이들을 별도의 static lib로 컴파일한 후 커스텀타깃을 사용하여 static lib를 결합할 수 있습니다.

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)

또, 애플을 사용하면, 정상적으로 동작합니다.libtool커스텀 타겟의 버전.

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

더 깔끔한 방법이 있어야 할 것처럼 여전히 솔기가 있다.

이것이 질문에 직접 답하는 것은 아니지만, 도움이 된다는 것을 알았습니다.

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

기본적으로 타깃에 필요한 모든 스태틱lib를 수집하여 하나의 스태틱lib로 결합하는 CMake 함수를 정의합니다.

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

다음은 실제 기능의 복사 및 붙여넣기입니다.

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

이 기능을 사용하여 원하는 수의 라이브러리에 가입할 수 있습니다.

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)

add_custom_target이 아닌 add_custom_command를 사용할 수 있는 장점이 있습니다.이렇게 하면 라이브러리(및 의존관계)는 필요할 때만 구축되며 매번 구축되는 것은 아닙니다.단점은 더미 파일 생성 인쇄입니다.

https://cmake.org/pipermail/cmake/2018-September/068263.html

CMake는 그것을 지원하지 않는 것 같습니다.

Marge하려는 라이브러리가 서드파티제인 경우 (learnvst 예에 따라) 이 코드는 .o 파일의 치환을 처리합니다(예를 들어 liba와 libb 둘 다 파일명이 zz.o인 경우).

## Create static library (by joining the new objects and the dependencies)
ADD_LIBRARY("${PROJECT_NAME}-static" STATIC ${SOURCES})
add_custom_command(OUTPUT lib${PROJECT_NAME}.a
                   COMMAND rm ARGS -f *.o
                   COMMAND ar ARGS -x ${CMAKE_BINARY_DIR}/lib${PROJECT_NAME}-static.a
                   COMMAND rename ARGS 's/^/lib${PROJECT_NAME}-static./g' *.o
                   COMMAND rename ARGS 's/\\.o/.otmp/g' *.o
                   COMMAND ar ARGS -x ${CMAKE_SOURCE_DIR}/lib/a/liba.a
                   COMMAND rename ARGS 's/^/liba./g' *.o
                   COMMAND rename ARGS 's/\\.o/.otmp/g' *.o
                   COMMAND ar ARGS -x ${CMAKE_SOURCE_DIR}/lib/b/libb.a
                   COMMAND rename ARGS 's/^/libb./g' *.o
                   COMMAND rename ARGS 's/\\.o/.otmp/g' *.o
                   COMMAND rename ARGS 's/\\.otmp/.o/g' *.otmp
                   COMMAND ar ARGS -r lib${PROJECT_NAME}.a *.o
                   COMMAND rm ARGS -f *.o
                   DEPENDS "${PROJECT_NAME}-static")

add_custom_target(${PROJECT_NAME} ALL DEPENDS lib${PROJECT_NAME}.a)

그렇지 않으면 라이브러리가 사용자의 것이라면 CMake OBJECT 라이브러리를 사용해야 합니다.이 라이브러리는 이들 라이브러리의 Marge에 매우 적합한 메커니즘입니다.

이를 위해서는 스태틱라이브러리를 조합하는 조작이 아니라 모든 것을 링크하는 데 필요한 비트가 포함된 CMake Config 파일을 사용자에게 제공하는 것이 좋습니다.CMake를 사용하여 이러한 파일을 생성하거나 pkg-config 파일을 생성할 수 있습니다.또, 「이러한 라이브러리와 링크 하는 방법을 가르쳐 주세요」툴의 다른 형식도 사용할 수 있습니다.

링크처의 라이브러리에 관심이 있는 유저도 있을 가능성이 있습니다.또, 링크처에서는, 같은 라이브러리의 카피나 버전을 사용하고 있는 경우도 있습니다.바로 이 경우 솔루션이 엉망으로 되어 사용자가 여러 코드를 통합할 수 없게 됩니다.그 이유는 의존관계(정적 라이브러리의 의존관계를 하나의 정적 라이브러리에 결합할 때 수행하는 작업)의 복사본을 반드시 사용해야 한다고 판단했기 때문입니다.

언급URL : https://stackoverflow.com/questions/37924383/combining-several-static-libraries-into-one-using-cmake

반응형