zoukankan      html  css  js  c++  java
  • uboot 顶层makefile细节分析

        uboot的源文件众多,学习庞然大物首先找到脊椎--顶层的makfile,逐一破解。但是,uboot的makefile同样是一个庞然大物,所以也要找到它的主线。倘若过分专注部分细节,很难做到把握全局,实际上也不可能很好理解细节。

        介于此,笔者已经写了一篇uboot makefile整体解析,可以先从主体上把握makefile。然后,再读这篇makefile强大功能实现的细节,才能做到循序渐进。

        说明:uboot顶层makefile的注释机会全部源码都搬上来了,而注释都是黑体加粗以与源码有强烈的区别。

    VERSION = 1            //主版本号

    PATCHLEVEL = 1       //次级版本号

    SUBLEVEL = 6

    EXTRAVERSION =     //版本号扩展

    U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)   //这个Uboot的版本为1.1.6

    VERSION_FILE = $(obj)include/version_autogenerated.h

    //生成uboot的版本信息

    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/)

    //获取主机架构,笔者电脑是酷睿双核,#uname –m输出结构是i686,执行替换命令sed -e s/i.86/i386/后,就变成了i386,

    //并且将这个值保存在HOSTARCH变量中。

    HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' |

               sed -e 's/(cygwin).*/cygwin/')

    //获取主机操作系统,执行#uname -s可以查看自己的操作系统类型,笔者使用的操作系统是Linux。tr '[:upper:]' '[:lower:]'

    //具有大写转小写的功能,所以最终HOSTOS的值为linux。

    export     HOSTARCH HOSTOS

    //将两个变量导出,可以供嵌套的makefile调用

    # Deal with colliding definitions from tcsh etc.

    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.

    //上边的注释讲的是uboot支持编译时,将目标文件生成在其他的目录中,这样可以保持源文件的干净,另外从目标

    //文件的生成目录中查看生成的文件时一目了然。如果想要这样做,提供了两种方法。第一种方法是执行命令

    //#make O=/tmp/build all;第二种方法,可以先在命令行模式定义环境变量export BUILD_DIR=/tmp/build',

    //然后再执行make就行了。通常懒惰的编译方式就是让生成的目标文件和源文件混在一起,那么BUILD_DIR就会没定义。

    ifdef O

    ifeq ("$(origin O)", "command line")

    BUILD_DIR := $(O)

    endif

    endif

    //这是第一种方法的实现过程,$(origin O)是输出变量O的定义来源,假设在命令行模式输//入#make O=/tmp/build all

    //来编译,那么O的定义来源是命令行,函数的输出是command line。

    ifneq ($(BUILD_DIR),)

    //倘若BUILD_DIR定义过了,也就是不希望目标文件与源文件混在一起,那么直到“endif # //ifneq ($(BUILD_DIR),)”

    //的内容都有效;倘若没有定义BUILD_DIR,那么这部分代码将不起作用。

    saved-output := $(BUILD_DIR)

    // 将BUILD_DIR保存在saved-output变量中

    # Attempt to create a output directory.

    $(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})

    //-d是判断BUILD_DIR是否存在,倘若不存在就就创建。mkdir的-p参数代表若路径中的

    //某些目录不存在,也一并创建

    # Verify if it was successful.

    //核查BUILD_DIR是否已经创建

    BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)

    //试图进入$(BUILD_DIR),倘若能进入,则将它的路径赋给BUILD_DIR,注意这时的BUILD_DIR已经是一个真实存在

    //目录的代言人,而之前的只是希望创建的目录。需要说明的是倘若BUILD_DIR还没有创建,那么cd $(BUILD_DIR)将执

    //行错误,返回值是空,虽然这时发生错误,但是编译会忽略这个错误还能继续进行

    $(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))

    // 如果没有创建成功,就执行error函数,输出信息output directory "$(saved-output)" does not exist),然后编译终止

    endif # ifneq ($(BUILD_DIR),)

    OBJTREE             := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))

    //如果BUILD_DIR不为空,目标目录就等于BUILD_DIR;倘若没定义,就取为当前目录

    SRCTREE   := $(CURDIR)//源文件目录等于当前文件夹

    TOPDIR         := $(SRCTREE)//顶层目录等于源文件目录

    LNDIR           := $(OBJTREE)  //连接目录等于BUILD_DIR

    export     TOPDIR SRCTREE OBJTREE//将这三个变量导出

    MKCONFIG   := $(SRCTREE)/mkconfig//指定mkconfig的位置

    export MKCONFIG //将MKCONFIG变量导出

    ifneq ($(OBJTREE),$(SRCTREE)) //如果目标目录和源文件目录不相等

    REMOTE_BUILD        := 1  //就定义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.

    //obj和src的定义也出现在了顶层目录的config.mk中,但是config.mk中的定义由于受下边//红色加粗判断语句的影响,只有

    //在“make *_config”执行后($(OBJTREE)/include/config.mk就会存在),再执行的make程序才能将config.mk包含进顶

    //层makefile中。而在没有先执行“make *_config”或者$(OBJTREE)/include/config.mk不存在的情况下,如果想执行unconfig

    //, clean, clobber, distclean,而这些命令用到了变量obj、src,所以这里提前包含进去。

    //但是我也有疑惑,为什么不能将顶层目录的config.mk包含在全局中,设计者为什么要把它放在条件执行里边。

    ifneq ($(OBJTREE),$(SRCTREE))

    obj := $(OBJTREE)/

    src := $(SRCTREE)/

    else

    obj :=

    src :=

    endif

    export obj src

    //定义变量obj和src,并将这两个变量导出,obj是编译目标文件的前缀,从而实现生成的目标文件在于源文件相区别的目录中

    #########################################################################

    ifeq ($(OBJTREE)/include/config.mk,$(wildcard $(OBJTREE)/include/config.mk))

    //这个ifeq横跨的范围非常广,用红色加粗字体表明。只有$(OBJTREE)/include/config.mk存在(也就是说已经

    //执行了make *.config)的情况下,这部分包含的内容才有效

    # load ARCH, BOARD, and CPU configuration

    include $(OBJTREE)/include/config.mk   //将make *_config生成的config.mk文件包含进来

    export     ARCH CPU BOARD VENDOR SOC //导出5个变量以供其他子目录的makefile调用

    ifndef CROSS_COMPILE

    ifeq ($(HOSTARCH),ppc)

    CROSS_COMPILE =

    else

    ifeq ($(ARCH),ppc)

    CROSS_COMPILE = powerpc-linux-

    endif

    ifeq ($(ARCH),arm)

    CROSS_COMPILE = arm-linux- //根据变量ARCH可以使这行满足条件,确定了交叉编译使//用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 //将顶层的config.mk也包含进来

    #########################################################################

    # 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)

    //将带生成的库文件作为伪目标来处理

    # Add GCC lib

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

    //添加系统标准库

    # 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)

    //定义make执行要首先处理的目录

    ifeq ($(CONFIG_NAND_U_BOOT),y)

    NAND_SPL = nand_spl

    U_BOOT_NAND = $(obj)u-boot-nand.bin

    endif

    //假若定义了CONFIG_NAND_U_BOOT,那么将总目标all中添加$(obj)u-boot-nand.bin,否则不添加

    __OBJS := $(subst $(obj),,$(OBJS))

    __LIBS := $(subst $(obj),,$(LIBS))

    //OBJS、LIBS除去$(obj)部分的路径

    #########################################################################

    ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)

    //总目标的构成,也可以人为添加其他的目标

    all:          $(ALL)

    //定义总目标all

    $(obj)u-boot.hex:   $(obj)u-boot

                  $(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@

    //生成16进制的可执行程序

    $(obj)u-boot.srec:   $(obj)u-boot

                  $(OBJCOPY) ${OBJCFLAGS} -O srec $< $@

    $(obj)u-boot.bin:    $(obj)u-boot

                  $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@

    //生成2进制的可执行程序

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

    //生成反汇编文件u-boot.dis

    $(obj)u-boot:         depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)

                  UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e 's/.*(__u_boot_cmd_.*)/-u1/p'|sort|uniq`;

                  cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS)

                         --start-group $(__LIBS) --end-group $(PLATFORM_LIBS)

                         -Map u-boot.map -o u-boot

    //生成elf格式的u-boot文件

    $(OBJS):

                  $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))

    //中间目标文件生成

    $(LIBS):

                  $(MAKE) -C $(dir $(subst $(obj),,$@))

    //中间库文件生成

    $(SUBDIRS):

                  $(MAKE) -C $@ all

    //SUBDIRS目录处理

    $(NAND_SPL):     version

                  $(MAKE) -C nand_spl/board/$(BOARDDIR) all

    $(U_BOOT_NAND):     $(NAND_SPL) $(obj)u-boot.bin

                  cat $(obj)nand_spl/u-boot-spl-16k.bin $(obj)u-boot.bin > $(obj)u-boot-nand.bin

    version:

                  @echo -n "#define U_BOOT_VERSION "U-Boot " > $(VERSION_FILE);

                  echo -n "$(U_BOOT_VERSION)" >> $(VERSION_FILE);

                  echo -n $(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion

                          $(TOPDIR)) >> $(VERSION_FILE);

                  echo """ >> $(VERSION_FILE)

    //版本头文件生成

    gdbtools:

                  $(MAKE) -C tools/gdb all || exit 1

    updater:

                  $(MAKE) -C tools/updater all || exit 1

    env:

                  $(MAKE) -C tools/env all || exit 1

    depend dep:

                  for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done

    //执行make depend或者make dep都能触发命令

    tags ctags:

                  ctags -w -o $(OBJTREE)/ctags `find $(SUBDIRS) include

                                lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH)

                                fs/cramfs fs/fat fs/fdos fs/jffs2

                                net disk rtc dtt drivers drivers/sk98lin common

                         ( -name CVS -prune ) -o ( -name '*.[ch]' -print )`

    etags:

                  etags -a -o $(OBJTREE)/etags `find $(SUBDIRS) include

                                lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH)

                                fs/cramfs fs/fat fs/fdos fs/jffs2

                                net disk rtc dtt drivers drivers/sk98lin common

                         ( -name CVS -prune ) -o ( -name '*.[ch]' -print )`

    $(obj)System.map: $(obj)u-boot

                  @$(NM) $< |

                  grep -v '(compiled)|(.o$$)|( [aUw] )|(..ng$$)|(LASH[RL]DI)' |

                  sort > $(obj)System.map

    //生成map文件

    #########################################################################

    else

    all $(obj)u-boot.hex $(obj)u-boot.srec $(obj)u-boot.bin

    $(obj)u-boot.img $(obj)u-boot.dis $(obj)u-boot

    $(SUBDIRS) version gdbtools updater env depend

    dep tags ctags etags $(obj)System.map:

           @echo "System not configured - see README" >&2

           @ exit 1

    endif

    //倘若没有include $(OBJTREE)/include/config.mk 文件,将打印出错误信息“System not configured

    //- see README”,并且make程序也结束

    .PHONY : CHANGELOG

    CHANGELOG:

           git log --no-merges U-Boot-1_1_5.. |

           unexpand -a | sed -e 's/ss*$$//' > $@

    #########################################################################

    unconfig:

           @rm -f $(obj)include/config.h $(obj)include/config.mk

                  $(obj)board/*/config.tmp $(obj)board/*/*/config.tmp

    //删除配置文件,即与make *_config过程相反

    //剩下的*_config,属于相似内容,只举一个常见的smdk2410_config,以作说明

    smdk2410_config : unconfig
    @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0

    //执行make smdk2410_config,就会调用顶层目录中的mkconfig(shell脚本),同时给这个脚本传入参数

    //arm arm920t smdk2410 NULL s3c24x0。mkconfig脚本根据这些传入的参数生成与开发板相适应的一系列配置文件。

    #########################################################################

    //不要忘记makfile的最后边,还有一部分很重要的内容

    clean: //clean删除
    find $(OBJTREE) -type f
    ( -name 'core' -o -name '*.bak' -o -name '*~'
    -o -name '*.o' -o -name '*.a' ) -print
    | xargs rm -f
    rm -f $(obj)examples/hello_world $(obj)examples/timer
    $(obj)examples/eepro100_eeprom $(obj)examples/sched
    $(obj)examples/mem_to_mem_idma2intr $(obj)examples/82559_eeprom
    $(obj)examples/smc91111_eeprom $(obj)examples/interrupt
    $(obj)examples/test_burst
    rm -f $(obj)tools/img2srec $(obj)tools/mkimage $(obj)tools/envcrc
    $(obj)tools/gen_eth_addr
    rm -f $(obj)tools/mpc86x_clk $(obj)tools/ncb
    rm -f $(obj)tools/easylogo/easylogo $(obj)tools/bmp_logo
    rm -f $(obj)tools/gdb/astest $(obj)tools/gdb/gdbcont $(obj)tools/gdb/gdbsend
    rm -f $(obj)tools/env/fw_printenv $(obj)tools/env/fw_setenv
    rm -f $(obj)board/cray/L1/bootscript.c $(obj)board/cray/L1/bootscript.image
    rm -f $(obj)board/netstar/eeprom $(obj)board/netstar/crcek $(obj)board/netstar/crcit
    rm -f $(obj)board/netstar/*.srec $(obj)board/netstar/*.bin
    rm -f $(obj)board/trab/trab_fkt $(obj)board/voiceblue/eeprom
    rm -f $(obj)board/integratorap/u-boot.lds $(obj)board/integratorcp/u-boot.lds
    rm -f $(obj)include/bmp_logo.h
    rm -f $(obj)nand_spl/u-boot-spl $(obj)nand_spl/u-boot-spl.map

    clobber: clean //除了调用clean删除,还要再执行额外的删除命令
    find $(OBJTREE) -type f ( -name .depend
    -o -name '*.srec' -o -name '*.bin' -o -name u-boot.img )
    -print0
    | xargs -0 rm -f
    rm -f $(OBJS) $(obj)*.bak $(obj)ctags $(obj)etags $(obj)TAGS $(obj)include/version_autogenerated.h
    rm -fr $(obj)*.*~
    rm -f $(obj)u-boot $(obj)u-boot.map $(obj)u-boot.hex $(ALL)
    rm -f $(obj)tools/crc32.c $(obj)tools/environment.c $(obj)tools/env/crc32.c
    rm -f $(obj)tools/inca-swap-bytes $(obj)cpu/mpc824x/bedbug_603e.c
    rm -f $(obj)include/asm/proc $(obj)include/asm/arch $(obj)include/asm
    [ ! -d $(OBJTREE)/nand_spl ] || find $(obj)nand_spl -lname "*" -print | xargs rm -f

    ifeq ($(OBJTREE),$(SRCTREE))
    mrproper
    distclean: clobber unconfig //倘若目标目录与源目录相同,distclean调用clobber unconfig来删除
    else
    mrproper
    distclean: clobber unconfig //倘若目标目录与源目录不相同,distclean调用clobber unconfig来删除
    rm -rf $(OBJTREE)/*         //而且,还要删除OBJTREE目录下的所有内容
    endif

    backup: //打包备份
    F=`basename $(TOPDIR)` ; cd .. ;                         //跳到当前目录的外边,然后打包整个文件夹
    gtar --force-local -zcvf `date "+$ $F-%Y-%m-%d-%T.tar.gz"` $ $F //打包,名字中插入日期

    #########################################################################

    //最后,插入编译过程中重要的打印信息(要深入理解u-boot的makefile工作原理,必须做实际的实验,实际的make一下,

    //看看你正想了解的地方发生了什么)

    start.o

    libsmdk2410.a

    libarm920t.a

    all目标的实现

    参考博客:U-Boot Makefile分析

  • 相关阅读:
    2019 滴滴java面试笔试总结 (含面试题解析)
    2019 阿里java面试笔试总结 (含面试题解析)
    Mangos笔记
    Google 多源码管理工具 gclient
    构建系统介绍
    Ninja介绍
    小试 Ninja
    如何使用Ninja快速编译LLVM和Clang
    FMS4中的P2P功能
    Flex之HTML5视频播放解决方案
  • 原文地址:https://www.cnblogs.com/amanlikethis/p/3427682.html
Copyright © 2011-2022 走看看