U-Boot-1.1.6顶层Makefile分析(一)

    技术2022-07-10  135

    笔者对Makefile不熟,一点一点分析代码,要是出错了麻烦各位大佬在评论指出。感激不尽。 第一篇,先把Makefile前面的变量和环境分析完吧。分析到all目标,然后后面的就跟着make时的流程走应该会比较好理解。

    VERSION = 1 PATCHLEVEL = 1 SUBLEVEL = 6 EXTRAVERSION = U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) 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/powerpc/ppc/ \ -e s/macppc/ppc/) HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \ sed -e 's/\(cygwin\).*/cygwin/') export HOSTARCH HOSTOS

    VERSION:主版本号 PATCHLEVEL:补丁版本号 SUBLEVEL:次版本号 EXTRAVERSION:附加版本信息 U_BOOT_VERSION:1.1.6 HOSTARCH:使用uname -m(获取CPU架构,这个看自己的电脑),通过管道输入到sed命令,sed命令将i.86替换为i386,将sun4u替换为sparc64… HOSTOS:使用uname -s(获取操作系统),通过管道输入到tr,tr命令将大写字符转换成小写字符,通过管道输入到sed,sed将"(cygwin).*“替换为"cygwin”。 接着,使用export将主机架构和操作系统传递到子Makefile。 将这些变量输出的结果为: 执行make mytest

    # Deal with colliding definitions from tcsh etc. 处理来自tcsh等的冲突定义 VENDOR= ######################################################################### # # U-boot build supports producing a object files to the separate external # directory. Two use cases are supported: # # 1) Add O= to the make command line # 'make O=/tmp/build all' # # 2) Set environement variable BUILD_DIR to point to the desired location # 'export BUILD_DIR=/tmp/build' # 'make' # # The second approach can also be used with a MAKEALL script # 'export BUILD_DIR=/tmp/build' # './MAKEALL' # # Command line 'O=' setting overrides BUILD_DIR environent variable. # # When none of the above methods is used the local build is performed and # the object files are placed in the source directory. # 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

    ifeq ("$(origin O)", “command line”): 如果O的内容是来自命令行的,也就是在执行make的时候带了O参数。 BUILD_DIR := $(O): 将O的内容赋给BUILD_DIR变量。

    ifneq ($(BUILD_DIR),): 如果BUILD_DIR不为空。 saved-output := $(BUILD_DIR): 则将BUILD_DIR变量中的内容保存在saved-output变量中,此时saved-output变量保存的肯定是从命令行输入的O的内容。

    $(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}): 然后用-d 参数测试BUILD_DIR变量内容是不是一个目录,并且建立这个目录(如果目录不存在的话)。 BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd): 切换到目标目录下,使用pwd命令获取路径并且再次复制给BUILD_DIR,代码里给出的解释是“Verify if it was successful”,应该是验证上面创建文件夹的操作是否成功。

    $(if $(BUILD_DIR),,$(error output directory “$(saved-output)” does not exist)): 如果BUILD_DIR有数据,则不作任何操作,如果没有输出错误信息提示目标文件夹不存在。 OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR)): 如果BUILD_DIR有数据,则将BUILD_DIR付给OBJTREE变量,否则OBJTREE为当前路径。 SRCTREE := $(CURDIR): SRCTREE为当前路径 = 顶层Makefile所在路径。 TOPDIR := $(SRCTREE): TOPDIR = SRCTREE = 当前路径 = 顶层Makefile所在路径。 LNDIR := $(OBJTREE): LNDIR = OBJTREE = 设置的目标文件夹或者= 顶层Makefile所在路径。 export TOPDIR SRCTREE OBJTREE: 导出到子Makefile。 MKCONFIG := $(SRCTREE)/mkconfig: MKCONFIG为顶层Makefile所在路径下的mkconfig工具。 export MKCONFIG: 导出MKCONFIG到子Makefile。

    ifneq ($(OBJTREE),$(SRCTREE)) REMOTE_BUILD := 1 export REMOTE_BUILD endif: 如果OBJTREE不等于SRCTREE,也就是在make时使用O参数指定了目标文件夹,则REMOTE_BUILD赋值为1,并且将REMOTE_BUILD导出。

      其实在上面的英文说明也有讲到O参数是用来设置编译uboot产生的目标文件路径。   我在uboot-1.1.6版本的uboot中没有找到help参数,在最新的uboot-2020.07版本中有help参数,可以将uboot提供的操作输出出来。这里贴出输出的部分信息,其中就有O参数的说明:

    # $(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

    ifneq ($(OBJTREE),$(SRCTREE)): 如果在,make时使用O变量指定了目标文件夹路径,则: obj := $(OBJTREE)/: obj赋值为目标文件夹路径。 src := $(SRCTREE)/: src为当前路径= 顶层Makefile所在路径。 else obj := src := endif: 否则,obj和src都为空。 export obj src: 导出obj和src。   后面的分析以不使用O变量指定目标文件夹路径为准。

    ifeq ($(OBJTREE)/include/config.mk,$(wildcard $(OBJTREE)/include/config.mk)) # load ARCH, BOARD, and CPU configuration include $(OBJTREE)/include/config.mk export ARCH CPU BOARD VENDOR SOC ifndef CROSS_COMPILE ifeq ($(HOSTARCH),ppc) CROSS_COMPILE = else ifeq ($(ARCH),ppc) CROSS_COMPILE = powerpc-linux- endif ifeq ($(ARCH),arm) CROSS_COMPILE = arm-linux- endif ifeq ($(ARCH),i386) ifeq ($(HOSTARCH),i386) CROSS_COMPILE = else CROSS_COMPILE = i386-linux- endif endif ifeq ($(ARCH),mips) CROSS_COMPILE = mips_4KC- endif ifeq ($(ARCH),nios) CROSS_COMPILE = nios-elf- endif ifeq ($(ARCH),nios2) CROSS_COMPILE = nios2-elf- endif ifeq ($(ARCH),m68k) CROSS_COMPILE = m68k-elf- endif ifeq ($(ARCH),microblaze) CROSS_COMPILE = mb- endif ifeq ($(ARCH),blackfin) CROSS_COMPILE = bfin-elf- endif ifeq ($(ARCH),avr32) CROSS_COMPILE = avr32- endif endif endif export CROSS_COMPILE # load other configuration include $(TOPDIR)/config.mk

    ifeq ($(OBJTREE)/include/config.mk,$(wildcard $(OBJTREE)/include/config.mk)): 先解释下wildcard,wildcard会匹配目录下的文件,匹配方式由后面的参数部分指定。如果没有匹配到相应的文件则返回空,否则返回匹配的文件,中间用空格隔开。因此,这一句的解释就是检查是否有OBJTREE)/include/config.mk这个文件。 记住这个ifeq,它很长,长到all目标那边 include $(OBJTREE)/include/config.mk: 包含config.mk文件,这个文件的内容其实就是目标板子的一些信息,如下所示:

    ARCH = arm CPU = arm920t BOARD = smdk2410 SOC = s3c24x0

    注:config.mk文件是在执行make xxx_config时调用mkconfig脚本生成的。后面这四个变量以上述信息为例 export ARCH CPU BOARD VENDOR SOC: 导出到子Makefile中。 随后出现的十几行代码,都是在确定CROSS_COMPILE编译器类型,并且导出到子Makefile中。 include $(TOPDIR)/config.mk: 加载顶层目录下的config.mk文件,顶层目录下的config.mk根据ARCH、CPU、BOARD、SOC这四个变量确定了编译器和编译选项等信息。

    ######################################################################### # U-Boot objects....order is important (i.e. start must be first) OBJS = cpu/$(CPU)/start.o ifeq ($(CPU),i386) OBJS += cpu/$(CPU)/start16.o OBJS += cpu/$(CPU)/reset.o endif ifeq ($(CPU),ppc4xx) OBJS += cpu/$(CPU)/resetvec.o endif ifeq ($(CPU),mpc83xx) OBJS += cpu/$(CPU)/resetvec.o endif ifeq ($(CPU),mpc85xx) OBJS += cpu/$(CPU)/resetvec.o endif ifeq ($(CPU),mpc86xx) OBJS += cpu/$(CPU)/resetvec.o endif ifeq ($(CPU),bf533) OBJS += cpu/$(CPU)/start1.o cpu/$(CPU)/interrupt.o cpu/$(CPU)/cache.o OBJS += cpu/$(CPU)/cplbhdlr.o cpu/$(CPU)/cplbmgr.o cpu/$(CPU)/flush.o endif OBJS := $(addprefix $(obj),$(OBJS)) LIBS = lib_generic/libgeneric.a LIBS += board/$(BOARDDIR)/lib$(BOARD).a LIBS += cpu/$(CPU)/lib$(CPU).a ifdef SOC LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).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 LIBS += net/libnet.a LIBS += disk/libdisk.a LIBS += rtc/librtc.a LIBS += dtt/libdtt.a LIBS += drivers/libdrivers.a LIBS += drivers/nand/libnand.a LIBS += drivers/nand_legacy/libnand_legacy.a LIBS += drivers/sk98lin/libsk98lin.a LIBS += post/libpost.a post/cpu/libcpu.a LIBS += common/libcommon.a LIBS += $(BOARDLIBS) LIBS := $(addprefix $(obj),$(LIBS)) .PHONY : $(LIBS)

    OBJS = cpu/$(CPU)/start.o: OBJS = cpu/arm920t/start.o。 再根据CPU的类型往OBJS变量添加一些.o文件,这里CPU的类型为arm920t,所以此时的OBJS变量值为cpu/arm920t/start.o。 OBJS := $(addprefix ( o b j ) , (obj), (obj),(OBJS)): 调用文件名操作函数addprefix,在OBJS加上前缀obj,因为没有使用O变量,所以OBJS依然为cpu/arm920t/start.o。 接着,在LIBS变量中添加了相关的和通用的库文件,同样地给LIBS加上前缀obj。

    # Add GCC lib PLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc

    -L加库路径(使用dirname变量获取编译器和各种参数的所在的路径),使用-lgcc 代表链接器将连接GCC的支持库libgcc.a。看解释说Add GCC lib,所以PLATFORM_LIBS 应该是作为一个参数的存在。

    # The "tools" are needed early, so put this first # Don't include stuff already done in $(LIBS) SUBDIRS = tools \ examples \ post \ post/cpu .PHONY : $(SUBDIRS) ifeq ($(CONFIG_NAND_U_BOOT),y) NAND_SPL = nand_spl U_BOOT_NAND = $(obj)u-boot-nand.bin endif __OBJS := $(subst $(obj),,$(OBJS)) __LIBS := $(subst $(obj),,$(LIBS))

    SUBDIRS : tools examples post post/cpu。 ifeq ($(CONFIG_NAND_U_BOOT),y): 如果CONFIG_NAND_U_BOOT等于y,应该是在xxx_config文件里或者其他makefile中有这样定义:CONFIG_NAND_U_BOOT+=y。 NAND_SPL = nand_spl: 则NAND_SPL =nand_spl U_BOOT_NAND = $(obj)u-boot-nand.bin: U_BOOT_NAND = u-boot-nand.bin。 endif __OBJS := $(subst ( o b j ) , , (obj),, (obj),,(OBJS)): __OBJS = 去掉obj前缀的OBJS。 __LIBS := $(subst ( o b j ) , , (obj),, (obj),,(LIBS)): __LIBS = 去掉obj前缀的LIBS。

    ######################################################################### ######################################################################### ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND) 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.img: $(obj)u-boot.bin ./tools/mkimage -A $(ARCH) -T firmware -C none \ -a $(TEXT_BASE) -e 0 \ -n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \ sed -e 's/"[ ]*$$/ for $(BOARD) board"/') \ -d $< $@ $(obj)u-boot.dis: $(obj)u-boot $(OBJDUMP) -d $< > $@ $(obj)u-boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT) UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__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

    先说明下u-boot.srec为S-Record格式的image;     u-boot.bin为原始二进制文件的image;     System.map按链接地址由小到大的顺序列出了所有符号,可以称之为系统映射表;     U_BOOT_NAND = u-boot-nand.bin,应该是烧录到nand flash的二进制格式的image;因此, ALL: = u-boot.srec u-boot.bin System.map (u-boot-nand.bin如果CONFIG_NAND_U_BOOT不为y就没有) all: $(ALL):这里就是编译uboot的目标all了,all目标没有相应命令,所以要实现的就是相关的依赖文件,也就是ALL了。

      未完待续。。。

    Processed: 0.011, SQL: 9