CMake: Cách xây dựng các dự án bên ngoài và bao gồm các mục tiêu của chúng


114

Tôi có một Dự án A xuất thư viện tĩnh dưới dạng đích:

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

Bây giờ tôi muốn sử dụng Dự án A như một dự án bên ngoài từ Dự án B và bao gồm các mục tiêu đã xây dựng của nó:

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)

Vấn đề là tệp bao gồm chưa tồn tại khi CMakeLists của Dự án B được chạy.

Có cách nào để làm cho bao gồm phụ thuộc vào dự án bên ngoài đang được xây dựng không?

Cập nhật : Tôi đã viết một hướng dẫn CMake by Ví dụ ngắndựa trên điều này và các vấn đề phổ biến khác mà tôi gặp phải.

Câu trả lời:


67

Tôi nghĩ rằng bạn đang trộn lẫn hai mô hình khác nhau ở đây.

Như bạn đã lưu ý, ExternalProjectmô-đun rất linh hoạt chạy các lệnh của nó tại thời điểm xây dựng, vì vậy bạn không thể sử dụng trực tiếp tệp nhập của Dự án A vì tệp này chỉ được tạo sau khi Dự án A đã được cài đặt.

Nếu bạn muốn includetập tin nhập khẩu dự án A, bạn sẽ để cài đặt Dự án Một thủ công trước khi gọi CMakeLists.txt Dự án B - giống như bất kỳ phụ thuộc bên thứ ba khác thêm vào bằng cách này hoặc thông qua find_file/ find_library/ find_package.

Nếu bạn muốn tận dụng ExternalProject_Add, bạn sẽ cần thêm những thứ như sau vào CMakeLists.txt của mình:

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
Cảm ơn câu trả lời của bạn. Những gì bạn đề xuất tương tự như những gì tôi đã có trước đây. Tôi hy vọng có thể tìm thấy một cách để làm cho sử dụng trong những mục tiêu xuất khẩu vì nó có vẻ giống như một giao diện đẹp hơn so với quy định cụ thể các đường dẫn lib bằng tay ...
mirkokiefer

7
Tôi muốn tránh phải đưa nguồn của các dự án bên ngoài vào cây nguồn của mình. Sẽ là tuyệt vời nếu ExternalProject_Addchỉ cần cư xử giống như add_subdirectoryvà tiếp xúc với tất cả các mục tiêu. Giải pháp bạn mô tả ở trên có lẽ vẫn là sạch nhất.
mirkokiefer ngày

2
Cân nhắc tạo chúng cả hai bản dựng ExternalProject và sau đó có B phụ thuộc vào A, sau đó tệp CMakeLists cho dự án B sẽ bao gồm tệp mục tiêu từ dự án A, nhưng CMakeLists "Siêu xây dựng" của bạn sẽ chỉ xây dựng A và sau đó là B, cả hai đều là ExternalProjects ...
DLRdave

3
@DLRdave - Tôi đã thấy giải pháp Super Build được đề xuất một vài lần, nhưng tôi đoán tôi không chắc nó mang lại những lợi ích gì khi chỉ bao gồm một số dự án bên ngoài ExternalProject. Đó có phải là tính nhất quán, hay chính tắc hơn, hay thứ gì khác? Tôi chắc chắn rằng tôi đang thiếu một cái gì đó cơ bản ở đây.
Fraser

6
Một trong những vấn đề với giải pháp này là chúng tôi vừa mã hóa tên thư viện (alib.lib), điều này làm cho hệ thống xây dựng không đa nền tảng, vì các hệ điều hành khác nhau sử dụng các sơ đồ đặt tên khác nhau cho các thư viện được chia sẻ và thích ứng với các cách đặt tên khác nhau này chương trình là một trong những tính năng của CMake.
nsg

22

Bài đăng này có một câu trả lời hợp lý:

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

Tuy nhiên nó có vẻ khá hacky. Tôi muốn đề xuất một giải pháp thay thế - sử dụng các mô-đun con 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"

Sau đó, MyProject/dependencies/gtest/CMakeList.txtbạn có thể làm điều gì đó như:

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

Tôi đã không thử điều này rộng rãi nhưng nó có vẻ sạch hơn.

Chỉnh sửa: Có một nhược điểm của cách tiếp cận này: Thư mục con có thể chạy install()các lệnh mà bạn không muốn. Bài đăng này có một cách tiếp cận để vô hiệu hóa chúng nhưng nó đã bị lỗi và không hiệu quả với tôi.

Chỉnh sửa 2: Nếu bạn sử dụng add_subdirectory("googletest" EXCLUDE_FROM_ALL), có vẻ như các install()lệnh trong thư mục con không được sử dụng theo mặc định.


Điều này có lẽ chỉ là tôi quá thận trọng vì đây chỉ là một ví dụ và gtest có lẽ khá ổn định, nhưng tôi thực sự khuyên bạn nên luôn sử dụng một cụ thể GIT_TAGtrong quá trình sao chép, bạn có thể mất khả năng lặp lại của bản dựng vì 2 năm kể từ bây giờ ai đó chạy tập lệnh xây dựng sẽ nhận được phiên bản khác với những gì bạn đã làm. Tài liệu của CMake cũng khuyến nghị điều này.
jrh


1

cmake's ExternalProject_Addthực sự có thể được sử dụng, nhưng điều tôi không thích ở nó - là nó thực hiện một cái gì đó trong quá trình xây dựng, thăm dò liên tục, v.v. Tôi muốn xây dựng dự án trong giai đoạn xây dựng, không có gì khác. Tôi đã cố gắng ghi đè ExternalProject_Addtrong nhiều lần, rất tiếc là không thành công.

Sau đó, tôi cũng đã thử thêm mô-đun con git, nhưng điều đó kéo toàn bộ kho lưu trữ git, trong khi trong một số trường hợp nhất định, tôi chỉ cần tập hợp con của toàn bộ kho lưu trữ git. Những gì tôi đã kiểm tra - thực sự có thể thực hiện kiểm tra git thưa thớt, nhưng yêu cầu chức năng riêng biệt, mà tôi đã viết bên dưới.

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

Tôi đã thêm hai lệnh gọi hàm bên dưới chỉ để minh họa cách sử dụng hàm.

Ai đó có thể không thích kiểm tra cái chính / thân cây, vì cái đó có thể bị hỏng - khi đó luôn có thể chỉ định thẻ cụ thể.

Việc kiểm tra sẽ chỉ được thực hiện một lần cho đến khi bạn xóa thư mục bộ nhớ cache.


1

Tôi đã tìm kiếm giải pháp tương tự. Các câu trả lời ở đây và Hướng dẫn ở trên cùng là thông tin. Tôi đã nghiên cứu các bài đăng / blog được giới thiệu ở đây để xây dựng thành công của tôi. Tôi đang đăng CMakeLists.txt hoàn chỉnh phù hợp với tôi. Tôi đoán, điều này sẽ hữu ích như một mẫu cơ bản cho người mới bắt đầu.

"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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.