当前位置: 首页 > news >正文

Make 与 CMake:从手动编译到自动构建

视频学习资源

构建

在软件开发中,构建是指将源码文件和资源文件转换成可以运行的应用程序的过程

image-20260531204647660

构建通常会包括编译、链接、打包和部署等步骤。当只有一个源文件的时候我们可以直接使用一些编译器的指令,来手动完成构建的过程

比如可以使用gcc来编译一个C语言的源文件

$ gcc hello.c -o hello

或者使用javac来编译一个Java的源文件

$ javac hello.java

但项目庞大且复杂时,源文件、头文件和库文件有成百上千个时,手动编译就不现实了。这个时候就可以借助一些自动化的构建工具,来完成整个构建过程。

image-20260531204524918

不同平台会使用不同的构建工具,比如Make、CMake和Maven等等

下面介绍C语言项目中最常用的构建工具Make和CMake的基本用法

我们从一个最简单的场景说起:当你只有两个源文件 main.chello.c 时,一条 gcc main.c hello.c -o hello 就足够了。但当源文件变成几十上百个,并且分散在不同目录,还要链接不同的库,直接用 gcc 命令就会变得极其痛苦。

于是,自动化构建工具出现了。

1. Make 是什么?

Make 是一个经典的构建工具,它通过读取一个叫做 Makefile 的文件来确定:

  • 哪些文件需要先编译
  • 哪些文件需要重新编译
  • 如何执行编译链接命令

核心思想就是依赖管理增量构建:只有当源文件比目标文件“更新”时,才重新构建,避免每次都全量编译。

1.1 Makefile 的基本语法

Makefile 由一系列 规则(rule) 组成:

目标(target): 依赖(prerequisites)命令(recipe)

总的就是说两件事:

  • 第一行:文件的依赖是什么
  • 第二行:如何生成这个文件

注意:命令前必须是 Tab 键,不能用空格替代。

例如,一个极简的 Makefile:

hello: main.o hello.ogcc main.o hello.o -o hellomain.o: main.cgcc -c main.chello.o: hello.cgcc -c hello.cclean:rm -f hello *.o
  • hello 是最终要生成的可执行文件名字,它依赖于 main.ohello.o

  • main.o 依赖于 main.c,命令 gcc -c main.c 负责生成它

  • clean 是一个伪目标,用于清理生成的文件

    关于伪目标的理解:

    有的时候我们可能会需要执行一些并不生成文件的操作,比如清理临时文件,生成文档,打包等等,这个时候就是使用伪目标来定义这些规则。伪目标就是一个不生成文件的目标。他只是个标签,用来执行这些操作

    那在后续执行的时候在make后面带赛clean就会执行。这样就会删除所有.o文件

    make clean
    

    需要注意的地方:如果我们本地目录下有文件的文件名是clean,那么make clean的命令就会失效,因为这是make把clean当成文件名处理了。这种情况下我们在第一行显示的告诉Make这个clean是个伪目标

    .PHONY: cleanhello: main.o hello.ogcc main.o hello.o -o hello
    

    其他伪目标

    • all:但我们执行make时,Make会默认执行第一个规则,但是如果我们想要生成的目标文件不止一个的话,就可以使用all这个伪命令。示例如下,然后执行命令make all【当all是第一条规则时,如本例,可以直接make,不用带all】即可自己也会显示我们的提示信息“all done”

      .PHONY clean allall: hello worldecho "all done"hello: main.o hello.ogcc main.o hello.o -o helloworld: main.o hello.ogcc main.o hello.o -o hellomain.o: main.cgcc -c main.chello.o: hello.cgcc -c hello.cclean:rm -f hello *.o
      

执行 make 时,默认会找到第一个目标 hello,并根据依赖关系逐层构建。如果我们只改了 main.cmake 只会重新编译 main.o 并重新链接 hello

理解

大家应该知道这样写也是可以的,但是这看似省事,但并不在工程中引用

hello: main.c hello.cgcc main.c hello.c -o hello

因为如果这样写的话无论哪个文件有更新,都要重新编译这两个文件,这是因为我们省区了生成中间文件【.o文件】的步骤。实际中,更加规范和标准的做法是将编译和链接分开来写。也就是上面示例的写法。

1.2 使用变量和自动推导

