CMake ExternalProject 不解压依赖项
CMake ExternalProject does not unpack dependencies
我正在构建一个应用程序,它使用 tinyxml2
和我在 [=20] 中作为 .zip
文件提供的其他一些依赖项(即 Irrlicht
和 IrrKlang
) =] 我项目的子目录:
.
├── CMakeLists.txt
├── Dependencies
│ ├── irrKlang-1.5.0.zip
│ ├── irrKlang-32bit-1.5.0.zip
│ ├── irrKlang-64bit-1.5.0.zip
│ ├── irrlicht-1.8.4.zip
│ └── tinyxml2-master.zip
├── Editor
│ ├── CMakeLists.txt
│ └── Sources
│ └── main.cpp
└── Game
├── CMakeLists.txt
└── Sources
└── main.cpp
注意: 供参考,完整的资源可用 on GitHub,这里我偷工减料来提出问题更短。
顶层CMakeFiles.txt
设置为:
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
project(shoot-them VERSION 1.0.1 LANGUAGES CXX)
include(ExternalProject)
# few platform-specific variables like MAKE_COMMAND, PLATFORM_ARCH, etc.
# libraries (see below)
add_subdirectory(Game)
add_subdirectory(Editor
Irrlicht
和 IrrKlang
都带有针对 Windows x86 的预构建库,但不针对 Windows x64 和 OSX。因此我将它添加为这样的依赖项(使用 if(NOT IRRLICHT_LIBRARY_PATH)
只是将代码分成一个块:
if(NOT IRRLICHT_LIBRARY_PATH)
ExternalProject_Add(irrlicht-dep
URL ${CMAKE_CURRENT_LIST_DIR}/Dependencies/irrlicht-1.8.4.zip
PREFIX Dependencies/irrlicht
SOURCE_SUBDIR source/Irrlicht
CONFIGURE_COMMAND ""
BUILD_COMMAND "${MAKE_COMMAND}"
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(irrlicht-dep SOURCE_DIR)
set(IRRLICHT_PATH ${SOURCE_DIR})
add_library(irrlicht SHARED IMPORTED GLOBAL)
set_target_properties(
irrlicht PROPERTIES
IMPORTED_LOCATION ${IRRLICHT_PATH}/lib/${IRRLICHT_PATH_SUFFIX}/Irrlicht${CMAKE_LINK_LIBRARY_SUFFIX}
IMPORTED_IMPLIB ${IRRLICHT_PATH}/lib/${IRRLICHT_PATH_SUFFIX}/Irrlicht${CMAKE_IMPORT_LIBRARY_SUFFIX}
INTERFACE_INCLUDE_DIRECTORIES ${IRRLICHT_PATH}/include
)
endif()
我遵循与 IrrKlang
相同的原则。但是由于 tinyxml2
作为头文件和源文件出现,并且它与 CMakeLists.txt
打包在一起,我只是像这样包含它:
ExternalProject_Add(tinyxml2-dep
URL ${CMAKE_CURRENT_LIST_DIR}/Dependencies/tinyxml2-master.zip
PREFIX Dependencies/tinyxml2
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(tinyxml2-dep SOURCE_DIR)
set(TINYXML2_PATH ${SOURCE_DIR})
add_subdirectory(${TINYXML2_PATH})
我定义Game
和Editor
两个子项目如下:
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
project(Editor VERSION 1.0.1 LANGUAGES CXX)
set(EXECUTABLE_NAME Editor)
set(SOURCES Sources/main.cpp)
# platform-specific variables
set_target_properties(${EXECUTABLE_NAME} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS ON
)
add_dependencies(${EXECUTABLE_NAME} tinyxml2 irrlicht)
target_link_libraries(${EXECUTABLE_NAME} PUBLIC ${LIBRARIES} tinyxml2 irrlicht)
if(NOT APPLE)
# Copy libraries' DLLs
add_custom_command(
TARGET ${EXECUTABLE_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:tinyxml2> $<TARGET_FILE_DIR:${EXECUTABLE_NAME}>
)
endif()
# TODO: copy DLLs, not LIBs
add_custom_command(
TARGET ${EXECUTABLE_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:irrlicht> $<TARGET_FILE_DIR:${EXECUTABLE_NAME}>
)
我具体使用ExternalProject
的方式是因为
- 我想修复我构建项目时使用的版本
- 我不想安装库污染我的系统(因此没有
INSTALL
步骤,尽管我可能完全误解了这个概念)
- 我不依赖于 3rd 方存储库已死(因此将 ZIP 文件与源一起发送)
- 我不依赖令人难以置信的过时(因此可能无法工作)查找 CMake 模块
公平地说:我不知道使用 CMake 构建项目的任何最佳实践,所以我很可能对以上所有内容完全错误,所以请纠正我.
当我在 Visual Studio 2019 年 Windows 构建这个项目时,它就像一个魅力。但是每当我尝试在 OSX 上构建东西时,我都会失败:
- none 的依赖项甚至被解压
- (因为第 1 页) 永远不会设置
${TINYXML2_DIR}
- (因为 p.2) 无法找到
tinyxml2
目录,因此通过 add_subdirectory()
添加
- (因为第 3 页)
$<TARGET_FILE:tinyxml2>
表达式不计算
- (作为全局结果) 项目未构建
我构建项目的方式比较简单:
cmake -Bbuild -H. && cmake --build build
我做错了什么?
此外,使用 CMake 处理第三方依赖项的正确方法是什么?
我非常清楚 CMake 技术上 只是一个 makefile(粗略地说,因为每个构建工具链都不同)生成器,所以我的问题更多是关于如何告诉 CMake 为应该用我的项目构建的每种类型的依赖项生成正确的构建文件(预构建,使用 CMake 从源构建,使用自定义命令从源构建) .我原以为 ExternalProject
应该可以解决这个问题,但显然在这个过程中出现了可怕的错误。
我已经尝试过@Mizux 在 中建议的两种解决方案,并通过两种不同的方法取得了一些成功。
1。 vcpkg
这可以说是两者中较容易的一个。它需要安装 vcpkg。
例如参见this commit。
创建清单文件,vcpkg.json
在项目根目录下,列出项目使用的所有依赖项:
{
"name": "PROJECT_NAME",
"version-string": "0.1.0",
"dependencies": [
"irrlicht",
"tinyxml2"
]
}
您还可以使用 CLI 通过使用 vcpkg install
命令生成清单。
使用 CMake 中的 find_package
到每个目标的 link 库 - 在子 CMakeLists.txt
:
find_package(irrlicht CONFIG REQUIRED)
find_package(tinyxml2 CONFIG REQUIRED)
target_link_libraries(${EXECUTABLE_NAME} PUBLIC ${LIBRARIES} tinyxml2::tinyxml2 Irrlicht)
重要:配置项目时,将路径传递给vcpkg CMake模块:
cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=[vcpkg root]/scripts/buildsystems/vcpkg.cmake
2。 FetchContent + ExternalProject
由于我的项目依赖于 CMakeLists.txt
附带的库和不附带的库,所以这是一个很好的例子。
例如参见this commit。
首先,正如@Mizux 提到的,FetchContent
在配置时工作——它会在您配置项目时下载并解压缩依赖项(调用 cmake -S . -B build
)。然后,由于 irrlicht
不随 CMakeLists.txt
一起提供,您可以使用 ExternalProject_Add
通过自定义命令构建它(在我的例子中是 make
)或将其添加为 sub-directory 到项目。
FetchContent
部分:
include(FetchContent)
FetchContent_Declare(irrlicht
URL ${CMAKE_CURRENT_LIST_DIR}/Dependencies/irrlicht-1.8.4.zip
)
FetchContent_GetProperties(irrlicht)
if(NOT irrlicht_POPULATED)
FetchContent_Populate(irrlicht)
endif()
set(IRRLICHT_PATH ${irrlicht_SOURCE_DIR})
ExternalProject
部分:
include(ExternalProject)
if(NOT WIN32)
ExternalProject_Add(irrlicht-dep
SOURCE_DIR "${IRRLICHT_PATH}/source/Irrlicht"
BUILD_IN_SOURCE true
CONFIGURE_COMMAND ""
BUILD_COMMAND "${MAKE_COMMAND}"
)
endif()
add_library(irrlicht SHARED IMPORTED GLOBAL)
set_target_properties(
irrlicht PROPERTIES
IMPORTED_LOCATION ${IRRLICHT_PATH}/lib/${IRRLICHT_PATH_SUFFIX}/Irrlicht${CMAKE_LINK_LIBRARY_SUFFIX}
IMPORTED_IMPLIB ${IRRLICHT_PATH}/lib/${IRRLICHT_PATH_SUFFIX}/Irrlicht${CMAKE_IMPORT_LIBRARY_SUFFIX}
INTERFACE_INCLUDE_DIRECTORIES ${IRRLICHT_PATH}/include
)
注意 ExternalProject
配置中的重要位:
irrlicht_SOURCE_DIR
变量由 FetchContent
填充
SOURCE_DIR "${irrlicht_SOURCE_DIR}/source/Irrlicht"
- 告诉 ExternalProject
解压源 在哪里;如果这个目录不为空(它应该不是,因为 FetchContent
应该在项目配置时填充它),ExternalProject
将跳过下载阶段
BUILD_IN_SOURCE true
- 从源目录中构建
CONFIGURE_COMMAND ""
- 跳过依赖配置阶段,所以不会为依赖 执行cmake
BUILD_COMMAND "make"
- 使用特定命令从源构建依赖项
if(NOT WIN32)
- 仅使用 ExternalProject
来 构建 依赖;因为 Irrlicht 带有 pre-built 用于 Win32
的库,这应该为其他平台构建东西(包括 Win64
)
我正在构建一个应用程序,它使用 tinyxml2
和我在 [=20] 中作为 .zip
文件提供的其他一些依赖项(即 Irrlicht
和 IrrKlang
) =] 我项目的子目录:
.
├── CMakeLists.txt
├── Dependencies
│ ├── irrKlang-1.5.0.zip
│ ├── irrKlang-32bit-1.5.0.zip
│ ├── irrKlang-64bit-1.5.0.zip
│ ├── irrlicht-1.8.4.zip
│ └── tinyxml2-master.zip
├── Editor
│ ├── CMakeLists.txt
│ └── Sources
│ └── main.cpp
└── Game
├── CMakeLists.txt
└── Sources
└── main.cpp
注意: 供参考,完整的资源可用 on GitHub,这里我偷工减料来提出问题更短。
顶层CMakeFiles.txt
设置为:
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
project(shoot-them VERSION 1.0.1 LANGUAGES CXX)
include(ExternalProject)
# few platform-specific variables like MAKE_COMMAND, PLATFORM_ARCH, etc.
# libraries (see below)
add_subdirectory(Game)
add_subdirectory(Editor
Irrlicht
和 IrrKlang
都带有针对 Windows x86 的预构建库,但不针对 Windows x64 和 OSX。因此我将它添加为这样的依赖项(使用 if(NOT IRRLICHT_LIBRARY_PATH)
只是将代码分成一个块:
if(NOT IRRLICHT_LIBRARY_PATH)
ExternalProject_Add(irrlicht-dep
URL ${CMAKE_CURRENT_LIST_DIR}/Dependencies/irrlicht-1.8.4.zip
PREFIX Dependencies/irrlicht
SOURCE_SUBDIR source/Irrlicht
CONFIGURE_COMMAND ""
BUILD_COMMAND "${MAKE_COMMAND}"
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(irrlicht-dep SOURCE_DIR)
set(IRRLICHT_PATH ${SOURCE_DIR})
add_library(irrlicht SHARED IMPORTED GLOBAL)
set_target_properties(
irrlicht PROPERTIES
IMPORTED_LOCATION ${IRRLICHT_PATH}/lib/${IRRLICHT_PATH_SUFFIX}/Irrlicht${CMAKE_LINK_LIBRARY_SUFFIX}
IMPORTED_IMPLIB ${IRRLICHT_PATH}/lib/${IRRLICHT_PATH_SUFFIX}/Irrlicht${CMAKE_IMPORT_LIBRARY_SUFFIX}
INTERFACE_INCLUDE_DIRECTORIES ${IRRLICHT_PATH}/include
)
endif()
我遵循与 IrrKlang
相同的原则。但是由于 tinyxml2
作为头文件和源文件出现,并且它与 CMakeLists.txt
打包在一起,我只是像这样包含它:
ExternalProject_Add(tinyxml2-dep
URL ${CMAKE_CURRENT_LIST_DIR}/Dependencies/tinyxml2-master.zip
PREFIX Dependencies/tinyxml2
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(tinyxml2-dep SOURCE_DIR)
set(TINYXML2_PATH ${SOURCE_DIR})
add_subdirectory(${TINYXML2_PATH})
我定义Game
和Editor
两个子项目如下:
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
project(Editor VERSION 1.0.1 LANGUAGES CXX)
set(EXECUTABLE_NAME Editor)
set(SOURCES Sources/main.cpp)
# platform-specific variables
set_target_properties(${EXECUTABLE_NAME} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS ON
)
add_dependencies(${EXECUTABLE_NAME} tinyxml2 irrlicht)
target_link_libraries(${EXECUTABLE_NAME} PUBLIC ${LIBRARIES} tinyxml2 irrlicht)
if(NOT APPLE)
# Copy libraries' DLLs
add_custom_command(
TARGET ${EXECUTABLE_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:tinyxml2> $<TARGET_FILE_DIR:${EXECUTABLE_NAME}>
)
endif()
# TODO: copy DLLs, not LIBs
add_custom_command(
TARGET ${EXECUTABLE_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:irrlicht> $<TARGET_FILE_DIR:${EXECUTABLE_NAME}>
)
我具体使用ExternalProject
的方式是因为
- 我想修复我构建项目时使用的版本
- 我不想安装库污染我的系统(因此没有
INSTALL
步骤,尽管我可能完全误解了这个概念) - 我不依赖于 3rd 方存储库已死(因此将 ZIP 文件与源一起发送)
- 我不依赖令人难以置信的过时(因此可能无法工作)查找 CMake 模块
公平地说:我不知道使用 CMake 构建项目的任何最佳实践,所以我很可能对以上所有内容完全错误,所以请纠正我.
当我在 Visual Studio 2019 年 Windows 构建这个项目时,它就像一个魅力。但是每当我尝试在 OSX 上构建东西时,我都会失败:
- none 的依赖项甚至被解压
- (因为第 1 页) 永远不会设置
${TINYXML2_DIR}
- (因为 p.2) 无法找到
tinyxml2
目录,因此通过add_subdirectory()
添加
- (因为第 3 页)
$<TARGET_FILE:tinyxml2>
表达式不计算 - (作为全局结果) 项目未构建
我构建项目的方式比较简单:
cmake -Bbuild -H. && cmake --build build
我做错了什么?
此外,使用 CMake 处理第三方依赖项的正确方法是什么?
我非常清楚 CMake 技术上 只是一个 makefile(粗略地说,因为每个构建工具链都不同)生成器,所以我的问题更多是关于如何告诉 CMake 为应该用我的项目构建的每种类型的依赖项生成正确的构建文件(预构建,使用 CMake 从源构建,使用自定义命令从源构建) .我原以为 ExternalProject
应该可以解决这个问题,但显然在这个过程中出现了可怕的错误。
我已经尝试过@Mizux 在
1。 vcpkg
这可以说是两者中较容易的一个。它需要安装 vcpkg。
例如参见this commit。
创建清单文件,vcpkg.json
在项目根目录下,列出项目使用的所有依赖项:
{
"name": "PROJECT_NAME",
"version-string": "0.1.0",
"dependencies": [
"irrlicht",
"tinyxml2"
]
}
您还可以使用 CLI 通过使用 vcpkg install
命令生成清单。
使用 CMake 中的 find_package
到每个目标的 link 库 - 在子 CMakeLists.txt
:
find_package(irrlicht CONFIG REQUIRED)
find_package(tinyxml2 CONFIG REQUIRED)
target_link_libraries(${EXECUTABLE_NAME} PUBLIC ${LIBRARIES} tinyxml2::tinyxml2 Irrlicht)
重要:配置项目时,将路径传递给vcpkg CMake模块:
cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=[vcpkg root]/scripts/buildsystems/vcpkg.cmake
2。 FetchContent + ExternalProject
由于我的项目依赖于 CMakeLists.txt
附带的库和不附带的库,所以这是一个很好的例子。
例如参见this commit。
首先,正如@Mizux 提到的,FetchContent
在配置时工作——它会在您配置项目时下载并解压缩依赖项(调用 cmake -S . -B build
)。然后,由于 irrlicht
不随 CMakeLists.txt
一起提供,您可以使用 ExternalProject_Add
通过自定义命令构建它(在我的例子中是 make
)或将其添加为 sub-directory 到项目。
FetchContent
部分:
include(FetchContent)
FetchContent_Declare(irrlicht
URL ${CMAKE_CURRENT_LIST_DIR}/Dependencies/irrlicht-1.8.4.zip
)
FetchContent_GetProperties(irrlicht)
if(NOT irrlicht_POPULATED)
FetchContent_Populate(irrlicht)
endif()
set(IRRLICHT_PATH ${irrlicht_SOURCE_DIR})
ExternalProject
部分:
include(ExternalProject)
if(NOT WIN32)
ExternalProject_Add(irrlicht-dep
SOURCE_DIR "${IRRLICHT_PATH}/source/Irrlicht"
BUILD_IN_SOURCE true
CONFIGURE_COMMAND ""
BUILD_COMMAND "${MAKE_COMMAND}"
)
endif()
add_library(irrlicht SHARED IMPORTED GLOBAL)
set_target_properties(
irrlicht PROPERTIES
IMPORTED_LOCATION ${IRRLICHT_PATH}/lib/${IRRLICHT_PATH_SUFFIX}/Irrlicht${CMAKE_LINK_LIBRARY_SUFFIX}
IMPORTED_IMPLIB ${IRRLICHT_PATH}/lib/${IRRLICHT_PATH_SUFFIX}/Irrlicht${CMAKE_IMPORT_LIBRARY_SUFFIX}
INTERFACE_INCLUDE_DIRECTORIES ${IRRLICHT_PATH}/include
)
注意 ExternalProject
配置中的重要位:
irrlicht_SOURCE_DIR
变量由FetchContent
填充
SOURCE_DIR "${irrlicht_SOURCE_DIR}/source/Irrlicht"
- 告诉ExternalProject
解压源 在哪里;如果这个目录不为空(它应该不是,因为FetchContent
应该在项目配置时填充它),ExternalProject
将跳过下载阶段BUILD_IN_SOURCE true
- 从源目录中构建CONFIGURE_COMMAND ""
- 跳过依赖配置阶段,所以不会为依赖 执行BUILD_COMMAND "make"
- 使用特定命令从源构建依赖项if(NOT WIN32)
- 仅使用ExternalProject
来 构建 依赖;因为 Irrlicht 带有 pre-built 用于Win32
的库,这应该为其他平台构建东西(包括Win64
)
cmake