CMake:如何构建外部项目并包含其目标

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CMake:如何构建外部项目并包含其目标相关的知识,希望对你有一定的参考价值。

我有一个项目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 by Example tutorial

答案

我想你在这里混淆了两种不同的范例。

如您所述,高度灵活的ExternalProject模块在构建时运行其命令,因此您无法直接使用Project A的导入文件,因为它仅在安装了项目A后才创建。

如果你想要include项目A的导入文件,你必须在调用Project B的CMakeLists.txt之前手动安装Project A - 就像通过这种方式或通过find_file / find_library / find_package添加的任何其他第三方依赖项一样。

如果你想使用ExternalProject_Add,你需要在你的CMakeLists.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)
另一答案

This post有一个合理的答案:

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

然而,它似乎相当hacky。我想提出一个替代解决方案 - 使用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()命令。 This post has an approach to disable them,但它是马车,并没有为我工作。

编辑2:如果使用add_subdirectory("googletest" EXCLUDE_FROM_ALL),似乎意味着默认情况下不使用子目录中的install()命令。

另一答案

您还可以在辅助生成过程中强制构建从属目标

有关相关主题,请参阅my answer

另一答案

你可以尝试做的是在你的export中使用cmake的project_a命令。它使用install命令与EXPORT option有点不同,因为它在运行cmake时会产生一个project_a-targets.cmake文件。 project_a-targets.cmake文件中生成的导入目标最初指向项目二进制目录中的现有库文件,这些文件仅在运行build命令后生成。

为了更好地理解我正在谈论的,只需创建一个小的cmake项目,创建一个简单的库,然后是export命令(下面的代码尚未经过测试):

add_library (project_a lib.cpp)
export (
  TARGETS 
    project_a
  FILE
    project_a-targets.cmake
)

在您的简单示例上运行cmake命令后,您应该能够在二进制目录(或其子文件夹之一)中找到project_a-targets.cmake。检查文件时,您可能会注意到它当前指向一个不存在的库文件。只有在运行构建命令后,库才会存在。

所以回到你的问题,你需要更新project-aCMakeLists.txt以包含export命令。然后你需要确保在处理ExternalProject_Add之后,它调用配置步骤,这将生成project_a-targets.cmake,然后你可以调用应该工作的include(.../project_a-targets.cmake)。最后,您需要在project_bproject_a之间添加依赖关系,以便在尝试构建project_a之前强制构建project_b

以上是关于CMake:如何构建外部项目并包含其目标的主要内容,如果未能解决你的问题,请参考以下文章

使用 CMake 只构建一次外部库

如何在 CMake 项目中包含外部库

如何使用 CMake 构建和使用外部库

如何在 CMake 中构建过程后复制目标的所有运行时依赖项?

将目标属性添加到 CMake 中现有的导入库

如何在 CMake 中正确创建目标之间的依赖关系?