嵌入式之行(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的確瞭解不太深入。望諸君見諒!

推薦閱讀:

查看原文 >>
相關文章