tartarus's bolg tartarus's bolg
  • Linux and Unix Guide
  • CMake
  • gcc
  • gdb
  • bash
  • GNU Make
  • DDCA-ETH
  • CS106L
  • CS144
  • NJU PA
  • NJU OS(jyy)
  • C
  • C++
  • Python
  • reveal-md
  • LaTex
  • Paper Reading
  • TBD
  • Linux and Unix Guide
  • CMake
  • gcc
  • gdb
  • bash
  • GNU Make
  • DDCA-ETH
  • CS106L
  • CS144
  • NJU PA
  • NJU OS(jyy)
  • C
  • C++
  • Python
  • reveal-md
  • LaTex
  • Paper Reading
  • TBD
  • pdb

  • make

    • 1.0 声明😁
    • 2.0 makefile介绍
    • 3.0 书写makefile
    • 3.0 书写规则
      • 书写规则
      • 在规则中使用通配符
      • 文件搜索
      • 伪目标
      • 多目标
      • 静态模式
      • 自动生成依赖性
    • 4.0 书写命令
    • 5.0 使用变量
    • 6.0 使用条件判断
    • 7.0 使用函数
    • 8.0 make的运行
  • cmake

  • Linux and Unix

  • Basic_Software
  • make
tartarus
2023-08-29
目录

3.0 书写规则

# 书写规则

规则包含两个部分,一个是依赖关系 (target ... : prerequisites ...),一个是生成目标的方法 (recipe)。

# 在规则中使用通配符

make 支持三个通配符: * , ? 和 ~ 。这是和 Unix 的 B-Shell 是相同的。

