06_Uboot顶层Makefile分析_前期所做内容

目录

U-Boot顶层Makefile分析

版本号

MAKEFLAGS变量

命令输出

静默输出

设置编译结果输出目录

代码检查

模块编译

获取主机架构和系统

设置目标架构、交叉编译器和配置文件

调用scripts/Kbuild.include

交叉编译工具变量设置

导出其他变量


U-Boot顶层Makefile分析

在阅读uboot源码之前,肯定是要先看一下顶层Makefile,分析gcc版本代码的时候一定是先从顶层Makefile开始的,然后再是子Makefile,这样通过层层分析Makefile即可了解整个工程的组织结构。顶层Makefile也就是uboot根目录下的Makefile文件,由于顶层Makefile文件内容比较多,所以我们将其分开来看。

版本号

顶层Makefile一开始是版本号,内容如下(为了方便分析,顶层Makefile代码段前段行号采用 Makefile中的行号,因为uboot会更新,因此行号可能会与你所看的顶层Makefile有所不同):

 VERSION是主版本号, PATCHLEVEL是补丁版本号, SUBLEVEL是次版本号,这三个一,起构成了uboot的版本号,比如当前的uboot版本号就是"2016.03"。EXTRAVERSION是附加版本信息,NAME是和名字有关的,一般不使用这两个。

MAKEFLAGS变量

make是支持递归调用的,也就是在Makefile中使用"make”命令来执行其他的Makefile文件,一般都是子目录中的Makefile文件。假如在当前目录下存在一个"subdir”子目录,这个子目录中又有其对应的Makefile文件,那么这个工程在编译的时候其主目录中的Makefile就可以调用子目录中的Makefile,以此来完成所有子目录的编译。主目录的Makefile可以使用如下代码来编译这个子目录:

$(MAKE) -C subdir

$(MAKE)就是调用"make”命令, -C指定子目录。有时候我们需要向子make传递变量,这个时候使用"export”来导出要传递给子make的变量即可,如果不希望哪个变量传递给子make的话就使用“unexport”来声明不导出: 

export VARIABLE ......  //导出变量给子make。

unexport VARIABLE .......   //不导出变量给子make。

 

有两个特殊的变量:“SHELL”和"MAKEFLAGS”,这两个变量除非使用“unexport”声明,否则的话在整个make的执行过程中,它们的值始终自动的传递给子make。在uboot的主Makefile中有如下代码: 

 上述代码使用“+=”来给变量MAKEFLAGS追加了一些值,“-rR”表示禁止使用内置的隐含规则和变量定义,“--include-dir”指明搜索路径,”$(CURDIR)”表示当前目录。

 

命令输出

uboot默认编译是不会在终端中显示完整的命令,都是短命令,如图所示:

在终端中输出短命令虽然看起来很清爽,但是不利于分uboot的编译过程。可以通过设置变量"V=1"来实现完整的命令输出,这个在调试uboot的时候很有用,结果如图所示: 

 顶层Makefile中控制命令输出的代码如下:

上述代码中先使用ifeq来判断"$(origin V)"和"command line"是否相等。这里用到了Makefile中的函数origin,origin和其他的函数不一样,它不操作变量的值,origin用于告诉你变量是哪来的,语法为: 

$(origin <variable>) 

 variable是变量名, origin函数的返回值就是变量来源,因此$(origin V)就是变量V的来源。如果变量V是在命令行定义的那么它的来源就是"command line",这样"$(origin V)"和"commandline"就相等了。当这两个相等的时候变量 KBUILD_VERBOSE就等于V的值,比如在命令行中输入"V=1"的话那么KBUILD-VERBOSE=1。如果没有在命令行输入V的话KBUILD_VERBOSE=0。

第80行判断KBUILD_VERBOSE是否为1,如果KBUILD_VERBOSE为1的话变量quiet,和Q都为空,如果KBUILD_VERBOSE=0的话变量quiet为“quiet_“,变量Q为“@”,综上所述:

V=1的话:

 

V=0或者命令行不定义V的话: 

 

Makefile中会用到变量quiet和Q来控制编译的时候是否在终端输出完整的命令,在顶层Makefile中有很多如下所示的命令: 

 $(Q)$(MAKE) $(build)=tools

 如果V=0的话上述命令展开就是"@make $(build)-tools", make在执行的时候默认会在终端输出命令,但是在命令前面加上“@”就不会在终端输出命令了。当 V=1 的时候Q就为空,上述命令就是"make $(build)-tools”,因此在make执行的过程,命令会被完整的输出在终端上。

有些命令会有两个版本,比如:

quiet_cmd_sym ?= SYM  $@

cmd_sym ?= $(OBJDUMP) -t $<> $@

 

 sym命令分为"quiet_cmd_sym”和"cmd_sym”两个版本,这两个命令的功能都是一样的,区别在于make执行的时候输出的命令不同。quiet_cmd_xxx命令输出信息少,也就是短命令,而cmd_xxx命令输出信息多,也就是完整的命令。

如果变量quiet为空的话,整个命令都会输出。如果变量quiet为“quiet_”的话,仅输出短版本。如果变量quiet为"silent_”的话,整个命令都不会输出。

 

 

静默输出

设置V=0或者在命令行中不定义V的话,编译uboot的时候终端中显示的短命令,但是还是会有命令输出,有时候我们在编译uboot的时候不需要输出命令,这个时候就可以使用uboot的静默输出功能。编译的时候使用"make-s"即可实现静默输出,顶层Makefile中相应的代码如下:

 

第91行判断当前正在使用的编译器版本号是否为4.x,判断$(filter 4.%,$(MAKE_VERSION))和“”(空)是否相等,如果不相等的话就成立,执行里面的语句。也就是说$(filter4.%,$(MAKE_VERSION))不为空的话条件就成立,这里用到了Makefile中的filter函数,这是个过滤函数,函数格式如下:

$(filter <pattern...>,<text>)

filter函数表示以pattern模式过滤text字符串中的单词,仅保留符合模式pattern的单词,可以有多个模式。函数返回值就是符合pattern的字符串。因此$(filter 4.%,$(MAKE_VERSION))的含义就是在字符串"MAKE-VERSION”中找出符合"4.%”的字符(%为通配符),MAKE_VERSION是make工具的版本号,ubuntu16.04里面默认自带的make工具版本号为 4.1,大家可以输入“make-v”查看。因此$(filter 4.%,$(MAKE_VERSION))不为空,条件成立,执行92~94行的语句。

第92行也是一个判断语句,如果$(filter %s,$(firstword x$(MAKEFLAGS)))不为空的话条件,成立,变量quiet等于"silent”。这里也用到了函数filter,在$(firstword xS(MAKEFLAGS)))中过滤出符合"%s”的单词。到了函数firstword,函数firstword是获取首单词,函数格式如下:

 $(firstword <text>)

