U-Boot移植(二)——U-Boot编译过程分析(3)

时间:2022-07-24 16:35:12
在上一篇文章U-Boot编译过程分析(2)中,已经分析了“make borad_name_config”的作用,现在就分析下Makefile剩下的一些代码。


     24 VERSION = 2010
     25 PATCHLEVEL = 06
     26 SUBLEVEL =
     27 EXTRAVERSION =
     28 ifneq "$(SUBLEVEL)" ""
     29 U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
     30 else
     31 U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL)$(EXTRAVERSION)
     32 endif
这部分不用多说,定义了U-B00T的版本号2010.06。
     33 TIMESTAMP_FILE = $(obj)include/timestamp_autogenerated.h
     34 VERSION_FILE = $(obj)include/version_autogenerated.h
     35 
因为obj为空,所以定义TIMESTAMP_FILE为include/timestamp_autogenerated.h,定义VERSION_FILE为include/version_autogenerated.h

     36 HOSTARCH := $(shell uname -m | \
     37         sed -e s/i.86/i386/ \
     38             -e s/sun4u/sparc64/ \
     39             -e s/arm.*/arm/ \
     40             -e s/sa110/arm/ \
     41             -e s/ppc64/powerpc/ \
     42             -e s/ppc/powerpc/ \
     43             -e s/macppc/powerpc/)
     44 
     45 HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
     46             sed -e 's/\(cygwin\).*/cygwin/')
     47 

简化主机架构名称(HOSTARCH)和主机操作系统名称(HOSTOS)。通过shell中uname命令分别获取主机构架和系统名称,再通过sed命令进行替换。tr '[:upper:]' '[:lower:]' 命令完成大小写替换。由于我们一般都在个人电脑上进行开发和移植工作,所以HOSTARCH = i386,HOSTOS = linux。

     59 # Allow for silent builds
     60 ifeq (,$(findstring s,$(MAKEFLAGS)))
     61 XECHO = echo
     62 else
     63 XECHO = :
     64 endif
由于MAKEFLAGS为空找不到s,条件成立XECH0 = echo

     88 ifdef O
     89 ifeq ("$(origin O)", "command line")
     90 BUILD_DIR := $(O)
     91 endif
     92 endif
     93 
     94 ifneq ($(BUILD_DIR),)
     95 saved-output := $(BUILD_DIR)
     96 
     97 # Attempt to create a output directory.
     98 $(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})
     99 
    100 # Verify if it was successful.
    101 BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)
    102 $(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))
    103 endif # ifneq ($(BUILD_DIR),)
    104 

这部分代码主要就是指定编译链接时目标文件的输出目录。

89-91:origin函数用于查询变量的出处,此处期望他在命令行中定义。如果是在命令行中定义,那么编译输出目录就是命令行指定目录。

94-102:如果BUILD_DIR不为空,则进行存储,然后尝试建立目录。

其中有两种方式可以指定输出目录(在这部分代码有一段注释说明,教详细的说明了):

     71 # 1) Add O= to the make command line
     72 # 'make O=/tmp/build all'
     73 #
     74 # 2) Set environement variable BUILD_DIR to point to the desired location
     75 # 'export BUILD_DIR=/tmp/build'
     76 # 'make'
1)通过make命令参数 0直接指定输出目录

2)通过设定环境变量获得。

如果在编译时没有通过上述两种方式指定编译输出目录,则使用环境变量(即当前目录)。

    105 OBJTREE         := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
    106 SRCTREE         := $(CURDIR)
    107 TOPDIR          := $(SRCTREE)
    108 LNDIR           := $(OBJTREE)
    109 export  TOPDIR SRCTREE OBJTREE
105:if判断是一个三目函数,当BUILD_DIR存在时则将它赋值给OBJTREE(目标目录),否则使用当前环境变量
106-108:分别指定原目录、顶层目录和链接目录。

109:输出到环境变量(全局变量),留做他用。

    111 MKCONFIG        := $(SRCTREE)/mkconfig
    112 export MKCONFIG

这句脚本的重要性,已经在前面提到了,它是“make board_name_config”命令中直接用到的变量,指定了配置执行文件mkconfig的目录。

    114 ifneq ($(OBJTREE),$(SRCTREE))
    115 REMOTE_BUILD    := 1
    116 export REMOTE_BUILD
    117 endif
通过目标目录和原始目录确定是否执行远程编译,这个在上一篇 U-Boot编译过程分析(2)第二部份”创建架构相关的头文件的链接“中也提到了。

到这里建立外部目录部份就结束了。

代码119-132行代码的注释已经很清楚了,就不再赘述。对CDPATH不了解的,可以参考CDPATH学习

    136 # The "tools" are needed early, so put this first
    137 # Don't include stuff already done in $(LIBS)
    138 SUBDIRS = tools \
    139           examples/standalone \
    140           examples/api
    141 
    142 .PHONY : $(SUBDIRS)
伪目标SUBDIRS: 在后面编译中执行tools ,examples ,post,post/cpu 子目录下面的make文件。

    144 ifeq ($(obj)include/config.mk,$(wildcard $(obj)include/config.mk))
        .
        .
        .
        .
    450 else    # !config.mk
    451 all $(obj)u-boot.hex $(obj)u-boot.srec $(obj)u-boot.bin \
    452 $(obj)u-boot.img $(obj)u-boot.dis $(obj)u-boot \
    453 $(filter-out tools,$(SUBDIRS)) $(TIMESTAMP_FILE) $(VERSION_FILE) gdbtools \
    454 updater env depend dep tags ctags etags cscope $(obj)System.map:
    455         @echo "System not configured - see README" >&2
    456         @ exit 1
    457 
    458 tools:
    459         $(MAKE) -C tools
    460 tools-all:
    461         $(MAKE) -C tools HOST_TOOLS_ALL=y
    462 endif   # config.mk
