uboot 頂層makefile細節分析

    uboot的源文件衆多,學習龐然大物首先找到脊椎--頂層的makfile,逐一破解。可是,uboot的makefile一樣是一個龐然大物,因此也要找到它的主線。假若過度專一部分細節,很難作到把握全局,實際上也不可能很好理解細節。html

    介於此,筆者已經寫了一篇uboot makefile總體解析,能夠先從主體上把握makefile。而後,再讀這篇makefile強大功能實現的細節,才能作到按部就班。linux

    說明:uboot頂層makefile的註釋機會所有源碼都搬上來了,而註釋都是黑體加粗以與源碼有強烈的區別。ios

VERSION = 1            //主版本號git

PATCHLEVEL = 1       //次級版本號shell

SUBLEVEL = 6架構

EXTRAVERSION =     //版本號擴展app

U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)   //這個Uboot的版本爲1.1.6ide

VERSION_FILE = $(obj)include/version_autogenerated.h函數

//生成uboot的版本信息post

HOSTARCH := $(shell uname -m | \

       sed -e s/i.86/i386/ \

           -e s/sun4u/sparc64/ \

           -e s/arm.*/arm/ \

           -e s/sa110/arm/ \

           -e s/powerpc/ppc/ \

           -e s/macppc/ppc/)

//獲取主機架構,筆者電腦是酷睿雙核,#uname –m輸出結構是i686,執行替換命令sed -e s/i.86/i386/後,就變成了i386,

//而且將這個值保存在HOSTARCH變量中。

HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \

           sed -e 's/\(cygwin\).*/cygwin/')

//獲取主機操做系統,執行#uname -s能夠查看本身的操做系統類型,筆者使用的操做系統是Linux。tr '[:upper:]' '[:lower:]'

//具備大寫轉小寫的功能,因此最終HOSTOS的值爲linux。

export     HOSTARCH HOSTOS

//將兩個變量導出,能夠供嵌套的makefile調用

# Deal with colliding definitions from tcsh etc.

VENDOR=

#########################################################################

# U-boot build supports producing a object files to the separate external

# directory. Two use cases are supported:

# 1) Add O= to the make command line

# 'make O=/tmp/build all'

# 2) Set environement variable BUILD_DIR to point to the desired location

# 'export BUILD_DIR=/tmp/build'

# 'make'

# The second approach can also be used with a MAKEALL script

# 'export BUILD_DIR=/tmp/build'

# './MAKEALL'

# Command line 'O=' setting overrides BUILD_DIR environent variable.

# When none of the above methods is used the local build is performed and

# the object files are placed in the source directory.

//上邊的註釋講的是uboot支持編譯時,將目標文件生成在其餘的目錄中,這樣能夠保持源文件的乾淨,另外從目標

//文件的生成目錄中查看生成的文件時一目瞭然。若是想要這樣作,提供了兩種方法。第一種方法是執行命令

//#make O=/tmp/build all;第二種方法,能夠先在命令行模式定義環境變量export BUILD_DIR=/tmp/build',

//而後再執行make就好了。一般懶惰的編譯方式就是讓生成的目標文件和源文件混在一塊兒,那麼BUILD_DIR就會沒定義。

ifdef O

ifeq ("$(origin O)", "command line")

BUILD_DIR := $(O)

endif

endif

//這是第一種方法的實現過程,$(origin O)是輸出變量O的定義來源,假設在命令行模式輸//入#make O=/tmp/build all

//來編譯,那麼O的定義來源是命令行,函數的輸出是command line。

ifneq ($(BUILD_DIR),)

//假若BUILD_DIR定義過了,也就是不但願目標文件與源文件混在一塊兒,那麼直到「endif # //ifneq ($(BUILD_DIR),)」

//的內容都有效;假若沒有定義BUILD_DIR,那麼這部分代碼將不起做用。

saved-output := $(BUILD_DIR)

// 將BUILD_DIR保存在saved-output變量中

# Attempt to create a output directory.

$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})

//-d是判斷BUILD_DIR是否存在,假若不存在就就建立。mkdir的-p參數表明若路徑中的

//某些目錄不存在,也一併建立

