CMake : 외부 프로젝트를 빌드하고 대상을 포함하는 방법


113

정적 라이브러리를 대상으로 내보내는 프로젝트 A가 있습니다.

install(TARGETS alib DESTINATION lib EXPORT project_a-targets)
install(EXPORT project_a-targets DESTINATION lib/alib)

이제 프로젝트 A를 프로젝트 B의 외부 프로젝트로 사용하고 빌드 된 대상을 포함하려고합니다.

ExternalProject_Add(project_a
  URL ...project_a.tar.gz
  PREFIX ${CMAKE_CURRENT_BINARY_DIR}/project_a
  CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
)

include(${CMAKE_CURRENT_BINARY_DIR}/lib/project_a/project_a-targets.cmake)

문제는 프로젝트 B의 CMakeLists가 실행될 때 포함 파일이 아직 존재하지 않는다는 것입니다.

빌드중인 외부 프로젝트에 따라 include를 만드는 방법이 있습니까?

업데이트 : 나는이것과 내가 만난 다른 일반적인 문제를 기반으로 예제 자습서 의 짧은 CMake를 작성했습니다.

답변:


67

여기서 두 가지 다른 패러다임을 혼합하고 있다고 생각합니다.

앞서 언급했듯이 매우 유연한 ExternalProject모듈은 빌드시 명령을 실행하므로 프로젝트 A가 설치된 후에 만 ​​생성되므로 프로젝트 A의 가져 오기 파일을 직접 사용할 수 없습니다.

당신이 원하는 경우 include프로젝트 A의 가져 오기 파일, 당신은 것 다른 타사의 의존성이 방법을 추가하거나 통해처럼 - 수동으로 프로젝트 B의 CMakeLists.txt를 호출하기 전에 프로젝트를 설치 find_file/ find_library/ find_package.

을 사용하려면 ExternalProject_AddCMakeLists.txt에 다음과 같은 내용을 추가해야합니다.

ExternalProject_Add(project_a
  URL ...project_a.tar.gz
  PREFIX ${CMAKE_CURRENT_BINARY_DIR}/project_a
  CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
)

include(${CMAKE_CURRENT_BINARY_DIR}/lib/project_a/project_a-targets.cmake)

ExternalProject_Get_Property(project_a install_dir)
include_directories(${install_dir}/include)

add_dependencies(project_b_exe project_a)
target_link_libraries(project_b_exe ${install_dir}/lib/alib.lib)

2
답변 해 주셔서 감사합니다. 당신이 제안하는 것은 내가 전에했던 것과 비슷합니다. 나는 ... 그것은 수동으로 lib 디렉토리 경로를 지정하는 것보다 더 좋은 인터페이스처럼 보였다으로 내 보낸 대상을 활용할 수있는 방법을 찾기 위해 희망
mirkokiefer

7
소스 트리에 외부 프로젝트의 소스를 포함 할 필요가 없었습니다. 모든 대상을 ExternalProject_Add좋아 add_subdirectory하고 노출 하면 좋을 것 입니다. 위에서 설명한 솔루션이 여전히 가장 깨끗합니다.
mirkokiefer 2013 년

2
둘 다 ExternalProject 빌드로 만든 다음 B가 A에 종속되도록 한 다음 프로젝트 B의 CMakeLists 파일에 프로젝트 A의 대상 파일이 포함되지만 "수퍼 빌드"CMakeLists는 A와 B를 모두 ExternalProjects로 빌드합니다. ...
DLRdave

3
@DLRdave-Super Build 솔루션이 몇 번 권장되는 것을 보았지만 .NET을 통한 일부 외부 프로젝트 만 포함하여 제공하는 이점이 무엇인지 잘 모르겠습니다 ExternalProject. 일관성입니까, 아니면 더 표준입니까, 아니면 다른 것입니까? 나는 여기서 근본적인 것을 놓치고 있다고 확신합니다.
Fraser

6
이 솔루션의 문제점 중 하나는 라이브러리 이름 (alib.lib)을 하드 코딩하여 빌드 시스템이 크로스 플랫폼이 아닙니다. 다른 OS가 공유 라이브러리에 대해 다른 이름 지정 체계를 사용하고 이러한 다른 이름 지정에 적응하기 때문입니다. 스키마는 CMake의 기능 중 하나입니다.
핵 공급국 그룹 (NSG)

22

이 게시물 에는 합리적인 답변이 있습니다.

CMakeLists.txt.in:

cmake_minimum_required(VERSION 2.8.2)

project(googletest-download NONE)

