https://blog.csdn.net/q_z_r_s/article/details/80718518

从u-boot-2014.10版本引入Kbuild系统之后,Makefile的管理和组织跟以前版本的代码有了很大的不同,这使Makefile变得更加复杂。整个Makefile中,include很多其它不同用途的Makefile,各种目标和依赖也很多,因此要想搞清楚make的执行过程很困难;使用u-boot之前首先是对其进行配置,命令为:

make xxx_defconfig

上述命令执行之后会生成对应的.config文件,接下来就可以进行最后的编译工作了,直接输入make即可。
配置流程分析

执行make xxx_defconfig命令时,u-boot根目录下的Makefile中有唯一的规则匹配目标:

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

其中$(build)在kbuild.include中定义:

build := -f $(srctree)/scripts/Makefile.build obj

依赖scripts_basic:

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

可见scripts_basic没有进一步的依赖,展开后规则如下:

scripts_basic:
        $(Q) make -f ./scripts/Makefile.build obj=scripts/basic
        $(Q) rm -f .tmp_quiet_recordmcount

简而言之,scripts_basic规则

scripts_basic:
        $(Q) make -f ./scripts/Makefile.build obj=scripts/basic

的最终结果就是编译scripts/basic/fixdep.c生成主机上的可执行文件fixdep。

第二次调用scripts/Makefile.build进行编译
文件script/Makefile.build的开头会根据传入的obj=scripts/kconfig参数设置src=scripts/kconfig。然后搜寻$(srctree)/$(src)子目录下的makefile,由于src=scripts/kconfig参数不同于第一次调用的参数(src=scripts/basic),此处包含的makefile也不同于第一次的makefile了:

# 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)

这里替换展开后相当于:

include ./scripts/kconfig/Makefile

对于这里传入的xxx_defconfig,匹配的目标是:

%_defconfig: $(obj)/conf
        $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)

展开为:

xxx_defconfig: scripts/kconfig/conf
        $(Q)scripts/kconfig/conf  --defconfig=arch/../configs/xxx_defconfig Kconfig

此处目标xxx_defconfig依赖于scripts/kconfig/conf,接下来检查并生成依赖。

hostprogs-y := conf nconf mconf kxgettext qconf gconf

hostprogs-y指出conf被定义为主机上执行的程序,其依赖于另外两个文件:

conf-objs   := conf.o  zconf.tab.o

通过编译conf.c和zconf.tab.c生成conf-objs,并链接为scripts/kconfig/conf。

生成依赖后就是执行目标的命令了:

$(Q)scripts/kconfig/conf  --defconfig=arch/../configs/xxx_defconfig Kconfig

conf工具从根目录下开始树状读取默认的Kconfig文件,分析其配置并保存在内存中。分析完默认的Kconfig后再读取指定文件(即arch/../configs/xxx_defconfig)更新得到最终的符号表,并输出到.config文件中。

u-boot-2016 make配置过程分析

概述

本文基于u-boot树莓派3代配置过程进行分析,环境如下:
编译环境:Ubuntu 14.04 LTS
编译工具:arm-Linux-gnueabi-gcc

代码版本:u-boot v2016.09
配置文件:rpi_3_32b_defconfig

u-boot自v2014.10版本开始引入KBuild系统,Makefile的管理和组织跟以前版本的代码有了很大的不同,其Makefile更加复杂。整个Makefile中,嵌套了很多其它不同用途的Makefile,各种目标和依赖也很多,make分析很容易陷进去,让人摸不着头脑。

本文涉及的配置命令:

make rpi_3_32b_defconfig

实例执行配置命令

u-boot的编译跟kernel编译一样,分两步执行:
- 第一步:配置,执行make xxx_defconfig进行各项配置,生成.config文件
- 第二部:编译,执行make进行编译,生成可执行的二进制文件u-boot.bin或u-boot.elf

先从简单的make defconfig配置过程着手吧。
命令行输入:

make rpi_3_32b_defconfig V=1

编译输出如下:

配置命令参数说明:
- rpi_3_32b_defconfig 是树莓派3代32位编译的配置文件
- V=1 指示编译显示详细的输出。默认V=0,编译仅显示必要的简略信息

