zoukankan      html  css  js  c++  java
  • uboot主Makefile解析第二篇

    这篇主要分析Makefile文件开头部分对与一些编译环境以及编译路径等变量的初始化

    VERSION = 2010

    PATCHLEVEL = 12

    SUBLEVEL =

    EXTRAVERSION =

    ifneq "$(SUBLEVEL)"""

    U_BOOT_VERSION =$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)

    else

    U_BOOT_VERSION =$(VERSION).$(PATCHLEVEL)$(EXTRAVERSION)

    Endif

    U_BOOT_VERSION = 2010.12

    TIMESTAMP_FILE =$(obj)include/timestamp_autogenerated.h

    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/ppc64/powerpc/

                 -e s/ppc/powerpc/

                 -e s/macppc/powerpc/

                 -e s/sh.*/sh/)

    $(shell uname -m)表示执行uname –m的shell命令,他执行的结果是取出机器硬件名(这台机器是64位的,所以其得到的结果就是x86-84)。sed –e s/abc/def 的意思是寻找结果里面是否有’abc’字样,如果有,就用‘def’字样将其代替。

    所以整条语句的意思就是,取出uname –m的结果,并在其中寻找是否有i.86字样,如果有就用i386代替,是否有arm.*(*是通配符),如果有,就用arm代替……,本台机的结果是x86-64所以还保持原样,没有用其他字符替换,所以HOSTARCH=x86-64

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

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

      

    uname –s 表示取出本机操作系统内核名称,(这台机器是Linux),tr ‘[:upper:]’’[:lower:]’表示将结果里面的大写字符全部换成小写字符,sed –e‘s/Cygwin.*/Cygwin/’表示寻找结果里面是否有cygwin.*字符,如果有就用cygwin替换

    所以整句话的结果就是HOSTOS = linux(注意第一个字母L被换成了小写l)

    SHELL := $(shell if [ -x "

    BASH"];thenecho

    BASH;

          else if [-x /bin/bash];then echo /bin/bash;

         else echosh;fi;fi

           

    SHELL if[-x filename]   表示如果这个filename文件是可执行的,则为真,显然BASH变量是空的,则SHELL= /bin/bash  就是说shell命令的路径位于/bin/bash目录下

    export       HOSTARCH HOSTOS SHELL 

    表示将这些变量导出,供别的文件使用

    ifeq (,$(findstring s,$(MAKEFLAGS)))

    XECHO = echo

    else

    XECHO = :

    Endif

    If$( findstring string,text )表示从text中寻找string字样,如果找到了则返回text,如果没找到就返回空,显然MAKEFLAGS为空,所以 $(findstring s,$(MAKEFLAGS)) 返回为空,if语句为真,XECHO =echo,执行非静默编译,否则,执行静默编译(静默编译的意思就是在编译时控制台不打印任何信息)

    ifdef O

    ifeq ("$(origin O)", "commandline")

    BUILD_DIR := $(O)

    endif

    endif

    这句话表示如果在编译的命令里面加入参数O(如 make O=/tmp).,就把O后面所指定的值赋给变量BUILD_DIR,(BUILD_DIR表示uboot的编译路径)

    ifneq ($(BUILD_DIR),)

    saved-output := $(BUILD_DIR)

    如果BUILD_DIR不为空,则将BUILD_DIR的值赋给saved-output

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

    如果BUILD_DIR是一个目录名称,就把该目录创建出来

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

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

    Endif

    shell命令的意思是先进入到BUILD_DIR目录,然后调用PWD命令显示当前路径名,并把当前路径名赋给BUILD_DIR变量,If语句判断BUILD_DIR是否存在,如果还为空就显示错误(命令里面两个逗号之间表示空)

    OBJTREE            :=$(if $(BUILD_DIR),$(BUILD_DIR),$(CURIDR)) //输出目录

    SRCTREE             :=$(CURDIR) //源码目录

    TOPDIR                :=$(SRCTREE)  //顶层目录

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

    export        TOPDIR SRCTREE OBJTREE

    先看CURIDR变量,这是一个MAKEFILE的内嵌变量,代表当前路径,为了验证,可以如下做个测试:

         在/tmp/test目录下新建一个Makefile文件,在里面添加如下内容:

        all::

                   @echo $(CURDIR)

         

             然后在/tmp/test路径下执行Make命令,显示的就是/tmp/test

    如果定义了BUILD_DIR,就将BUILD_DIR赋给OBJTREE,如果没有定义,就将CURIDR赋给OBJTREE

    ……

    所以如果编译时没有定义编译路径,及没有定义BUILD_DIR,则以上所有变量都是CURDIR,即当前目录,也就是uboot的顶层目录

    MKCONFIG        :=$(SRCTREE)/mkconfig) 

    export MKCONFIG

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

    REMOTE_BUILD  := 1 

    export REMOTE_BUILD

    endif

    如果源码目录与输出目录不想等,则置位REMOTE_BUILD变量

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

    obj := $(OBJTREE)/

    src := $(SRCTREE)/

    else

    obj :=

    src :=

    endif

    export obj src

    如果源码目录和输出目录不想等,则变量obj和src被赋予相应的值,否则,两变量都为空

    SUBDIRS   =tools

              examples/standalone

             examples/api

              

    .PHONY : $(SUBDIRS)

    SUBIDRS被赋予了三个路径名称,并申明SUBIDRS是一个伪目标

    这篇主要分析跟编译和连接库文件相关的变量

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

    ……

    else   # !config.mk

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

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

    $(filter-out tools,$(SUBDIRS)) $(TIMESTAMP_FILE) $(VERSION_FILE)

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

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

             @ exit 1

    tools:

             $(MAKE) -C $@ all

    endif # config.mk

    ifeq语句判断include目录下是否存在config.mk文件(通过《uboot系列之-----uboot配置过程详细分析》一文可知,只有执行了make xxx_config之后,才存在该文件)。

    我们暂时不分析config.mk存在时的情况,因为太长了,先分析config.mk不存在的情况,即else后的语句,该语句把所有的目标都指向了一个依赖,就是打印出错信息“System not configured – see README”,然后退出。

    接下来看config.mk存在的情况:

    all:

    sinclude $(obj)include/autoconf.mk.dep

    sinclude $(obj)include/autoconf.mk

    先看目标all: 从makefile开始到现在,发现这个all是第一个目标,也就是说当我们执行make命令时,它是默认目标,但是该目标既没有依赖也没有规则,那它起到什么作用呢?接着往下看,sinclude $(obj)include/autoconf.mk.dep,他的意思是包含autoconf.mk.dep文件,进入到该文件,发现第一句是:

    include/autoconf.mk: include/common.h ,它也是一个目标,并且有很多的依赖

    再回到all的作用上来,如果没有这个all,那么当我们执行make 时,根据make法则,(它将第一个目标作为他的默认目标),也就是将autoconf.mk作为默认目标,显然这不是我们想要的结果,(我们执行make是想编译整个文件,也就是执行第365行的all: $(ALL)这个规则,而在这里加上一个看起来“毫无意义”的all,就能达到我们想要的目的,即执行第一个空的all目标后,会跳过autoconf.mk目标,执行下一个all目标)

    再来看sinclude ,sinclude也是包含文件的关键词,他与include的区别就是使用sinclude时,即使所需要包含的文件不存在,也不影响makefile的往下执行。

    而autoconf.mk.dep和autoconf.mk这两个文件是怎么来的呢,在makefile中有如下一段语句,说明了这两个文件的来源:

    $(obj)include/autoconf.mk.dep: $(obj)include/config.h include/common.h

             @$(XECHO) Generating $@ ;

             set -e ;

             : Generate the dependancies ;

             $(CC) -x c -DDO_DEPS_ONLY -M $(HOSTCFLAGS) $(CPPFLAGS)

                       -MQ $(obj)include/autoconf.mk include/common.h > $@

    $(obj)include/autoconf.mk: $(obj)include/config.h

             @$(XECHO) Generating $@ ;

             set -e ;

             : Extract the config macros ;

             $(CPP) $(CFLAGS) -DDO_DEPS_ONLY -dM include/common.h |

                       sed -n -f tools/scripts/define2mk.sed > $@.tmp &&

             mv $@.tmp $@

    这段规则没有完全看明白,望大侠们帮我分析一下

    include $(obj)include/config.mk

    export        ARCH CPU BOARD VENDOR SOC

    包含配置过程中生成的config.mk文件,并把该文件中的变量导出来

    ifeq ($(ARCH),arm)

    CROSS_COMPILE =/usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-

    Endif

    如果ARCH=arm,即体系架构为ARM ,就给变量CROSS_COMPILE赋值,改变量的值代表了交叉编译器的路径

    include $(TOPDIR)/config.mk

    包含顶层目录下的config.mk文件

    下面开始定义了目标文件变量(跟ARM无关的这里就省略没有列出来)

    OBJS  = $(CPUDIR)/start.o

    这里的CPUDIR目录在顶层目录的config.mk文件里面定义 CPUDIR=arch/$(ARCH)/cpu/$(CPU),对于smdk4412来说CPUDIR= arch/arm/cpu/armv7

    ……

    OBJS := $(addprefix $(obj),$(OBJS))

    在原有OBJS值前面加上一个路径$(obj)前缀,指明绝对路径,如果$(obj)为空,则OBJS相当于没变化

    下面开始定义了库文件变量(跟硬件平台无关的这里就省略没有列出来)

    …….

    LIBS += $(shell if [ -f board/$(VENDOR)/common/Makefile ]; then echo

             "board/$(VENDOR)/common/lib$(VENDOR).o"; fi)

    LIBS += $(CPUDIR)/lib$(CPU).o

    ifdef SOC

    LIBS += $(CPUDIR)/$(SOC)/lib$(SOC).o

    Endif

    对于smdk4412来说,board/Samsung/common/Makefile文件并不存在,所以第一个赋值无效,接下来的两个分别为

    LIBS+= arch/arm/cpu/armv7/libarmv7.o

    LIBS+= arch/arm/cpu/armv7/exynos/libexynos.o

    …..

    LIBS += arch/$(ARCH)/lib/lib$(ARCH).o

    LIBS+= arch/arm/lib/libarm.o

    ifeq ($(SOC),exynos)

    LIBS += $(CPUDIR)/s5p-common/libs5p-common.o

    Endif

    LIBS+= arch/arm/cpu/armv7/s5p-common/libs5p-common.o

    LIBBOARD = board/$(BOARDDIR)/lib$(BOARD).o

    LIBBOARD := $(addprefix $(obj),$(LIBBOARD))

    BOARDDIR定义在顶层目录的config.mk文件中  BOARDDIR = $(VENDOR)/$(BOARD) 对于smdk4412来说

    BOARDDIR = Samsung/smdk4212

    LIBBOARD = board/samsung/smdk4212/libsmdk4212.o

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

    PLATFORM_LIBS += $(PLATFORM_LIBGCC)

    export PLATFORM_LIBS

    dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`取得交叉编译器的libgcc.a的绝对路径

    $(CC) = $(CROSS_COMPILE)gcc

    $(CFLAGS)定义在config.mk

    -L表示编译时将其后所跟的目录作为第一个寻找库文件的目录

    LDPPFLAGS +=

             -include $(TOPDIR)/include/u-boot/u-boot.lds.h

             $(shell $(LD) --version |

               sed -ne 's/GNU ld version [09][09].[09][09].*/-DLD_MAJOR=1 -DLD_MINOR=2/p')

    $(LD)定义在顶层目录的config.mk文件

    LD= $(CROSS_COMPILE)ld,表示交叉编译器里面的连接工具

    shell $(LD) –version表示取得连接工具的版本,我这里的版本号是”GNU ld (GNU Binutils for Ubuntu) 2.22”

    ifeq ($(CONFIG_NAND_U_BOOT),y)

    NAND_SPL = nand_spl

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

    endif

    ifeq ($(CONFIG_ONENAND_U_BOOT),y)

    ONENAND_IPL = onenand_ipl

    U_BOOT_ONENAND = $(obj)u-boot-onenand.bin

    ONENAND_BIN ?= $(obj)onenand_ipl/onenand-ipl-2k.bin

    endif

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

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

    $subst(<from>,<to>,<text>)的功能是将<text>中的<from>字符串换成<to>字符串

    __OBJS相当于截取OBJS的相对路径,__LIBS类似

    本篇主要分析目标文件以及相关的依赖文件

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

    all:              $(ALL)

    当用make命令编译时,执行的就是这个all变量

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

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

    Vuboot.hex是hex格式的文件。它的依赖u-boot属于ELF文件,OBJCOPY和OBJCFLAGS定义在顶层config.mk文件,

    OBJCOPY = $(CROSS_COMPILE)objcopy ;objcopy命令的作用是将一种格式的文件拷贝成另外一种格式的文件。

    OBJCFLAGS += --gap-fill=0xff;   --gap-fill = 0xff是objcopy的参数,表示在拷贝过程中,用0xff来填充段与段之间的空隙。

    $<表示所有的依赖,即u-boot文件,$@表示所有的目标,即u-boot.hex

    整句话的意思就是将u-boot拷贝成u-boot.hex 

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

                       $(OBJCOPY)-O srec $< $@

    类似的,将u-boot拷贝成u-boot.srec文件

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

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

                       $(BOARD_SIZE_CHECK)

    ifeq ($(CONFIG_S5PC210),y)

                       ./mkbl2u-boot.bin bl2.bin 14336

    Endif

    将u-boot拷贝成u-boot.bin文件,同时执行$(BOARD_SIZE_CHECK),在Makefile前面部分,有如下语句定义了BOARD_SIZE_CHECK:

    ifneq($(CONFIG_BOARD_SIZE_LIMIT),)

    BOARD_SIZE_CHECK=

             @actual='wc -d $@ |awk '{print $$!}';

             limit=$(CONFIG_BOARD_SIZE_LIMIT);

            if test

    actual=gt

    limit; then

                     echo "$@ exceeds file size limit:";

                     echo "limit:$$limit bytes";

                    echo "actual: $$actual bytes";

                    echo "excess:$$((actual-limit))bytes";

                    exit 1;

            fi

    else

    BOARD_SIZE_CHECK =

    Endif

    首先看CONFIG_BOARD_SIZE_LIMIT是否被定义了(是否定义了,在autoconf.mk文件中查看,它的意思是规定了文件大小的上限),如果没定义,BOARD_SIZE_CHECK为空,如果定义了,就比较目标文件的实际大小与他的上限大小(CONFIG_BOARD_SIZE_LIMIT的值),如果前者大于后者,就退出,如前者小于后者,则继续执行。

    接着分析上面蓝色字体的部分

    如果在autoconf.mk中定义了CONFIG_S5PC210 = y,则执行mkbl2命令,这个命令是干嘛的呢?目前还不清楚

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

                   $(CREATE_LDR_ENV)

                   $(LDR)-T $(CONFIG_BFIN_CPU)-c $@ $<$(LDR_FLAGS)

                   $(BOARD_SIZE_CHECK)

    生成u-boot.ldr文件

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

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

                      

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

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

    生成u-boot.ldr.hex和u-boot.ldr.sec文件

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

                       $(obj)tools/mkimage -A $(ARCH) -T firmware -C none

                      -a$(CONFIG_SYS_TEXT_BASE) -e 0

                      -n$(shell sed -n -e 's/.*U_BOOT_VERSION//p'$(VERSION_FILE)|

                              sed -e 's/''[]*$$/for $(BOARD)board''/' )

                      -d$<$@

                      

    Mkimage是给u-boot.bin增加0x40个字节大小的头部信息的工具,其各个参数的意义如下:

      -A 所支持的体系架构,这里是arm

      -T  镜像类型

      -C none  不压缩

      -a  指定映像在内存中的加载地址,映像下载到内存中的时候,要按照这个参数来加载

      -e   指定映像运行的入口地址

      -n   指定映像名称

      -d   指定生成映像的源文件

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

                       $(obj)tools/mkimage -n $(IMX_CONFIG) -T imximage

                       -e $(CONFIG_SYS_TEXT_BASE) -d $<$@

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

                       $(obj)tools/mkimage -n $(CONFIG_SYS_KWD_CONFIG) -T kwbimage

                       -a $(CONFIG_SYS_TEXT_BASE) -e $(CONFIG_SYS_TEXT_BASE) -d $<$@

    类似于上面

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

                       $(OBJDUMP) -d $< > $@

    通过u-boot文件生成u-boot.dis  (u-boot.dis是u-boot的反汇编代码)

    GEN_UBOOT =

                      UNDEF_SYS='$(OBJDUMP) -x $(LIBBOARD) $(LIBS)|

                     sed -n -e 's/.*SYM_PREFIX)__u_boot_cmd_.*/-u1/p'sprt|uniq;

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

                         --start-group $(__LIBS) --end-group $(P;ATFORM_LIBS)

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

                      

    由连接工具生成u-boot文件,并生成映射文件u-boot.map,具体的细节我也没看很懂

    $(obj)u-boot: depend

                      $(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds

                      $(GEN_UBOOT)

    这个是ELF格式的u-boot文件生成规则,后面那些变量都是它的依赖

    先看depend

    depend dep:     $(TIMESTAMP_FILE) $(VERSION_FILE)  

                        $(obj)include/autoconf.mk

                        $(obj)include/generated/generic -asm-foosets.h

                       for dir in $(SUBDIRS) $(CPUDIR) $(dir $(LDSCRIPT));do

                                   $(MAKE) -C $$ dir _depend;done

    依次进入到$(SUBDIRS)$(CPUDIR)和$(dir $(LDSCRIPT))目录,执行make _depend命令。_depend定义在顶层目录的rules.mk文件中,总的说来,依赖depend就是在相关的目录下生成.depend文件,.depend文件的内容列出了每个目标文件的依赖文件

    UBOOT的Makefile至此就大致分析完了,可能还有些地方说的不是很明白,望大家指正,接下来就要分析源码了

  • 相关阅读:
    【火炉炼AI】机器学习028-五分钟教你打造机器学习流水线
    【火炉炼AI】机器学习027-项目案例:用聚类算法建立客户细分模型
    【火炉炼AI】机器学习026-股票数据聚类分析-近邻传播算法
    【火炉炼AI】机器学习024-无监督学习模型的性能评估--轮廓系数
    【火炉炼AI】机器学习025-自动估算集群数量-DBSCAN算法
    【火炉炼AI】机器学习023-使用层次聚类算法构建模型
    【火炉炼AI】机器学习022-使用均值漂移聚类算法构建模型
    【火炉炼AI】机器学习021-使用K-means进行图片的矢量量化操作
    A Secret hdu 6153
    Fleet of the Eternal Throne HDU6138
  • 原文地址:https://www.cnblogs.com/AndyChen1/p/8339991.html
Copyright © 2011-2022 走看看