自己寫一個簡單通用的Makefile

轉自:http://blog.csdn.net/u011913612/article/details/52102241

一.makefile的作用

          Makefile是用於自動編譯和鏈接的,一個工程有很多文件組成,每一個文件的改變都會導致工程的重新鏈接,但是不是所有的文件都需要重新編譯,Makefile中記錄有文件的信 息,在make時會決定在鏈接的時候需要重新編譯哪些文件。Makefile的宗旨就是:讓編譯器知道要編譯一個文件需要依賴其他的哪些文件。當那些依賴文件有了改變,編譯器會自動發現最終的生成文件已經過時,而應該重新編譯相應的模塊。 makefile帶來的好處就是—"自動化編譯",一旦寫好,只需要一個make命令,整個工程完全自動編譯,極大的提高了軟件開發的效率。默認的情況下,make命令會在當前目錄下按順序找尋文件名爲"GNUmakefile"、"makefile"、"Makefile"的文件,找到了解釋這個文件。當然也可以使用make -f DIR/makefile 來指定用於makefile文件

二.makefile的幾點基礎知識

      1.賦值符號的區別
         =  是最基本的賦值,用到了之後才賦值,不能在變量後追加內容
     := 是覆蓋之前的值,立即賦值,可以在變量後追加內容
    ?= 是如果沒有被賦值過就賦予等號後面的值
    += 是添加等號後面的值

2.自動變量

    $<    第一個依賴文件的名稱
    $?    所有的依賴文件,以空格分開,這些依賴文件的修改日期比目    標的創建日期晚
    $@  目標的完整名稱
    $^    所有的依賴文件,以空格分開,不包含重複的依賴文件

3.幾個常用的函數

1. $(patsubst %.c,%.o,x.c.c bar.c)
把字串“x.c.cbar.c”符合模式[%.c]的單詞替換成[%.o],返回結果是“x.c.obar.o”
2.$(filter <pattern...>,<text> )
以<pattern>模式過濾<text>字符串中的單詞,保留符合模式<pattern>的單詞。可以有多個模式。 
3.$(filter-out <pattern...>,<text> )
4.$(foreach <var>,<list>,<text> )
把參數<list>中的單詞逐一取出放到參數<var>所指定的變量中, 然後再執行<text>所包含的表達式。每一次<text>會返回一個字符串,循環這個過程。
5.shell函數,例如files := $(shell echo *.c)

三.通用Makefile的編譯過程

       從頂層開始遞歸進入子目錄,當進入到一個目錄的最底層時,開始使用編譯器編譯,再將該層的所有.o文件打包成build-in.o,返回它的上一層目錄再遞歸進入子目錄,當編譯完所有的子目錄後,就開始編譯頂層的.c文件,最後將頂層的.o文件和頂層每個子目錄的build-in.o鏈接成我們的目標文件。

思維導圖:


四、實戰

假如有這樣一個目錄結構的工程

a

----d

----a.c

b

----b.c

c

----c.c

main.c



頂級Makefile:

CROSS_COMPILE =
AS		= $(CROSS_COMPILE)as
LD		= $(CROSS_COMPILE)ld
CC		= $(CROSS_COMPILE)gcc
CPP		= $(CC) -E
AR		= $(CROSS_COMPILE)ar
NM		= $(CROSS_COMPILE)nm

STRIP		= $(CROSS_COMPILE)strip
OBJCOPY		= $(CROSS_COMPILE)objcopy
OBJDUMP		= $(CROSS_COMPILE)objdump

export AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMP

CFLAGS := -Wall -O2 -g

LDFLAGS := 

export CFLAGS LDFLAGS

TOPDIR := $(shell pwd)
export TOPDIR

TARGET := test


 obj-y += main.o
 obj-y += a/
 obj-y += b/
 obj-y += c/

all : 
	make -C ./ -f $(TOPDIR)/Makefile.build
	$(CC) $(LDFLAGS) -o $(TARGET) built-in.o


clean:
	rm -f $(shell find -name "*.o")
	rm -f $(shell find -name "*.d")
	rm -f $(TARGET)
.PHONY:all clean 


這裏前面就是定義一些變量,all是工程默認的目標,它是一個僞目標,進入僞目標後執行的命令就是執行Makefile.build,這裏就會引起遞歸調用,在Makefile.build中又會調用Makefile.build.一直到Makefile.build返回以後,會使用Makefile.build最後生成的built-in.o生成最終的目標文件。

