U-Boot顶层Makefile分析

参考:U-Boot顶层Makefile介绍
作者:一只青木呀
发布时间: 2020-10-22 16:22:17
网址:https://blog.csdn.net/weixin_45309916/article/details/109218569

目录

  • 0、学习目的
  • 1、准备好uboot源码
  • 2、分析顶层Makefile
    • 2.1、版本号
    • 2.2、MAKEFLAGS 变量
    • 2.3、命令输出(参数V的效果)
    • 2.4、静默输出
    • 2.5、设置编译结果输出目录
    • 2.6、代码检查
    • 2.7、模块编译
    • 2.8、获取主机架构和系统
    • 2.9 、设置目标架构、交叉编译器和配置文件
    • 2.10、调用 scripts/Kbuild.include
    • 2.11、交叉编译工具变量设置
    • 3.12、导出其他变量
    • 2.13、make xxx_defconfig 过程
    • 2.14、 Makefile.build 脚本分析(接上一节内容)
      • 2.14.1、scripts_basic 目标对应的命令
      • 2.14.2、 %config 目标对应的命令
      • 总结make xxx_defconfig过程
    • 2.15、 make 编译过程
      • 总结make命令的流程

0、学习目的

分析顶层Makefile主要是为了知道以后要改哪些东西。

Makefile配置好哪些C文件需要编译,配置好以后通过make命令编译即可。

1、准备好uboot源码

这里分析的是2016.版本的uboot。

uboot源码下载:https://blog.csdn.net/weixin_45309916/article/details/109176510

下载好后打开顶层Makefile。
在这里插入图片描述
一共是1600多行,接下来就开始分析啦

2、分析顶层Makefile

在阅读 uboot 源码之前,肯定是要先看一下顶层 Makefile,分析 gcc 版本代码的时候一定是先从顶层 Makefile 开始的,然后再是子 Makefile,这样通过层层分析 Makefile 即可了解整个工程的组织结构。顶层 Makefile 也就是 uboot 根目录下的 Makefile 文件,由于顶层 Makefile 文件内容比较多,所以我们将其分开来看。

2.1、版本号

顶层 Makefile 一开始是版本号,内容如下(为了方便分析,顶层 Makefile 代码段前段行号采用 Makefile 中的行号,因为 uboot 会更新,因此行号可能会与你所看的顶层 Makefile 有所不同):
在这里插入图片描述
VERSION 是主版本号, PATCHLEVEL 是补丁版本号, SUBLEVEL 是次版本号,这三个一起构成了 uboot 的版本号,比如当前的 uboot 版本号就是“2016.03”。 EXTRAVERSION 是附加版本信息, NAME 是和名字有关的,一般不使用这两个。

2.2、MAKEFLAGS 变量

make 是支持递归调用的,也就是在 Makefile 中使用“make”命令来执行其他的Makefile文件,一般都是子目录中的 Makefile 文件。假如在当前目录下存在一个“subdir”子目录,这个子目录中又有其对应的 Makefile 文件,那么这个工程在编译的时候其主目录中的 Makefile 就可以调用子目录中的 Makefile,以此来完成所有子目录的编译。主目录的 Makefile 可以使用如下代码来编译这个子目录:

$(MAKE) -C subdir

$(MAKE)就是调用“make”命令, -C 指定子目录。有时候我们需要向子 make 传递变量,这个时候使用“export”来导出要传递给子 make 的变量即可,如果不希望哪个变量传递给子make 的话就使用“unexport”来声明不导出:

export VARIABLE …… //导出变量给子 make 。
unexport VARIABLE…… //不导出变量给子 make。

有两个特殊的变量:“SHELL”和“MAKEFLAGS”,这两个变量除非使用“unexport”声明,否则的话在整个make的执行过程中,它们的值始终自动的传递给子make。在uboot的主Makefile中有如下代码:

MAKEFLAGS += -rR --include-dir=$(CURDIR)

上述代码使用“+=”来给变量 MAKEFLAGS 追加了一些值,“-rR”表示禁止使用内置的隐含规则和变量定义,“–include-dir”指明搜索路径, ”$(CURDIR)”表示当前目录。

2.3、命令输出(参数V的效果)

uboot 默认编译(就是输入编译命令make -j12)是不会在终端中显示完整的命令,都是短命令,如下图所示:
在这里插入图片描述
在终端中输出短命令虽然看起来很清爽,但是不利于分析 uboot 的编译过程。可以通过设置变量“V=1“来实现完整的命令输出(就是输入编译命令make V=1 -j12),这个在调试 uboot 的时候很有用,结果如下图所示:
在这里插入图片描述
顶层 Makefile 中控制命令输出的代码如下:
在这里插入图片描述
上述代码中先使用 ifeq 来判断"$(origin V)"和"command line"是否相等。这里用到了 Makefile中的函数 origin, origin 和其他的函数不一样,它不操作变量的值, origin 用于告诉你变量是哪来的,语法为:

$(origin <variable>)

variable 是变量名, origin 函数的返回值就是变量来源,因此( o r i g i n V ) 就 是 变 量 V 的 来 源 。 如 果 变 量 V 是 在 命 令 行 定 义 的 那 么 它 的 来 源 就 是 " c o m m a n d l i n e " , 这 样 " (origin V)就是变量 V 的来源。如果变量 V 是在命令行定义的那么它的来源就是"command line",这样"(originV)就是变量V的来源。如果变量V是在命令行定义的那么它的来源就是"commandline",这样"(origin V)"和"commandline"就相等了。当这两个相等的时候变量 KBUILD_VERBOSE 就等于 V 的值,比如在命令行中输 入 “ V=1 “ 的 话 那 么 KBUILD_VERBOSE=1 。 如 果 没 有 在 命 令 行 输 入 V 的 话KBUILD_VERBOSE=0。

