zoukankan      html  css  js  c++  java
  • u-boot Makefile整体解析

    一、概述   

    1、理解u-boot的makefile需要的准备

      linux常用命令、shell脚本基础知识、makefile脚本基础知识

    2、Makefile的元素

        万变不离其宗,无论工程多么复杂,文件多么庞大,其实源于最简单的makefile。Makefile典型的规则如下。 

    目标:依赖1,依赖2••••••
    
           命令
    
    

         举一个简单的例子

    nand.bin : head.o nand.o main.o
           arm-linux-ld -Tnand.lds -o nand_elf  head.o main.o
           arm-linux-objcopy -O binary -S nand_elf nand.bin
    
    head.
    o:head.S arm-linux-gcc -Wall -c -o head.o head.S
    nand.
    o:nand.c s3c2440_addr.h arm-linux-gcc -Wall -c -o nand.o nand.c
    main.
    o:main.c s3c2440_addr.h arm-linux-gcc -Wall -c -o main.o main.c
    clean:
    rm -f nand.bin nand_elf head.o nand.o main.o

         形象的表达:待实现一个产品(目标),这个目的需要确定的原材料才能实现(依赖),还需要一套加工手段来制作(编译规则)。

          当最初的makefile设计完成后,为了实现可裁剪、自动化编译等目的,不得不加入更多的makefile规则,当然make工具相应的功能也需要增加。可以肯定:这些增加的规则无非是更加方便、灵活的生成指定目标,更加方便的生成依赖关系,更加方便的生成编译规则。 

    举例:
    
        更加方便灵活的的生成指定目标:通过选项指定生成目标的路径
    
        更加方便的生成依赖:自动生成依赖文件、通过变量选择编译的源码
    
        更加方便的生成指定规则:隐含规则、模式规则、通过变量选择使用的编译器及选项
    
    

    3、u-boot Makefile体系的组成    

       Uboot是一个庞大的工程,需要从众多的源文件中选择部分源文件,采用合适的编译手段,生成特定的目标文件。这不是一个简单的任务,需要顶层的makefile,众多的顶层makefile,makefile的include文件(config.mk、rules.mk)等来完成这个工作。

    u-bootMakefile 体系
    名    称 描    述
    顶层Makefile 从总体上控制着u-boot的编译、连接,定义总目标u-boot.bin
    顶层config.mk 规定了编译的规则,被所有Makefile所调用
    顶层rules.mk 生成依赖关系,被各级子目录Makefile所调用
    各级子目录Makefile 决定当前目录的编译、连接
    顶层mkconfig 在编译之前运行,为编译做准备

      mkconfig(shel l脚本)是在编译之前做一些准备,笔者认为可以不算做Makefile集体中的一部分。

    u-boot编译过程中生成的与编译相关的文件
    名称 描述
    include/config.mk mkconfig所生成,被顶层Makefile所包含,定义ARCH、CPU等全局变量
    各级子目录.depend 各级子目录makefile所生成,并被各级子目录makefile所包含,定义依赖关系

     二、Makefile的目标

    1、Makefile的总目标

            顶层Makefile的总目标all是一个伪目标,有几个分目标(目标文件)组成:$(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)。其实,用户也可以添加其他目标,如$(obj)u-boot.dis、$(obj)u-boot.img、$(obj)u-boot.hex等等。由于是一个伪目标,所以只要输入命令make all,总会重建几个分目标。 

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

    2、Makefile的各级子目录中的目标

      子目录中的all定义了当前目录所有要生成的目标,LIB定义了当前目录要生成的静态库,OBJS定义了当前目录由“.c”文件编译生成的目标“.o”,SOBJS定义了当前目录由“.S”文件编译生成的目标“.o”。

    all:    $(obj).depend $(START) $(LIB)
    $(LIB): $(obj).depend $(OBJS) $(SOBJS)

     三、Makefile的依赖

    1、总目标的依赖

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

      总目标的依赖是由“depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)”构成,重要的元素是“$(OBJS) $(LIBS)”。下面,我们看看“$(OBJS) $(LIBS)”,它们又是什么构成的,从而查看u-boot是怎样选择参与编译连接的文件的。

          编译的过程是(*.c *.S) -- > (*.o *.a) --> (*.bin),但是make工具分析Makefile的过程是相反的。为了生成(*.bin) 需要依赖(*.o *.a),为了生成(*.o *.a)间接的又需要(*.c *.S),可以说(*.c *.S)是最初的依赖(原材料)。

         Uboot是一个通用的启动代码,可以在众多的硬件平台上例如arm、powerpc,和操作系统上运行,例如vxworks、linux等等。源码中有相应的源文件包,例如lib_ppc,lib_arm等等。其实,还有各种驱动,网卡、显示器、串口、键盘等等。但是,对于一个特定的应用目标,它的硬件平台是确定的,所用的操作系统也是确定的,它的外设也是确定的。所以,需要对这些源文件进行裁剪。

          在执行make all之前,首先要执行make *_config例如make smdk2410_config,通过执行mkconfig脚本,可以确定所用cpu的架构(ARCH)例如arm ,cpu的种类(CPU)例如arm920t,开发板(BOARD)例如smdk2410 ,soc(SOC)例如 s3c24x0,这些信息保存在mkconfig脚本生成的makefile包含文件include/config.mk中。include/config.mk会被包含进入顶层makefile中,根据这个文件所定义的变量值,从而确定用那些文件(依赖),例如lib_arm/,board/smdk2410,cpu/arm920t等等。这是一种大方向上的裁剪方式。

          至于所用外设之类的裁剪方式,不是通过makefile来实现的,而是通过C语言的预处理实现的,配置文件是include/configs/<board_name>.h,例如include/configs/smdk2410.h。倘若smdk2410开发板上有CS8900网卡,可以通过在配置文件中定义一个宏来实现对网卡驱动的调用,例如“#define CONFIG_DRIVER_CS8900       1”。

        本文主要讨论的是makefile的分析,所以就来看看makefile是怎样裁剪源文件的。 

    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
    
    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)
    View Code

           可以看到,通过调用ARCH、CPU、BOARD、SOC等变量来确定总目标依赖来的构成。

    2、各级子母录的依赖

         依赖关系对于更新了源文件重建目标文件来说是重要的。比如下边这个依赖关系,我们希望在更改了nand.c,或者更改了s3c2440_addr.h后,执行make都能重建nand.o。 

    nand.o:nand.c s3c2440_addr.h

         通常c源文件更新了,对应的目标文件需要重建,由于模式规则(%.o:%.c)的支持,是能自动实现的。如果c源程序包含的头文件如果改变了,也需要重建此c源程序的目标文件。但是,困难在于描述c源程序依赖的头文件相当困难,因为c源程序当前文件包含的头文件可能本身还包含其他的头文件,而且人工描述头文件的依赖实在是麻烦的工作。

        gcc提供了一种自动产生依赖关系的方法,例如假设想查看test.c的依赖,执行命令gcc –M test.c就OK了。

        Uboot自动生成依赖关系的方法是在每个子文件夹的makefile中调用rules.mk生成的.depend依赖关系文件。

    rules.mk的内容如下: 

    #########################################################################
    _depend: $(obj).depend
    $(obj).depend:       $(src)Makefile $(TOPDIR)/config.mk $(SRCS)
                  @rm -f $@
                  @for f in $(SRCS); do 
                         g=`basename $$f | sed -e 's/(.*).w/1.o/'`; 
                         $(CC) -M $(HOST_CFLAGS) $(CPPFLAGS) -MQ $(obj)$$g $$f >> $@ ; 
                  done
    #########################################################################

     四、makefile编译规则

        源文件(*.c *.S) -- > 目标文件、库文件(*.o *.a) -->总目标 (*.srec *.bin *.map)。

     1、 总目标的编译规则 库文件 (*.o *.a) -->总目标 (*.srec *.bin *.map)

    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_.*)/-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
    
    $(OBJS):
                  $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))
    
    $(LIBS):
                  $(MAKE) -C $(dir $(subst $(obj),,$@))
    
    $(SUBDIRS):
                  $(MAKE) -C $@ all
    
    $(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
    
    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
    View Code

     2、子目录中的编译规则   源文件(*.c *.S) -- > 目标文件、库文件(*.o *.a) 

      子目录中的编译规则是在顶层目录的config.mk中给出,如下所示。

    $(obj)%.s:      %.S
           $(CPP) $(AFLAGS) -o $@ $<
    
    $(obj)%.o:      %.S
           $(CC) $(AFLAGS) -c -o $@ $<
    
    $(obj)%.o:      %.c
           $(CC) $(CFLAGS) -c -o $@ $<

         库文件的编译规则在各子目录中的makefile中给出,举例board/smdk2410/libsmdk2410.a的生成规则。   board/smdk2410文件夹中的makefile内容如下: 

    $(LIB):   $(obj).depend $(OBJS) $(SOBJS)
           $(AR) $(ARFLAGS) $@ $(OBJS) $(SOBJS)

        注意:分析Makefile的细节,必须要分析它的包含文件config.mk,它确定了程序编译、连接的选项,以及目标文件生成的规则等等内容。 

    五、u-boot整个编译过程

    1、makefile整体解析过程

      为了生成u-boot.bin这个文件,首先要生成构成u-boot.bin的各个库文件、目标文件。为了各个库文件、目标文件就必须进入各个子目录执行其中的Makefile。由此,确定了整个编译的命令顺序

    2、makefile整体编译过程

      首先,根据各个库文件、目标文件出现的先后顺序,依次进入各个子目录编译从而生成这些目标

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

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

      然后,回到顶层目录,继续执行顶层Makefile的总目标,最后生成u-boot.bin。

    六、u-boot编译的特点

    1、编译子目录

      编译子目录的方法是进入子目录,然后执行子目录中的Makefile

    2、子目录Makefile的内容

      子目录Makefile的编译规则以及依赖是由顶层的config.mk、rules.mk构成,子目录的Makefile在使用时是用include包含进来的。

    附:uboot下载地址:ftp://ftp.denx.de/pub/u-boot/

     

     

  • 相关阅读:
    网络测量中基于Sketch方法的简单介绍
    Reading SBAR SDN flow-Based monitoring and Application Recognition
    Reading Meticulous Measurement of Control Packets in SDN
    Reading SketchVisor Robust Network Measurement for Sofeware Packet Processing
    ovs加dpdk在日志中查看更多运行细节的方法
    后缀数组
    (转载)LCA问题的Tarjan算法
    Codeforces Intel Code Challenge Final Round (Div. 1 + Div. 2, Combined) A. Checking the Calendar(水题)
    Vijos 1816统计数字(计数排序)
    卡特兰数
  • 原文地址:https://www.cnblogs.com/amanlikethis/p/3419858.html
Copyright © 2011-2022 走看看