1. 前言

 UBOOT版本:uboot2018.03,开发板myimx8mmek240。

2. 概述

Ubootb编译第一步通常是执行make xxx_config,在编译指定顶层目录生成.config文件,这种方式要求厂商提供一个基础的xxx_config文件(通常来说开发者不会通过执行make menuconfig从零开始配置,这个工作过量太大了)。本文接下来的章节主要解析这条指令背后主要做了什么。我是用的开发板执行命令为:make myimx8mmek240-8mm-2g_defconfig

3. build变量的定义

在scripts/Kbuild.include 中定义:

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

4. 目标%config的定义

在顶层Makefile中定义:

# We need some generic definitions (do not try to remake the file).
scripts/Kbuild.include: ;
include scripts/Kbuild.include //注意这个引用 ...... config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@ %config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@

4.1 依赖 scripts_basic

(参考:linux内核Makefile中的变量build— 过渡篇(五)

# 顶层Makefile
# Basic helpers built in scripts/
PHONY += scripts_basic
scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic
$(Q)rm -f .tmp_quiet_recordmcount

展开变量build

# 顶层Makefile
# Basic helpers built in scripts/
PHONY += scripts_basic
scripts_basic:
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.build obj=scripts/basic
$(Q)rm -f .tmp_quiet_recordmcount

 make -f $(srctree)/scripts/Makefile.build obj=scripts/basic的解析如下:

这是一种不指定目标的情况,由于未指定目标,这时会使用Makefile.build中的默认目标__build。然后更进一步,会使用$(obj)/Makefile(scripts/basic/Makefile)中定义的变量来进行目标匹配。

__build在Makefile.build中的构建规则为:

__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
$(subdir-ym) $(always)
@:

4.1.1 语句$(if $ (KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y))

在顶层Makefile中,KBUILD_BUILTIN的定义如下:

# note:顶层Makefile
KBUILD_BUILTIN := 1
export KBUILD_MODULES KBUILD_BUILTIN

该语句展开为:

$(builtin-target) $(lib-target) $(extra-y)

(1)lib-target

# note:顶层Makefile
ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
lib-target := $(obj)/lib.a
endif

在此语句之前obj-y := ;obj-m := ;obj-未定义。因此lib-target为空

(2)builtin-target ​​​​​​​

# note:顶层Makefile
ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)
builtin-target := $(obj)/built-in.o
endif

在此语句之前obj-y := ; obj-m := ;obj-未定义 ; subdir-m := ;并且在所包含的文件中也没有给这些变量增加值。lib-target 为空。因此builtin-target为空

(3)extra-y未定义
综上:语句$(if $ (KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y))为空

4.1.2 语句 $(if $ (KBUILD_MODULES),$(obj-m) $(modorder-target))*

在顶层Makefile中,KBUILD_BUILTIN的定义如下:

# note:顶层Makefile
KBUILD_MODULES :=

综上:语句$(if $ (KBUILD_MODULES),$(obj-m) $(modorder-target))为空

4.1.3 $(subdir-ym)

在scripts/Makefile.lib中,subdir-ym的定义如下:

# note:scripts/Makefile.lib
# Subdirectories we need to descend into
subdir-ym := $(sort $(subdir-y) $(subdir-m))
......
subdir-ym := $(addprefix $(obj)/,$(subdir-ym))

subdir-y与subdir-m都为空。
综上:语句$(subdir-ym)为空

4.1.4 $(always) 重点关注

在scripts/Makefile.build有如下定义

# note:scripts/Makefile.build
# The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)

4.1.4.1 src的定义

在scripts/Makefile.build有如下定义

# note:scripts/Makefile.build
# Modified for U-Boot
prefix := tpl
src := $(patsubst $(prefix)/%,%,$(obj))
ifeq ($(obj),$(src))
prefix := spl
src := $(patsubst $(prefix)/%,%,$(obj))
ifeq ($(obj),$(src))
prefix := .
endif
endif

在命令make -f ./scripts/Makefile.build obj=scripts/basic我们传入了obj=scripts/basic
所以src = obj = scripts/basic, prefix := .

4.1.4.2 kbuild-dir的定义

在scripts/Makefile.build有如下定义

kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))

所以 kbuild-dir = ./scripts/basic

4.1.4.3 kbuild-file的定义

在scripts/Makefile.build有如下定义

kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)

