• 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名字
      1. VERSION = 2016
      2. PATCHLEVEL = 03
      3. SUBLEVEL =
      4. EXTRAVERSION =
      5. 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_表示整个命令都不会输出
        1. ifeq ("$(origin V)", "command line")
        2. KBUILD_VERBOSE = $(V)
        3. endif
        4. ifndef KBUILD_VERBOSE
        5. KBUILD_VERBOSE = 0
        6. endif
        7. ifeq ($(KBUILD_VERBOSE),1)
        8. quiet =
        9. Q =
        10. else
        11. quiet=quiet_
        12. Q = @
        13. endif

    静默输出

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

    1. ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
    2. ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
    3. quiet=silent_
    4. endif
    5. else # make-3.8x
    6. ifneq ($(filter s% -s%,$(MAKEFLAGS)),)
    7. quiet=silent_
    8. endif
    9. endif
    10. export quiet Q KBUILD_VERBOSE

    设置编译结果输出目录

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

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

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

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

    使用命令

    • “make C=1”使能代码检查,检查那些需要重新编译的文 件。
    • “make C=2”用于检查所有的源码文件
    1. ifeq ("$(origin C)", "command line")
    2. KBUILD_CHECKSRC = $(C)
    3. endif
    4. ifndef KBUILD_CHECKSRC
    5. KBUILD_CHECKSRC = 0
    6. endif
    • 判断 C 是否来源于命令行,如果 C 来源于命令行,那就将 C 赋值给变量 KBUILD_CHECKSRC,如果命令行没有 C 的话 KBUILD_CHECKSRC 就为 0

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

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

    1. ifdef SUBDIRS
    2. KBUILD_EXTMOD ?= $(SUBDIRS)
    3. endif
    4. ifeq ("$(origin M)", "command line")
    5. KBUILD_EXTMOD := $(M)
    6. endif
    7. PHONY += all
    8. ifeq ($(KBUILD_EXTMOD),)
    9. _all: all
    10. else
    11. _all: modules
    12. endif
    13. ifeq ($(KBUILD_SRC),)
    14. srctree := .
    15. else
    16. ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
    17. srctree := ..
    18. else
    19. srctree := $(KBUILD_SRC)
    20. endif
    21. endif
    22. objtree := .
    23. src := $(srctree)
    24. obj := $(objtree)
    25. VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
    26. 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

    获取主机架构和系统

    1. HOSTARCH := $(shell uname -m | \
    2. sed -e s/i.86/x86/ \
    3. -e s/sun4u/sparc64/ \
    4. -e s/arm.*/arm/ \
    5. -e s/sa110/arm/ \
    6. -e s/ppc64/powerpc/ \
    7. -e s/ppc/powerpc/ \
    8. -e s/macppc/powerpc/\
    9. -e s/sh.*/sh/)
    10. HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
    11. sed -e 's/\(cygwin\).*/cygwin/')
    12. 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" role="presentation">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-
    1. ifeq ($(HOSTARCH),$(ARCH))
    2. CROSS_COMPILE ?=
    3. endif
    4. ARCH ?= arm
    5. CROSS_COMPILE ?= arm-linux-gnueabihf-
    6. KCONFIG_CONFIG ?= .config
    7. 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

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

    交叉编译工具变量设置

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

    1. AS = $(CROSS_COMPILE)as
    2. ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2> /dev/null),)
    3. LD = $(CROSS_COMPILE)ld.bfd
    4. else
    5. LD = $(CROSS_COMPILE)ld
    6. endif
    7. CC = $(CROSS_COMPILE)gcc
    8. CPP = $(CC) -E
    9. AR = $(CROSS_COMPILE)ar
    10. NM = $(CROSS_COMPILE)nm
    11. LDR = $(CROSS_COMPILE)ldr
    12. STRIP = $(CROSS_COMPILE)strip
    13. OBJCOPY = $(CROSS_COMPILE)objcopy
    14. OBJDUMP = $(CROSS_COMPILE)objdump

    导出其他变量

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

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

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

    make distclean过程

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

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

    make xxx_defconfig 过程

    1. # 定义了变量 version_h,这变量保存版本号文件,此文件是自动生成的
    2. version_h := include/generated/version_autogenerated.h
    3. # 定义了变量 timestamp_h,此变量保存时间戳文件,此文件也是自动生成的
    4. timestamp_h := include/generated/timestamp_autogenerated.h
    5. # 定义了变量 no-dot-config-targets
    6. no-dot-config-targets := clean clobber mrproper distclean \
    7. help %docs check% coccicheck \
    8. ubootversion backup
    9. # 定义了变量 config-targets,初始值为 0
    10. config-targets := 0
    11. # 定义了变量 mixed-targets,初始值为 0
    12. mixed-targets := 0
    13. # 定义了变量 dot-config,初始值为 1
    14. dot-config := 1
    15. # 判断 MAKECMDGOALS 中是否包含 no-dot-config-targets 中的任何目标
    16. # make xxx_defconfig 则MAKECMDGOALS=xxx_defconfig 则dot-config依旧为1
    17. ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
    18. # 判断 MAKECMDGOALS 中是否只包含 no-dot-config-targets 中的目标
    19. ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
    20. # 如果上述条件成立,则将dot-config变量设置为0。这意味在这种情况下,不需要使用配置文件
    21. dot-config := 0
    22. endif
    23. endif
    24. # 行判断KBUILD_EXTMOD是否为空
    25. ifeq ($(KBUILD_EXTMOD),)
    26. # 检查 MAKECMDGOALS 是否包含以 config 开头的目标
    27. ifneq ($(filter config %config,$(MAKECMDGOALS)),)
    28. # 说明 MAKECMDGOALS 包含以 config 开头的目标,则将 config-targets 变量设置为 1
    29. config-targets := 1
    30. # 检查 MAKECMDGOALS 中的目标是否超过一个
    31. ifneq ($(words $(MAKECMDGOALS)),1)
    32. # 说明 MAKECMDGOALS 中包含不止一个目标,则将 mixed-targets 变量设置为 1
    33. mixed-targets := 1
    34. endif
    35. endif
    36. endif
    37. # 判断是否有混合目标,如果 mixed-targets 变量的值为1,表示存在混合目标
    38. ifeq ($(mixed-targets),1)
    39. # 将 MAKECMDGOALS 中的目标以及 __build_one_by_one 添加到伪目标(PHONY)列表中
    40. PHONY += $(MAKECMDGOALS) __build_one_by_one
    41. # 过滤出MAKECMDGOALS中除了__build_one_by_one之外的目标,并将其依赖设为 __build_one_by_one。使用@:使其成为一个空目标
    42. $(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one @:
    43. # 定义 __build_one_by_one 目标,执行以下命令
    44. __build_one_by_one:
    45. # 循环遍历 MAKECMDGOALS 中的每一个目标,并分别调用 make 进行构建
    46. $(Q)set -e; \
    47. for i in $(MAKECMDGOALS); do \
    48. $(MAKE) -f $(srctree)/Makefile $$i; \
    49. done
    50. else
    51. # 如果 mixed-targets 不为1,则判断是否有配置目标
    52. ifeq ($(config-targets),1)
    53. # 设置默认的配置文件为 sandbox_defconfig
    54. KBUILD_DEFCONFIG := sandbox_defconfig
    55. # 导出 KBUILD_DEFCONFIG 和 KBUILD_KCONFIG 变量
    56. export KBUILD_DEFCONFIG KBUILD_KCONFIG
    57. # 定义 config 目标,其依赖为 scripts_basic、outputmakefile 和 FORCE
    58. config: scripts_basic outputmakefile FORCE
    59. $(Q)$(MAKE) $(build)=scripts/kconfig $@
    60. %config: scripts_basic outputmakefile FORCE
    61. $(Q)$(MAKE) $(build)=scripts/kconfig $@
    62. else
    63. # 如果 config-targets 不为1,则判断 dot-config 是否为1
    64. ifeq ($(dot-config),1)
    65. # 如果 dot-config 为1,则包含 include/config/auto.conf 文件
    66. -include include/config/auto.conf
    67. ......

    分析 make xxx_defconfig

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

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

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

    FORCE 

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

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

    outputmakefile

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

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

    scripts_basic

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

    1. PHONY += scripts_basic
    2. scripts_basic:
    3. # 使用 $(MAKE) 命令进入 scripts/basic 目录进行构建
    4. $(Q)$(MAKE) $(build)=scripts/basic
    5. # 删除文件 .tmp_quiet_recordmcount
    6. $(Q)rm -f .tmp_quiet_recordmcount
    7. # 定义一个模式规则,表示scripts/basic/目录下的所有目标都依赖于scripts_basic目标
    8. 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))
      • 还会遇到一串代码引用
        1. kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
        2. kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuilddir)/Kbuild,$(kbuild-dir)/Makefile)
        3. 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的规则为以下代码 

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

        • 因此展开后目标__build 为

          1. __build:$(builtin-target) $(lib-target) $(extra-y)) $(subdir-ym) $(always)
          2. @:
        • 可以看出目标__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规则
        1. %_defconfig: $(obj)/conf
        2. $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@$(Kconfig)
        3. %_config: %_defconfig
        4. @:
      • 第一行带入参数就是 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文件

    1. # 默认目标_all
    2. PHONY := _all
    3. _all:
    4. PHONY += all
    5. # 如果没有参数就默认依赖于all,KBUILD_EXTMOD表示要不要编译模块
    6. ifeq ($(KBUILD_EXTMOD),)
    7. _all: all
    8. else
    9. _all: modules
    10. endif
    11. all: $(ALL-y)
    12. ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map u-boot.cfg binary_size_check
    13. ...
    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文件
        1. ifeq ($(CONFIG_OF_SEPARATE),y)
        2. u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
        3. $(call if_changed,cat)
        4. u-boot.bin: u-boot-dtb.bin FORCE
        5. $(call if_changed,copy)
        6. else
        7. u-boot.bin: u-boot-nodtb.bin FORCE
        8. $(call if_changed,copy)
        9. 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
          1. u-boot-nodtb.bin: u-boot FORCE
          2. $(call if_changed,objcopy)
          3. $(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
          4. $(BOARD_SIZE_CHECK)
      3. 发现u-boot-nodtb.bin依赖于u-boot,所以我们下面开始找u-boot
        1. u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
        2. $(call if_changed,u-boot__)
        3. ifeq ($(CONFIG_KALLSYMS),y)
        4. $(call cmd,smap)
        5. $(call cmd,u-boot__) common/system_map.o
        6. 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的规则
          1. u-boot.lds: $(LDSCRIPT) prepare FORCE
          2. $(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 的文件
            1. cmd_drivers/gpio/built-in.o := arm-linux-gnueabihf-ld.bfd -r -o
            2. 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 等等

     

  • 相关阅读:
    自动化测试之路 —— Appium使用教程
    钟汉良日记:出门在外靠什么?
    计算机网络——计算机网络体系结构(3/4)-计算机网络体系结构分层思想举例
    C语言:输入t行字符串,每行字符串有10个字符
    java计算机毕业设计留守儿童帮扶网站源码+mysql数据库+系统+lw文档+部署
    Vue组件自定义事件
    项目平台——测试报表的实现(六)
    冰冰学习笔记:二叉树的进阶OJ题与非递归遍历
    装配ftp服务
    Trainer--学习笔记
  • 原文地址:https://blog.csdn.net/weixin_59669309/article/details/140349140