Makefile 完全指南:从入门到工程化,自动化构建不再难


🔥草莓熊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 后可快速上手。

✨把这些内容吃透超牛的!放松下吧✨ ʕ˘ᴥ˘ʔ づきらど

相关推荐
我登哥MVP1 分钟前
VS Code 安装 Claude Code 并接入 DeepSeek V4 Model
人工智能·python·node.js·agent·codex·deepseek·claude code
vsropy1 分钟前
安装虚拟机VMware
linux·windows
unique2 分钟前
AI Native 调研报告
人工智能
云烟成雨TD2 分钟前
Spring AI Alibaba 1.x 系列【73】两步 RAG
java·人工智能·spring
ai产品老杨4 分钟前
解耦视频高并发与边缘计算AI布控:基于Docker的高性能安防平台,破局GB28181/RTSP协议兼容与源码交付痛点
人工智能·音视频·边缘计算
Jason_chen4 分钟前
Linux 3.0 串口机制深度解析:传统8250驱动与基础RS-232/485支持
linux·前端
CHrisFC5 分钟前
LIMS 系统 AI 建设路径:从自动化到智能化的演进之路
运维·人工智能·自动化
Jason_chen5 分钟前
Linux 5.10 串口机制深度解析:serial_core重构与RS-485自动方向控制革命
linux
饼干哥哥6 分钟前
一口气搭了300个AI Agents并发处理跨境运营的dirty work
人工智能
AI行业学习6 分钟前
CC‑Switch v3.16.1-下载、配置、安装(2026‑06‑01 最新官方版)
开发语言·人工智能·windows·python