所以kbuild-file = ./scripts/basic/Makefile

所以include $(kbuild-file)即为include ./scripts/basic/Makefile

在./scripts/basic/Makefile中:

# note:scripts/basic/Makefile
hostprogs-y := fixdep
always := $(hostprogs-y) # fixdep is needed to compile other host programs
$(addprefix $(obj)/,$(filter-out fixdep,$(always))): $(obj)/fixdep

所以always = fixdep

4.1.4.4 又在Makefile.build中包含include scripts/Makefile.lib

而在scripts/Makefile.lib中

# note:scripts/Makefile.lib
always := $(addprefix $(obj)/,$(always))

所以最终 always = scripts/basic/fixdep

故__build 展开如下:

__build: scripts/basic/fixdep

@:

1. 在Makefile.build中有如下定义:

# note:scripts/Makefile.build
ifneq ($(hostprogs-y)$(hostprogs-m),)
include scripts/Makefile.host
endif

在./scripts/basic/Makefile中hostprogs-y = fixdep, 所以scripts/Makefile.host被包含进Makefile.build;


在scripts/Makefile.host中

# note:scripts/Makefile.host
__hostprogs := $(sort $(hostprogs-y) $(hostprogs-m))

hostprogs-m为空,所以__hostprogs = fixdep。


在scripts/Makefile.host中

# note:scripts/Makefile.host
# C code
# Executables compiled from a single .c file
host-csingle := $(foreach m,$(__hostprogs), \
$(if $($(m)-objs)$($(m)-cxxobjs)$($(m)-sharedobjs),,$(m)))
......
host-csingle := $(addprefix $(obj)/,$(host-csingle))

因为fixdep-objs与fixdep-cxxobjs都不存在,所以host-csingle = fixdep; 又 host-csingle := $ (addprefix $ (obj)/,$(host-csingle)),所以host-csingle = scripts/basic/fixdep


host-csingle规则如下:

# note:scripts/Makefile.host
$(host-csingle): $(obj)/%: $(src)/%.c FORCE
$(call if_changed_dep,host-csingle)

等价于:

scripts/basic/fixdep:scripts/basic/fixdep.c FORCE
$(call if_changed_dep,host-csingle)

2. if_changed_dep在scripts/Kbuild.include中定义

# note:scripts/Kbuild.include
# Execute the command and also postprocess generated .d dependencies file.
if_changed_dep = $(if $(strip $(any-prereq) $(arg-check) ), \
@set -e; \
$(echo-cmd) $(cmd_$(1)); \
scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).tmp;\
rm -f $(depfile); \
mv -f $(dot-target).tmp $(dot-target).cmd)

2.1 $(strip $(any-prereq) $(arg-check) )
(1) any-prereq在scripts/Kbuild.include中定义

# note:scripts/Kbuild.include
any-prereq = $(filter-out $(PHONY),$?) $(filter-out $(PHONY) $(wildcard $^),$^)

$ ? 表示所有比目标还要新的依赖文件;$ ^ 表示所有的依赖文件,$(filter-out $ (PHONY), $?)就是过滤到比目标还要新的依赖文件中的伪目标,即为 scripts/basic/fixdep.c, $ (filter-out $ (PHONY) $ (wildcard $ ^ ), $^)表示过滤掉所有的依赖文件中的伪目标与存在的依赖文件,这里为空,所以any-prereq = scripts/basic/fixdep.c


(2) arg-check在scripts/Kbuild.include中定义:

# note:scripts/Kbuild.include
ifneq ($(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是在make命令行中定义,我们并没有定义,所以:

arg-check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) $(filter-out $(cmd_$@), $(cmd_$(1))) )

<1> $ (filter-out $ (cmd_ $ (1)), $ (cmd_ $@)) 表示过滤掉 $(cmd_ $@)中符合 $(cmd_ $(1))的项。 $(1)表示if_changed_dep函数的第一个参数host-csingle, $@表示目标文件scripts/basic/fixdep。
<2> cmd_scripts/basic/fixdep并没有定义,所以 $(filter-out $(cmd_ $(1)), $(cmd_ $@))为空;
<3> cmd_host-csingle 在Makefile.host中定义:

cmd_host-csingle    = $(HOSTCC) $(hostc_flags) -o $@ $<  $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F))