第 80 行判断 KBUILD_VERBOSE 是否为 1,如果 KBUILD_VERBOSE 为 1 的话变量 quiet和 Q 都为空,如果 KBUILD_VERBOSE=0 的话变量 quiet 为“quiet_“,变量 Q 为“@” ,综上所述:
V=1 的话:

KBUILD_VERBOSE=1
quiet= 空 。
Q= 空。

V=0 或者命令行不定义 V 的话:

KBUILD_VERBOSE=0
quiet= quiet_。
Q= @。

Makefile 中会用到变量 quiet 和 Q 来控制编译的时候是否在终端输出完整的命令,在顶层Makefile 中有很多如下所示的命令:

$(Q)$(MAKE) $(build)=tools

如果 V=0 的话上述命令展开就是“@ make $(build)=tools”, make 在执行的时候默认会在终端输出命令,但是在命令前面加上“@”就不会在终端输出命令了(shell脚本语法)。当 V=1 的时候 Q 就为空,上述命令就是“make $(build)=tools”,因此在 make 执行的过程,命令会被完整的输出在终端上。

有些命令会有两个版本,比如:

quiet_cmd_sym ?= SYM $@
cmd_sym ?= $(OBJDUMP) -t $< > $@

sym 命令分为“quiet_cmd_sym”和“cmd_sym”两个版本,这两个命令的功能都是一样的,区别在于 make 执行的时候输出的命令不同。 quiet_cmd_xxx 命令输出信息少,也就是短命令,而 cmd_xxx 命令输出信息多,也就是完整的命令

如果变量 quiet 为空的话,整个命令都会输出。
如果变量 quiet 为“quiet_”的话,仅输出短版本。
如果变量 quiet 为“silent_”的话,整个命令都不会输出。

2.4、静默输出

设置 V=0 或者在命令行中不定义 V 的话,编译 uboot 的时候终端中显示的短命令,但是还是会有命令输出,有时候我们在编译 uboot 的时候不需要输出任何命令,这个时候就可以使用 uboot 的静默输出功能。编译的时候使用“make -s”即可实现静默输出,顶层 Makefile中相应的代码如下:
在这里插入图片描述

2.5、设置编译结果输出目录

uboot 可以将编译出来的目标文件输出到单独的目录中,在 make 的时候使用“O”来指定输出目录,比如“make O=out”就是设置目标文件输出到 out 目录中。这么做是为了将源文件和编译产生的文件分开,当然也可以不指定 O 参数,不指定的话源文件和编译产生的文件都在同一个目录内,一般我们不指定 O 参数。顶层 Makefile 中相关的代码如下:

