the 2nd edition: 20170419
雖然我會自己寫 makefile, 但那只是最簡單的部份, 我通常只記得住 $@, $<, $^ 這三個, 也夠我用好上幾年。不過也該來記住一點進階的東西。
Default Compilation Rules
有兩種:
Suffix Rule
.SUFFIXES: .c.o: gcc -c -o $@ -O $*.c
Suffix Rules 目前已經被廢棄, 不要再用這種語法, 又稱為 Old-Fashioned Suffix Rules。
Pattern Rule
%.o : %.c gcc $(CFLAGS) -c $<
新的 Suffix Rules 又分為 double-suffix, single-suffix, 或是 Pattern Rule, 上面展示的 double-suffix 的形式。
ref:
10.7 Old-Fashioned Suffix Rules
https://www.gnu.org/software/make/manual/html_node/Suffix-Rules.html
我老是被這樣的語法所疑惑, 原來這是兩種不同的東西。這個寫法若是 .h 改變了, source code 不會被重新編譯, 不是太好用的語法。
macro substitution
makefile L2 把 main.aa 換成 main.c; stack.aa 換成 stack.c 很怪異的語法, 令我困惑。所以 sources 就是 main.c stack.c
執行結果
descent@descent-u:tmp$ make clean
echo main.aa stack.aa
main.aa stack.aa
echo main.c stack.c
main.c stack.c
測試 pattern rule, L4, L5 是我自己定義的 pattern rule。
執行結果
descent@descent-u:tmp$ make main.aa ls / bin etc hd_mnt lib32 mnt sbin tmp vmlinuz.old boot floppy home lib64 opt selinux ubiquity-apt-clone work cdrom hd1_mnt initrd.img lost+found proc srv usr C:\nppdf32Log\debuglog.txt hd2_mnt initrd.img.old material root sys var dev hd3_mnt lib media run tftpboot vmlinuz ls main.aa ls: cannot access main.aa: No such file or directory make: *** [main.aa] Error 2
makefile 很複雜, 不過這樣已經足夠來使用, 沒有 IDE 的環境已經足夠累人, 撰寫 makefile 就不要再累死自己。其實 $@, $<, $^ 這三個也很夠用。
make -n 則可以列出 makefile 要執行的內容。
在 makefile 中使用 ifeq 和 or
$(filter ,)
名稱:過濾函數——filter。
功能:以模式過濾字符串中的單詞,保留符合模式的單詞。可以有多個模式。
返回:返回符合模式的字串。
ex:
if SELECT_A==y || SELECT_B==y
ifeq (y, $(filter $(SELECT_A) $(SELECT_B), y))
gcc -MM, 產生 makefile rule, ex:
看了《Makefile header 檔的相依性檢查》這篇之後, 我決定好好把 gcc -MM 這樣的語法看懂。
list 1 是 simple compiler 的 makefile, 我修改成類似的作法, 沒有看不懂的 sed 指令。我不確定這是正規或是正確的寫法, 看來還可以用, 這是我自己亂試的版本。
要加入一個新的 c++ source code 檔案, 在 list 1. L21 加入 .o 檔名即可。ex: new_file.cpp, 把 new_file.o 加入到 OBJS 列表即可。
折磨人的是 L40, L42, L48, 一個一個來, 先看 L42, 所有副檔名 .d 的從 .cpp 製造出來, 先把 L48 include 刪掉, 鍵入以下指令:
descent@debian64:simple_compiler$ make astnode.d g++ -g -std=c++14 -Wall -m32 -MM -MF astnode.d astnode.cpp descent@debian64:simple_compiler$ cat astnode.d astnode.o: astnode.cpp astnode.h symbol_table.h mytype.h env.h token.h
得到了 astnode.d, 裡頭已經幫我們寫好 astnode.o 的相依性, 那要怎麼補上 g++ 這個編譯動作呢? 加入 L45, 每個 .o 檔, 由 .cpp 製造出來, 用
$(CXX) $(CXXFLAGS) -c $<這樣的指令製造出來。
最後結果大概是這樣:
astnode.o: astnode.cpp astnode.h symbol_table.h mytype.h env.h token.h g++ -c astnode.cpp
另外一個問題是怎麼讓這些 .d 檔案變成 makefile 的內容呢? 這樣我就不用複製其內容, 貼到 makefile 裡頭。
用 L48 include, 所以我要
include c_parser.d include astnode.d include env.d include lexer.d include op.d include symbol_table.d include token.d好啦! 我有 c_parser.o astnode.o env.o lexer.o op.o symbol_table.o token.o 這麼多 .o 檔, 難道要一個一個寫嗎? 看起來一點都不高明是吧! L48 就是在做這件事情, 所以 L48 那行你不喜歡, 也可以像上面那樣, 自己寫上所有要 include 的 .d 檔案。
include qq, qq 是一個檔案, 當這個檔案不存在時, 會怎麼樣,
makefile:42: qq: No such file or directory
會這樣, 很合理的, 但 gnu make 有個特異功能, 如果加上 qq 這個 target, makefile 會去執行這個 rule。
qq: echo "i am qq"
而 qq 存在時, 就不會執行這個 rule。
所以當 astnode.d 不存在時, 便會執行
g++ -g -std=c++14 -Wall -m32 -MM -MF astnode.d astnode.cpp產生 astnode.d。
再來是《Makefile header 檔的相依性檢查》提到的 .h 改名或是不存在了, 會有
descent@debian64:simple_compiler$ make astnode.d g++ -g -std=c++14 -Wall -m32 -MM -MF astnode.d astnode.cpp astnode.cpp:2:16: fatal error: op.h: No such file or directory compilation terminated. makefile:47: recipe for target 'astnode.d' failed make: *** [astnode.d] Error 1
相信比以下的訊息容易看懂。
make: *** No rule to make target 'name.h', needed by 'name.d'. Stop.
剩下那個 L40 是幹麻的, 很簡單, 我測試產生 .d 用的, 沒幹麻。
列出 makefile target
https://gist.github.com/pvdb/777954
make -rpn | sed -n -e '/^$/ { n ; /^[^ ]*:/p }'
來試試看 list 1 的 makefile 會有什麼結果:
ref:
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。