所以arg-check = $ (filter-out $ (cmd_$ @), $ (cmd_$ (1))) = $ (HOSTCC) $ (hostc_flags) -o $@ $< $(HOST_LOADLIBES) $(HOSTLOADLIBES_ $(@F))

$(any-prereq) $(arg-check)都为非空,所以:

if_changed_dep = @set -e;   \ /如果任何语句的执行结果不是true则应该退出
$(echo-cmd) $(cmd_$(1)); \
scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).tmp;\
rm -f $(depfile); \
mv -f $(dot-target).tmp $(dot-target).cmd)

2.2 $(echo-cmd) $(cmd_ $ (1))等价于$(echo-cmd) $(cmd_host-csingle)

echo-cmd = $(if $( $(quiet)cmd_$(1)),echo '  $(call escsq, $( $(quiet)cmd_ $(1))) $(echo-why)';)

quiet=quiet_,在顶层Makefile分析过(当然如果你想看到更详细的打印,您可以通过传入V值,来改变), $(cmd_host-csingle)上面分析过,存在,所以:

echo-cmd = echo '  $(call escsq,$(cmd_host-csingle))$(echo-why)';

在scripts/Kbuild.include中:

# Escape single quote for use in echo statements
escsq = $(subst $(squote),'\$(squote)',$1)
ifeq ($(KBUILD_VERBOSE),2)
why = \
$(if $(filter $@, $(PHONY)),- due to target is PHONY, \
$(if $(wildcard $@), \
$(if $(strip $(any-prereq)),- due to: $(any-prereq), \
$(if $(arg-check), \
$(if $(cmd_$@),- due to command line change, \
$(if $(filter $@, $(targets)), \
- due to missing .cmd file, \
- due to $(notdir $@) not in $$(targets) \
) \
) \
) \
), \
- due to target missing \
) \
) echo-why = $(call escsq, $(strip $(why)))
endif

KBUILD_VERBOSE一般我们会采用默认值0(需要调试编译除外),所以 echo-why 为空。

quiet_cmd_host-csingle 	= HOSTCC  $@ //用来打印
cmd_host-csingle = '$(HOSTCC) $(hostc_flags) -o $@ $< $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F))'

$(HOSTCC)为cc,此处不再深入解释,hostc_flags在Makefile.host中定义:

#####
# Handle options to gcc. Support building with separate output directory _hostc_flags = $(HOSTCFLAGS) $(HOST_EXTRACFLAGS) \
$(HOSTCFLAGS_$(basetarget).o) //-Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
_hostcxx_flags = $(HOSTCXXFLAGS) $(HOST_EXTRACXXFLAGS) \
$(HOSTCXXFLAGS_$(basetarget).o)
ifeq ($(KBUILD_SRC),) //KBUILD_SRC在make命令行定义,此处未定义
__hostc_flags = $(_hostc_flags)
__hostcxx_flags = $(_hostcxx_flags)
else
__hostc_flags = -I$(obj) $(call flags,_hostc_flags)
__hostcxx_flags = -I$(obj) $(call flags,_hostcxx_flags)
endif
hostc_flags = -Wp,-MD,$(depfile) $(__hostc_flags)

在scripts/Kbuild.include中:

comma   := ,
dot-target = $(dir $@).$(notdir $@) //scripts/basic/.fixdep
depfile = $(subst $(comma),_,$(dot-target).d) //scripts/basic/.fixdep.d
hostc_flags = -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer

综上
cmd_host-csingle = cc -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -o scripts/basic/fixdep scripts/basic/fixdep.c

所以echo-cmd 的作用是打印quiet_cmd_host-csingle(HOSTCC $ @ )或者cmd_host-csingle(根据顶层MakefileV值决定),$ (cmd_$(1))即为执行cmd_host-csingle生成fixdep同时生成fixdep的依赖文件.fixdep.d

(2.3) scripts/basic/fixdep $(depfile) $@ ’ $(make-cmd)’ > $(dot-target).tmp
等价于:scripts/basic/fixdep scripts/basic/.fixdep.d scripts/basic/fixdep ‘cc -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -o scripts/basic/fixdep scripts/basic/fixdep.c’ > scripts/basic/.fixdep.tmp

(2.4) rm -f $(depfile)
删除scripts/basic/.fixdep.d

(2.5) mv -f $(dot-target).tmp $(dot-target).cmd)
将scripts/basic/.fixdep.tmp重命名为scripts/basic/.fixdep.cmd

