
🔥草莓熊Lotso: 个人主页
❄️个人专栏: 《C++知识分享》 《Linux 入门到实践:零基础也能懂》
✨生活是默默的坚持,毅力是永久的享受!
🎬 博主简介:

文章目录
- 前言:
- [一. Makefile 核心认知:什么是 Makefile?](#一. Makefile 核心认知:什么是 Makefile?)
-
- [1.1 背景介绍](#1.1 背景介绍)
- [1.2 核心作用](#1.2 核心作用)
- [1.3 核心概念](#1.3 核心概念)
- [二. 入门案例:最简单的 Makefile](#二. 入门案例:最简单的 Makefile)
-
- [2.1 源文件hello.c](#2.1 源文件hello.c)
- [2.2 基础 Makefile](#2.2 基础 Makefile)
- [2.3 实际操作演示](#2.3 实际操作演示)
- [三. Makefile 语法规则:深入理解依赖与命令](#三. Makefile 语法规则:深入理解依赖与命令)
-
- [3.1 基本语法格式](#3.1 基本语法格式)
- [3.2 依赖关系与推导](#3.2 依赖关系与推导)
- [3.3 伪目标(.PHONY)](#3.3 伪目标(.PHONY))
- [四. 进阶用法:变量与函数(工程化必备)](#四. 进阶用法:变量与函数(工程化必备))
-
- [4.1 变量定义与使用](#4.1 变量定义与使用)
- [4.2 常用函数(简化文件列表)](#4.2 常用函数(简化文件列表))
- [4.3 自动变量(简化命令)](#4.3 自动变量(简化命令))
- [4.4 工程化示例(多源文件适配,简化 Makefile/makefile)](#4.4 工程化示例(多源文件适配,简化 Makefile/makefile))
- 结尾:
前言:
在 Linux C/C++ 开发中,当项目源文件增多时,手动输入gcc命令编译会变得繁琐且容易出错 ------ 不仅要记住文件依赖关系,还要处理编译顺序和参数。
Makefile的核心价值就是 "自动化构建":一旦写好配置,只需执行make命令,就能自动完成编译、链接、清理等一系列操作,极大提升开发效率。本文从 Makefile 的基本原理入手,逐步拆解语法规则、依赖关系、伪目标、变量与函数,最后通过工程化示例演示实战用法,帮你从 "手动编译" 升级到 "自动化构建",轻松应对中小型项目开发。
一. Makefile 核心认知:什么是 Makefile?
1.1 背景介绍
- 会不会写makefile,从侧面说明了一个人是否具备完成大型工程的能力
- 一个工程中的源文件不计其数,其按类型,功能,模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于更复杂的操作
- makefile 带来的好处就是 -- "自动化编译",一旦写好,只需要一个 make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
- make 是一个命令工工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi 的 make,Visual C++ 的 namke,Linux下GNU的make。可见,makefile都成为了一种在工厂方面的编译方法。
- make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。
1.2 核心作用
- 定义源文件之间的依赖关系(如可执行文件依赖目标文件,目标文件依赖源文件);
- 指定编译规则(如如何将.c文件编译为.o文件,如何链接为可执行文件);
- 支持增量编译(只重新编译修改过的文件,减少重复工作);
- 提供项目清理功能(一键删除编译产物,方便重新构建)。
1.3 核心概念
- make:解释 Makefile 规则的命令工具,默认查找当前目录下名为Makefile或makefile的文件;
- 目标(target):要生成的文件或要执行的命令(如可执行文件、清理操作);
- 依赖(prerequisites):生成目标所需要的文件或条件(如生成myproc依赖myproc.c);
- 命令(command):生成目标的具体操作(如gcc -o myproc myproc.c),必须以 Tab 键开头。
二. 入门案例:最简单的 Makefile
假设项目只有一个源文件hello.c,要生成可执行文件hello,并支持清理编译产物。
2.1 源文件hello.c
bash
#include<stdio.h>
int main()
{
printf("hello Makefile!");
return 0;
}
2.2 基础 Makefile

bash
# 目标:依赖
code: code.c
# 命令(必须Tab开头):将code.c编译为可执行文件code
gcc -o code code.c
# 伪目标:清理编译产物
.PHONY: clean
clean:
# -f表示强制删除,忽略不存在的文件
rm -f code
2.3 实际操作演示
bash
[Lotso@VM-4-4-centos lesson10]$ make
gcc -o hello hello.c
[Lotso@VM-4-4-centos lesson10]$ ll
total 912
-rwxrwxr-x 1 Lotso Lotso 8360 Nov 20 13:45 hello
-rw-rw-r-- 1 Lotso Lotso 81 Nov 20 13:44 hello.c
-rw-rw-r-- 1 Lotso Lotso 76 Nov 20 13:00 hello_copy.c
-rwxrwxr-x 1 Lotso Lotso 8360 Nov 20 09:23 hello_dynamic
-rwxrwxr-x 1 Lotso Lotso 861216 Nov 20 09:22 hello_static
-rw-rw-r-- 1 Lotso Lotso 69 Nov 20 13:44 makefile
-rwxrwxr-x 1 Lotso Lotso 8424 Nov 19 23:39 soft
-rw-rw-r-- 1 Lotso Lotso 250 Nov 19 22:27 soft.c
-rw-rw-r-- 1 Lotso Lotso 90 Nov 19 23:11 soft.cpp
-rwxrwxr-x 1 Lotso Lotso 8968 Nov 19 23:12 softpp
[Lotso@VM-4-4-centos lesson10]$ ./hello
hello Makefile!
[Lotso@VM-4-4-centos lesson10]$ make clean
rm -f hello
[Lotso@VM-4-4-centos lesson10]$ ll
total 900
-rw-rw-r-- 1 Lotso Lotso 81 Nov 20 13:44 hello.c
-rw-rw-r-- 1 Lotso Lotso 76 Nov 20 13:00 hello_copy.c
-rwxrwxr-x 1 Lotso Lotso 8360 Nov 20 09:23 hello_dynamic
-rwxrwxr-x 1 Lotso Lotso 861216 Nov 20 09:22 hello_static
-rw-rw-r-- 1 Lotso Lotso 69 Nov 20 13:44 makefile
-rwxrwxr-x 1 Lotso Lotso 8424 Nov 19 23:39 soft
-rw-rw-r-- 1 Lotso Lotso 250 Nov 19 22:27 soft.c
-rw-rw-r-- 1 Lotso Lotso 90 Nov 19 23:11 soft.cpp
-rwxrwxr-x 1 Lotso Lotso 8968 Nov 19 23:12 softpp

核心用法:
- 编译项目: 在 Makefile 所在目录执行
make,自动查找第一个目标(code),检查依赖是否更新,执行编译命令; - 清理项目: 执行
make clean,执行clean目标下的命令,删除可执行文件; - 增量编译: 修改
code.c后再次执行make,只会重新编译修改后的文件,而非全部重编。
三. Makefile 语法规则:深入理解依赖与命令
3.1 基本语法格式
bash
目标(target): 依赖(prerequisites)
命令1
命令2
...
- 目标可以是可执行文件、目标文件(
.o)、伪目标(如clean); - 依赖可以是源文件、目标文件、其他目标;
- 命令必须以Tab 键 开头(不能用空格,否则
make会报错); - 注释以
#开头,单行有效。
3.2 依赖关系与推导
参考图示 :(Makefile 会自动推导目标文件的依赖)

但是我们一般是用不到这么多步的,最佳实践如下:
bash
# 最终目标:可执行文件code.exe,依赖code.o
code.exe: code.o
gcc code.o -o code.exe
# 目标文件code.o,依赖code.c
code.o: code.c
gcc -c code.c -o code.o
# 伪目标clean
.PHONY: clean
clean:
rm -f code.exe code.o

3.3 伪目标(.PHONY)
- 伪目标不是实际文件 ,而是一个 "命令标签"(如
clean); - 作用 :避免项目中存在与伪目标同名的文件,导致
make误判为 "目标已存在,无需执行"; - 语法 :
/.PHONY:伪目标名,例如/.PHONY: clean,确保make clean总是执行命令。
📌 结论:
.PHONY:让make忽略源文件和可执行目标文件的M时间对比
图示理解 :(什么叫总是被执行?)


四. 进阶用法:变量与函数(工程化必备)
当项目源文件增多时,手动写每个文件的依赖和命令会很繁琐。Makefile 支持变量和函数,可简化配置、提高复用性。
4.1 变量定义与使用
变量用于存储重复出现的内容(如编译器、编译参数、文件列表),语法: 变量名=值 ,使用时 $(变量名)。
示例:
bash
# 定义变量:编译器、可执行文件名、目标文件列表、清理命令
CC = gcc # 编译器
BIN = myproc # 可执行文件名
OBJ = myproc.o # 目标文件列表
RM = rm -f # 清理命令
CFLAGS = -Wall -g # 编译参数(-Wall显示所有警告,-g生成调试信息)
# 最终目标:依赖OBJ变量
$(BIN): $(OBJ)
$(CC) $(CFLAGS) -o $(BIN) $(OBJ)
# 目标文件依赖源文件(可省略,make自动推导)
myproc.o: myproc.c
# 清理伪目标
.PHONY: clean
clean:
$(RM) $(BIN) $(OBJ)
4.2 常用函数(简化文件列表)
Makefile 提供内置函数,可自动获取文件列表,无需手动罗列。
(1)wildcard:获取指定模式的文件
bash
# 获取当前目录下所有.c文件,存入SRC变量
SRC = $(wildcard *.c)
(2)替换文件后缀
bash
# 将SRC中的.c文件替换为.o文件,存入OBJ变量
Obj=$(Src:.c=.o)
4.3 自动变量(简化命令)
Makefile 提供自动变量,替代命令中重复出现的目标和依赖,简化书写:
| 自动变量 | 含义 | 示例 |
|---|---|---|
$@ |
当前目标文件名 | $(CC) -o $@ $^ |
$^ |
所有不重复的依赖文件 | $(CC) -o $@ $^ |
$< |
第一个依赖文件名 | $(CC) -c $< -o $@ |
4.4 工程化示例(多源文件适配,简化 Makefile/makefile)
假设项目有多个源文件,Makefile 可自动适配,简化后的 Makefile 如下所示:
bash
Bin=code.exe # 定义变量
#Src=$(shell ls *.c) # 做法1 -- 采用shell命令行方式,获取当前所有.c文件名
Src=$(wildcard *.c) # 做法2 -- 使用 wildcard 函数,获取当前所有.c文件名
Obj=$(Src:.c=.o) # 将Src的所有同名 .c 替换成 .o 形成目标文件列表
Echo=echo
cc=gcc
Rm=rm -f
Flags=-c -Wall # 编译选项
LD_Flags=-o # 链接选项
$(Bin):$(Obj)
@$(Echo) "我要开始链接了...$(Obj) -> $(Bin)" # $@: 代表目标文件名 $^: 代表依赖文件列表
@$(cc) $(LD_Flags) $@ $^
%.o:%.c # %.c: 展开当前目录下的所有.c %.o: 同时展开同名.o
@$(Echo) "我要开始编译了...$< -> $@" # @: 不回显命令
@$(cc) $(Flags) $< # %<: 对展开的依赖.c文件,一个个的交给gcc
.PHONY:clean
clean:
$(Rm) $(Obj) $(Bin) # $(RM): 替换,用变量内容替换它
.PHONY:debug
debug:
@$(Echo) "Bin: $(Bin)"
@$(Echo) "Obj: $(Obj)"
@$(Echo) "Src: $(Src)"
~

结尾:
html
🍓 我是草莓熊 Lotso!若这篇技术干货帮你打通了学习中的卡点:
👀 【关注】跟我一起深耕技术领域,从基础到进阶,见证每一次成长
❤️ 【点赞】让优质内容被更多人看见,让知识传递更有力量
⭐ 【收藏】把核心知识点、实战技巧存好,需要时直接查、随时用
💬 【评论】分享你的经验或疑问(比如曾踩过的技术坑?),一起交流避坑
🗳️ 【投票】用你的选择助力社区内容方向,告诉大家哪个技术点最该重点拆解
技术之路难免有困惑,但同行的人会让前进更有方向~愿我们都能在自己专注的领域里,一步步靠近心中的技术目标!
结语:Makefile 的核心是 "规则定义" 和 "自动化推导",掌握变量、函数和自动变量后,可轻松适配中小型项目的构建需求。对于大型项目(如 Linux 内核),Makefile 会结合更复杂的语法(如条件判断、循环),但基础逻辑不变。建议在实际项目中多动手编写 Makefile,从简单案例逐步过渡到工程化配置,形成自己的模板。后续可进一步学习cmake(跨平台构建工具),但其核心思想与 Makefile 一致,掌握 Makefile 后可快速上手。
✨把这些内容吃透超牛的!放松下吧✨ ʕ˘ᴥ˘ʔ づきらど