从输出的log看,make rpi_3_32b_defconfig的执行主要分为3个部分,见图上的标示:
- 1. 执行make -f ./scripts/Makefile.build obj=scripts/basic,编译生成scripts/basic/fixdep工具

- 2. 执行make -f ./scripts/Makefile.build obj=scripts/kconfig rpi_3_32b_defconfig编译生成scripts/kconfig/conf工具

- 3. 执行scripts/kconfig/conf --defconfig=arch/../configs/rpi_3_32b_defconfig Kconfig生成最终的.config配置文件

跟原始的代码相比,执行make rpi_3_32b_defconfig后文件夹内容的变化如下:

被后续编译用到的文件是.config。

详细配置流程分析

言归正传,整个配置流程的目的就是为了生成.config文件,下面详细分析.config文件是如何一步一步生成的。

Makefile的核心是依赖和命令。对于每个目标,首先会检查依赖,如果依赖存在,则执行命令更新目标;如果依赖不存在,则会以依赖为目标,先生成依赖,待依赖生成后,再执行命令生成目标。

1. 顶层make defconfig规则

执行make xxx_defconfig命令时,u-boot根目录下的Makefile中有唯一的规则匹配目标:

  1. %config: scripts_basic outputmakefile FORCE
  2. $(</span>Q)<span class="hljs-variable">$(MAKE) $(</span>build)=scripts/kconfig <span class="hljs-variable">$@

