make命令

用 make 進行巨集編譯

在本章一開始我們提到過 make 的功能是可以簡化編譯過程裏面所下達的命令,同時還具有很多很方便的功能!那麼底下咱們就來試看看使用make 簡化下達編譯命令的流程吧!


小標題的圖示爲什麼要用 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://vbird.dic.ksu.edu.tw/linux_basic/0520source/main.tgz來下載。由於這四個文件裏面包含了相關性,並且還用到數學函式在裏面,所以如果你想要讓這個程序可以跑,那麼就需要這樣編譯:

# 1. 先進行目標檔的編譯,最終會有四個 *.o 的檔名出現:
[root@www ~]# gcc -c main.c
[root@www ~]# gcc -c haha.c
[root@www ~]# gcc -c sin_value.c
[root@www ~]# gcc -c cos_value.c

# 2. 再進行連結成爲運行檔,並加入 libm 的數學函式,以產生 main 運行檔:
[root@www ~]# gcc -o main main.o haha.o sin_value.o cos_value.o \
> -lm -L/usr/lib -L/lib

# 3. 本程序的運行結果,必須輸入姓名、360 度角的角度值來計算:
[root@www ~]# ./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@www ~]# 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@www ~]# rm -f main *.o   <==先將之前的目標檔去除
[root@www ~]# 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@www ~]# make
make: `main' is up to date.
# 看到了吧!是否很方便呢!只會進行升級 (update) 的動作而已。

或許你會說:『如果我創建一個 shell script 來將上面的所有動作都集結在一起,不是具有同樣的效果嗎?』呵呵!效果當然不一樣,以上面的測試爲例,我們僅寫出 main 需要的目標檔,結果 make 會主動的去判斷每個目標檔相關的原始碼文件,並直接予以編譯,最後再直接進行連結的動作!真的是很方便啊!此外,如果我們更動過某些原始碼文件,則 make 也可以主動的判斷哪一個原始碼與相關的目標檔文件有升級過,並僅升級該文件,如此一來,將可大大的節省很多編譯的時間呢!要知道,某些程序在進行編譯的行爲時,會消耗很多的CPU 資源呢!所以說, make 有這些好處:

  • 簡化編譯時所需要下達的命令;
  • 若在編譯完成之後,修改了某個原始碼文件,則 make 僅會針對被修改了的文件進行編譯,其他的object file 不會被更動;
  • 最後可以依照相依性來升級 (update) 運行檔。

既然 make 有這麼多的優點,那麼我們當然就得好好的瞭解一下 make 這個令人關心的傢伙啦!而 make 裏面最需要注意的大概就是那個守則文件,也就是 makefile 這個文件的語法啦!所以底下我們就針對 makefile 的語法來加以介紹羅。


小標題的圖示makefile 的基本語法與變量

make 的語法可是相當的多而複雜的,有興趣的話可以到 GNU (注1)去查閱相關的說明,鳥哥這裏僅列出一些基本的守則,重點在於讓讀者們未來在接觸原始碼時,不會太緊張啊!好了,基本的 makefile 守則是這樣的:

標的(target): 目標檔1 目標檔2
<tab>   gcc -o 欲創建的運行檔 目標檔1 目標檔2

那個標的 (target) 就是我們想要創建的資訊,而目標檔就是具有相關性的 object files ,那創建運行檔的語法就是以 <tab> 按鍵開頭的那一行!特別給他留意喔,『命令列必須要以 tab 按鍵作爲開頭』才行!他的守則基本上是這樣的:

  • 在 makefile 當中的 # 代表註解;
  • <tab> 需要在命令行 (例如 gcc 這個編譯器命令) 的第一個字節;
  • 標的 (target) 與相依文件(就是目標檔)之間需以『:』隔開。

同樣的,我們以剛剛上一個小節的範例進一步說明,如果我想要有兩個以上的運行動作時,例如下達一個命令就直接清除掉所有的目標檔與運行檔,該如何製作呢?

# 1. 先編輯 makefile 來創建新的守則,此守則的標的名稱爲 clean :
[root@www ~]# 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@www ~]# make clean  <==就是這裏!透過 make 以 clean 爲標的
rm -rf main main.o haha.o sin_value.o cos_value.o

如此一來,我們的 makefile 裏面就具有至少兩個標的,分別是 main 與 clean ,如果我們想要創建 main 的話,輸入『make main』,如果想要清除有的沒的,輸入『makeclean』即可啊!而如果想要先清除目標檔再編譯 main 這個程序的話,就可以這樣輸入:『make clean main』,如下所示:

[root@www ~]# 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 裏面怎麼重複的數據這麼多啊!沒錯!所以我們可以再藉由 shellscript 那時學到的『變量』來更簡化 makefile 喔:

[root@www ~]# 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@www ~]# CFLAGS="-Wall" make clean main
# 這個動作在上 make 進行編譯時,會去取用 CFLAGS 的變量內容!

也可以這樣:

[root@www ~]# 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@www ~]# 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}

這樣是否稍微瞭解了 makefile (也可能是 Makefile) 的基本語法?這對於你未來自行修改原始碼的編譯守則時,是很有幫助的喔!^_^!




###################################################################

例解 Linux 下 Make 命令

Linux 下 make 命令是系統管理員和程序員用的最頻繁的命令之一。管理員用它通過命令行來編譯和安裝很多開源的工具,程序員用它來管理他們大型複雜的項目編譯問題。本文我們將用一些實例來討論 make 命令背後的工作機制。

Make 如何工作的

對於不知道背後機理的人來說,make 命令像命令行參數一樣接收目標。這些目標通常存放在以 “Makefile” 來命名的特殊文件中,同時文件也包含與目標相對應的操作。更多信息,閱讀關於 Makefiles 如何工作的系列文章。

當 make 命令第一次執行時,它掃描 Makefile 找到目標以及其依賴。如果這些依賴自身也是目標,繼續爲這些依賴掃描 Makefile 建立其依賴關係,然後編譯它們。一旦主依賴編譯之後,然後就編譯主目標(這是通過 make 命令傳入的)。

現在,假設你對某個源文件進行了修改,你再次執行 make 命令,它將只編譯與該源文件相關的目標文件,因此,編譯完最終的可執行文件節省了大量的時間。

Make 命令實例

下面是本文所使用的測試環境:

OS —— Ubunut 13.04
Shell —— Bash 4.2.45
Application —— GNU Make 3.81

下面是工程的內容:

$ ls 
anotherTest.c Makefile test.c test.h

下面是 Makefile 的內容:

all: test 

test: test.o anotherTest.o 
    gcc -Wall test.o anotherTest.o -o test

test.o: test.c 
    gcc -c -Wall test.c 

anotherTest.o: anotherTest.c 
    gcc -c -Wall anotherTest.c 

clean: 
    rm -rf *.o test

現在我們來看 Linux 下一些 make 命令應用的實例:

1. 一個簡單的例子

爲了編譯整個工程,你可以簡單的使用 make 或者在 make 命令後帶上目標 all

$ make 
gcc -c -Wall test.c 
gcc -c -Wall anotherTest.c 
gcc -Wall test.o anotherTest.o -o test

你能看到 make 命令第一次創建的依賴以及實際的目標。

如果你再次查看目錄內容,裏面多了一些 .o 文件和執行文件:

$ ls 
anotherTest.c anotherTest.o Makefile test test.c test.h test.o

現在,假設你對 test.c 文件做了一些修改,重新使用 make 編譯工程:

$ make 
gcc -c -Wall test.c 
gcc -Wall test.o anotherTest.o -o test

你可以看到只有 test.o 重新編譯了,然而另一個 Test.o 沒有重新編譯。

現在清理所有的目標文件和可執行文件 test,你可以使用目標 clean:

$ make clean
rm -rf *.o test

$ ls
anotherTest.c Makefile test.c test.h

你可以看到所有的 .o 文件和執行文件 test 都被刪除了。

2. 通過 -B 選項讓所有目標總是重新建立

到目前爲止,你可能注意到 make 命令不會編譯那些自從上次編譯之後就沒有更改的文件,但是,如果你想覆蓋 make 這種默認的行爲,你可以使用 -B 選項。

下面是個例子:

$ make
make: Nothing to be done for `all’.

$ make -B
gcc -c -Wall test.c
gcc -c -Wall anotherTest.c
gcc -Wall test.o anotherTest.o -o test

你可以看到儘管 make 命令不會編譯任何文件,然而 make -B 會強制編譯所有的目標文件以及最終的執行文件。



發佈了7 篇原創文章 · 獲贊 7 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章