学习路线指引(点击解锁) | 知识定位 | 人群定位 |
---|---|---|
Python实战微信订餐小程序 | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
Python量化交易实战 | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
UBOOT版本:uboot2018.03,开发板myimx8mmek240。
Ubootb编译第一步通常是执行make xxx_config,在编译指定顶层目录生成.config文件,这种方式要求厂商提供一个基础的xxx_config文件(通常来说开发者不会通过执行make menuconfig从零开始配置,这个工作过量太大了)。本文接下来的章节主要解析这条指令背后主要做了什么。我是用的开发板执行命令为:make myimx8mmek240-8mm-2g_defconfig
在scripts/Kbuild.include 中定义:
| | ### |
| | # Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj= |
| | # Usage: |
| | # $(Q)$(MAKE) $(build)=dir |
| | build := -f $(srctree)/scripts/Makefile.build obj |
在顶层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 $@ |
(参考: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) |
| | @: |
在顶层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))为空。
在顶层Makefile中,KBUILD_BUILTIN的定义如下:
| | # note:顶层Makefile |
| | KBUILD\_MODULES := |
综上:语句$(if $ (KBUILD_MODULES),$(obj-m) $(modorder-target))为空。
在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)为空。
在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) |
在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 := .
在scripts/Makefile.build有如下定义
| | kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) |
所以 kbuild-dir = ./scripts/basic
在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
而在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
| | # 顶层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不为空时,才会编译到这里。
| | # 顶层Makefile |
| | PHONY += FORCE |
| | FORCE: |
实际上它是一个伪目标,从上面看到,FORCE 既没有依赖的规则,其底下也没有可执行的命令。如果一个规则没有命令或者依赖,而且它的目标不是一个存在的文件名,在执行此规则时,目标总会被认为是最新的。也就是说,这个规则一旦被执行,make 就认为它所表示的目标已经被更新过。当将这样的目标(FORCE)作为一个规则的依赖时(如上),由于依赖总被认为是被更新过的,所以作为依赖所在的规则定义的命令总会被执行。
等价于: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="" -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="" -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 |
| | # |
经过前面的分析可知:当执行make xxx_deconfig时,最终会执行以下两个语句: (1)make -f $(srctree)/scripts/Makefile.build obj=scripts/basic (2)make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_deconfig