uboot学习:(四)顶层makefile分析

目录

版本号

MAKEFLAGS变量

命令输出

静默输出

设置编译结果输出目录

代码检查(一般不需要使用,了解就行)

模块编译(一般不用uboot编译模块,了解就行)

获取主机架构和系统

设置目标架构、交叉编译器和配置文件

包含 scripts/Kbuild.include

交叉编译工具变量设置

导出其他变量

make distclean过程

make xxx_defconfig 过程

分析 make xxx_defconfig

FORCE 

outputmakefile

scripts_basic

最后

第一条命令

第二条命令

 总结

继续分析一下u-boot.bin生成过程

总结过程

总结

 


版本号

  • VERSION主版本号
  • PATCHLEVEL补丁版本号
  • SUBLEVEL次版本号
  • EXTRAVERSION附加版本信息
  • NAME名字
    VERSION = 2016
    PATCHLEVEL = 03
    SUBLEVEL =
    EXTRAVERSION =
    NAME =

MAKEFLAGS变量

  • 只要不声明为unexport,MAKEFLAGS变量就会自动传到子makefile中
  • 大多数是指明当前目录
    MAKEFLAGS += -rR --include-dir=$(CURDIR)
  •  “+=”来给变量 MAKEFLAGS 追加了一些值
  • “-rR”表示禁止使用内置的隐 含规则和变量定义
  • “--include-dir”指明搜索路径
  • ”$(CURDIR)”表示当前目录

命令输出

当编译uboot时,如果加上V=1参数,就会详细打印编译信息,如果不加入只打印简单的编译信息

  • 会先判断V这个变量是否是在命令行来的
  • 如果是就给quiet和Q变量赋值为空
  • 如果不是就给quiet赋值为quiet_   Q赋值为@
    • 在后面会用到这个Q变量,@就表示不会在终端输出命令,为空就完整输出命令
    • 在后面会用到这个quiet变量,quiet_表示输出短文本,为空表示整个命令都输出,silent_表示整个命令都不会输出
      ifeq ("$(origin V)", "command line")
          KBUILD_VERBOSE = $(V)
      endif
      ifndef KBUILD_VERBOSE
          KBUILD_VERBOSE = 0
      endif
      
      ifeq ($(KBUILD_VERBOSE),1)
          quiet =
          Q =
      else
          quiet=quiet_
          Q = @
      endif
      

静默输出

当编译的时候加上-s参数,就会什么都不输出,简单就是quiet=silent_,最后会将三个变量导出去给子makefile

ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
    quiet=silent_
endif
else # make-3.8x
ifneq ($(filter s% -s%,$(MAKEFLAGS)),)
    quiet=silent_
endif
endif

export quiet Q KBUILD_VERBOSE

设置编译结果输出目录

make 的时候使用“O”来指定 输出目录,比如“make O=out”就是设置目标文件输出到 out 目录中

也可以不指定 O 参数,不指定的话源文件和编译产生的文件都在 同一个目录内,一般我们不指定 O 参数

ifeq ($(KBUILD_SRC),)
ifeq ("$(origin O)", "command line")
    KBUILD_OUTPUT := $(O)
endif
PHONY := _all
_all:
$(CURDIR)/Makefile Makefile: ;
ifneq ($(KBUILD_OUTPUT),)
saved-output := $(KBUILD_OUTPUT)
KBUILD_OUTPUT := $(shell mkdir -p $(KBUILD_OUTPUT) && cd $(KBUILD_OUTPUT) \ && /bin/pwd)
endif
endif
  • 判断“O”是否来自于命令行,如果来自命令行的话条件成立,KBUILD_OUTPUT 就为$(O),因此变量 KBUILD_OUTPUT 就是输出目录
  • 判断 KBUILD_OUTPUT 是否为空
  • 调用 mkdir 命令,创建 KBUILD_OUTPUT 目录,并且将创建成功以后的绝对路 径赋值给 KBUILD_OUTPUT。至此,通过 O 指定的输出目录就存在了

代码检查(一般不需要使用,了解就行)

使用命令

  • “make C=1”使能代码检查,检查那些需要重新编译的文 件。
  • “make C=2”用于检查所有的源码文件
ifeq ("$(origin C)", "command line")
    KBUILD_CHECKSRC = $(C)
endif

ifndef KBUILD_CHECKSRC
    KBUILD_CHECKSRC = 0
