Makefile基础使用
Makefile是一种用于管理和自动化软件编译过程的文本文件。它通常包含了一系列规则这些规则描述了如何根据源代码文件生成可执行文件或者其他目标文件。Makefile的核心概念是规则和依赖关系规则定义了如何生成一个或多个目标文件而依赖关系则指定了生成目标文件所需要的源文件或其他依赖文件。1.环境安装安装 build-essential工具包sudo apt install -y build-essentialbuild-essential 是 Ubuntu 等 Debian 系系统的元软件包集成 gcc、g、make 等编译必备工具。 安装它可一键配齐 C/C 源码编译环境省去逐个安装编译器与构建工具的繁琐操作。VSCode插件2.基础使用2.1 创造项目环境hello.c#include hello.h #include stdio.h void say_hello() { printf(Hello world!\n); }hello.h#ifndef __HELLO_H__ #define __HELLO_H__ void say_hello(); #endifmain.c#include hello.h int main() { say_hello(); return 0; }2.2 为main.c和hello.c编写基本Makefile# Makefile内容通常由以下部分组成 # 目标: 前置依赖 # 需要执行的命令 # 放在第一个的是默认目标 # 目标为编译出main文件依赖main.o和hello.o文件 # 编译的命令为 gcc -o main hello.o main.o main: hello.o main.o gcc -o main hello.o main.o # main.o目标依赖main.c hello.h # 编译命令为gcc -c main.c main.o: main.c hello.h gcc -c main.c # hello.hello.c hello.h # 编译命令为gcc -c hello.c hello.o: hello.c hello.h gcc -c hello.c # clean目标可以清理编译的临时文件 clean: rm -f main main.o hello.o规则是Makefile的构建单元Make工具通过解析这些规则来执行构建过程。1.规则的基本结构我们用空行将Makefile的不同规则划分开来。规则有两行构成第一行为目标和前置依赖二者通过冒号区分开来目标在前前置依赖在后。# 目标: 前置依赖 # 需要执行的命令2.目标本条规则需要生成的目标文件名。3.前置依赖生成目标文件需要的依赖文件列表。4.命令一系列将被Shell执行的命令用于从前置依赖构建目标。需要注意的是Makefile中每个规则的命令必须以一个制表符tab开始而不能是空格。否则会提示“缺失分隔符”。5.gcc的-c参数不仅可以将汇编代码转换为机器码还可以直接将C语言源文件转换为机器码gcc -c main.c就是第二种用法这里省略了-o main.o。默认情况下在指定-c参数时gcc会将与源文件名去掉扩展名再加上后缀.o作为目标文件的名称。2.3 测试执行make命令make clean make ./main2.4 引入变量# 定义变量objects objects : hello.o\ main.o # 在目标中引入变量 main: $(objects) gcc -o main $(objects) main.o: main.c hello.h gcc -c main.c hello.o: hello.c hello.h gcc -c hello.c # clean目标中也可以引入变量 clean: rm -f main $(objects)objects为变量名:的组合相当于C语言中的表示赋值:后面为变量的值\为续行符表示命令或定义延续到下一行。此处的作用是将hello.o和main.o合并为一行此处的定义等价于objects : hello.o main.o。$(变量名)表示获取变量的值2.4 引入make自动推导更改Makefilemake可以根据目标自动加入所需的依赖文件和命令。例如main.o目标会默认将main.c作为依赖加入同时也可以自动推导出编译main.o的命令于是我们的Makefile就可以改成以下内容objects : hello.o\ main.o main: $(objects) gcc -o main $(objects) # 利用make的自动推导 clean: rm -f main $(objects)依赖文件的作用要注意的是虽然这种方式精简Makefile的内容但是当没有显式声明的依赖文件发生更改时Make无法追踪。只有在Makefile中显式声明依赖的头文件才会被追踪当它们发生更改时重新执行make命令会再次执行相应规则的命令。2.5 引入伪目标伪目标并不代表实际的文件名它们更多的是行为或动作的标识符。伪目标并不生成具体文件。PHONY目标①.PHONY是Makefile中一个特殊的目标用于声明其它目标是伪目标。② 语法.PHONY:伪目标名称③ 目标为clean的规则没有前置依赖这是因为它是用来执行清理操作的并不是要生成名为clean的文件因此不需要前置依赖。我们可以将clean声明为伪目标。# 定义变量objects objects : hello.o\ main.o # 在目标中引入变量 main: $(objects) gcc -o main $(objects) main.o: hello.h hello.o: hello.h # 声明伪目标 .PHONY: clean # clean目标中也可以引入变量 clean: rm -f main $(objects)为什么需要伪目标我们发现执行make clean并没有像我们预想的那样删除文件而是告诉我们“clean”已是最新。这是因为make将clean作为普通目标处理它先检查clean的依赖不存在然后发现clean文件已存在且没有依赖更新因为不存在自然不需要更新因此不会执行规则下的命令并在控制台输出以上内容。显然这不是我们期望的行为。将某些不生成目标文件的行为或动作如清理、安装声明为伪目标可以确保无条件执行规则下的命令。即便执行make命令时当前目录下存在与目标同名的文件依然可以得到我们期望的效果。2.6 忽略错误rm前面的-告诉make如果该命令执行失败不要停止执行剩余的过程即忽略错误。# 定义变量objects objects : hello.o\ main.o # 在目标中引入变量 main: $(objects) gcc -o main $(objects) main.o: hello.h hello.o: hello.h # 声明伪目标 .PHONY: clean # clean目标中也可以引入变量 clean: -rm main $(objects)2.7 目标名和命令中输出文件名的关系# 定义变量objects objects : hello.o\ main.o # 在目标中引入变量 main: $(objects) gcc -o main123 $(objects) main.o: hello.h hello.o: hello.h # 声明伪目标 .PHONY: clean # clean目标中也可以引入变量 clean: -rm main123 $(objects)当前目录下不存在名为main的文件只有名为mian123的文件可以得出结论规则中的命令决定了生成目标文件的名称。目标名并不影响目标文件名。我们发现make没有提示目标文件已是最新而是重新执行了gcc -o main123 hello.o main.o。这是因为make会按照目标名称在当前目录下追踪目标文件如果不存在与目标同名的文件会再次执行规则下的命令。make输出的文件名取决于规则下的命令而目标名称决定make追踪的目标文件名。如果二者不一致make就会认为目标文件不存在而不断执行命令。我们应确保命令生成的目标文件名和目标名一致。vscode-makefile-term插件在插件市场搜索并安装vscode-makefile-term插件完成后在VScode中打开Makefile文件可以看到每个target上方都出现了执行按钮。