总结:生成scripts/basic/fixdep的过程中会先打印cc -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -o scripts/basic/fixdep scripts/basic/fixdep.c同时执行该语句生成fixdep,再用fixdep生成.fixdep.cmd

4.2 依赖 outputmakefile

# 顶层Makefile
# outputmakefile generates a Makefile in the output directory, if using a
# separate output directory. This allows convenient use of make in the
# output directory.
outputmakefile:
ifneq ($(KBUILD_SRC),)
$(Q)ln -fsn $(srctree) source
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
$(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif

上面批注已经说的很清楚了,outputmakefile 在输出目录中生成一个 Makefile,如果使用单独的输出目录。 这允许在输出目录中方便地使用 make。当KBUILD_SRC不为空时,才会编译到这里。

4.3 依赖 FORCE

# 顶层Makefile
PHONY += FORCE
FORCE:

实际上它是一个伪目标,从上面看到,FORCE 既没有依赖的规则,其底下也没有可执行的命令。如果一个规则没有命令或者依赖,而且它的目标不是一个存在的文件名,在执行此规则时,目标总会被认为是最新的。也就是说,这个规则一旦被执行,make 就认为它所表示的目标已经被更新过。当将这样的目标(FORCE)作为一个规则的依赖时(如上),由于依赖总被认为是被更新过的,所以作为依赖所在的规则定义的命令总会被执行。

4.4 规则 $ (Q)$(MAKE) $(build)=scripts/kconfig $@

等价于:make -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig myimx8mmek240-8mm-2g_defconfig

在scripts/kconfig/Makefile中(至于为什么会引用scripts/kconfig/Makefile,参见4.1小节)

# note:scripts/kconfig/Makefile
ifdef KBUILD_KCONFIG
Kconfig := $(KBUILD_KCONFIG)
else
Kconfig := Kconfig
endif %_defconfig: $(obj)/conf
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) //$< = $(obj)/conf # Added for U-Boot (backward compatibility)
%_config: %_defconfig
@:

编译的流程为:
(1)先编译scripts/kconfig/conf可执行文件;
(2)再执行scripts/kconfig/conf --defconfig=arch/…/configs/myimx8mmek240-8mm-2g_defconfig Kconfig语句

编译打印如下

make -f ./scripts/Makefile.build obj=scripts/kconfig myimx8mmek240-8mm-2g_defconfig
cc -Wp,-MD,scripts/kconfig/.conf.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -I/usr/include/ncursesw -DCURSES_LOC="<curses.h>" -DNCURSES_WIDECHAR=1 -DLOCALE -c -o scripts/kconfig/conf.o scripts/kconfig/conf.c
cat scripts/kconfig/zconf.tab.c_shipped > scripts/kconfig/zconf.tab.c
cat scripts/kconfig/zconf.lex.c_shipped > scripts/kconfig/zconf.lex.c
cat scripts/kconfig/zconf.hash.c_shipped > scripts/kconfig/zconf.hash.c
cc -Wp,-MD,scripts/kconfig/.zconf.tab.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -I/usr/include/ncursesw -DCURSES_LOC="<curses.h>" -DNCURSES_WIDECHAR=1 -DLOCALE -Iscripts/kconfig -c -o scripts/kconfig/zconf.tab.o scripts/kconfig/zconf.tab.c
cc -o scripts/kconfig/conf scripts/kconfig/conf.o scripts/kconfig/zconf.tab.o
scripts/kconfig/conf --defconfig=arch/../configs/myimx8mmek240-8mm-2g_defconfig Kconfig
#
# configuration written to .config
#

5 总结

经过前面的分析可知:当执行make xxx_deconfig时,最终会执行以下两个语句:
(1)make -f $(srctree)/scripts/Makefile.build obj=scripts/basic
(2)make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_deconfig

