cmake项目中使用第三方库的正确方法
Correct way to use third-party libraries in cmake project
我正在处理使用一些第三方库的项目。我可以将它们作为 git submodule
克隆并放入我的项目中,并使用 add_subdirectory
在我的项目中使用它们。但是编译这些库需要很多时间,而且我无法在我的项目中管理 cmake
变量,并且 install()
命令使我的包包含许多我不需要的东西。
那么在我的项目中使用第三方库的正确方法是什么。我不能告诉客户端安装这些库作为我项目的依赖项。
回答这个问题需要涵盖几个方面,您会发现以下两个部分:
- 配置文件包
- ExternalProject CMake 模块
配置文件包
如果您正在考虑集成不在项目范围内构建的库,第一步是确保这些库都提供配置文件包。
配置文件包通常包含 FooConfig.cmake
、FooConfigVersion.cmake
和 FooTargets.cmake
.
等文件
一般来说,如果库 Foo
已经在使用 CMake 并且已经提供了一个配置文件包,使用 -DFoo_DIR:PATH=/path/to/build-or-install-dir/
配置您的项目允许您从自己的调用 find_package(Foo REQUIRED)
项目。这将导入您可以 link 针对您自己的库或可执行文件的 CMake 目标。
现在,如果库 Foo
尚未使用 CMake,则有以下选项:
案例一:
- (a) 库
Foo
已经在使用 CMake
- (b) 但不要 提供配置文件包
- 行动:我建议改进他们的构建系统
案例二:
- (1) 库
Foo
不 使用 CMake
- (2) 和
Foo
的维护者愿意过渡到 CMake(或者至少在他们当前的构建系统旁边有 CMakeLists.txt
)
- 行动:我建议改进他们的构建系统
案例三:
- (1) 库
Foo
不 使用 CMake
- (2) 和
Foo
的维护者不想过渡到 CMake
- (3) 但维护者愿意从他们当前的构建系统生成配置文件包
- 行动:我建议帮助他们。例如,这是为 Qt5 所做的,它现在提供了一个配置文件包。
案例四:
(1) 库 Foo
不是 使用 CMake
(2) 和 Foo
的维护者不想(或尚未准备好)过渡到 CMake。
(3) 并且当前构建系统运行不佳,或者库难以使用更广泛的编译器构建,或者不支持交叉编译
action:创建一个名为 foo-cmake-buildsystem
的项目(最好在 GitHub 上),允许通过以下任一方式构建库
- 使用现有源代码树的路径配置项目
- 让项目为您下载源代码
- 例如,这是为 CPython 完成的。 GitHub
上有一个名为 python-cmake-buildsystem
的项目
案例五:
- (1)
Foo
的维护者出于任何原因不想转换,或者维护替代构建系统是不可能的,或者库已经在系统上可用
- action:您可以创建一个
FindFoo.cmake
来创建导入的目标。
- 此类文件可以特定于您的项目,也可以直接贡献给 CMake
- 例如
FindOpenSSL.cmake
、FindGit.cmake
、... 的情况
要了解有关配置文件包的更多信息,请参阅 https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html
ExternalProject CMake 模块
如果图书馆 Foo
是:
- (1) 在系统上不可用:
- 或者无法使用包管理器安装
- 或与维护软件包(debian、conda-forge、chocolatey 等)的社区合作以获得这样的软件包是不可能的
- (2) 或者需要为你的项目专门编译
然后,ExternalProject
CMake 模块将允许您从自己的项目中下载、配置、构建...这些项目。
实现这一目标的方法很少。
这是一个运行良好的系统:您可以设置一个我们称之为的 2 级构建系统:SuperBuild
.
为了支持 SuperBuild
方法,您的 CMakeLists.txt 可以具有以下结构:
project(AwesomeProject)
[...]
option(Awesome_ENABLE_EXTRA "Enable more awesome stuff" OFF)
option(AwesomeProject_SUPERBUILD "Build ${PROJECT_NAME} and the projects it depends on." ON)
[...]
if(AwesomeProject_SUPERBUILD)
include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake")
return()
endif()
find_package(Foo REQUIRED)
add_library(AwesomeLib ....)
target_library_libraries(AwesomeLib PUBLIC Foo)
[...]
然后,在文件 SuperBuild.cmake
中,您大致会有以下两个调用:
ExternalProject_Add(Foo
GIT_REPOSITORY "git://github.com/Foo/Foo"
GIT_TAG "123456"
SOURCE_DIR ${CMAKE_BINARY_DIR}/Foo
BINARY_DIR ${CMAKE_BINARY_DIR}/Foo-build
CMAKE_CACHE_ARGS
-DFOO_ENABLE_BAR:BOOL=1
INSTALL_COMMAND ""
)
ExternalProject_Add(AwesomeProject
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}
BINARY_DIR ${CMAKE_BINARY_DIR}/AwesomeProject-build
DOWNLOAD_COMMAND ""
UPDATE_COMMAND ""
CMAKE_CACHE_ARGS
-Foo_DIR:PATH=${CMAKE_BINARY_DIR}/Foo-build
-DAwesome_ENABLE_EXTRA:BOOL=${Awesome_ENABLE_EXTRA}
INSTALL_COMMAND ""
)
这意味着您通常的构建树现在可以在子目录 AwesomeProject-build
.
中找到
注意Foo-build
和AwesomeProject-build
是两个独立的构建树,它们之间的link
就是上面讨论的配置文件包。
这可以通过使用 -Foo_DIR:PATH=${CMAKE_BINARY_DIR}/Foo-build
配置 AwesomeProject
子项目来实现
和调用 find_package(Foo REQUIRED)
.
如果您使用 VisualStudio 等工具,您可以打开在这些子目录中找到的解决方案文件。
要了解有关外部项目的更多信息:https://cmake.org/cmake/help/latest/module/ExternalProject.html
结论
还有很多细节,但我希望这能让您更好地了解什么是可能的。
我正在处理使用一些第三方库的项目。我可以将它们作为 git submodule
克隆并放入我的项目中,并使用 add_subdirectory
在我的项目中使用它们。但是编译这些库需要很多时间,而且我无法在我的项目中管理 cmake
变量,并且 install()
命令使我的包包含许多我不需要的东西。
那么在我的项目中使用第三方库的正确方法是什么。我不能告诉客户端安装这些库作为我项目的依赖项。
回答这个问题需要涵盖几个方面,您会发现以下两个部分:
- 配置文件包
- ExternalProject CMake 模块
配置文件包
如果您正在考虑集成不在项目范围内构建的库,第一步是确保这些库都提供配置文件包。
配置文件包通常包含 FooConfig.cmake
、FooConfigVersion.cmake
和 FooTargets.cmake
.
一般来说,如果库 Foo
已经在使用 CMake 并且已经提供了一个配置文件包,使用 -DFoo_DIR:PATH=/path/to/build-or-install-dir/
配置您的项目允许您从自己的调用 find_package(Foo REQUIRED)
项目。这将导入您可以 link 针对您自己的库或可执行文件的 CMake 目标。
现在,如果库 Foo
尚未使用 CMake,则有以下选项:
案例一:
- (a) 库
Foo
已经在使用 CMake - (b) 但不要 提供配置文件包
- 行动:我建议改进他们的构建系统
- (a) 库
案例二:
- (1) 库
Foo
不 使用 CMake - (2) 和
Foo
的维护者愿意过渡到 CMake(或者至少在他们当前的构建系统旁边有CMakeLists.txt
) - 行动:我建议改进他们的构建系统
- (1) 库
案例三:
- (1) 库
Foo
不 使用 CMake - (2) 和
Foo
的维护者不想过渡到 CMake - (3) 但维护者愿意从他们当前的构建系统生成配置文件包
- 行动:我建议帮助他们。例如,这是为 Qt5 所做的,它现在提供了一个配置文件包。
- (1) 库
案例四:
(1) 库
Foo
不是 使用 CMake(2) 和
Foo
的维护者不想(或尚未准备好)过渡到 CMake。(3) 并且当前构建系统运行不佳,或者库难以使用更广泛的编译器构建,或者不支持交叉编译
action:创建一个名为
foo-cmake-buildsystem
的项目(最好在 GitHub 上),允许通过以下任一方式构建库- 使用现有源代码树的路径配置项目
- 让项目为您下载源代码
- 例如,这是为 CPython 完成的。 GitHub 上有一个名为
python-cmake-buildsystem
的项目
案例五:
- (1)
Foo
的维护者出于任何原因不想转换,或者维护替代构建系统是不可能的,或者库已经在系统上可用 - action:您可以创建一个
FindFoo.cmake
来创建导入的目标。- 此类文件可以特定于您的项目,也可以直接贡献给 CMake
- 例如
FindOpenSSL.cmake
、FindGit.cmake
、... 的情况
- (1)
要了解有关配置文件包的更多信息,请参阅 https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html
ExternalProject CMake 模块
如果图书馆 Foo
是:
- (1) 在系统上不可用:
- 或者无法使用包管理器安装
- 或与维护软件包(debian、conda-forge、chocolatey 等)的社区合作以获得这样的软件包是不可能的
- (2) 或者需要为你的项目专门编译
然后,ExternalProject
CMake 模块将允许您从自己的项目中下载、配置、构建...这些项目。
实现这一目标的方法很少。
这是一个运行良好的系统:您可以设置一个我们称之为的 2 级构建系统:SuperBuild
.
为了支持 SuperBuild
方法,您的 CMakeLists.txt 可以具有以下结构:
project(AwesomeProject)
[...]
option(Awesome_ENABLE_EXTRA "Enable more awesome stuff" OFF)
option(AwesomeProject_SUPERBUILD "Build ${PROJECT_NAME} and the projects it depends on." ON)
[...]
if(AwesomeProject_SUPERBUILD)
include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake")
return()
endif()
find_package(Foo REQUIRED)
add_library(AwesomeLib ....)
target_library_libraries(AwesomeLib PUBLIC Foo)
[...]
然后,在文件 SuperBuild.cmake
中,您大致会有以下两个调用:
ExternalProject_Add(Foo
GIT_REPOSITORY "git://github.com/Foo/Foo"
GIT_TAG "123456"
SOURCE_DIR ${CMAKE_BINARY_DIR}/Foo
BINARY_DIR ${CMAKE_BINARY_DIR}/Foo-build
CMAKE_CACHE_ARGS
-DFOO_ENABLE_BAR:BOOL=1
INSTALL_COMMAND ""
)
ExternalProject_Add(AwesomeProject
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}
BINARY_DIR ${CMAKE_BINARY_DIR}/AwesomeProject-build
DOWNLOAD_COMMAND ""
UPDATE_COMMAND ""
CMAKE_CACHE_ARGS
-Foo_DIR:PATH=${CMAKE_BINARY_DIR}/Foo-build
-DAwesome_ENABLE_EXTRA:BOOL=${Awesome_ENABLE_EXTRA}
INSTALL_COMMAND ""
)
这意味着您通常的构建树现在可以在子目录 AwesomeProject-build
.
注意Foo-build
和AwesomeProject-build
是两个独立的构建树,它们之间的link
就是上面讨论的配置文件包。
这可以通过使用 -Foo_DIR:PATH=${CMAKE_BINARY_DIR}/Foo-build
配置 AwesomeProject
子项目来实现
和调用 find_package(Foo REQUIRED)
.
如果您使用 VisualStudio 等工具,您可以打开在这些子目录中找到的解决方案文件。
要了解有关外部项目的更多信息:https://cmake.org/cmake/help/latest/module/ExternalProject.html
结论
还有很多细节,但我希望这能让您更好地了解什么是可能的。