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. Django学习之Cookie和Session

    一.Cookie 1.Cookie的由来 2.什么是Cookie 3.Cookie的原理 4.查看Cookie 二.Django中操作Cookie 1.获取Cookie 2.设置Cookie 3.删除 ...

  2. [VBA]批量新建指定名称的工作表

    sub 批量新建指定名称的工作表() Dim i As Integer For i = 2 To 10    '根据实际情况修改i大小 Worksheets.Add after:=Worksheets ...

  3. WPF DevExpress Chart控件 界面绑定数据源,不通过C#代码进行绑定

    <Grid x:Name="myGrid" Loaded="Grid_Loaded" DataContext="{Binding PartOne ...

  4. 在性能测试时使用nmon进行监控服务器性能

    在使用Jmeter进行性能测试,可以使用nmon进行服务器的监控. 一.nmon说明 nmon分为工具包和分析包(nmonanalyser) nmon安装很简单,根据服务器版本,下载相应的版本后,进行 ...

  5. 【Java基础】内部类

    非静态内部类不能拥有静态变量 为什么 下面这段代码,如果Lazyholder没有static修饰,则编译不过 class Singleton2 { private static class LazyH ...

  6. WebMvcConfigurer 与 WebMvcConfigurationSupport避坑指南

    我们知道,在Spring Boot 2.0后用自己的的配置类继承WebMvcConfigurerAdapter时,idea会提示这个类已经过时了. 通常情况下我们会采用下面两种代替方案: 实现WebM ...

  7. Java ——异常处理

    本节重点思维导图 详细戳——> 异常处理

  8. LeetCode算法题-Maximize Distance to Closest Person(Java实现)

    这是悦乐书的第328次更新,第351篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第198题(顺位题号是849).在一排座位中,1表示一个人坐在该座位上,0表示座位是空的 ...

  9. if you wanna the rainbow, you have to deal with the rain.

    bulk. n. 大量 reluctant. adj. 不情愿的 terrorist. n. 恐怖分子 recognition. n. 认出 tout.v. 兜售 conceal.v. 隐藏 dras ...

  10. Android——LruCache源码解析

    以下针对 Android API 26 版本的源码进行分析. 在了解LruCache之前,最好对LinkedHashMap有初步的了解,LruCache的实现主要借助LinkedHashMap.Lin ...