note/gnu-make

GNU Make

相关链接: isaac/Makefile

我曾经十分瞧不起 Make。因为乍一看正常的 Makefile,这不就是给 shell 脚本加几个标签,然后运行吗?

直到自己稍微会用一些了,才感叹到 Make 的强大。

Do not ask whether a statement is true until you know what it means.

— Erret Bishop

Make 的基本用法中构造的对象是文件。 通过以下方式指定源文件和输出文件:

file.out: file.c
	gcc file.c -o file.out

这告诉了 Make 输出文件为 a.out ,它由一个源文件 a.c 来构建。这样 Make 只会在 a.out 不存在或 a.c 有更新时运行 gcc a.c -o a.out

这样的依赖也可以是多层的:

file.out: file.o
	gcc file.o -o file.out

file.o: file.S
	gcc -c file.S -o file.o

file.S: file.c
	gcc file.c -S -o file.S
$: make file.out
gcc file.c -S -o file.S
gcc -c file.S -o file.o
gcc file.o -o file.out
$: touch file.o # 只更新了 file.o
$: make file.out
gcc file.o -o file.out

关键符号们

$:首先当然就是它了,$ 再加上一个字符就构成了为数众多的其他关键符号。有些关键符号需要用到多于一个字符,那么就用括号包裹起来,以这种形式出现 $() 。以 $() 包裹着还表示这是一个 Makefile 里的变量,虽然它和 shell 里面的变量长得很像,不过请把他们区分开。如果你仅仅是想在 Makefile 中表示一个普普通通的(字面上的) $ ,请使用 $$ 来转义。

  • %:目标和依赖中可以使用的通配符。依赖中通配符表示的实际值,会和你指定的目标匹配到的那一段保持一致。

    # Okay:
    %.out: %.c
    	# Okay:
    	gcc $< -o $@
    	# Bad:
    	gcc %.c -o %.out
  • $@:目标文件,可在 rule 的内容(shell 脚本)中使用。

  • $<:“输入” 文件,即依赖文件。可在 rule 的内容中使用。
  • $^:所有 “输入” 文件。
  • $*% 匹配到的那部分,可在内容中使用。

函数

$(strip string) 移除字符串首尾的空格。

$(wildcard filename) 返回匹配的文件名。

$(firstword string) $(lastword string) 如名。

一些坑

匹配

Make 支持通配符。当一个文件同时满足两个规则的通配符时,它会优先匹配最接近的那个。

%.js:
	blabla-1

%.bundle.js:
	blabla-2
$: make app.bundle.js
blabla-2

空规则

如果一个规则的命令为空,Make 会尝试寻找其他匹配这个文件名的规则执行。然后就可能会出错。

# 命令为空!
dist/%: generate

.PHONY: generate
generate:
	blabla-1

%.html:
	blabla-2 > $@
$: make dist/a.html
# Expected: blabla-1
blabla-2 > dist/a.html

“变量”

嗯,Makefile 中的变量不是实际意义上的变量。Make 会事先把 Makefile 运行一遍,将变量计算出结果后再传入 shell 运行。

About Me