判断是否存在include/config.mk文件,从上篇文章可知,该文件是在make board_name_config命令时执行的(见 U-Boot编译过程分析(2第3部份)。

假设不存在config.mk文件,而是直接执行make all命令,进入else分支,输出错误信息并返回。

再看看正常执行”make all“命令

    149 all:
    150 sinclude $(obj)include/autoconf.mk.dep
    151 sinclude $(obj)include/autoconf.mk

include/autoconf.mk文件中是与开发板相关的一些宏定义,make all需要通过一些宏确定执行那写操作。其依赖make <board_name>_config命令生成的include/config.h.执行make <board_name>_config后再执行make all命令就更新了include/atuoconf.mk。生成规则如下(448以后就是make <board_name>_config命令相关配置,,从3688到最后是一些清理工作相关脚步如clean命令):

    428 #
    429 # Auto-generate the autoconf.mk file (which is included by all makefiles)
    430 #
    431 # This target actually generates 2 files; autoconf.mk and autoconf.mk.dep.
    432 # the dep file is only include in this top level makefile to determine when
    433 # to regenerate the autoconf.mk file.
    434 $(obj)include/autoconf.mk.dep: $(obj)include/config.h include/common.h
    435         @$(XECHO) Generating $@ ; \
    436         set -e ; \
    437         : Generate the dependancies ; \
    438         $(CC) -x c -DDO_DEPS_ONLY -M $(HOSTCFLAGS) $(CPPFLAGS) \
    439                 -MQ $(obj)include/autoconf.mk include/common.h > $@
    440 
    441 $(obj)include/autoconf.mk: $(obj)include/config.h
    442         @$(XECHO) Generating $@ ; \
    443         set -e ; \
    444         : Extract the config macros ; \
    445         $(CPP) $(CFLAGS) -DDO_DEPS_ONLY -dM include/common.h | \
    446                 sed -n -f tools/scripts/define2mk.sed > $@.tmp && \
    447         mv $@.tmp $@
    448
编译选项“-dM”的作用是输出include/common.h中定义的所有宏。根据上面的规则,编译器提取include/common.h中定义的宏,然后输出给tools/scripts/define2mk.sed脚本处理,处理的结果就是include/autoconf.mk文件。其中tools/scripts/define2mk.sed脚本的主要完成了在include/common.h中查找和处理以“CONFIG_”开头的宏定义的功能。
include/common.h文件包含了include/config.h文件,而include/config.h文件又包含了config_defaults.h,configs/<board_name>.h,asm/config.h文件。因此include/autoconf.mk实质上就是config_defaults.h,configs/<board_name>.h,asm/config.h三个文件中“CONFIG_”开头的有效的宏定义的集合。

将common.h中的所有#include 的h文件并且h文件中的include文件 都放到autoconf.mk.dep中。

将common.h中的所有的#define 变量,和h文件中的#define变量,都放到autoconf.mk中。。

    153 # load ARCH, BOARD, and CPU configuration
    154 include $(obj)include/config.mk
    155 export  ARCH CPU BOARD VENDOR SOC
将make <bord_name>_config命令生成的include/config.mk包含进来。
    157 # set default to nothing for native builds
    158 ifeq ($(HOSTARCH),$(ARCH))
    159 CROSS_COMPILE ?=
    160 endif
  若主机与目标机器体系架构相同,则使用gcc编译器而不是交叉编译器。
    162 # load other configuration
    163 include $(TOPDIR)/config.mk

最后将U-Boot顶层目录下的config.mk文件包含进来,该文件包含了对编译的一些设置。对U-Boot顶层目录下的config.mk文件分析见U-Boot编译过程分析(4)     

    166 # U-Boot objects....order is important (i.e. start must be first)
    167 
    168 OBJS  = $(CPUDIR)/start.o
     . 
     .
    180 OBJS := $(addprefix $(obj),$(OBJS))
    181 
    182 LIBS  = lib/libgeneric.a
     .
     . 
    247 LIBS := $(addprefix $(obj),$(LIBS))
    248 .PHONY : $(LIBS) $(TIMESTAMP_FILE) $(VERSION_FILE)
    249 
    250 LIBBOARD = board/$(BOARDDIR)/lib$(BOARD).a
    251 LIBBOARD := $(addprefix $(obj),$(LIBBOARD))
    252 
    253 # Add GCC lib
     .
     .
    264 export PLATFORM_LIBS
   LIBS变量指明了U-Boot需要的库文件,包括平台/开发板相关的目录、通用目录下相应的库,编译器的库,都通过相应的子目录编译得到的。
  OBJS := $(addprefix $(obj),$(OBJS))  #addprefix是将 obj放到OBJS的前面,其实就是将所有的文件 加上 路径名 。


Makefile文件还有的最后一部分从266行到426行就是具体的编译规则了。

到这里Makefile文件就分析结束了,里面可能有很多解释不到位或者错误的地方,希望大家指正。