对于目标,rpi_3_32b_defconfig,展开则有:

    1. rpi_3_32b_defconfig: scripts_basic outputmakefile FORCE
    2. $(</span><span class="hljs-constant">Q</span>)<span class="hljs-variable">$(MAKE) $(</span>build)=scripts/kconfig rpi_3_32b_defconfig</div></div></li></ol></code><ul class="pre-numbering" style="opacity: 0;"><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li></ul><div class="hljs-button" data-title="复制"></div></pre><ul class="pre-numbering" style="opacity:0;"><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li></ul><div class="save_code tracking-ad" style="display:none;"><a target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets_01.png" alt="save_snippets_01.png"></a></div><ul class="pre-numbering"><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li></ul><p>其中<code>$(build)在kbuild.include中定义:

      build := -f $(srctree)/scripts/Makefile.build obj

      i. 依赖scripts_basic

      依赖scripts_basic:

      1. # Basic helpers built in scripts/
      2. PHONY += scripts_basic
      3. scripts_basic:
      4. $(</span><span class="hljs-constant">Q</span>)<span class="hljs-variable">$(MAKE) $(</span>build)=scripts/basic</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-variable">$(Q)rm -f .tmp_quiet_recordmcount

      可见scripts_basic没有进一步的依赖,展开后规则如下:

      1. scripts_basic:
      2. $(Q) make -f ./scripts/Makefile<span class="hljs-preprocessor">.build</span> obj=scripts/basic</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> $(Q) rm -f .tmp_quiet_recordmcount

      ii. 依赖outputmakefile

      依赖outputmakefile:

      1. PHONY += outputmakefile
      2. # outputmakefile generates a Makefile in the output directory, if using a
      3. # separate output directory. This allows convenient use of make in the
      4. # output directory.
      5. outputmakefile:
      6. ifneq ($(</span>KBUILD_SRC),)</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-variable">$(Q)ln -fsn $(</span>srctree) <span class="hljs-keyword">source</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="8"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-variable">$(Q)$(</span>CONFIG_SHELL) <span class="hljs-variable">$(srctree)/scripts/mkmakefile \
      7. $(</span>srctree) <span class="hljs-variable">$(objtree) $(</span>VERSION) <span class="hljs-variable">$(PATCHLEVEL)
      8. endif

      outputmakefile也没有进一步的依赖。
      如果执行如下命令:

      make rpi_3_32b_defconfig O=out

      那么所有生成的目标都将放到out目录,此时会通过outputmakefile导出一个makefile到out目录进行编译。

      由于在当前目录下编译,$(KBUILD_SRC)

      为空,不需要导出makefile文件,outputmakefile为空目标。

      iii. 依赖FORCE

      依赖FORCE:

      1. PHONY += FORCE
      2. FORCE:

      FORCE被定义为一个空目标。
      如果一个目标添加FORCE依赖,每次编译都会去先去执行FORCE(实际上什么都不做),然后运行命令更新目标,这样就能确保目标每次都会被更新。在这里也就保证目标rpi_3_32b_defconfig的命令:

      $(</span><span class="hljs-constant">Q</span>)<span class="hljs-variable">$(MAKE) $(build)=scripts/kconfig rpi_3_32b_defconfig

      总是能够被执行。

      以上是rpi_3_32b_defconfig

      的所有依赖,分析完依赖后再分析命令。

      2. 顶层make defconfig的命令

      i. 依赖scripts_basic的命令

      目标rpi_3_32b_defconfig

      的三个依赖scripts_basicoutputmakefileFORCE中,只有scripts_basic需要执行命令,如下

      1. scripts_basic:
      2. $(Q) make -f ./scripts/Makefile<span class="hljs-preprocessor">.build</span> obj=scripts/basic</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> $(Q) rm -f .tmp_quiet_recordmcount

      然后Make命令会转到文件scripts/Makefile.build

      去执行。

      第一次调用scripts/Makefile.build进行编译
      文件script/Makefile.build

      的开头会根据传入的obj=scripts/basic参数设置src=scripts/basic:

      1. prefix := tpl
      2. src := $(</span>patsubst <span class="hljs-variable">$(prefix)/%,%,$(</span>obj))</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">ifeq (<span class="hljs-variable">$(obj),$(</span>src))</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">prefix := spl</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">src := <span class="hljs-variable">$(patsubst $(</span>prefix)/<span class="hljs-variable">%,</span><span class="hljs-variable">%,</span><span class="hljs-variable">$(obj))
      3. ifeq ($(</span>obj),<span class="hljs-variable">$(src))
      4. prefix := .
      5. endif
      6. endif

      然后搜寻$(srctree)/$(src)

      子目录下的makefile,并包含进来:

      1. # The filename Kbuild has precedence over Makefile
      2. kbuild-dir := $(</span><span class="hljs-keyword"><span class="hljs-keyword">if</span></span> <span class="hljs-variable">$(filter /%,$(</span>src)),<span class="hljs-variable">$(src),$(</span>srctree)/<span class="hljs-variable">$(src))
      3. kbuild-file := $(</span><span class="hljs-keyword"><span class="hljs-keyword">if</span></span> <span class="hljs-variable">$(wildcard $(</span>kbuild-dir)/Kbuild),<span class="hljs-variable">$(kbuild-dir)/Kbuild,$(</span>kbuild-dir)/Makefile)</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">include</span> <span class="hljs-variable">$(kbuild-file)

      这里展开替换后相当于:

      include ./scripts/basic/Makefile

      文件scripts/basic/Makefile

      中定义了编译在主机上执行的工具fixdep:

      1. hostprogs-y := fixdep
      2. always := $(</span>hostprogs-y)</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-comment"><span class="hljs-comment"># fixdep is needed to compile other host programs</span></span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-variable">$(addprefix $(</span>obj)/,<span class="hljs-variable">$(filter-out fixdep,$(</span>always)))<span class="hljs-symbol">:</span> <span class="hljs-variable">$(obj)/fixdep

      工具fixdep用于更新每一个生成目标的依赖文件*.cmd

      上面定义的这个$(always)

      scripts/Makefile.build里会被添加到targets中:

      targets += $(extra-y)</span> <span class="hljs-variable">$(MAKECMDGOALS) $(always)

      关于如何编译主机上可执行的程序,会在另外的文章中分析。

      简而言之,scripts_basic

      规则

      1. scripts_basic:
      2. $(Q) make -f ./scripts/Makefile.build obj=scripts/basic

      的最终结果就是编译scripts/basic/fixdep.c

      生成主机上的可执行文件fixdep。至于为什么要编译fixdep和如何使用fixdep,会在另外的文章中分析。

      ii. 顶层rpi_3_32b_defconfig的命令

      完成对依赖scripts_basic

      的更新后,接下来就是执行顶层目标的命令完成对rpi_3_32b_defconfig的更新,展开后的规则如下:

      1. rpi_3_32b_defconfig: scripts_basic outputmakefile FORCE
      2. make -f ./scripts/Makefile.build obj= scripts/kconfig rpi_3_32b_defconfig

      其中$(build)

      kbuild.include中定义:

      1. ###
      2. # Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
      3. # Usage:
      4. # $(Q)$(MAKE) $(build)=dir</span></span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">build <span class="hljs-symbol">:</span>= -f $(srctree)/scripts/Makefile.build obj

      这个make命令会第二次转到scripts/Makefile.build

      去执行。

      第二次调用scripts/Makefile.build进行编译
      文件script/Makefile.build

      的开头会根据传入的obj=scripts/kconfig参数设置src=scripts/kconfig。然后搜寻$(srctree)/$(src)子目录下的makefile,由于src=scripts/kconfig参数不同于第一次调用的参数(src=scripts/basic),此处包含的makefile也不同于第一次的makefile了:

      1. # The filename Kbuild has precedence over Makefile
      2. kbuild-dir := $(</span><span class="hljs-keyword"><span class="hljs-keyword">if</span></span> <span class="hljs-variable">$(filter /%,$(</span>src)),<span class="hljs-variable">$(src),$(</span>srctree)/<span class="hljs-variable">$(src))
      3. kbuild-file := $(</span><span class="hljs-keyword"><span class="hljs-keyword">if</span></span> <span class="hljs-variable">$(wildcard $(</span>kbuild-dir)/Kbuild),<span class="hljs-variable">$(kbuild-dir)/Kbuild,$(</span>kbuild-dir)/Makefile)</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-keyword">include</span> <span class="hljs-variable">$(kbuild-file)

      这里替换展开后相当于:

      include ./scripts/kconfig/Makefile

      文件scripts/kconfig/Makefile

      中定义了所有匹配%config的目标:

      1. PHONY += xconfig gconfig menuconfig config silentoldconfig update-po-config \
      2. localmodconfig localyesconfig
      3.  
      4. PHONY += oldnoconfig savedefconfig defconfig
      5.  
      6. PHONY += kvmconfig
      7.  
      8. PHONY += tinyconfig

      对于这里传入的rpi_3_32b_defconfig

      ,匹配的目标是:

      1. %_defconfig: $(</span>obj)/conf</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-variable">$(Q)$&lt;</span> <span class="hljs-variable">$(silent) --defconfig=arch/$(</span>SRCARCH)/configs/<span class="hljs-variable">$@ $(Kconfig)

      展开为:

      1. rpi_3_32b_defconfig: scripts/kconfig/conf
      2. $(Q)scripts/kconfig/conf --defconfig=arch/../configs/rpi_3_32b_defconfig Kconfig

      此处目标rpi_3_32b_defconfig

      依赖于scripts/kconfig/conf,接下来检查并生成依赖。

      hostprogs-y := conf nconf mconf kxgettext qconf gconf

      hostprogs-y

      指出conf被定义为主机上执行的程序,其依赖于另外两个文件:

      conf-objs   := conf.o  zconf.tab.o

      通过编译conf.c

      zconf.tab.c生成conf-objs,并链接为scripts/kconfig/conf

      生成依赖后就是执行目标的命令了:

      $(Q)scripts/kconfig/conf  --defconfig=arch/../configs/rpi_3_32b_defconfig Kconfig

      工具scripts/kconfig/conf

      的操作会在单独的文章中分析,此处只做简要的说明:

      conf

      工具从根目录下开始树状读取默认的Kconfig文件,分析其配置并保存在内存中。分析完默认的Kconfig后再读取指定文件(即arch/../configs/rpi_3_32b_defconfig)更新得到最终的符号表,并输出到.config文件中。

      至此完成了make rpi_3_32b_defconfig

      执行配置涉及的所有依赖和命令的分析。

      make defconfig配置流程简图

      整个配置流程阐述得比较啰嗦,可以用一个简单的依赖图表示,如下:

      (可以将图片拖到浏览器的其他窗口看大图)

