CMake

CMake

一、cmake介绍

在早期和当今的Linux工程管理中,make工程管理器占据了半壁江山,但make的配置文件`Makefile的语法晦涩难懂,接触过的人都清楚,Makefile写起来非常困难。解决这个问题的办法无法有两个:

  • 使用 automake 工具生成configure脚本,让其自动生成Makefile。
  • 使用 cmake 工具直接生成Makefile。

cmake是什么

cmake 是一款跨平台的免费开源软件工具,用于使用与编译器无关的方法来管理软件的构建过程。比如,在 Android Studio 上进行 NDK 开发默认就是使用 cmake 管理 C/C++ 代码,在 Linux 环境下有大量项目都使用 cmake 来管理,因此最好对 cmake 有一定的了解。

图标

cmake最重要的作用就是协助我们自动生成项目所需要的Makefile,以便于工程管理器make可以指导编译器的工作,他们的关系大概如下:

关系

1.开发者编写CMakeLists.txt,指导cmake自动生成Makefile

2.自动生成的Makefile,指导make和gcc,将源代码编译成可执行程序

这样一来,我们就摆脱了晦涩难写的Makeifle,转而编写简单可爱的 CMakeLists.txt ,利用cmake即可管理整个项目。

源码隔离

一般而言,为了项目工程观感更干净、利落,我们不希望源码跟编译工具文档像上面的例子那样混在一起,因此对于cmake而言,通常的操作是:在源码中创建一个专门用于存储编译输出文件的存储区域,不妨命名为 build/ ,使自动生成的文件与开发者编写的源码隔离:

# 创建一个构建目录,专门用来存放构建过程中产生的非源码文件
gec@ubuntu:~/cmake$ mkdir build/

# 进入该构建目录
gec@ubuntu:~/cmake$ cd build
gec@ubuntu:~/cmake/build$

# 在构建目录中执行 `cmake`
gec@ubuntu:~/cmake/build$ cmake ..

二、安装cmake

在 ubuntu 中安装 cmake

ubuntu安装 cmake 只需一条指令:

sudo apt install cmake

可以通过以下指令来查看当前已安装的cmake的版本,若需要升级版本,可直接使用 apt 指令来升级:

# 查看cmake版本
gec@ubuntu:~$ cmake --version

# 升级cmake
gec@ubuntu:~$ sudo apt upgrade cmake

三、简单示例

入门操作
假设有一C语言源程序main.c,其内容如下:

#include <stdio.h>

int main(int argc, char const *argv[])
{
    printf("一起学习cmake\n");
    return 0;
}

现使用 cmake 来管理该项目的构建工作,在 main.c 同一目录下,编写一个 CMakeLists.txt:

gec@ubuntu:~/cmake$ tree
.
├── CMakeLists.txt
└── main.c
gec@ubuntu:~/cmake$ 

注意:

  • 配置文件的名称 CMakeLists.txt 是固定的,不可改成别的名字
  • 配置文件的名称 CMakeList.txt 不区分大小写,但一般的写法如此所示。
  • add_executable 用来指明最终要生成一个可执行文件的名称,及其所依赖的源文件列表。
# CMakeLists.txt
add_executable(main main.c)

在源码目录中,直接执行命令 “cmake .”(其中 . 表示 CMakeLists.txt 所在的位置在当前目录) 来读取 CMakeLists.txt 并生成 Makefile:

示例
此时会发现,目录中自动生成了Makefile了:
示例

有了Makefile,就可以直接make编译项目程序了:

示例

此时,程序main已经编译完毕。

文件 CMakeLists.txt cmake 的配置文件,用来告诉 cmake 如何生成最终的 Makefile。此处的 CMakeLists.txt 非常简单,仅包含一句话:

多文件编译

语法:
  add_executable(可执行文件 源文件1 源文件2 ... ...)

假设某可执行文件 main 由多个源码文件编译而成:
add_executable(main main.c a.c b.c)

指定头文件路径

语法:
  include_directories(头文件所在路径)

假设文件结构如下:
.
├── build/
├── inc/
│   └── head.h
└── main.c
├── CMakeLists.txt

其中,主程序 main.c 依赖于头文件 head.h,那么在与之同目录的 CMakeLists.txt 中,需要以其所在路径为基准添加头文件相对路径或绝对路径:

# CMakeLists.txt
include_directories(./inc)  # 相对路径,基于CMakeLists.txt所在路径
add_executable(main main.c)
或者
# CMakeLists.txt
include_directories(/home/gec/cmake/inc)  # 绝对路径
add_executable(main main.c)

设定变量和调用shell命令

语法:
  set(变量名称 变量值)

在指定头文件路径的例子中,当使用绝对路径时,若采用上述方式则在不同电脑间执行会很容易出现错误,更好的方式是让 CMakeLists.txt 在被解析的时候自动获取其所在路径,这可以在 CMakeLists.txt中嵌套 shell 命令来达到:

# 定义一个变量 SRCDIR,使其值等于当前路径
set(SRCDIR $(pwd))

# 引用变量 SRCDIR 的值来设定头文件所在路径 
include_directories($(SRCDIR)/inc)

指明库路径和链接指定的库文件

语法:
  link_directories(库所在路径)
  target_link_libraries(目标文件 库名1 库名2 ... ...)

假设主程序 main.c 引用了 lib/ 下各个库文件的接口,则编译时就必须链接相关的库文件。在顶层 CMakeLists.txt 中添加两行 target_link_libraries() 语句:

# CMakeLists.txt
set(SRCDIR $(pwd))
include_directories($(SRCDIR)/inc/)

add_executable(main main.c)
target_link_libraries(main a)  # 指明可执行文件main依赖于库a
target_link_libraries(main b)  # 指明可执行文件main依赖于库b

add_subdirectory(lib/)
posted @ 2025-11-15 13:25  林明杰  阅读(3)  评论(0)    收藏  举报