VERSION = 12 V$ a" d: @8 U% p" A
PATCHLEVEL = 15 _# L, Z$ i$ B9 |
SUBLEVEL = 65 p* d& ~+ C8 {& ^
EXTRAVERSION =' |& h) n0 e# H& \' N! Q+ w
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
2 d9 E$ A, }, _5 K d5 l6 bVERSION_FILE = $(obj)include/version_autogenerated.h #版本文件
, e l# ^" J6 e
# uname 命令将正在使用的操作系统名写到标准输出中3 V u. V5 l$ A& u" }
# -m 显示硬件运行系统的机器 ID 号
# 定义变量,HOSTARCH里面存储的是机器ID号,即主机架构类型$ Q8 |. i0 D/ d7 w
HOSTARCH := $(shell uname -m | \ #可以把shell脚本写到makefile里面,这是make中的shell function, 相当于shell中的命令替换。
' N, c" A) {/ o sed -e s/i.86/i386/ \ #shell uname -m 获得机器硬件名称
, F( t: s; Q7 ]$ N* t/ V -e s/sun4u/sparc64/ \ #sed -e s/arm.*/arm/ 的意思是把前缀为arm的所有模式替换为arm。结合前面的uname命令来理解就是:, D! C: z& X+ Y+ d9 y
-e s/arm.*/arm/ \ #把uname -m的结果(主机架构类型或者称为机器ID号)通过管道传递给sed命令,然后把前缀为arm的所有模式替换为arm。
3 X9 f5 A2 s* }4 t- L' Q -e s/sa110/arm/ \ #sed的语法:sed [ -n ] Script [ File ... ]% ]$ v2 p5 l1 s0 N z
-e s/powerpc/ppc/ \ # sed [ -n ] [ -e Script ] ... [ -f ScriptFile ] ... [ File ... ]! d: I4 X8 c9 o0 m0 c' Y
-e s/macppc/ppc/) #sed 命令根据编辑脚本,去修改指定的 File 文件(这里file是作为一个输入参数的)的行,并将其写到标准输出。( v9 }0 |1 h# z4 m$ E" ]
#sed 命令包含很多功能,用于选择要修改的行(请注意,sed是针对行进行操作的),并只对选择的行作更改。
) }+ B3 H$ m* q4 r #sed 命令使用两个工作空间来保留修改的行:保留选定行的 "模式空间" 和暂时存储行的 "保留空间"。, R& M( X& Z* [
#这里所谓的编辑脚本是由单独的子命令构成的,每个单独的行上对应着一个子命令。sed 子命令的一般格式如下:[address-range] function[modifiers],即:[地址范围] 函数[修改符]& k2 Y7 K9 D5 e6 B" r( ~) B# d7 {
#sed 命令通过将一个输入行读入模式空间,依次应用所有的 sed 子命令(这些子命令的地址选择了该行),4 o$ i7 {; x+ T0 G" g
#并将模式空间写到标准输出来处理每个输入的 File 输入文件(file就是输入参数)。然后清除模式空间,并对输入的 File 中指定的每行重复该过程。
, z: V6 K) r, Y6 i% |6 N8 s #一些 sed 子命令使用保留空间来保存后继检索的所有的,或部分的模式空间。
" _' ^' A7 I0 Q9 m5 U( D) u0 J #当命令包含地址(行号或搜索模式)时,该命令只会对被寻址的行起作用。否则,该命令适用于所有的行。, c0 z! Z p0 J, v: x* f
#注意,这里的参数"-e"的意思是:使用 Script 变量作为编辑脚本。如果你只使用一个 -e 标志并且不使用-f 标志,则可以省略 -e 标志。
S T8 q6 g9 k #注意,这里的 "sed -e s/arm.*/arm/"表示把前缀为arm的所有模式替换为arm,也就是sed s/pattern/replacement/flags
* S! A5 G, {' H- n& m #用 replacement 字符串代替在模式空间中首次出现的 pattern 参数。除了空格或换行符,在 s 子命令之后显示的任何字符都能代替 /(斜杠)分隔符。
/ Y: o, R9 V; l7 i! L
7 m3 `1 N7 c: k I* p5 p1 S4 Q5 F5 B#uname -s 表示:显示系统名,标志缺省为开,即uname命令不带任何参数的输出和加参数-s输出相同,即Linux: @ L% ^' x+ C0 ^$ Y' m
#tr是一个shell命令,这里 tr '[:upper:]' '[:lower:]'的意思是把管道中的Linux 中的大写字母L 转换成小写字母l5 v& R- E. q' e4 w0 c( e1 `' }* p
#下面这句话的意思是定义变量HOSTOS,HOSTOS里面存放的是主机安装的,并且当前正在运行的操作系统。
5 i- ?+ ~! z0 R: q; A$ aHOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
) W3 u! }* o' h- U sed -e 's/\(cygwin\).*/cygwin/') # 这一句的意思是检测出主机安装的,并且当前正在运行的操作系统名,并把这个系统名中的大写字母转换为小写字母,
* U, ~& q6 `) a0 `5 R7 c # 然后在通过sed流编辑器匹配所有的,这个系统名中出现的"\(cygwin\).*"模式,然后再用"cygwin"模式替换。
/ {) t5 Y7 }( H; n! _ N9 n9 a#export HOSTARCH HOSTOS 的意思是输出两个makefile变量HOSTARCH HOSTOS
export HOSTARCH HOSTOS
# Deal with colliding definitions from tcsh etc. 用来处理来自tcsh的互相冲突的定义等等
0 v; Y4 o. ~1 g1 B! Q r# 一般来说,shell可以分成两类。第一类是由 Bourne shell 衍生出来的包括 1 ~+ [& h: Z7 z9 |, ]7 V! j
# sh,ksh,bash,与zsh。第二类是由 C shell 衍生出来的,包括 csh 与
) t/ \. X7 @( @( g; P# tcsh。除此之外还有一个 rc,有人认为该自成一类,有人认为该归类在Bourne shell。
VENDOR= #开发商
#########################################################################' a; o& Y5 U. w4 \- l% D% m7 g
#2 n/ z; K" r& D' c% Y
# U-boot build supports producing a object files to the separate external0 q% w( X: A1 k5 V M! s
# U-boot 的编译过程可以支持向一个自己定义的路径生成最终的目标文件6 I) o2 a# S4 J* m2 t$ _& L
# directory. Two use cases are supported:! _; W u" j+ W& H2 h+ G! i
# 这里提供了两种用法:# Z" X- Y) K4 E, k+ W- Q+ [
#
' R" Y) }3 c: |6 N' N) q# 1) Add O= to the make command line #第一种用法:通过在终端执行命令make O=/dir(即你指定的生成的目标文件的存放目录)
+ u. z3 a, q+ t# w( T# 'make O=/tmp/build all', O; F# V" _: p; }/ _
#
6 m3 v" |6 g6 k- e9 T# 2) Set environement variable BUILD_DIR to point to the desired location #第二种用法:通过设置环境变量来指定目标文件存放目录,如下所示:
4 m) L1 G/ p4 W5 F* o# 'export BUILD_DIR=/tmp/build'
( R( P0 T; {( Y# 'make'
! {! z3 E( r5 Q G## D. u+ P( Y; K" }
# The second approach can also be used with a MAKEALL script #第二种方法也可以写成一个MAKEALL脚本,然后执行MAKEALL,如下所示:
- P8 L" }- |; K. I& L5 O# 'export BUILD_DIR=/tmp/build'' \$ y+ T) \3 F0 o% ]+ h
# './MAKEALL'
" q2 D5 g- p' y& k* ]#
9 D$ f( I3 C( m8 C1 U8 m. G8 X# Command line 'O=' setting overrides BUILD_DIR environent variable. #命令行'O='设置会覆盖环境变量BUILD_DIR的设置
1 E7 H B# w) i0 _#8 z1 v+ f) w- f( R; c/ y+ x
# When none of the above methods is used, the local build is performed and #如果都不采用上面两种方法,那么目标文件最终要存放到源码顶层目录,也就是U-BOOT顶层目录
. d; H6 [' m9 t4 x# the object files are placed in the source directory.
! J+ Y8 ^/ B% { x) O#
#理解了上面一段英文,这里就不难理解了
9 P. l& c. d; i' M3 m+ [- f#方法1
ifdef O #如果变量'O' 已经被定义过# @! T: e9 Q4 I
ifeq ("$(origin O)", "command line") #如果变量'O' 在命令行中定义过# R( X+ h" x: w
BUILD_DIR := $(O) #就把变量'O' 的值(目标文件存放目录)赋给BUILD_DIR+ O# a$ g) ~' ~5 b# T: W9 A0 q) y
endif
( O. h: }7 Q. p7 Kendif
" ]! t- A( R. m2 k5 C#方法23 {7 R9 w6 B& [; w
ifneq ($(BUILD_DIR),) #如果变量BUILD_DIR不为空,即环境变量BUILD_DIR 被定义过8 B' D m) [3 K# q
saved-output := $(BUILD_DIR) #那么把它的值赋给saved-output
# Attempt to create a output directory. #生成一个输出路径,即目标文件存放目录BUILD_DIR
" r4 z, x. d+ X* d; T$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})
#shell [ -d ${BUILD_DIR} ] 是什么意思?是不是生成一个目录的意思?
# Verify if it was successful. 测试目录是否创建成功
) r5 m& A) R. ~7 \7 j* MBUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd) #这又是什么意思,说明对shell还不够理解!' [# C6 n4 h& f
$(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))
$ w6 I" d, a6 e- Q S& l#这里用了一个if函数,意思是如果如果$(BUILD_DIR) 非空,则什么都不执行(返回空),否则执行error函数,输出错误信息) H. m L7 N: e
endif
# ifneq ($(BUILD_DIR),) #意思是:如果没有定义目标文件存放目录
/ E6 @9 {' G: x2 X0 `#Makefile中定义了源码以及生成目标文件存放的目录,目标文件存放目录BUILD_DIR可以通过make O=dir指定。如果没有指定,则设定为源码顶层目录。
0 ~. k0 ]. A6 ?' f; O#一般编译的时候不指定输出目录,则BUILD_DIR为空。其他目录变量如下:
+ @5 x( Z3 l3 z# B7 y. MOBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR)) #如果$(BUILD_DIR)不为空,则返回$(BUILD_DIR),并赋给OBJTREE,即自己定制的目标存放目录8 H) F0 Y% V1 F0 G+ @! Z. j* T
SRCTREE := $(CURDIR) #把当前源码所在目录 $(CURDIR) 赋给SRCTREE
/ _& n9 J+ t: A$ R/ qTOPDIR := $(SRCTREE) #把当前源码所在目录 $(CURDIR) 赋给SRCTREE
9 X/ U9 C T u$ ZLNDIR := $(OBJTREE) #存放生成的目录文件5 F5 k& V' C5 }. K
export TOPDIR SRCTREE OBJTREE
7 w% F* Z7 n7 ~* D1 Q1 l3 {
w$ A4 p. t5 n9 N+ P' f3 VMKCONFIG := $(SRCTREE)/mkconfig #MKCONFIG指向源码所在目录(U-BOOT顶层目录)下的mkconfig配置文件8 G: u3 d" r4 Y
export MKCONFIG
#在编译UBOOT之前,我们先要执行:% L! O! O9 |, v( }! J; h J
#make smdk2410_config9 g& o6 `/ Z- h0 S5 u
#从本Makefile的下文可以判断出smdk2410_config为Makefile的一个目标。" w4 r: M4 {, e1 P" ]
#smdk2410_config: unconfig的意思是为smdk2410开发板建立一个编译项。1 H F7 M, ~! c
#显然,执行#make smdk2410_config时,先执行unconfig目标(不指定输出目标时,obj,src变量均为空),unconfig下面的命令主要任务是清理上' f6 t2 c; J9 _2 `+ U6 ?5 M) `
#一次执行make *_config时生成的头文件和makefile的包含文件。 Z2 _& u% b0 m' G& ^! Y& l
#主要是include/config.h和include/config.mk
. @+ O/ H! S3 }5 s#然后执行命令: @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0 O e" I" Z9 Y/ D& o; `
#arm 表示CPU的构架是基于ARM体系的
. k5 F+ ` ~- o# [& N#arm920t 表示CPU的类型是arm920t
: J% j/ a- E# H3 ?6 z1 L#smdk2410 表示开发板的型号
1 Y: x ]3 K( m* C2 x- n, D#NULL 表示开发商或者经销商的名称,这里为空
% {0 H$ Z' y( R+ u8 V/ G z#s3c24x0 表示基于S3C2410的片上系统
3 M1 f; Z/ [( {) A6 g#MKCONFIG指向UBOOT顶层目录下的mkconfig脚本配置文件,后面五个字符串是传入的参数(好像$(@:_config=)也是一个参数)。
6 w0 f$ K& n x#下面来分析一下mkconfig这个脚本配置文件,点击链接:
0 A; q0 i& _- X8 Whttp://zqwt.012.blog.163.com/blog/static/120446842010325102158182/
5 f7 R3 F. g! G' q
ifneq ($(OBJTREE),$(SRCTREE)) #当目标存放目录不是U-BOOT顶层目录(源码目录)时
4 d; E: J$ X; e0 mREMOTE_BUILD := 1 #定义变量REMOTE_BUILD := 1 这个变量算是一个flag吧3 L1 F% I3 a& y3 i- s
export REMOTE_BUILD
% A* P1 n9 f7 ^4 B, Nendif
# $(obj) and (src) are defined in config.mk(顶层目录下) but here in main Makefile& a B" p. E( B3 i4 u
# we also need them before config.mk is included which is the case for( \( b3 v; y! S1 |( r) ?
# some targets like unconfig, clean, clobber, distclean, etc.
+ c4 L' m) g. M7 B9 E7 B. h! G% R# $(obj) and $(src)都被定义在顶层目录下的config.mk脚本配置文件里面,! i$ r S$ n8 @' V
# 但是在这个主Makefile里面,我们同样需要他们,; Z; A* z% T* `4 M+ K5 I! U
# 因为在主Makefile文件包含config.mk之前,$(obj) and $(src)偶尔地会成为这些目标的case:
& Q: @6 i7 v- Z M# unconfig, clean, clobber, distclean, etc
& R1 z* Q1 _/ i! P
ifneq ($(OBJTREE),$(SRCTREE)) #当目标存放目录不是U-BOOT顶层目录(源码目录)时& V( I+ ^: b6 V4 T, {9 \
obj := $(OBJTREE)/ #定义变量obj,让其等于目标存放目录3 l; Y/ p% Q6 D* ?/ v+ \- e
src := $(SRCTREE)/ #定义变量src,让其等于uboot顶层目录- E# P* F2 v# x p4 F
else+ b; G! h: K2 E$ o/ |4 K; L O/ a7 Z
obj := #否则,这两个变量都定义为空/ ?/ J4 R- y, s; W. o
src :=
$ a. X. v1 ]. w- I6 W0 u9 fendif
_: V. \4 v$ K6 G7 }( l" e: bexport obj src
/ f. {5 m$ k( ^$ R0 R
' K) {2 [7 Y7 O/ K
5 S R3 d( }/ f& w#########################################################################
! G' l8 s- E9 e }* B" K
; R8 v& c t+ Q
- h2 b& s6 ?3 v' G8 D7 p" p. p" x, }3 n& U& _
ifeq ($(OBJTREE)/include/config.mk,$(wildcard $(OBJTREE)/include/config.mk)) #这句话让人费解!
" j# ~, H+ D5 v& O: `# 通配符在规则中可以自动扩展,但设置在变量中或在函数的参数中通配符一般不能正常扩展。
+ B1 E: |8 ^& I1 s# 如果您需要在这些场合扩展通配符,您应该使用函数wildcard,格式如下:; g- X' c, O+ U0 A& K2 L/ B& e
# $(wildcard pattern...)
* f! `- z! S! Z, T5 e
# load ARCH, BOARD, and CPU configuration7 k0 ?* R, ~" s! s K* _
# 加载ARCH, BOARD, and CPU 配置' W" O) ~! h6 G- k: m5 H, |
include $(OBJTREE)/include/config.mk # 这时候,开始包含/include/config.mk的 * o8 r X7 M; s* l: |# e0 ^
export ARCH CPU BOARD VENDOR SOC
5 A7 E6 b) N. s8 X8 `
#指定交叉编译器前缀
1 c; P! p& U0 P Yifeq ($(ARCH),arm) " x; _$ ?% L8 ?4 `' y
CROSS_COMPILE = arm-linux-
3 }' E+ ~7 t, b9 U#这里你可以把交叉编译器的安装路径加到arm-linux-之前,比如你的交叉编译器安装路径是/root/u-boot/usr/local/arm/3.3.2/bin/1 r( h. q* A3 n; P x' X0 V
#你可以这样定义CROSS_COMPILE = /root/u-boot/usr/local/arm/3.3.2/bin/arm-linux-
5 Z( X0 Y: }- [5 w, h8 |#这样一来,你在终端进行编译的时候就不用指定CROSS_COMPILE=arm-linux-了
( z& y; L, F& [# d#但请注意:在编译内核的时候,交叉编译器必须安装在/usr/local/arm下,否则会发生错误!!!!!' M7 b/ d- J& Z, D' E
endif
export CROSS_COMPILE
2 A: U. w6 _$ I) J" `
1 u9 o7 u2 p2 f( Z# I
# load other configuration 加载其他设置,这里是包含顶层目录下的config.mk配置文件,这个文件主要做了三个工作:
1 D) b# S+ O ~1 `. |# 1、定义了交叉编译器 2、定义了编译选项 3、定义了编译规则
7 ~# X" F5 a8 |- _1 l* o# 对本文件具体的分析,请查看链接:& B+ D& R' j* B, b5 r
http://zqwt.012.blog.163.com/blog/static/12044684201032541139914/
include $(TOPDIR)/config.mk
#########################################################################
) O4 v; ?2 x6 F5 b# U-Boot objects....order is important (i.e. start must be first)
" M! l1 q& ], u2 h# uboot目标...书写顺序很重要,比如start.o必须排在第一位& M5 ?. X1 o r- C5 M
#########################################################################4 k, L" r4 F" y
OBJS = cpu/$(CPU)/start.o ; \! f6 k! g1 f
#start.o必须放在目标文件的第一位,因为uboot执行的第一段代码就是start.S
) C: U* l$ F t- E8 e% V( ]# j#具体原因可以查看链接脚本/u-boot-1.1.6/board/smdk2410/u-boot.lds,点击连接:" @! s# H6 |* S# h8 U
http://zqwt.012.blog.163.com/blog/static/120446842010320101137932/
- X( I3 E7 t+ A+ M+ K4 V5 T.....................................
4 c, p4 |# f9 kOBJS := $(addprefix $(obj),$(OBJS)) #这句的意思是把目标文件存放路径以前缀的形式加到start.O之前,然后再赋给OBJS
* \; s( N) M' E% }. W/ Z4 ]#以下是编译UBOOT需要的库文件
8 e: S9 r) c3 X7 j9 kLIBS = lib_generic/libgeneric.a
LIBS += board/$(BOARDDIR)/lib$(BOARD).a # 严重平台依赖的
LIBS += cpu/$(CPU)/lib$(CPU).a # 严重平台依赖的
ifdef SOC' Q S* t" \3 y
LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a # 严重平台依赖的* y0 i9 S4 v4 _% G8 f2 Z
endif
LIBS += lib_$(ARCH)/lib$(ARCH).a # 严重平台依赖的
0 {3 N) q# b" [' m" b5 ~LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
4 e& N* N( ?7 v( Z# H0 } x F fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
% U8 A- l+ ]# PLIBS += net/libnet.a
3 K @7 O0 L0 z/ N RLIBS += disk/libdisk.a
3 N; e! n- G* O3 U( q# [$ A2 yLIBS += rtc/librtc.a
' l4 ?( c! C; v! dLIBS += dtt/libdtt.a# Y5 i# c/ Y! q: g" F# \3 |
LIBS += drivers/libdrivers.a! r c/ @% `6 y; ~" j! q
LIBS += drivers/nand/libnand.a
, M8 _) `' u) O& C& [$ m( aLIBS += drivers/nand_legacy/libnand_legacy.a# w3 Z2 T ?7 ]
LIBS += drivers/sk98lin/libsk98lin.a; N* H, F1 p3 D* t- v. u& b
LIBS += post/libpost.a post/cpu/libcpu.a
* K g* h0 d2 u6 r. x* R" n/ i5 mLIBS += common/libcommon.a# ?* O+ x- T8 n7 K
LIBS += $(BOARDLIBS)
LIBS := $(addprefix $(obj),$(LIBS))
.PHONY : $(LIBS) # 这是一个伪目标
#根据所生成的include/config.mk文件定义的几个变量ARCH, CPU, BOARD, SOC,我们可以
5 ~5 S8 c& ~7 Y3 N#确定硬件平台依赖的目录文件。smdk2410平台相关(依赖)目录以及对应生成的库文件如下:# E6 \# n: u" B( }, d9 f. t: T5 a' E
#board/smdk2410/: 库文件board/smdk2410/libsmdk2410.a
/ ?% Y" i7 N5 G#cpu/arm920t/: 库文件cpu/arm920t/libarm920t.a
# |; u& G3 F' M2 H$ D ]#cpu/arm920t/s3c24x0: 库文件cpu/arm920t/s3c24x0/libs3c24x0.a6 j1 w2 R( s, S4 i0 G" [
#lib_arm: 库文件lib_arm/libarm.a7 [: u8 Y+ W4 i3 _0 P, ^. T
#include/asm-arm: 头文件2 |7 e8 U; W( s+ x- `
#include/cnofigs/smdk2410.h:头文件
# Add GCC lib: J) h4 w7 N- j: }, n" h5 q
PLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc
# The "tools" are needed early, so put this first
; d, B* }. B |2 ^! V9 A# Don't include stuff already done in $(LIBS) 不要包含已经在 $(LIBS) 中的任何东西
3 @: K. X- d+ D. b* Q" s#
( @* W) {( T' |! f/ c# 伪目标SUBDIRS:用于执行tools、examples、post、post\cpu子目录下的make文件
( ]7 {9 u1 K9 rSUBDIRS = tools \/ B7 k4 e2 Y( Q& _6 a
examples \
. Q1 y" x. g% L# t+ V( a. r. O post \
; f. Y% Y* y/ H post/cpu
; h3 j0 b* r9 G/ u$ L.PHONY : $(SUBDIRS)
ifeq ($(CONFIG_NAND_U_BOOT),y)
; l; c2 i+ r# u% S4 F S8 Y+ CNAND_SPL = nand_spl
. [/ f( @' m# cU_BOOT_NAND = $(obj)u-boot-nand.bin
1 m, g9 x* x7 L8 tendif
__OBJS := $(subst $(obj),,$(OBJS))
* h( y3 ^% i- G7 R__LIBS := $(subst $(obj),,$(LIBS))
#########################################################################
9 I* A% `% j$ F& q; ~#########################################################################
" x; d/ \/ _6 o- \5 \' z: p) U/ j
6 ?& ]; Y: I& N- F#这里是最终要生成的各种镜像文件u-boot.hex、u-boot.srec、u-boot.bin、System.map、u-boot.img( x& Z2 h3 _9 W$ z
ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)% \; o8 b' o# |$ Q) N5 U
all: $(ALL)
0 B/ m5 w) o" ?$(obj)u-boot.hex: $(obj)u-boot
7 q1 |2 O+ u* j+ d) G $(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@ # $<,$@ 分别表示使用该规则的源文件和目标文件
$(obj)u-boot.srec: $(obj)u-boot
& _0 W7 B/ Q* {( B: g# t1 }4 b $(OBJCOPY) ${OBJCFLAGS} -O srec $< $@
$(obj)u-boot.bin: $(obj)u-boot
. H$ w$ U( w% P- b" j $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
$(obj)u-boot.img: $(obj)u-boot.bin
: d* m) g# l* x ./tools/mkimage -A $(ARCH) -T firmware -C none \" o0 d+ B4 c* \+ y; t/ D- p
-a $(TEXT_BASE) -e 0 \! q7 O6 Q) F& J; I. o
-n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \- M$ Z8 K+ Q; N+ Z4 [
sed -e 's/"[ ]*$$/ for $(BOARD) board"/') \
+ j- i- @% [% c) Q% z -d $< $@
$(obj)u-boot.dis: $(obj)u-boot
* F0 `' K. d3 R+ R: p $(OBJDUMP) -d $< > $@
& O" t# P! @ Y6 M- ^7 b7 O
#此处生成的是uboot的ELF文件镜像
! U) @3 M) d O. Q: g9 y$(obj)u-boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
8 L( X N) |* @0 v UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\8 m$ ?5 H- V: @( l
cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
# M5 B; p) }+ v( Z) w --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
! Q9 H! m. e5 j; D$ k -Map u-boot.map -o u-boot
5 W# `$ p- s: o+ p$ _( j
#依赖目标$(OBJS),也就是cpu/start.o
' u" D& b! P1 S3 S$(OBJS):1 w% q) c" t* I+ b
$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))
' T) t; n7 s5 \1 W% q 6 b2 M; Y0 o! x L3 u
#依赖目标$(LIBS),目标很多,都是每个子目录的库文件*.a,通过执行相应子目录下的make来完成
( S% O" {* t. k. R* e7 e; `$(LIBS):. l& ?; D6 ^: j3 |/ g
$(MAKE) -C $(dir $(subst $(obj),,$@))1 x& Y8 c) C G( g0 j, C
% f" a9 _7 r4 A0 N2 N
#这里解释一下这个makefile函数 $(dir names...) 7 U( y' `5 K, Q& |0 ~1 X8 n3 X
#抽取‘names’中每一个文件名的路径部分,文件名的路径部分包括从文件名的开始到最后一个斜杠(含斜杠)
/ N6 k @1 g; Z* g* M#之前的一切字符。如果文件名中没有斜杠,路径部分是‘./’。如:" ?& W& f }5 J
#$(dir src/foo.c hacks)! P1 e5 {0 J' D
#产生的结果为 ‘src/ ./’。
4 H* N1 g: C t I( s0 a7 M$(SUBDIRS):
8 H1 N$ h% |$ X& z" X* R $(MAKE) -C $@ all
$(NAND_SPL): version
, Y: ~: ?. M, A X8 u% L $(MAKE) -C nand_spl/board/$(BOARDDIR) all
$(U_BOOT_NAND): $(NAND_SPL) $(obj)u-boot.bin
/ A! Y6 X8 ?0 V$ z0 O cat $(obj)nand_spl/u-boot-spl-16k.bin $(obj)u-boot.bin > $(obj)u-boot-nand.bin
, [. u' e8 w. m: h; q6 H- b% g#依赖目标version:生成版本信息到版本文件VERSION_FILE中
2 l! P& |1 T4 z) ] M3 Sversion:' l) M3 ~( h4 X T
@echo -n "#define U_BOOT_VERSION \"U-Boot " > $(VERSION_FILE); \
, H- V) |# L- K# u8 H echo -n "$(U_BOOT_VERSION)" >> $(VERSION_FILE); \
+ [" p. _ |3 W1 K echo -n $(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion \7 u0 t, Y( `- L
$(TOPDIR)) >> $(VERSION_FILE); \
% b. y2 k0 V& J* f' d( C echo "\"" >> $(VERSION_FILE)
gdbtools:; X1 p# m! f; x u6 m1 k2 g. F
$(MAKE) -C tools/gdb all || exit 1
updater:
$ a. z4 g! c: v $(MAKE) -C tools/updater all || exit 1
env:
7 i6 \2 `: N7 _% v2 o $(MAKE) -C tools/env all || exit 1
" S# J2 @, b6 V% T H
0 y) n# p+ ?! f9 l% a8 u5 [7 S
#依赖目标depend:生成各个子目录.depend文件,.depend列出每个目标文件的依赖文件。( T6 w9 R& Y9 ~3 F! b
#生成方法:调用每个子目录的make_depend
4 S. x z }# j6 i9 B- L1 O6 _3 Jdepend dep:/ a9 n$ c( }( p9 w2 u& V
for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done
# ~: v) @' f! V/ j& f9 h- Ltags ctags: F2 \" Y6 y- g, f% [$ N! t/ _' y
ctags -w -o $(OBJTREE)/ctags `find $(SUBDIRS) include \; P0 @% j6 M3 z
lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \
+ E- |0 W4 o: Q fs/cramfs fs/fat fs/fdos fs/jffs2 \
H- r% l" x/ G. s# u net disk rtc dtt drivers drivers/sk98lin common \
9 h6 L K3 D" ^8 }9 I \( -name CVS -prune \) -o \( -name '*.[ch]' -print \)`
etags:
% N8 k# \- }. c3 \+ I etags -a -o $(OBJTREE)/etags `find $(SUBDIRS) include \8 X! P4 @* M' e
lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \9 S, A, C3 A$ g0 @6 y$ F- }
fs/cramfs fs/fat fs/fdos fs/jffs2 \
6 F* q+ H7 q( j# ?8 g net disk rtc dtt drivers drivers/sk98lin common \* |0 G: o0 ?! h1 G G) P" Y
\( -name CVS -prune \) -o \( -name '*.[ch]' -print \)`
$(obj)System.map: $(obj)u-boot e4 q1 Z* h- c
@$(NM) $< | \2 m X W: L! ]; V
grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \( [* s l' c$ m( E% F; V7 ]# W
sort > $(obj)System.map
2 H& x/ l7 D" u' e- s
; V, R6 d5 t8 ~& ^# k# p5 a' V" o9 a6 X#########################################################################
+ U3 V Q7 e c1 I# z$ G/ _else
; Z" ]7 Y; B! N9 {2 rall $(obj)u-boot.hex $(obj)u-boot.srec $(obj)u-boot.bin \
8 ~0 M" n( l+ q" z' B$(obj)u-boot.img $(obj)u-boot.dis $(obj)u-boot \
: C# K8 w ]# A$(SUBDIRS) version gdbtools updater env depend \
! o0 K9 U. R# e* O" Y( odep tags ctags etags $(obj)System.map:
8 _+ S) ` I, ?: b" f3 c. ?( x @echo "System not configured - see README" >&2# V" y0 x8 s6 }" ?/ l
@ exit 1% \( }* v3 C# _. c8 Y% g5 f
endif
.PHONY : CHANGELOG9 j( W# ]7 o" Z$ g2 M( `
CHANGELOG:
L# E9 _4 w; H git log --no-merges U-Boot-1_1_5.. | \
# L% ^5 a( S( A4 _5 B" G J unexpand -a | sed -e 's/\s\s*$$//' > $@
$ y2 S3 ?, C8 P$ ]9 I8 Q#########################################################################
8 q) @& {0 s3 o s; [# 这里就是我们所谓的unconfig,应该比较熟悉了!4 s( P8 |) |# ]. ?& i+ |
# 很明显,它所做的工作就是清除上次生成的include/config.h和include/config.mk; m, c c- e, n
# 以及开发板目录下的一些临时配置文件
* s1 t2 x$ j" O- }% R# unconfig:3 H: ]! N* L. s7 A
# @rm -f $(obj)include/config.h $(obj)include/config.mk \' i+ W/ F3 M4 J" {/ F# `9 P' P
# $(obj)board/*/config.tmp $(obj)board/*/*/config.tmp0 u0 |: y4 @/ V! o# _3 _) \7 a% y5 N
#########################################################################
( J4 ~4 y, p" o0 t................................
. P X2 I ?3 _9 A# ?7 Xsmdk2400_config : unconfig m; _% B5 n4 [, [) l1 [" x$ S4 ~
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2400 NULL s3c24x0
. z, k" e7 p4 I3 k * ?% ?. a1 X4 E5 k% d) `, @
................................
smdk2410_config : unconfig9 d; o2 }& p! @: x6 S N) S1 h
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
1 z9 u# }* O1 A( g( L#我删了一部分内容,那些几乎都是各种不同板子的*_config,也就是目标的定义。
最后总结一下,当然这里也参考了前辈们的许多宝贵经验,顶层Makefile的主要任务就是组织整个u-boot工程的编译,概括可以分为一下几个步骤:
1、首先通过执行make *_config传入$(@:_config=), ARCH, CPU, BOARD, VENDOR, SOC参数(一共六个参数但不
( [/ I9 M1 M3 ~, d/ h: b( | ' T" u6 @8 q: x( j
一定同时存在),给mkconfig。
2、mkconfig接收到传递过来的参数后,将include头文件夹相应的头文件夹链接好,生成config.h
3、然后执行make分别调用各个子目录的makefile文件,以生成所有的obj文件(包括start.o)和obj库文件*.a。
4、最后,通过链接器把所有目标文件链接起来,生成uboot镜像。不同格式的镜像都是调用相应工具,经
由elf镜像间接或者直接的生成的。