12k star 项目 cmake-examples 阅读和点评

12k star 项目 cmake-examples 阅读和点评

Author: ChrisZZ
Time: 2024.06.17

项目概要

CMake 是 C/C++ 项目的主流工具, 应该有不少 CMake 用户尝试过寻找参考例子。 在 github 上搜索关键字 "cmake examples",搜索结果中排名第一个的是 https://github.com/ttroy50/cmake-examples 这个项目, 目前12.1k star。

今天来简单阅读和点评下这个项目. 整体而言,它给出的例子都比较简单, 覆盖范围也还算比较广泛,考虑到大部分 commit 是9年前和5年前提交,对于初学者而言很多例子很容易看懂, 并且每个子目录搭配了 README 文档解释说明, 整体质量还是可以的。
不过, 类似于 CMake 官方的 CMake Tutorials(至少截止3.30-rc3的版本), cmake-examples 这个仓库也有头重脚轻、详略不当的问题, 在自定义包、 执行安装后导入等方面, 例子太少, Windows 上的 DLL 等情况没覆盖到, RPATH 也没有提及, 只能说尚未覆盖中级 CMake 使用场景, 距离实际 C/C++ 生产, 仍然有不小距离。

下面来具体看下各个目录情况。

01-basic

A-hello-cmake
B-hello-headers
C-static-library
D-shared-library
E-installing
F-build-type
G-compile-flags
H-third-party-library
I-compiling-with-clang
J-building-with-ninja
K-imported-targets
L-cpp-standard
README.adoc

本节有12个子目录,一个顶级目录的文档 README.adoc。 adoc 采用了类似 Markdown 的语法,不过稍显复杂,建议改用 markdown 比较好。

A-hello-cmake

最简单的 CMake 工程, 创建了可执行程序。 作者指定了 cmake_minimum_required(VERSION 3.5), 这是很棒的一点: 在最近的 CMake 版本中, 如果指定的最小版本小于3.5,官方会提示 warning, 即将不再支持。 因此指定 3.5 为最小 cmake 版本, 至少目前来说仍然能保持很好的兼容性。 不过如果想用一些 CMake 高版本的特性, 那还是指定更高版本, 例如3.15是一个重要的里程碑, 也可以考虑指定3.25.

B-hello-headers

以 modern cmake 的方式, 指定头文件搜索目录。

C-static-library

创建静态库, 并链接到可执行程序上。 CMakeLists.txt 里使用了4空格缩进, 差评。 好的 CMakeLists.txt 应当使用2空格缩进。

D-shared-library

一个不完整的例子, 也可以认为有点糟糕的例子。 没有指定 dllimport/dllexport 这些函数符号修饰。

E-installing

安装程序的例子。 在 linux 下可以用, 但是对于 Windows 用不了, 因为被安装的是动态库, 但是 Windows 上的 DLL 本质上和 可执行程序差不多, 算是 runtime, 而不是 library。

既然用 CMake 是为了跨平台, 那应该覆盖 Windows 和 Linux 两大常见系统, macOS 上基本和 Linux 差不多。 那没有覆盖 Windows 还算是跨平台吗? 显然这时候和 Makefile 半斤八两。

F-build-type

在样例代码中提到了 CMAKE_BUILD_TYPE 和 CMAKE_CONFIGURATIONS_TYPES, 但是文档和代码中没有对 CMAKE_CONFIGURATION_TYPES 做设定和说明, 差评。

应当提及这两个变量的区别:分别用于 single-config generator 和 mulit-config generator, 然后引申出 Visual Studio, XCode, Ninja Multi-Config. 在 Linux 上, 也是可以 Ninja Multi-Config 的,为啥不写例子呢?

G-compile-flags

糟糕的例子。应当用 cmake_compile_definitions() 替代。

H-third-party-library

引入了三方库 boost。 是一个糟糕的例子, 这个糟糕体是 CMake 自身负主要责任, 因为 cmake 只是一个胶水, 给用户提供的 find_package() 看似可以指定版本号, 但是这 boost 是 apt-get 安装的, 那么系统里通常只有一个版本, 如果有多个版本, 应该由专门的包管理器, 可以下载比如20个连续的版本, 而不是只能听从 apt-get 官方的安排, 只有零星几个版本可以选择,还未必能选择到最新的版本。

I-compiling-with-clang

例子写复杂了。 其实用 CC 和 CXX 环境变量就行了。

J-building-with-ninja

还行, 终于用上 ninja 了。但是写的太潦草, 应该拿稍微有点重量的项目, 比如 ncnn 或者 opencv, 分别用 make 和 ninja 作为 generator, 比对编译耗时。 不然为什么用户要用 ninja 呢?