【u-boot-2018.05】make配置过程分析的更多相关文章

  1. Spring Boot之实现自动配置

    GITHUB地址:https://github.com/zhangboqing/springboot-learning 一.Spring Boot自动配置原理 自动配置功能是由@SpringBootA ...

  2. 【spring boot】spring boot中使用定时任务配置

    spring boot中使用定时任务配置 =============================================================================== ...

  3. 4、Spring Boot 2.x 自动配置原理

    1.4 Spring Boot 自动配置原理 简介 spring boot自动配置功能可以根据不同情况来决定spring配置应该用哪个,不应该用哪个,举个例子: Spring的JdbcTemplate ...

  4. 黑马_13 Spring Boot:05.spring boot 整合其他技术

    13 Spring Boot: 01.spring boot 介绍&&02.spring boot 入门 04.spring boot 配置文件 05.spring boot 整合其他 ...

  5. 学记:为spring boot写一个自动配置

    spring boot遵循"约定优于配置"的原则,使用annotation对一些常规的配置项做默认配置,减少或不使用xml配置,让你的项目快速运行起来.spring boot的神奇 ...

  6. Spring Boot 探索系列 - 自动化配置篇

    26. Logging Prev  Part IV. Spring Boot features  Next 26. Logging Spring Boot uses Commons Logging f ...

  7. Spring Boot 2.0 教程 | 配置 Undertow 容器

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 文章首发于个人网站 https://ww ...

  8. Microsoft Artificial Intelligence Conference(2018.05.21)

    时间:2018.05.21地点:北京嘉丽大酒店

  9. Spring Boot实践——用外部配置填充Bean属性的几种方法

    引用:https://blog.csdn.net/qq_17586821/article/details/79802320 spring boot允许我们把配置信息外部化.由此,我们就可以在不同的环境 ...