# kbuild supports saving output files in a separate directory.
# To locate output files in a separate directory two syntaxes are supported.
# In both cases the working directory must be the root of the kernel src.
# 1) O=
# Use "make O=dir/to/store/output/files/"
#
# 2) Set KBUILD_OUTPUT
# Set the environment variable KBUILD_OUTPUT to point to the directory
# where the output files shall be placed.
# export KBUILD_OUTPUT=dir/to/store/output/files/
# make
#
# The O= assignment takes precedence over the KBUILD_OUTPUT environment
# variable.# KBUILD_SRC is set on invocation of make in OBJ directory
# KBUILD_SRC is not intended to be used by the regular user (for now)
ifeq ($(KBUILD_SRC),)# OK, Make called in directory where kernel src resides
# Do we want to locate output files in a separate directory?
ifeq ("$(origin O)", "command line")  KBUILD_OUTPUT := $(O)
endif# That's our default target when none is given on the command line
PHONY := _all
_all:# Cancel implicit rules on top Makefile
$(CURDIR)/Makefile Makefile: ;ifneq ($(KBUILD_OUTPUT),)
# Invoke a second make in the output directory, passing relevant variables
# check that the output directory actually exists
saved-output := $(KBUILD_OUTPUT)
KBUILD_OUTPUT := $(shell mkdir -p $(KBUILD_OUTPUT) && cd $(KBUILD_OUTPUT) \&& /bin/pwd)
$(if $(KBUILD_OUTPUT),, \$(error failed to create output directory "$(saved-output)"))PHONY += $(MAKECMDGOALS) sub-make$(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make@:sub-make: FORCE$(Q)$(MAKE) -C $(KBUILD_OUTPUT) KBUILD_SRC=$(CURDIR) \-f $(CURDIR)/Makefile $(filter-out _all sub-make,$(MAKECMDGOALS))# Leave processing to above invocation of make
skip-makefile := 1
endif # ifneq ($(KBUILD_OUTPUT),)
endif # ifeq ($(KBUILD_SRC),)
ifeq ("$(origin O)", "command line")  

判断“O”是否来自于命令行,如果来自命令行的话条件成立, KBUILD_OUTPUT就为$(O),因此变量 KBUILD_OUTPUT 就是输出目录。

ifneq ($(KBUILD_OUTPUT),)

判断 KBUILD_OUTPUT 是否为空。

KBUILD_OUTPUT := $(shell mkdir -p $(KBUILD_OUTPUT) && cd $(KBUILD_OUTPUT) \&& /bin/pwd)

调用 mkdir 命令,创建 KBUILD_OUTPUT 目录,并且将创建成功以后的绝对路径赋值给 KBUILD_OUTPUT。至此,通过 O 指定的输出目录就存在了。

2.6、代码检查

uboot 支持代码检查,使用命令“make C=1”使能代码检查,检查那些需要重新编译的文件。“make C=2”用于检查所有的源码文件,顶层 Makefile 中的代码如下:
在这里插入图片描述
第 176 行判断 C 是否来源于命令行,如果 C 来源于命令行,那就将 C 赋值给变量KBUILD_CHECKSRC,如果命令行没有 C 的话 KBUILD_CHECKSRC 就为 0。

2.7、模块编译

在 uboot 中允许单独编译某个模块,使用命令“ make M=dir”即可,旧语法“ makeSUBDIRS=dir”也是支持的。顶层 Makefile 中的代码如下:
在这里插入图片描述
第 186 行 判 断 是 否 定 义 了 SUBDIRS , 如 果 定 义 了 SUBDIRS , 变 量KBUILD_EXTMOD=SUBDIRS,这里是为了支持老语法“make SUBIDRS=dir”

第 190 行判断是否在命令行定义了 M,如果定义了的话 KBUILD_EXTMOD=$(M)。

第 197 行判断 KBUILD_EXTMOD 时为空,如果为空的话目标_all 依赖 all,因此要先编译出 all。否则的话默认目标_all 依赖 modules,要先编译出 modules,也就是编译模块。一般情况下我们不会在 uboot 中编译模块,所以此处会编译 all 这个目标。

第 203 行判断 KBUILD_SRC 是否为空,如果为空的话就设置变量 srctree 为当前目录,即srctree 为“.”,一般不设置 KBUILD_SRC。

第 214 行设置变量 objtree 为当前目录。

第 215 和 216 行分别设置变量 src 和 obj,都为当前目录。

第 218 行设置 VPATH。

第 220 行导出变量 scrtree、 objtree 和 VPATH。

2.8、获取主机架构和系统

接下来顶层 Makefile 会获取主机架构和系统,也就是我们电脑的架构和系统,代码如下:
在这里插入图片描述
第 227 行定义了一个变量 HOSTARCH,用于保存主机架构,这里调用 shell 命令“uname -m”获取架构名称,结果如下图所示
在这里插入图片描述
从上图可以看出当前电脑主机架构为“x86_64”, shell 中的“|”表示管道,意思是将左边的输出作为右边的输入, sed -e 是替换命令,“sed -e s/i.86/x86/”表示将管道输入的字符串中的“i.86”替换为“x86”,其他的“sed -s”命令同理。对于我的电脑而言, HOSTARCH=x86_64。第 237 行定义了变量 HOSTOS,此变量用于保存主机 OS 的值,先使用 shell 命令“name -s”来获取主机 OS,结果如下图所示:
在这里插入图片描述
从上图可以看出此时的主机 OS 为“Linux”,使用管道将“Linux”作为后面“tr ‘[:upper:]’’[:lower:]’”的输入,“tr ‘[:upper:]’ ‘[:lower:]’”表示将所有的大写字母替换为小写字母,因此得到“linux”。最后同样使用管道,将“linux”作为“sed -e ‘s/(cygwin).*/cygwin/’”的输入,用于将cygwin.*替换为 cygwin。因此, HOSTOS=linux。

第 240 行导出 HOSTARCH=x86_64, HOSTOS=linux。

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

编 译 uboot 的 时 候 需 要 设 置 目 标 板 架 构 和 交 叉 编 译 器 ,“ make ARCH=armCROSS_COMPILE=arm-linux-gnueabihf-”就是用于设置 ARCH 和 CROSS_COMPILE,在顶层Makefile 中代码如下:
在这里插入图片描述
第 245 行判断 HOSTARCH 和 ARCH 这两个变量是否相等,主机架构(变量 HOSTARCH)是x86_64,而我们编译的是 ARM 版本 uboot,肯定不相等,所以 CROS_COMPILE= arm-linuxgnueabihf-

第 249 行定义变量 KCONFIG_CONFIG, uboot 是可以配置的,这里设置配置文件为.config,前面树莓派学习过: .config 默认是没有的,需要使用命令“make xxx_defconfig”对 uboot 进行配置,配置完成以后就会在 uboot 根目录下生成.config。默认情况下.config 和xxx_defconfig 内容是一样的,因为.config 就是从 xxx_defconfig 复制过来的。如果后续自行调整了 uboot 的一些配置参数,那么这些新的配置参数就添加到了.config 中,而不是 xxx_defconfig。相当于 xxx_defconfig 只是一些初始配置,而.config 里面的才是实时有效的配置。

2.10、调用 scripts/Kbuild.include

主 Makefile 会调用文件 scripts/Kbuild.include这个文件,顶层 Makefile 中代码如下:
在这里插入图片描述
scripts/Kbuild.include文件部分内容:
在这里插入图片描述
在 uboot 的编译过程中会用到 scripts/Kbuild.include 中的这些变量

2.11、交叉编译工具变量设置

上面我们只是设置了 CROSS_COMPILE 的名字,但是交叉编译器其他的工具还没有设置,顶层 Makefile 中相关代码如下:
在这里插入图片描述

3.12、导出其他变量

接下来在顶层 Makefile 会导出很多变量,代码如下:
在这里插入图片描述
这些变量中大部分都已经在前面定义了,我们重点来看一下下面这几个变量:

ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR

在这里插入图片描述
修改好顶层 Makefile 以后,命令行输入以下命令:

make mytest

结果如图下所示:
在这里插入图片描述
从上图可以看到这 7 个变量的值,这 7 个变量是从哪里来的呢?在 uboot 根目录下有个文件叫做 config.mk,这 7 个变量就是在 config.mk(在uboot源码根目录下) 里面定义的,打开 config.mk 内容如下:
在这里插入图片描述
在这里插入图片描述

  • 第 25 行 定 义 变 量 ARCH(架构) , 值 为 $(CONFIG_SYS_ARCH:"%"=%) , 也 就 是 提 取CONFIG_SYS_ARCH 里面双引号“”之间的内容。比如 CONFIG_SYS_ARCH=“arm”的话,ARCH=arm

  • 第 26 行定义变量 CPU,值为$(CONFIG_SYS_CPU:"%"=%)。

  • 第 32 行定义变量 BOARD(板子),值为(CONFIG_SYS_BOARD:"%"=%)。

在这里插入图片描述

  • 第 34 行定义变量 VENDOR,值为$(CONFIG_SYS_VENDOR:"%"=%)。

  • 第 37 行定义变量 SOC,值为$(CONFIG_SYS_SOC:"%"=%)。

  • 第 44 行定义变量 CPUDIR(CPU目录),值为 arch/( A R C H ) / c p u (ARCH)/cpu(ARCH)/cpu(if ( C P U ) , / (CPU),/(CPU),/(CPU),)。

  • 第 46 行 sinclude 和 include 的功能类似,在 Makefile 中都是读取指定文件内容,这里读取文件( s r c t r e e ) / a r c h / (srctree)/arch/(srctree)/arch/(ARCH)/config.mk 的内容。 sinclude 读取的文件如果不存在的话不会报错

  • 第 47 行读取文件( s r c t r e e ) / (srctree)/(srctree)/(CPUDIR)/config.mk 的内容。

  • 第 50 行读取文件( s r c t r e e ) / (srctree)/(srctree)/(CPUDIR)/$(SOC)/config.mk 的内容。

  • 第 54 行 定 义 变 量 BOARDDIR , 如 果 定 义 了 VENDOR 那 么BOARDDIR=( V E N D O R ) / (VENDOR)/(VENDOR)/(BOARD),否则的 BOARDDIR=$(BOARD)。

  • 第 60 行读取文件( s r c t r e e ) / b o a r d / (srctree)/board/(srctree)/board/(BOARDDIR)/config.mk

接下来需要找到 CONFIG_SYS_ARCH、 CONFIG_SYS_CPU、 CONFIG_SYS_BOARD、CONFIG_SYS_VENDOR 和 CONFIG_SYS_SOC 这 5 个变量的值。这 5 个变量在 uboot 根目录下的.config 文件中有定义,定义如下:
在这里插入图片描述
根据示例代码可知:

ARCH = arm
CPU = armv7
BOARD = mx6ullevk
VENDOR = freescale
SOC = mx6
CPUDIR = arch/arm/cpu/armv7
BOARDDIR = freescale/mx6ullevk

在 config.mk 中读取的文件有:

arch/arm/config.mk
arch/arm/cpu/armv7/config.mk
arch/arm/cpu/armv7/mx6/config.mk (此文件不存在)
board/ freescale/mx6ullevk/config.mk (此文件不存在)

2.13、make xxx_defconfig 过程

在编译 uboot 之前要使用“make xxx_defconfig”命令来配置 uboot,那么这个配置过程是如何运行的呢?在顶层 Makefile 中有如下代码:

xxx_defconfig 对应代码第476行,下面有详解:
在这里插入图片描述
在这里插入图片描述
第 422 行定义了变量 version_h,这变量保存版本号文件,此文件是自动生成的。文件include/generated/version_autogenerated.h 内容如下图所示:
在这里插入图片描述
第 423 行定义了变量 timestamp_h,此变量保存时间戳文件,此文件也是自动生成的。文件include/generated/timestamp_autogenerated.h 内容如下图所示:
在这里插入图片描述

  • 第 425 行定义了变量 no-dot-config-targets。

  • 第 429 行定义了变量 config-targets,初始值为 0。

  • 第 430 行定义了变量 mixed-targets,初始值为 0

  • 第 431 行定义了变量 dot-config,初始值为 1。

  • 第 433 行将 MAKECMDGOALS 中不符合 no-dot-config-targets 的部分过滤掉,剩下的如果不为空的话条件就成立。 MAKECMDGOALS 是 make 的一个环境变量,这个变量会保存你所指定的终极目标列表,比如执行“make mx6ull_alientek_emmc_defconfig”,那么 MAKECMDGOALS就为 mx6ull_alientek_emmc_defconfig。很明显过滤后为空,所以条件不成立,变量 dot-config 依旧为 1。

  • 第439行判断KBUILD_EXTMOD是否为空,如果KBUILD_EXTMOD为空的话条件成立,经过前面的分析,我们知道 KBUILD_EXTMOD 为空,所以条件成立。

  • 第 440 行将 MAKECMDGOALS 中不符合“config”和“%config”的部分过滤掉,如果剩下的部分不为空条件就成立,很明显此处条件成立,变量 config-targets=1。

  • 第 442 行统计 MAKECMDGOALS 中的单词个数,如果不为 1 的话条件成立。此处调用Makefile 中的 words 函数来统计单词个数, words 函数格式如下:

$(words <text>)

很明显, MAKECMDGOALS 的单词个数是 1 个,所以条件不成立, mixed-targets 继续为0。综上所述,这些变量值如下:

config-targets = 1
mixed-targets = 0
dot-config = 1
  • 第 448 行如果变量 mixed-targets 为 1 的话条件成立,很明显,条件不成立。

  • 第 465 行如果变量 config-targets 为 1 的话条件成立,很明显,条件成立,执行这个分支。

  • 第 473 行,没有目标与之匹配,所以不执行。

  • 第 476 行,有目标与之匹配,当输入“make xxx_defconfig”的时候就会匹配到%config 目标,目标“%config”依赖于 scripts_basic、 outputmakefile 和 FORCE。

    • FORCE 在顶层 Makefile的 1610 行有如下定义:
      在这里插入图片描述
      可以看出 FORCE 是没有规则和依赖的,所以每次都会重新生成 FORCE。当 FORCE 作为其他目标的依赖时,由于 FORCE 总是被更新过的,因此依赖所在的规则总是会执行的
    • 依赖 scripts_basic 和 outputmakefile 在顶层 Makefile 中的内容如下:
      在这里插入图片描述
    • 第 408 行,判断 KBUILD_SRC 是否为空,只有变量 KBUILD_SRC 不为空的时候outputmakefile 才有意义,经过下面的代码分析 KBUILD_SRC 为空,所以 outputmakefile 无效。只有 scripts_basic 是有效的。
      在这里插入图片描述
      在这里插入图片描述
    • 第 396~398 行是 scripts_basic 的规则,其对应的命令用到了变量 Q、 MAKE 和 build,其中:
Q=@或为空   //Q代表要不要显示完整的命令行,无关紧要
MAKE=make

变量 build 是在 scripts/Kbuild.include 文件中有定义,定义如下:
在这里插入图片描述
从代码可以看出 build=-f $(srctree)/scripts/Makefile.build obj,经过前面的分析可知,变量 srctree 为”.”,因此:

build=-f ./scripts/Makefile.build obj

scripts_basic 展开以后如下:

scripts_basic:
@make -f ./scripts/Makefile.build obj=scripts/basic //也可以没有@,视配置而定
@rm -f . tmp_quiet_recordmcount //也可以没有@

scripts_basic 会调用文件./scripts/Makefile.build

接着回到示例代码中的%config 处,内容如下:

%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@

将命令展开就是:

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

同样也跟文件./scripts/Makefile.build 有关,使用如下命令配置 uboot,并观察其配置过程:

make mx6ull_14x14_ddr512_emmc_defconfig V=1

配置过程如下图所示:
在这里插入图片描述
从上图可以看出,我们的分析是正确的,接下来就要结合下面两行命令重点分析一下文件 scripts/Makefile.build。
①、 scripts_basic 目标对应的命令

@make -f ./scripts/Makefile.build obj=scripts/basic

②、 %config 目标对应的命令

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

2.14、 Makefile.build 脚本分析(接上一节内容)

从上一小节可知,“ make xxx_defconfig“配置 uboot 的时候如下两行命令会执行脚本scripts/Makefile.build:

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

依次来分析一下:

2.14.1、scripts_basic 目标对应的命令

scripts_basic 目标对应的命令为: @make -f ./scripts/Makefile.build obj=scripts/basic。打开文件 scripts/Makefile.build,有如下代码:
在这里插入图片描述
第 9 行定义了变量 prefix 值为 tpl。

第 10 行定义了变量 src,这里用到了函数 patsubst,此行代码展开后为:

$(patsubst tpl/%,%, scripts/basic)

patsubst 是替换函数,格式如下:

$(patsubst <pattern>,<replacement>,<text>)

此函数用于在 text 中查找符合 pattern 的部分,如果匹配的话就用 replacement 替换掉。pattenr 是可以包含通配符“%”,如果 replacement 中也包含通配符“%”,那么 replacement 中的这个“%”将是 pattern 中的那个“%”所代表的字符串。函数的返回值为替换后的字符串。因此,第 10 行就是在“scripts/basic”中查找符合“tpl/%”的部分,然后将“tpl/”取消掉,但是“scripts/basic”没有“tpl/”,所以 src= scripts/basic。

第 11 行判断变量 obj 和 src 是否相等,相等的话条件成立,很明显,此处条件成立。

第 12 行和第 9 行一样,只是这里处理的是“spl”,“scripts/basic”里面也没有“spl/”,所以src 继续为 scripts/basic。

第 15 行因为变量 obj 和 src 相等,所以 prefix=.。

继续分析 scripts/Makefile.build,有如下代码:
在这里插入图片描述
将 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。最
后将 59 行展开,即:

include ./scripts/basic/Makefile

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

继续分析 scripts/Makefile.build,如下代码:
在这里插入图片描述
__build 是默认目标,因为命令“@make -f ./scripts/Makefile.build obj=scripts/basic”没有指定目标,所以会使用到默认目标: __build。在顶层 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。这 5 个依赖的具体内容我们就不通过源码来分析了,直接在 scripts/Makefile.build 中输入图所示内容,将这 5 个变量的值打印出来:
在这里插入图片描述
执行如下命令:

make mx6ull_14x14_ddr512_emmc_defconfig V=1

结果如下图所示:
在这里插入图片描述
从上图可以看出,只有 always 有效,因此__build 最终为:

__build: scripts/basic/fixdep
@:

__build 依赖于 scripts/basic/fixdep,所以要先 scripts/basic/fixdep.c 编译,生成 fixdep,前面已经读取了 scripts/basic/Makefile 文件。

综上所述, scripts_basic 目标的作用就是编译出 scripts/basic/fixdep 这个软件(具体作用未知)。

2.14.2、 %config 目标对应的命令

%config 目 标 对 应 的 命 令 为 : @make -f ./scripts/Makefile.build obj=scripts/kconfigxxx_defconfig,各个变量值如下:

src= scripts/kconfig
kbuild-dir = ./scripts/kconfig
kbuild-file = ./scripts/kconfig/Makefile
include ./scripts/kconfig/Makefile

可以看出, Makefilke.build 会读取 scripts/kconfig/Makefile 中的内容,此文件有如下所示内容:
在这里插入图片描述
目标%_defconfig 刚好和我们输入的 xxx_defconfig 匹配,所以会执行这条规则。依赖为$(obj)/conf,展开后就是 scripts/kconfig/conf。接下来就是检查并生成依赖 scripts/kconfig/conf。conf 是主机软件,到这里我们就打住,不要纠结 conf 是怎么编译出来的,否则就越陷越深,太绕了,像 conf 这种主机所使用的工具类软件我们一般不关心它是如何编译产生的。如果一定要看是 conf 是怎么生成的,可以输入如下命令重新配置 uboot,在重新配置 uboot 的过程中就会输出 conf 编译信息。

make distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_
defconfig V=1

在这里插入图片描述
得到 scripts/kconfig/conf 以后就要执行目标%_defconfig 的命令:

$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)