波浪号( ~ )字符在文件名中也有比较特殊的用途:

  • 如果是 ~/test ,这就表示当前用户的 $HOME 目录下的 test 目录。
  • 而 ~ningxu/test 则表示用户 ningxu 的宿主目录下的 test` 目录。
    参考链接 Meaning of Tilde in Linux Bash (opens new window)

通配符 * 代替了你一系列的文件,如 *.c 表示所有后缀为 c 的文件。一个需要我们注意的是,如果我们的文件名中有通配符,如: * ,那么可以用转义字符 \ ,如 \* 来表示真实的 * 字符,而不是任意长度的字符串。

clean:
    rm -f *.o
1
2

值得注意的是:通配符同样可以用在变量中,但 *.o 并不会在变量中被展开:

objects = *.o
1

objects 的值就是 *.o 。Makefile 中的变量其实就是 C/C++ 中的宏。如果你要让通配符在变量中展开,也就是让 objects 的值是所有 .o 的文件名的集合,那么,你可以这样:

objects := $(wildcard *.o)
1

# 文件搜索

文件搜索路径的设置方法:

方法一:
如果没有指明变量 VPATH,make 只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make 就会在当前目录找不到的情况下,到所指定的目录中去找寻文件了。

VPATH = src:../headers
1

上面的定义指定两个目录,“src” 和 “../headers”,make 会按照这个顺序进行搜索。目录由 “冒号” 分隔。(当然,当前目录永远是最高优先搜索的地方)

方法二:
vpath 可以指定不同的文件在不同的搜索目录中,它有三种使用方法:

vpath <pattern> <directories>
为符合模式 <pattern> 的文件指定搜索目录 <directories> 。

vpath <pattern>
清除符合模式 <pattern> 的文件的搜索目录。

vpath
清除所有已被设置好了的文件搜索目录。
vpath 使用方法中的 <pattern> 需要包含 % 字符。 % 的意思是匹配零或若干字符.

vpath %.h ../headers
该语句表示,要求 make 在 ../headers 目录下搜索所有以 .h 结尾的文件。(如果某文件在当前目录没有找到的话)

如果连续的 vpath 语句中出现了相同的 <pattern> ,或是被重复了的 <pattern> ,那么,make 会按照 vpath 语句的先后顺序来执行搜索。如:

vpath %.c foo
vpath %   blish
vpath %.c bar
1
2
3

其表示 .c 结尾的文件,先在 “foo” 目录,然后是 “blish”,最后是 “bar” 目录。

# 伪目标

使用一个特殊的标记 “.PHONY” 来显式地指明一个目标是 “伪目标”,向 make 说明,不管是否有这个文件,这个目标就是 “伪目标”,伪目标永远会被执行。

伪目标最常见的使用方法就是声明 clean 为伪目标,这样无论是否有 clean 这个文件存在,目标都会被执行。

.PHONY : clean
clean :
    rm *.o temp
1
2
3

伪目标的其他使用方法:

  1. 使用伪目标作为 target 生成若干个可执行文件
    伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为 “默认目标”,只要将其放在第一个。
all : prog1 prog2 prog3
.PHONY : all

prog1 : prog1.o utils.o
    cc -o prog1 prog1.o utils.o

prog2 : prog2.o
    cc -o prog2 prog2.o

prog3 : prog3.o sort.o utils.o
    cc -o prog3 prog3.o sort.o utils.o
1
2
3
4
5
6
7
8
9
10
11

Makefile 中的第一个目标会被作为其默认目标。我们声明了一个 “all” 的伪目标,其依赖于其它三个目标。因为伪目标总是被执行,所以其它三个目标的规则总是会被决议。也就达到了我们一口气生成多个目标的目的。

  1. 伪目标也可以成为依赖 (prerequisites):
.PHONY : cleanall cleanobj cleandiff

cleanall : cleanobj cleandiff
    rm program

cleanobj :
    rm *.o

cleandiff :
    rm *.diff
1
2
3
4
5
6
7
8
9
10

# 多目标

Makefile 的规则中的目标可以不止一个,其支持多目标,有可能我们的多个目标同时依赖于一个文件,并且其生成的命令大体类似。于是我们就能把其合并起来。

bigoutput littleoutput : text.g
    generate text.g -$(subst output,,$@) > $@
1
2

$@ 表示目标的集合,就像一个数组, $@ 依次取出目标,并执于命令。

等价于:

bigoutput : text.g
    generate text.g -big > bigoutput
littleoutput : text.g
    generate text.g -little > littleoutput
1
2
3
4

# 静态模式

静态模式可以更容易的定义多目标,语法为:

<targets ...> : <target-pattern> : <prereq-patterns ...>
    <commands>
    ...
1
2
3
  • targets 定义了一系列的目标文件,可以有通配符。是目标的一个集合。
  • target-pattern 是指明了 targets 的模式,也就是的目标集模式 (所有的目标都必须满足该模式)。
  • prereq-patterns 是目标的依赖模式,它对 target-pattern 形成的模式再进行一次依赖目标的定义。

如果我们的 <target-pattern> 定义成 %.o ,意思是 <target> 集合的所有文件都是以 .o 结尾的,而如果我们的 <prereq-patterns> 定义成 %.c ,意思是对 <target-pattern> 所形成的目标集进行二次定义,其计算方法是,取 <target-pattern> 模式中的 % (也就是去掉了 .o 这个结尾),并为其加上 .c 这个结尾,形成的新集合。

objects = foo.o bar.o

all: $(objects)

$(objects): %.o: %.c
    $(CC) -c $(CFLAGS) $< -o $@
1
2
3
4
5
6
  • 目标模式 %.o 表示 $(objects) 中 .o 所有目标都是以 .o 结尾,也就是 foo.o , bar.o
  • 依赖模式 %.c 表示 %.o 的 % ,也就是 foo , bar ,并为其加下 .c 的后缀,于是,我们的依赖目标就是 foo.c , bar.c 。
  • $< 和 $@ 则是自动化变量, $< 表示第一个依赖文件, $@ 表示目标集(也就是 foo.o , bar.o )

$@ 表示目标的集合,就像一个数组, $@ 依次取出目标,并执于命令,上面的规则展开后就得到:

foo.o : foo.c
    $(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
    $(CC) -c $(CFLAGS) bar.c -o bar.o
1
2
3
4

“静态模式规则” 的用法很灵活,如果用得好,那会是一个很强大的功能。再看一个例子:

files = foo.elc bar.o lose.o

$(filter %.o,$(files)): %.o: %.c
    $(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc,$(files)): %.elc: %.el
    emacs -f batch-byte-compile $<
1
2
3
4
5
6

$(filter %.o,$(files)) 表示调用 Makefile 的 filter 函数,过滤 $(files) 集,只要其中模式为 “%.o” 的内容

# 自动生成依赖性

TBD: 自动生成依赖性我后续会参考 nemu 中 makefile 的方法来添加.

上次更新: 12/27/2023, 8:55:47 AM
3.0 书写makefile
4.0 书写命令

← 3.0 书写makefile 4.0 书写命令→

Theme by Vdoing | Copyright © 2023-2023 tartarus | CC BY-NC-SA 4.0
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式