C++工程:总结 CMake 添加第三方库依赖方式git submodule、 find_library、FetchContent、CPM等

CMake 已经成为了C++工程管理的主流方式,功能非常强大,现在大多数的 C++ 库都已经支持CMake,下面以 jsoncpp 为例,介绍几种引入第三方库的方式。

1. 代码依赖

这种方式是把第三方库的完整代码直接添加到我们的项目中,当做项目代码的一部分进行编译,这种方式会把第三方代码和我们的代码混在一起,并不推荐使用。首先我们需要到 jsoncpp 下载需要的头文件和实现代码,放到项目当中。

工程文件目录
├── CMakeLists.txt
├── jsoncpp
│   ├── include
│   │   └── json
│   │       ├── autolink.h
│   │       ├── config.h
│   │       ├── features.h
│   │       ├── forwards.h
│   │       ├── json.h
│   │       ├── reader.h
│   │       ├── value.h
│   │       └── writer.h
│   ├── json_batchallocator.h
│   ├── json_internalarray.inl
│   ├── json_internalmap.inl
│   ├── json_reader.cpp
│   ├── json_value.cpp
│   ├── json_valueiterator.inl
│   └── json_writer.cpp
└── main.cpp
CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
project(includes_full_code)
set(CMAKE_CXX_STANDARD 14)
# 包含头文件
include_directories(./jsoncpp/include)
set(jsoncpp jsoncpp/json_reader.cpp jsoncpp/json_writer.cpp jsoncpp/json_value.cpp)
# 添加可执行代码
add_executable(includes_full_code main.cpp ${jsoncpp})
main.cpp

后面的示例的main.cpp都是一样

#include <iostream>
#include "json/json.h"
int main() {
    Json::Value json;
    json["name"] = "Wiki";
    json["age"] = 18;
    std::cout << json.toStyledString() << std::endl;
    return 0;
}

完整代码:includes_full_code_exmaple

2. 内部工程依赖

这种方式和上面 代码依赖 的方式类似,不同的是内部工程依赖会把第三方库的管理职责交给第三方库工程CMakeLists.txt文件,这种方式的好处是职责分明,是最常用的依赖方式。

工程文件目录

目录结果和上面的案例相似,不同的是jsoncpp文件夹多了一个 CMakeLists.txt 文件

├── CMakeLists.txt
├── jsoncpp
│   ├── CMakeLists.txt
│   ├── include
│   │   └── json
│   │       ├── autolink.h
│   │       ├── config.h
│   │       ├── features.h
│   │       ├── forwards.h
│   │       ├── json.h
│   │       ├── reader.h
│   │       ├── value.h
│   │       └── writer.h
│   ├── json_batchallocator.h
│   ├── json_internalarray.inl
│   ├── json_internalmap.inl
│   ├── json_reader.cpp
│   ├── json_value.cpp
│   ├── json_valueiterator.inl
│   └── json_writer.cpp
└── main.cpp
jsoncpp/CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
project(jsoncpp)
add_library(${PROJECT_NAME} json_reader.cpp json_value.cpp json_writer.cpp)
target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include)
CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
project(multi_cmakelists)
# 添加子工程
add_subdirectory(jsoncpp)
add_executable(${PROJECT_NAME} main.cpp)
# 链接子工程
target_link_libraries(${PROJECT_NAME} jsoncpp)

完整代码:multi_cmakelists_example

这种方式除了引入第三方依赖,通常我们也会用这种方式来管理项目中的各个子模块,每个模块都有独立的CMakeLists.txt文件,从而实现子工程的单独引用,源码请看 subdirectory_example

3. find_library:编译库方式引入

这种方式是用来依赖已经打包好的二进制文件,这种方式也分为静态库(.a、.lib)和动态库(.so、.dll)方式引入,这种方式也可以查找本机已经安装好的库,比如 Android 的 log 库就是通过这种方式引入。

生成.a文件

运行上面的 内部工程依赖 案例后,我们我们可以从项目中找到编译好的 multi_cmakelists/cmake-build-debug/jsoncpp/libjsoncpp.a 文件。

工程文件目录

和上面不同的是,这里只需要导入jsoncpp的头文件和.a文件。

├── CMakeLists.txt
├── jsoncpp
│   ├── include
│   │   └── json
│   │       ├── autolink.h
│   │       ├── config.h
│   │       ├── features.h
│   │       ├── forwards.h
│   │       ├── json.h
│   │       ├── reader.h
│   │       ├── value.h
│   │       └── writer.h
│   └── libjsoncpp.a
└── main.cpp
CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
project(find_library_example)
include_directories(jsoncpp/include)
add_executable(${PROJECT_NAME} main.cpp)
find_library(jsoncpp_lib NAMES jsoncpp PATHS ./jsoncpp)
target_link_libraries(${PROJECT_NAME} ${jsoncpp_lib})

