严正声明:本文系作者davidhopper原创,未经许可,不得转载。
众所周知,对于外部依赖库的管理是CMake C++工程中一个令人头疼的问题。人们一直希望能有一个工具来自动配置CMake C++工程的外部依赖库。现在,这个自动化工具终于诞生了,这就是CPM(https://github.com/cpm-cmake/CPM.cmake)。只要电脑连接了互联网,任何CMake C++项目都可借助 CPM 添加外部依赖库。
CPM的优缺点分别为:
-
优点
-
极其轻量化,即插即用,无需安装,仅需下载一个CPM.cmake脚本文件;
-
跨平台 ,可用于任何操作系统;
-
可与Git联合使用实现对依赖库的版本管理。
-
不足
-
所有依赖库最初都要从网上下载构建,如离线使用需设置环境变量:CPM_SOURCE_CACHE 。
一、下载CPM
将CPM加入当前CMake工程,仅需从GitHub网站下载最新版本的CPM.cmake脚本文件,或者从其他项目中将该文件复制到当前项目。
从GitHub网站下载的命令如下所示:
# 在当前项目的根目录创建cmake目录
mkdir cmake
# 下载CPM.cmake文件到cmake目录
wget -O cmake/CPM.cmake https://github.com/cpm-make/CPM.cmake/releases/latest/download/get_cpm.cmake
说明:国内因为墙的原因,访问GitHub网站网速很慢,一般难以下载成功(说明:反正我在不用代理情况下没有下载成功过)。要解决该问题,可以使用代理。如无代理,也可以从Gitte网站寻找替代源。例如,我在Gitte网站找到了一个替代源(版本不是最新),直接将其克隆下来,命令如下所示:
git clone https://gitee.com/hejuncheng1/CPM.cmake
之后,再将该工程中的“CPM.cmake”文件复制到当前CMake项目:
# 在当前项目的根目录创建cmake目录
mkdir cmake
# 将“CPM.cmake”文件复制到cmake目录
# ~/code/CPM.cmake需要修改为你的实际地址
cp ~/code/CPM.cmake/cmake/CPM.cmake ./cmake
二、完整使用语法
CPM语法简单,使用方便,通过调用CPMAddPackage就可安装一个外部库,具体语法如下:
CPMAddPackage(
NAME # 依赖库的唯一名称,一般是网页链接,如果是GitHub依赖库,还需通过GITHUB_REPOSITORY属性指定GitHub仓库名
VERSION # 依赖库的最低版本号(可选,缺省为0)
OPTIONS # 传递给依赖库的配置选项(可选)
DOWNLOAD_ONLY # 如设置,则从网上下载依赖库(可选)
[...] # 依赖库的源,用于转发给CMake自带的FetchContent_Declare函数(可选),详见下文描述。
)
上述语法中的[...]
表示依赖库的源,用于转发给CMake自带的FetchContent_Declare函数。源可以通过GIT_REPOSITORY
指定,也可直接通过URL地址指定,例如https://example.com/my-package-1.2.3.zip
。如果源由GIT_REPOSITORY
指定,可使用GIT_TAG
指定依赖库的版本号;如不使用GIT_TAG
,则依赖库的版本号由VERSION
指定。GIT_TAG
也可以设置为特定的提交(即Git的commit id,例如64913822041ca9c7a30f5c61fe4584577a3df3e9)或分支名称(即Git的branch,例如 master),但不建议这样做,因为在此情况下依赖库仅在缓存清除时才会更新。如果额外的可选参数EXCLUDE_FROM_ALL
设置为真值,则默认情况下不会构建依赖库中定义的任何目标。
示例:添加GitHub网站中的依赖库googletest,配置如下:
CPMAddPackage(
NAME googletest # 添加依赖库:googletest
GITHUB_REPOSITORY google/googletest # GitHub仓库是:google/googletest,CPM会在GitHub网站找到该仓库,并得到对应的仓库链接:https://github.com/google/googletest
GIT_TAG release-1.8.1 # Git标签是release-1.8.1
VERSION 1.8.1 # 版本号是1.8.1
OPTIONS # 可选参数包括:INSTALL_GTEST:关,gtest_force_shared_crt,开
"INSTALL_GTEST OFF"
"gtest_force_shared_crt ON"
)
三、简单使用语法
以上完整语法过于啰嗦,可使用更为简洁的形式:
# 通过URI添加依赖库
CPMAddPackage("uri")
# 通过URI添加指定版本号的依赖库
CPMAddPackage("uri@version")
# 通过URI添加指定标签的依赖库
CPMAddPackage("uri#tag")
# 通过URI添加指定版本号和指定标签的依赖库
CPMAddPackage("uri@version#tag")
“uri”指网址,例如“https://example.com/my-package-1.2.3.zip”,GitHub、GitLab、BitBucket等开源代码库网址可以使用缩写形式,如下表所示:
序号 | 缩写网址 | 完整网址 |
---|---|---|
1 | gh:user/name | https://github.com/user/name.git |
2 | gl:user/name | https://gitlab.com/user/name.git |
3 | bb:user/name | https://bitbucket.org/user/name.git |
以下是相关示例:
# 基于URL添加依赖库,版本号依据网址内容推断为:1.2.3
CPMAddPackage("https://example.com/my-package-1.2.3.zip")
# 基于URL添加依赖库,版本号依据网址内容推断为:1.2.3,并基于给定的MD5码作依赖库完整性验证
CPMAddPackage("https://example.com/my-package-1.2.3.zip#MD5=68e20f674a48be3使用8d60e129f600faf7d")
# 基于URL添加依赖库,版本号显式地指定
CPMAddPackage("https://example.com/my-package.zip@1.2.3")
# 基于GitHub网址添加依赖库,依赖库名Catch2,版本号为:2.5.0
# gh:catchorg/Catch2对应的完整网址为:https://github.com/catchorg/Catch2.git
CPMAddPackage("gh:catchorg/Catch2@2.5.0")
# 基于GitHub网址添加依赖库,依赖库名json,版本号为:3.9.1
# gh:nlohmann/json对应的完整网址为:https://github.com/lohmann/json.git
CPMAddPackage("gh:nlohmann/json@3.9.1")
四、补充说明
调用CPMAddPackage, 以下变量将在当前CMake工程的作用域内定义:
- <dependency>_SOURCE_DIR
- <dependency>_BINARY_DIR
- <dependency>_ADDED
<dependency>是依赖库名。例如通过CPMAddPackage添加依赖库boost后,将在CMake工程的作用域内定义如下变量:boost_SOURCE_DIR、boost_BINARY_DIR、boost_ADDED。
几个特殊变量的说明:
- CPM_SOURCE_CACHE
为了避免从网上重新下载依赖库,可以使用CPM_SOURCE_CACHE。只要依赖库之前已经下载过,CMake项目在离线状态下就可以使用之前下载的缓存,而不会反复到网上去下载。传递该参数的方法如下:
# /path/to/local/dep表示依赖库的本地缓存路径,也就是之前已下载好的依赖库目录
cmake -Bbuild -DCPM_SOURCE_CACHE=/path/to/local/dep
当然,也可直接在配置文件.bashrc
或.bash_profile
中,添加上述变量:.
export CPM_SOURCE_CACHE=${HOME}/.cache/CPM
-
CPM_DOWNLOAD_ALL
若设置该变量,CPM会下载所有依赖库。该变量同样可以通过cmake命令传递或通过配置文件配置,如上所示。 -
CPM_USE_LOCAL_PACKAGES
-
CPM_LOCAL_PACKAGES_ONLY
如果本机已安装有依赖库,无需从网上下载,可使用CPM_USE_LOCAL_PACKAGES,这样CPM就会使用CMake的find_package
函数在本机查找依赖库,如找不到再从网上下载。如果CPM_LOCAL_PACKAGES_ONLY被设置,则CPM只会在本机寻找依赖库,如果本机没有安装依赖库,将报本地依赖库不存在的错误。
五、完整示例
以下是一个使用CPM管理依赖库的一个完整示例。
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
# 创建工程,名称为:MyProject
project(MyProject)
# 基于test.cpp生成可执行文件tests
add_executable(tests tests.cpp)
# 添加对其他库的依赖
# 首先包含cmake/CPM.cmake文件,以便CPM能够生效
include(cmake/CPM.cmake)
# 使用简洁语法从GitHub网站添加一个依赖库Catch2,版本号为2.5.0
CPMAddPackage("gh:catchorg/Catch2@2.5.0")
# 使用完整语法直接基于URL添加另一个依赖库nlohmann_json,版本号为3.7.3
# 当然也可使用简洁语法从GitHub网站添加,如下所示。这是只是为了描述不同的用法故意这么做而已
# CPMAddPackage("gh:nlohmann/json@3.7.3")
CPMAddPackage(
NAME nlohmann_json
VERSION 3.7.3
# the git repo is incredibly large, so we download the archived include directory
URL https://github.com/nlohmann/json/releases/download/v3.7.3/include.zip
URL_HASH SHA256=87b5884741427220d3a33df1363ae0e8b898099fbc59f1c451113f6732891014
)
# 生成可执行文件tests时,需要链接依赖库Catch2、nlohmann_json
target_link_libraries(tests Catch2 nlohmann_json)