UBOOT编译--- make xxx_deconfig过程详解(一)的更多相关文章

  1. C/C++编译和链接过程详解 (重定向表,导出符号表,未解决符号表)

    详解link  有 些人写C/C++(以下假定为C++)程序,对unresolved external link或者duplicated external simbol的错误信息不知所措(因为这样的错 ...

  2. (转载) C/C++编译和链接过程详解 (重定向表,导出符号表,未解决符号表)

    转载http://blog.csdn.net/neo_ustc/article/details/9024839 有 些人写C/C++(以下假定为C++)程序,对unresolved external ...

  3. uboot引导linux内核过程详解【转】

    http://blog.chinaunix.net/uid-7828352-id-4472376.html 写的不错,尤其是uboot向linux内核传递参数的过程写的比较详细.

  4. uboot主Makefile分析(t配置和编译过程详解)

    1.编译uboot前需要三次make make distcleanmake x210_sd_configmake -j4 make distclean为清楚dist文件. make x210_sd_c ...

  5. uboot配置和编译过程详解【转】

    本文转载自:http://blog.csdn.net/czg13548930186/article/details/53434566 uboot主Makefile分析1 1.uboot version ...

  6. uboot配置和编译过程详解

    根据朱有鹏老师讲解整理 一.uboot主Makefile分析 1.uboot version确定(Makefile的24-29行) include/version_autogenerated.h文件是 ...

  7. Android编译过程详解(一)

    Android编译过程详解(一) 注:本文转载自Android编译过程详解(一):http://www.cnblogs.com/mr-raptor/archive/2012/06/07/2540359 ...

  8. cegui-0.8.2编译过程详解

    cegui 编译过程详解(cegui-0.8.2) cegui配置整了好长时间了,在一位大牛帮助下终于搞定了,网上的教程大多是老版本的,cegui-0.8.2版的配置寥寥无几,现在总结一下,献给正在纠 ...

  9. GCC 概述:C 语言编译过程详解

    Tags: C Description: 关于 GCC 的个人笔记 GCC 概述 对于 GCC 6.1 以及之后的版本,默认使用的 C++ 标准是 C++ 14:使用 -std=c++11 来指定使用 ...

随机推荐

  1. 054_末晨曦Vue技术_处理边界情况之组件之间的循环引用

    组件之间的循环引用 点击打开视频讲解更详细 假设你需要构建一个文件目录树,像访达或资源管理器那样的.你可能有一个 <tree-folder> 组件,模板是这样的: <p> &l ...

  2. 【java】学习路径45-多线程-线程生命周期

    线程分为五大状态:新建.就绪.运行.阻塞.死亡. New,Runnable,Running,Blocked,Terminated. 新建状态(New: 创建好一个系统对象,在调用start()之前,线 ...

  3. Android下的IPC通信方式

    一.Bundle Android的Activity.Service.Receiver都支持在Intent传递Bundle数据,Bundle实现了Parcelable接口, 所以能很方便的在不同进程之间 ...

  4. CDH6.2.0安装并使用基于HBase的Geomesa

    1. 查看CDH 安装的hadoop 和 hbase 对应的版本 具体可以参考以下博客: https://www.cxyzjd.com/article/spark_Streaming/10876290 ...

  5. 2020年12月-第01阶段-前端基础-HTML CSS 项目阶段(二)

    品优购项目(二) 1. 品优购首页布局 命名集合: 名称 说明 快捷导航栏 shortcut 头部 header 标志 logo 购物车 shopcar 搜索 search 热点词 hotwrods ...

  6. KVM更改虚拟机默认存储路径

    Virt默认的虚拟机存储路径是/var/lib/libvirt/images,如下图所示 接下来我们创建一个新的存储池,用来存储新建的虚拟机.存储池的名称为vm, 路径为/home/kvm/ (/ho ...

  7. http和https分别是什么?

    http中文名:超文本传输协议英文名:Hyper Text Transfer Protocol解释:是一个简单的请求-响应协议,它通常运行在TCP之上.它指定了客户端可能发送给服务器什么样的消息以及得 ...

  8. while.for循环和基本数据类型内置方法

    while循环补充说明 流程控制之for循环 基本数据类型内置方法 内容详细 1.死循环 真正的死循环是一旦执行,Cpu的功耗会急速上升 知道系统采取紧急措施 所以 尽量不要让cpu长时间不断运算. ...

  9. Redis核心设计原理(深入底层C源码)

    Redis 基本特性 1. 非关系型的键值对数据库,可以根据键以O(1) 的时间复杂度取出或插入关联值 2. Redis 的数据是存在内存中的 3. 键值对中键的类型可以是字符串,整型,浮点型等,且键 ...

  10. 工作7年收集到的git命令

    概念 git 中的术语解释: 仓库也叫版本库(repository) stage:暂存区,add 后会存到暂存区,commit 后提交到版本库 git 安装 linux 下安装 git 第一种方法:y ...