# Verify if it was successful.

//覈查BUILD_DIR是否已經建立

BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)

//試圖進入$(BUILD_DIR),假若能進入,則將它的路徑賦給BUILD_DIR,注意這時的BUILD_DIR已是一個真實存在

//目錄的代言人,而以前的只是但願建立的目錄。須要說明的是假若BUILD_DIR尚未建立,那麼cd $(BUILD_DIR)將執

//行錯誤,返回值是空,雖然這時發生錯誤,可是編譯會忽略這個錯誤還能繼續進行

$(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))

// 若是沒有建立成功,就執行error函數,輸出信息output directory "$(saved-output)" does not exist),而後編譯終止

endif # ifneq ($(BUILD_DIR),)

 

OBJTREE             := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))

//若是BUILD_DIR不爲空,目標目錄就等於BUILD_DIR;假若沒定義,就取爲當前目錄

SRCTREE   := $(CURDIR)//源文件目錄等於當前文件夾

TOPDIR         := $(SRCTREE)//頂層目錄等於源文件目錄

LNDIR           := $(OBJTREE)  //鏈接目錄等於BUILD_DIR

export     TOPDIR SRCTREE OBJTREE//將這三個變量導出

 

MKCONFIG   := $(SRCTREE)/mkconfig//指定mkconfig的位置

export MKCONFIG //將MKCONFIG變量導出

 

ifneq ($(OBJTREE),$(SRCTREE)) //若是目標目錄和源文件目錄不相等

REMOTE_BUILD        := 1  //就定義REMOTE_BUILD變量並取值爲1

export REMOTE_BUILD    //而後再將變量導出

endif

 

# $(obj) and (src) are defined in config.mk but here in main Makefile

# we also need them before config.mk is included which is the case for

# some targets like unconfig, clean, clobber, distclean, etc.

//obj和src的定義也出如今了頂層目錄的config.mk中,可是config.mk中的定義因爲受下邊//紅色加粗判斷語句的影響,只有

//在「make *_config」執行後($(OBJTREE)/include/config.mk就會存在),再執行的make程序才能將config.mk包含進頂

//層makefile中。而在沒有先執行「make *_config」或者$(OBJTREE)/include/config.mk不存在的狀況下,若是想執行unconfig

//, clean, clobber, distclean,而這些命令用到了變量obj、src,因此這裏提早包含進去。

//可是我也有疑惑,爲何不能將頂層目錄的config.mk包含在全局中,設計者爲何要把它放在條件執行裏邊。

ifneq ($(OBJTREE),$(SRCTREE))

obj := $(OBJTREE)/

src := $(SRCTREE)/

else

obj :=

src :=

endif

export obj src

//定義變量obj和src,並將這兩個變量導出,obj是編譯目標文件的前綴,從而實現生成的目標文件在於源文件相區別的目錄中

#########################################################################

ifeq ($(OBJTREE)/include/config.mk,$(wildcard $(OBJTREE)/include/config.mk))

//這個ifeq橫跨的範圍很是廣,用紅色加粗字體代表。只有$(OBJTREE)/include/config.mk存在(也就是說已經

//執行了make *.config)的狀況下,這部分包含的內容纔有效

# load ARCH, BOARD, and CPU configuration

include $(OBJTREE)/include/config.mk   //將make *_config生成的config.mk文件包含進來

export     ARCH CPU BOARD VENDOR SOC //導出5個變量以供其餘子目錄的makefile調用

 

ifndef CROSS_COMPILE

ifeq ($(HOSTARCH),ppc)

CROSS_COMPILE =

else

ifeq ($(ARCH),ppc)

CROSS_COMPILE = powerpc-linux-

endif

ifeq ($(ARCH),arm)

CROSS_COMPILE = arm-linux- //根據變量ARCH可使這行知足條件,肯定了交叉編譯使//用arm-linux-

endif

ifeq ($(ARCH),i386)

ifeq ($(HOSTARCH),i386)

CROSS_COMPILE =

else

CROSS_COMPILE = i386-linux-

endif

endif

ifeq ($(ARCH),mips)

CROSS_COMPILE = mips_4KC-