include(ExternalProject)
ExternalProject_Add(googletest
  GIT_REPOSITORY    https://github.com/google/googletest.git
  GIT_TAG           master
  SOURCE_DIR        "${CMAKE_BINARY_DIR}/googletest-src"
  BINARY_DIR        "${CMAKE_BINARY_DIR}/googletest-build"
  CONFIGURE_COMMAND ""
  BUILD_COMMAND     ""
  INSTALL_COMMAND   ""
  TEST_COMMAND      ""
)

CMakeLists.txt:

# Download and unpack googletest at configure time
configure_file(CMakeLists.txt.in
               googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
execute_process(COMMAND ${CMAKE_COMMAND} --build .
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )

# Prevent GoogleTest from overriding our compiler/linker options
# when building with Visual Studio
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)

# Add googletest directly to our build. This adds
# the following targets: gtest, gtest_main, gmock
# and gmock_main
add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
                 ${CMAKE_BINARY_DIR}/googletest-build)

# The gtest/gmock targets carry header search path
# dependencies automatically when using CMake 2.8.11 or
# later. Otherwise we have to add them here ourselves.
if (CMAKE_VERSION VERSION_LESS 2.8.11)
  include_directories("${gtest_SOURCE_DIR}/include"
                      "${gmock_SOURCE_DIR}/include")
endif()

# Now simply link your own targets against gtest, gmock,
# etc. as appropriate

그러나 그것은 꽤 엉망인 것 같습니다. 대체 솔루션을 제안하고 싶습니다-Git 하위 모듈을 사용하십시오.

cd MyProject/dependencies/gtest
git submodule add https://github.com/google/googletest.git
cd googletest
git checkout release-1.8.0
cd ../../..
git add *
git commit -m "Add googletest"

그런 다음 다음 MyProject/dependencies/gtest/CMakeList.txt과 같이 할 수 있습니다.

cmake_minimum_required(VERSION 3.3)

if(TARGET gtest) # To avoid diamond dependencies; may not be necessary depending on you project.
    return()
endif()

add_subdirectory("googletest")

나는 이것을 광범위하게 시도하지 않았지만 더 깨끗해 보인다.

편집 :이 방법에는 단점이 있습니다. 하위 디렉터리에서 install()원하지 않는 명령을 실행할 수 있습니다 . 이 게시물에는 비활성화하는 방법이 있지만 버그가 많고 저에게 효과적이지 않았습니다.

편집 2 : 사용 add_subdirectory("googletest" EXCLUDE_FROM_ALL)하는 경우 install()하위 디렉토리 의 명령이 기본적으로 사용되지 않는 것 같습니다 .


이것은 단지 예일 뿐이고 gtest는 아마도 꽤 안정적 일 수 있기 때문에 지나치게주의를 기울이는 것일 것입니다.하지만 GIT_TAG클론 중에 항상 특정을 사용하는 것이 좋습니다. 지금부터 2 년 후에 빌드 스크립트를 실행하는 누군가가 당신이 한 것과 다른 버전. CMake의 문서 도 이것을 권장합니다.
jrh

5

편집 : CMake는 이제이를 지원합니다. 새로운 답변을 참조하십시오 .

보조 make 프로세스에서 종속 대상을 강제로 빌드 할 수도 있습니다.

관련 주제에 대한 내 대답 을 참조하십시오 .


1

cmake는 ExternalProject_Add실제로 사용할 수 있지만 내가 좋아하지 않은 점은 빌드, 연속 폴링 등에서 무언가를 수행한다는 것입니다. 빌드 단계에서 프로젝트를 빌드하는 것을 선호합니다. ExternalProject_Add불행히도 성공하지 못한 채 여러 번의 시도로 재정의 를 시도했습니다.

그런 다음 git 하위 모듈도 추가하려고 시도했지만 전체 git 저장소를 드래그하지만 특정 경우에는 전체 git 저장소의 하위 집합 만 필요합니다. 내가 확인한 것-실제로 희소 git 체크 아웃을 수행하는 것이 가능하지만 아래에 작성한 별도의 기능이 필요합니다.