相关的变量值如下:

silent=-s 或为空
SRCARCH=..
Kconfig=Kconfig

将其展开就是:

@ scripts/kconfig/conf --defconfig=arch/../configs/xxx_defconfig Kconfig

上述命令用到了 xxx_defconfig 文件,比如 mx6ull_alientek_emmc_defconfig。这里会将mx6ull_alientek_emmc_defconfig 中的配置输出到.config 文件中,最终生成 uboot 根目录下的.config 文件。

总结make xxx_defconfig过程

在这里插入图片描述
至此,make xxx_defconfig 就分析完了,接下来就要分析一下 u-boot.bin 是怎么生成的了。

2.15、 make 编译过程

配置好 uboot 以后就可以直接 make 编译了(也就是前面Makefile配置好哪些C文件需要编译),因为没有指明目标,所以会使用默认目标,主Makefile 中的默认目标如下:
在这里插入图片描述

目标_all 又依赖于 all,如下所示:
在这里插入图片描述
如 果 KBUILD_EXTMOD 为 空 的 话 _all 依 赖 于 all 。 这 里 不 编 译 模 块 , 所 以KBUILD_EXTMOD 肯定为空, _all 的依赖就是 all。在主 Makefile 中 all 目标规则如下:
在这里插入图片描述
从 802 行可以看出, all 目标依赖$(ALL-y),而在顶层 Makefile 中, ALL-y 如下:
在这里插入图片描述
从示例代码代码可以看出, ALL-y 包含 u-boot.srec、 u-boot.bin、 u-boot.sym、System.map、 u-boot.cfg 和 binary_size_check 这几个文件。根据 uboot 的配置情况也可能包含其他的文件,比如:

ALL-$(CONFIG_ONENAND_U_BOOT) += u-boot-onenand.bin

CONFIG_ONENAND_U_BOOT 就是 uboot 中跟 ONENAND 配置有关的,如果我们使能了ONENAND,那么在.config 配置文件中就会有“CONFIG_ONENAND_U_BOOT=y”这一句。相当于 CONFIG_ONENAND_U_BOOT 是个变量,这个变量的值为“y”,所以展开以后就是:

ALL-y += u-boot-onenand.bin

这个就是.config 里面的配置参数的含义,这些参数其实都是变量,后面跟着变量值,会在顶层 Makefile 或者其他 Makefile 中调用这些变量。

ALL-y 里面有个 u-boot.bin,这个就是我们最终需要的 uboot 二进制可执行文件,所作的所有工作就是为了它。在顶层 Makefile 中找到 u-boot.bin 目标对应的规则,如下所示
在这里插入图片描述
第 825 行判断 CONFIG_OF_SEPARATE 是否等于 y,如果相等,那条件就成立,在.config中搜索“CONFIG_OF_SEPARAT”,没有找到,说明条件不成立。
在这里插入图片描述

第 832 行就是目标 u-boot.bin 的规则,目标 u-boot.bin 依赖于 u-boot-nodtb.bin,命令为$(callif_changed,copy) , 这 里 调 用 了 if_changed , if_changed 是 一 个 函 数 , 这 个 函 数 在scripts/Kbuild.include 中有定义,而顶层 Makefile 中会包含 scripts/Kbuild.include 文件,这个前面已经说过了。