endif

ifeq ($(ARCH),nios)

CROSS_COMPILE = nios-elf-

endif

ifeq ($(ARCH),nios2)

CROSS_COMPILE = nios2-elf-

endif

ifeq ($(ARCH),m68k)

CROSS_COMPILE = m68k-elf-

endif

ifeq ($(ARCH),microblaze)

CROSS_COMPILE = mb-

endif

ifeq ($(ARCH),blackfin)

CROSS_COMPILE = bfin-elf-

endif

ifeq ($(ARCH),avr32)

CROSS_COMPILE = avr32-

endif

endif

endif

 

export     CROSS_COMPILE  //導出交叉編譯變量

 

# load other configuration

include $(TOPDIR)/config.mk //將頂層的config.mk也包含進來

#########################################################################

# U-Boot objects....order is important (i.e. start must be first)

 

OBJS  = cpu/$(CPU)/start.o

ifeq ($(CPU),i386)

OBJS += cpu/$(CPU)/start16.o

OBJS += cpu/$(CPU)/reset.o

endif

ifeq ($(CPU),ppc4xx)

OBJS += cpu/$(CPU)/resetvec.o

endif

ifeq ($(CPU),mpc83xx)

OBJS += cpu/$(CPU)/resetvec.o

endif

ifeq ($(CPU),mpc85xx)

OBJS += cpu/$(CPU)/resetvec.o

endif

ifeq ($(CPU),mpc86xx)

OBJS += cpu/$(CPU)/resetvec.o

endif

ifeq ($(CPU),bf533)

OBJS += cpu/$(CPU)/start1.o      cpu/$(CPU)/interrupt.o  cpu/$(CPU)/cache.o

OBJS += cpu/$(CPU)/cplbhdlr.o   cpu/$(CPU)/cplbmgr.o   cpu/$(CPU)/flush.o

endif

//肯定目標文件構成

OBJS := $(addprefix $(obj),$(OBJS))

//給待生成的目標文件帶上路徑

LIBS  = lib_generic/libgeneric.a

LIBS += board/$(BOARDDIR)/lib$(BOARD).a

LIBS += cpu/$(CPU)/lib$(CPU).a

ifdef SOC

LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a

endif

LIBS += lib_$(ARCH)/lib$(ARCH).a

LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \

       fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a

LIBS += net/libnet.a

LIBS += disk/libdisk.a

LIBS += rtc/librtc.a

LIBS += dtt/libdtt.a

LIBS += drivers/libdrivers.a

LIBS += drivers/nand/libnand.a

LIBS += drivers/nand_legacy/libnand_legacy.a

LIBS += drivers/sk98lin/libsk98lin.a

LIBS += post/libpost.a post/cpu/libcpu.a

LIBS += common/libcommon.a

LIBS += $(BOARDLIBS)

//肯定庫文件構成

LIBS := $(addprefix $(obj),$(LIBS))

//給待生成的庫文件帶上路徑

.PHONY : $(LIBS)

//將帶生成的庫文件做爲僞目標來處理

# Add GCC lib

PLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc

//添加系統標準庫

# The "tools" are needed early, so put this first

# Don't include stuff already done in $(LIBS)

SUBDIRS      = tools \

         examples \

         post \

         post/cpu

.PHONY : $(SUBDIRS)

//定義make執行要首先處理的目錄

ifeq ($(CONFIG_NAND_U_BOOT),y)

NAND_SPL = nand_spl

U_BOOT_NAND = $(obj)u-boot-nand.bin

endif

//倘若定義了CONFIG_NAND_U_BOOT,那麼將總目標all中添加$(obj)u-boot-nand.bin,不然不添加

__OBJS := $(subst $(obj),,$(OBJS))

__LIBS := $(subst $(obj),,$(LIBS))

//OBJS、LIBS除去$(obj)部分的路徑

#########################################################################

ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)

//總目標的構成,也能夠人爲添加其餘的目標

all:          $(ALL)

//定義總目標all

