uboot构建框架2-kbuild框架简要分析

其实网络上介绍kbuild框架的资料书籍一大把,有的介绍非常详细,大家不妨可以参考。本文试图从一些线索来简要分析kbuild框架,好让我们从一个方面了解一条线,不至于陷入kbuild框架的各种大坑里面。

kbuild是个什么鬼?

kbuild来源于linux内核,是linux内核用来构建输出内核镜像的make框架。因为这套框架做得比较好,uboot也借过来用了。kbuild中的k,我想应该就是kernel的意思吧。这是我的一个简单理解,想看专业的解释,可以参考内核里面的文档。

kbuild在哪?

在uboot源代码根目录,我们可以看到这么几个文件:

1.Makefile文件

2.Kbuild文件

3.Kconfig文件

4.scripts目录

源代码目录文件如下:

Makefile文件好理解,一个make工程要能够使用make功能,就必须要有makefile文件。这个Makefile就是make的主Makefile,正是因为这个Makefile,我们在uboot根目录下面才能输入各种make命令,比如make menuconfig。

Kbuild文件我们可以打开看一下,看起来也是一个make脚本,这里面定义了各种变量,也定义了少量规则,看起来貌似挺复杂,我们不知道干嘛用的。不过看看文件开头的注释,大概能猜出这个文件是干嘛用的,我们暂且就信了这个注释,就这么理解吧:

#
# Kbuild for top-level directory of U-Boot
# This file takes care of the following:
# 1) Generate generic-asm-offsets.h
# 2) Generate asm-offsets.h

看到没,这个file是用来生成generic-asm-offsets.h和asm-offsets.h这两个头文件的,至于这两个货色是干嘛用的,暂且不表,原因是我也不知道,没法表。

再来看下Kconfig文件。这个文件看起来不像脚本,看起来像是配置文件,这个配置文件遵循某种规范,如下:

这个语法,我想应该是有资料可查的,否则别人没法知道怎么使用对吧,所有的规则都是人为定义的,并不是天生的。其实讲这个规则的资料网上非常多,大家可以去搜索一下,本文主要还是简要介绍为主,就不详细展开了,否则,这么一篇小文章,是远远不够的。我们需要知道的是,这个配置文件是kbuild配置框架的一个输入,kbuild会根据这个配置文件生成配置界面给大家选,仅此而已。

我们再来看下scripts目录,我们可以ls命令查看一下:

sunke@droresrv:~/work/MYiR-iMX-Uboot$ ls -l ./scripts/
total 444
drwxrwxr-x 2 sunke sunke   4096 May 31 11:03 basic
-rwxrwxr-x 1 sunke sunke    514 May 14 15:05 binutils-version.sh
-rwxrwxr-x 1 sunke sunke 129427 May 14 15:05 checkpatch.pl
-rwxrwxr-x 1 sunke sunke   5513 May 14 15:05 checkstack.pl
-rwxrwxr-x 1 sunke sunke   5132 May 14 15:05 cleanpatch
-rw-rw-r-- 1 sunke sunke  14132 May 14 15:05 docproc.c
-rwxrwxr-x 1 sunke sunke    439 May 14 15:05 dtc-version.sh
-rwxrwxr-x 1 sunke sunke   5041 May 14 15:05 fill_scrapyard.py
-rwxrwxr-x 1 sunke sunke    312 May 14 15:05 gcc-stack-usage.sh
-rwxrwxr-x 1 sunke sunke    819 May 14 15:05 gcc-version.sh
-rwxrwxr-x 1 sunke sunke  58501 May 14 15:05 get_maintainer.pl
-rw-rw-r-- 1 sunke sunke  11557 May 14 15:05 Kbuild.include
drwxrwxr-x 3 sunke sunke   4096 May 31 11:03 kconfig
-rwxrwxr-x 1 sunke sunke  73838 May 14 15:05 kernel-doc
-rwxrwxr-x 1 sunke sunke    205 May 14 15:05 ld-version.sh
-rwxrwxr-x 1 sunke sunke    460 May 14 15:05 Lindent
-rwxrwxr-x 1 sunke sunke   4821 May 14 15:05 mailmapper
-rw-rw-r-- 1 sunke sunke    548 May 14 15:05 Makefile
-rw-rw-r-- 1 sunke sunke   4321 May 14 15:05 Makefile.autoconf
-rw-rw-r-- 1 sunke sunke  14262 May 30 09:26 Makefile.build
-rw-rw-r-- 1 sunke sunke   3070 May 14 15:05 Makefile.clean
-rw-rw-r-- 1 sunke sunke   2003 May 14 15:05 Makefile.extrawarn
-rw-rw-r-- 1 sunke sunke   4655 May 14 15:05 Makefile.host
-rw-rw-r-- 1 sunke sunke  15510 May 14 15:05 Makefile.lib
-rw-rw-r-- 1 sunke sunke   8156 May 14 15:05 Makefile.spl
-rw-rw-r-- 1 sunke sunke    261 May 14 15:05 Makefile.uncmd_spl
-rwxrwxr-x 1 sunke sunke   1183 May 14 15:05 mkmakefile
-rwxrwxr-x 1 sunke sunke   2756 May 14 15:05 objdiff
-rwxrwxr-x 1 sunke sunke   3974 May 14 15:05 setlocalversion
-rwxrwxr-x 1 sunke sunke    569 May 14 15:05 show-gnu-make