随机推荐

  1. Spring boot 自定义一个starter pom

    用过springboot的自动配置会觉得非常方便,我们完全可以自己写一个starter pom,这样不仅可以有自动配置功能,而且具有更通用的的耦合度低的配置, 新建一个starter的maven项目, ...

  2. c++实验10 图的应用实验

    大体与上次实验相同,特点为图是邻接表存储结构 --博客后半部分有程序的所有代码-- 1.图邻接表存储结构表示及基本操作算法实现 所加载的库函数或常量定义及类的定义: #include "Se ...

  3. Vue知识整理11:列表渲染(v-for来实现)

    简单的v-for结构显示迭代数据 通过value别名 显示下面各个属性值 通过index 和key获取同类数组索引,或者不同属性的key属性名

  4. 2018 icpc 沈阳

    https://codeforces.com/gym/101955 J 签到 #include<iostream> #include<cstring> #include< ...

  5. delphi 跨版本DLL调用嵌入窗体实现

    delphi 能实现把别的DLL的窗体句柄查到后,贴到PANL之中,此类文章网上不少,而如果是delphi不同版本开发的DLL互调时,一些控件内部的定义有所区别,因为无法(至少目前我觉得理论上不可行) ...

  6. 测开之路一百四十七:用WTForms实现编辑功能

    接上一篇的内容 把原先的数据库模型全部给默认值,后面form赋值的时候就不用传位置参数了 把视图逻辑修改一下 # 视图层from datetime import datetimefrom flask. ...

  7. PyQt5学习一---环境的安装和配置

    PyQt5环境安装 1.Python环境(我在练习的时候是用的Python3.6.8) 2.PyQt5安装 首先安装sip pip install sip 然后安装PyQt5-tools pip in ...

  8. mysql数据库delete数据时不支持表别名!!!

    mysql数据库delete数据时不支持表别名!!! mysql delete时候 提示语法错误!如下sql: 去掉 表别名的时候: 正确的写法例如: DELETE FROM COMMENTS_REP ...

  9. 各种CNN模型

    Resnet: model_urls = { 'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth', 'res ...

  10. GCC 环境变量 & eclipse CDT 头文件配置

     转:http://blog.csdn.net/statdm/article/details/7751000 GCC 环境变量 & eclipse CDT 头文件配置   在unix 下使用e ...