- 為什么要使用make
先來想像一個案例,假設我的可執行文件里面包含了四個源代碼文件,分別是 main.c haha.c sin_value.c cos_value.c 這四個文件,這四個文件的目的是:
main.c :主要的目的是讓使用者輸入角度數據與調用其他三支副程序;
haha.c :輸出一堆有的沒有的訊息而已;
sin_value.c :計算使用者輸入的角度(360) sin 數值;
cos_value.c :計算使用者輸入的角度(360) cos 數值。
這四個文件你可以到 http://linux.vbird.org/linux_basic/0520source/main.tgz 來下載。由於這四個文件里面包含了相關性,並且還用到數學函數在里面,所以如果你想要讓這個程序可以跑, 那么就需要這樣編譯:
# 1. 先進行目標文件的編譯,最終會有四個 *.o 的文件名出現: [root@study ~]# gcc -c main.c [root@study ~]# gcc -c haha.c [root@study ~]# gcc -c sin_value.c [root@study ~]# gcc -c cos_value.c # 2. 再進行鏈接成為可執行文件,並加入 libm 的數學函數,以產生 main 可執行文件: [root@study ~]# gcc -o main main.o haha.o sin_value.o cos_value.o -lm # 3. 本程序的執行結果,必須輸入姓名、360 度角的角度值來計算: [root@study ~]# ./main Please input your name: VBird <==這里先輸入名字 Please enter the degree angle (ex> 90): 30 <==輸入以 360 度角為主的角度 Hi, Dear VBird, nice to meet you. <==這三行為輸出的結果喔! The Sin is: 0.50 The Cos is: 0.87
編譯的過程需要進行好多動作啊!而且如果要重新編譯,則上述的流程得要重新來一遍,光是找出這些指令就夠煩人的了! 如果可以的話,能不能一個步驟就給他完成上面所有的動作呢?那就利用 make 這個工具吧! 先試看看在這個目錄下創建一個名為 makefile 的文件,內容如下:
# 1. 先編輯 makefile 這個規則檔,內容只要作出 main 這個可執行文件 [root@study ~]# vim makefile main: main.o haha.o sin_value.o cos_value.o gcc -o main main.o haha.o sin_value.o cos_value.o -lm # 注意:第二行的 gcc 之前是 <tab> 按鍵產生的空格喔! # 2. 嘗試使用 makefile 制訂的規則進行編譯的行為: [root@study ~]# rm -f main *.o <==先將之前的目標文件去除 [root@study ~]# make cc -c -o main.o main.c cc -c -o haha.o haha.c cc -c -o sin_value.o sin_value.c cc -c -o cos_value.o cos_value.c gcc -o main main.o haha.o sin_value.o cos_value.o -lm # 此時 make 會去讀取 makefile 的內容,並根據內容直接去給他編譯相關的文件啰! # 3. 在不刪除任何文件的情況下,重新執行一次編譯的動作: [root@study ~]# make make: `main' is up to date. # 看到了吧!是否很方便呢!只會進行更新 (update) 的動作而已。
如此一來,將可大大的節省很多編譯的時間,要知道,某些程序在進行編譯的行為時,會消耗很多的 CPU
資源呢!所以說, make 有這些好處:
-
- 簡化編譯時所需要下達的指令;
- 若在編譯完成之后,修改了某個源代碼文件,則 make 僅會針對被修改了的文件進行編譯,其他的 object file 不會被更動;
- 最后可以依照相依性來更新 (update) 可執行文件。
- makefile語法
基本的 makefile 規則是這樣的:標的(di四聲)
標的(target): 目標文件1 目標文件2 <tab> gcc -o 欲創建的可執行文件 目標文件1 目標文件2
那個標的 (target) 就是我們想要創建的信息,而目標文件就是具有相關性的 object files ,那創建可執行文件的語法就是以 <tab> 按鍵開頭的那一行!特別給他留意喔,“命令列必須要以 tab 按鍵作為開頭”才行!他的規則基本上是這樣:
-
- 在 makefile 當中的 # 代表注解;
- <tab> 需要在命令行 (例如 gcc 這個編譯器指令) 的第一個字符;
- 標的 (target) 與相依文件(就是目標文件)之間需以“:”隔開。
同樣的,我們以剛剛上一個小節的范例進一步說明,如果我想要有兩個以上的執行動作時, 例如下達一個指令就直接清除掉所有的目標文件與可執行文件,該如何制作呢?
# 1. 先編輯 makefile 來創建新的規則,此規則的標的名稱為 clean : [root@study ~]# vi makefile main: main.o haha.o sin_value.o cos_value.o gcc -o main main.o haha.o sin_value.o cos_value.o -lm clean: rm -f main main.o haha.o sin_value.o cos_value.o # 2. 以新的標的 (clean) 測試看看執行 make 的結果: [root@study ~]# make clean <==就是這里!通過 make 以 clean 為標的 rm -rf main main.o haha.o sin_value.o cos_value.o
如此一來,我們的 makefile 里面就具有至少兩個標的,分別是 main 與 clean ,如果我們想要創建 main 的話,輸入“make main”,如果想要清除有的沒的,輸入“make clean”即可啊!而如果想要先清除目標文件再編譯 main 這個程序的話,就可以這樣輸入:“make clean main”,如下所示:
[root@study ~]# make clean main rm -rf main main.o haha.o sin_value.o cos_value.o cc -c -o main.o main.c cc -c -o haha.o haha.c cc -c -o sin_value.o sin_value.c cc -c -o cos_value.o cos_value.c gcc -o main main.o haha.o sin_value.o cos_value.o -lm
這樣就很清楚了吧!但是,你是否會覺得,咦! makefile 里面怎么重復的數據這么多啊!沒錯!所以我們可以再借由 shell script 那時學到的“變量”來更簡化 makefile 喔:
[root@study ~]# vi makefile LIBS = -lm OBJS = main.o haha.o sin_value.o cos_value.o main: ${OBJS} gcc -o main ${OBJS} ${LIBS} clean: rm -f main ${OBJS}
與 bash shell script 的語法有點不太相同,變量的基本語法為:
1. 變量與變量內容以“=”隔開,同時兩邊可以具有空格;
2. 變量左邊不可以有 <tab> ,例如上面范例的第一行 LIBS 左邊不可以是 <tab>;
3. 變量與變量內容在“=”兩邊不能具有“:”;
4. 在習慣上,變量最好是以“大寫字母”為主;
5. 運用變量時,以 ${變量} 或 $(變量) 使用;
6. 在該 shell 的環境變量是可以被套用的,例如提到的 CFLAGS 這個變量!
7. 在指令列模式也可以給予變量。
由於 gcc 在進行編譯的行為時,會主動的去讀取 CFLAGS 這個環境變量,所以,你可以直接在 shell 定義出這個環境變量,也可以在makefile 文件里面去定義,更可以在指令列當中給予這個咚咚呢!例如:
[root@study ~]# CFLAGS="-Wall" make clean main # 這個動作在上 make 進行編譯時,會去取用 CFLAGS 的變量內容! 也可以這樣: [root@study ~]# vi makefile LIBS = -lm OBJS = main.o haha.o sin_value.o cos_value.o CFLAGS = -Wall main: ${OBJS} gcc -o main ${OBJS} ${LIBS} clean: rm -f main ${OBJS}
咦!我可以利用指令列進行環境變量的輸入,也可以在文件內直接指定環境變量,那萬一這個 CFLAGS 的內容在指令列與 makefile 里面並不相同時,以那個方式輸入的為主?呵呵!問了個好問題啊! 環境變量取用的規則是這樣的:
1. make 指令列后面加上的環境變量為優先;
2. makefile 里面指定的環境變量第二;
3. shell 原本具有的環境變量第三。
此外,還有一些特殊的變量需要了解的喔:
-
- $@:代表目前的標的(target)
所以我也可以將 makefile 改成:
[root@study ~]# vi makefile LIBS = -lm OBJS = main.o haha.o sin_value.o cos_value.o CFLAGS = -Wall main: ${OBJS} gcc -o $@ ${OBJS} ${LIBS} <==那個 $@ 就是 main ! clean: rm -f main ${OBJS}
Over......