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

Linux操作系统从入门到实战(九)Linux开发工具(中)自动化构建-make/Makefile知识讲解

Linux操作系统从入门到实战(九)Linux开发工具(中)自动化构建-make/Makefile

  • 前言
  • 一、 make/Makefile是什么?
    • 1. 我们先想个问题:手动编译代码有多麻烦?
    • 2. 为了解决麻烦,才有了自动化工具
    • 3. 什么是 Makefile ?
    • 4. 什么是 make ?
  • 二、如何使用 make/Makefile
    • 1. 如何创建第一个Makefile?
      • (1) 创建源文件
      • (2) 创建Makefile文件
      • (3) 编写Makefile规则
      • (4) 执行make命令
    • 2. 依赖关系与命令
      • (1) 依赖关系
      • (2) make的执行逻辑
      • (3) 为什么需要依赖关系?
    • 3. 如何清理编译结果?
      • (1) 添加clean目标
      • (2) 执行清理命令
      • (3) .PHONY 标签的作用
    • 4. Makefile的执行规则
      • (1) 默认执行第一个目标
      • (2) 如何执行其他目标?
      • (3) 多个目标的执行顺序
    • 5. 常见错误与注意事项
      • (1) 命令行必须以Tab键开头
      • (2) 文件名大小写敏感
  • 三、伪目标是什么,为什么伪目标总被执行?
    • 1. make如何判断是否需要重新编译?
    • 2. 什么是伪目标(.PHONY)?
      • (1)定义:
      • (2) 常见用途:
    • 3. 为什么伪目标总是被执行?
      • (1)核心原因:
      • (2) 对比实验:
    • 4. 为什么需要伪目标?
      • (1) 避免与文件冲突
      • (2) 强制执行命令
    • 5. 常见伪目标示例
      • 1. clean目标(最常见)
      • 2. all目标(批量编译多个程序)
      • 3. install目标(安装程序到系统)


前言

  • 前面的博客里我们讲解了vim编译,g++/gcc编译的步骤和动静态链接与库的相关知识
  • 接下来我们继续讲解Linux开发工具自动化构建-make/Makefile

我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的Linux知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12879535.html?spm=1001.2014.3001.5482


一、 make/Makefile是什么?

1. 我们先想个问题:手动编译代码有多麻烦?

  • 假设你写了一个小项目,里面有好几个 .c 源文件(比如 a.cb.cmain.c)。
  • 要让它们变成能运行的程序,得用 gcc 编译,比如:
    gcc a.c b.c main.c -o myprogram

这看起来简单,但如果项目变大呢?

  • 假设有 100 个源文件,每次都手动敲这串命令,累不说,还容易输错;
    在这里插入图片描述

  • 如果只改了 a.c,手动编译时还得重新编译所有文件,浪费时间

  • 不同文件有依赖关系(比如 main.c 要用 a.c 的结果),手动编译得记清顺序,错一步就失败

2. 为了解决麻烦,才有了自动化工具

就像做复杂的菜时,你需要一份「步骤清单」(先切菜、再炒肉、最后炖),再找个「帮手」按清单一步步做。

在编程里,「步骤清单」就是 Makefile,「帮手」就是 make

3. 什么是 Makefile ?

Makefile 是一个我们自己创建的文件(名字就叫 Makefile,没有后缀),里面写的是「编译规则」

比如在一个 Makefile 里,你可以在里面写下:

现在看一下代码,后面会详细讲

# 规则1:最终要生成的程序叫 myprogram,它依赖 a.o、b.o、main.o
myprogram: a.o b.o main.ogcc a.o b.o main.o -o myprogram  # 生成 myprogram 的命令# 规则2:a.o 依赖 a.c
a.o: a.cgcc -c a.c  # 把 a.c 编译成 a.o(中间文件)# 规则3:b.o 依赖 b.c
b.o: b.cgcc -c b.c# 规则4:main.o 依赖 main.c
main.o: main.cgcc -c main.c

简单说,Makefile 就像一份「菜谱」:

  • 明确最终要做什么(比如 myprogram 这个程序);
  • 明确需要哪些「原材料」(比如 a.o 这些中间文件);
  • 明确每一步怎么做(比如用什么命令编译,先编译哪个、后编译哪个)。

4. 什么是 make ?

make 是 Linux 自带的一个命令工具(就像 lscd 一样,输入 make 就能运行)。

  • 它的作用很单纯:Makefile 里的规则,然后自动执行命令