重复写文件名和命令很容易出错,我们可以用变量自动变量简化:

CC = gcc
CFLAGS = -Wall -g
TARGET = hello
OBJS = main.o hello.o$(TARGET): $(OBJS)$(CC) $^ -o $@main.o: main.c$(CC) $(CFLAGS) -c $<hello.o: hello.c$(CC) $(CFLAGS) -c $<clean:rm -f $(TARGET) *.o
  • $@ 代表目标文件(hello
  • $^ 代表所有依赖文件(main.o hello.o
  • $< 代表第一个依赖文件(main.chello.c

更激进地,Make 还能用模式规则(利用通配符)自动推导 .c.o 的编译命令,最终简化为:

CC = gcc
CFLAGS = -Wall -g
TARGET = hello
OBJS = main.o hello.o$(TARGET): $(OBJS)$(CC) $^ -o $@%.o: %.c$(CC) $(CFLAGS) -c $<clean:rm -f $(TARGET) *.o

到这里,你已经能看懂绝大多数手写的 Makefile 了。


2. 为什么还需要 CMake?

手写 Makefile 有两个痛点:

  1. 跨平台困难。Make 的语法在 Windows 下需要 NMake 或 MinGW,命令和路径处理各不相同。
  2. 项目规模一大,依赖关系、子目录、库查找、安装等会让 Makefile 变得极其臃肿。

CMake 应运而生:它不直接构建项目,而是生成构建文件(例如 Unix 下的 Makefile,Windows 下的 Visual Studio 工程,或者 Ninja 文件)。也就是说,CMake 是一层更高阶的抽象,我们写 CMakeLists.txt,CMake 帮我们生成平台相关的构建配置。

3. CMake 的核心概念

3.1 一个最小的 CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(Hello)add_executable(hello main.c hello.c)

三行代码就能构建一个可执行文件,比手写 Makefile 简单得多。这里做的事情:

  • 声明 CMake 最低版本要求
  • 定义项目名称
  • 添加一个名为 hello 的可执行文件,由 main.chello.c 编译生成

3.2 构建流程

通常使用 外部构建(out-of-source build),让生成的临时文件与源码分离:

mkdir build
cd build
cmake ..
make

cmake .. 会读取上层目录的 CMakeLists.txt,并在 build 目录下生成 Makefile,之后 make 完成编译。如果需要用其他生成器,可以指定:
cmake -G "Visual Studio 17 2022" ..

3.3 常用的 CMake 命令与变量

以下示例涵盖了实际项目中常见的操作:包含目录、添加库、链接库等。

cmake_minimum_required(VERSION 3.15)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)# 添加子目录(通常用于管理多个模块)
add_subdirectory(src)# 收集源文件(更灵活的方式)
file(GLOB_RECURSE SOURCES src/*.cpp src/*.h)# 生成可执行文件
add_executable(myapp ${SOURCES})# 将头文件目录提供给编译器
target_include_directories(myapp PRIVATE include)# 添加一个库(STATIC/SHARED)
add_library(mylib STATIC lib/mylib.cpp)# 将库链接到可执行文件
target_link_libraries(myapp PRIVATE mylib)

常用变量:

变量 含义
CMAKE_SOURCE_DIR 顶层 CMakeLists.txt 所在目录
CMAKE_BINARY_DIR 执行 cmake 的 build 目录
PROJECT_NAME 当前项目名称
CMAKE_CXX_STANDARD 使用的 C++ 标准

作用域关键字 PUBLICPRIVATEINTERFACE 用于精确控制头文件/库的传播范围,是与现代 CMake 紧密相关的重点,后面进阶学习时会经常遇到。

3.4 查找外部库

用 CMake 查找系统中的库非常方便:

find_package(OpenCV REQUIRED)
target_link_libraries(myapp PRIVATE ${OpenCV_LIBS})
target_include_directories(myapp PRIVATE ${OpenCV_INCLUDE_DIRS})

通过 find_package,CMake 会自动根据模块或配置文件寻找库的位置并设置好对应的变量。

4. 对比总结

特性 Make CMake
语言 Makefile(自己的语法) CMakeLists.txt(高层 DSL)
跨平台 依赖 Make 实现 (GNU/nmake) 生成不同平台的构建文件
增量构建 支持 继承底层构建工具(如 Make)
依赖管理 手动指定 内置查找模块、包管理(FetchContent等)
可读性 小项目清晰,大项目繁杂 结构清晰,模块化程度高

一句话理解:Make 直接告诉编译器该怎么做,CMake 先帮你生成“该怎么做”的说明书,然后再交给 Make 或其他工具去执行。

5. 实战小抄:从读懂到会用

看懂 Makefile 的检查点

  • 寻找第一个目标(默认构建目标)
  • 关注变量定义和 $(...) 引用
  • 理解 $@, $^, $< 的含义
  • 看到 %.o: %.c 就是模式规则,自动处理同类型文件

看懂 CMakeLists.txt 的检查点

  • add_executableadd_library 是构建核心
  • target_link_librariestarget_include_directories 决定编译和链接的依赖关系
  • find_package 负责查找外部依赖
  • set(...) 用于设置变量和选项
  • 注意 if(), else(), endif() 的逻辑分支以及循环

当你能够顺畅阅读这两种代码时,就已经拿到了理解大型 C/C++ 项目构建体系的第一把钥匙。后续无论是自己写库还是接手遗留项目,都能从构建脚本中快速摸清脉络。

http://www.jsqmd.com/news/928852/

相关文章:

  • 轻松获取网页视频:猫抓浏览器插件的资源嗅探魔法
  • OpencvSharp 算子学习教案之 - Cv2.SetNumThreads
  • 2026杭州静奢风家装,我跑了十几家门店,推荐这5个品牌 - 高定
  • 推荐系统信息茧房与过度拟合:技术机理与工程缓解策略
  • 医院HIS与云PACS/RIS接口对接实战:门诊住院检查单同步的那些“坑”与填坑指南
  • 神经网络机器翻译:从编码器-解码器到Transformer的架构演进与应用实践
  • 2026年中国精密光学机械市场竞争力推荐品牌:显微成像与光路配套核心品牌深度解析 - 博客万
  • pgsql语法
  • 失效分析实战:部件寿命延长2倍 成本直降25% - 速递信息
  • Oracle EBS 的资产模块(Fixed Assets, FA)本质上是一个“基于策略驱动、账簿隔离、全生命周期可追溯”的财务引擎
  • XZ3621宽输入电压范围:4V至30V 3A 130kHz电流输出同步降压稳压器
  • 图解Transformer:现代AI的通用基石
  • 2026年 江苏厂房降温/车间降温设备推荐榜单:冷风机/工业冷风机/移动式冷风机/负压风机/镀锌板厂房风机/玻璃钢负压风机/永磁负压风机品质之选 - 品牌企业推荐师(官方)
  • UE5 GAS系统避坑指南:从碰撞检测到ApplyGameplayEffectSpecToSelf的完整流程详解
  • Node-RED实战:用node-red-contrib-modbus节点快速读取RS485温湿度传感器数据
  • 4D 成像雷达深度解析 | 全网独家复现篇 | 原理拆解、代码实现、车企量产落地与典型应用案例
  • Ava Studio 技术架构与短视频广告批量生成原理解析
  • 线上人气评选如何制作?云众评选小程序三分钟搞定 - 微信投票小程序
  • PHP与Redis缓存实践完整方案
  • 2026汇泉胶粉选购指南:纸品包装全场景裱纸胶粉权威推荐 - 速递信息
  • 《2026 年 IT 行业最有前途的 7 个方向,选错了再努力也没用》
  • 如何彻底解决Switch手柄问题:Joy-Con Toolkit完整指南
  • attention 的mask 的简单实现
  • 从Input.GetAxis到手感调优:详解Unity中移动与旋转的平滑处理与参数配置
  • ChatGPT核心原理、高阶应用与提示词实战指南
  • 2026四川绵阳江油手机店哪家好?二手手机、手机分期去哪家? - 博客万
  • 如何平衡CSP-J备赛与校内学习
  • 变更管理在软考中级系统集成项目管理工程师考试中占多少分 - 众智商学院官方
  • 【Gemini推送通知优化实战指南】:20年专家亲授5大性能瓶颈与98%送达率提升方案
  • 3步解锁经典游戏潜能:WarcraftHelper魔兽争霸III终极优化方案