注:本文是学习朱老师课程整理的笔记,基于uboot-1.3.4和s5pc11x分析。
- 版本号的确定
VERSION = 1 #主版本号
PATCHLEVEL = 3 #次版本号
SUBLEVEL = 4 #再次版本号
EXTRAVERSION = #另外附加的版本信息
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
VERSION_FILE = $(obj)include/version_autogenerated.h
通过上面4个变量用“.”分隔开共同构成了最终的版本号。
include/version_autogenerated.h
文件是编译过程中自动生成的一个文件,所以源目录中没有,但是编译过后的uboot中就有了。它里面的内容是一个宏定义,宏定义的值内容就是Makefile中配置的uboot的版本号:
#define U_BOOT_VERSION "U-Boot 1.3.4"
- CPU架构和操作系统
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/ppc64/ppc/ \
-e s/macppc/ppc/)
HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
sed -e 's/\(cygwin\).*/cygwin/')
export HOSTARCH HOSTOS
“sed –e”表示后面跟的是一串命令脚本。
“s/i.86/i386/”表示要从标准输入中,查找到内容为“i.86”,然后替换成“i386”。其中“i.86”表达式的“.”为通配符。遇到 ’i.86’ 就替换为 ‘i386’。
shell中的“|”叫做管道,管道的作用就是把管道前面一个运算式的输出作为后面一个的输入再去做处理,最终的输出才是我们整个式子的输出。
HOSTARCH:主机的CPU的架构。直接在shell中执行“uname –m”输出主机CPU的体系架构类型i686。“i686”可以匹配命令“sed -e s/i.86/i386/”中的“i.86”,因此在作者的机器上执行Makefile,HOSTARCH将被设置成“i386”。
HOSTOS:主机操作系统。直接在shell中执行“uname –s”得到Linux。
- 静默编译
# Allow for silent builds
ifeq (,$(findstring s,$(MAKEFLAGS)))
XECHO = echo
else
XECHO = :
endif
在用“make”进行编译时命令行会打印出来很多编译信息。如果不希望看到这些编译信息,就是在编译时用“make -s”,“-s”会作为MAKEFLAGS传给Makefile,在这段代码作用下,寻找参数是否有‘s’选项,如果有,和逗号前的空不等,执行else后面的内容,XECHO变量就会被变成空(默认等于echo),于是实现了静默编译。
- 编译输出目录
-默认的是原地编译。如果需要指定具体的输出目录编译则有2种方式来指定输出目录。
第一种:make O=输出目录
第二种:export BUILD_DIR=输出目录,然后再make。
如果两个都指定了,则第一种会覆盖第二种。
以下为代码实现过程:
ifdef O
ifeq ("$(origin O)", "command line")
BUILD_DIR := $(O)
endif
endif
ifneq ($(BUILD_DIR),)
saved-output := $(BUILD_DIR)
# Attempt to create a output directory.
$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})
# Verify if it was successful.
BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)
$(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))
endif # ifneq ($(BUILD_DIR),)
OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
SRCTREE := $(CURDIR)
TOPDIR := $(SRCTREE)
LNDIR := $(OBJTREE)
export TOPDIR SRCTREE OBJTREE
MKCONFIG := $(SRCTREE)/mkconfig
export MKCONFIG
ifneq ($(OBJTREE),$(SRCTREE))
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.
ifneq ($(OBJTREE),$(SRCTREE))
obj := $(OBJTREE)/
src := $(SRCTREE)/
else
obj :=
src :=
endif
export obj src
在18-20行中:
OBJTREE:编译出的.o文件存放的目录的根目录。在默认编译下,OBJTREE等于当前目录;在O=xx编译下,OBJTREE就等于XX目录。
SRCTREE: 源码目录,其实就是源代码的根目录,也就是当前目录。
在默认编译下,OBJTREE和SRCTREE相等;在O=xx这种编译下OBJTREE和SRCTREE不相等。Makefile中定义这两个变量,就是为了记录编译后的.o文件往哪里放。
在24行定义了个MKCONFIG的变量,这个变量很重要,在后面使用,它的值就是我们源码根目录下面的mkconfig。这个mkconfig是一个脚本,这个脚本就是uboot配置阶段的配置脚本。(对mkconfig的具体分析见uboot下mkconfig脚本的分析)
- CROSS_COMPILE
ifeq ($(obj)include/config.mk,$(wildcard $(obj)include/config.mk))
# load ARCH, BOARD, and CPU configuration
include $(obj)include/config.mk
export ARCH CPU BOARD VENDOR SOC
……
ifndef CROSS_COMPILE #1.在编译时用make CROSS_COMPILE=xxxx来设置
ifeq ($(ARCH),arm) #2.在Makefile中去更改设置CROSS_COMPILE的值
#CROSS_COMPILE = arm-linux- #相对路径
CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi- #绝对路径
endif
……
export CROSS_COMPILE
ARCH上面导出的,值来自于我们的配置过程,它的值会影响后面的CROSS_COMPILE环境变量的值。
我们可以在编译时用make CROSS_COMPILE=xxxx来设置,也可以在Makefile中去更改设置CROSS_COMPILE的值,如果用在编译时传参的方法可以覆盖Makefile里面的设置。
CROSS_COMPILE是定义交叉编译工具链的前缀的。定义这些前缀是为了在后面用(前缀加上后缀就是各种工具链中的工具名字)。在不同CPU架构上的交叉编译工具链,只是前缀不一样,后缀都是一样的。因此定义时把前缀和后缀分开,只需要在定义前缀时区分各种架构即可实现可移植性。
CROSS_COMPILE的设置值只要能保证找到那个交叉编译工具链即可,不一定非得是全路径的,相对路径也可以。如果已经将工具链导出到环境变量(有时还需设置符号链接),这样CROSS_COMPILE = arm-linux-就可以。
- 编译选项设置
# load other configuration
include $(TOPDIR)/config.mk
详细的分析见《uboot根目录下的config.mk的分析》
- U-Boot objects
#########################################################################
# U-Boot objects....order is important (i.e. start must be first)
OBJS = cpu/$(CPU)/start.o
……
LIBS = lib_generic/libgeneric.a
LIBS += $(shell if [ -f board/$(VENDOR)/common/Makefile ]; then echo \
"board/$(VENDOR)/common/lib$(VENDOR).a"; fi)
LIBS += cpu/$(CPU)/lib$(CPU).a
ifdef SOC
LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a
endif
ifeq ($(CPU),ixp)
LIBS += cpu/ixp/npe/libnpe.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 fs/ext4/libext4fs.a
LIBS += net/libnet.a
OBJS、LIBS所代表的.o、.a文件就是u-boot的编译材料。
- 编译
ALL += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND) $(U_BOOT_ONENAND) $(obj)u-boot.dis
all: $(ALL)
$(obj)u-boot.hex: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@
$(obj)u-boot.srec: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@
$(obj)u-boot.bin: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
……
$(obj)u-boot: depend $(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT)
UNDEF_SYM=`$(OBJDUMP) -x $(LIBBOARD) $(LIBS) | \
sed -n -e 's/.*\($(SYM_PREFIX)__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
整个主Makefile中的第一个目标all,也就是默认目标,我们直接在uboot根目录下make其实就等于make all,就等于make这个目标。最后的u-boot是最终编译链接生成的elf格式的可执行文件。
- 配置
smdk2410_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
smdk2410_config是主Makefile中的一个目标。,我们配置开发板时使用:make smdk2410_config,因此分析smdk2410_config是主Makefile中的一个目标。
unconfig的在Makefile的上面有定义:
unconfig:
@rm -f $(obj)include/config.h $(obj)include/config.mk \
$(obj)board/*/config.tmp $(obj)board/*/*/config.tmp \
$(obj)include/autoconf.mk $(obj)include/autoconf.mk.dep \
$(obj)board/$(VENDOR)/$(BOARD)/config.mk
“unconfig”字面意思来理解就是未配置。这个符号用来做为我们各个开发板配置目标的依赖。目的是当我们已经配置过一个开发板后再次去配置时还可以配置。
MKCONFIG的变量已经定义,它的值就是我们源码根目录下面的mkconfig。