開始使用Linux編程時,一個很討厭的問題就是如何寫Makefile文件,由于在Linux下不像在Windows下那么熟悉,有那么多好的軟件(也許是對Linux孤陋寡聞了)。雖然象Kylix和Anjuta這樣的集成編譯環境,但是Kylix太大太慢,用它編寫console程序不亞于高射炮打蚊子——大材小用,而Anjuta又太不穩定,況且字體有那么難看。不說了,還是言歸正傳,看看Makefile該如何編寫。
1. 簡單的GCC語法:
如果你只有一個文件(或者只有幾個文件),那么就可以不寫Makefile文件(當然有Makefile更加方便),用gcc直接編譯就行了。在這里我們只介紹幾個我經常用的幾個參數,第一是 “-o”,它后面的參數表示要輸出的目標文件,再一個是 “-c”,表示僅編譯(Compile),不連接(Make),如果沒有”-c”參數,那么就表示連接,如下面的幾個命令:
gcc –c test.c,表示只編譯test.c文件,成功時輸出目標文件test.o
gcc –c test.c –o test.o ,與上一條命令完全相同
gcc –o test test.o,將test.o連接成可執行的二進制文件test
gcc –o test test.c,將test.c編譯并連接成可執行的二進制文件test
gcc test.c –o test,與上一條命令相同
gcc –c test1.c,只編譯test1.c,成功時輸出目標文件test1.o
gcc –c test2.c,只編譯test2.c,成功時輸出目標文件test2.o
gcc –o test test1.o test2.o,將test1.o和test2.o連接為可執行的二進制文件test
gcc –c test test1.c test2.c,將test1.o和test2.o編譯并連接為可執行的二進制文件test
注:如果你想編譯cpp文件,那么請用g++,否則會有類似如下莫名其妙的錯誤:
cc3r3i2U.o(.eh_frame+0x12): undefined reference to `__gxx_personality_v0’......
還有一個參數是”-l”參數,與之緊緊相連的是表示連接時所要的鏈接庫,比如多線程,如果你使用了pthread_create函數,那么你就應該在編譯語句的最后加上”-lpthread”,”-l”表示連接,”pthread”表示要連接的庫,注意他們在這里要連在一起寫,還有比如你使用了光標庫curses,那么呢就應該在后面加上”-lcurses”,比如下面的寫法:
gcc –o test test1.o test2.o –lpthread –lcurses
當然gcc的參數我感覺有幾百個,不過我們平時在x86機器上用的就這么些,況且這里也不是GCC教程,所以,就此打住。
2. Makefile基本語法
我這里沒有Makefile的詳細設計書,只是憑著看別人的Makefile文件和一些網上的參考資料,作一些簡單的介紹(我自己理解的,不對的地方還請各位老大們指出,鄙人將不甚感激)
2.1 目標:
大家在看別人使用Makefile文件時肯定經常見到有的人常用make all, make install, make clean等命令,同樣只有一個Makefile文件,那么all、install、clean參數是如何控制Makefile文件的運行呢(這句話有問題,但我不知道該怎么說,大家能看懂我的意思,就放我一馬吧)?在這里,如果向上面的命令如果能夠正確運行的話,那么在Makefile文件里一定有這樣的幾行,他們的開始一定是
all: ×××××××
×××××××××××
install: ××××××
×××××××××××
clean: ×××××××××
×××××××××××
當然也不盡然,因為all,install,clean我們可以用其他的變量來代替,但是著了我們就簡單起見,就下定論了,各位別怪。
在上面提到的all,install,clean等就是我們所說的目標。make all命令,就告訴make我們將執行all所指定的目標。為了便于理解Make程序的流程,我們給大家看一個與gcc毫無關系的Makefile文件:
# #表示Makefile文件中的注釋,下面是Makefile文件的具體內容
all:
@echo you have typed command “make all”
clean:
@echo you have typed command “make clean”
install:
@ehco you have typed command “make $@”
#Makefile文件結束
注意在這里,all:、clean:、install:行要頂格些,而所有的@echo前要加tab鍵來跳格縮進。下面是運行結果:
[root@xxx test]#cat Makefile
# #表示Makefile文件中的注釋,下面是Makefile文件的具體內容
all:
@echo you have typed command “make all”
clean:
@echo you have typed command “make clean”
install:
@ehco you have typed command “make $@”
[root@xxx test]#make all
you have typed command “make all”
[root@xxx test]#make clean
you have typed command “make clean”
[root@xxx test]#make install
you have typed command “make install”
[root@xxx test]#
不知大家注意到沒有,我們在Makefile文件里有一個符號$@,其中$表示變量名,其后的要當作變量來解釋,$@是Makefile預先定義的一個變量,表示目標命令,比如在上面的文件里屬于install目標,那么$@就表示install,同樣,如果你將clean目標下面的加引號的”make clean”換為:”make $@”,那么命令make clean的輸出與原來是一摸一樣的。大家可以下來試試。
2.2 依賴
我們現在提出這樣一個問題:我如何用一個make命令將替代所有的make all, make install,make clean命令呢?當然我們可以象剛才那樣寫一個Makefile文件:
[root@xxx test]#cat Makefile
# #表示Makefile文件中的注釋,下面是Makefile文件的具體內容
all:
@echo you have typed command “make all”
clean:
@echo you have typed command “make clean”
install:
@ehco you have typed command “make $@”
doall:
@echo you have typed command “make $@l”
@echo you have typed command “make all”
@echo you have typed command “make clean”
@ehco you have typed command “make install”
[root@xxx test]#make doall
you have typed command “make doall”
you have typed command “make all”
you have typed command “make clean”
you have typed command “make install”
[root@xxx test]#
在這里,doall:目標有4調語句,他們都是連在一起并都是由tab鍵開始的。當然,這樣能夠完成任務,但是太笨了,我們這樣來寫:
[root@xxx test]#cat Makefile
# #表示Makefile文件中的注釋,下面是Makefile文件的具體內容
all:
@echo you have typed command “make all”
clean:
@echo you have typed command “make clean”
install:
@ehco you have typed command “make $@”
doall: all clean install
@echo you have typed command “make $@l”
[root@xxx test]#make doall
you have typed command “make all”
you have typed command “make clean”
you have typed command “make install”
you have typed command “make doall”
[root@xxx test]#
相信大家已經看清了doall:的運行方式,它先運行all目標,然后運行clean目標,然后是install,最后是自己本身的目標,并且每個$@還是保持著各自的目標名稱。
在這里,我們稱all, clean, install為目標doall所依賴的目標,簡稱為doall的依賴。也就是你要執行doall,請先執行他們(all, clean, install),最后在執行我的代碼。
注意依賴一定是Makefile里面的目標,否則你非要運行,結局是注定的:
[root@xxx test]#cat Makefile
all:
@echo you have typed command “make all”
xxx: all WAHAHA:
[root@xxx test]make xxx
you have typed command “make all”
make: *** No rule to make target ‘WAHAHA’, needed by `xxx’, Stop.
【輕松一下】我們能否利于“相互依賴”來作弄一下make?
[root@xxx test]#cat Makefile
tar1: tar2
tar2: tar1
@echo this line cann’t be shown on you screen!
[root@xxx test]make tar1
make: Circular tar2 <- tar1 dependency dropped.
呵呵,騙不了的
3.實戰:
有了上面的說明,我們就可以開始寫一些弱智一些地Makefile文件了。比如我們有如下的文件:
tmp/
+---- include/
| +---- f1.h
| +----f2.h
+----f1.c
+----f2.c
+---main.c
其中f1.c中#include “include/f1.h”,f2.c中#include”include/f2.h”,main.c中又#include”include/f1.h”, #include”include/f2.h”,主函數在main.c中,要將他們聯合起來編譯為目標為testmf的文件,我們就可以按下面的方式寫(當然是弱智的):
[root@xxx test]#cat Makefile
main: main.o f1.o f2.o
gcc –o testmf main.o f1.o f2.o
f1.o: f1.c
gcc –c –o file1.o file1.c
f2.o: f2.c
gcc –c –o file2.o file2.c
main.o
gcc –c –o main.o main.c
clean:
rm –rf f1.o f2.o main.o testmf
[root@xxx test]make
gcc –c –o main.o main.c
gcc –c –o file1.o file1.c
gcc –c –o file2.o file2.c
gcc –o testmf main.o f1.o f2.o
[root@xxx test]ls
f1.c f1.o f2.c f2.o main.c main.o include/ testmf
如果你的程序沒有問題的話,就應該可以執行了./testmf
大家可能發現問題了:對目標文件f1.o f2.o和main.o,他們的寫法是如此的類似,我們能夠將他們一塊寫?有的,不過今天沒時間了,以后再寫吧。