怪怪,这个目录下面,内容可就多了。乍看起来丝毫没有头绪,怎么办?其实我们还是不要偏离我们的主题,我们的主题是探讨make相关的东西,kbuild框架也是make相关的一种框架,那么好,我们只关心Makefile行不行?我们看下,这个目录下面跟makefile相关的文件大致有:

Makefile、Makefile.autoconf、Makefile.build、Makefile.clean等Makefile.开头的文件,还有一个Kbuild.include,还有一个Kconfig目录。

里面都有些啥?

看起来还是挺多,怎么办?一个好的办法是,我们一个个打开看下,这里面都有些啥玩意。在看本文的时候,建议大家手上有一份代码(本文使用的uboot版本是2016.03),这样我就不用贴图了,好让文章看起来不那么又臭又长。我们会发现,首先这个Kbuild.include里面包含的内容,基本可以肯定就是make脚本,这里面定义了一些make变量,目前我们并不知道这些make变量是干嘛用的,而且看起来这些变量定义颇有些意思,频繁使用了make的函数和自动化变量。比如:

###
# Name of target with a '.' as filename prefix. foo/bar.o => foo/.bar.o
dot-target = $(dir $@).$(notdir $@) 

这个变量使用了dir函数和notdir函数,用于通过规则的目标文件信息构造一个点分字符串,至于这个变量干嘛的,我们没有看到使用的地方,当然是不得而知的,我们暂且感受一下氛围。这个文件里面,诸如此类的变量,定义了不少,我们暂且不用管干什么用的,我们可以大致扫一遍这些变量,好让我们以后某个地方看到的时候,还能想起来,这个变量定义在Kbuild.include文件。当然,我得给大家介绍一下这里面的重要变量,否则本文是不是就没有意义了,什么都让大家自己看。

1.build变量:

###
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
# Usage:
# $(Q)$(MAKE) $(build)=dir
build := -f $(srctree)/scripts/Makefile.build obj 

build变量用于将make过程引入到Makefile.build,使用该Makefile规则来构建目标。

2.clean变量:

###
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.clean obj=
# Usage:
# $(Q)$(MAKE) $(clean)=dir
clean := -f $(srctree)/scripts/Makefile.clean obj 

clean变量用于将make过程引入到Makefile.clean,使用该Makefile规则来清除产物。

3.echo-cmd变量:

# echo command.
# Short version is used, if $(quiet) equals `quiet_', otherwise full one.
echo-cmd = $(if $($(quiet)cmd_$(1)),\
        echo '  $(call escsq,$($(quiet)cmd_$(1)))$(echo-why)';)

该变量用于构造命令打印输出。比如我们在编译时看到的:

CC /arch/arm/cpu/armv7/cpu.o

4.if_changed系列变量:

# Execute command if command has changed or prerequisite(s) are updated.
#
if_changed = $(if $(strip $(any-prereq) $(arg-check)),                       \   
        @set -e;                                                             \   
        $(echo-cmd) $(cmd_$(1));                                             \   
        printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd)

很多地方使用这个if_changed系列变量来执行规则命令。在此以if_changed作为例子,我们展开分析一下:

首先我们看到,这个变量的值是一个make的if函数,这个函数根据第一个参数的展开情况,来决定后面的值。如果展开非空,函数的值就是第二个参数。第一个参数为:

$(strip $(any-prereq) $(arg-check))

这里利用strip函数将首尾空格去掉,也把字符串中间的空格整理一下。最后输出的值,是以空格为间隔的一个个字符串。这里引用了两个变量any-prereq和arg-check。any-prereq变量为:

any-prereq = $(filter-out $(PHONY),$?) $(filter-out $(PHONY) $(wildcard $^),$^)

看起来好像挺复杂,实际上我们一个个去看,也相对简单,各个击破吧。首先,这个变量的值由两部分组成,分别是:

1.$(filter-out $(PHONY),$?)
2.$(filter-out $(PHONY) $(wildcard $^),$^)

