Makefile学习记录

2025-2-23|2025-2-23
FollyCoolly
FollyCoolly
type
status
date
slug
summary
tags
category
icon
password

make 简介

The make utility automatically determines which pieces of a large program need to be recompiled, and issues commands to recompile them. 

Makefile 结构

makefile 的内容由多个 rule 组成。
rule 大概长这样:

rule 相关概念

target

可以是一个object file(目标文件),也可以是一个可执行文件,还可以是一个标签(label)。对于标签这种特性,在后续的“伪目标”章节中会有叙述。

prerequisites

生成该target所依赖的文件和/或target。

recipe

该target要执行的命令(任意的shell命令)。
prerequisites中如果有一个以上的文件比target文件要新的话,recipe所定义的命令就会被执行。

make 工作原理

GNU的make工作时的执行步骤如下:
  1. 读入所有的Makefile。
  1. 读入被include的其它Makefile。
  1. 初始化文件中的变量。
  1. 推导隐式规则,并分析所有规则。
  1. 为所有的目标文件创建依赖关系链。
  1. 根据依赖关系,决定哪些目标要重新生成。
  1. 执行生成命令。
默认的情况下,make命令会在当前目录下按顺序寻找文件名为 GNUmakefile 、 makefile 和 Makefile 的文件。在这三个文件名中,最好使用 Makefile 这个文件名,因为这个文件名在排序上靠近其它比较重要的文件,比如 README

包含其它 Makefile

在Makefile使用 include 指令可以把别的Makefile包含进来, ,被包含的文件会原模原样的放在当前文件的包含位置。 环境变量 .INCLUDE_DIRS 包含当前 make 会寻找的目录列表。你应当避免使用命令行参数 -I 来寻找以上这些默认目录,否则会使得 make “忘掉”所有已经设定的包含目录,包括默认目录。
如果你的当前环境中定义了环境变量 MAKEFILES ,那么make会把这个变量中的值做一个类似于 include 的动作。这个变量中的值是其它的Makefile,用空格分隔。只是,它和 include 不同的是,从这个环境变量中引入的Makefile的“默认目标”(the default goal)不会起作用,如果环境变量中定义的文件发现错误,make也会不理。建议不要使用这个环境变量。

通配符

make支持三个通配符: * , ? 和 ~ 。这是和Unix的B-Shell是相同的。
如果我们的文件名中有通配符,如: * ,那么可以用转义字符 \ ,如 \* 来表示真实的 * 字符,而不是任意长度的字符串。 波浪号( ~ )字符在文件名中也有比较特殊的用途。如果是 ~/test ,这就表示当前用户的 $HOME 目录下的test目录。而 ~user/test 则表示用户 user 的宿主目录下的 test 目录。 通配符同样可以用在变量中。但是在变量中不会展开,因为变量类似于 C/C++ 中的宏。 注意以下两个写法的区别:

伪目标

“伪目标”并不是一个文件,只是一个标签,由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过显式地指明这个“目标”才能让其生效。
为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显式地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”。
伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为“默认目标”,只要将其放在第一个。

条件判断

条件表达式的语法为:
以及:
<conditional-directive>可以是
  • ifeq
  • ifneq
  • ifdef
  • ifndef

函数调用

函数调用,很像变量的使用,也是以 $ 来标识的,其语法如下:
$(<function> <arguments>)
或是:
${<function> <arguments>}
这里, <function> 就是函数名,make支持的函数不多。 <arguments> 为函数的参数,参数间以逗号 , 分隔,而函数名和参数之间以“空格”分隔。函数调用以 $ 开头,以圆括号或花括号把函数名和参数括起。感觉很像一个变量,是不是?函数中的参数可以使用变量,为了风格的统一,函数和变量的括号最好一样,如使用 $(subst a,b,$(x)) 这样的形式,而不是 $(subst a,b, ${x}) 的形式。因为统一会更清楚,也会减少一些不必要的麻烦。

用法细节

换行

使用反斜杠( \ )

定义变量

为了makefile的易维护,在makefile中我们可以使用变量。
通过$(变量名)引用变量。

自动推导

只要make看到一个 .o 文件,它就会自动的把 .c 文件加在依赖关系中,如果make找到一个 whatever.o ,那么 whatever.c 就会是 whatever.o 的依赖文件。并且 cc -c whatever.c 也会被推导出来。

在不同位置声明target的依赖

clean

每个Makefile中都应该写一个清空目标文件( .o )和可执行文件的规则,这不仅便于重编译,也很利于保持文件的清洁。
前面说过, .PHONY 表示 clean 是一个“伪目标”。而在 rm 命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。

指定 makefile

 

参考资料

 
 
 
 
 
[解决]检测到 localhost 代理配置,但未镜像到 WSL。NAT 模式下的 WSL 不支持 localhost 代理。LLVM 项目中的常见数据结构
Loading...