嵌入式之行(5):我的Makefile
嵌入式之行(5):我的Makefile |
嵌入式Linux | |||
作者:李遲 | |||
2011-01-15 20:11 | |||
說明: 1、文中多處出現Makefile,它可以認為是一個具體的文件——即文件名就是「Makefile」,也可以認為它是抽象的「Makefile」,比如下文說到的「兩個Makefile」,它們的名稱肯定是不同的,但它們都是「Makefile」。——不知這樣說,閣下能不能明白,我也沒有好的文字表達了。 2、本文以小笑自己從網路、書籍總結的Makefile模板來講一下有關的Makefile知識、技巧,當然,不可能很完整,不過能正常使用。
本文不打算講述Makefile的來源、好處以及其它一些理論的知識,有關Makefile的知識可以寫成一本書。網路上的《跟我一起寫 Makefile》是一篇很好的文章,建議看一下。此處給出小笑的一個Makefile例子,它能應付基本的項目管理。小笑的畢業設計程序就是在這個Makefile基礎上修改而來的。閑話不說,進入主題。 先來看一下具體的Makefile模板文件。其中的紅色為小笑寫的注釋。 ###################################### # my Makefile template # # Uage: # compile:"make all" or just "make" # clean:"make clean" # # ChangeLog: # 2010-4-21: # add some info # 2010-4-20: # new for DEBUG # another way to change SRCS to OBJECTS # another way to generate .o file ########################################
### 宏定義DEBUG,我沒有找到好的辦法,只好出此下策。 DEBUG = y
# 這些就是傳說中的編譯器了,比如CC是C編譯器、CPP是C++編譯器,其它們都是宏來著。 # 也可以定義其它一些編譯器,比如交叉編譯器CROSS_COMPILER=arm-linux-gcc等等。 CC = gcc CPP = g++
CROSS_COMPILER = arm-linux-gcc
###=====================================
#####>>>>>!!!!! C編譯的一些標誌,如打開警告,調試標誌等 !!!!!!<<<<<##### ### C CFLAGS = -Wall
##! 這裡就是添加調試或優化標誌,當然,也可以在上述宏中使用,不必這樣麻煩。 ifeq ($(DEBUG), y) CFLAGS += -g else CFLAGS += -O2 endif
#####>>>>>!!!!! 這是C++語言編譯的一些標誌,同C !!!!!!<<<<<##### ### C++ CPPFLAGS = -Wall
##! 一樣的 ifeq ($(DEBUG), y) CPPFLAGS += -g else CPPFLAGS += -O2 endif
###=====================================
#####>>>>>!!!!! 這些是別的一些宏,如鏈接庫位置、名稱等(我不知如何移稱呼這個宏,百度吧) !!!!!!<<<<<##### ### 如-lpthread or -lncurses or -lpanel or -lmenu or -lm,等。 ### 注意:有些庫不是Linux默認的,比如多線程的pthread,如果編譯時不加上的話,編譯是不會通過的,此外,還有ncurses庫、SDL庫等等,要注意一下。 ### 在此處添加 LDFLAGS = LDFLAGS +=
# 這個是刪除使用到的宏 RM = rm -rf
###=====================================
### 此處添加目標名稱 #####>>>>>!!!!! 最好起一個有意義的名稱,比如採集視頻數據的,可以是capture,顯示用的,可以是display,等等 !!!!!!<<<<<##### target =
### 此處添加目標文件(即.o文件) OBJECTS = .o OBJECTS += .o
### 這是生成.o文件的另外一種方法,有點麻煩,但也可以。 # 此處添加源文件 #SRCS = #SRCS +=
# 生成相應的.o文件 #OBJECTS = $(SRCS:.c=.o) #OBJECTS = $(SRCS:.cpp=.o) ###=====================================
### 真正的編譯開始,all是一個偽目標,編譯時的「make all」中的「all」就是它。 all: $(target)
############################## # 這是另外一種方法: # foo.o:foo.c foo.h(可以不使用.h文件的!) # (tab) $(CC) $(CPPFLAGS) -c $< -o $@ # # thread.o: thread.cpp thread.h # $(CC) $(CPPFLAGS) -c $< -o $@ # main.o: main.cpp thread.cpp # $(CC) $(CPPFLAGS) -c $< -o $@ # # 這種方法就是實打實的,需要什麼,添加什麼,我也用過,也不麻煩,不妨一試。 ############################## # 這種方法簡便一些 # 上述列出了所有用到的.o文件,都是依賴條件。 # $^:所有的依賴文件,$<:第一個依賴文件,$@:目標文件(可執行的程序) $(target): $(OBJECTS) $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@
# 清除,比如中間文件,目標文件。 clean: @echo "Cleaning..." $(RM) $(OBJECTS) $(target) @echo "Done"
# 安裝。其實這個命令除了顯示一些信息外,什麼事也沒做。 # 因為一般程序編譯安裝都是:make;make install;這樣做,以防萬一。 # 也可試試刪除這幾行,執行一下make install,看看效果。 install: @echo " Note:" @echo "To install or not install,that is the question" @echo
# 聲明瞭三個偽目標 .PHONY:all clean install
### end of the Makefile
Makefile的格式如下所示: target:prerequisites command 下面是《跟我一起寫 Makefile》中的介紹: target也就是一個目標文件,可以是ObjectFile,也可以是執行文件。還可以是一個標籤(Label)。 prerequisites就是要生成那個target所需要的文件。 command就是make需要執行的命令。(任意的Shell命令)
下文所講的「目標文件」,可能是指生成的「可執行文件」,也可能是指這裡的「target」,假設讀者應當能區別出來。 需要執行的
在使用Makefile來make程序時,最常見的提示信息就是「Nothing to be done for XXX」,很多人看到這個信息,很不理解(試一下那個make install測試吧)。我認為在兩種情況下會出現這個提示信息,第一種情況,這個程序已經編譯過一次了,已經生成了.o和可執行文件了,所有目標均已是最新,不需要再做一次「無用功」了——make是何等講究效率!如果你想每次make all都從編譯一次,可以在Makefile的all後添加clean,這個clean必須放在第一位置才能在每次編譯前都清除一些中間生成的文件(.o文件)和目標文件。如下: all: clean $(target) 不要擔心clean在後面纔出現,Makefile不管先後的。 另一種情況是聲明瞭一個「偽目標」,但又make它,就會出現上面的提示信息,比如上述的Makefile模板中,如果去掉與目標install相關的幾行語句,但最後卻聲明它是「偽目標」的話,當執行「make install」後,就會出現「Nothing to be done for install」。
當make一個不存在的目標時,會提示: Make: *** No rule to make target 『XXX』. Stop. 因為make確實找不到XXX,當然也不會去執行了。 make程序時,也不一定是「make all」,只要是一個在Makefile文件中出現的目標文件(target)即可。如果你將「all」改為「love」的話,你輸入「make love」,照樣能順利通過編譯,閣下不妨一試。
此外,還有一些make的技巧,比如一個程序要應用於兩個平臺(我寫的視頻採集程序要在PC上執行,也要在ARM上執行),程序是不用改多少的。最關鍵的就是編譯器,當應用於ARM平臺時,只要修改Makefile中的編譯器就可以了。所以我的工程目錄下有兩個Makefile,比如PC平臺中的為Makefile,而ARM平臺的為arm-Makefile。這樣,在編譯PC平臺的程序時,可以直接「make all」,因為make首先找到的是Makefile,就不會執行到arm-Makefile了。那麼,交叉編譯怎麼辦呢?make有一個-f選項,可以選擇自定義的Makefile。不過在編譯過程中,出現了一些問題,我明明在Makefile中指明瞭編譯器為arm-linux-gcc了,但編譯過程中有些文件還是被gcc所編譯,造成鏈接的失敗,我實在沒有辦法,只能顯式指定CC選項了。這樣,在交叉編譯時,簡單的「make all」,就變成了「make –f arm-Makefile CC=arm-linux-gcc」,由於同時要處理兩個平臺,不得不出此下策了。(其實也沒有多麼麻煩。)
我還發現gcc的-M選項的好處。此選項是在編譯時指定某一個宏,在條件編譯中特別有用。在PC機上,攝像頭的設備文件為/dev/video,但在ARM開發板上卻為/dev/video1,我也不知是怎麼搞的,在開發板中,video不是video1的鏈接文件,我試了幾次,結果還是一樣。沒辦法,只好在程序顯式使用宏定義來指定設備文件名稱了。不過,為了方便,我使用了條件編譯。如下: #ifdef __ARM__ #define device 「/dev/video1」 #else #define device 「/dev/video」 #endif
即使用__ARM__來選擇設備文件的名稱。 這時,gcc的-D選項就派上用場了,在arm-Makefile文件中的CFLAGS中添加了-D__ARM__,在Makefile文件中不添加,這樣就很好解決了兩個平臺的設備文件名稱問題了。當然,如果僅僅只是針對ARM平臺的話,這些也用不著,但不失為一種方法。(更正:以前寫成「-M」選項是錯誤的!正確的選項為「-D」,特此說明並致歉!) 本著「夠用即可」的原則,不再詳細介紹Makefile了。畢竟,小笑也不太懂。 上述的Makefile可以認為是一個Makefile的模板,可以在它的基礎上修改,成為適合自己的工程的「Makefile」,再總結出一個自己的Makefile模板。
再:寫完這篇文章後,我讀了幾次,感覺不像我寫作風格,總是表達不出想要表達的意思。自己文筆水平是一個問題,二來對Makefile的確瞭解不太深入。望諸君見諒!
|
推薦閱讀: