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_basic,outputmakefile和FORCE中,只有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. SecureCRT通过密钥登录

    转载  https://blog.csdn.net/langkeziju/article/details/53024031 说明:一般的密码方式登录容易被密码暴力破解.所以一般我们会将 SSH 的端口 ...

  2. jQuery file upload上传图片的流程

    先触发_onChange[jquery.fileupload.js] _onChange: function (e) { var that = this, data = { fileInput: $( ...

  3. vue的组件通讯 父传子 -- 子传父-- 兄弟组件的传值 vue的组件传值

    首先文字简单撸一下 父子传子   -------首先在父组件上绑定一个属性,在子组件里用props接收,可以是数组或者是对象 子传父   ------在父组件升上自定义一个方法,在子组件里通过this ...

  4. springboot打war包部署tomcat服务器,以及表单提交数据乱码处理

    小白觉得springboot打成jar包直接使用内嵌的tomcat或jetty容器(java -jar xxx.jar)运行项目不利于定位问题,我还是习惯于查看tomcat或nginx的日志来定位问题 ...

  5. OpenStack Nova 高性能虚拟机之 NUMA 架构亲和

    目录 文章目录 目录 写在前面 计算平台体系结构 SMP 对称多处理结构 NUMA 非统一内存访问结构 MPP 大规模并行处理结构 Linux 上的 NUMA 基本对象概念 NUMA 调度策略 获取宿 ...

  6. java四舍五入及注意点

    package com.example.newtest.test; import java.math.BigDecimal; import java.math.RoundingMode; import ...

  7. Matlab 文件格式化/Matlab Source File Formattor

    由于需要使用到别人编写的Matlab代码文件,但是呢不同的人有不同的风格,有的写得就比较糟糕了. 为了更好地理解代码的内容,一个比较美观的代码会让人身心愉悦. 但是在网上并没有找到一个比较好的实现,此 ...

  8. 【ABAP系列】SAP ABAP 实现FTP的文件上传与下载

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP 实现FTP的文 ...

  9. php php-fpm、nginx和js

    1 php-fpm是什么 php-fpm是php fastCGI process manager的缩写.它是php的进程管理器,对每个请求的处理都是一个进程. php-fpm管理了一个进程池,假如进程 ...

  10. 【扩展事件】跟踪超过3秒的SQL

    msdn 扩展事件:点击打开链接 转自:https://blog.csdn.net/yenange/article/details/52592814 -- 删除事件会话 IF EXISTS(SELEC ...