从目标出发理解CMake
多数时候我们在学习一门新的编程语言时,总是从它提供的各类操作学起,而这往往总是不得要领,比如我入门过多次CMake,但过后总会忘记其提供的各种操作,不得要领。有一天,我yy到:计算机、编程语言、或者数据库、甚至各种软件本质上是完成某种任务的“机器”,其提供给用户操作列表完成某种任务。这个结论提供给了我学习一种新软件的思路:思考它作为一种状态机希望去完成哪类任务,进而倒退其提供的各种操作。
0. 简单编译任务
CMake提供用户各类操作以完成C++编译任务,C++编译任务究竟是什么?
从一个项目的角度出发,我们最终产物(target)有两种:可执行文件,库。可执行文件与库之间,库与库之间存在依赖关系,而其本身由于若干个目标文件组成,每个目标文件由一份源代码(.cc)编译。
因此,CMake提供给用户三种最基础指令用于描述一个程序构成,依赖关系。
- add_library:编译库
- add_executable:编译可执行文件
- target_link_libraries:设置库和可执行文件之间的依赖关系
以如下目录结构目录为例子:
// ls 1
CMakeLists.txt foo.cc foo.h main.cc
// CMakeLists.txt
cmake_minimum_required(VERSION 3.15...4.0)
project(MyProject VERSION 1.0
DESCRIPTION "Very nice project"
LANGUAGES CXX)
add_library(foo SHARED foo.cc)
add_executable(main main.cc)
target_link_libraries(main PUBLIC foo)
0.1 头文件依赖
在C++中,目标文件除了与其他文件相互依赖,还依赖于头文件(.h),编译器会包含若干条Include目录,在编译目标文件时会在这些Include目录中查找源代码中的头文件。
因此,CMake需要提供一种操作:
- target_include_directories,用于为一个目标添加搜索的Include目录。
此外,这个操作还提供了一种“继承”的特性,当某个目标(A)以PUBLIC的方式添加某个目录时,若其他目标(B)与该目标(A)链接,其会继承其添加的目录,即B可以与A一样的方式使用头文件。
1. 一个简单的项目
一个简单的项目编译任务往往不只是一个程序,其会包含第三方库子项目,核心程序子项目,测试子项目,每个项目生产不同的target起不同作用(核心程序,测试程序,公用库),target之间会有依赖关系。
因此,CMake提供给用户项目级的管理操作:
add_subdirectory:用于在root project加入一个sub project,加入后相当于将target放置于root project的“空间”中,处于root project下的target能够通过target_link_libraries相互构建依赖关系。
💡 这里很自然可以想到可见性的问题,CMake应该会提供一些操作target之间的可见性。
同时,在项目中往往会有依赖系统第三方库的需求,因此CMake提供了:
find_package: 在系统中查看第三方库的目标,查找到后将目标加入root_project被后续目标进行链接
💡 关于find_package的原理,from gpt:
第三方库的存储方式:头文件:/usr/*/include
库文件:/usr/lib/
配置文件:/usr/lib/cmake/xxConfig.cmake
cmake默认会从以下路径查找对应库的XXXConfig.cmake或者FindXXX.cmake,其中记录了各个target到具体库文件的对应关系
CMAKE_PREFIX_PATH
CMAKE_INSTALL_PREFIX
CMAKE_MODULE_PATH(仅用于 Module 模式)
系统默认路径:/usr/lib/cmake、/usr/local/lib/cmake 等
N. mysql
在理解到CMake提供的基本功能后,快进到一个真正的大型C++项目mysql理解CMake还需要提供什么功能。mysql源代码包含多个子项目:
- client:mysql各类工具,如mysqladmin, mysqldump
- components: mysql server的多种插件实现
- extra/*: mysql依赖的第三方库
- libchangestreams:mysql cdc libsql::mysql server
- libs/mysql: mysql各类工具之间公用库
- sql-common: mysql server与client之间公用代码
…
todo!