第二節-make
當項目變得越來越大的時候,還用gcc命令來編譯項目就沒有那麼吸引人了。make工具能夠使編譯和連結全自動化。在使用make工具的時候,程式員只需設定好項目所包含的檔案和怎樣去搭配好他們,接下來的make工具會自動完成編譯和連結的工作。Make能夠加速你的編譯過程,因為假如你有十個.c的源檔案但你只變動了一個檔案而其它的九個檔案在之前編譯過那麼它會知道在連結之前只需重新編譯那一個單獨的檔案。Make有一些複雜的特性,但用它來作一些簡單的事情是非常簡單的。
運行make
進入到你的項目目錄並且直接在控制台中輸入make命令就能運行make了,或者在emacs中輸入[esc]-x compile效果也是一樣的。通常情況下,make會在目前的目錄下尋找一個名為MakeFile或是makefile的檔案作為運行說明。如果在編譯其中的一個目標檔案的時候發生了錯誤,那麼錯誤提示寫入到標準錯誤提示中或是emacs編譯緩衝中。
Makefiles
Makefile檔案中包含著一系列的變數定義和依賴規則。makefile中的一個變數是指
用於代表一些字串的名字。這和c的先行編譯器中大量的替代很類似。這些變數通常用於表示一系列要搜尋的目錄,編譯器的選項,要啟動並執行程式的名字。變數使用之前不許要提前聲明,你只需要使用“=”來設定,例如:
CC = gcc
就會產生一個叫做CC的變數,並且將它的值設定成gcc,變數名是大小寫敏感的,通常我們會用大寫來命名變數。
雖然我們能夠為變數自訂變數名,但是有一些約定俗成的變數最好用於預設的設定,這樣可以在使用makefile的時候更順手。最重要的幾個變數是:CC,CFLAGS和LDFLAGS。
CC
C編譯器的名稱,這個在make的大部分版本中預設cc或gcc。
CFLAGS
用於在編譯的時候傳給C的編譯器的命令。這通常用來設定包含的路徑和非標準目錄或是編譯版本。
LDFLAGS
傳給連結器的命令。這個通常用來包含庫檔案(-l)和設定庫的搜尋路徑(-L)。
在需要引用一個變數的時候,只需將變數名擴起來,並且在前面加上一個美元符($)...
CFLAGS = -g -I/usr/class/cs107/include
$(CC) $(CFLAGS) -c binky.c
第一行是用來設定CFLAGS變數,加入一些調試資訊,將-I/usr/class/cs107/include加入到搜尋目錄中去。第二行是引用CC變數來獲得編譯器的名字,引用CFLAGS來獲得編譯器的編譯命令。沒有被賦值的變數將是一個空的字串。
第二個makefile的重要組成部分是依賴/建立規則,用於將源檔案產生目標檔案的規則。規則的書寫順序並沒有多大影響,除了第一條規則被當成預設的規則——在make被調用的時候,如果沒有任何參數,這條規則就會被調用(通常會是這種情況)。
一條命令通常包含兩行:一條命令列,一條依賴行。
下面是一個例子:
binky.o : binky.c binky.h akbar.h
tab$(CC) $(CFLAGS) -c binky.c
這個依賴行指明了目標檔案binky.o在binky.c binky.h akbar.h這三個檔案中任何一個變化了之後都要再編譯一遍,binky.o依賴於這三個檔案。通常情況下,目標檔案依賴於它的源檔案和它裡麵包含的任何的非系統檔案。程式員負責在makefile檔案中表述清楚源檔案中的依賴。在上面的例子中,很明顯源檔案是bink.c,標頭檔包括binky.h和akbar.h——它們中任何一個檔案發生了改變,binky.c都要被重新編譯一遍。(make工具依賴於嘗試自動化makefile的裝置,但這已經超出了我們的討論範圍了。)
命令列列出了建立binky.o的命令——在調用C編譯器的時候使用,不管之前是否設定過(實際上可以有多行命令)。實質上,依賴行是一個觸發器,用於設定什麼時候去做什麼是。命令則指明了做什麼。
命令列之前必須有tab定位字元用於縮排——如果只是用空格的話是沒用的,雖然在你的編輯器中多個的空格看起來和一個定位字元是一樣的。(這樣設定的原因是在make設計之初,當make的設計者意識到tab格式是一個很糟糕的設計的時候,他們依然維持了原先的設計只是為了相容之前的版本——雖然那時之後10個使用者。這就是“向後”在“向後相容”這個詞中的原因。最好不要再考慮這個問題了。)
因為空白格和tab的問題,請確保你的編輯器可以分清多個空格和tab。當你在一些終端程式中使用複製/粘貼的時候,這可能會引發一些問題。為了確定在行首是否有一個tab字元,請將游標移至行首然後向右移動一個字元。如果游標移動了8個位置,那麼那個位置是一個tab字元,如果是一個空格一個空格的移動的,那麼你得刪除所有的空格然後輸入一個定位字元。
在標準的編譯中,命令列可以省略,make會更具源檔案的格式使用預設的建立規則,.c是C檔案,.f是Fortran檔案等等。預設的C檔案建立規則如下..
$(CC) $(CFLAGS) -c source-file.c
我們普遍會依賴與上面的預設建立規則——大部分的調整可以用修改CFLAGS變數來實現。下面十一個簡單的但非常具有代表性的makefile。它編譯了下面的檔案:main.c, binky.c, binky.h, akbar.c,akbar.h, 和defs.h。這些檔案會產生一些中間檔案——main.0,binky.o和akbar.o。這些檔案最後會連結在一起,產生一個可執行檔program。在makefile中空白行會被忽略,而注釋的標誌是'#'。
## A simple makefile
CC = gcc
CFLAGS = -g -I/usr/class/cs107/include
LDFLAGS = -L/usr/class/cs107/lib -lgraph
PROG = program
HDRS = binky.h akbar.h defs.h
SRCS = main.c binky.c akbar.c
## This incantation says that the object files
## have the same name as the .c files, but with .o
OBJS = $(SRCS:.c=.o)
## This is the first rule (the default)
## Build the program from the three .o's
$(PROG) : $(OBJS)
tab$(CC) $(LDFLAGS) $(OBJS) -o $(PROG)
## Rules for the source files -- these do not have
## second build rule lines, so they will use the
## default build rule to compile X.c to make X.o
main.o : main.c binky.h akbar.h defs.h
binky.o : binky.c binky.h
akbar.o : akbar.c akbar.h defs.h
## Remove all the compilation and debugging files
clean :
tabrm -f core $(PROG) $(OBJS)
## Build tags for these sources
TAGS : $(SRCS) $(HDRS)
tabetags -t $(SRCS) $(HDRS)
第一條命令是用三個.o檔案來編譯器,接下來是定義了三個命令比如
“main.o : main.c binky.h akbar.h defs.h”
用來定義.o目標檔案的源檔案依賴,
這些規則定義了要建立些什麼檔案,但是命令列被省略掉了,因此會採用預設的規則來建立.o檔案,使用和.c檔案相同的檔案名稱。最後,make會自動識別X.o的源檔案X.c,所以X.c在規則也可以被省略。所以第一條規則可以去掉main.c,改寫成這樣:
"main.o : binky.h akbar.h defs.h".
接下來的命令,clean和TAGS,也執行了一些使過程簡化的操作。clean命令是用來移除所有的目標檔案、可執行檔,如果你沒有調試過,有意個核心檔案也會被移除,所以你可以從原始檔案來執行建立。如果你想上初所有的編譯和調試所產生的檔案的話,你可以用make clean命令。如果你換了一個系統,這個系統和你目標庫原始編譯的系統的機構不一樣,那麼你也需要使用make clean 命令,而且你需要從草稿重新編譯。TAGS規則用來建立一些標籤,Unix的編寫者可以用它來作為搜尋的標誌。
在Emacs中進行編譯
Emacs對編譯過程有著內建的支援。鍵入M-x compile即可在emacs中編譯你的代碼。你會得到一個編譯命令提示。如果你寫了makefile,只要輸入make並斷行符號,makefile就會被讀取,一些命令就會執行。在這個時候Emacs的緩衝區就會分離開來,編譯錯誤資訊會放在最新產生的緩衝區。為了到錯誤產生的代碼所在的行,只需把游標定位在錯誤資訊上,並按^c-^c,這樣就會跳轉到對應的代碼上(“cc”是很早的一個C編譯器的簡稱)。