let's Make

make

基本語法

1
2
3
4
5
targets : prerequisites
command
# or
targets : prerequisites ; command
command

範例

範例 1

範例一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

範例 2 使用變數

第一次改進使用變數
1
2
3
4
5
6
7
8
9
10
11
#edit : main.o kbd.o command.o display.o \
# insert.o search.o files.o utils.o
# cc -o edit main.o kbd.o command.o display.o \
# insert.o search.o files.o utils.o
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
# ...
clean :
rm edit $(objects)

範例 3 隱藏規則(自動推導)

遇到 x.o 會推導出 x.c 為相依,並自動推導出要做 cc -c x.c

1
2
3
# whatever.o
whatever.o : whatever.c
cc -c whatever.c
使用隱藏規則
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
.PHONY : clean # 指 clean 為假目標文件
clean :
-rm edit $(objects) # -rm 代表有出錯也不管繼續做

寫作習慣

  • 第一個 label 通常是最終目標 (也是默認目標)
  • 最後一個 label 為 clean
  • \# 表示 #
    all
    clean
    install
    print 列出已改文件
    tar 打包
    dist 壓縮
    TAGS 更新所有目標,以备完整地重编译使用
    check test 測試 make 流程

Make (尋找 makefile)

  • GNUmakefile
  • makefile
  • Makefile 推薦
  • make -f Make.Linux or make --file Make.AIX

引用 makefile

Makefile
1
include foo.make a.mk b.mk c.mk e.mk f.mk

引用 Makefile 尋找位置

  • -I or --include-dir
  • /include (通常是 /usr/local/bin or /usr/include)
  • 環境變數 MAKEFILES (最好別用,但是出事情最好查一下的地方)

特殊變數

  • VPATH = src:../headers, 當文件找不到時去 src, ..headers 尋找
  • MAKECMDGOALS
  • SHELL,嵌套执行 make 會自動 export
  • MAKEFLAGS,嵌套执行 make 會自動 export,-C -f -h -o -W 參數不會被帶過去
vpath in Makefile 延伸
1
2
3
4
5
6
# vpath <pattern> <directories> 
vpath %.h ../headers # 符合 .h 去 ../headers 找
# vpath <pattern>
# 清除符合模式<pattern>的文件的搜索目录
# vpath
# 清除所有已被设置好了的文件搜索目录。

特殊目標

  • .PHONY
  • .INTERMEDIATE : mid 中間目標
  • .SECONDARY : sec 不必刪除的中間目標
  • .PRECIOUS
  • .SUFFIXES: .hack .win 後綴規則(舊規則),.SUFFIXES: # 删除默认的后缀

偽目標 .PHONY

偽目標不需要生成檔案,如 clean。可以當預設目標,且每次執行一定會執行(不必檢查相依後再產生)

1
.PHONY : clean

嵌套执行 make

變數可以透過 make -e out=1 傳入。

或者用 export out = 1export 表示全部變數皆傳下去,可以用 unexport out 取消傳入。

也能用 override out = 2 禁止外面傳入

1
2
3
4
5
subsystem:
cd subdir && $(MAKE)
# 其等价于:
subsystem:
$(MAKE) -C subdir

define

1
2
3
4
define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef

變數宣告

  • =,有變數中的變數的話,要以各變數的最終值展開
  • :=,宣告時展開
  • +=,apped
  • ?=,當變數沒有宣告,才做賦值動作

指令??

  • @:不要顯示執行的指令
  • -:即使執行錯誤也不會中斷

隱規則

中間過程生成物會被 rm -f 方式移除

a -> b -> c,b 會被自動移除

1
2
3
4
# c
%.o : %.c
$(CC) –c $(CPPFLAGS) $(CFLAGS)
# cc -c "C 预处理器参数" "C 语言编译器参数"

自動化變數 $@ $<

$@
表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,”$@”就是匹配于
目标中模式定义的集合。
$%
仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是”foo.a
(bar.o)”,那么,”$%”就是”bar.o”,”$@”就是”foo.a”。如果目标不是函数库文件(Unix 下是[.a],Windows 下是[.lib]),那么,其值为空。
$<
依赖目标中的第一个目标名字。如果依赖目标是以模式(即”%”)定义的,那么”$<”将
是符合模式的一系列的文件集。注意,其是一个一个取出来的。
$?
所有比目标新的依赖目标的集合。以空格分隔。
$^
所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量
会去除重复的依赖目标,只保留一份。
$+
这个变量很像”$^”,也是所有依赖目标的集合。只是它不去除重复的依赖目标。
$
这个变量表示目标模式中”%”及其之前的部分。如果目标是”dir/a.foo.b”,并且目标的
模式是”a.%.b”,那么,”$
“的值就是”dir/a.foo”。这个变量对于构造有关联的文件名是比 较有较。如果目标中没有模式的定义,那么”$“也就不能被推导出,但是,如果目标文件的 后缀是 make 所识别的,那么”$“就是除了后缀的那一部分。例如:如果目标是”foo.c”,因 为”.c”是 make 所能识别的后缀名,所以,”$*”的值就是”foo”。这个特性是 GNU make 的

$?
代表已被更新的dependencies的值
也就是dependencies中比target還要新的值

$@
代表target的值

$<
代表第一個dependencies的值

$*
代表target所指定的檔案 不包含副檔名

define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef

foo.c : foo.y
$(run-yacc)
$@:$^

“$^”就是“foo.y”
“$@”就是“foo.c”

1
2
3
4
5
6
7
8
9
10
#<targets ...>: <target-pattern>: <prereq-patterns ...>
# <commands>
# $@ : $< : ??

objects = foo.o bar.o

all: $(objects)

$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
$@ 範例
1
2
3
4
5
6
7
8
9
bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@

# 等價於

bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput
$< 範例
1
2
3
4
5
6
7
8


# 等價於

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
objects = *.o  # "*.o"
objects := $(wildcard *.o) # 所有 .o 檔

p39