if_changed 在 Kbuild.include 中的定义如下:
在这里插入图片描述
第 227 行为 if_changed 的描述,根据描述,在一些先决条件比目标新的时候,或者命令行有改变的时候, if_changed 就会执行一些命令。

第 257 行就是函数 if_changed, if_changed 函数引用的变量比较多,也比较绕,我们只需要知道它可以从 u-boot-nodtb.bin 生成 u-boot.bin 就行了。

既然 u-boot.bin 依赖于 u-boot-nodtb.bin,那么肯定要先生成 u-boot-nodtb.bin 文件,顶层Makefile 中相关代码如下:
在这里插入图片描述
目标 u-boot-nodtb.bin 又依赖于 u-boot,顶层 Makefile 中 u-boot 相关规则如下:
在这里插入图片描述
目标 u-boot 依赖于 u-boot_init、 u-boot-main 和 u-boot.lds, u-boot_init 和 u-boot-main 是两个变量,在顶层 Makefile 中有定义,值如下:
在这里插入图片描述
$ (head-y)跟 CPU 架构有关,我们使用的是 ARM 芯片,所以 head-y 在 arch/arm/Makefile 中被指定为:

head-y := arch/arm/cpu/$(CPU)/start.o

根据分析,我们知道 CPU=armv7,因此 head-y 展开以后就是:

head-y := arch/arm/cpu/armv7/start.o

因此:

u-boot-init= arch/arm/cpu/armv7/start.o

$(libs-y)在顶层 Makefile 中被定义为 uboot 所有子目录下 build-in.o 的集合,代码如下:
在这里插入图片描述
在这里插入图片描述
从上面的代码可以看出, libs-y 都是 uboot 各子目录的集合,最后:

libs-y := $(patsubst %/, %/built-in.o, $(libs-y))

这里调用了函数 patsubst,将 libs-y 中的“/”替换为”/built-in.o”,比如“drivers/dma/”就变为了“drivers/dma/built-in.o”,相当于将 libs-y 改为所有子目录中 built-in.o 文件的集合。那么 uboot-main 就等于所有子目录中 built-in.o 的集合。

这个规则就相当于将以 u-boot.lds 为链接脚本,将 arch/arm/cpu/armv7/start.o 和各个子目录下的 built-in.o 链接在一起生成 u-boot。
u-boot.lds 的规则如下:

u-boot.lds: $(LDSCRIPT) prepare FORCE
$(call if_changed_dep,cpp_lds)

