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_VERBOSEKBUILD_VERBOSE = 0
      endififeq ($(KBUILD_VERBOSE),1)quiet =Q =
      elsequiet=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
endifexport 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)
endififndef KBUILD_CHECKSRCKBUILD_CHECKSRC = 0
endif
  • 判断 C 是否来源于命令行,如果 C 来源于命令行,那就将 C 赋值给变量 KBUILD_CHECKSRC,如果命令行没有 C 的话 KBUILD_CHECKSRC 就为 0

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

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

ifdef SUBDIRSKBUILD_EXTMOD ?= $(SUBDIRS)
endififeq ("$(origin M)", "command line")KBUILD_EXTMOD := $(M)
endif
PHONY += allifeq ($(KBUILD_EXTMOD),)_all: all
else_all: modules
endif
ifeq ($(KBUILD_SRC),)srctree := .
elseifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))srctree := ..elsesrctree := $(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
elseLD = $(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 := 0endif
endif# 行判断KBUILD_EXTMOD是否为空
ifeq ($(KBUILD_EXTMOD),)# 检查 MAKECMDGOALS 是否包含以 config 开头的目标ifneq ($(filter config %config,$(MAKECMDGOALS)),)# 说明 MAKECMDGOALS 包含以 config 开头的目标,则将 config-targets 变量设置为 1config-targets := 1# 检查 MAKECMDGOALS 中的目标是否超过一个ifneq ($(words $(MAKECMDGOALS)),1)# 说明 MAKECMDGOALS 中包含不止一个目标,则将 mixed-targets 变量设置为 1mixed-targets := 1endifendif
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_defconfigKBUILD_DEFCONFIG := sandbox_defconfig# 导出 KBUILD_DEFCONFIG 和 KBUILD_KCONFIG 变量export KBUILD_DEFCONFIG KBUILD_KCONFIG# 定义 config 目标,其依赖为 scripts_basic、outputmakefile 和 FORCEconfig: 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 是否为1ifeq ($(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
endifall: $(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 等等

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/871284.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

隔离驱动-视频课笔记

目录 1、需要隔离的原因 1.2、四种常用的隔离方案 2、脉冲变压器隔离 2.1、脉冲变压器的工作原理 2.2、泄放电阻对开关电路的影响 2.3、本课小结 3、光耦隔离驱动 3.1、光耦隔离驱动原理 3.2、光耦隔离驱动的电源进行分析 3.3、本课小结 4、自举升压驱动 4.1…

大数据开发中的元数据:从基础到高级应用的全面指南

在大数据开发中&#xff0c;元数据&#xff08;Metadata&#xff09;是指描述数据的数据。元数据可以提供有关数据结构、数据类型、数据约束和数据关系的重要信息。合理利用元数据可以显著提高数据建模和管理的效率。本文将详细介绍如何根据元数据建表&#xff0c;并提供一些代…

2024年公司电脑屏幕监控软件推荐|6款好用的屏幕监控软件盘点!

在当今的商业环境中&#xff0c;确保员工的工作效率和数据安全是每个企业管理者的重要任务。屏幕监控软件通过实时监控和记录员工的电脑活动&#xff0c;帮助企业有效地管理和优化工作流程。 1.固信软件 固信软件https://www.gooxion.com/ 主要特点&#xff1a; 实时屏幕监控…

养殖业饲料加工新选择,粉碎机械提升效率

在当今畜牧业快速发展的时代&#xff0c;饲料加工设备成为提升养殖效益的重要一环。其中&#xff0c;饲料加工粉碎机凭借其G效、便捷的特点&#xff0c;成为了养殖场的得力助手。 饲料加工粉碎机作为养殖业的重要设备之一&#xff0c;其主要功能是将各种原料如玉米、豆粕、麦…

Sentinel限流算法:滑动时间窗算法、漏桶算法、令牌桶算法。拦截器定义资源实现原理

文章目录 滑动时间窗算法基本知识源码算法分析 漏桶算法令牌桶算法拦截器处理web请求 滑动时间窗算法 基本知识 限流算法最简单的实现就是使用一个计数器法。比如对于A接口来说&#xff0c;我要求一分钟之内访问量不能超过100&#xff0c;那么我们就可以这样来实现&#xff1…

(一)高并发压力测试调优篇——MYSQL数据库的调优

前言 在实际项目开发中&#xff0c;很多业务场景下都需要考虑接口的性能要求&#xff0c;追求高并发、高吞吐量。那么对于此类问题如何入手呢&#xff1f;关注作者&#xff0c;不迷路。本节内容主要介绍在数据库db方面的优化&#xff0c;以mysql数据库为例。 关于db的优化&am…

7、matlab实现SGM/BM/SAD立体匹配算法计算视差图

1、matlab实现SGM/BM/SAD立体匹配算法计算视差图简介 SGM&#xff08;Semi-Global Matching&#xff09;、BM&#xff08;Block Matching&#xff09;和SAD&#xff08;Sum of Absolute Differences&#xff09;都是用于计算立体匹配&#xff08;Stereo Matching&#xff09;的…

远程帮客户解决“应用程序无法正常启动0xc000007b,请单击确定关闭应用程序”的问题

今天收到反馈&#xff0c;SmartPipe软件&#xff0c;在客户机器上报错&#xff0c;无法正常运行&#xff0c;采用远程控制软件进入客户电脑&#xff0c;发现电脑报错如下&#xff1a; 因为客户的电脑是win7&#xff0c;而之前发生过win7电脑上无法运行OCC编写的软件的情况&…

产品经理-一份标准需求文档的8个模块(14)

一份标准优秀的产品需求文档包括&#xff1a; ❑ 封面&#xff1b; ❑ 文档修订记录表&#xff1b; ❑ 目录&#xff1b; ❑ 引言&#xff1b; ❑ 产品概述&#xff1a;产品结构图 ❑ 详细需求说明&#xff1a;产品逻辑图、功能与特性简述列表、交互/视觉设计、需求详细描述&am…

Java实现数据结构——双链表

目录 一、前言 二、实现 2.1 类的创建 三、对链表操作实现 3.1 打印链表 3.2 插入数据 3.2.1 申请新节点 3.2.2 头插 ​编辑 3.2.3 尾插 3.2.4 链表长度 3.2.5 任意位置插入 3.3 删除数据 3.3.1 头删 3.3.2 尾删 3.3.3 删除指定位置数据 3.3.4 删除指定数据 3…

涉案财物管理系统|八大模块可视化展示

涉案财物管理系统DW-S405系统基于物联网技术规范涉案财物管理流程&#xff0c;确保涉案财物的安全性、完整性和合法性&#xff1b;可以提高办案效率&#xff0c;减少办案成本&#xff0c;实现资源共享。 涉案财物管理系统DW-S405主要分为 8 大模块数据展示。 1、案件信息&…

Linux C | 管道open打开方式

Linux C | 管道open打开方式 1.参考 1. 管道 2.现象 是的&#xff0c;这段代码在调用 open(AUDIOIN_FIFO, O_RDONLY) 时可能会被阻塞。原因是 FIFO&#xff08;命名管道&#xff09;在以只读模式打开时&#xff0c;如果没有其他进程以写模式打开该 FIFO&#xff0c;open 调用将…

防火墙综合实验二

目录 实验要求 IP地址配置 需求七 需求八 需求九 需求十 需求十一 实验要求 接防火墙综合实验一&#xff01; 7&#xff0c;办公区设备可以通过电信链路和移动链路上网(多对多的NAT&#xff0c;并且需要保留一个公网IP不能用来转换)。 8&#xff0c;分公司设备可以通过…

美无定论,娜扎亦菲各自绽放你更爱哪一款?

娜扎亦菲各自绽放你更爱哪一款&#xff1f; 哎呀&#xff0c;这个问题可真是让我头疼呢&#xff0c; 就像让我在两个糖果店里选择最甜的那一颗一样难&#xff01; 古力娜扎和刘亦菲&#xff0c;两位都是娱乐圈里璀璨的明珠&#xff0c; 美得各有千秋&#xff0c;让人怎么舍得…

C++基础入门(上)

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 C基础入门(上) 收录于专栏【C语法基础】 本专栏旨在分享学习C的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. C发展历史 2. C版本…

Goland 通道

channel通道 目录 channel通道 channel介绍 channel基本使用 有缓存通道和无缓存通道的区别 通道的初始化&#xff0c;写入数据到通道&#xff0c;从通道读取数据及基本的注意事项 channel的关闭和遍历 channel的关闭 为什么关闭 如何优雅地关闭通道 channel的遍历 chan…

小众好玩的赛车游戏:环道巨星 CIRCUIT SUPERSTARS中文安装包

《环道巨星》&#xff08;Circuit Superstars&#xff09;是一款由赛车迷亲手为其他赛车迷打造的俯视角赛车游戏。荟集史上各类赛车运动&#xff0c;旨在提供刺激好玩的驾驶体验&#xff1b;而游戏自带的高技术难度将促使玩家长时间磨砺技巧&#xff0c;以达成完美的一圈。 游戏…

【系统架构设计师】九、软件工程(面向对象方法|逆向工程)

目录 六、面向对象方法 6.1 基本概念 6.2 面向对象的分析 6.2.1 用例关系 6.2.2 类之间的关系 6.3 面向对象的设计 6.4 面向对象设计原则与设计模式 6.5 面向对象软件的测试 七、逆向工程 历年真题练习 六、面向对象方法 面向对象的分析方法 (Object-Oriented Analys…

【Linux网络】数据链路层【上】{初识数据链路层/以太网/路由表/MAC地址表/ARP表/NAT表}

文章目录 1.初识数据链路层2.认识以太网2.0前导知识以太网帧和MAC帧CMSA/CD以太网的最小帧长限制是64字节IP层和MAC层 2.1以太网帧格式 3.预备知识计算机网络通信以太网和wifi路由表/MAC地址表/ARP表/NAT表/ACL表 用于同一种数据链路节点的两个设备之间进行信息传递。 1.初识数…

Apache AGE 聚合函数

简介 一般来说&#xff0c;聚合函数 aggr(expr) 会处理每个聚合键在传入记录中找到的所有匹配行&#xff08;键使用等价性进行比较&#xff09;。 在常规聚合&#xff08;即形式为 aggr(expr) 的情况下&#xff09;&#xff0c;聚合值列表是候选值列表&#xff0c;其中所有空…