endif
  • 判断 C 是否来源于命令行,如果 C 来源于命令行,那就将 C 赋值给变量 KBUILD_CHECKSRC,如果命令行没有 C 的话 KBUILD_CHECKSRC 就为 0

模块编译(一般不用uboot编译模块,了解就行)

允许单独编译某个模块,使用命令“make M=dir”即可,旧语法“make SUBDIRS=dir”也是支持的

ifdef SUBDIRS
    KBUILD_EXTMOD ?= $(SUBDIRS)
endif

ifeq ("$(origin M)", "command line")
    KBUILD_EXTMOD := $(M)
endif
PHONY += all

ifeq ($(KBUILD_EXTMOD),)
    _all: all
else
    _all: modules
endif
ifeq ($(KBUILD_SRC),)
    srctree := .
else
    ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
        srctree := ..
    else
        srctree := $(KBUILD_SRC)
    endif
endif
objtree := .
src := $(srctree)
obj := $(objtree)
VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))

export srctree objtree VPATH
  • 如果定义了SUBDIRS ,变量 KBUILD_EXTMOD=SUBDIRS
  • 判断是否在命令行定义了 M,如果定义了的话 KBUILD_EXTMOD=$(M)
  • 判断 KBUILD_EXTMOD 时为空,如果为空的话目标_all 依赖 all,因此要先编译 出 all。否则的话默认目标_all 依赖 modules,要先编译出 modules,也就是编译模块
  • 判断 KBUILD_SRC 是否为空,如果为空的话就设置变量 srctree 为当前目录,即 srctree 为“.”,一般不设置 KBUILD_SRC
  • 设置变量 objtree 为当前目录
  • 设置变量 src 和 obj,都为当前目录
  • 导出变量 scrtree、objtree 和 VPATH到子makefile

获取主机架构和系统

HOSTARCH := $(shell uname -m | \
    sed -e s/i.86/x86/ \
    -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/)

HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
    sed -e 's/\(cygwin\).*/cygwin/')

export HOSTARCH HOSTOS
  • 定义了一个变量 HOSTARCH,用于保存主机架构,这里调用 shell 命令“uname -m”获取架构名称
  • shell 中的“|”表示管道,意思是将 左边的输出作为右边的输入
  • sed -e 是替换命令
    • sed -e s/i.86/x86/”表示将管道输入的字符串 中的“i.86”替换为“x86”
  • 定义了变量 HOSTOS,此变量用于保存主机 OS 的值,先使用 shell 命令“uname -s”来获取主机 OS
  • 使用管道将“Linux”作为后面“tr '[:upper:]' '[:lower:]'”的输入
  • “tr '[:upper:]' '[:lower:]'”表示将所有的大写字母替换为小写字母
  • “sed -e 's/\(cygwin\).*/cygwin/'”用于将 cygwin.*替换为 cygwin,如果主机是linux最终HOSTOS=linux
  • 导出变量到子makefile

设置目标架构、交叉编译器和配置文件

编 译 uboot 的 时 候 需 要 设 置 目 标 板 架 构 和 交 叉 编 译 器 ,“ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-”就是用于设置 ARCH 和 CROSS_COMPILE

或者直接在主makefile里加入,就不需要每次编译设置了

  • ARCH ?= arm
  • CROSS_COMPILE ?= arm-linux-gnueabihf-
ifeq ($(HOSTARCH),$(ARCH))
    CROSS_COMPILE ?=
endif
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-gnueabihf-
KCONFIG_CONFIG ?= .config
export KCONFIG_CONFIG
  • 行判断 HOSTARCH 和 ARCH 这两个变量是否相等,主机架构(变量 HOSTARCH)是 x86_64,而我们编译的是 ARM 版本 uboot,肯定不相等,所以 CROSS_COMPILE= arm-linuxgnueabihf-
  • 定义变量 KCONFIG_CONFIG,uboot 是可以配置 的,这里设置配置文件为.config,.config 默认是没有的,需要使用命令“make xxx_defconfig” 对 uboot 进行配置,配置完成以后就会在 uboot 根目录下生成.config。
  • 默认情况下.config 和 xxx_defconfig 内容是一样的,因为.config 就是从 xxx_defconfig 复制过来的。
  • 如果后续自行调整 了 uboot 的一些配置参数,那么这些新的配置参数就添加到了.config 中,而不是 xxx_defconfig。 相当于 xxx_defconfig 只是一些初始配置,而.config 里面的才是实时有效的配置

