UBOOT编译--- make xxx_deconfig过程详解(一)
make xxx_deconfig过程详解
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过程详解(一)的更多相关文章
- C/C++编译和链接过程详解 (重定向表,导出符号表,未解决符号表)
详解link 有 些人写C/C++(以下假定为C++)程序,对unresolved external link或者duplicated external simbol的错误信息不知所措(因为这样的错 ...
- (转载) C/C++编译和链接过程详解 (重定向表,导出符号表,未解决符号表)
转载http://blog.csdn.net/neo_ustc/article/details/9024839 有 些人写C/C++(以下假定为C++)程序,对unresolved external ...
- uboot引导linux内核过程详解【转】
http://blog.chinaunix.net/uid-7828352-id-4472376.html 写的不错,尤其是uboot向linux内核传递参数的过程写的比较详细.
- uboot主Makefile分析(t配置和编译过程详解)
1.编译uboot前需要三次make make distcleanmake x210_sd_configmake -j4 make distclean为清楚dist文件. make x210_sd_c ...
- uboot配置和编译过程详解【转】
本文转载自:http://blog.csdn.net/czg13548930186/article/details/53434566 uboot主Makefile分析1 1.uboot version ...
- uboot配置和编译过程详解
根据朱有鹏老师讲解整理 一.uboot主Makefile分析 1.uboot version确定(Makefile的24-29行) include/version_autogenerated.h文件是 ...
- Android编译过程详解(一)
Android编译过程详解(一) 注:本文转载自Android编译过程详解(一):http://www.cnblogs.com/mr-raptor/archive/2012/06/07/2540359 ...
- cegui-0.8.2编译过程详解
cegui 编译过程详解(cegui-0.8.2) cegui配置整了好长时间了,在一位大牛帮助下终于搞定了,网上的教程大多是老版本的,cegui-0.8.2版的配置寥寥无几,现在总结一下,献给正在纠 ...
- GCC 概述:C 语言编译过程详解
Tags: C Description: 关于 GCC 的个人笔记 GCC 概述 对于 GCC 6.1 以及之后的版本,默认使用的 C++ 标准是 C++ 14:使用 -std=c++11 来指定使用 ...
随机推荐
- 【Go实战基础】GO语言是什么,有哪些优势
一.简介 2007年,为了提高在多核.网络机器(networked machines).大型代码库(codebases)的业务场景下的开发效率,Google 首席软件工程师决定创造一种语言那就是 Go ...
- 超实用在线工具!能将文字加密为Emoji表情
试想一下,如果你需要将一段比较敏感的内容发送给你的好友. 但如果这段内容不小心外泄,被别人看到了,可能会带来很多麻烦. 那么,有什么方法能够让传输的文本内容不那么容易被"看破"呢? ...
- Cannot resolve method 'println(java.lang.String)'
jsp文件中println爆红 <% int sum = 0; for (int i = 1; i <=100 ; i++) { sum+=i; } out.println("& ...
- 端口安全 | DHCP snooping
1.端口安全用于防止mac地址的欺骗.mac地址泛洪攻击.主要思想就是在交换机的端口下通过手工或者自动绑定mac地址,这就就只能是绑定的mac地址能够通过. 2.通过静态的端口绑定:将mac地址手工静 ...
- day01-GUI坦克大战01
JavaGUI-坦克大战 1.Java绘图坐标体系 坐标体系介绍:下图说明了一个Java坐标体系.坐标原点位于左上角,以像素为单位.在Java坐标体系中,第一个是x坐标,表示当前位置为水平方向,距离坐 ...
- python 中matplotlib 绘图
python 中matplotlib 绘图 数学建模需要,对于绘图进行简单学习 matpoltlib之类的包安装建议之间用anaconda 绘制一条y=x^2的曲线 #比如我们要绘制一条y=x^2的曲 ...
- 第十三篇:axios网络通信
好了这事一个非常艰巨的任务 解释以下的全部代码 <template> <div class="hello"> <p style="colo ...
- 基于 Gitea 服务端渲染的 Jupyter Notebooks
本指南将向您展示如何通过配置外部渲染器来使 Gitea 呈现 Jupyter Notebooks.当然,你还可以根据本指南来为你的 Gitea 实例配置其他类型的文档渲染器,甚至是二进制文件!相信Gi ...
- 超详细的格式化输出(format的基本玩法)
一.format的基本玩法 一.什么是format format是字符串内嵌(字符串内嵌:字符串中再嵌套字符串,加入双引号或单引号)的一个方法,用于格式化字符串.以大括号{}来标明被替换的字符串 fo ...
- 一文读懂,硬核 Apache DolphinScheduler3.0 源码解析
点亮 ️ Star · 照亮开源之路 https://github.com/apache/dolphinscheduler 本文目录 1 DolphinScheduler的设计与策略 1.1 分布 ...