Linux 开发工具(四) 熟悉自动化构建 make/Makefile
目录一. make/makefile 的概念1.1 Makefile的基本语法1.2 代码示例二. make/makefile 的使用2.1 文件的清理2.2 make 的使用细节2.2.1 make/make【目标名】2.2.2 make 的执行规则2.2.2.1 stat2.3 makefile 的使用细节2.3.1 .PHONY2.3.2 makefile 的依赖关系推导三. make/makefile的进阶使用3.1 变量的定义和使用3.1.1 变量实现代码的编译3.1.2 特殊变量 $ $^3.2 多文件情况3.2.1 版本13.2.2 版本2一. make/makefile 的概念make是 Linux 中的一个自动化构建工具主要用于自动执行编译、链接等操作。你可以把它理解为帮我们自动调用 gcc/g不用每次手动输入一大堆编译命令。makefile是一个描述构建规则的文件告诉 make哪些文件需要生成生成文件需要依赖哪些文件应该执行什么命令。1.1 Makefile的基本语法目标文件: 依赖文件1 依赖文件2 ...依赖列表可为空 依赖方法1 依赖方法2 ...myproc: myproc.cpp g myproc.cpp -o myproc myproc → 目标文件最终要生成的文件 myproc.cpp → 依赖文件生成目标所需要的文件 : → 依赖关系表示 myproc 依赖 myproc.cpp g ... → 依赖方法告诉 make 如何生成目标文件1.2 代码示例# 使用相对路径 bin/myproc: src/myproc.cpp mkdir -p bin g -o bin/myproc src/myproc.cpp # 也可以使用绝对路径 # /home/lzx/myproc: /home/lzx/code/myproc.cpp # g -o /home/lzx/myproc /home/lzx/code/myproc.cpp# 默认读取当前目录中的 Makefile make # -f指定 Makefile 文件路径不切换工作目录 make -f /home/lzx/project/Makefile # -C指定工作目录切换目录后执行 make make -C /home/lzx/project # -C 和 -f 组合使用 make -C /home/lzx/project -f Makefile二. make/makefile 的使用2.1 文件的清理当我们完成编译想要清除编译好的可执行文件时可以在makefile输入以下指令2.2 make 的使用细节2.2.1 make/make【目标名】make默认选择 Makefile 中的第一个目标进行构建但我们也可以通过make【目标名】指定其他目标。#Makefile myproc: myproc.cpp g myproc.cpp -o myproc test: test.cpp g test.cpp -o test hello: hello.cpp g hello.cpp -o hellomake # 默认构建第一个目标 myproc make test # 指定构建 test make hello # 指定构建 hello2.2.2 make 的执行规则make也并不是一定会对目标进行构建为什么呢因为 make的作用不仅是执行编译命令还会根据目标文件和依赖文件的时间戳判断是否需要重新构建。myproc: myproc.cpp g myproc.cpp -o myproc情况make 的行为myproc不存在执行命令生成目标文件myproc.cpp比myproc更新重新编译更新目标文件myproc已存在且比myproc.cpp更新不重新编译认为目标已经是最新的2.2.2.1 stat我们之前提到 make 会根据目标文件和依赖文件的时间戳来判断是否构建指令stat可以让我们更直观的理解。stat是 Linux 中用于查看文件或目录详细属性信息的命令。时间含义什么情况下会变化Accessatime最近一次访问文件内容的时间读取文件内容时时间可能更新Modifymtime文件内容最后修改的时间修改文件内容时更新Changectime文件状态最后变化的时间修改权限、所有者、硬链接数等信息时更新修改文件内容也会更新Birth文件创建时间表示文件创建的时间前提是文件系统支持记录make 正是基于目标文件、依赖文件的修改时间(Modify)来判断的。2.3 makefile 的使用细节2.3.1 .PHONY.PHONY 用于声明伪目标告诉make这个目标不是一个真正需要生成的文件而是一个需要执行的任务。我们在之前提到了make的执行规则要判断执行文件和目标文件的时间戳而.PHONY 的作用就是告诉 make指定的目标是伪目标不对应一个需要生成的实际文件因此不需要通过检查同名目标文件的时间戳来判断是否执行命令。2.3.2 makefile 的依赖关系推导make从上往下寻找目标的依赖关系找到最底层的依赖后再从下往上依次执行构建命令。#Makefile myproc: myproc.o gcc myproc.o -o myproc myproc.o: myproc.s gcc -c myproc.s -o myproc.o myproc.s: myproc.i gcc -S myproc.i -o myproc.s myproc.i: myproc.c gcc -E myproc.c -o myproc.i① 从上往下查找依赖入栈myproc ↓ myproc.o ↓ myproc.s ↓ myproc.i ↓ myproc.c源文件已存在② 从下往上构建目标出栈gcc -E myproc.c -o myproc.i # 预处理 gcc -S myproc.i -o myproc.s # 编译 gcc -c myproc.s -o myproc.o # 汇编 gcc myproc.o -o myproc # 链接三. make/makefile的进阶使用3.1 变量的定义和使用Makefile 中可以使用定义变量通过$(变量名)引用变量。我们会发现每行命令前面都有符号的作用主要是取消命令的回显如果不加结果如下3.1.1 变量实现代码的编译3.1.2 特殊变量 $ $^$和$^都是Makefile 中的自动变量用于简化构建命令。自动变量含义$当前规则的目标文件$^当前规则的所有依赖文件去重3.2 多文件情况3.2.1 版本1BIN myproc CC g OBJ myproc.o FLAGS -c # 1. 默认目标是 myproc依赖 myproc.o $(BIN): $(OBJ) $(CC) $^ -o $ # 2. % 是模式匹配符用于匹配文件名中的相同部分 # 例如make 需要生成 myproc.o # %.o → myproc.o此时 % 匹配到 myproc # %.cpp → myproc.cpp将 myproc 代入依赖文件名 # 因此推导出myproc.o 依赖 myproc.cpp %.o: %.cpp $(CC) $(FLAGS) $ -o $ # 3. $ 是自动变量表示当前规则的第一个依赖文件 # 这里 $ → myproc.cpp # 而 $ → myproc.o表示当前目标文件 # 所以上面的命令展开后为 # g -c myproc.cpp -o myproc.o%模式匹配符根据目标文件名匹配相同部分推导出依赖文件名。$自动变量表示当前规则的第一个依赖文件。$自动变量表示当前规则的目标文件。3.2.2 版本2# 1. 定义变量 BIN myproc # 最终生成的可执行文件 CC g # 编译器 SRC $(shell ls *.cpp) # 获取当前目录下所有 .cpp 文件 # SRC $(wildcard *.cpp) # 另一种写法更推荐 OBJ $(SRC:.cpp.o) # 将所有 .cpp 后缀替换为 .o LFLAGS -o # 指定输出文件名 FLAGS -c # 只编译生成 .o不链接 RM rm -f # 删除命令 # 2. 定义最终目标及其依赖关系 # $(BIN) → myproc # $(OBJ) → main.o add.o sub.o假设存在这三个源文件 # 表示 myproc 依赖这些 .o 文件 $(BIN): $(OBJ) $(CC) $^ $(LFLAGS) $ echo linking... $^ to $ # $^ → 所有依赖文件即 main.o add.o sub.o # $ → 当前目标文件即 myproc # 实际执行g main.o add.o sub.o -o myproc # 3. 定义模式规则分别生成 .o 文件 # % 是模式匹配符 # 需要生成 main.o 时% 匹配 main推导出 main.cpp # 需要生成 add.o 时% 匹配 add推导出 add.cpp # 每次匹配都对应一条独立的编译规则 %.o: %.cpp $(CC) $(FLAGS) $ echo compiling... $ to $ # $ → 当前规则的第一个依赖文件例如 main.cpp # $ → 当前规则的目标文件例如 main.o # 实际执行g -c main.cpp # 默认生成 main.o # 表示不回显命令本身1.$( )引用变量或调用函数引用变量$(CC)获取变量CC的值。调用函数例如$(wildcard *.cpp)。示例CC g那么$(CC)就会展开为g。2.wildcard使用 Makefile 自带的功能查找文件示例SRC $(wildcard *.cpp)。*.cpp表示匹配所有以.cpp结尾的文件。查找结果会赋值给变量SRC。按照make的工作目录查找而不是 Makefile 所在的目录。3.shell执行 Shell 命令并获取输出示例SRC $(shell ls *.cpp)。$(shell ...)会执行括号中的 Shell 命令并获取其标准输出。ls *.cpp用于列出匹配的.cpp文件结果赋值给SRC。同样以make的工作目录为基