包含 scripts/Kbuild.include

scripts/Kbuild.include: ;
include scripts/Kbuild.include
  • 使用“include”包含了文件 scripts/Kbuild.include,此文件里面定义了很多变量,后面会用到

交叉编译工具变量设置

之前设置了交叉编译的前缀,交叉编译器其他的工具还没设置

AS = $(CROSS_COMPILE)as
ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2> /dev/null),)
    LD = $(CROSS_COMPILE)ld.bfd
else
    LD = $(CROSS_COMPILE)ld
endif
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
LDR = $(CROSS_COMPILE)ldr
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump

导出其他变量

因为导出了很多变量,我只看几个ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR,这 7 个变量是在其他文件里面定义的

可以输出这几个变量查看,加入下面代码,运行make mytest就可以查看

mytest:
    @echo ‘ARCH=' $(ARCH)
    @echo ‘CPU =' $(CPU )
    @echo ‘BOARD =' $(BOARD )
    @echo ‘VENDOR =' $(VENDOR )
    @echo ‘SOC =' $(SOC )
    @echo ‘CPUDIR =' $(CPUDIR )
    @echo ‘BOARDDIR=' $(BOARDDIR)
  • 在 uboot 根目录下有个文件叫做 config.mk,这 7 个变量就是在 config.mk 里面定义的,后面会包含一个子makefile,在这个makefile里会包含mk,就可以获取变量了
  • 这7个值在config.mk中又是从.config里面获取的,.config是make 默认配置文件后生成的,里面有芯片,cpu,板子信息

make distclean过程

在uboot学习:(一)中的步骤1.1中,会进行清理过程,下面是分析,在主makefile搜索distclean