firstword函数用于取出text字符串中的第一个单词,函数的返回值就是获取到的单词。当使用“make-s”编译的时候, “-s”会作为MAKEFLAGS变量的一部分传递给Makefile。在顶层Makfile中添加如图所示的代码: 

 图中的两行代码用于输出$(firstword x$(MAKEFLAGS))的结果,最后修改文件mx6ull alientek emmc.sh,在里面加入"-s”选项,结果如图所示:

 修改完成以后执行mx6ull_alientek_emmc.sh,结果如图所示:

 从图可以看出第一个单词是"xrRs",将$(filter %s ,$(firstword x$(MAKEFLAGS))展开就是$(filter %s, xrRs),而$(filter %s, xrRs)的返回值肯定不为空,条件成立, quiet-silent_第101行使用export导出变量quiet、Q和KBUILD_VERBOSE。

 

设置编译结果输出目录

uboot可以将编译出来的目标文件输出到单独的目录中,在make的时候使用“O”来指定输出目录,比如"make O-out”就是设置目标文件输出到out目录中。这么做是为了将源文件和编译产生的文件分开,当然也可以不指定0参数,不指定的话源文件和编译产生的文件都在同一个目录内,一般我们不指定0参数。顶层Makefile中相关的代码如下:

 第124行判断"O”是否来自于命令行,如果来自命令行的话条件成立, KBUILD_OUTPUT就为$(O),因此变量KBUILD_OUTPUT就是输出目录。

第135行判断KBUILD_OUTPUT是否为空。

第139行调用mkdir命令,创建KBUILD_OUTPUT目录,并且将创建成功以后的绝对路径赋值给KBUILD_OUTPUT。至此,通过O指定的输出目录就存在了。

 

代码检查

uboot支持代码检查,使用命令"make C=1”使能代码检查,检查那些需要重新编译的文件。“make C-2”用于检查所有的源码文件,顶层Makefile中的代码如下:

第176行判断C是否来源于命令行,如果C来源于命令行,那就将C赋值给变量KBUILD_CHECKSRC,如果命令行没有C的话KBUILD_CHECKSRC就为0。 

 

 

模块编译

在uboot中允许单独编译某个模块,使用命令"make M=dir”即可,旧语法"makeSUBDIRS=dir”也是支持的。顶层Makefile中的代码如下:

 第186行判断是否定义了SUBDIRS, 如果定义了SUBDIRS ,变量KBUILD_EXTMOD=SUBDIRS,这里是为了支持老语法“make SUBIDRS=dir”

第190 行判断是否在命令行定义了M,如果定义了的话KBUILD_EXTMOD=$(M)

第197行判断KBUILD_EXTMOD时为空,如果为空的话目标_all 依赖all,因此要先编译出all。否则的话默认目标_all依赖modules,要先编译出modules,也就是编译模块。一般情况下我们不会在uboot中编译模块,所以此处会编译all这个目标。

第203行判断KBUILD_SRC是否为空,如果为空的话就设置变量sretree为当前目录,即srctree为“.”,一般不设置 KBUILD_SRC。

第214行设置变量objtree为当前目录。

第215和216行分别设置变量src和obj,都为当前目录。

第218行设置VPATH。

第220行导出变量scrtree、objtree和VPATH。

获取主机架构和系统

接下来顶层Makefile会获取主机架构和系统,也就是我们电脑的架构和系统,代码如下:

 第227行定义了一个变量HOSTARCH,用于保存主机架构,这里调用shell命令“uname-m获取架构名称,结果如图所示:

 从图可以看出当前电脑主机架构为“x86_64", shell中的"1”表示管道,意思是将左边的输出作为右边的输入, sed-e是替换命令,"sed-e s/i.86/x86/”表示将管道输入的字符串中的"i.86"替换为"x86",其他的"sed-es"命令同理。对于我的电脑而言, HOSTARCH-x86_64。

第237行定义了变量HOSTOS,此变量用于保存主机OS的值,先使用shell命令"uname-s”来获取主机OS,结果如图所示:

从图可以看出此时的主机OS为“Linux”,使用管道将“Linux”作为后面“tr'[:upper:]"[:lower:]"的输入, "tr'[:upper:]'"[:lower:]'"表示将所有的大写字母替换为小写字母,因此得到“linux”。最后同样使用管道,将“linux”作为"sed-e'sA(cygwinl).*/cygwin/”的输入,用于将cygwin.*替换为 cygwin*因此,HOSTOS=linux。

第240行导出HOSTARCH=x86_64, HOSTOS=linux。

设置目标架构、交叉编译器和配置文件

编译uboot 的时候需要设置目标板架构和交叉编译器,

“make ARCH=armCROSS_COMPILE=arm-linux-gnueabihf-”就是用于设置ARCH和 CROSS_COMPILE,在顶层Makefile中代码如下:

 第245行判断HOSTARCH和ARCH这两个变量是否相等,主机架构(变量HOSTARCH)是x86-64,而我们编译的是ARM版本uboot,肯定不相等,所以CROsS_COMPILE= arm-linuxgnueabihf-。从示例代码可以看出,每次编译 uboot 的时候都要在make 命令后面设置ARCH 和 CROSS_COMPILE,使用起来很麻烦,可以直接修改顶层Makefile,在里面加入 ARCH和CROSS COMPILE的定义,如图所示:

 按照图所示,直接在顶层Makefile里面定义ARCH和CROSS_COMPILE,这样就不用每次编译的时候都要在make命令后面定义ARCH和CROSS_COMPILE。

继续回到示例代码中,第249行定义变量KCONFIG-CONFIG, uboot是可以配置的,这里设置配置文件为.config, .config默认是没有的,需要使用命令“make xxx_defconfig"对uboot 进行配置,配置完成以后就会在uboot根目录下生成.config。默认情况下.config和xxx_defconfig 内容是一样的,因为.config就是从xxx_defconfig复制过来的。如果后续自行调整了uboot的一些配置参数,那么这些新的配置参数就添加到了.config中,而不是xxx_defconfig。相当于 xxx_defconfig只是一些初始配置,而.config里面的才是实时有效的配置。

调用scripts/Kbuild.include

 示例代码中使用"include"包含了文件scripts/Kbuild.include,此文件里面定义了很多变量,如图所示:

 在uboot的编译过程中会用到scripts/Kbuild.include中的这些变量。

 

 

交叉编译工具变量设置

上面只是设置了CROSS-COMPILE的名字,但是交叉编译器其他的工具还没有设置,顶层Makefile中相关代码如下:

 

导出其他变量

接下来在顶层Makefile会导出很多变量,代码如下:

这些变量中大部分都已经在前面定义了,我们重点来看一下下面这几个变量:

ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR

这7个变量在顶层Makefile是找不到的,说明这7个变量是在其他文件里面定义的,先来看一下这7个变量都是什么内容,在顶层Makefile中输入如图所示的内容:

 修改好顶层Makefile以后执行如下命令:

make ARCH=arm CROSS COMPILE=arm-linux-gnueabihf- mytest

从图可以看到这7个变量的值,这7个变量是从哪里来的呢?在uboot根目录下有个文件叫做 config.mk,这7个变量就是在config.mk里面定义的,打开config.mk内容如下: 

第25行定义变量ARCH,值为$(CONFIG_SYS_ARCH:"%"=%),也就是提取.CONFIG_SYS_ARCH里面双引号"”之间的内容。比如CONFIG_SYS_ARCH= "arm"的话,ARCH=arm。

第 26 行定义变量 CPU,值为$(CONFIG_SYS_CPU:"%"=%)。

第32行定义变量BOARD,值为(CONFIG_SYS_BOARD:"%"=%)。

第34行定义变量VENDOR,值为$(CONFIG_SYS_VENDOR:"%"=%)。

第37 行定义变量 SOC,值为$(CONFIG_SYS_SOC:"%"=%)。

第44行定义变量CPUDIR,值为arch/$(ARCH)/cpu$(if $(CPU),/$(CPU),)。

第46行sinclude和include的功能类似,在Makefile中都是读取指定文件内容,这里读取文件$(srctree)/arch/S(ARCH)/config.mk的内容。sinclude读取的文件如果不存在的话不会报错。

第47行读取文件$(srctree)/$(CPUDIR)/config.mk的内容。

第50行读取文件$(srctree)/$(CPUDIR)/S(SOC)/config.mk的内容。

第54行定义变量BOARDDIR,如果定义了VENDOR 那么BOARDDIR=$(VENDOR)/$(BOARD),否则的 BOARDDIR=$(BOARD)。

第60行读取文件$(srctree)/board/$(BOARDDIR)/config.mk。

 接下来需要找到CONFIG_SYS_ARCH、CONFIG_SYS_CPU、CONFIG_SYS_BOARD、CONFIG_SYS_VENDOR和CONFIG_SYS_SOC这5个变量的值。这5个变量在uboot根目录下的.config文件中有定义.定义如下:

 根据示例代码可知: 

 在config.mk中读取的文件有:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/17410.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TCP/IP网络编程(一)

TCP/IP网络编程读书笔记 第1章 理解网络编程和套接字1.1 理解网络编程和套接字1.1.1 构建打电话套接字1.1.2 编写 Hello World 套接字程序 1.2 基于Linux的文件操作1.2.1 底层访问和文件描述符1.2.2 打开文件1.2.3 关闭文件1.2.4 将数据写入文件1.2.5 读取文件中的数据1.2.6 文…

操作系统考试复习——第四章 存储器管理 4.1 4.2

存储器的层次结构&#xff1a; 存储器的多层结构&#xff1a; 存储器至少分为三级&#xff1a;CPU寄存器&#xff0c;主存和辅存。 但是一般分为6层为寄存器&#xff0c;高速缓存&#xff0c;主存储器&#xff0c;磁盘缓存&#xff0c;固定磁盘&#xff0c;可移动存储介质。…

( “ 图 “ 之 拓扑排序 ) 207. 课程表 ——【Leetcode每日一题】

❓207. 课程表 难度&#xff1a;中等 你这个学期必须选修 numCourses 门课程&#xff0c;记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出&#xff0c;其中 prerequisites[i] [ai, bi] &#xff0c;表示如果要学习课…

自动驾驶中地图匹配定位技术总结

引言 汽车定位是让自动驾驶汽车知道自身确切位置的技术&#xff0c;在自动驾驶系统中担负着相当重要的职责。汽车定位涉及多种传感器类型和相关技术&#xff0c;主要可分为卫星定位、惯性导航定位、地图匹配定位以及多传感器融合定位几大类。其中地图匹配定位技术利用道路物理…

redis使用总结

目录 redis安装与登录redis 持久化RDB(Redis DataBase)AOF(Append Only File)RDB-AOF混合持久纯缓存模式 redis 的 keyredis 的数据类型和常见应用场景StringListHashMapSet集合ZSet有序集合bitmap位图HyperLogLog基数统计GEO 地理空间Stream 流bitfiled redis 事务事务的正常执…

【微机原理】8088/8086微处理器

目录 一、8088/8086的功能结构 1.总线接口部件&#xff08;BIU&#xff09; 2.执行部件&#xff08;EU&#xff09; 二、8088/8086的寄存器结构&#xff08;14个&#xff09; 溢出标志的概念 溢出和进位的区别 8086CPU是Intel系列的16位微处理器&#xff0c;他有16根数据…

servlet技术

什么是Servlet? Servlet 是 javaEE 规范之一. 规范就是接口 Servlet 是 javaWeb三大组件之一 三大组件分别是: Servlet程序, Flter过滤器, Listener监听器Servlet 是运行在服务器上的一个 java 小程序, 他可以接收客户端发送过来的请求, 并响应数据给客户端. 手动实现S…

电话号码的字母组合

题目&#xff1a;17. 电话号码的字母组合 - 力扣&#xff08;Leetcode&#xff09; 思路&#xff1a; 给定一个电话号码字符串 digits&#xff0c;须输出它所能表示的所有字母组合。我们可以先定义一个数字字符到字母表的映射表 numToStr&#xff0c;然后再用 Combine 函数递归…

Apache Kafka 进阶(一)

官网 Apache Kafka是一个开源的分布式事件流平台&#xff0c;被数千家公司用于高性能数据管道、流分析、数据集成和关键任务应用。 核心能力 高吞吐量 在网络有限的吞吐量下&#xff0c;使用延迟低至2ms的机器集群交付消息。可扩展性 将生产集群扩展到1000个代理&#xff0c…

互联网陪诊系统功能方案

互联网陪诊系统是一款为用户提供陪同患者到医院就医全程陪同&#xff0c;排队约号&#xff0c;排队检查&#xff0c;排队缴费&#xff0c;取送结果&#xff0c;代办买药&#xff0c;代办问诊等。 业务线上预约平台&#xff0c;让客户享受到最为专业的医院助医服务. 功能介绍 专…

阿里云g8i服务器Intel Xeon(Sapphire Rapids) Platinum 8475B

阿里云服务器ECS通用型实例规格族g8i采用2.7 GHz主频的Intel Xeon(Sapphire Rapids) Platinum 8475B处理器&#xff0c;3.2 GHz睿频&#xff0c;g8i实例采用阿里云全新CIPU架构&#xff0c;可提供稳定的算力输出、更强劲的I/O引擎以及芯片级的安全加固。阿里云百科分享阿里云服…

PMP项目管理-[第八章]质量管理

质量管理知识体系&#xff1a; 规划质量管理&#xff1a; 管理质量&#xff1a; 控制质量 &#xff1a; 8.1 质量和等级的区别 质量定义&#xff1a;作为实现的性能或成果&#xff0c;是一系列内在特性满足要求的程度 等级定义&#xff1a;作为设计意图&#xff0c;是对用途相同…

DP(9)--插头DP

DP(9)--插头DP /* Mondriaan’s Dream题目大意&#xff1a;在 N*M 的棋盘内铺满 1*2 或 2*1 的多米诺骨牌&#xff0c;求方案数。 砖只有横放和竖放两种状态&#xff0c;把横放记为两个0&#xff0c;竖放记为上1下0&#xff0c;逐格DP&#xff0c;每次无论前一格…

详解MySQL慢SQL定位、分析

目录 1.概述 2.慢SQL定位 3.SQL性能分析 3.1.例子 3.2.SQL性能分析 3.3.参数说明 3.3.1.id 3.3.2.select_type 3.3.3.key_len 3.3.4.rows 3.3.5.type 3.3.6.extra 1.概述 解决慢SQL的问题无非3步&#xff1a; 定位慢SQL分析慢SQL优化慢SQL 本文将按顺序介绍前两…

【MySQL】SQL优化

上一篇索引是针对查询语句进行优化,但在MySQL中可不仅有查询语句,针对其他的SQL语句同样也能进行优化 文章目录 1.插入数据2.主键优化3.order by 优化4.group by优化5.limit优化6.update优化 1.插入数据 插入数据所使用的关键字为insert,SQL语句为 insert into 表名(字段1,字…

恢复item2和oh-my-zsh的配置

1. 首先正常安装item2 2. 加载onedrive里的传家宝iterm2_default_profile.json&#xff0c;让iterm2的配置生效 2. 然后正常安装oh-my-zsh (官方步骤&#xff1a; sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)&q…

BUUCTF ciscn_2019_c_1

小白垃圾做题笔记而已&#xff0c;不建议阅读。 1前期&#xff1a; 其实刚开始拿到程序的时候我还以为是逆向题放错地方了。唉&#xff0c;做题太少了。啥也不会。我是大笨蛋。 题目中用的是ubuntu18&#xff0c;我的ubuntu没怎么用过&#xff0c;vmtools都不能用&#xff0c…

什么是GPT模型,GPT下载和国内镜像

什么是GPT模型&#xff0c;GPT模型是通过预训练的方式&#xff0c;采用无监督学习方式&#xff0c;大量语料输入&#xff0c;经过多次训练后得到模型。它能够自动学习并理解自然语言中的语义、句法和语法信息&#xff0c;并可以用于文本生成、对话系统、情感分析、机器翻译等自…

零死角玩转stm32中级篇3-SPI总线

本篇博文目录: 一.基础知识1.什么是SPI2.SPI和IIC有什么不同3.SPI的优缺点4.SPI是怎么实现通信的5.SPI 数据传输的步骤6.SPI菊花链7.通过SPI实现数据的读和写 二.STM32F103C8T6芯片SPI协议案例代码 一.基础知识 1.什么是SPI SPI&#xff08;Serial Peripheral Interface&#…

Flask开发之环境搭建

目录 1、安装flask 2、创建Flask工程 ​编辑 3、初始化效果 4、运行效果 5、设置Debug模式 6、设置Host 7、设置Port 8、在app.config中添加配置 1、安装flask 如果电脑上从没有安装过flask&#xff0c;则在命令行界面输入以下命令&#xff1a; pip install flask 如果电…
最新文章