VERSION = 10 @4 [( j n4 ]4 _& W
PATCHLEVEL = 1
3 ?0 J4 _2 X/ s- n- Z# _/ S1 vSUBLEVEL = 6+ `) k! `0 g! [9 l
EXTRAVERSION =1 ]. k$ ]6 B5 S
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
' }2 k3 X3 ]+ ?/ p1 `VERSION_FILE = $(obj)include/version_autogenerated.h #版本文件
. d1 c$ s Z, c% G$ J
# uname 命令将正在使用的操作系统名写到标准输出中
/ j* H- s3 P4 d# h2 r1 {/ q# -m 显示硬件运行系统的机器 ID 号
# 定义变量,HOSTARCH里面存储的是机器ID号,即主机架构类型6 h: z/ b9 h# U2 m
HOSTARCH := $(shell uname -m | \ #可以把shell脚本写到makefile里面,这是make中的shell function, 相当于shell中的命令替换。
5 F! P4 }* T5 D' h1 q4 j0 [ sed -e s/i.86/i386/ \ #shell uname -m 获得机器硬件名称
+ ~$ @# Z1 n% n" V) Q$ \3 [ -e s/sun4u/sparc64/ \ #sed -e s/arm.*/arm/ 的意思是把前缀为arm的所有模式替换为arm。结合前面的uname命令来理解就是:
C4 K0 I% t1 I' ^# Z -e s/arm.*/arm/ \ #把uname -m的结果(主机架构类型或者称为机器ID号)通过管道传递给sed命令,然后把前缀为arm的所有模式替换为arm。
x f) a# G+ } -e s/sa110/arm/ \ #sed的语法:sed [ -n ] Script [ File ... ]0 E P, [ C( i o4 M! z+ h3 x
-e s/powerpc/ppc/ \ # sed [ -n ] [ -e Script ] ... [ -f ScriptFile ] ... [ File ... ]
7 A( H' Z* x5 ]9 x8 D( N -e s/macppc/ppc/) #sed 命令根据编辑脚本,去修改指定的 File 文件(这里file是作为一个输入参数的)的行,并将其写到标准输出。2 k' G) ~% g, [6 g" V" z5 B' f: r
#sed 命令包含很多功能,用于选择要修改的行(请注意,sed是针对行进行操作的),并只对选择的行作更改。 k: _. N$ h3 u
#sed 命令使用两个工作空间来保留修改的行:保留选定行的 "模式空间" 和暂时存储行的 "保留空间"。
3 x, y7 {9 s, k; i' V6 ~ #这里所谓的编辑脚本是由单独的子命令构成的,每个单独的行上对应着一个子命令。sed 子命令的一般格式如下:[address-range] function[modifiers],即:[地址范围] 函数[修改符]
: z+ N" v! o# ~) c+ ?2 y, Z #sed 命令通过将一个输入行读入模式空间,依次应用所有的 sed 子命令(这些子命令的地址选择了该行),% i0 J4 m: r N) O: h& Y
#并将模式空间写到标准输出来处理每个输入的 File 输入文件(file就是输入参数)。然后清除模式空间,并对输入的 File 中指定的每行重复该过程。
- y8 U, i% l3 R; d #一些 sed 子命令使用保留空间来保存后继检索的所有的,或部分的模式空间。1 ]- S1 A9 L( M5 ^& e
#当命令包含地址(行号或搜索模式)时,该命令只会对被寻址的行起作用。否则,该命令适用于所有的行。4 k% T- S( f* Y. m
#注意,这里的参数"-e"的意思是:使用 Script 变量作为编辑脚本。如果你只使用一个 -e 标志并且不使用-f 标志,则可以省略 -e 标志。
$ b/ ~) b3 D9 m9 O9 e0 w1 E T #注意,这里的 "sed -e s/arm.*/arm/"表示把前缀为arm的所有模式替换为arm,也就是sed s/pattern/replacement/flags
( N1 s3 y. X* r4 m! @3 S4 t' z #用 replacement 字符串代替在模式空间中首次出现的 pattern 参数。除了空格或换行符,在 s 子命令之后显示的任何字符都能代替 /(斜杠)分隔符。
% t) K; D2 A; d+ D! v: ?6 @
& r/ U- R; w- F1 G& L4 ^#uname -s 表示:显示系统名,标志缺省为开,即uname命令不带任何参数的输出和加参数-s输出相同,即Linux
& p W) Y3 A1 M) r% S% S% W#tr是一个shell命令,这里 tr '[:upper:]' '[:lower:]'的意思是把管道中的Linux 中的大写字母L 转换成小写字母l
5 G: F) g" ~' k4 N#下面这句话的意思是定义变量HOSTOS,HOSTOS里面存放的是主机安装的,并且当前正在运行的操作系统。
0 |$ i9 G ^8 [7 J9 P3 F+ sHOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \5 M* A' c! [& |9 q3 f2 b
sed -e 's/\(cygwin\).*/cygwin/') # 这一句的意思是检测出主机安装的,并且当前正在运行的操作系统名,并把这个系统名中的大写字母转换为小写字母,
' h/ A' z4 R$ D # 然后在通过sed流编辑器匹配所有的,这个系统名中出现的"\(cygwin\).*"模式,然后再用"cygwin"模式替换。
2 @- ~2 b- Y; r! k5 e#export HOSTARCH HOSTOS 的意思是输出两个makefile变量HOSTARCH HOSTOS
export HOSTARCH HOSTOS
# Deal with colliding definitions from tcsh etc. 用来处理来自tcsh的互相冲突的定义等等! P N! Y4 ^0 y* d& m& H7 \# L
# 一般来说,shell可以分成两类。第一类是由 Bourne shell 衍生出来的包括
; s6 V1 j9 j Y. c% A0 f# sh,ksh,bash,与zsh。第二类是由 C shell 衍生出来的,包括 csh 与 7 r+ h ?2 j( Q7 J _/ ]
# tcsh。除此之外还有一个 rc,有人认为该自成一类,有人认为该归类在Bourne shell。
VENDOR= #开发商
#########################################################################1 V+ z7 E3 Y9 L: O
#
! @' x8 @) U7 g1 @ q9 Q# U-boot build supports producing a object files to the separate external0 u. k. j% Z3 }' u p! Y
# U-boot 的编译过程可以支持向一个自己定义的路径生成最终的目标文件7 L0 O$ j9 c* o2 Z0 o0 r9 |4 D
# directory. Two use cases are supported:
( |) n( @8 h" ]5 Z# 这里提供了两种用法:
5 D8 i" I' N8 d# {( n#4 K% `* v1 o h! \; b) c( _ i. l8 Y
# 1) Add O= to the make command line #第一种用法:通过在终端执行命令make O=/dir(即你指定的生成的目标文件的存放目录)
/ S$ r. {# E. {/ B% A, o# 'make O=/tmp/build all'" A8 K+ L0 q8 L- ?
#& N+ l6 q. J- a' C. `6 C1 `
# 2) Set environement variable BUILD_DIR to point to the desired location #第二种用法:通过设置环境变量来指定目标文件存放目录,如下所示:( I9 U* W: G+ O6 ]1 o
# 'export BUILD_DIR=/tmp/build'! l2 n3 Z# P" s# v9 L b5 i1 q
# 'make'
4 l0 y! L/ V4 `/ C4 a5 p#
. E+ b) B& E, v0 W0 G6 }; D# The second approach can also be used with a MAKEALL script #第二种方法也可以写成一个MAKEALL脚本,然后执行MAKEALL,如下所示:, h4 e, r u9 C/ T' A/ D4 Z
# 'export BUILD_DIR=/tmp/build'
' v8 j& _) M; o' _& [: ~! U8 g# './MAKEALL'1 N5 K, [# @' u/ k
#
/ [* E( Y2 g( `6 n# Command line 'O=' setting overrides BUILD_DIR environent variable. #命令行'O='设置会覆盖环境变量BUILD_DIR的设置
' G: y+ H4 f' j( \, _1 r6 j' Y& N#
1 R5 u3 v1 H5 f Y S: X# When none of the above methods is used, the local build is performed and #如果都不采用上面两种方法,那么目标文件最终要存放到源码顶层目录,也就是U-BOOT顶层目录# P0 Z( x, M9 k" R3 ^% Y# J9 B
# the object files are placed in the source directory.- `5 O9 q0 [) X. Z2 E
#
#理解了上面一段英文,这里就不难理解了
$ J& I2 } m0 b1 y" l# E#方法1
ifdef O #如果变量'O' 已经被定义过
+ b9 Y8 z0 {( L# Difeq ("$(origin O)", "command line") #如果变量'O' 在命令行中定义过
* W8 B j! U1 R- p. ], ~BUILD_DIR := $(O) #就把变量'O' 的值(目标文件存放目录)赋给BUILD_DIR
; Q0 l; {1 W3 V! v; ^endif8 v+ n( y' O t; Y
endif
+ ?0 f9 V- ~1 ?! Q# {+ z+ Z# e
#方法25 r1 d/ k j$ e" w% V' q- e8 B
ifneq ($(BUILD_DIR),) #如果变量BUILD_DIR不为空,即环境变量BUILD_DIR 被定义过
9 c4 k7 Y1 L) q3 b! x: i) i- h3 jsaved-output := $(BUILD_DIR) #那么把它的值赋给saved-output
# Attempt to create a output directory. #生成一个输出路径,即目标文件存放目录BUILD_DIR/ Z/ d2 _$ f3 C
$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})
#shell [ -d ${BUILD_DIR} ] 是什么意思?是不是生成一个目录的意思?
# Verify if it was successful. 测试目录是否创建成功
' ?+ a0 ~6 X7 y% VBUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd) #这又是什么意思,说明对shell还不够理解!
7 q6 b. A1 N$ z! H0 y" S' u$(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist)) , D; l; i a% E$ x
#这里用了一个if函数,意思是如果如果$(BUILD_DIR) 非空,则什么都不执行(返回空),否则执行error函数,输出错误信息& A0 F, B/ T9 `: `6 w, B
endif
# ifneq ($(BUILD_DIR),) #意思是:如果没有定义目标文件存放目录
& C. [7 i" s# y5 Z6 E' g6 V- w#Makefile中定义了源码以及生成目标文件存放的目录,目标文件存放目录BUILD_DIR可以通过make O=dir指定。如果没有指定,则设定为源码顶层目录。0 s4 o; U/ Y8 I% q4 x
#一般编译的时候不指定输出目录,则BUILD_DIR为空。其他目录变量如下:
) L, ~" Y) O% v; N2 g" @OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR)) #如果$(BUILD_DIR)不为空,则返回$(BUILD_DIR),并赋给OBJTREE,即自己定制的目标存放目录- t! s4 F* b6 U6 q$ I
SRCTREE := $(CURDIR) #把当前源码所在目录 $(CURDIR) 赋给SRCTREE 0 ]! t1 t; _$ J$ U' r6 |+ p! E
TOPDIR := $(SRCTREE) #把当前源码所在目录 $(CURDIR) 赋给SRCTREE
n. U& a$ f; oLNDIR := $(OBJTREE) #存放生成的目录文件0 i& ^$ f1 w5 f$ y) J& f
export TOPDIR SRCTREE OBJTREE
5 @/ k1 d* H6 c
@ T" Z# s$ l6 \2 X0 h
MKCONFIG := $(SRCTREE)/mkconfig #MKCONFIG指向源码所在目录(U-BOOT顶层目录)下的mkconfig配置文件
! |) S: A+ T5 p' J+ p) y \export MKCONFIG
#在编译UBOOT之前,我们先要执行:
" F* V @' q; C) {% t5 X* G }#make smdk2410_config
* ~4 j8 `) E% M0 J6 J#从本Makefile的下文可以判断出smdk2410_config为Makefile的一个目标。
. ?% X& c! x3 C& {. m; q' F: I#smdk2410_config: unconfig的意思是为smdk2410开发板建立一个编译项。
* }3 V# W( o" O W9 U4 g#显然,执行#make smdk2410_config时,先执行unconfig目标(不指定输出目标时,obj,src变量均为空),unconfig下面的命令主要任务是清理上, X+ D3 d1 }+ h+ h6 H- N
#一次执行make *_config时生成的头文件和makefile的包含文件。8 U7 U; |/ Q( g5 ~/ C4 z
#主要是include/config.h和include/config.mk G' q8 H E" N
#然后执行命令: @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x02 _. f9 P0 w3 X/ l% [
#arm 表示CPU的构架是基于ARM体系的) \% V! X# `3 |) A' g6 A
#arm920t 表示CPU的类型是arm920t* {7 \. k/ M! k/ B( M
#smdk2410 表示开发板的型号' K! c0 Q' e: K8 R! b0 \0 k7 ^
#NULL 表示开发商或者经销商的名称,这里为空( `4 X. Y& L; l3 a6 }1 |
#s3c24x0 表示基于S3C2410的片上系统
9 ^: R; M4 u- }#MKCONFIG指向UBOOT顶层目录下的mkconfig脚本配置文件,后面五个字符串是传入的参数(好像$(@:_config=)也是一个参数)。
' R) y: s4 z+ L#下面来分析一下mkconfig这个脚本配置文件,点击链接:: C9 o' K7 V6 t" V
http://zqwt.012.blog.163.com/blog/static/120446842010325102158182/
) B) v# _! z! n( c5 c& H+ yifneq ($(OBJTREE),$(SRCTREE)) #当目标存放目录不是U-BOOT顶层目录(源码目录)时" z3 ^: h- c! }& Q% m b
REMOTE_BUILD := 1 #定义变量REMOTE_BUILD := 1 这个变量算是一个flag吧
, n$ c t9 n% h' X8 N: Bexport REMOTE_BUILD
7 | z. E7 @4 J" c3 K5 W( v$ Fendif
# $(obj) and (src) are defined in config.mk(顶层目录下) but here in main Makefile! Q% `+ t5 X0 E% u
# we also need them before config.mk is included which is the case for
# ^% q U, A$ L$ B& h# some targets like unconfig, clean, clobber, distclean, etc.
0 o* p( e% l* C$ U: i( y2 ?: C# $(obj) and $(src)都被定义在顶层目录下的config.mk脚本配置文件里面,# ]- R, w, j6 a' R3 O
# 但是在这个主Makefile里面,我们同样需要他们,
* I! I* B K7 e* [# 因为在主Makefile文件包含config.mk之前,$(obj) and $(src)偶尔地会成为这些目标的case:
f3 Y$ x: V/ C2 u# unconfig, clean, clobber, distclean, etc
0 A$ P" x% f9 s% o
ifneq ($(OBJTREE),$(SRCTREE)) #当目标存放目录不是U-BOOT顶层目录(源码目录)时& [. L5 P" J$ ]: [8 s
obj := $(OBJTREE)/ #定义变量obj,让其等于目标存放目录, ` ^6 S. M2 u) Z' A9 ]
src := $(SRCTREE)/ #定义变量src,让其等于uboot顶层目录
! E K# F$ M% n. ~else
! B: |6 E) G$ x( Q- R9 j+ F3 Dobj := #否则,这两个变量都定义为空
" t0 _' r4 u3 G, O% X5 m @: Ysrc :=
4 V3 I& e+ c' u S: L; @endif# p% z$ f7 [" A4 ^
export obj src
: O( b% x& m) W7 o& \' ?# D; V& X# [, q/ d% e
, S) P4 F+ z- t8 d
#########################################################################
# T7 D4 F: r& j# x9 D: s k& z& t. M5 M0 E
9 I* p' [; |& B3 t7 U. K# A$ B
8 j |* ~+ x0 I$ P
ifeq ($(OBJTREE)/include/config.mk,$(wildcard $(OBJTREE)/include/config.mk)) #这句话让人费解!
; Z- ^3 ^/ l% o# i- w0 j9 ]# 通配符在规则中可以自动扩展,但设置在变量中或在函数的参数中通配符一般不能正常扩展。) _0 H9 h9 [( t+ _/ G. Z
# 如果您需要在这些场合扩展通配符,您应该使用函数wildcard,格式如下:
9 s4 g9 n. J! q2 `# $(wildcard pattern...)
; B# E7 K ]) X( i# load ARCH, BOARD, and CPU configuration
- T. ~5 U! z, j; S3 W4 e# 加载ARCH, BOARD, and CPU 配置5 v: B+ `2 I8 z- M! p; h
include $(OBJTREE)/include/config.mk # 这时候,开始包含/include/config.mk的
: y2 i6 s* e/ E9 ^* Texport ARCH CPU BOARD VENDOR SOC
' j/ u) l7 z- E: s
#指定交叉编译器前缀
8 y9 d P( a/ f7 ]1 o& {2 Eifeq ($(ARCH),arm)
- ], e( a& p5 _0 v; @: A. ICROSS_COMPILE = arm-linux-5 V: a. `% ?4 z) S2 v
#这里你可以把交叉编译器的安装路径加到arm-linux-之前,比如你的交叉编译器安装路径是/root/u-boot/usr/local/arm/3.3.2/bin/
% y7 O7 V6 R: U) O#你可以这样定义CROSS_COMPILE = /root/u-boot/usr/local/arm/3.3.2/bin/arm-linux-
9 ^) z; G. p+ [6 ^5 P( y#这样一来,你在终端进行编译的时候就不用指定CROSS_COMPILE=arm-linux-了
3 v) p2 `( \0 i; B# s q#但请注意:在编译内核的时候,交叉编译器必须安装在/usr/local/arm下,否则会发生错误!!!!!
2 A' v" D1 M6 N# l) I3 Jendif
export CROSS_COMPILE
) |4 D& ` W4 J/ d0 {' u: c
- Y# z: o( ~+ Z5 k) Y# load other configuration 加载其他设置,这里是包含顶层目录下的config.mk配置文件,这个文件主要做了三个工作:
/ K! L: a' z0 O( }7 H9 F0 T# 1、定义了交叉编译器 2、定义了编译选项 3、定义了编译规则
: k z3 }$ I5 ?! o6 \3 Y" z, w5 W# 对本文件具体的分析,请查看链接:
; B8 s, j8 E6 \: Yhttp://zqwt.012.blog.163.com/blog/static/12044684201032541139914/
include $(TOPDIR)/config.mk
#########################################################################
% Y O4 a! L5 \5 n! u# U-Boot objects....order is important (i.e. start must be first)
6 G# ~/ R& F, t9 h# uboot目标...书写顺序很重要,比如start.o必须排在第一位& z5 b) F/ ~! J: M3 `# u
#########################################################################
- B7 o9 H$ |0 p: S' DOBJS = cpu/$(CPU)/start.o
1 b x- t) r/ H7 K6 X#start.o必须放在目标文件的第一位,因为uboot执行的第一段代码就是start.S
5 b) E. q5 N% Z#具体原因可以查看链接脚本/u-boot-1.1.6/board/smdk2410/u-boot.lds,点击连接:
3 |" `2 D; c% ?" ?- S5 d& _http://zqwt.012.blog.163.com/blog/static/120446842010320101137932/
, A. ?. F! Z' e- U) Q& P5 I.....................................
9 C) ^& w2 t1 \0 ]; w! GOBJS := $(addprefix $(obj),$(OBJS)) #这句的意思是把目标文件存放路径以前缀的形式加到start.O之前,然后再赋给OBJS
' R- U. y) J- `+ ~3 g m4 J#以下是编译UBOOT需要的库文件
( V8 H$ t0 ~% VLIBS = lib_generic/libgeneric.a
LIBS += board/$(BOARDDIR)/lib$(BOARD).a # 严重平台依赖的
LIBS += cpu/$(CPU)/lib$(CPU).a # 严重平台依赖的
ifdef SOC
4 V% n) g7 X, z# iLIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a # 严重平台依赖的
9 ?! y9 T% l X; ^& Oendif
LIBS += lib_$(ARCH)/lib$(ARCH).a # 严重平台依赖的
5 u) F2 w4 Z- u& ALIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
) Y) I9 \) U R5 C f6 y$ ? fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
6 c: [- o+ K# L8 c& BLIBS += net/libnet.a
9 p. j; P8 A' b' L m: d8 TLIBS += disk/libdisk.a; T9 _2 n* b* H% i* ^8 W
LIBS += rtc/librtc.a
& L8 m* A. S! c9 {LIBS += dtt/libdtt.a, K' d7 X( W& G3 ^( ^
LIBS += drivers/libdrivers.a
8 b1 w/ l/ E! V; q0 n1 }LIBS += drivers/nand/libnand.a
5 d2 V6 v9 n* [; A, T/ K# c! dLIBS += drivers/nand_legacy/libnand_legacy.a- {8 x# b# z! b; L- B5 a g) s
LIBS += drivers/sk98lin/libsk98lin.a3 r4 ]. |* k4 x/ A& e/ O
LIBS += post/libpost.a post/cpu/libcpu.a" C* \ n) Z, p: ]
LIBS += common/libcommon.a
8 t: J7 f7 V" A0 K3 N4 dLIBS += $(BOARDLIBS)
LIBS := $(addprefix $(obj),$(LIBS))
.PHONY : $(LIBS) # 这是一个伪目标
#根据所生成的include/config.mk文件定义的几个变量ARCH, CPU, BOARD, SOC,我们可以
1 c+ D. _3 C7 S2 s* X( S1 V#确定硬件平台依赖的目录文件。smdk2410平台相关(依赖)目录以及对应生成的库文件如下:
! p+ G5 J) Q: t3 l7 q#board/smdk2410/: 库文件board/smdk2410/libsmdk2410.a3 Y% C) z4 O$ V: }9 T! u2 B
#cpu/arm920t/: 库文件cpu/arm920t/libarm920t.a6 f7 _) ?/ U& j* |8 v( U
#cpu/arm920t/s3c24x0: 库文件cpu/arm920t/s3c24x0/libs3c24x0.a1 x! m! P2 ~/ F) s& ~
#lib_arm: 库文件lib_arm/libarm.a
$ `$ N9 t. _7 |4 `2 s#include/asm-arm: 头文件
3 D: G1 ~6 r# ?; V; Q#include/cnofigs/smdk2410.h:头文件
# Add GCC lib
# F; J# g, W* {4 |# F: DPLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc
# The "tools" are needed early, so put this first- n0 y7 o8 a: N! D& a* k4 l
# Don't include stuff already done in $(LIBS) 不要包含已经在 $(LIBS) 中的任何东西
% U2 c3 O. I# I: v#( I! h x9 v) b. W
# 伪目标SUBDIRS:用于执行tools、examples、post、post\cpu子目录下的make文件# |5 W* w. @, h9 c9 a* R, _
SUBDIRS = tools \
: A, y. E/ V0 |; w. X. n9 w* k examples \/ Q4 ]& s w/ D, M2 d6 U
post \
1 Z2 j& u( x+ s$ O8 W post/cpu
& p0 R4 ]* j* g.PHONY : $(SUBDIRS)
ifeq ($(CONFIG_NAND_U_BOOT),y)
9 r$ }; g/ r6 Z0 I5 PNAND_SPL = nand_spl
- _! a' H- \+ B( |U_BOOT_NAND = $(obj)u-boot-nand.bin3 U) |, e' }# i# q4 w. u+ A& Z
endif
__OBJS := $(subst $(obj),,$(OBJS))
) G/ e: [7 u, w% d5 x__LIBS := $(subst $(obj),,$(LIBS))
#########################################################################
* W6 h% K# P. `/ K#########################################################################
, J8 V( p* ?# V5 _6 `/ j1 h4 P
" s" ]0 {% C. L F8 Q#这里是最终要生成的各种镜像文件u-boot.hex、u-boot.srec、u-boot.bin、System.map、u-boot.img3 w n- L% ]# i2 s/ p
ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)6 S. L, T5 i1 e% r9 O. I
all: $(ALL)1 a3 } u( C- x8 E
$(obj)u-boot.hex: $(obj)u-boot
3 ~+ n7 m; n4 [& b) [; o; }- r $(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@ # $<,$@ 分别表示使用该规则的源文件和目标文件
$(obj)u-boot.srec: $(obj)u-boot 3 [- O% o' w8 c- f1 Z
$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@
$(obj)u-boot.bin: $(obj)u-boot
, P1 Q9 o" N' s& b $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
$(obj)u-boot.img: $(obj)u-boot.bin
2 p$ y. s6 D: H l# w7 y ./tools/mkimage -A $(ARCH) -T firmware -C none \" w: X" e/ d% R' ^( }: a& p
-a $(TEXT_BASE) -e 0 \
) w/ F9 R2 A3 I7 Q& n) w -n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
* X7 {0 ~! ^2 O' C+ h+ ^( ] sed -e 's/"[ ]*$$/ for $(BOARD) board"/') \1 B9 C& n. n/ a
-d $< $@
$(obj)u-boot.dis: $(obj)u-boot( }6 I6 K& @8 a$ W- }3 N, R
$(OBJDUMP) -d $< > $@
$ M- e. g% t6 L5 J+ |#此处生成的是uboot的ELF文件镜像9 F2 i/ U5 E+ j' x
$(obj)u-boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
/ T3 t% V8 b* B+ S k0 W2 O2 J UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\ y/ L7 E: Q1 h* X& f; r2 N
cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
7 L# }1 h1 z0 V7 _' U --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \: ?/ Q& c" j+ _! w4 a* ~7 j+ r7 x8 r
-Map u-boot.map -o u-boot
$ B1 z' Q1 e8 W; w5 w& l1 w" }
#依赖目标$(OBJS),也就是cpu/start.o2 z; ~0 p; Y5 b& ]5 y0 ^+ n
$(OBJS):# C0 G. p7 O3 T( O, i- f
$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))
9 r8 R; E# ]2 R1 ^' }' x
" p8 O, g5 |7 M* A( E1 t#依赖目标$(LIBS),目标很多,都是每个子目录的库文件*.a,通过执行相应子目录下的make来完成
z6 x9 U( \( H$(LIBS):
- s1 l" e* v0 Z1 H $(MAKE) -C $(dir $(subst $(obj),,$@))( t. l! m9 {0 H3 Y) u6 Q
- K) v* u( {6 @, ?- _6 F" Y#这里解释一下这个makefile函数 $(dir names...) N9 s6 v, r6 s, ?- i; U; B
#抽取‘names’中每一个文件名的路径部分,文件名的路径部分包括从文件名的开始到最后一个斜杠(含斜杠)
' }* \9 W5 ^8 M6 |% j% e T#之前的一切字符。如果文件名中没有斜杠,路径部分是‘./’。如:2 A2 O( }1 C/ @! y/ C
#$(dir src/foo.c hacks)
( t# r4 @6 o* V" D9 P5 F' Y9 K#产生的结果为 ‘src/ ./’。
& z# c9 m, L0 `' i$(SUBDIRS):
4 s) X! {+ P& C $(MAKE) -C $@ all
$(NAND_SPL): version3 `* P: p2 q/ x) V& F* l5 ^
$(MAKE) -C nand_spl/board/$(BOARDDIR) all
$(U_BOOT_NAND): $(NAND_SPL) $(obj)u-boot.bin
/ D, K' \$ M) G( I7 D cat $(obj)nand_spl/u-boot-spl-16k.bin $(obj)u-boot.bin > $(obj)u-boot-nand.bin
6 K& l" G* T* P9 Z
#依赖目标version:生成版本信息到版本文件VERSION_FILE中3 v5 M+ U! D- u/ f; S. o
version:' r2 [0 Y7 s: G5 b' X& O5 s# `+ A
@echo -n "#define U_BOOT_VERSION \"U-Boot " > $(VERSION_FILE); \
# B4 }& ?8 H: @+ E# k/ L" ~7 e echo -n "$(U_BOOT_VERSION)" >> $(VERSION_FILE); \' V( P0 }9 B& ]8 A3 ]
echo -n $(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion \
& A4 v8 G$ t; `; s/ X $(TOPDIR)) >> $(VERSION_FILE); \, `4 @( r1 j' _! S2 B* P9 U' {# N
echo "\"" >> $(VERSION_FILE)
gdbtools:
8 X2 @; H- H! X0 L $(MAKE) -C tools/gdb all || exit 1
updater:
9 H6 |# F. ^( V) Q0 W$ E $(MAKE) -C tools/updater all || exit 1
env:( W# i/ R6 x' E* ^( f
$(MAKE) -C tools/env all || exit 1
* M S0 h% m; _7 U8 g! W# u% _
- Y( c' r! J# f; J; h( U#依赖目标depend:生成各个子目录.depend文件,.depend列出每个目标文件的依赖文件。9 ~# t, x2 F7 x
#生成方法:调用每个子目录的make_depend
* p5 W2 |9 B3 v3 Z& l* Sdepend dep:
, V8 ^6 ?" b" d" Z9 }% C' ~ for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done
0 V7 O& n l+ t$ D- C
tags ctags:+ }1 ?" q% I; r; @
ctags -w -o $(OBJTREE)/ctags `find $(SUBDIRS) include \
U, ]' z4 V7 J2 S- g lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \
# }( u$ M; K+ I8 S6 g: W$ x' T fs/cramfs fs/fat fs/fdos fs/jffs2 \
/ v6 |7 w, R3 L( u! w net disk rtc dtt drivers drivers/sk98lin common \
0 N' Y) g- Y% O. d \( -name CVS -prune \) -o \( -name '*.[ch]' -print \)`
etags:
. D# R, u/ `# a2 f2 B6 y etags -a -o $(OBJTREE)/etags `find $(SUBDIRS) include \
, P0 T3 {( Y8 g3 x8 N& x) P# W lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \
( I: M5 C: Q/ `# c+ |* k. x9 e4 ? A fs/cramfs fs/fat fs/fdos fs/jffs2 \& U0 |+ ?# M/ S% b. x- c7 Z# }
net disk rtc dtt drivers drivers/sk98lin common \
, ~- _( M: B2 l \( -name CVS -prune \) -o \( -name '*.[ch]' -print \)`
$(obj)System.map: $(obj)u-boot! b0 a' e. O. @# q. K$ x! u3 y! U
@$(NM) $< | \
$ D5 A- t0 U2 Q% A grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \ F; o. F: A5 G
sort > $(obj)System.map
& N- c5 p6 O/ Q9 i5 |
2 h# e3 i! W; g: Z4 Y, ~* D#########################################################################- q. W5 x+ `( M+ s2 O
else/ o& S# @# G1 ~7 v5 R
all $(obj)u-boot.hex $(obj)u-boot.srec $(obj)u-boot.bin \$ R/ u6 y ^7 Y* u
$(obj)u-boot.img $(obj)u-boot.dis $(obj)u-boot \
4 H: B U. \+ e: h* ]2 E `2 O8 Y$(SUBDIRS) version gdbtools updater env depend \
G0 f$ F, `8 `% Fdep tags ctags etags $(obj)System.map:. r9 I7 k: u" D
@echo "System not configured - see README" >&23 A4 w- d1 M9 V+ `8 g! A
@ exit 11 F9 G' D$ J6 K8 i" q* L
endif
.PHONY : CHANGELOG
4 @) { L) Y( W U! G" w/ O+ [* j5 O4 wCHANGELOG:
- ^# Y8 K# O4 @2 F' S% r1 X( h git log --no-merges U-Boot-1_1_5.. | \1 Q, p2 u' t) ~: m" n1 J3 @
unexpand -a | sed -e 's/\s\s*$$//' > $@
0 k9 l' q. Y$ ]6 l$ {; f) Z2 d
#########################################################################
% B, s/ K ?( t, H5 r9 A# 这里就是我们所谓的unconfig,应该比较熟悉了!/ P! v' b% p2 B0 b m( y
# 很明显,它所做的工作就是清除上次生成的include/config.h和include/config.mk
! w; s+ O# T0 F1 l# E; w# 以及开发板目录下的一些临时配置文件0 N& E- |4 F( K* U
# unconfig:$ @1 U. b$ q7 c0 Y/ F+ t. C8 Z
# @rm -f $(obj)include/config.h $(obj)include/config.mk \
* W- o: H2 [9 M, U4 h9 p# V# $(obj)board/*/config.tmp $(obj)board/*/*/config.tmp" p$ `+ ~; _8 X1 Z Z$ A
#########################################################################
8 `9 Q9 Y5 |0 P- s, d+ L. w7 Z................................
2 W3 t& b4 s6 j" Z/ E3 gsmdk2400_config : unconfig4 F3 T/ h9 b" T. @1 L& B3 y
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2400 NULL s3c24x0
4 U* P0 T7 E5 r0 F2 c! K - u9 l! z! Z& I7 r) \9 l* _
................................
smdk2410_config : unconfig' d: { @* F) c2 P- `. ?
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
+ w1 [8 L' j) }9 Y- i# D6 O#我删了一部分内容,那些几乎都是各种不同板子的*_config,也就是目标的定义。
最后总结一下,当然这里也参考了前辈们的许多宝贵经验,顶层Makefile的主要任务就是组织整个u-boot工程的编译,概括可以分为一下几个步骤:
1、首先通过执行make *_config传入$(@:_config=), ARCH, CPU, BOARD, VENDOR, SOC参数(一共六个参数但不
4 s% {( b9 S1 I0 ~7 b 7 a0 B b3 D. g% I
一定同时存在),给mkconfig。
2、mkconfig接收到传递过来的参数后,将include头文件夹相应的头文件夹链接好,生成config.h
3、然后执行make分别调用各个子目录的makefile文件,以生成所有的obj文件(包括start.o)和obj库文件*.a。
4、最后,通过链接器把所有目标文件链接起来,生成uboot镜像。不同格式的镜像都是调用相应工具,经
由elf镜像间接或者直接的生成的。