PHCNY += distclean
distclean: mrproper
    @find $(srctree) $(RCS_FIND_IGNORE) \
        \( -name '*.orig' -o -name '*.rej' -o -name '*~'\
        ...
        -type f -print | xargs rm -f
    @rm -f boards.cfg
  • 就是删除一些文件,所以不需要仔细看 

make xxx_defconfig 过程

# 定义了变量 version_h,这变量保存版本号文件,此文件是自动生成的
version_h := include/generated/version_autogenerated.h

# 定义了变量 timestamp_h,此变量保存时间戳文件,此文件也是自动生成的
timestamp_h := include/generated/timestamp_autogenerated.h

# 定义了变量 no-dot-config-targets
no-dot-config-targets := clean clobber mrproper distclean \
    help %docs check% coccicheck \
    ubootversion backup

# 定义了变量 config-targets,初始值为 0
config-targets := 0

# 定义了变量 mixed-targets,初始值为 0
mixed-targets := 0

# 定义了变量 dot-config,初始值为 1
dot-config := 1

# 判断 MAKECMDGOALS 中是否包含 no-dot-config-targets 中的任何目标
# make xxx_defconfig   则MAKECMDGOALS=xxx_defconfig  则dot-config依旧为1 
ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
    # 判断 MAKECMDGOALS 中是否只包含 no-dot-config-targets 中的目标
    ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
        # 如果上述条件成立,则将dot-config变量设置为0。这意味在这种情况下,不需要使用配置文件
        dot-config := 0
    endif
endif

# 行判断KBUILD_EXTMOD是否为空
ifeq ($(KBUILD_EXTMOD),)
    # 检查 MAKECMDGOALS 是否包含以 config 开头的目标
    ifneq ($(filter config %config,$(MAKECMDGOALS)),)
        # 说明 MAKECMDGOALS 包含以 config 开头的目标,则将 config-targets 变量设置为 1
        config-targets := 1
        # 检查 MAKECMDGOALS 中的目标是否超过一个
        ifneq ($(words $(MAKECMDGOALS)),1)
            # 说明 MAKECMDGOALS 中包含不止一个目标,则将 mixed-targets 变量设置为 1
            mixed-targets := 1
        endif
    endif
endif

# 判断是否有混合目标,如果 mixed-targets 变量的值为1,表示存在混合目标
ifeq ($(mixed-targets),1)
    # 将 MAKECMDGOALS 中的目标以及 __build_one_by_one 添加到伪目标(PHONY)列表中
    PHONY += $(MAKECMDGOALS) __build_one_by_one
    # 过滤出MAKECMDGOALS中除了__build_one_by_one之外的目标,并将其依赖设为 __build_one_by_one。使用@:使其成为一个空目标
    $(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one @:
    # 定义 __build_one_by_one 目标,执行以下命令
    __build_one_by_one:
        # 循环遍历 MAKECMDGOALS 中的每一个目标,并分别调用 make 进行构建
        $(Q)set -e; \
        for i in $(MAKECMDGOALS); do \
            $(MAKE) -f $(srctree)/Makefile $$i; \
        done
else
    # 如果 mixed-targets 不为1,则判断是否有配置目标
    ifeq ($(config-targets),1)
        # 设置默认的配置文件为 sandbox_defconfig
        KBUILD_DEFCONFIG := sandbox_defconfig
        # 导出 KBUILD_DEFCONFIG 和 KBUILD_KCONFIG 变量
        export KBUILD_DEFCONFIG KBUILD_KCONFIG
        # 定义 config 目标,其依赖为 scripts_basic、outputmakefile 和 FORCE
        config: scripts_basic outputmakefile FORCE
            $(Q)$(MAKE) $(build)=scripts/kconfig $@
        %config: scripts_basic outputmakefile FORCE
            $(Q)$(MAKE) $(build)=scripts/kconfig $@
    else
        # 如果 config-targets 不为1,则判断 dot-config 是否为1
        ifeq ($(dot-config),1)
            # 如果 dot-config 为1,则包含 include/config/auto.conf 文件
            -include include/config/auto.conf
......

分析 make xxx_defconfig

在uboot学习:(一)中的步骤1.2中,会进行默认配置make xxx_defconfig,下面是配置过程分析

我们主要看下面后面得两段代码

%config: scripts_basic outputmakefile FORCE
    $(Q)$(MAKE) $(build)=scripts/kconfig $@
  • 依赖了scripts_basic和outputmakefile和FORCE这三个文件 

FORCE 

在顶层makefile中有定义,可以搜索看看 

PHONY += FORCE
FORCE:
  • 可以看出 FORCE 是没有规则和依赖的,所以每次都会重新生成 FORCE。当 FORCE 作为 其他目标的依赖时,由于 FORCE 总是被更新过的,因此依赖所在的规则总是会执行的

outputmakefile

在顶层makefile中有定义,可以搜索看看

PHONY += outputmakefile
outputmakefile:
# 判断 KBUILD_SRC 是否为空。如果 KBUILD_SRC 变量不为空,表示内核源码树与构建目录是分开的
ifneq ($(KBUILD_SRC),)
    # 创建一个符号链接 source,指向 $(srctree),其中 $(srctree) 是内核源码树的路径
    $(Q)ln -fsn $(srctree) source
    # 使用 $(CONFIG_SHELL) 运行脚本 $(srctree)/scripts/mkmakefile
    # 该脚本生成一个 Makefile 文件,用于在源码树和对象树之间进行构建
    $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
        # 传递给脚本参数srctree源码树,objtree对象树,内核版本号和补丁级别VERSION和PATCHLEVEL
        $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif
  • 定义了一个名为 outputmakefile 的伪目标,该目标主要用于在源码树与构建目录分开时,创建符号链接并生成必要的 Makefile 文件。通过这种方式,可以在构建目录中正确引用源码树,并执行内核构建过程
  • 首先要判断 KBUILD_SRC 是什么,是否为空,可以输出看看是否为空,一般是为空
    • KBUILD_SRC:表示源码树的路径,如果为空,则表示构建在源码树内进行;如果不为空,则表示构建在源码树外进行

scripts_basic

在顶层makefile中有定义,可以搜索看看

PHONY += scripts_basic
scripts_basic:
    # 使用 $(MAKE) 命令进入 scripts/basic 目录进行构建
    $(Q)$(MAKE) $(build)=scripts/basic
    # 删除文件 .tmp_quiet_recordmcount
    $(Q)rm -f .tmp_quiet_recordmcount
# 定义一个模式规则,表示scripts/basic/目录下的所有目标都依赖于scripts_basic目标
scripts/basic/%: scripts_basic ;
  • 确保在构建内核时,scripts/basic 目录中的基本脚本总是首先被构建
  • 通过定义 scripts_basic 伪目标,并设置依赖关系,可以确保这些基本脚本在构建过程中总是处于最新状态,并且在构建 scripts/basic 目录下的其他目标之前已经被构建

里面最重要得就是$(Q)$(MAKE) $(build)=scripts/basic,下面是分析

  • $(Q)表示是否要打印输出信息
  • $(MAKE)就是make
  • build在scripts/Kbuild.include文件中定义
    build := -f $(srctree)/scripts/Makefile.build obj
    • srctree表示当前目录,也就是 . /
    • build就等于 -f ./scripts/Makefile.build obj
  • 最后得到的是 make -f ./scripts/Makefile.build obj = scripts/basic

最后

最后回到 分析 make xxx_defconfig 里的一句 $(Q)$(MAKE) $(build)=scripts/kconfig $@

  • $@表示目标文件,就是xxx_defconfig
  • 最后展开得到的是 make -f ./scripts/Makefile.build obj = scripts/kconfig xxx_defconfig

现在我们得到了两条命令

  • make -f ./scripts/Makefile.build obj = scripts/basic
  • make -f ./scripts/Makefile.build obj = scripts/kconfig xxx_defconfig

第一条命令

  • 在./scripts/Makefile.build中
    • 会用到一个src变量,可以输出查看他的值,为scripts/basic
      src := $(patsubst $(prefix)/%,%,$(obj))
    • 还会遇到一串代码引用
      kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
      kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuilddir)/Kbuild,$(kbuild-dir)/Makefile)
      include $(kbuild-file)
      • 将 kbuild-dir 展开后为$(if $(filter /%, scripts/basic), scripts/basic, ./scripts/basic)

        • 因为没有以“/”为开头的单词,所以$(filter /%, scripts/basic)的结果为空

        • 最后kbuilddir=./scripts/basic

      • 将 kbuild-file 展开后为$(if $(wildcard ./scripts/basic/Kbuild), ./scripts/basic/Kbuild, ./scripts/basic/Makefile)

        • 因为 scrpts/basic 目录中没有 Kbuild 这个文件

        • 最后kbuild-file= ./scripts/basic/Makefile

      • 最后一行就为 include ./scripts/basic/Makefile

        • 也就是读取 scripts/basic 下面的 Makefile 文件

    • 因为第一条命令参数只有一个参数赋值, 没有默认目标,就表示需要默认目标,就要找到__build,而__build的规则为以下代码 

      __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target)$(extra-y)) \
          $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
          $(subdir-ym) $(always)
          @:
      
      • 其中要用到顶层makefile的变量,KBUILD_BUILTIN=1和KBUILD_MODULES=0,可以打印出来查看

      • 因此展开后目标__build 为

        __build:$(builtin-target) $(lib-target) $(extra-y)) $(subdir-ym) $(always)
            @:
      • 可以看出目标__build 有 5 个依赖:builtin-target、lib-target、extra-y、subdir-ym 和 always,可以打印这几个依赖来查看

      • 最后只有always=scripts/basic/fixdep,其他都是空

      • 因此__build 最终为__build: scripts/basic/fixdep 

      • 所以要编译出scripts/basic/fixdep这个文件(软件),对应的.c就是scripts/basic/fixdep.c,在scripts/basic/Makefile 里可以编译出来,前面已经读取了 scripts/basic/Makefile 文件,就已经编译出来了

  • 最后表明 scripts_basic 目标的作用就是编译出 scripts/basic/fixdep 这个软件,也就是第一条命令的作用是为了编译出这个软件,这个软件就不分析了,分析的很少,有兴趣可以了解一下

