前面我們已經介紹過了如何通過gcc編譯代碼組建檔案,但是,當項目較多的時候,往往則需要一個自動化的編譯工具輔助我們完成這項操作。像Windows那樣通過Ctrl+F5即可一鍵完成項目所有編譯工作。
Makefile文法基礎
在Linux下,自動化編譯工具是通過make命令來完成的(一些工具廠商也提供了它們自己的make命令,如gmake等),make命令的基本格式如下:
make [-f makefile] [label]
它可以通過-f參數指定輸入檔案,當省略-f參數時,預設輸入檔案名稱為Makefile,由於我們通常不用這個-f參數,往往就用預設的Makefile檔案名稱。
Makefile是一個文字檔,它是基於一定的文法規則的,它的基本執行規則定義如下:
target : [prerequisites]
command
- target 標籤,用於標誌當前構建的規則,它也可以是檔案。
- prerequisites 依賴項,在構建該標籤的時候先執行的規則
- command make需要執行的命令。(任意的Shell命令)
注意:Makefile的target是頂格寫的,而Command需要加一個Tab鍵。我這裡為了排版看起來舒服點,每一行都多加了一個Tab鍵,如果要使用本文的Makefile樣本,請去掉各行的第一個Tab鍵,否則make的時候報錯。
例如,我們編寫一個簡單的Makefile:
clean:
@echo "clean"
all:
@echo "all"
當我們直接執行make命令的時候,輸出如下:
tianfang > make
clean
tianfang > make all
all
tianfang > make clean
clean
從中我們可以看到:預設情況下構建第一個標籤。可以通過在命令列參數中通過參數構建指定標籤。
然後我們再來看看依賴性是如何工作的,這次我們修改一下Makefile,讓all標籤依賴於clean標籤:
clean :
@echo "clean"
all : clean
@echo "all"
再次執行make all的時候,發現會先執行clean標籤:
tianfang > make all
clean
all
用Makefile來構建項目
通過對Makefile的文法有一個簡單的瞭解後,下面就可以用Makefile簡化我們的構建操作了。還是針對前面的那個stack的例子吧,首先我們來實現一個最簡單的例子:
all :
gcc -o run main.c stack.c
這樣直接通過make命令就可以實現對gcc -o run main.c stack.c整條命令的執行了。
更加一步,我們想實現增量編譯,則要實現如下規則:
- 如果這個工程沒有編譯過,那麼我們的所有C檔案都要編譯並被連結。
- 如果這個工程的某幾個C檔案被修改,那麼我們只編譯被修改的C檔案,並連結目標程式。
這個時候就需要前面的依賴性出馬了:
run : stack.o main.o
gcc -o run main.o stack.o
stack.o : stack.c
gcc -c stack.c
main.o : main.c
gcc -c main.c
這裡的target都是檔案,run預設依賴於stack.o和main.o,因此,當構建run的時候,就會先構建stack.o和main.o,輸出方式如下:
tianfang > make
gcc -c stack.c
gcc -c main.c
gcc -o run main.o stack.o
當我們只改了其中某個檔案的時候,例如stack.c,這是由於main.c沒有變化,因此不會重新編譯main.o,只會重新構建stack.o和run,從而實現我們的增量編譯的目的。(這個其實才是make比shell或指令碼語言編寫的批處理方式要強大的地方)
tianfang > make
gcc -c stack.c
gcc -o run main.o stack.o
通過自動推導改進Makefile
通過上面的例子可以看到,雖然我們可以實現增量編譯,但是整個Makefile過程是非常複雜的,需要對每個.o檔案編寫編譯指令碼。如果專案檔較多,並且有增刪的話,則編寫Makefile檔案非常麻煩。
為了改進這個問題,makefile提供了一個自動推導的功能,通過它可以簡化我們的編寫過程。例如,前面的例子可以簡化如下:
CC = gcc
objs = stack.o main.o
run : $(objs)
$(CC) -o run $(objs)
這裡我們引入了兩個變數,第一個行的CC制定了編譯器為gcc(如果不指定則是預設的cc),第二行制定了我們的obj檔案。
這樣,只需要執行make命令即可生產我們的程式:
tianfang > make
gcc -c -o stack.o stack.c
gcc -c -o main.o main.c
gcc -o run stack.o main.o
可可以看到,make命令會自動推匯出如何根編出.o檔案來。如果我們的專案檔變化了,只需要改objs變數即可,非常方便。
不過,有的時候我們可能覺得這中自動推導的方式不夠用,需要手動控制編譯選項,這個時候我們可以自己指定推導規則:
CC = gcc
objs = stack.o main.o
run : $(objs)
$(CC) -o run $(objs)
$(objs): %.o: %.c
$(CC) -c -g $< -o $@
Makefile的功能非常強大,對應的文法也是非常複雜的,由於網上已經有人寫得很詳細了,因此我這裡並不打算系統的介紹Makefile的各種規則,如果想進一步的瞭解,可以參考跟我一起寫 Makefile這篇文章。