$(obj)u-boot.hex:   $(obj)u-boot

              $(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@

//生成16進制的可執行程序

$(obj)u-boot.srec:   $(obj)u-boot

              $(OBJCOPY) ${OBJCFLAGS} -O srec $< $@

$(obj)u-boot.bin:    $(obj)u-boot

              $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@

//生成2進制的可執行程序

$(obj)u-boot.img:   $(obj)u-boot.bin

              ./tools/mkimage -A $(ARCH) -T firmware -C none \

              -a $(TEXT_BASE) -e 0 \

              -n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \

                     sed -e 's/"[      ]*$$/ for $(BOARD) board"/') \

              -d $< $@

$(obj)u-boot.dis:    $(obj)u-boot

              $(OBJDUMP) -d $< > $@

//生成反彙編文件u-boot.dis

$(obj)u-boot:         depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)

              UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\

              cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \

                     --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \

                     -Map u-boot.map -o u-boot

//生成elf格式的u-boot文件

$(OBJS):

              $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))

//中間目標文件生成

$(LIBS):

              $(MAKE) -C $(dir $(subst $(obj),,$@))

//中間庫文件生成

$(SUBDIRS):

              $(MAKE) -C $@ all

//SUBDIRS目錄處理

$(NAND_SPL):     version

              $(MAKE) -C nand_spl/board/$(BOARDDIR) all

$(U_BOOT_NAND):     $(NAND_SPL) $(obj)u-boot.bin

              cat $(obj)nand_spl/u-boot-spl-16k.bin $(obj)u-boot.bin > $(obj)u-boot-nand.bin

version:

              @echo -n "#define U_BOOT_VERSION \"U-Boot " > $(VERSION_FILE); \

              echo -n "$(U_BOOT_VERSION)" >> $(VERSION_FILE); \

              echo -n $(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion \

                      $(TOPDIR)) >> $(VERSION_FILE); \

              echo "\"" >> $(VERSION_FILE)

//版本頭文件生成

gdbtools:

              $(MAKE) -C tools/gdb all || exit 1

updater:

              $(MAKE) -C tools/updater all || exit 1

env:

              $(MAKE) -C tools/env all || exit 1

depend dep:

              for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done

//執行make depend或者make dep都能觸發命令

tags ctags:

              ctags -w -o $(OBJTREE)/ctags `find $(SUBDIRS) include \

                            lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \

                            fs/cramfs fs/fat fs/fdos fs/jffs2 \

                            net disk rtc dtt drivers drivers/sk98lin common \

                     \( -name CVS -prune \) -o \( -name '*.[ch]' -print \)`

etags:

              etags -a -o $(OBJTREE)/etags `find $(SUBDIRS) include \

                            lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \

                            fs/cramfs fs/fat fs/fdos fs/jffs2 \

                            net disk rtc dtt drivers drivers/sk98lin common \

                     \( -name CVS -prune \) -o \( -name '*.[ch]' -print \)`

$(obj)System.map: $(obj)u-boot

              @$(NM) $< | \

              grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \

              sort > $(obj)System.map

//生成map文件

#########################################################################

else

all $(obj)u-boot.hex $(obj)u-boot.srec $(obj)u-boot.bin \

$(obj)u-boot.img $(obj)u-boot.dis $(obj)u-boot \

$(SUBDIRS) version gdbtools updater env depend \

dep tags ctags etags $(obj)System.map:

       @echo "System not configured - see README" >&2

       @ exit 1

endif

//假若沒有include $(OBJTREE)/include/config.mk 文件,將打印出錯誤信息「System not configured

//- see README」,而且make程序也結束

.PHONY : CHANGELOG

CHANGELOG:

       git log --no-merges U-Boot-1_1_5.. | \

       unexpand -a | sed -e 's/\s\s*$$//' > $@

#########################################################################

unconfig:

       @rm -f $(obj)include/config.h $(obj)include/config.mk \

              $(obj)board/*/config.tmp $(obj)board/*/*/config.tmp

//刪除配置文件,即與make *_config過程相反

//剩下的*_config,屬於類似內容,只舉一個常見的smdk2410_config,以做說明

smdk2410_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0

//執行make smdk2410_config,就會調用頂層目錄中的mkconfig(shell腳本),同時給這個腳本傳入參數

