前面我們已經介紹過了如何通過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這篇文章。