第二条命令

与第一条命令不同的是 obj = scripts/kconfig 和 xxx_defconfig

和第一条命令一样先从src开始分析,因为obj换了,所以src也会更换,对应的其他变量也变了

  • 打印出来对应变化的参数或者命令,src = scripts/kconfig,kbuild-dir = ./scripts/kconfig,kbuild-file = ./scripts/kconfig/Makefile,include ./scripts/kconfig/Makefile
  • 最后就关系到新的makefile,就是./scripts/kconfig/Makefile,下面分析这个makefile
  • ./scripts/kconfig/Makefile
    • 因为第二条命令带有目标参数xxx_defconfig,所以在makefile找%_defconfig规则
      %_defconfig: $(obj)/conf
      $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@$(Kconfig)
      %_config: %_defconfig
          @:
    • 第一行带入参数就是 xxx_defconfig: scripts/kconfig/conf,也就是xxx_defconfig依赖于scripts/kconfig/conf文件
    • 这个conf是由下面一行的静默编译得来的,第二行展开就是@ scripts/kconfig/conf --defconfig=arch/../configs/xxx_defconfig Kconfig
  • 最终上述命令用到了 xxx_defconfig 文件,比如 mx6ull_alientek_emmc_defconfig。这里会将 mx6ull_alientek_emmc_defconfig 中的配置输出到.config 文件中,最终生成 uboot 根目录下 的.config 文件

 总结

  1. 先是运行make xxx_defconfig命令
  2. 然后就分析顶层的makefile找到文件中的%config,有依赖和命令两个
    1. 依赖
      1. scripts_basic
        1. make -f ./scripts/Makefile.build obj=scripts/basic
        2. 生成:scripts/basic/fixdep
      2. outputmakefile
      3. FORCE
    2. 命令
      1. 命令:make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
        1. 生成:scripts/kconfig/conf
      2. 命令:scripts/kconfig/conf --defconfig=arch/../configs/xxx_defconfig Kconfig
        1. 生成:.config