比如刚才写好了 Makefile,你在终端敲一句 make,它就会:

  1. 先看最终要生成 myprogram,发现需要 a.ob.omain.o
  2. 检查这些 .o 文件有没有,如果没有,就按规则编译 a.c 生成 a.o,以此类推;
  3. 最后把所有 .o 文件拼起来,生成 myprogram

如果之后我们只改了 a.c,再敲 make 时,它会聪明地只重新编译 a.omyprogram,其他没改的文件不碰——省时间!

二、如何使用 make/Makefile

1. 如何创建第一个Makefile?

(1) 创建源文件

首先,创建一个简单的C语言程序code.c
在这里插入图片描述

// code.c
#include <stdio.h>
int main() {printf("Hello, Makefile!\n");return 0;
}

在这里插入图片描述

(2) 创建Makefile文件

在相同目录下创建Makefile文件(注意大小写和文件名):

touch Makefile
vim Makefile

在这里插入图片描述

(3) 编写Makefile规则

Makefile中写入以下内容:

# 目标: 依赖文件
code: code.cgcc -o code code.c  # 注意这行必须以Tab键开头

在这里插入图片描述

(4) 执行make命令

在终端输入make,它会自动读取Makefile并执行编译:

在这里插入图片描述

$ make
gcc -o code code.c

此时,当前目录下会生成可执行文件code,运行它:

$ ./code

在这里插入图片描述

2. 依赖关系与命令

(1) 依赖关系

在Makefile中,每行规则的基本格式是:

目标文件: 依赖文件列表命令1命令2...
  • 目标文件:要生成的文件(如code
  • 依赖文件:生成目标所需要的文件(如code.c
  • 命令:生成目标的具体操作(如gcc -o code code.c

(2) make的执行逻辑

当你执行make时,它会:

  1. 检查目标文件是否存在
  2. 如果不存在,或依赖文件比目标文件更新(即依赖文件被修改过)
  3. 则执行对应的命令来生成/更新目标文件

(3) 为什么需要依赖关系?

假设你修改了code.c,再次执行make时:

$ make
make: 'code' is up to date.

make会自动判断:由于code已存在且code.c未修改,因此无需重新编译——这就是Makefile智能编译能力,大大节省时间!

在这里插入图片描述

3. 如何清理编译结果?

(1) 添加clean目标

Makefile中新增一个clean目标:

code: code.cgcc -o code code.c.PHONY: clean
clean:rm -f code  # 删除生成的可执行文件

在这里插入图片描述

(2) 执行清理命令

当需要删除编译结果时,执行:

$ make clean
rm -f code

在这里插入图片描述

(3) .PHONY 标签的作用

.PHONY用于声明clean是一个伪目标(即它不是真正的文件名),防止目录中真的存在名为clean的文件导致冲突。

4. Makefile的执行规则

(1) 默认执行第一个目标

当你直接输入make时,它会执行Makefile中的第一个目标(如上面的code)。

(2) 如何执行其他目标?

通过指定目标名来执行特定规则,例如:

$ make clean    # 只执行clean目标下的命令
$ make code     # 只执行code目标下的命令(等同于直接make)

(3) 多个目标的执行顺序

Makefile会按照依赖关系自动处理执行顺序。例如:

all: program1 program2program1: file1.cgcc -o program1 file1.cprogram2: file2.cgcc -o program2 file2.c

执行make all时,会先编译program1,再编译program2

5. 常见错误与注意事项

(1) 命令行必须以Tab键开头

在Makefile中,命令行必须以Tab键开头,否则会报错:

# 错误示例(使用空格缩进)
code: code.cgcc -o code code.c  # 错误!会提示"*** missing separator.  Stop."# 正确示例(使用Tab键缩进)
code: code.cgcc -o code code.c

(2) 文件名大小写敏感

Makefilemakefile是不同的文件,GNU make默认优先读取GNUmakefile,然后是makefile,最后是Makefile。建议统一使用Makefile

三、伪目标是什么,为什么伪目标总被执行?

1. make如何判断是否需要重新编译?

当我们执行make时,它会检查两件事:

  • 目标文件(如code)是否存在
  • 依赖文件(如code.c)的修改时间是否比目标文件更新

如果目标文件不存在,或依赖文件被修改过(修改时间比目标文件晚),make就会执行命令重新生成目标文件。

举个例子

# Makefile
code: code.cgcc -o code code.c
  • 第一次执行make:生成code文件
  • 第二次执行makemake发现code已存在,且code.c没修改,==“code”已是最新 ==
    在这里插入图片描述
$ make
gcc -o code code.c  # 第一次执行,生成code
$ make
make: “code”已是最新  

2. 什么是伪目标(.PHONY)?

(1)定义:

伪目标是用.PHONY声明的目标,它不代表一个真实的文件,而是一个动作名称

(2) 常见用途:

  • 清理编译结果(如clean目标)
  • 执行测试(如test目标)
  • 批量操作(如all目标)

示例:

.PHONY: clean  # 声明clean是伪目标
clean:rm -f code  # 删除生成的可执行文件

3. 为什么伪目标总是被执行?

(1)核心原因:

伪目标不对应真实文件,因此make无法检查它的修改时间
当你执行make 伪目标名时,make会直接执行对应的命令,而不做任何检查。

(2) 对比实验:

情况1:普通目标(无.PHONY)

hello:echo "Hello, World!"

执行效果:

$ make hello
echo "Hello, World!"
Hello, World!
$ make hello
make: 'hello' is up to date.  # 第二次不会执行,因为默认认为hello是文件且已存在

情况2:伪目标(有.PHONY)

.PHONY: hello
hello:echo "Hello, World!"

执行效果:

$ make hello
echo "Hello, World!"
Hello, World!
$ make hello
echo "Hello, World!"  # 每次都会执行!因为hello是伪目标
Hello, World!

4. 为什么需要伪目标?

(1) 避免与文件冲突

如果目录中真的有一个名为clean的文件,会发生什么?

# 错误示例:没有.PHONY的clean目标
clean:rm -f code

此时执行make cleanmake会认为:“clean文件已存在,无需执行任何命令”——这显然不是你想要的!

(2) 强制执行命令

对于cleantest这类目标,你总是希望它们无条件执行而不是根据文件时间判断

5. 常见伪目标示例

1. clean目标(最常见)

.PHONY: clean
clean:rm -f *.o       # 删除所有.o文件rm -f executable  # 删除可执行文件

2. all目标(批量编译多个程序)

.PHONY: all
all: program1 program2  # 先编译program1,再编译program2program1: file1.cgcc -o program1 file1.cprogram2: file2.cgcc -o program2 file2.c

3. install目标(安装程序到系统)

.PHONY: install
install:cp program /usr/bin/  # 复制程序到系统目录

以上就是这篇博客的全部内容,下一篇我们将继续探索Linux的更多精彩内容。

我的个人主页
欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的Linux知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12879535.html?spm=1001.2014.3001.5482

非常感谢您的阅读,喜欢的话记得三连哦

在这里插入图片描述

http://www.xdnf.cn/news/1126171.html

相关文章:

  • 12.6 Google黑科技GShard:6000亿参数MoE模型如何突破显存限制?
  • 导出内存溢出案例分析
  • 学习秒杀系统-实现秒杀功能(商品列表,商品详情,基本秒杀功能实现,订单详情)
  • JavaScript认识+JQuery的依赖引用
  • ethers.js-8-bigNmber和callstatic模拟
  • 2025年最新香港站群服务器租用价格参考
  • 探索阿里云ESA:开启边缘安全加速新时代
  • 基于Ruoyi和PostgreSQL的统一POI分类后台管理实战
  • 论文阅读:arxiv 2025 A Survey on Data Contamination for Large Language Models
  • 从12kW到800V,AI服务器电源架构变革下,功率器件如何解题?
  • redisson 设置了过期时间,会自动续期吗
  • 【网络安全】大型语言模型(LLMs)及其应用的红队演练指南
  • 经典排序算法之希尔排序
  • docker 方式gost代理搭建以及代理链实施
  • HTTP常见误区
  • 具身智能零碎知识点(六):VAE 核心解密:重参数化技巧(Reparameterization Trick)到底在干啥?
  • 第二章 OB 存储引擎高级技术
  • JavaScript进阶篇——第四章 解构赋值(完全版)
  • IT岗位任职资格体系及发展通道——研发岗位任职资格标准体系
  • 进程探秘:从 PCB 到 fork 的核心原理之旅
  • 从零开始的云计算生活——第三十二天,四面楚歌,HAProxy负载均衡
  • 测试tcpdump,分析tcp协议
  • JAVA学习笔记 使用notepad++开发JAVA-003
  • Bootstrap-HTML(七)Bootstrap在线图标的引用方法
  • SELinux 详细解析
  • 【安卓笔记】RxJava之flatMap的使用
  • python原生处理properties文件
  • 第十四章 Stream API
  • 【第二章自定义功能菜单_MenuItemAttribute_顶部菜单栏(本章进度1/7)】
  • 零售企业用户行为数据画像的授权边界界定:合规与风险防范