這篇主要分析Makefile文件開頭部分對與一些編譯環境以及編譯路徑等變量的初始化linux
VERSION = 2010shell
PATCHLEVEL = 12api
SUBLEVEL =bash
EXTRAVERSION =架構
ifneq "$(SUBLEVEL)"""工具
U_BOOT_VERSION =$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)測試
else字體
U_BOOT_VERSION =$(VERSION).$(PATCHLEVEL)$(EXTRAVERSION)spa
Endif操作系統
U_BOOT_VERSION = 2010.12
TIMESTAMP_FILE =$(obj)include/timestamp_autogenerated.h
VERSION_FILE = $(obj)include/version_autogenerated.h
HOSTARCH := $(shell uname -m | \
sed-e s/i.86/i386/ \
-e s/sun4u/sparc64/ \
-e s/arm.*/arm/ \
-e s/sa110/arm/ \
-e s/ppc64/powerpc/ \
-e s/ppc/powerpc/ \
-e s/macppc/powerpc/\
-e s/sh.*/sh/)
$(shell uname -m)表示執行uname –m的shell命令,他執行的結果是取出機器硬件名(這臺機器是64位的,因此其獲得的結果就是x86-84)。sed –e s/abc/def 的意思是尋找結果裏面是否有’abc’字樣,若是有,就用‘def’字樣將其代替。
因此整條語句的意思就是,取出uname –m的結果,並在其中尋找是否有i.86字樣,若是有就用i386代替,是否有arm.*(*是通配符),若是有,就用arm代替……,本臺機的結果是x86-64因此還保持原樣,沒有用其餘字符替換,因此HOSTARCH=x86-64
HOSTOS := $(shell uname -s | tr'[:upper:]' '[:lower:]' | \
sed -e 's/cygwin.*/(cygwin/')
uname –s 表示取出本機操做系統內核名稱,(這臺機器是Linux),tr ‘[:upper:]’’[:lower:]’表示將結果裏面的大寫字符所有換成小寫字符,sed –e‘s/Cygwin.*/Cygwin/’表示尋找結果裏面是否有cygwin.*字符,若是有就用cygwin替換
因此整句話的結果就是HOSTOS = linux(注意第一個字母L被換成了小寫l)
SHELL := $(shell if [ -x "
BASH; \
else if [-x /bin/bash];then echo /bin/bash;\
else echosh;fi;fi
SHELL if[-x filename] 表示若是這個filename文件是可執行的,則爲真,顯然BASH變量是空的,則SHELL= /bin/bash 就是說shell命令的路徑位於/bin/bash目錄下
export HOSTARCH HOSTOS SHELL
表示將這些變量導出,供別的文件使用
ifeq (,$(findstring s,$(MAKEFLAGS)))
XECHO = echo
else
XECHO = :
Endif
If$( findstring string,text )表示從text中尋找string字樣,若是找到了則返回text,若是沒找到就返回空,顯然MAKEFLAGS爲空,因此 $(findstring s,$(MAKEFLAGS)) 返回爲空,if語句爲真,XECHO =echo,執行非靜默編譯,不然,執行靜默編譯(靜默編譯的意思就是在編譯時控制檯不打印任何信息)
ifdef O
ifeq ("$(origin O)", "commandline")
BUILD_DIR := $(O)
endif
endif
這句話表示若是在編譯的命令裏面加入參數O(如 make O=/tmp).,就把O後面所指定的值賦給變量BUILD_DIR,(BUILD_DIR表示uboot的編譯路徑)
ifneq ($(BUILD_DIR),)
saved-output := $(BUILD_DIR)
若是BUILD_DIR不爲空,則將BUILD_DIR的值賦給saved-output
$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})
若是BUILD_DIR是一個目錄名稱,就把該目錄建立出來
BUILD_DIR := $(shell cd $(BUILD_DIR) &&/bin/pwd)
$(if $(BUILD_DIR),,$(error output directory"$(saved-output)" does not exist))
Endif
shell命令的意思是先進入到BUILD_DIR目錄,而後調用PWD命令顯示當前路徑名,並把當前路徑名賦給BUILD_DIR變量,If語句判斷BUILD_DIR是否存在,若是還爲空就顯示錯誤(命令裏面兩個逗號之間表示空)
OBJTREE :=$(if $(BUILD_DIR),$(BUILD_DIR),$(CURIDR)) //輸出目錄
SRCTREE :=$(CURDIR) //源碼目錄
TOPDIR :=$(SRCTREE) //頂層目錄
LNDIR :=$(OBJTREE) //鏈接目錄
export TOPDIR SRCTREE OBJTREE
先看CURIDR變量,這是一個MAKEFILE的內嵌變量,表明當前路徑,爲了驗證,能夠以下作個測試:
在/tmp/test目錄下新建一個Makefile文件,在裏面添加以下內容:
all::
@echo $(CURDIR)
而後在/tmp/test路徑下執行Make命令,顯示的就是/tmp/test
若是定義了BUILD_DIR,就將BUILD_DIR賦給OBJTREE,若是沒有定義,就將CURIDR賦給OBJTREE
……
因此若是編譯時沒有定義編譯路徑,及沒有定義BUILD_DIR,則以上全部變量都是CURDIR,即當前目錄,也就是uboot的頂層目錄
MKCONFIG :=$(SRCTREE)/mkconfig)
export MKCONFIG
ifneq ($(OBJTREE),$(SRCTREE))
REMOTE_BUILD := 1
export REMOTE_BUILD
endif
若是源碼目錄與輸出目錄不想等,則置位REMOTE_BUILD變量
ifneq ($(OBJTREE),$(SRCTREE))
obj := $(OBJTREE)/
src := $(SRCTREE)/
else
obj :=
src :=
endif
export obj src
若是源碼目錄和輸出目錄不想等,則變量obj和src被賦予相應的值,不然,兩變量都爲空
SUBDIRS =tools\
examples/standalone\
examples/api
.PHONY : $(SUBDIRS)
SUBIDRS被賦予了三個路徑名稱,並申明SUBIDRS是一個僞目標
這篇主要分析跟編譯和鏈接庫文件相關的變量
ifeq ($(obj)include/config.mk,$(wildcard $(obj)include/config.mk))
……
else # !config.mk
all $(obj)u-boot.hex $(obj)u-boot.srec $(obj)u-boot.bin \
$(obj)u-boot.img $(obj)u-boot.dis $(obj)u-boot \
$(filter-out tools,$(SUBDIRS)) $(TIMESTAMP_FILE) $(VERSION_FILE) \
updater depend dep tags ctags etags cscope $(obj)System.map:
@echo "System not configured - see README" >&2
@ exit 1
tools:
$(MAKE) -C $@ all
endif # config.mk
ifeq語句判斷include目錄下是否存在config.mk文件(經過《uboot系列之-----uboot配置過程詳細分析》一文可知,只有執行了make xxx_config以後,才存在該文件)。
咱們暫時不分析config.mk存在時的狀況,由於太長了,先分析config.mk不存在的狀況,即else後的語句,該語句把全部的目標都指向了一個依賴,就是打印出錯信息「System not configured – see README」,而後退出。
接下來看config.mk存在的狀況:
all:
sinclude $(obj)include/autoconf.mk.dep
sinclude $(obj)include/autoconf.mk
先看目標all: 從makefile開始到如今,發現這個all是第一個目標,也就是說當咱們執行make命令時,它是默認目標,可是該目標既沒有依賴也沒有規則,那它起到什麼做用呢?接着往下看,sinclude $(obj)include/autoconf.mk.dep,他的意思是包含autoconf.mk.dep文件,進入到該文件,發現第一句是:
include/autoconf.mk: include/common.h \,它也是一個目標,而且有不少的依賴
再回到all的做用上來,若是沒有這個all,那麼當咱們執行make 時,根據make法則,(它將第一個目標做爲他的默認目標),也就是將autoconf.mk做爲默認目標,顯然這不是咱們想要的結果,(咱們執行make是想編譯整個文件,也就是執行第365行的all: $(ALL)這個規則,而在這裏加上一個看起來「毫無心義」的all,就能達到咱們想要的目的,即執行第一個空的all目標後,會跳過autoconf.mk目標,執行下一個all目標)
再來看sinclude ,sinclude也是包含文件的關鍵詞,他與include的區別就是使用sinclude時,即便所須要包含的文件不存在,也不影響makefile的往下執行。
而autoconf.mk.dep和autoconf.mk這兩個文件是怎麼來的呢,在makefile中有以下一段語句,說明了這兩個文件的來源:
$(obj)include/autoconf.mk.dep: $(obj)include/config.h include/common.h
@$(XECHO) Generating $@ ; \
set -e ; \
: Generate the dependancies ; \
$(CC) -x c -DDO_DEPS_ONLY -M $(HOSTCFLAGS) $(CPPFLAGS) \
-MQ $(obj)include/autoconf.mk include/common.h > $@
$(obj)include/autoconf.mk: $(obj)include/config.h
@$(XECHO) Generating $@ ; \
set -e ; \
: Extract the config macros ; \
$(CPP) $(CFLAGS) -DDO_DEPS_ONLY -dM include/common.h | \
sed -n -f tools/scripts/define2mk.sed > $@.tmp && \
mv $@.tmp $@
這段規則沒有徹底看明白,望大俠們幫我分析一下
include $(obj)include/config.mk
export ARCH CPU BOARD VENDOR SOC
包含配置過程當中生成的config.mk文件,並把該文件中的變量導出來
ifeq ($(ARCH),arm)
CROSS_COMPILE =/usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
Endif
若是ARCH=arm,即體系架構爲ARM ,就給變量CROSS_COMPILE賦值,改變量的值表明了交叉編譯器的路徑
include $(TOPDIR)/config.mk
包含頂層目錄下的config.mk文件
下面開始定義了目標文件變量(跟ARM無關的這裏就省略沒有列出來)
OBJS = $(CPUDIR)/start.o
這裏的CPUDIR目錄在頂層目錄的config.mk文件裏面定義 CPUDIR=arch/$(ARCH)/cpu/$(CPU),對於smdk4412來講CPUDIR= arch/arm/cpu/armv7
……
OBJS := $(addprefix $(obj),$(OBJS))
在原有OBJS值前面加上一個路徑$(obj)前綴,指明絕對路徑,若是$(obj)爲空,則OBJS至關於沒變化
下面開始定義了庫文件變量(跟硬件平臺無關的這裏就省略沒有列出來)
…….
LIBS += $(shell if [ -f board/$(VENDOR)/common/Makefile ]; then echo \
"board/$(VENDOR)/common/lib$(VENDOR).o"; fi)
LIBS += $(CPUDIR)/lib$(CPU).o
ifdef SOC
LIBS += $(CPUDIR)/$(SOC)/lib$(SOC).o
Endif
對於smdk4412來講,board/Samsung/common/Makefile文件並不存在,因此第一個賦值無效,接下來的兩個分別爲
LIBS+= arch/arm/cpu/armv7/libarmv7.o
LIBS+= arch/arm/cpu/armv7/exynos/libexynos.o
…..
LIBS += arch/$(ARCH)/lib/lib$(ARCH).o
LIBS+= arch/arm/lib/libarm.o
ifeq ($(SOC),exynos)
LIBS += $(CPUDIR)/s5p-common/libs5p-common.o
Endif
LIBS+= arch/arm/cpu/armv7/s5p-common/libs5p-common.o
LIBBOARD = board/$(BOARDDIR)/lib$(BOARD).o
LIBBOARD := $(addprefix $(obj),$(LIBBOARD))
BOARDDIR定義在頂層目錄的config.mk文件中 BOARDDIR = $(VENDOR)/$(BOARD) 對於smdk4412來講
BOARDDIR = Samsung/smdk4212
LIBBOARD = board/samsung/smdk4212/libsmdk4212.o
PLATFORM_LIBGCC = -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) –lgcc
PLATFORM_LIBS += $(PLATFORM_LIBGCC)
export PLATFORM_LIBS
dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`取得交叉編譯器的libgcc.a的絕對路徑
$(CC) = $(CROSS_COMPILE)gcc
$(CFLAGS)定義在config.mk
-L表示編譯時將其後所跟的目錄做爲第一個尋找庫文件的目錄
LDPPFLAGS += \
-include $(TOPDIR)/include/u-boot/u-boot.lds.h \
$(shell $(LD) --version | \
sed -ne 's/GNU ld version [0−9][0−9]∗\.[0−9][0−9]∗.*/-DLD_MAJOR=\1 -DLD_MINOR=\2/p')
$(LD)定義在頂層目錄的config.mk文件
LD= $(CROSS_COMPILE)ld,表示交叉編譯器裏面的鏈接工具
shell $(LD) –version表示取得鏈接工具的版本,我這裏的版本號是」GNU ld (GNU Binutils for Ubuntu) 2.22」
ifeq ($(CONFIG_NAND_U_BOOT),y)
NAND_SPL = nand_spl
U_BOOT_NAND = $(obj)u-boot-nand.bin
endif
ifeq ($(CONFIG_ONENAND_U_BOOT),y)
ONENAND_IPL = onenand_ipl
U_BOOT_ONENAND = $(obj)u-boot-onenand.bin
ONENAND_BIN ?= $(obj)onenand_ipl/onenand-ipl-2k.bin
endif
__OBJS := $(subst $(obj),,$(OBJS))
__LIBS := $(subst $(obj),,$(LIBS)) $(subst $(obj),,$(LIBBOARD))
$subst(<from>,<to>,<text>)的功能是將<text>中的<from>字符串換成<to>字符串
__OBJS至關於截取OBJS的相對路徑,__LIBS相似
本篇主要分析目標文件以及相關的依賴文件
ALL += $(obj)u-boot.srec $(obj)u-boot.bin$(obj)System.map $(U_BOOT_NAND) $(U_BOOT_ONENAND)
all: $(ALL)
當用make命令編譯時,執行的就是這個all變量
$(obj)u-boot.hex: $(obj)u-boot
$(OBJCOPY)${OBJCFLAGS} -O ihex $< $@
Vuboot.hex是hex格式的文件。它的依賴u-boot屬於ELF文件,OBJCOPY和OBJCFLAGS定義在頂層config.mk文件,
OBJCOPY = $(CROSS_COMPILE)objcopy ;objcopy命令的做用是將一種格式的文件拷貝成另一種格式的文件。
OBJCFLAGS += --gap-fill=0xff; --gap-fill = 0xff是objcopy的參數,表示在拷貝過程當中,用0xff來填充段與段之間的空隙。
$<表示全部的依賴,即u-boot文件,$@表示全部的目標,即u-boot.hex
整句話的意思就是將u-boot拷貝成u-boot.hex
$(obj)u-boot.srec: $(obj)u-boot
$(OBJCOPY)-O srec $< $@
相似的,將u-boot拷貝成u-boot.srec文件
$(obj)u-boot.bin: $(obj)u-boot
$(OBJCOPY)${OBJCFLAGS} -O binary $< $@
$(BOARD_SIZE_CHECK)
ifeq ($(CONFIG_S5PC210),y)
./mkbl2u-boot.bin bl2.bin 14336
Endif
將u-boot拷貝成u-boot.bin文件,同時執行$(BOARD_SIZE_CHECK),在Makefile前面部分,有以下語句定義了BOARD_SIZE_CHECK:
ifneq($(CONFIG_BOARD_SIZE_LIMIT),)
BOARD_SIZE_CHECK=\
@actual='wc -d $@ |awk '{print $$!}';\
limit=$(CONFIG_BOARD_SIZE_LIMIT);\
if test
limit; then\
echo "$@ exceeds file size limit:";\
echo "limit:$$limit bytes";\
echo "actual: $$actual bytes";\
echo "excess:$$((actual-limit))bytes";\
exit 1;\
fi
else
BOARD_SIZE_CHECK =
Endif
首先看CONFIG_BOARD_SIZE_LIMIT是否被定義了(是否認義了,在autoconf.mk文件中查看,它的意思是規定了文件大小的上限),若是沒定義,BOARD_SIZE_CHECK爲空,若是定義了,就比較目標文件的實際大小與他的上限大小(CONFIG_BOARD_SIZE_LIMIT的值),若是前者大於後者,就退出,如前者小於後者,則繼續執行。
接着分析上面藍色字體的部分
若是在autoconf.mk中定義了CONFIG_S5PC210 = y,則執行mkbl2命令,這個命令是幹嗎的呢?目前還不清楚
$(obj)u-boot.ldr: $(obj)u-boot
$(CREATE_LDR_ENV)
$(LDR)-T $(CONFIG_BFIN_CPU)-c $@ $<$(LDR_FLAGS)
$(BOARD_SIZE_CHECK)
生成u-boot.ldr文件
$(obj)u-boot.ldr.hex: $(obj)u-boot.ldr
$(OBJCOPY)${OBJCFLAGS}-O ihex $<$@ -I binary
$(obj)u-boot.ldr.srec: $(obj)u-boot.ldr
$(OBJCOPY)${OBJCFLAGS} -O srec $< $@ -I binary
生成u-boot.ldr.hex和u-boot.ldr.sec文件
$(obj)u-boot.img: $(obj)u-boot.bin
$(obj)tools/mkimage -A $(ARCH) -T firmware -C none\
-a$(CONFIG_SYS_TEXT_BASE) -e 0\
-n$(shell sed -n -e 's/.*U_BOOT_VERSION//p'$(VERSION_FILE)|\
sed -e 's/''[]*$$/for $(BOARD)board''/' )\
-d$<$@
Mkimage是給u-boot.bin增長0x40個字節大小的頭部信息的工具,其各個參數的意義以下:
-A 所支持的體系架構,這裏是arm
-T 鏡像類型
-C none 不壓縮
-a 指定映像在內存中的加載地址,映像下載到內存中的時候,要按照這個參數來加載
-e 指定映像運行的入口地址
-n 指定映像名稱
-d 指定生成映像的源文件
$(obj)u-boot.imx: $(obj)u-boot.bin
$(obj)tools/mkimage -n $(IMX_CONFIG) -T imximage\
-e $(CONFIG_SYS_TEXT_BASE) -d $<$@
$(obj)u-boot.kwb: $(obj)u-boot.bin
$(obj)tools/mkimage -n $(CONFIG_SYS_KWD_CONFIG) -T kwbimage\
-a $(CONFIG_SYS_TEXT_BASE) -e $(CONFIG_SYS_TEXT_BASE) -d $<$@
相似於上面
$(obj)u-boot.dis:$(obj)u-boot
$(OBJDUMP) -d $< > $@
經過u-boot文件生成u-boot.dis (u-boot.dis是u-boot的反彙編代碼)
GEN_UBOOT = \
UNDEF_SYS='$(OBJDUMP) -x $(LIBBOARD) $(LIBS)|\
sed -n -e 's/.*SYM_PREFIX)__u_boot_cmd_.*/-u\1/p'sprt|uniq;\
cd $(LDR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS)\
--start-group $(__LIBS) --end-group $(P;ATFORM_LIBS)
-Map u-boot.map -o u-boot
由鏈接工具生成u-boot文件,並生成映射文件u-boot.map,具體的細節我也沒看很懂
$(obj)u-boot: depend\
$(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
$(GEN_UBOOT)
這個是ELF格式的u-boot文件生成規則,後面那些變量都是它的依賴
先看depend
depend dep: $(TIMESTAMP_FILE) $(VERSION_FILE) \
$(obj)include/autoconf.mk\
$(obj)include/generated/generic -asm-foosets.h
for dir in $(SUBDIRS) $(CPUDIR) $(dir $(LDSCRIPT));do\
$(MAKE) -C $$ dir _depend;done
依次進入到$(SUBDIRS)$(CPUDIR)和$(dir $(LDSCRIPT))目錄,執行make _depend命令。_depend定義在頂層目錄的rules.mk文件中,總的說來,依賴depend就是在相關的目錄下生成.depend文件,.depend文件的內容列出了每一個目標文件的依賴文件
UBOOT的Makefile至此就大體分析完了,可能還有些地方說的不是很明白,望你們指正,接下來就要分析源碼了