接下来的重点就是各子目录下的 built-in.o 是怎么生成的,以 drivers/gpio/built-in.o 为例,在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

从命令“cmd_drivers/gpio/built-in.o”可以看出, drivers/gpio/built-in.o 这个文件是使用 ld 命令由文件 drivers/gpio/mxc_gpio.o 生成而来的, mxc_gpio.o 是 mxc_gpio.c 编译生成的.o 文件,这个是 NXP 的 I.MX 系列的 GPIO 驱动文件。这里用到了 ld 的“-r”参数,参数含义如下:

-r –relocateable: 产生可重定向的输出,比如,产生一个输出文件它可再次作为‘ld’ 的输入,这经常被叫做“部分链接”,当我们需要将几个小的.o 文件链接成为一个.o 文件的时候,需要使用此选项。

最终将各个子目录中的 built-in.o 文件链接在一起就形成了 u-boot,使用如下命令编译 uboot就可以看到链接的过程:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_
defconfig V=1
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- V=1

编译的时候会有如下图所示内容输出:
在这里插入图片描述

在这里插入图片描述 grep命令搜索ox87800000

在这里插入图片描述

将其整理一下,内容如下:

arm-linux-gnueabihf-ld.bfd -pie --gc-sections -Bstatic -Ttext 0x87800000 \
-o u-boot -T u-boot.lds \
arch/arm/cpu/armv7/start.o \
--start-group arch/arm/cpu/built-in.o \
arch/arm/cpu/armv7/built-in.o \
arch/arm/imx-common/built-in.o \
arch/arm/lib/built-in.o \
board/freescale/common/built-in.o \
board/freescale/mx6ull_alientek_emmc/built-in.o \
cmd/built-in.o \
common/built-in.o \
disk/built-in.o \
drivers/built-in.o \
drivers/dma/built-in.o \
drivers/gpio/built-in.o \
……
drivers/spi/built-in.o \
drivers/usb/dwc3/built-in.o \
drivers/usb/emul/built-in.o \
drivers/usb/eth/built-in.o \
drivers/usb/gadget/built-in.o \
drivers/usb/gadget/udc/built-in.o \
drivers/usb/host/built-in.o \
drivers/usb/musb-new/built-in.o \
drivers/usb/musb/built-in.o \
drivers/usb/phy/built-in.o \
drivers/usb/ulpi/built-in.o \
fs/built-in.o \
lib/built-in.o \
net/built-in.o \
test/built-in.o \
test/dm/built-in.o \
--end-group arch/arm/lib/eabi_compat.o \
-L /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/../lib/gcc/arm-linuxgnueabihf/4.9.4 -lgcc -Map u-boot.map

可以看出最终是用 arm-linux-gnueabihf-ld.bfd 命令将 arch/arm/cpu/armv7/start.o 和其他众多的 built_in.o 链接在一起,形成 u-boot。

目标 all 除了 u-boot.bin 以外还有其他的依赖,比如 u-boot.srec 、 u-boot.sym 、 System.map、u-boot.cfg 和 binary_size_check 等等,这些依赖的生成方法和 u-boot.bin 很类似

总结make命令的流程

在这里插入图片描述

make xxx_defconfig: 用于配置 uboot,这个命令最主要的目的就是生成.config 文件。

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

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

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

相关文章

思科路由器的硬件组成

思科路由器的硬件组成常建议常建议CCNA的学员们&#xff0c;除了能灵活配置与应用思科的路由器和交换机以外&#xff0c;还需要掌握对路由器与交换机的常规维护工作&#xff0c;比如IOS的安装与升级&#xff0c;这个过程好比为一台全新的计算机安装操作系统&#xff0c;在此之前…

linux gone kde 图形程序 兼容,Oracle8i HowTo

现在假设你要在Linux上安装Oracle服务器,请先过以下5关&#xff1a;系统要求规划硬盘创建用户创建目录设置参数1.系统要求前提条件是能够安装运行Linux&#xff0c;然后应满足以下硬件要求&#xff1a;内存 RequirementsMemory  最小要求128 MB RAM.更多(256MB)内存可以提…

U-Boot启动流程详解

参考&#xff1a;U-Boot顶层目录链接脚本文件&#xff08;u-boot.lds&#xff09;介绍 作者&#xff1a;一只青木呀 发布时间&#xff1a; 2020-10-23 13:52:23 网址&#xff1a;https://blog.csdn.net/weixin_45309916/article/details/109240625 目录链接脚本 u-boot.lds 详解…

linux下使用nginx搭建集群,CentOS(linux) 下Nginx的安装(Nginx+Tomcat集群第一步)

CentOS(linux) 下Nginx的安装(NginxTomcat集群)CentOS 7.4(腾讯云)pcre库zlib库opensslNginx服务器安装gcc g开发类库yum -y install gcc automake autoconf libtool makeyum install gcc gcc-cwget https://ftp.pcre.org/pub/pcre/pcre-8.42.tar.gz解压pcre库tar -zxvf pcre-8…

在线生成透明ICO图标神器

此神器的链接为&#xff1a;http://ico.duduxuexi.com/ 大家可以将这个网址收藏好&#xff0c;本人亲测十分好用&#xff01;对我们的ios,安卓以及windows开发都有极大的好处。转载于:https://www.cnblogs.com/geeksongs/p/10040935.html

Vim的使用和快捷键介绍

参考&#xff1a;Linux–Vim的使用以及快捷键大全 作者&#xff1a;一只青木呀 发布时间&#xff1a; 2020-07-12 12:43:19 网址&#xff1a;https://blog.csdn.net/weixin_45309916/article/details/107297756 目录安装vim三种常用的模式三种常用模式的切换vim 快捷键1.一般模…

手机当电脑麦克风 linux,WO Mic让手机成为电脑的无线麦克风