Makefile.build:

PHONY := build
build :
obj-y :=
subdir-y :=
include Makefile
__subdir-y	:= $(patsubst %/,%,$(filter %/, $(obj-y)))
subdir-y	+= $(__subdir-y)
subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o)
cur_objs := $(filter-out %/, $(obj-y))
dep_files := $(foreach f,$(cur_objs),.$(f).d)
#dep_files := $(wildcard $(dep_files))
#ifneq ($(dep_files),)
#  include $(dep_files)
#endif
PHONY += $(subdir-y)
build : $(subdir-y) built-in.o
$(subdir-y):
	make -C $@ -f $(TOPDIR)/Makefile.build
built-in.o : $(cur_objs) $(subdir_objs)
	$(LD) -r -o $@ $^
dep_file = [email protected]
%.o : %.c
	$(CC) $(CFLAGS) -Wp,-MD,$(dep_file) -c -o $@ $<
.PHONY : $(PHONY)

Makefile.build會加載頂級目錄下的Makefile,在頂級目錄下的Makefile中已經給obj-y添加了一些條目,subdir-y就是獲取子目錄,然後對每一個子目錄又調用Makefile.build。

當遞歸到沒有子目錄的目錄時,Makefile.build開始返回,並使用$(CC)對源文件進行編譯,將所有的.c生成.o文件,並將當前目錄下的.o和子目錄下的build-in.o連接成當前目錄下的build-in.o,並回返上級目錄,一次往復,最終返回到頂級目錄,在頂級目錄下生成build-in.o。返回到頂級目錄後,Makefile.build返回到了Makefile中,Makefile在使用build-in.o生成指定的目標文件。至此,遞歸結束,整個系統編譯完成。

這裏的-MD選項和[email protected]的作用是 生成頭文件的依賴關係時,把依賴關係寫入到這個文件中去。

要想徹底看懂的話,建議認真推倒下每一個變量的值,比對目錄與文件,會更好理清編譯的過程。

子目錄下的Makefile:

obj-y += a.o
obj-y += d/

子目錄的Makefile就是添加該目錄中的文件與子目錄,子目錄以/結尾。

驗證:

執行make

打印如下:

make -C ./ -f /home/dragon/liujinwei/uni-makefile/Makefile.build
make[1]: Entering directory `/home/dragon/liujinwei/uni-makefile'
make -C a -f /home/dragon/liujinwei/uni-makefile/Makefile.build
make[2]: Entering directory `/home/dragon/liujinwei/uni-makefile/a'
make -C d -f /home/dragon/liujinwei/uni-makefile/Makefile.build
make[3]: Entering directory `/home/dragon/liujinwei/uni-makefile/a/d'
gcc -Wall -O2 -g -Wp,-MD,.d.o.d -c -o d.o d.c
ld -r -o built-in.o d.o
make[3]: Leaving directory `/home/dragon/liujinwei/uni-makefile/a/d'
gcc -Wall -O2 -g -Wp,-MD,.a.o.d -c -o a.o a.c
ld -r -o built-in.o a.o d/built-in.o
make[2]: Leaving directory `/home/dragon/liujinwei/uni-makefile/a'
make -C b -f /home/dragon/liujinwei/uni-makefile/Makefile.build
make[2]: Entering directory `/home/dragon/liujinwei/uni-makefile/b'
gcc -Wall -O2 -g -Wp,-MD,.b.o.d -c -o b.o b.c
ld -r -o built-in.o b.o
make[2]: Leaving directory `/home/dragon/liujinwei/uni-makefile/b'
make -C c -f /home/dragon/liujinwei/uni-makefile/Makefile.build
make[2]: Entering directory `/home/dragon/liujinwei/uni-makefile/c'
gcc -Wall -O2 -g -Wp,-MD,.c.o.d -c -o c.o c.c
ld -r -o built-in.o c.o
make[2]: Leaving directory `/home/dragon/liujinwei/uni-makefile/c'
gcc -Wall -O2 -g -Wp,-MD,.main.o.d -c -o main.o main.c
ld -r -o built-in.o main.o a/built-in.o b/built-in.o c/built-in.o
make[1]: Leaving directory `/home/dragon/liujinwei/uni-makefile'
gcc  -o test built-in.o

#### make completed successfully  ####


make之後的目錄:

頂級:


a目錄:


d目錄:




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章