cmake_minimum_required(VERSION 3.25.2)
project("My_Project" VERSION 1.0.0 LANGUAGES C CXX)
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
include(BuildExternalProject)
include(FindGlfw)
add_executable(exec src/main.cpp)
target_link_libraries(exec
PUBLIC
glfw_target
)
include(ExternalProject)
macro(append_cmake_prefix_path)
list(APPEND CMAKE_PREFIX_PATH ${ARGN})
string(REPLACE ";" "|" CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH}")
endmacro()
function(BuildExternalProject)
set(one_value_args
NAME # name of directory in <INSTALL_DIR>/NAME"
LIB_NAME # For example, the glfw library needs us to link against the target glfw3 not glfw
GIT_REPO # URL : https://github.com/user/repo.git
GIT_TAG # Release tags: "3.3.8","2.3.2" ...
INSTALL_PATH # "${PROJECT_SOURCE_DIR}/external" in this example
BUILD_TYPE # "Release", "Debug" ...
ARCHITECTURE # "x64","x86"...
)
set(multi_value_args
EXTRA_CMAKE_ARGS
EXT_BUILD_ARGS
EXT_INSTALL_ARGS
)
# Parse arguments.
cmake_parse_arguments(
_ARGS # Main prefix for parsed args
"${options}"
"${one_value_args}"
"${multi_value_args}"
${ARGN} # Number of Args
)
# Set new path
set(INSTALL_PATH_NEW_PREFIX ${_ARGS_INSTALL_PATH}/${_ARGS_ARCHITECTURE}-${_ARGS_BUILD_TYPE}/${_ARGS_NAME})
set(INSTALL_PATH_LIB ${INSTALL_PATH_NEW_PREFIX}/lib)
set(INSTALL_PATH_INCLUDE ${INSTALL_PATH_NEW_PREFIX}/include)
set(_UTILITY_NAME ${_ARGS_NAME}_utility)
ExternalProject_Add(
${_UTILITY_NAME}
PREFIX ${_UTILITY_NAME}
GIT_REPOSITORY ${_ARGS_GIT_REPO}
GIT_TAG ${_ARGS_GIT_TAG}
CMAKE_CACHE_ARGS # CMAKE_ARGS does not use the FORCE option, this is a problem especially in IDEs
"-DCMAKE_BUILD_TYPE:STRING=${_ARGS_BUILD_TYPE}"
# This is where the install will
"-DCMAKE_INSTALL_PREFIX:PATH=${INSTALL_PATH_NEW_PREFIX}"
# Addition parsed args
${_ARGS_EXTRA_CMAKE_ARGS}
# Commands to build
BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target install --config ${_ARGS_BUILD_TYPE}
COMMAND ${_ARGS_EXT_BUILD_ARGS}
# Commands to install
INSTALL_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target install --config ${_ARGS_BUILD_TYPE}
COMMAND ${_ARGS_EXT_INSTALL_ARGS}
CONFIGURE_HANDLED_BY_BUILD ON # No config steps are passed
BUILD_ALWAYS FALSE
UPDATE_DISCONNECTED TRUE
)
# We append to `CMAKE_INSTALL_PREFIX`
append_cmake_prefix_path(${INSTALL_PATH_NEW_PREFIX})
# We make the temporary directory
file(MAKE_DIRECTORY ${INSTALL_PATH_INCLUDE})
# We make the new dummy imported library
add_library(${_ARGS_LIB_NAME} INTERFACE IMPORTED GLOBAL ${_UTILITY_NAME})
# Setup the include and libs/dlls,etc
target_include_directories(${_ARGS_LIB_NAME} INTERFACE ${INSTALL_PATH_INCLUDE})
target_link_libraries(${_ARGS_LIB_NAME} INTERFACE ${INSTALL_PATH_LIB}/*.lib)
add_dependencies(${_ARGS_LIB_NAME} ${_UTILITY_NAME})
endfunction()
最终项目结构
Project
|
|-src
| |-main.cpp
|
|-external
| |-x64-Debug
| | |-glfw # Everything under this is auto generated
| | |-include/GLFW/..
| | |-lib
| | | |-cmake/*Config.cmake; *.cmake # This is where we need to point CMAKE_INSTALL_PREFIX
| | | | # so find_package() can find it you need it to in the future
| | | |-pkgconfig/*.pc
| |
| |-x64-Release
| |-glfw # Everything under this is auto generated
| |-include/GLFW/..
| |-lib
| | |-cmake/*Config.cmake; *.cmake # This is where we need to point CMAKE_INSTALL_PREFIX
| | | # so find_package() can find it you need it to in the future
| | |-pkgconfig/*.pc
|
|
|
|-CMakeLists.txt
|
|-cmake
| |-BuildExternalProject.cmake
| |-FindGLFW.cmake
获取内容声明与外部项目添加
1.由于FetchContent_Declare()使目标在配置时可用,并且如果源代码非常大,您可能不希望在配置时下载或构建所有库,特别是如果您的项目只需要外部项目中的几个子目标。这些参数可以作为可选参数通过ExternalProject_Add()传递到子项目中。 1.从docs of FetchContent_Declare
3条答案
按热度按时间yhived7q1#
您误解了
ExternalProject
的工作原理。您 * 不能 *find_package(messages REQUIRED)
,因为它还没有构建。ExternalProject
只是创建构建子项目所需的构建步骤。您有两种选择:
1.使用
add_subdirectory
或FetchContent
代替ExternalProject
。在这种情况下,您不需要find_package
调用。这有效地将子项目添加到主构建中并导入子项目的目标。1.使用 * 两个 *
ExternalProject
调用:一个用于messages
,另一个用于main_project
,这取决于messages
。如果messages
使用export(EXPORT)
函数,你可以将CMAKE_PREFIX_PATH
或messages_ROOT
指向build目录,否则你需要运行messages
的安装步骤,并在build目录中设置一个安装前缀。然后main_project
内部的find_project(messages REQUIRED)
调用将成功,这可能需要重新构造您的构建。一般来说,
ExternalProject
只在定义 * super-build * 时有用,super-build是一系列相互依赖的CMake构建,而super-build只在需要完全不同的配置时选项时有用,比如不同的工具链(例如,你正在交叉编译,但需要一个代码生成器在编译机器上运行)。如果不是这样,最好使用FetchContent
或add_subdirectory
加上git子模块。最好在CMake 3.14+中使用
FetchContent
,因为它添加了FetchContent_MakeAvailable
宏,可以减少样板文件。文件:
https://cmake.org/cmake/help/latest/module/ExternalProject.htmlhttps://cmake.org/cmake/help/latest/module/FetchContent.html
zyfwsgd62#
因为我喜欢保持我的CMake文件不可知我如何得到我的包。
我使用的是
FetchContent
,添加了这个文件(Findalib.cmake
):然后,在我的CMake文件中:
这样,包只在我声明依赖关系的文件中声明,并且只有当我试图找到它们时才获取它们。
ddrv8njm3#
从@Alex Reinking的回答来看,通常情况下,最好少一些样板文件,并且只在超级构建时使用ExternalProject。然而,有时库不支持
FetchContent()
,就像@Blackclaws指出的那样。也就是说,使用ExternalProject_Add()
更好一些,随着我们继续开发C++项目或我们的项目变得更大,这一点对我们来说会很清楚。下面是一个没有
FetchContent_Declare()
或find_package()
的替代解决方案,尽管它没有经过彻底测试。在没有外部包管理器的纯CMake中可以做到这一点。请参阅下面的详细信息部分。事先了解目标是有帮助的(.lib/.dll/.so/.dylib),并包括由ExternalProject_Add()
命令返回的 * 实用程序 * 导出的目录、命名空间等。您可以修改此示例以包含official docs中的更多可用功能
TL;DR:详细信息
在配置项目时,我们可以使用一个虚拟库来使cmake触发构建,而不会导致
ExternalProject_Add()
出错,并传递创建目标的Debug
配置的步骤,我们可以在项目中使用另一个目标的Release
配置,等等。1.您需要通过
CMAKE_CACHE_ARGS
而不是CMAKE_ARGS
将构建和安装参数指定给ExternalProject_Add()
,因为它们被临时参数叠加,并被结转(特别是在IDE中,如Visual Studio代码。如果您的项目使用调试构建,但ExternalProject指定发布构建)1.将
Location/to/the/install/directory/of/your/target
附加到CMAKE_INSTALL_PREFIX
1.创建临时目录文件(MAKE_DIRECTORY
Location/to/the/include/directory/of/your/target
)。这将在配置时抑制 * 位置不存在 * 错误。1.下一步是创建一个虚拟的 * 导入接口库 *,它是链接到主可执行文件的目标。这一步需要设置
IMPORTED_LOCATION
和INTERFACE_INCLUDE_DIRECTORIES
以及其他属性。文档在这里,cmake-properties中。1.最后,
add_dependencies(dummy_target_name exported_target_name)
删除输出中抛出的错误(The返回的目标在带有CMakeTools扩展的Visual Studio代码中被称为实用程序。我在这里也将其称为实用程序。不确定它在原生cmake中是否被称为实用程序)
示例
我们将使用
ExternalProject_Add()
构建GLFW。初始项目结构
主要CMakeLists.txt文件:
FindGLFW.cmake
cmake/构建外部项目。cmake:
最终项目结构
获取内容声明与外部项目添加
1.由于
FetchContent_Declare()
使目标在配置时可用,并且如果源代码非常大,您可能不希望在配置时下载或构建所有库,特别是如果您的项目只需要外部项目中的几个子目标。这些参数可以作为可选参数通过ExternalProject_Add()
传递到子项目中。1.从docs of FetchContent_Declare
ExternalProject_Add()
提供了一个更大的选项集,可以在配置、构建、安装、测试等阶段运行。它本身就是一个项目,可以帮助直接公开完全或部分构建的二进制文件、库等。来源