1.概念
1.1 什么是makefile
Makefile 是一种文本文件,用于描述软件项目的构建规则和依赖关系,通常用于自动化软件构建过程。它包含了一系列规则和指令,告诉构建系统如何编译和链接源代码文件以生成最终的可执行文件、库文件或者其他目标文件。
1.2 使用makefile的优点
Makefile 主要用于管理大型项目的构建过程,它可以:
- 自动检测源代码文件的变化,只编译发生变化的文件,以提高构建效率。
- 根据依赖关系自动构建所需的目标文件,无需手动管理编译顺序。
- 支持通过简单的命令来进行构建、清理和其他任务,提高了项目的可维护性和可重复性。
- 可以轻松地添加自定义的构建规则和操作,满足特定项目的需求。
Makefile 使用一种称为 Make 的构建工具来解析和执行其中的规则。Make 工具会根据 Makefile 中定义的规则和依赖关系,确定需要重新构建的目标文件,并执行相应的命令来完成构建过程。
2.演变的过程
2.1 gcc编译
在我们平常linux下编译文件的时候通过gcc全部编译链接生成可执行的文件
这样编译导致的后果是每次改动某一个代码,其他文件都要跟着编译,非常的繁琐每次,那我们有什么更简单的方法吗,有
2.2 gcc单个文件编译后链接
很明显,大家已经看到了,虽然有一点优化,但不多。这也是不可取的。这时候在linux编译链接的时候就出现了makefile文件。
2.3 Makefile的工作原理
Makefile 的工作原理主要涉及两个部分:规则(rules)和依赖关系(dependencies)。Makefile 中包含了一系列规则,每个规则描述了如何生成一个或多个目标文件,并指定了生成目标文件所需的依赖文件和生成命令。Make 工具会根据这些规则来自动执行构建过程。
下面是 Makefile 的工作原理的详细解释:
1.目标和依赖关系定义:
- Makefile 中的每个规则由一个或多个目标(targets)和其对应的依赖关系(dependencies)组成。目标通常是文件名,代表生成的目标文件或执行的操作。依赖关系是目标文件所依赖的文件列表,通常是源文件或其他目标文件。
- 每个规则的格式通常为
target: dependenciescommand
target
是目标文件或操作的名称,dependencies
是生成target
所需的依赖文件列表,command
是生成target
的命令。
检查文件时间戳:- 在执行 Makefile 时,Make 工具会检查每个目标文件和其依赖文件的时间戳,以确定哪些文件需要重新生成。
- 如果目标文件不存在,或者其时间戳早于任何一个依赖文件的时间戳,那么 Make 工具会执行生成目标文件的命令。
-
执行生成命令:
- 当确定需要重新生成目标文件时,Make 工具会执行该目标的生成命令。生成命令通常是编译源文件、链接目标文件或执行其他操作的命令。
- Make 工具会按照 Makefile 中规定的顺序依次执行每个目标的生成命令,直到所有目标文件都被成功生成。
-
递归处理依赖:
- 如果一个目标文件的依赖文件也是其他目标文件,则 Make 工具会递归处理这些依赖关系,确保所有依赖文件都被生成。
-
增量构建:
- Make 工具会根据文件的时间戳判断哪些文件需要重新生成,从而实现增量构建。只有发生了变化的文件及其依赖文件才会重新生成,提高了构建效率。
-
通过这样的方式,Makefile 提供了一种简洁而有效的方法来管理项目的构建过程,自动化了源代码的编译、链接和其他构建操作,使得项目的开发和维护更加高效和可靠。
2.4 初阶Makefile文件
- 这个版本的Makefile虽然有了一定的用途,但还是无法解决文件太多的问题。如何解决这一难题呢,我们接着向下看
-
2.5 中阶Makefile文件
虽然这个看起来已经比较完善了,但是依旧在文件太多的时候,会出现很多编译.c的文件,那有没有什么办法,让其不用这么麻烦呢-
2.6 后阶Makefile文件
-
CXX = gcc TARGET = aio OBJ = BintrayTree.o Queue.o test.oCXXFLAGS = -c -Wall$(TARGET) : $(OBJ) # $@ 表示目标文件 # $^ 所依赖文件$(CXX) -o $@ $^ # % 是一个通配符,用于匹配任意长度的字符序列 %.o : %.c # $< 第一个依赖文件$(CXX) $(CXXFLAGS) $< -o $@# .PHONY防止出现于clear重名的文件而发生歧义 .PHONY: clear # make clear 执行下面的命令(删除后缀.o和编译链接后的目标文件) clear:rm -r *.o $(TARGET)
-
这样我们,其实已经足够优化了,但是我们任然有可优化的地方,比如,我们可不可不列出链接的依赖文件,当然可以的
-
2.7 终极版本
- 到这里,我们就可以很清楚的认识到Makefile的优化过程
CXX = gcc TARGET = aio #将后缀为.c的文件放入windcard中 SRC = $(wildcard *.c) #路径替换,将SRC中的.c替换为.o OBJ = $(patsubst %.c, %.o, $(SRC))CXXFLAGS = -c -Wall$(TARGET) : $(OBJ) # $@ 表示目标文件 # $^ 所依赖文件$(CXX) -o $@ $^ # % 是一个通配符,用于匹配任意长度的字符序列 %.o : %.c # $< 第一个依赖文件$(CXX) $(CXXFLAGS) $< -o $@# .PHONY防止出现于clear重名的文件而发生歧义 .PHONY: clear # make clear 执行下面的命令(删除后缀.o和编译链接后的目标文件) clear:rm -r *.o $(TARGET)
3,Makefile的编码规则 - 我们之前已经有所了解Makefile的编码规则,但是在这里我还是觉得有必要总体讲一下
在编写 Makefile 时,遵循一些编码规则可以使其更加清晰、易于维护和跨平台。下面是一些常见的 Makefile 编码规则:
1.缩进使用Tab键:
Makefile 中命令行必须使用 Tab 键进行缩进,而不是空格。这是因为 Make 工具默认使用 Tab 键作为命令行的缩进标识,使用空格可能会导致 Make 解析错误。
2.目标和依赖关系之间的冒号:
目标(target)和依赖关系(dependencies)之间使用冒号(:)分隔。冒号前面是目标,后面是依赖关系。
3.命令行前面的Tab键:
在每个规则中,命令行必须以 Tab 键开头,表示该命令是该规则的执行命令。除了注释以外,任何其他以 Tab 键开头的行都被视为命令。
4.变量使用大写字母:
为了与命令和目标区分开,通常将变量使用大写字母命名。例如:CC = gcc。
5.使用变量代替硬编码的命令和路径:
使用变量来代替硬编码的命令和路径,使得 Makefile 更加灵活和可移植。例如,将编译器命令使用变量表示:CC = gcc,然后在规则中使用 $(CC) 来引用。 -
6.使用伪目标:
对于不产生实际文件的操作(如清理、安装等),使用伪目标(.PHONY)来定义。这样可以确保即使存在同名文件,也不会误导 Make 工具。例如:.PHONY: clean。
7.注释使用 #:
使用 # 符号来添加注释,使得 Makefile 更具可读性。注释可以帮助理解 Makefile 中每个规则的作用。
8.模块化设计:
将 Makefile 模块化,分成多个小的 Makefile 文件,然后通过 include 指令引入。这样可以提高 Makefile 的可维护性和可读性,减少重复代码。
9.合理使用条件语句:
可以使用条件语句(如 ifeq、ifdef 等)来根据不同的条件执行不同的规则或命令,以实现更灵活的构建过程。
10.跨平台兼容性:
考虑到不同平台下的路径分隔符和命令格式的差异,编写具有跨平台兼容性的 Makefile。可以使用自动化工具或条件语句来处理不同平台下的差异。遵循这些规则可以使 Makefile 更加规范、易读和易于维护,有助于提高项目的构建效率和可靠性。
4,每期一问
-
4.1 上期答案
// 计算树的高度 int getHeight(struct TreeNode* root) {if (root == NULL) {return 0;}int leftHeight = getHeight(root->left);int rightHeight = getHeight(root->right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1; }// 判断是否是平衡二叉树的辅助函数 bool isBalancedHelper(struct TreeNode* root) {if (root == NULL) {return true;}int leftHeight = getHeight(root->left);int rightHeight = getHeight(root->right);if (abs(leftHeight - rightHeight) <= 1 && isBalancedHelper(root->left) && isBalancedHelper(root->right)) {return true;}return false; }// 判断是否是平衡二叉树 bool isBalanced(struct TreeNode* root) {return isBalancedHelper(root); }
4.2 本期问题. - 力扣(LeetCode)