#-----------------------------------------------------------------------------
#
# Performs sparse (partial) git checkout
#
#   into ${checkoutDir} from ${url} of ${branch}
#
# List of folders and files to pull can be specified after that.
#-----------------------------------------------------------------------------
function (SparseGitCheckout checkoutDir url branch)
    if(EXISTS ${checkoutDir})
        return()
    endif()

    message("-------------------------------------------------------------------")
    message("sparse git checkout to ${checkoutDir}...")
    message("-------------------------------------------------------------------")

    file(MAKE_DIRECTORY ${checkoutDir})

    set(cmds "git init")
    set(cmds ${cmds} "git remote add -f origin --no-tags -t master ${url}")
    set(cmds ${cmds} "git config core.sparseCheckout true")

    # This command is executed via file WRITE
    # echo <file or folder> >> .git/info/sparse-checkout")

    set(cmds ${cmds} "git pull --depth=1 origin ${branch}")

    # message("In directory: ${checkoutDir}")

    foreach( cmd ${cmds})
        message("- ${cmd}")
        string(REPLACE " " ";" cmdList ${cmd})

        #message("Outfile: ${outFile}")
        #message("Final command: ${cmdList}")

        if(pull IN_LIST cmdList)
            string (REPLACE ";" "\n" FILES "${ARGN}")
            file(WRITE ${checkoutDir}/.git/info/sparse-checkout ${FILES} )
        endif()

        execute_process(
            COMMAND ${cmdList}
            WORKING_DIRECTORY ${checkoutDir}
            RESULT_VARIABLE ret
        )

        if(NOT ret EQUAL "0")
            message("error: previous command failed, see explanation above")
            file(REMOVE_RECURSE ${checkoutDir})
            break()
        endif()
    endforeach()

endfunction()


SparseGitCheckout(${CMAKE_BINARY_DIR}/catch_197 https://github.com/catchorg/Catch2.git v1.9.7 single_include)
SparseGitCheckout(${CMAKE_BINARY_DIR}/catch_master https://github.com/catchorg/Catch2.git master single_include)

함수 사용 방법을 설명하기 위해 아래에 두 개의 함수 호출을 추가했습니다.

누군가는 마스터 / 트렁크가 손상되었을 수 있으므로 체크 아웃하는 것을 좋아하지 않을 수 있습니다. 그러면 특정 태그를 항상 지정할 수 있습니다.

체크 아웃은 캐시 폴더를 지울 때까지 한 번만 수행됩니다.


1

비슷한 솔루션을 찾고있었습니다. 여기에 대한 답변과 상단의 튜토리얼은 유익합니다. 여기에 언급 된 게시물 / 블로그를 연구하여 성공적으로 구축했습니다. 나는 완전한 CMakeLists.txt를 게시하고 있습니다. 초보자를위한 기본 템플릿으로 도움이 될 것 같습니다.

"CMakeLists.txt"

cmake_minimum_required(VERSION 3.10.2)

# Target Project
project (ClientProgram)

# Begin: Including Sources and Headers
include_directories(include)
file (GLOB SOURCES "src/*.c")
# End: Including Sources and Headers


# Begin: Generate executables
add_executable (ClientProgram ${SOURCES})
# End: Generate executables


# This Project Depends on External Project(s) 
include (ExternalProject)

# Begin: External Third Party Library
set (libTLS ThirdPartyTlsLibrary)
ExternalProject_Add (${libTLS}
    PREFIX          ${CMAKE_CURRENT_BINARY_DIR}/${libTLS}
# Begin: Download Archive from Web Server
    URL             http://myproject.com/MyLibrary.tgz
    URL_HASH        SHA1=<expected_sha1sum_of_above_tgz_file>
    DOWNLOAD_NO_PROGRESS ON
# End: Download Archive from Web Server

# Begin: Download Source from GIT Repository
#    GIT_REPOSITORY  https://github.com/<project>.git
#    GIT_TAG         <Refer github.com releases -> Tags>
#    GIT_SHALLOW     ON
# End: Download Source from GIT Repository

# Begin: CMAKE Comamnd Argiments
    CMAKE_ARGS      -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/${libTLS}
    CMAKE_ARGS      -DUSE_SHARED_LIBRARY:BOOL=ON
# End: CMAKE Comamnd Argiments    
)

# The above ExternalProject_Add(...) construct wil take care of \
# 1. Downloading sources
# 2. Building Object files
# 3. Install under DCMAKE_INSTALL_PREFIX Directory

# Acquire Installation Directory of 
ExternalProject_Get_Property (${libTLS} install_dir)

# Begin: Importing Headers & Library of Third Party built using ExternalProject_Add(...)
# Include PATH that has headers required by Target Project
include_directories (${install_dir}/include)

# Import librarues from External Project required by Target Project
add_library (lmytls SHARED IMPORTED)
set_target_properties (lmytls PROPERTIES IMPORTED_LOCATION ${install_dir}/lib/libmytls.so)
add_library (lmyxdot509 SHARED IMPORTED)
set_target_properties(lmyxdot509 PROPERTIES IMPORTED_LOCATION ${install_dir}/lib/libmyxdot509.so)

# End: Importing Headers & Library of Third Party built using ExternalProject_Add(...)
# End: External Third Party Library

# Begin: Target Project depends on Third Party Component
add_dependencies(ClientProgram ${libTLS})
# End: Target Project depends on Third Party Component

# Refer libraries added above used by Target Project
target_link_libraries (ClientProgram lmytls lmyxdot509)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.