//arm arm920t smdk2410 NULL s3c24x0。mkconfig腳本根據這些傳入的參數生成與開發板相適應的一系列配置文件。

#########################################################################

//不要忘記makfile的最後邊,還有一部分很重要的內容

clean: //clean刪除
find $(OBJTREE) -type f \
\( -name 'core' -o -name '*.bak' -o -name '*~' \
-o -name '*.o' -o -name '*.a' \) -print \
| xargs rm -f
rm -f $(obj)examples/hello_world $(obj)examples/timer \
$(obj)examples/eepro100_eeprom $(obj)examples/sched \
$(obj)examples/mem_to_mem_idma2intr $(obj)examples/82559_eeprom \
$(obj)examples/smc91111_eeprom $(obj)examples/interrupt \
$(obj)examples/test_burst
rm -f $(obj)tools/img2srec $(obj)tools/mkimage $(obj)tools/envcrc \
$(obj)tools/gen_eth_addr
rm -f $(obj)tools/mpc86x_clk $(obj)tools/ncb
rm -f $(obj)tools/easylogo/easylogo $(obj)tools/bmp_logo
rm -f $(obj)tools/gdb/astest $(obj)tools/gdb/gdbcont $(obj)tools/gdb/gdbsend
rm -f $(obj)tools/env/fw_printenv $(obj)tools/env/fw_setenv
rm -f $(obj)board/cray/L1/bootscript.c $(obj)board/cray/L1/bootscript.image
rm -f $(obj)board/netstar/eeprom $(obj)board/netstar/crcek $(obj)board/netstar/crcit
rm -f $(obj)board/netstar/*.srec $(obj)board/netstar/*.bin
rm -f $(obj)board/trab/trab_fkt $(obj)board/voiceblue/eeprom
rm -f $(obj)board/integratorap/u-boot.lds $(obj)board/integratorcp/u-boot.lds
rm -f $(obj)include/bmp_logo.h
rm -f $(obj)nand_spl/u-boot-spl $(obj)nand_spl/u-boot-spl.map

clobber: clean //除了調用clean刪除,還要再執行額外的刪除命令
find $(OBJTREE) -type f \( -name .depend \
-o -name '*.srec' -o -name '*.bin' -o -name u-boot.img \) \
-print0 \
| xargs -0 rm -f
rm -f $(OBJS) $(obj)*.bak $(obj)ctags $(obj)etags $(obj)TAGS $(obj)include/version_autogenerated.h
rm -fr $(obj)*.*~
rm -f $(obj)u-boot $(obj)u-boot.map $(obj)u-boot.hex $(ALL)
rm -f $(obj)tools/crc32.c $(obj)tools/environment.c $(obj)tools/env/crc32.c
rm -f $(obj)tools/inca-swap-bytes $(obj)cpu/mpc824x/bedbug_603e.c
rm -f $(obj)include/asm/proc $(obj)include/asm/arch $(obj)include/asm
[ ! -d $(OBJTREE)/nand_spl ] || find $(obj)nand_spl -lname "*" -print | xargs rm -f

ifeq ($(OBJTREE),$(SRCTREE))
mrproper \
distclean: clobber unconfig //假若目標目錄與源目錄相同,distclean調用clobber unconfig來刪除
else
mrproper \
distclean: clobber unconfig //假若目標目錄與源目錄不相同,distclean調用clobber unconfig來刪除
rm -rf $(OBJTREE)/*         //並且,還要刪除OBJTREE目錄下的全部內容
endif

backup: //打包備份
F=`basename $(TOPDIR)` ; cd .. ; \                         //跳到當前目錄的外邊,而後打包整個文件夾
gtar --force-local -zcvf `date "+$ $F-%Y-%m-%d-%T.tar.gz"` $ $F //打包,名字中插入日期

#########################################################################

//最後,插入編譯過程當中重要的打印信息(要深刻理解u-boot的makefile工做原理,必須作實際的實驗,實際的make一下,

//看看你正想了解的地方發生了什麼)

start.o

libsmdk2410.a

libarm920t.a

all目標的實現

 

參考博客:U-Boot Makefile分析

相關文章
相關標籤/搜索