这次分析源码根目录下的Makefile,它负责读入配置过的信息,通过OBJS、LIBS等变量设置能够参与镜像链接的目标文件,设定编译的目标等等。
1 HOSTARCH := $(shell uname -m |
2 sed -e s/i.86/x86/
3 -e s/sun4u/sparc64/
4 -e s/arm.*/arm/
5 -e s/sa110/arm/
6 -e s/ppc64/powerpc/
7 -e s/ppc/powerpc/
8 -e s/macppc/powerpc/
9 -e s/sh.*/sh/)
10
11 HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' |
12 sed -e 's/(cygwin).*/cygwin/')
13
14 # Set shell to bash if possible, otherwise fall back to sh
15 SHELL := $(shell if [ -x '$$BASH' ]; then echo $$BASH;
16 else if [ -x /bin/bash ]; then echo /bin/bash;
17 else echo sh; fi; fi)
18
19 export HOSTARCH HOSTOS SHELL
上述代码通过shell处理字符数据,得到宿主机的架构、操作系统、shell类型等变量,Makefile中调用shell命令的方式有两种,一种是$(shell cmd_name),另一种是命令前后加上反引号``,如$(shell uname -m)或者`uname -m`。这里可以看到,sed的选项可以是多个,之前不知道还能这么用,学习了。
1 ifdef O
2 ifeq ('$(origin O)', 'command line')
3 BUILD_DIR := $(O)
4 endif
5 endif
6
7 ifneq ($(BUILD_DIR),)
8 saved-output := $(BUILD_DIR)
9
10 # Attempt to create a output directory.
11 $(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})
12
13 # Verify if it was successful.
14 BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)
15 $(if $(BUILD_DIR),,$(error output directory '$(saved-output)' does not exist))
16 endif # ifneq ($(BUILD_DIR),)
17
18 OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
19 SPLTREE := $(OBJTREE)/spl
20 SRCTREE := $(CURDIR)
21 TOPDIR := $(SRCTREE)
22 LNDIR := $(OBJTREE)
23 export TOPDIR SRCTREE OBJTREE SPLTREE
24
25 MKCONFIG := $(SRCTREE)/mkconfig
26 export MKCONFIG
27
28 ifneq ($(OBJTREE),$(SRCTREE))
29 REMOTE_BUILD := 1
30 export REMOTE_BUILD
31 endif
BUILD_DIR可以通过命令行中O=xxx指定。BUILD_DIR和SRCTREE是相同的,代表native build,REMOTE_BUILD = 0。最后将各个变量输出到全局,TOPDIR在子Makefile中经常使用,它的内容是源码根目录的绝对路径。
MKCONFIG代表根目录下的mkconfig脚本,这个脚本的内容在前面已经分析过了。
1 all:
2 sinclude $(obj)include/autoconf.mk.dep
3 sinclude $(obj)include/autoconf.mk
这里出现了all,虽然它的确是我们的目标,但是这里并不是要描述它的依赖和生成命令,而是为了防止sinclude的文件中的某个目标成为默认目标,造成严重的逻辑错误。这是一个小技巧,纠正了我对于Makefile的错误认识:之前认为某个目标的依赖关系必须在一条描述中写完,要是这个目标的多个依赖分散在多处写的话,每一部分依赖都有一条命令与之对应。这是错的,其实目标的依赖关系可以分成多次写完,你只需要在某一处指明要执行的命令就行了,在U-Boot的Makefile中,由源文件生成目标文件的命令都是放在模式规则中描述的,而目标文件对于头文件的依赖是在其他地方描述的,这一点已经在分析具体子Makefile的时候说过了。
从内容上来说,它从autoconf.mk文件中读入了很多配置信息。
1 include $(obj)include/config.mk
2 export ARCH CPU BOARD VENDOR SOC
前面分析mkconfig脚本的时候,我们知道include/config.mk文件的主要内容是指定ARCH CPU SOC VENDOR BOARD等信息,这里又输出到全局,对于后面的配置影响很大。
1 OBJS = $(CPUDIR)/start.o
2 ifeq ($(CPU),x86)
3 OBJS += $(CPUDIR)/start16.o
4 OBJS += $(CPUDIR)/resetvec.o
5 endif
6 ifeq ($(CPU),ppc4xx)
7 OBJS += $(CPUDIR)/resetvec.o
8 endif
9 ifeq ($(CPU),mpc85xx)
10 OBJS += $(CPUDIR)/resetvec.o
11 endif
12
13 OBJS := $(addprefix $(obj),$(OBJS))
重要变量OBJS登场,它表示U-Boot必须要编译链接的那些目标文件,而后面可选的驱动、文件系统、网络配置都放在LIBS变量中描述。OBJS变量中,一定要注意目标文件的顺序,这关系到处理器能否正常工作,倘若你把启动配置cpu的代码放在了镜像文件的后面,而非开始位置,那么一定会出错。
1 HAVE_VENDOR_COMMON_LIB = $(if $(wildcard board/$(VENDOR)/common/Makefile),y,n)
2
3 LIBS-y += lib/libgeneric.o
4 LIBS-y += lib/lzma/liblzma.o
5 LIBS-y += lib/lzo/liblzo.o
6 LIBS-y += lib/zlib/libz.o
7 LIBS-$(CONFIG_TIZEN) += lib/tizen/libtizen.o
8 LIBS-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/lib$(VENDOR).o
9 LIBS-y += $(CPUDIR)/lib$(CPU).o
10 ifdef SOC
11 LIBS-y += $(CPUDIR)/$(SOC)/lib$(SOC).o
12 endif
13 ifeq ($(CPU),ixp)
14 LIBS-y += drivers/net/npe/libnpe.o
15 endif
16 LIBS-$(CONFIG_OF_EMBED) += dts/libdts.o
17 LIBS-y += arch/$(ARCH)/lib/lib$(ARCH).o
18 LIBS-y += fs/cramfs/libcramfs.o
19 fs/ext4/libext4fs.o
20 fs/fat/libfat.o
21 fs/fdos/libfdos.o
22 fs/jffs2/libjffs2.o
23 fs/reiserfs/libreiserfs.o
24 fs/ubifs/libubifs.o
25 fs/yaffs2/libyaffs2.o
26 fs/zfs/libzfs.o
首先说一下Makefile中的wildcard函数,它的本意是通配符,但是这里的用途是判断 一个路径是否存在,如果你写的路径不合法,不存在,那么返回空字符串。
可以看到LIBS-y变量开始收割了,它包含了lib fs arch net drivers等各个目录下的目标文件,因为这些功能都是可选的,因此以lib的形式出现。那么浏览一下它包含了哪些目标文件吧!
1. lib目录下的通用库文件
2. arch/arm/cpu/armv7/libarmv7.o 实现armv7的cache等部件的管理。
3. arch/arm/cpu/armv7/s5pc1xx/libs5pc1xx.o 实现了reset clock等部件的管理。
4. arch/arm/lib/libarm.o 架构相关的底层库。
5. fs/下的诸多文件系统
6. net目录下的libnet.o 网络协议栈相关的库。
7. drivers/ :block dma fpga gpio i2c input misc mmc mtd net power pci spi等文件下的libxx.o
1 ifeq ($(SOC),s5pc1xx)
2 LIBS-y += $(CPUDIR)/s5p-common/libs5p-common.o
3 endif
包含s5p系列soc的公用库,比如timer srom watchdog等。
1 LIBS := $(addprefix $(obj),$(sort $(LIBS-y)))
2 .PHONY : $(LIBS)
3
4 LIBBOARD = board/$(BOARDDIR)/lib$(BOARD).o
5 LIBBOARD := $(addprefix $(obj),$(LIBBOARD))
LIBS-y经过排序、清理掉重复目标文件之后,放在新的变量LIBS中,并将它声明为伪目标。
LIBBOARD := board/samsung/goni/libgoni.o,这也是一个重要的变量。
1 # Add GCC lib
2 ifdef USE_PRIVATE_LIBGCC
3 ifeq ('$(USE_PRIVATE_LIBGCC)', 'yes')
4 PLATFORM_LIBGCC = $(OBJTREE)/arch/$(ARCH)/lib/libgcc.o
5 else
6 PLATFORM_LIBGCC = -L $(USE_PRIVATE_LIBGCC) -lgcc
7 endif
8 else
9 PLATFORM_LIBGCC := -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc
10 endif
11 PLATFORM_LIBS += $(PLATFORM_LIBGCC)
12 export PLATFORM_LIBS
这里添加LIBGCC,看不明白,暂不研究。
1 __OBJS := $(subst $(obj),,$(OBJS))
2 #__LIBS := $(subst $(obj),,$(LIBS)) $(subst $(obj),,$(LIBBOARD))
3
4 __LIBS := $(subst $(obj),,$(LIBBOARD)) $(subst $(obj),,$(LIBS))
重量级变量登场!__OBJS是除去了obj前缀的OBJS,就是start.o。__LIBS则由两部分组成,LIBBOARD在前,LIBS在后,其实官方源码中两者的顺序是相反的。为什么要修改呢?这就跟S5PV210的启动流程有关系了。移植的时候,我们选择修改goni的代码,其中要修改的文件之一是board/samsung/goni/lowlevel_init.S,根据路径可以知道这里的文件都会被链接进libgoni.o中,也就是LIBBOARD变量里,如果它放在LIBS后面,那么lowlevel_init.S中的代码将会在镜像靠后的位置,绝对在16KB以后,而S5PV210在启动的时候会将SD卡的前16KB搬移到iRAM中,这将无法运行我们修改的代码,所以修改。
1 BOARD_SIZE_CHECK =
2 @actual=`wc -c $@ | awk '{print $$1}'`;
3 limit=$(CONFIG_BOARD_SIZE_LIMIT);
4 if test $$actual -gt $$limit; then
5 echo '$@ exceeds file size limit:';
6 echo ' limit: $$limit bytes';
7 echo ' actual: $$actual bytes';
8 echo ' excess: $$((actual - limit)) bytes';
9 exit 1;
10 fi
这里就是一个检查镜像大小的函数,如果超出了用户设定的大小就报错,理解起来很简单,不多说,一般可以不用这个功能。
1 ALL-y += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map
2
3 ALL-$(CONFIG_NAND_U_BOOT) += $(obj)u-boot-nand.bin
4 ALL-$(CONFIG_ONENAND_U_BOOT) += $(obj)u-boot-onenand.bin
5 ALL-$(CONFIG_SPL) += $(obj)spl/u-boot-spl.bin
6 ALL-$(CONFIG_OF_SEPARATE) += $(obj)u-boot.dtb $(obj)u-boot-dtb.bin
7
8 # enable combined SPL/u-boot/dtb rules for tegra
9 ifeq ($(SOC),tegra20)
10 ifeq ($(CONFIG_OF_SEPARATE),y)
11 ALL-y += $(obj)u-boot-dtb-tegra.bin
12 else
13 ALL-y += $(obj)u-boot-nodtb-tegra.bin
14 endif
15 endif
16
17 all: $(ALL-y) $(SUBDIR_EXAMPLES)
终于开始描述all的依赖了,all的依赖是ALL-y,可以看到这个变量的内容是u-boot.srec u-boot.bin System.map,另外,我们还可以通过宏来给它增添新的内容:u-boot-nand.bin u-boot-onenand.bin u-boot-spl.bin u-boot.dtb u-boot-dtb.bin。
上面这段代码的最后就写清楚了all的所有依赖:ALL-y $(SUBDIR_EXAMPLES),看一看这两个变量里面的内容吧。
ALL-y其实指定的是u-boot镜像的生成格式。u-boot是包含各种调试信息、符号表等元素的镜像文件,可以用于阅读分析,但是不适合在嵌入式平台上烧写,因为文件容量大。u-boot.bin是不包含地址信息的二进制指令数据流,文件容量小,我们一般使用这个进行烧写。u-boot.srec,这是一种S-Record格式的文件,是由Motorola公司推出的一种用于flash烧写的镜像文件格式,每一行都由S字母开头,后面有类型字段、地址字段、数据字段以及校验和等等。u-boot.hex,这也是一种烧写flash的镜像文件格式,它每行都由0x3a这个字节开始,后面有一字节的行长度字段、两字节的地址字段、一字节的数据类型字段、数据以及校验和。u-boot里还支持生成其他格式的镜像文件,但是我暂时用不着,先不分析。
SUBDIR_EXAMPLES指向了examples目录下的文件,主要生成一些测试文件,暂不深究。
我们关注的是u-boot.bin文件,因此看一下它的依赖:
1 $(obj)u-boot: depend
2 $(SUBDIR_TOOLS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
3 $(GEN_UBOOT)
u-boot的依赖有7个:depend SUBDIR_TOOLS OBJS LIBBOARD LIBS LDSCRIPT u-boot.lds,生成的命令是$(GEN_UBOOT),下面逐个看一下。
1 # Explicitly make _depend in subdirs containing multiple targets to prevent
2 # parallel sub-makes creating .depend files simultaneously.
3 depend dep: $(TIMESTAMP_FILE) $(VERSION_FILE)
4 $(obj)include/autoconf.mk
5 $(obj)include/generated/generic-asm-offsets.h
6 $(obj)include/generated/asm-offsets.h
7 for dir in $(SUBDIRS) $(CPUDIR) $(LDSCRIPT_MAKEFILE_DIR) ; do
8 $(MAKE) -C $$dir _depend ; done
depend、dep两个目标拥有同样的依赖关系,执行相同的命令。不难发现depend的命令中并没有生成depend这个文件,因此它的作用相当于一个伪目标,每次执行make命令,都会发现depend目标不存在,接着就开始检查depend的各个依赖。更新depend目标时,也是更换到其他路径下执行make _depend命令,结合前面对于rules.mk的分析知道,_depend也是一个伪目标,用于生成某个文件夹下的所有文件依赖关系,这里很绕,慢慢体会。下面看一看depend的依赖。
include/autoconf.mk这个文件中有很多重要的配置信息,它的依赖关系如下所示。
1 $(obj)include/autoconf.mk: $(obj)include/config.h
2 @$(XECHO) Generating $@ ;
3 set -e ;
4 : Extract the config macros ;
5 $(CPP) $(CFLAGS) -DDO_DEPS_ONLY -dM include/common.h |
6 sed -n -f tools/scripts/define2mk.sed > $@.tmp &&
7 mv $@.tmp $@
include/autoconf.mk文件的依赖是include/config.h,config.h是执行make s5p_goni_config的时mkconfig脚本自动生成的文件,所以说,变更配置才会更新autoconf.mk文件,这也进一步说明autoconf.mk文件确实是用来描述当前配置选项的。
分析一下上面用到的各条命令,第一句就是echo Generating include/autoconf.mk。第二句,set -e的作用是监测当前进程中命令的执行情况,当有命令执行出错时直接退出,这里的关键字是“当前进程”,如果你以 “./example.sh”方式执行某个脚本时出现的错误并不能被捕捉,因为这种方式的调用是以子进程的方式执行的,所以应该使用“source example.sh”或者“ . example.sh”的方式。第三句,cpp -dM include/common.h | sed -n -f tools/scripts/define2mk.sed > include/autoconf.mk.tmp && mv include/autoconf.mk.tmp include/autoconf.mk,cpp是gcc的预处理器,-dM选项会输出很多预处理信息,比如宏定义,类型定义等等,而且它会递归地解析你包含的头文件,比如说这里的include/common.h文件中有一句#include
上一篇:U-Boot Makefile分析(4)具体子Makefile的分析
下一篇:U-Boot bootargs简析
- 热门资源推荐
- 热门放大器推荐
- STEVAL-ILL035V1,使用带有集成升压控制器的 LED7708 多通道 LED 背光驱动器的演示板
- ST2S06B 双路同步整流带复位或抑制、0.5 A、1.5 MHz 可调降压开关稳压器的典型应用电路
- LT3957EUHE、5V 至 16V 输入、-12V 输出反相转换器的典型应用电路
- LT3475IFE-1 演示板、双路降压型 1.5A LED 驱动器
- 使用 NXP Semiconductors 的 BFU730F 的参考设计
- AD5347 并行接口、八路电压输出、10 位 DAC 的典型应用
- 使用 TC7662B 负电荷泵转换器的典型应用电路
- AKD4358,用于 AK4358 192kHz PCM 数据、用于 DVD 音频和 SACD 应用的 DSD 数据的评估板
- 具有输出电池短路保护功能的 ADA4432-1 SD 视频滤波放大器的典型应用电路
- 使用 Analog Devices 的 ADA4062-2ARMZ-RL7 的参考设计



现代雷达系统的信号设计
MM1304XJ

BFR340T






京公网安备 11010802033920号