https://subingwen.cn/cmake/CMake-primer/
1. CMake 概述
CMake是一个项目构建工具,并且是跨平台的。Cmake跟Makefile其实是差不多的,只不过makefile更底层些。大多是 IDE 软件都集成了 make,比如:VS 的 nmake、linux 下的 GNU make、Qt 的 qmake 等,如果自己动手写 makefile,会发现,makefile 通常依赖于当前的编译平台,而且编写 makefile 的工作量比较大,解决依赖关系时也容易出错。
而 CMake 恰好能解决上述问题, 其允许开发者指定整个工程的编译流程,在根据编译平台,自动生成本地化的Makefile和工程文件
,最后用户只需 make 编译即可,所以可以把 CMake 看成一款自动生成 Makefile 的工具
,其编译流程如下图:
蓝色虚线
表示使用makefile
构建项目的过程红色实线
表示使用cmake
构建项目的过程
首先需要创建一个脚本文件
CMakeLists.txt
文件,在该文件中指定一些指令,然后执行cmake
命令,执行cmake
后会生成一个Makefile
文件,此时makefile文件中会存在一些编译指令,最后再执行make
命令就可以执行makefile
中的一系列指令生成可执行文件
(编译过程:
先调用预处理器,再调用汇编器,再调用连接器,最后生成可执行文件)。
cmake不仅仅可以以生成可执行文件,还可以生成库文件,包括:动态库和静态库。生成了这些库文件就可以引入到另外的第三方项目中,为什么给第三方项目使用的是动态库或者静态库,而不是使用源代码呢?原因主要有两点: 1.为了对源代码的保密 2. 比如一个库是由100个源文件编译生成,如果直接100个源文件引入到第三方项目中是非常麻烦和臃肿的,并且不容易维护。为了便利和可维护,将这100个源文件编译为库文件,供第三方项目调用。
介绍完 CMake 的作用之后,再来总结一下它的优点
:
跨平台
- 能够管理大型项目
- 简化编译构建过程和编译过程
- 可扩展:可以为 cmake 编写特定功能的模块,扩充 cmake 功能
2. CMake 的使用
CMake 支持大写、小写、混合大小写
的命令。如果在编写 CMakeLists.txt
文件时使用的工具有对应的命令提示,那么大小写随缘即可,不要太过在意。
2.1 注释
2.1.1 注释行
CMake 使用 #
进行行注释,可以放在任何位置。
# 这是一个 CMakeLists.txt 文件
cmake_minimum_required(VERSION 3.0.0)
2.1.2 注释块
CMake 使用#[[ ]]
形式进行块注释。
#[[ 这是一个 CMakeLists.txt 文件。
这是一个 CMakeLists.txt 文件
这是一个 CMakeLists.txt 文件]]
cmake_minimum_required(VERSION 3.0.0)
2.2 只有源文件
2.2.1 共处一室
(1) 准备工作,为了方便测试,在我本地电脑准备了这么几个测试文件
add.c
#include <stdio.h>
#include "head.h"
int add(int a, int b)
{
return a+b;
}
sub.c
#include <stdio.h>
#include "head.h"
// 你好
int subtract(int a, int b)
{
return a-b;
}
mult.c
#include <stdio.h>
#include "head.h"
int multiply(int a, int b)
{
return a*b;
}
div.c
#include <stdio.h>
#include "head.h"
double divide(int a, int b)
{
return (double)a/b;
}
head.h
#ifndef _HEAD_H
#define _HEAD_H
// 加法
int add(int a, int b);
// 减法
int subtract(int a, int b);
// 乘法
int multiply(int a, int b);
// 除法
double divide(int a, int b);
#endif
main.c
#include <stdio.h>
#include "head.h"
int main()
{
int a = 20;
int b = 12;
printf("a = %d, b = %d\n", a, b);
printf("a + b = %d\n", add(a, b));
printf("a - b = %d\n", subtract(a, b));
printf("a * b = %d\n", multiply(a, b));
printf("a / b = %f\n", divide(a, b));
return 0;
}
(2) 上述文件的目录结构如下:
$ tree
.
├── add.c
├── div.c
├── head.h
├── main.c
├── mult.c
└── sub.c
(3)添加 CMakeLists.txt 文件
在上述源文件所在目录下添加一个新文件 CMakeLists.txt
,文件内容如下:
cmake_minimum_required(VERSION 3.0)
project(CALC)
add_executable(app add.c div.c main.c mult.c sub.c)
接下来依次介绍一下在 CMakeLists.txt 文件中添加的三个命令:
-
cmake_minimum_required
:指定使用的 cmake 的最低
版本- 可选,非必须,如果不加可能会有警告 -
-
project
:定义工程名称
,并可指定工程的版本、工程描述、web 主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
# PROJECT 指令的语法是:
project(<PROJECT-NAME> [<language-name>...])
project(<PROJECT-NAME>
[VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
[DESCRIPTION <project-description-string>]
[HOMEPAGE_URL <url-string>]
[LANGUAGES <language-name>...])
add_executable:
定义工程会生成一个可执行程序
add_executable(可执行程序名 源文件名称)
这里的可执行程序名和 project 中的项目名没有任何关系
源文件名可以是一个也可以是多个,如有多个可用空格或 ; 间隔
# 样式1
add_executable(app add.c div.c main.c mult.c sub.c)
# 样式2
add_executable(app add.c;div.c;main.c;mult.c;sub.c)
(4) 执行 CMake 命令
万事俱备只欠东风,将 CMakeLists.txt 文件编辑好之后,就可以执行cmake
命令了。
# cmake 命令原型
$ cmake CMakeLists.txt文件所在路径
$ tree
.
├── add.c
├── CMakeLists.txt
├── div.c
├── head.h
├── main.c
├── mult.c
└── sub.c
0 directories, 7 files
robin@OS:~/Linux/3Day/calc$ cmake .
当执行 cmake
命令之后,CMakeLists.txt 中的命令就会被执行,所以一定要注意给 cmake 命令指定路径的时候一定不能出错。
执行命令之后,看一下源文件所在目录中是否多了一些文件:
$ tree -L 1
.
├── add.c
├── CMakeCache.txt # new add file
├── CMakeFiles # new add dir
├── cmake_install.cmake # new add file
├── CMakeLists.txt
├── div.c
├── head.h
├── main.c
├── Makefile # new add file
├── mult.c
└── sub.c
我们可以看到在对应的目录下生成了一个 makefile
文件,此时再执行 make
命令,就可以对项目进行构建得到所需的可执行程序了。
$ make
Scanning dependencies of target app
[ 16%] Building C object CMakeFiles/app.dir/add.c.o
[ 33%] Building C object CMakeFiles/app.dir/div.c.o
[ 50%] Building C object CMakeFiles/app.dir/main.c.o
[ 66%] Building C object CMakeFiles/app.dir/mult.c.o
[ 83%] Building C object CMakeFiles/app.dir/sub.c.o
[100%] Linking C executable app
[100%] Built target app
# 查看可执行程序是否已经生成
$ tree -L 1
.
├── add.c
├── app # 生成的可执行程序
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
├── CMakeLists.txt
├── div.c
├── head.h
├── main.c
├── Makefile
├── mult.c
└── sub.c
最终可执行程序 app 就被编译出来了(这个名字是在 CMakeLists.txt 中指定的)。
2.2.2 VIP 包房
通过上面的例子可以看出,如果在 CMakeLists.txt 文件所在目录执行了 cmake 命令之后就会生成一些目录和文件(包括 makefile 文件),如果再基于 makefile文件执行 make
命令,程序在编译过程中还会生成一些中间文件和一个可执行文件,这样会导致整个项目目录看起来很混乱
,不太容易管理和维护,此时我们就可以把生成的这些与项目源码无关的文件统一放到一个对应的目录里边,比如将这个目录命名为 build
:
$ mkdir build
$ cd build
$ cmake ..
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/robin/Linux/build
现在 cmake 命令是在 build
目录中执行的,但是 CMakeLists.txt
文件是 build 目录的上一级
目录中,所以 cmake 命令后指定的路径为..
,即当前目录的上一级目录。
当命令执行完毕之后,在 build 目录中会生成一个makefile
文件
$ tree build -L 1
build
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
└── Makefile
1 directory, 3 files
这样就可以在 build 目录中执行 make 命令编译项目,生成的相关文件自然也就被存储到 build 目录中了。这样通过 cmake 和 make 生成的所有文件就全部和项目源文件隔离开了
,各回各家,各找各妈。
参考
作者: 苏丙榅
链接: https://subingwen.cn/cmake/CMake-primer/
来源: 爱编程的大丙