K-imported-targets

导入库。糟糕的例子。 其实如果要展示 imported 库, 应该手动设置库和头文件的目录, 也就是自行创建 imported target。 不然这和用 find_package() 没区别。

L-cpp-standard

给了三种写法。注意, -std=c++11 硬编码写法显然是该删除而不应该被展示的。 如果展示, 应该展示失败的案例,例如 -std=c++17 不被 MSVC 编译器识别。

小节

01-basic 的例子其实挺多的, 新人第一次学习 CMake 看这些也还算凑合, 学一下应该能干一些简单的活儿了, 但是细节还是不太行。

02-sub-projects

A-basic
README.adoc

这一节只有一个子目录, 不免让人觉得 “萎了”。 凭啥第一节放10+个例子, 第二节就一个??

这一节只包含了最简单的 add_subdirectory 的使用例子。 仍然需要补充的例子:

  • add_subdirectory 包含的目录, 是外部目录
  • 用 add_subdiretory 的方式, 包含 fmt 源码
  • add_subdiretory 添加的目录, 是包含了 prebuilt(预编译的库和头文件), 然后手写 CMakeLists.txt 文件提供 target 供外部 CMakeLists.txt 使用

03-code-generation

README.adoc
configure-files
protobuf

这一节的两个子目录, 都还是有一定实用价值的。

不过说回来, 找 protobuf 库和编译器, 应该交给一个成熟的包管理器, 不应该依赖系统 apt 安装的。 毕竟在 Windows 上没有 apt, 但是 CMake 自称跨平台, 就不应该依赖 apt 这个特定的系统包管理器。

04-static-analysis

README.adoc
clang-analyzer
clang-format
cppcheck
cppcheck-compile-commands

这一节的内容, 包含了4个子目录, 也还算丰富。是构建完 target 后, 执行一些静态分析, 其实本质是把静态分析工具黏合起来。 在 CI/CD 中可以集成, 本地构建其实还是略显麻烦, 不如让 IDE 自动提示。

举一个更糟糕的例子吧, sonar 扫描。 如果开了 Sonar 扫描, 比如在 VSCode 里安装了插件, 各种 warning 都报告给你, 正经活儿都干不了了。静态扫描的东西,适合一段时间后开启, 不适合日常开发时每次编译都触发。

05-unit-testing

README.adoc
boost
catch2-vendored
google-test-download

包含了三个测试框架的使用例子, 对于5年前的项目来说, 还算丰富。 不过现在(2024年)来看, 建议增加几个别的:

  • catch2, 是 header-only 的
  • doctest

06-installer

README.adoc
deb

显然, 作者只考虑了 Debian/Ubuntu 这样的 Linux 发行版, 没考虑 Windows 用户。 既然使用 CMake, 要做跨平台构建, 还是推荐用 .zip 作为包名。 一定要给 .deb 的例子, 那最好把 .msi, .dmg, .rpm 也放上来。

我对制作 .deb 包没兴趣, 相信大部分 CMake 初学者对于制作 .deb 包也没兴趣。

07-package-management

A-using-system-provide-packages
B-vendoring-code
C-external-project-add
D-conan
README.adoc

这一节提供了4个例子, 对于2024年的cmake入门教程来说显然是不够的, 至少还应该添加:

  • vcpkg
  • CPM

anyway, 聊胜于无。 conan 比较好的一点是, 支持锁定版本或指定版本号范围。

使用系统库,使用 vendor 库, 这都是 CMake 官方没有提供包管理器、只想着胶水黏合的设计理念下的做法, 很容易在各个平台上表现不一样, 并不能稳定的提供跨平台的体验。

external-project-add 使用了网络下载的方式, 通常网络状况不好, 因此实用价值方面有折扣; 如果是改为内网或国内专门的镜像站点, 提供源码, 那应该还算可用。

dokerfiles

作者提供了自己环境的 docker, 包括 ubuntu14.04, 16.04, 还是比较用心的, 值的表扬。

总结

本文对 cmake-examples 仓库中的每个子目录都做了阅读和点评, 从内容分布来看, 你也可以看到“头重脚轻”的问题了。基础内容比较多, 这是 cmake 设计理念导致的; 中级、进阶内容少, 这只能说明 cmake-examples 仓库中还有待改进, 也许作者也没有很丰富的 cmake 使用体验, 那么对于稍微 senior 的 cmake 学习, 就还是另外找其他资料吧!

posted @ 2024-06-17 21:42  ChrisZZ  阅读(31)  评论(0编辑  收藏  举报