Makefile筆記
引言
在了解Makefile之前,首先要了解C/C++的程式是怎麼被執行的,
C/C++的原始碼(.c / .cpp),需要經由編譯器(compiler)編譯成機械碼(.o / .obj),
也就是電腦系統看得懂的語言,最後經由Linker(連結器)將所有的*.o / *.obj做結合,產生.exe檔案。
Makefile 基本認識
如果單純只有少少的幾個檔案(.c / .h)之類的,
那只要在termial跑簡單幾個指令就可以完成編譯、連結的動作
$ gcc -c main.c // 產生 main.o
$ gcc -o test main.o // 產生 test.exe
這樣就結束了,但是當檔案一多,且彼此有了相依性(必須先執行A,才能再執行B),
指令的下達就會變得非常的複雜。
此時有一個非常方便的工具 make,下達這個指令後,
電腦會到路徑下找到Makefile去依照裡面所撰寫的法則來進行編譯、連結。
p.s.
1. .h: 方法聲明
2. .c: 方法實現
3. .o: 機械碼
使用Makefile的好處
1. 如果這個專案沒有編譯過,那麼我們的所有程式碼都要編譯並被連結
2. 如果這個專案的某幾份程式碼被修改,那麼我們只編譯被修改的程式,並連結目標程式 (會依檔案更改的時間做判斷)
3. 如果這個專案的標頭檔被改變了,那麼我們需要編譯引用了這幾個標頭檔的程式碼,並連結目標程式 (會依檔案更改的時間做判斷)
Makefile 包含
1、顯式規則。顯式規則說明了,如何生成一個或多的的目標檔案。這是由Makefile的書寫者明顯指出,要生成的檔案,檔案的相依性檔案,生成的命令。
2、隱晦規則。由於我們的make有自動推導的功能,所以隱晦的規則可以讓我們比較粗糙地簡略地書寫Makefile,這是由make所支持的。
3、變量的定義。在Makefile中我們要定義一系列的變量,變量一般都是字串,當Makefile被執行時,
其中的變量都會被擴展到相應的引用位置上。
4、檔案指示。其包括了三個部分,一個是在一個Makefile中引用另一個 Makefile,就像C語言中的include一樣;
另一個是指根據某些情況指定Makefile中的有效部分,就像C語言中的預編譯#if一樣;還有就是定義一個多行的命令。
5、註釋。Makefile中只有行註釋,和UNIX的Shell腳本一樣,其註釋是用「#」字符,
這個就像C/C++中的「//」一樣。如果你要在你的Makefile中使用「#」字符,可以用反斜框進行轉義,如:「\#」。
最後,還值得一提的是,在Makefile中的命令,必須要以[Tab]鍵開始。
Makefile 語法規則
冒號前面為目標文件,冒號後面表示需要產生目標文件的需要文件(相依性),tab後面加入命令
# 產生test,需要main.o,如果有main.o執行下面命令以產生test
test:main.o
<tab> gcc -o test main.o
目標文件假設為.o檔案,make會自動將所對應的.cpp / .c 檔案歸類於必要條件,因此不用書寫。
main.o:(main.cpp) test.cpp
g++ -c main.cpp
1. 變數巨集
obj = main.o ball.o test.o
// 可以用 $(obj) 來表示這三個檔案
2. 自動化變數
$<: 表示第一個必要條件的檔名
$@: 工作目標的檔名
$^: 所有必要條件的檔名,用空格隔開
test: main.o
g++ -o $@ $<
main.o: main.cpp test.cpp
g++ -c $^
3. 特殊變數
-: 表示即使該行指令出錯,也不會中斷執行,而 make 只要遇到任何錯誤就會中斷執行。
但像是在進行 clean 時,也許根本沒有任何檔案可以 clean,因而 rm 會傳回錯誤值,
因而導致 make 中斷執行。我們可以利用 - 來關閉錯誤中斷功能,讓 make 不會因而中斷。
@: 不要顯示執行的指令,因執行 make 指令後會在終端機印出正在執行的指令
在下例中,若不使用 .PHONY 來指定 clean 為 fake 項目的話,若目錄中同時存在了一個名為 clean 的檔案,
則 clean 這個項目將被視為要建立 clean 這個檔案,但 clean 這個項目卻又沒有任何的 dependencies,
也因此,clean 項目將永遠被視為 up-to-date,永遠不會被執行。
因為利用了 .PHONY 來指定 clean 為 fake 項目,所以 make 不會去檢查目錄中是否存在了一個名為 clean 的檔案。
如此也可以提昇 make 的執行效率。
p.s. 使用clean,要下達make clean的指令
.PHONY: clean
clean:
rm *.o
Makefile簡單試寫
obj = source/main.o source/ball.o
test:source/main.o source/ball.o
g++ -o test source/main.o source/ball.o
#g++ -o $@ $^
main.o:source/main.cpp source/ball.cpp
g++ -c source/main.cpp
#Ball.o:(source/ball.cpp) header/ball.h
Ball.o:source/ball.cpp header/ball.h
g++ -c source/ball.cpp
.PHONY:clean
clean:
echo "clean..."
rm -f test source/*.o
補充說明
*.h檔案作為聲明使用,通常只會被*.cpp /*.c 所include
若有以下檔案: main.cpp, ball.cpp, ball.h
ball.cpp 會 include ball.h
main.cpp 也會include ball.h 但是不用include ball.cpp,因為只需要聲明,若main執行到需要用到ball的function時,
會jump到ball.cpp記憶體所在位置執行。(我的理解啦XD)
參考資料
沒有留言:
張貼留言