完整代码:find_library_example

这种方式在 Android 开发很常见,比如我们引入xlog实现日志打印就可以通过这种方式实现,代码参考 xlog_example

4. FetchContent

FetchContent 是 cmake 3.11.0 版本开始提供的功能,可以非常方便用来添加第三方依赖。

工程文件目录
├── CMakeLists.txt
└── main.cpp
CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
project(fetch_content_example)
include(FetchContent)
#FetchContent_Declare(jsoncpp
#        GIT_REPOSITORY https://github.com/open-source-parsers/jsoncpp.git
#        GIT_TAG 1.9.4)
# 建议使用压缩包的方式依赖,下载速度更快
FetchContent_Declare(jsoncpp
        URL https://github.com/open-source-parsers/jsoncpp/archive/1.9.4.tar.gz)
FetchContent_MakeAvailable(jsoncpp)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} jsoncpp_lib)

建议通过压缩包的方式引入,因为直接引入git仓库可能会很慢。

完整代码:fetch_content_example

Android SDK 的 CMake 的默认版本是3.10.2,并不支持FetchContent,如果想在Android开发中使用需要安装3.11.0以上版本的cmake,为了降低团队的协同成本,并不建议在 Android 工程使用,建议使用内部工程的方式引入。

5. CPM

CPM.cmake 是在 FetchContent 的基础上封装而来,相比 FetchContent 更加简单易用,使用CPM需要到 CPM.cmake 下载cmake目录的文件CPM.cmake、get_cpm.cmake和testing.cmake,添加到项目当中。

工程文件目录
├── CMakeLists.txt
├── cmake
│   ├── CPM.cmake
│   ├── get_cpm.cmake
│   └── testing.cmake
└── main.cpp

CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
project(cpm_example)
include(cmake/CPM.cmake)
#CPMAddPackage(
#        GIT_REPOSITORY https://github.com/open-source-parsers/jsoncpp.git
#        GIT_TAG 1.9.4)
# 建议使用压缩包的方式依赖,下载速度更快
CPMAddPackage(
        NAME jsoncpp
        URL https://github.com/open-source-parsers/jsoncpp/archive/1.9.4.tar.gz)

add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} jsoncpp_lib)

这种方式的细节不需要我们自己处理,都交给了CPM解决,这种方式也同样不建议在 Android 工程使用。

完整代码:cpm_example

6. find_package

find_package 是 cmake 3.19.0 版本开始提供的功能,可以非常方便添加,这种方式主要是从本机上查找已经安装好的库,需要提前通过命令安装。

安装jsoncpp

我的Mac OS,通过下面方法安装可以成功,其它系统可能会出错

# 拉取代码
git clone https://github.com/open-source-parsers/jsoncpp
cd jsoncpp
mkdir -p build/debug
cd build/debug
# 生成Makefile
cmake -DCMAKE_BUILD_TYPE=release -DBUILD_STATIC_LIBS=OFF -DBUILD_SHARED_LIBS=ON -DARCHIVE_INSTALL_DIR=. -DCMAKE_INSTALL_INCLUDEDIR=include -G "Unix Makefiles" ../..
# 安装
make && make install

如果提示没有安装cmake,需要自行安装cmake

工程文件目录
├── CMakeLists.txt
└── main.cpp
CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
project(find_package_example)
find_package(jsoncpp REQUIRED)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} jsoncpp_lib)
完整代码:find_package_example

使用这种方式是需要有个大前提,电脑必须已经安装好了对应的库,否则无法正常工作,这种方式只有在特定的场景下使用,比如调用电脑的opencv、openssl。

7. git submodule

这种方式是利用git的submodule实现,推荐Android使用,通过git添加另外一个仓库的依赖,可更新另外一个仓库的依赖,但是代码不会包含进来。

# 在A仓库添加B仓库依赖,操作完后需要提交上去
git submodule add https://github.com/taoweiji/B.git

A仓库拉取及submodule仓库的更新

git clone https://github.com/taoweiji/A.git
git submodule init && git submodule update

8. Android 动态依赖

C++工程:以 xlog 为例介绍 Android NDK 如何依赖第三方C++动态库

总结

C++添加依赖的方式有很多种,没有绝对的好与差,应该根据不同的场景使用不同的依赖方式,例如在Android工程中,我们应该尽量不要改变默认的CMake版本,避免增加环境的依赖。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容