WO Mic 是一款可以让 Android 手机成为电脑无线麦克风的应用&#xff0c;支持 windows 和 Linux&#xff0c;除了 WiFi&#xff0c;还能使用蓝牙以及 USB 数据线(只有安卓支持数据线连接)。官方下载页面如下&#xff1a;一、windows下安装连接windows主机上需要安装客户端和驱动…

如何编写 Cloud9 JavaScript IDE 的功能扩展

上周末我们在JSConf.eu发布了 Cloud9 IDE &#xff0c;同时发布了对应的GitHub项目。在4天时间里该项目得到340个人的关注和将近50个fork。Cloud9的口号是由"由Javascripters 为Javascripters创建的IED"&#xff0c;这口号有点递归&#xff0c;它意味着你可以hack这个…

make工具和Makefile基础语法(含有交叉编译、pthread_create()的处理)

目录含有交叉编译、pthread_create()处理初次使用1、编写Makefile文件2、make3、make cleanMakefile基本语法1、Makefile规则格式2、变量3、变量赋值符&#xff08;、:、?、&#xff09;4、模式规则&#xff08;通配符&#xff09;5、自动化变量6、伪目标7、条件判断8、函数使…

树莓派超声波模块测距

参考&#xff1a;树莓派超声波模块测距及C语言demo 作者&#xff1a;一只青木呀 发布时间&#xff1a; 2020-07-22 16:54:16 网址&#xff1a;https://blog.csdn.net/weixin_45309916/article/details/107516949 前面这篇博文已经讲过了&#xff1a;树莓派外设开发基础&#xf…

spring cloud (一、服务注册demo_eureka)

首先我的博客记理论知识很少&#xff0c;大家对spring boot、spring cloud 、分布式 、微服务什么的一点概念都没有的还请先去百度看看理论&#xff0c;知道了是做什么用的&#xff0c;然后再写下demo ,这样学起来才没有那么迷糊&#xff01; 我一般写下的demo都是我踩了很多坑…

JavaFX 的 UI 控件集 ControlsFX

出处&#xff1a;http://www.oschina.net/p/controlsfx JavaFX 的 UI 控件集 ControlsFX ControlsFX 开源项目旨在为 JavaFX 开发提供更多的 UI 控件和其他工具。 该项目主要针对JavaFX 8.0&#xff08;捆绑在JDK 8中&#xff09;&#xff0c;考虑到JavaFX中已经包含了一些控件…

shell 脚本编写使用

目录一、什么是shell 脚本二、shell 脚本写法三、shell 脚本语法1、第一个 shell 脚本2、read命令3、数值计算4、test命令5、中括号判断符6、默认变量7、条件判断8、函数9、循环一、什么是shell 脚本 终端中输入一系列命令完成一些操作&#xff0c;但是我们一条一条输入命令&a…

javaweb开发之处理表单上传文件和文件下载

2019独角兽企业重金招聘Python工程师标准>>> 一、基于表单的上传文件 1. enctype属性 当表单需要上传文件时&#xff0c;需指定表单 enctype 的值为 multipart/form-data。 在 form 元素的语法中&#xff0c;enctype 属性指定将数据发送到服务器时浏览器使用的编码…

单片机预备知识(电平、进制转换、字节、数据类型)

参考&#xff1a;郭天祥十天带你精通51单片机 网址&#xff1a;https://www.bilibili.com/video/BV1DW411a7mz/?spm_id_from333.788.videocard.0 目录电平特性二进制进制转换1K字节等于多少字节基本数据类型电平特性 二进制 进制转换 也可参考这篇&#xff1a;进制问题和C语言…

[summary] 单调队列

2019独角兽企业重金招聘Python工程师标准>>> 很久没做单调队列了╮(╯_╰)╭ 已经不太会了.... 单调队列究其本质就是队列,加上队尾可以删除. 队列都是从队尾插入,队首输出.单调队列也一样. 以队尾到队首递增的单调队列为例,我们需要保证队列的单调性,所以当插入一…

段错误的调试方法(printf输出、GDB)

参考&#xff1a;段错误产生原因及简单的调试方法 参考&#xff1a;如何解决段错误 参考&#xff1a;C语言gdb调试之精髓&#xff08;常用命令、多进程、多线程、程序日志&#xff09; 网址&#xff1a;https://www.bilibili.com/video/BV1ei4y1V758?fromsearch&seid40373…

STM32使用IIC总线通讯协议在OLED屏幕上显示字符串、汉字、单总线获取DHT11模块温湿度并通过IIC显示到屏幕(软件IIC)

参考&#xff1a;基于stm32软件IIC的oled显示温湿度 作者&#xff1a;ZPZ DayUp 发布时间&#xff1a; 2021-07-25 20:52:43 网址&#xff1a;https://blog.csdn.net/m0_56197680/article/details/119077076?spm1001.2014.3001.5501 目录软件模拟IIC时序(起始、停止、应答、发…

w ndows7端口在哪里,win7电脑遇到端口被占用的情况该如何查看并将其关闭

Windows7操作系统的酷炫和强大已经深受用户们的喜欢了&#xff0c;这里根大家分享的是教你查看win7电脑端口是否被占用的技巧&#xff0c;端口是我们在进行远程或者打印机等都会遇到的&#xff0c;但是有很多用户会遇到端口被占用的情况&#xff0c;遇到这样的问题首先就要找出…

STM32F103五分钟入门系列(十三)独立看门狗IWDG

参考&#xff1a;STM32F103五分钟入门系列&#xff08;十三&#xff09;独立看门狗IWDG 作者&#xff1a;自信且爱笑‘ 发布时间&#xff1a;2021-07-31 19:50:28 网址&#xff1a;https://blog.csdn.net/Curnane0_0/article/details/119269391?utm_sourceapp&app_version…