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

Linux 入门五:Makefile—— 从手动编译到工程自动化的蜕变

一、概述:Makefile—— 工程编译的 “智能指挥官”

1. 为什么需要 Makefile?

  • 手动编译的痛点:当工程包含数十个源文件时,每次修改都需重复输入冗长的编译命令(如gcc file1.c file2.c -o app),且无法自动识别哪些文件需要重新编译。
  • Makefile 的核心价值:通过定义 “目标 - 依赖 - 命令” 规则,实现自动化编译。只需执行make命令,即可根据文件修改时间智能判断编译顺序,避免重复工作,大幅提升开发效率。
  • 本质:一个名为Makefile(或makefile)的文本文件,存储编译规则,由make命令解析执行。

2. 核心概念快速入门

  • 目标(Target):要生成的文件(如可执行文件app)或伪操作(如清理编译产物的clean)。
  • 依赖(Prerequisites):生成目标所需的文件(如app依赖main.ofunc.o)。
  • 命令(Command):生成目标的具体操作,需以Tab 键开头(Makefile 严格要求)。

二、简单使用:从第一个 Makefile 起步

1. 创建与编辑 Makefile

# 创建文件
touch Makefile
# 用vim编辑(推荐用Visual Studio Code等IDE提升体验)
vim Makefile

2. 编写第一个编译规则:编译单文件程序

# 目标:生成可执行文件hello,依赖hello.c
hello: hello.c# 命令:用gcc编译,-o指定输出文件名,@禁止回显命令本身@echo "正在编译hello..."gcc hello.c -o hello# 伪目标:清理编译产物,.PHONY避免与同名文件冲突
.PHONY: clean
clean:@echo "清理编译产物..."rm -f hello  # -f强制删除,即使文件不存在也不报错

3. 执行 Makefile

# 编译目标(首次执行会生成hello)
make
# 输出:
# 正在编译hello...
# (若命令前无@,会额外回显"gcc hello.c -o hello")# 清理产物
make clean
# 输出:清理编译产物...(同时删除hello文件)

4. 关键语法解析

  • 注释#开头的行,用于解释规则(如# 伪目标:清理编译产物)。
  • 自动推导:Makefile 默认知道.c文件可编译为.o文件(如main.o依赖main.c,无需显式书写规则)。
  • 伪目标:用.PHONY声明(如clean),确保即使存在同名文件,make clean也会执行命令。

三、变量:让 Makefile 告别 “硬编码”

1. 自定义变量:四种赋值方式对比

赋值符号特性示例适用场景
=递归展开(可引用后续定义的变量)CFLAGS = -Wall\nOBJECTS = $(SRCS:.c=.o)需要动态计算值的场景
:=立即展开(定义时直接计算)SRCS := $(wildcard *.c)避免递归引用导致的循环定义
+=追加值(在原有值后添加新内容)LIBS += -lm(追加数学库)逐步构建复杂参数
?=惰性赋值(仅在未定义时生效)CC ?= gcc(默认用 gcc,可被命令行覆盖)设置默认值

2. 自动变量:依赖文件的 “快捷引用”

在模式规则(如%.o: %.c)中,自动变量可简化代码:

变量含义示例(目标main.o依赖main.c
$@当前目标文件名命令中$@代表main.o
$<第一个依赖文件命令中$<代表main.c
$^所有依赖文件(去重)依赖a.c b.c时,$^代表a.c b.c
$?比目标新的依赖文件仅重新编译修改过的文件

示例:多文件编译(使用自动变量)

CC := gcc          # 立即赋值,指定编译器
CFLAGS := -Wall -g  # 编译选项:开启警告和调试信息
TARGET := app       # 目标文件名
SRCS := main.c func.c  # 显式列出源文件(或用wildcard函数自动收集)
OBJS := $(SRCS:.c=.o)  # 将.c替换为.o,生成目标文件列表$(TARGET): $(OBJS)$(CC) $(OBJS) -o $(TARGET)  # 链接所有.o文件%.o: %.c$(CC) $(CFLAGS) -c $< -o $@  # 编译单个.c到.o,$<是源文件,$@是目标文件.PHONY: clean
clean:@rm -f $(OBJS) $(TARGET)

3. 预定义变量:Makefile 的 “内置工具”

Makefile 自带常用工具变量,可直接使用:

  • CC:C 编译器(默认cc,通常设为gcc)。
  • AR:归档工具(用于静态库,默认ar)。
  • RM:删除命令(默认rm -f,自动添加-f强制删除)。
  • CXX:C++ 编译器(默认g++)。

示例:使用预定义变量

main.o: main.c$(CC) -c main.c -o main.o  # 等价于`gcc -c main.c -o main.o`(若CC=gcc)

四、函数:让 Makefile 更 “聪明”

1. 文件搜索函数:wildcard

  • 功能:按模式匹配文件,返回匹配的文件列表(支持通配符*)。
  • 语法$(wildcard 模式),如$(wildcard src/*.c)获取src/目录下所有.c文件。
  • 示例:自动收集所有源文件
    SRCS := $(wildcard *.c)  # 收集当前目录所有.c文件
    OBJS := $(SRCS:.c=.o)     # 转换为.o文件列表app: $(OBJS)$(CC) $(OBJS) -o app
    

2. 字符串替换函数:patsubst

  • 功能:按指定模式替换字符串中的部分内容。
  • 语法$(patsubst 原模式, 新模式, 字符串),如$(patsubst %.c, %.o, a.c b.cpp)a.o b.o(需配合手动处理.cpp 文件)。
  • 示例:灵活处理混合格式源文件
    SRCS := a.c b.cpp c.c
    # 分别将.c和.cpp转换为.o(需分步处理)
    C_OBJS := $(patsubst %.c, %.o, $(filter %.c, $(SRCS)))
    CPP_OBJS := $(patsubst %.cpp, %.o, $(filter %.cpp, $(SRCS)))
    OBJS := $(C_OBJS) $(CPP_OBJS)
    

五、选项:扩展 make 命令的能力

1. -f:指定非默认Makefile

  • 场景:项目存在多个 Makefile(如Makefile.linuxMakefile.win),需显式指定。
  • 用法
    make -f Makefile.linux  # 执行指定文件中的规则,而非默认的Makefile
    

2. -C:切换目录执行

  • 场景:工程分模块存放(如src/lib/目录各有独立 Makefile)。
  • 用法
    # 总控Makefile,编译所有模块
    all:@make -C src  # 进入src目录,执行该目录下的Makefile@make -C lib  # 进入lib目录,执行该目录下的Makefile.PHONY: clean
    clean:@make -C src clean  # 清理src模块@make -C lib clean  # 清理lib模块
    

3. 其他实用选项

选项含义示例
-n干运行,仅打印命令不执行(调试用)make -n 查看编译步骤是否正确
-s静默模式,不回显命令(仅显示输出)make -s 隐藏编译命令,输出更简洁
-j N并行编译,N 为线程数(加快多核 CPU 编译速度)make -j 4 使用 4 个线程编译

六、实战模板:三种常用 Makefile 写法

模板一:生成可执行文件(多文件编译)

# 一、变量定义
CC := gcc              # C编译器
CFLAGS := -Wall -g -Iinclude  # 编译选项:警告+调试+头文件路径
SRCS := $(wildcard src/*.c)  # 自动收集src目录下所有.c文件
OBJS := $(patsubst src/%.c, obj/%.o, $(SRCS))  # 生成obj/目录下的.o文件# 二、目标规则
# 1. 最终目标:生成可执行文件app
app: $(OBJS)@echo "链接生成可执行文件..."$(CC) $(OBJS) -o app -Llib -lm  # -L指定库路径,-lm链接数学库# 2. 模式规则:src/xxx.c → obj/xxx.o(自动创建obj目录)
obj/%.o: src/%.c@mkdir -p obj  # 确保obj目录存在$(CC) $(CFLAGS) -c $< -o $@# 三、伪目标
.PHONY: clean
clean:@echo "清理编译产物..."@rm -f app $(OBJS)  # 删除可执行文件和所有.o文件@rm -rf obj  # 删除obj目录

模板二:生成动态库(.so 文件)

# 一、变量定义
SO_NAME := libmylib.so  # 动态库名称
CC := gcc
CFLAGS := -fPIC -Wall  # -fPIC生成位置无关代码(动态库必需)
SHLIB_FLAGS := -shared  # 生成动态库的关键选项
SRCS := $(wildcard src/*.c)
OBJS := $(SRCS:.c=.o)# 二、目标规则
# 1. 生成动态库
$(SO_NAME): $(OBJS)$(CC) $(SHLIB_FLAGS) $(OBJS) -o $(SO_NAME)# 2. 编译.o文件(与可执行文件规则类似)
%.o: %.c$(CC) $(CFLAGS) -c $< -o $@# 三、伪目标
.PHONY: clean
clean:@rm -f $(OBJS) $(SO_NAME)

模板三:生成静态库(.a 文件)

# 一、变量定义
A_NAME := libmylib.a  # 静态库名称
AR := ar rcs          # ar命令参数:r(添加)c(创建)s(生成索引)
CC := gcc
CFLAGS := -Wall
SRCS := $(wildcard src/*.c)
OBJS := $(SRCS:.c=.o)# 二、目标规则
# 1. 生成静态库(打包所有.o文件)
$(A_NAME): $(OBJS)$(AR) $(A_NAME) $(OBJS)  # 将.o文件打包成静态库# 2. 编译.o文件
%.o: %.c$(CC) $(CFLAGS) -c $< -o $@# 三、伪目标
.PHONY: clean
clean:@rm -f $(OBJS) $(A_NAME)

七、常见易错点与避坑指南

  1. 命令前必须用 Tab 键

    • 错误:命令行以空格开头,导致 “missing separator (did you mean TAB instead of 8 spaces?)” 错误。
    • 正确:所有命令行必须以 Tab 键开头(可在编辑器中设置 Tab 为 4 个空格,但最终需确保是 Tab 符)。
  2. 伪目标未声明.PHONY

    • 后果:若当前目录存在名为clean的文件,make clean会认为目标已存在,不执行清理命令。
    • 正确:始终为清理等伪目标添加.PHONY: clean声明。
  3. 变量引用格式错误

    • 错误:直接写变量名(如CC=gcc),应使用$(CC)引用变量。
    • 正确:所有变量引用需用$(变量名)${变量名}格式。
  4. 依赖关系遗漏

    • 后果:头文件(.h)修改后,未将其加入依赖,导致.o文件未重新编译。
    • 正确:在规则中显式依赖头文件(如main.o: main.c defs.h),或利用 Makefile 自动推导(需确保头文件包含正确)。

八、作业:从模仿到独立编写

1. 任务一:解析经典 Makefile

  • 下载开源项目(如nginxredis)的 Makefile,分析以下内容:
    ① 如何定义编译选项(CFLAGSCXXFLAGS)?
    ② 静态库 / 动态库的生成规则有何不同?
    ③ clean目标如何处理多层目录的编译产物?

2. 任务二:编写三个万能模板(强化版)

  • 可执行文件模板:添加对 C++ 文件的支持(.cpp文件用g++编译),使用wildcard递归搜索子目录源文件(如src/**/*.c)。
  • 动态库模板:添加版本号(如libmylib.so.1.0.0),使用ln -s创建软链接(如libmylib.so → libmylib.so.1.0.0)。
  • 静态库模板:支持多架构编译(如同时生成x86arm版本),通过变量ARCH切换编译选项。

3. 任务三:实战复杂工程

创建一个包含以下结构的项目:

project/
├─ Makefile       # 总控Makefile
├─ src/
│  ├─ main.c
│  ├─ func.c
│  └─ Makefile     # 模块Makefile
├─ include/
│  └─ func.h
└─ lib/           # 编译生成的库文件存放目录

要求总控 Makefile 使用-C选项调用src/目录下的 Makefile,最终在lib/目录生成可执行文件。

总结:Makefile 让工程编译 “化繁为简”

通过掌握 Makefile 的核心规则、变量、函数和选项,你将实现从手动编译到自动化编译的跨越。记住以下关键点:

  • 规则是基础:每个目标必须明确依赖和命令,利用自动推导简化常规编译步骤。
  • 变量提效率:自定义变量减少重复输入,自动变量和预定义变量提升代码简洁性。
  • 函数增智能wildcardpatsubst自动处理文件列表,适应复杂工程结构。
  • 选项扩场景-f-C应对多 Makefile 和分模块编译,-j加速编译过程。

现在,打开你的项目,用 Makefile 替代繁琐的手动命令,让编译过程从此高效、智能!

相关文章:

Linux 入门五:Makefile—— 从手动编译到工程自动化的蜕变

一、概述&#xff1a;Makefile—— 工程编译的 “智能指挥官” 1. 为什么需要 Makefile&#xff1f; 手动编译的痛点&#xff1a;当工程包含数十个源文件时&#xff0c;每次修改都需重复输入冗长的编译命令&#xff08;如gcc file1.c file2.c -o app&#xff09;&#xff0c;…...

通过websocket给服务端发送订单催单提醒消息

controller层 GetMapping("/reminder/{id}")public Result Remainder(PathVariable("id") Long id){orderService.remainder(id);return Result.success();} 实现类 Overridepublic void remainder(Long id) {Orders ordersDB orderMapper.getById(id);…...

【NumPy科学计算:高性能数组操作核心指南】

目录 前言&#xff1a;技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析核心概念图解关键技术模块技术选型对比 二、实战演示环境配置要求核心代码实现运行结果验证 三、性能对比测试方法论量化数据对比结果分析 四、最佳实践推荐方案 ✅常见错误 ❌调试技…...

c++ 表格控件 UltimateGrid 控件实例

控件区域&#xff1a; 使用效果&#xff1a; 代码如下&#xff1a; void MyUGCtrl::OnSetup() { m_nButtonIndex AddCellType(&m_button); SetNumberCols(6); AppendRow(); CUGCell cell; int rows, cols; int row 0; // 头部 int nHeaderRow -1; …...

从单机版到超级APP:MCP如何解锁AI的超能力

MCP&#xff1a;AI界的“万能充电宝”——让AI从此告别“语言不通”的尴尬&#xff01; 开篇&#xff1a;AI咖啡馆的尴尬日常 想象一下这样的场景&#xff1a; 一位AI助手在咖啡馆里手忙脚乱——它想帮用户点杯咖啡&#xff0c;但需要先写代码调用天气API&#xff08;“今天下…...

【算法】 欧拉函数与欧拉降幂 python

欧拉函数 欧拉函数 ϕ ( n ) \phi(n) ϕ(n) 表示小于等于 n 的正整数中与 n 互质的数的个数。即&#xff1a; ϕ ( n ) ∣ { k ∈ Z ∣ 1 ≤ k ≤ n , gcd ⁡ ( k , n ) 1 } ∣ \phi(n) \left| \{ k \in \mathbb{Z}^ \mid 1 \leq k \leq n, \gcd(k, n) 1 \} \right| ϕ(n)…...

【Python] pip制作离线包

制作离线安装包是一种非常实用的方法&#xff0c;尤其是在网络环境受限或需要在多台机器上部署相同环境时。以下是详细的步骤&#xff0c;帮助您创建一个包含所有依赖项的离线安装包&#xff0c;并在后续环境中复用。 步骤 1&#xff1a;准备工具和环境 确保您有一台可以访问互…...

什么是回表?哪些数据库存在回表?

目录 一、什么是回表1. 回表的核心流程2. 示例说明3. 回表的性能问题4. 总结 二、哪些数据库会有回表1. MySQL&#xff08;InnoDB&#xff09;2. Oracle3. 其他数据库&#xff08;如 SQL Server、PostgreSQL&#xff09;4. 总结 三、非聚集索引与聚集索引的区别及产生原因1. 聚…...

linux 内存踩踏导致的空指针问题分析纪要

1&#xff0c;查看日志信息打印 我们看到日志发现发包的skb模块有NULL pointer情况&#xff0c;我们看代码分析skb指针不可能出现是空指针&#xff0c;这个时候我们怀疑可能是出现了踩内存导致的空指针情况&#xff0c;所以我们首先需要找到系统PANIC的条件&#xff0c;也就是…...

使用 VcXsrv 在 Windows 10 上运行 Ubuntu 图形界面

VcXsrv 是一款用于 Windows 的开源 X 服务器&#xff0c;它允许在 Windows 系统上显示 Linux 的图形应用程序。当在 Windows 10 上安装并正确配置 VcXsrv 后&#xff0c;通过设置 WSL2 中的DISPLAY环境变量&#xff0c;使其指向运行 VcXsrv 的 Windows 主机的 IP 地址&#xff…...

LSTM-SVM长短期记忆神经网络结合支持向量机组合模型多特征分类预测/故障诊断,适合新手小白研究学习(Matlab完整源码和数据)

LSTM-SVM长短期记忆神经网络结合支持向量机组合模型多特征分类预测/故障诊断&#xff0c;适合新手小白研究学习&#xff08;Matlab完整源码和数据&#xff09; 目录 LSTM-SVM长短期记忆神经网络结合支持向量机组合模型多特征分类预测/故障诊断&#xff0c;适合新手小白研究学习…...

Autoware源码总结

Autoware源码网站 项目简介 教程 Autoware的整体架构如下图&#xff0c;主要包括传感器sensing、高精地图map data、车辆接口vehicle interface、感知perception&#xff08;动态障碍物检测detection、跟踪tracking、预测prediction&#xff1b;交通信号灯检测detection、分类c…...

QT聊天项目DAY01

1.新建初始项目 2.修改UI格式 运行效果 3.创建登录界面 设计登录界面UI 设计布局 调整布局间距 往水平布局中拖入标签和文本输入框 更换控件名称并固定高度 添加窗口部件 往现有的资源文件中导入图片 添加水平布局 4.设置登陆界面为主窗口的核心组件 #pragma once#include &l…...

【NumPy科学计算引擎:从基础操作到高性能实践】

目录 前言&#xff1a;技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析关键技术模块说明技术选型对比 二、实战演示环境配置核心代码实现运行结果验证 三、性能对比测试方法论量化数据对比结果分析 四、最佳实践推荐方案 ✅常见错误 ❌调试技巧 五、应用…...

MySQL InnoDB 索引与B+树面试题20道

1. B树和B+树的区别是什么? 数据存储位置: B树:所有节点(包括内部节点和叶子节点)均存储数据。 B+树:仅叶子节点存储数据,内部节点仅存储键值(索引)。 叶子节点结构: B+树:叶子节点通过双向链表连接,支持高效的范围查询。 查询稳定性: B+树:所有查询必须走到叶子…...

论文精度:基于LVNet的高效混合架构:多帧红外小目标检测新突破

论文地址:https://arxiv.org/pdf/2503.02220 目录 一、论文背景与结构 1.1 研究背景 1.2 论文结构 二、核心创新点解读 2.1 三大创新突破 2.2 创新结构原理 2.2.1 多尺度CNN前端 2.2.2 视频Transformer设计 三、代码复现指南 3.1 环境配置 3.2 数据集准备 3.3 训…...

ORM查询的补充

一&#xff0c;ORM查询的补充&#xff1a; 1&#xff0c;连接查询&#xff1a; 反向查询: 先介绍一下什么是正向查询&#xff0c;比如我们之前的数据表之间建立的一对多的关系&#xff0c;我们通过文章找到相应的作者是属于正向查询的&#xff08;由多到一&#xff09;&…...

【C语言-全局变量】

【C语言-全局变量】 1.能局部就局部&#xff0c;别啥都往全局塞2.尽量用结构体对零散变量封装3.函数传参4.静态变量模块化5 单例模式, 限制全局实例数量6. 配置化全局参数——集中管理可调参数7. 事件驱动架构&#xff1a;消息队列通信策略选择建议 参考https://mp.weixin.qq.c…...

mysql 商城商品属性开发的动态解决方案

终极方案&#xff1a;动态属性解决方案 推荐使用 JSON 字段 虚拟列索引 的组合方案 结合灵活存储与查询优化&#xff0c;平衡扩展性与性能 完整实现步骤 步骤 1&#xff1a;创建基础表结构 CREATE TABLE products (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(100) NO…...

python利用open-cv和SSIM和特征值比较两个图片的相似性

以下是关于 **SSIM&#xff08;结构相似性指数&#xff09;** 和 **特征匹配** 的详细解释及实际示例&#xff0c;帮助理解它们的区别和应用场景&#xff1a; --- ### **1. SSIM&#xff08;结构相似性指数&#xff09;** #### **含义**&#xff1a; - **SSIM** 是一种衡量两…...

蔚来汽车智能座舱接入通义大模型,并使用通义灵码全面提效

为加速AI应用在企业市场落地&#xff0c;4月9日&#xff0c;阿里云在北京召开AI势能大会。阿里云智能集团资深副总裁、公共云事业部总裁刘伟光发表主题演讲&#xff0c;大模型的社会价值正在企业市场释放&#xff0c;阿里云将坚定投入&#xff0c;打造全栈领先的技术&#xff0…...

QT 老版本下载地址被禁 如何下载

前提&#xff1a; 想用老版本的QT 5.12 系列&#xff0c;但是QT官方已经封禁了国内IP 访问&#xff0c;5.15之前的版本&#xff0c;而且5.14.2是最后一个离线exe版本 ; Index of /official_releases/qt 基本不可用&#xff1b;全部改为在线安装&#xff1b; 收集了一下地址&am…...

VMWare Workstation Pro17.6最新版虚拟机详细安装教程(附安装包教程)

目录 前言 一、VMWare虚拟机下载 二、VMWare虚拟机安装 三、运行虚拟机 前言 VMware 是全球领先的虚拟化技术与云计算解决方案提供商&#xff0c;通过软件模拟计算机硬件环境&#xff0c;允许用户在一台物理设备上运行多个独立的虚拟操作系统或应用。其核心技术可提升硬件…...

【数据结构】红黑树超详解 ---一篇通关红黑树原理(含源码解析+动态构建红黑树)

一.什么是红黑树 红黑树是一种自平衡的二叉查找树&#xff0c;是计算机科学中用到的一种数据结构。1972年出现&#xff0c;最初被称为平衡二叉B树。1978年更名为“红黑树”。是一种特殊的二叉查找树&#xff0c;红黑树的每一个节点上都有存储表示节点的颜色。每一个节点可以是…...

uni-app初学

文章目录 1. pages.json 页面路由2. 图标3. 全局 CSS4. 首页4.1 整体框架4.2 完整代码4.3 轮播图 swiper4.3.1 image 4.4 公告4.4.1 uni-icons 4.5 分类 uni-row、uni-col4.6 商品列表 uni-row、uni-col 小程序开发网址&#xff1a; 注册小程序账号 微信开发者工具下载 uniapp …...

PHP多维数组

在 PHP 中&#xff0c;多维数组是数组的数组&#xff0c;允许你存储和处理更复杂的数据结构。多维数组可以有任意数量的维度&#xff0c;但通常我们最常用的是二维数组&#xff08;数组中的数组&#xff09;。 首先来介绍一下一维数组&#xff0c; <?php//一维数组 $strAr…...

数学建模:针对汽车行驶工况构建思路的延伸应用

前言&#xff1a; 汽车行驶工况构建的思简单理解为将采集的大量数据进行“去除干扰、数据处理&#xff0c;缩减至1800S的数据”&#xff0c;并可达到等效替换的目的&#xff0c;可以使在试验室快速复现&#xff1b;相应的解决思路、办法可应用在 “通过能量流采集设备大量采集…...

go语言内存泄漏的常见形式

go语言内存泄漏 子字符串导致的内存泄漏 使用自动垃圾回收的语言进行编程时&#xff0c;通常我们无需担心内存泄漏的问题&#xff0c;因为运行时会定期回收未使用的内存。但是如果你以为这样就完事大吉了&#xff0c;哪里就大错特措了。 因为&#xff0c;虽然go中并未对字符串…...

当DRAM邂逅SSD:新型“DRAM+”存储技术来了!

在当今快速发展的科技领域&#xff0c;数据存储的需求日益增长&#xff0c;对存储设备的性能和可靠性提出了更高的要求。传统DRAM以其高速度著称&#xff0c;但其易失性限制了应用范围&#xff1b;而固态硬盘SSD虽然提供非易失性存储&#xff0c;但在速度上远不及DRAM。 为了解…...

论文精度:YOLOMG:基于视觉的无人机间检测算法——外观与像素级运动融合详解

论文地址:https://arxiv.org/pdf/2503.07115 1. 论文概述 论文标题:YOLOMG: Vision-based Drone-to-Drone Detection with Appearance and Pixel-Level Motion Fusion 作者:Hanqing Guo, Xiuxiu Lin, Shiyu Zhao 发表:未明确会议/期刊(推测为预印本或待发表) 核心贡献:…...