前两个都是make的filter-out函数,这是个反过滤函数,是将第二个参数里面符合第一个参数条件的值去掉,留下的值作为结果。$?变量表示所有比目标文件更新的依赖文件列表,$(PHONY)是包含伪目标的变量。所以,第一个结果就是:所有比目标更新的依赖文件,这些依赖文件里面不包含伪目标。第二个呢?看起来好像稍微复杂一点,别怕,我们各个击破。首先看$^,这个变量表示规则的所有依赖文件列表,使用空格分隔。$(wildcard $^)使用了wildcard函数,这个函数表示列出当前所有的依赖文件,注意,这里列出的依赖文件,是文件系统中有的,也就是说,这里列出所有已经生成的依赖文件。那么,第二个结果就显而易见了:所有依赖文件,这些依赖文件里面除去了伪目标和已经生成的文件,也就是所有还未生成的依赖文件且没有伪目标文件。所以,any-prereq变量的值就包含:所有比目标更新的依赖文件和所有还未生成的依赖文件,这里面不包含伪目标。是不是就感觉清晰了?

剩下一个arg-check变量。这个变量定义如下:

ifneq ($(KBUILD_NOCMDDEP),1)
# Check if both arguments has same arguments. Result is empty string if equal.
# User may override this check using make KBUILD_NOCMDDEP=1
arg-check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \
                    $(filter-out $(cmd_$@),   $(cmd_$(1))) )
else
arg-check = $(if $(strip $(cmd_$@)),,1)
endif

这个变量根据KBUILD_NOCMDDEP的值而异,如果KBUILD_NOCMDDEP=1,则arg-check的值为:

$(if $(strip $(cmd_$@)),,1)

我们发现,这个是个if函数,值要嘛是空,要嘛是1。什么时候是空呢?当$(cmd_$@)展开不为空,则整个值就是空,否则这个值展开为空,整个值就是1。感觉好绕,咱也不知道具体是干什么的,知道有这么个东西,暂且留着吧,兴许后面深入下去,迷雾也会自然揭开。总之,当:

$(strip $(any-prereq) $(arg-check))

展开不为空时,if_changed就等于:

@set -e;                                                             \   
        $(echo-cmd) $(cmd_$(1));                                             \   
        printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd

整理一下,得到:

@set -e; $(echo-cmd) $(cmd_$(1)); printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd

由三个shell命令组成,第一个表示shell发生错误就退出。第二个呢,echo-cmd我们上面分析过,是输出命令字符串。$(cmd_$(1))根据参数而定,由参数和cmd_组成一个新变量进行展开,展开的结果是个shell命令,在这里执行。这里有点类似于回调的概念,具体命令的值是什么,这里不知道,要根据传进来的值而定,你看,多么神奇,一个make脚本都可以设计这么巧妙,不得不惊叹linux这帮人的软件工程水平了,这里不涉及到多少高深的算法,有的只是一些精巧的软件工程设计。第三个很显然,是一个打印命令,打印的值貌似重定向到了一个文件中,单纯看这里其实看不出这打印的是啥,我们暂且不表,知道是个打印就行了。

这一切是怎么联系起来的?

以上是Kbuild.include文件里面的几个重要变量,我们这里简要介绍一下,这些变量后面会使用到。接下来的Makefile.*文件,我们可以自己一个个打开看下,里面具体是些啥。实际上,每个这类文件都聚合了某一类的功能,比如这个Makefile.build聚合了跟构建有关的规则,Makefile.clean聚合了跟clean有关的规则,仅此而已,因为代码比较多,这里就不具体分析了,待后面分析具体的构建目标时,自然会有提及。接下来,我们分析下,这些文件是如何串起来的。我们知道,根目录里面其实就是那个Makefile在其作用,可以把这个Makefile看做总入口,Kbuild.include和Makefile.*都是通过这个总入口引入的。我们大概看下:

根目录Makefile文件的第327行,有如下语句:

# We need some generic definitions (do not try to remake the file).
scripts/Kbuild.include: ;
include scripts/Kbuild.include

这里通过include引入了Kbuild.include文件。那么,那些Makefile.*,我们拿Makefile.build举例,是怎么引入的呢?我们看主Makefile的第836行:

%.imx: %.bin
        $(Q)$(MAKE) $(build)=arch/arm/imx-common $@

这里是构造u-boot.imx的规则,我们看到这条规则的命令,使用了build变量,结合上面对build变量的讨论,我们展开得到:

        $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.build obj=arch/arm/imx-common $@

看到没,这里通过-f选项引入了Makefile.build作为构建makefile。其中Makefile.build里面,会利用obj指定的目录,包含进arch/arm/imx-common/Makefile,从而使用了子目录里面的makefile规则。通过这种方法,就以主Makefile为入口,进入到了kbuild系列规则makefile里面。同理,clean也是这么做的。

Makefile.lib里面同样定义了一堆变量,这些变量可能比较通用,所以聚合在此作为库一样的功能。这个文件是通过include被引入的。比如,Makefile.build第73行:

include scripts/Makefile.lib

引入了该文件定义的所有内容。

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值