继续分析一下u-boot.bin生成过程

上面是分析了uboot学习:(一)中的1.1和1.2这两条命令

现在开始是uboot学习:(一)中的1.3这条命令,目的是为了生成u-boot.bin文件

# 默认目标_all
PHONY := _all
_all:

PHONY += all
# 如果没有参数就默认依赖于all,KBUILD_EXTMOD表示要不要编译模块
ifeq ($(KBUILD_EXTMOD),)
    _all: all
else
    _all: modules
endif

all: $(ALL-y)

ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map u-boot.cfg binary_size_check
...
  1. 因为配置好 uboot 以后就可以直接 make 编译了,1.3这条命令因为只有参数V=1和-j12,所以没有指明目标,所以会使用默认目标,所以就直接在makefile里找默认目标_all
  2. _all默认目标有一个依赖all
  3. all依赖又有一个依赖$(ALL-y)
  4. ALL-y有多个依赖,u-boot.srec,u-boot.bin,u-boot.sym,System.map,u-boot.cfg,binary_size_check等依赖
    1. 我这主分析u-boot.bin,因为我们目的就为了生成u-boot.bin文件
      ifeq ($(CONFIG_OF_SEPARATE),y)
      u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
          $(call if_changed,cat)
      u-boot.bin: u-boot-dtb.bin FORCE
          $(call if_changed,copy)
      else
      u-boot.bin: u-boot-nodtb.bin FORCE
          $(call if_changed,copy)
      endif
      
    2. 因为在makefile里面没有找到CONFIG_OF_SEPARATE的定义,就知道u-boot.bin依赖于u-boot-nodtb.bin,然后命令就是$(call if_changed,copy)
      1. if_changed 是 一 个 函 数 , 这 个 函 数 在 scripts/Kbuild.include 中有定义,而顶层 Makefile 中会包含 scripts/Kbuild.include 文件
      2. 函数的作用就是从 u-boot-nodtb.bin 生成 u-boot.bin,所以我们下面开始找u-boot-nodtb.bin,先生成u-boot-nodtb.bin
        u-boot-nodtb.bin: u-boot FORCE
            $(call if_changed,objcopy)
            $(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
            $(BOARD_SIZE_CHECK)
    3. 发现u-boot-nodtb.bin依赖于u-boot,所以我们下面开始找u-boot
       u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
          $(call if_changed,u-boot__)
      ifeq ($(CONFIG_KALLSYMS),y)
          $(call cmd,smap)
          $(call cmd,u-boot__) common/system_map.o
      endif
      
    4. 发现u-boot再次依赖于u-boot-init,u-boot.lds,u-boot-main三个依赖
      1. 在顶层makefile中有定义u-boot-init := $(head-y)变量
        1. $(head-y)跟 CPU 架构有关,我们使用的是 ARM 芯片,所以 head-y 在 arch/arm/Makefile 中,head-y := arch/arm/cpu/$(CPU)/start.o
        2. cpu变量我们前面也知道了,所以就得到了head-y,也就得到了u-boot-init
      2. 在顶层makefile中有定义u-boot-main := $(libs-y)变量
        1. $(libs-y)在顶层 Makefile 中被定义为 uboot 所有子目录下 build-in.o 的集合
        2. 也就是u-boot-main被定义为 uboot 所有子目录下 build-in.o 的集合
      3. 找u-boot.lds的规则
        u-boot.lds: $(LDSCRIPT) prepare FORCE
             $(call if_changed_dep,cpp_lds)
        1. 就相当于将以 u-boot.lds 为链接脚本,将 arch/arm/cpu/$(CPU)/start.o 和各个子目录 下的 built-in.o 链接在一起生成 u-boot
      4. 那现在就是找built-in.o的是怎么生成的,因为有很多个built-in.o文件,所以就拿drivers/gpio/built-in.o 为例
        1. 在 drivers/gpio/目录下会有个名为.built-in.o.cmd 的文件
          cmd_drivers/gpio/built-in.o := arm-linux-gnueabihf-ld.bfd -r -o
          drivers/gpio/built-in.o drivers/gpio/mxc_gpio.o
        2. 可以看出drivers/gpio/built-in.o 这个文件是使用 ld 命令由文件 drivers/gpio/mxc_gpio.o 生成而来的
        3. mxc_gpio.o 又是 mxc_gpio.c 编译生成的.o 文件
        4. 这里用到了 ld 的“-r”参数,-r –relocateable: 产生可重定向的输出,比如,产生一个输出文件它可再次作为‘ld’的输 入,这经常被叫做“部分链接”,当我们需要将几个小的.o 文件链接成为一个.o 文件的时候,需 要使用此选项
    5. u-boot-init有一个变量head-y,最终的文件arch/arm/cpu/armv7/start.o
  5. 最终将各个子目录中的 built-in.o 文件链接在一起就形成了 u-boot,当使用V=1命令编译就可以看到链接的文件
  6. 最终是用 arm-linux-gnueabihf-ld.bfd 命令将 arch/arm/cpu/armv7/start.o 和其他众多 的 built_in.o 链接在一起,形成 u-boot
  7. 目标 all 除了 u-boot.bin 以外还有其他的依赖,比如 u-boot.srec 、u-boot.sym 、System.map、 u-boot.cfg 和 binary_size_check 等等,这些依赖的生成方法和 u-boot.bin 很类似

总结过程

  1. make第三条命令
  2. 匹配默认目标_all
  3. _all依赖all
  4. all依赖ALL-y
  5. ALL-y依赖u-boot.srec,u-boot.bin,u-boot.sym,System.map,u-boot.cfg,binary_size_check等其他配置依赖
    1. 我们主要获取u-boot.bin文件
    2. u-boot.bin依赖u-boot-nodtb.bin
    3. u-boot-nodtb.bin依赖u-boot
    4. u-boot依赖u-boot-init,u-boot.lds,u-boot-main
      1. u-boot-init依赖有head-y变量,head-y的变量为arch/arm/cpu/$(CPU)/start.o
      2. u-boot-main依赖有libs-y变量,libs-y的变量为uboot 所有子目录下 build-in.o 的集合

总结

  • make xxx_defconfig
    • 用于配置 uboot,这个命令最主要的目的就是生成.config 文件
  • make
    • 用于编译 uboot,这个命令的主要工作就是生成二进制的 u-boot.bin 文件和其他的一 些与 uboot 有关的文件,比如 u-boot.imx 等等

 

相关推荐

  1. Makefile学习

    2024-07-14 18:32:05       32 阅读
  2. Makefile学习笔记

    2024-07-14 18:32:05       26 阅读
  3. Makefiel技巧与分析

    2024-07-14 18:32:05       20 阅读
  4. [Makefile] 第章:大型项目中的makefile

    2024-07-14 18:32:05       18 阅读
  5. uboot学习及内核更换_incomplete

    2024-07-14 18:32:05       65 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-14 18:32:05       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-14 18:32:05       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-14 18:32:05       58 阅读
  4. Python语言-面向对象

    2024-07-14 18:32:05       69 阅读

热门阅读

  1. 代码随想录算法训练营第38天

    2024-07-14 18:32:05       30 阅读
  2. stm32出现hardfault-自动化分析map文件

    2024-07-14 18:32:05       18 阅读
  3. 深度学习-4-PyTorch中的数据加载器Dataset和DataLoader

    2024-07-14 18:32:05       17 阅读
  4. defineProps和defineEmits

    2024-07-14 18:32:05       18 阅读
  5. 常见排序算法

    2024-07-14 18:32:05       15 阅读
  6. 高阶面试-mongodb

    2024-07-14 18:32:05       17 阅读
  7. 【无标题】

    2024-07-14 18:32:05       19 阅读
  8. Apache Kylin: 大数据时代的分析引擎

    2024-07-14 18:32:05       20 阅读