1)实验平台:正点原子领航者ZYNQ开发板
2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_linhanz.html
4)对正点原子FPGA感兴趣的同学可以加群讨论:876744900
5)关注正点原子公众号,获取最新资料
http://weixin.qq.com/r/hEhUTLbEdesKrfIv9x2W (二维码自动识别)
第十六章Linux内核顶层Makefile详解
前几章我们重点讲解了如何移植uboot到领航者开发板上,从本章开始我们就开始学习如何移植Linux内核。同uboot一样,在具体移植之前,我们先来学习一下Linux内核的顶层Makefile文件,因为顶层Makefile控制着Linux内核的编译流程。
1.1Linux内核获取
关于Linux的起源以及发展历史,这里就不啰嗦了,网上相关的介绍太多了。即使写到这里也只是水一下教程页数而已,没有任何实际的意义。有限的时间还是放到有意义的事情上吧,Linux由Linux基金会管理与发布,Linux官网为https://www.kernel.org,所以你想获取最新的Linux版本就可以在这个网站上下载,网站界面如下图所示:
![6ac8e89bd4e6da8b4801fa2c2a67292c.png](https://i-blog.csdnimg.cn/blog_migrate/b23e72bcda974a95c37074c1419f452e.jpeg)
图 27.1.1 linux官网
从上图可以看出最新的稳定版Linux已经到了5.1.4,大家没必要追新,因为4.x版本的Linux和5.x版本没有本质上的区别,5.x更多的是加入了一些新的平台、新的外设驱动而已。
Xilinx会从https://www.kernel.org下载某个版本的Linux内核,然后将其移植到自己的芯片平台上,测试成功以后就会将其开放给Xilinx的芯片平台开发者。开发者下载Xilinx提供的Linux内核,然后将其移植到自己的产品上。后面的移植我们使用Xilinx提供的Linux源码,Xilinx提供的Linux源码已经放到了开发板光盘中,路径为:ZYNQ开发板资料盘(A盘)4_SourceCodeZYNQ_70203_Embedded_Linux资源文件kernellinux-xlnx-xilinx-v2018.3.tar.gz。
1.2Linux内核编译初次编译
先看一下如何编译Linux源码,这里编译一下领航者开发板移植好的Linux源码,已经放到了开发板光盘中,路径为:ZYNQ开发板资料盘(A盘)4_SourceCodeZYNQ_70203_Embedded_Linux资源文件kernel linux-4.14.0-atk-v2018.3.tar.gz。可以在Ubuntu中新建一个名为“alientek_linux”的文件夹,然后将linux- linux-4.14.0-atk-v2018.3.tar.gz这个压缩包拷贝到新建的alientek_linux文件夹中并解压。此处我们使用20.1节下载的linux内核。进入到下载到内核目录处,查看内核目录结构,命令如下:
- cd ~/work/petalinux/linux-4.14/
- ls
可以看到Linux源码根目录如下图所示:
![2b0ab5cf89cf38c2fb813e0b8aa079c7.png](https://i-blog.csdnimg.cn/blog_migrate/d29fa752552c98f16d22965be920141d.png)
图 27.2.1正点原子提供的Linux源码根目录
其中oe-logs和oe-workdir是Petalinux工具生成的链接目录,不是linux内核的源码目录。
现在我们讲解一下如何编译出对应的Linux镜像文件。新建名为“zynq.sh”的shell脚本,然后在这个shell脚本里面输入如下所示内容:
示例代码 zynq.sh文件内容
- 1 #!/bin/sh
- 2 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
- 3 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- xilinx_zynq_defconfig
- 4 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
- 5 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16
第2行,执行“make distclean”,清理工程,所以zynq.sh每次都会清理一下工程。如果通过图形界面配置了Linux,但是还没保存新的配置文件,那么就要慎重使用zynq.sh编译脚本了,因为它会把你的配置信息都删除掉。
第3行,执行“make xxx_defconfig”,配置工程。
第4行,执行“make menuconfig”,打开图形配置界面,对Linux进行配置,如果不想每次编译都打开图形配置界面的话可以将这一行删除掉。
第5行,执行“make”,编译Linux源码。
可以看出,Linux的编译过程基本和uboot一样,都要先执行“make xxx_defconfig”来配置一下,然后在执行“make”进行编译。如果需要使用图形界面配置的话就执行“make menuconfig”。
使用chmod给予zynq.sh可执行权限,然后运行此shell脚本,命令如下:
- ./zynq.sh
编译的时候会弹出Linux图形配置界面,如下图所示:
![3e7ca4d1ec219d9bbef2b6df96f9a843.png](https://i-blog.csdnimg.cn/blog_migrate/5648bc6ffa79f11b7e31e55b8f368cf6.jpeg)
图 27.2.2 Linux图形配置界面
Linux的图行界面配置和uboot是一样的,这里我们不需要做任何的配置,直接按两下ESC键退出,退出图形界面以后会自动开始编译Linux。等待编译完成,完成以后如下图所示:
![00631d3752d05cf29b20fdb2128c5fd8.png](https://i-blog.csdnimg.cn/blog_migrate/a862418faad1f5ea07d913cd9e540c38.png)
图 27.2.3 Linux编译完成
编译完成以后就会在arch/arm/boot这个目录下生成一个叫做zImage的文件,zImage就是我们要用的Linux镜像文件。另外也会在arch/arm/boo/dts下生成很多.dtb文件,这些.dtb就是设备树文件,比如zynq-altk.dtb就是领航者开发板对应的设备树。
1.3Linux工程目录分析
将正点原子提供的Linux源码进行解压,解压完成以后的目录如下图所示:
![f373281e549e5ea7a3532a8437919e31.png](https://i-blog.csdnimg.cn/blog_migrate/a7b77e574ea983fb8db11dbd1e0605f8.png)
图 27.3.1未编译的Linux源码目录
上图就是正点原子提供的未编译的Linux源码目录文件,我们在分析Linux之前一定要先在Ubuntu中编译一下Linux,因为编译过程会生成一些文件,而生成的这些恰恰是分析Linux不可或缺的文件。编译后的Linux目录如下图所示:
![edb98e2733e0513de42852e69f2ebc76.png](https://i-blog.csdnimg.cn/blog_migrate/defe487018c39311e2dd9a0806875a92.png)
图 27.3.2编译后的Linux目录
上图中重要的文件夹或文件的含义如下表所示:
表 26.2.2.1 Linux目录
![279c4b68333d56d1aac35f0c9294ff7e.png](https://i-blog.csdnimg.cn/blog_migrate/1dbb1ed90f6dac26d6a0cb340e31bc8b.png)
![d30192e81d498600289d66e118fd4104.png](https://i-blog.csdnimg.cn/blog_migrate/0e86e25f34fa66e5a6322b4469531258.jpeg)
上表中的很多文件夹和文件我们都不需要去关心,我们要关注的文件夹或文件如下:
1、arch目录
这个目录是和架构有关的目录,比如arm、arm64、avr32、x86等等架构。每种架构都对应一个目录,在这些目录中又有很多子目录,比如boot、common、configs等等,以arch/arm为例,其子目录如下图所示:
![e92ee62162948376d5caafda690042b9.png](https://i-blog.csdnimg.cn/blog_migrate/2b4bd27110115e55ad15fa80c762be91.jpeg)
图 27.3.3 arch/arm子目录
上图是arch/arm的一部分子目录,这些子目录用于控制系统引导、系统调用、动态调频、主频设置等。arch/arm/configs目录是不同平台的默认配置文件:xxx_defconfig,如下图所示:
![5592bbcb63f368e5dcd374341bb01123.png](https://i-blog.csdnimg.cn/blog_migrate/11d68e76c2cd3c3bf45dde7325a1ca09.jpeg)
图 27.3.4配置文件
在arch/arm/configs中就包含有领航者开发板的默认配置文件:xilinx_zynq_defconfig,执行“make xilinx_zynq_defconfig”即可完成配置。arch/arm/boot/dts目录里面是对应开发平台的设备树文件,正点原子领航者开发板对应的设备树文件如下图所示:
![60ce3bcca9267ad6658d2ab02907c34f.png](https://i-blog.csdnimg.cn/blog_migrate/7ad0954f65f4d43ddc0abce99cf92c12.png)
图 27.3.5正点原子领航者开发板对应的设备树
arch/arm/boot目录中有编译出来的Image和zImage镜像文件,而zImage就是我们要用的linux镜像文件。
arch/arm/mach-xxx目录分别为相应平台的驱动和初始化文件,比如mach-zynq目录里面就是ZYNQ系列CPU的驱动和初始化文件。
2、block目录
block是Linux下块设备目录,像SD卡、EMMC、NAND、硬盘等存储设备就属于块设备,block目录中存放着管理块设备的相关文件。
3、crypto目录
crypto目录里面存放着加密文件,比如常见的crc、crc32、md4、md5、hash等加密算法。
4、Documentation目录
此目录里面存放着Linux相关的文档,如果要想了解Linux某个功能模块或驱动架构的功能,就可以在Documentation目录中查找有没有对应的文档。
5、drivers目录
设备驱动程序目录,此目录根据驱动类型的不同,分门别类进行整理,比如drivers/i2c就是I2C相关驱动目录,drivers/gpio就是GPIO相关的驱动目录,这是我们学习的重点。
6、firmware目录
此目录用于存放固件。
7、fs目录
此目录存放文件系统相关代码,比如fs/ext2、fs/ext4、fs/f2fs等,分别是ext2、ext4和f2fs等文件系统。
8、include目录
头文件目录。
9、init目录
此目录存放Linux内核启动的时候初始化代码。
10、ipc目录
IPC为进程间通信,ipc目录是进程间通信的具体实现代码。
11、kernel目录
Linux内核代码。与平台相关的部分代码放在arch/*/kernel目录下,其中*代表各种处理器平台
12、lib目录
lib是库的意思,lib目录都是一些公用的库函数。
13、mm目录
此目录存放与平台无关的内存管理代码,与平台相关的内存管理代码放在arch/*/mm目录下。
14、net目录
此目录存放网络相关代码。
15、samples目录
此目录存放一些示例代码文件。
16、scripts目录
脚本目录,Linux编译的时候会用到很多脚本文件,这些脚本文件就保存在此目录中。
17、security目录
此目录存放安全相关的文件。
18、sound目录
此目录存放音频相关驱动文件,音频驱动文件并没有存放到drivers目录中,而是单独的目录。
19、tools目录
此目录存放一些编译的时候使用到的工具。
20、usr目录
此目录存放与initramfs有关的代码。
21、virt目录
此目录存放虚拟机相关文件。
22、.config文件
根uboot一样,.config保存着Linux最终的配置信息,编译Linux的时候会读取此文件中的配置信息。最终根据配置信息来选择编译Linux哪些模块,哪些功能。
23、Kbuild文件
有些Makefile会读取此文件。
24、Kconfig文件
图形化配置界面的配置文件。
25、Makefile文件
Linux顶层Makefile文件,建议好好阅读一下此文件。
26、README文件
此文件详细讲解了如何编译Linux源码,以及Linux源码的目录信息,建议仔细阅读一下此文件。
关于Linux源码目录就分析到这里,接下来分析一下Linux的顶层Makefile。
1.4VSCode工程创建
在分析Linux的顶层Makefile之前,先创建VSCode工程,创建过程和uboot一样。创建好以后将文件.vscode/settings.json改为如下所示内容:
示例代码 settings.json文件内容
- 1 {
- 2 "search.exclude": {
- 3 "**/node_modules": true,
- 4 "**/bower_components": true,
- 5 "**/*.o":true,
- 6 "**/*.su":true,
- 7 "**/*.cmd":true,
- 8 "Documentation":true,
- 9
- 10 /* 屏蔽不用的架构相关的文件 */
- 11 "arch/alpha":true,
- 12 "arch/arc":true,
- 13 "arch/arm64":true,
- 14 "arch/avr32":true,
- 15 "arch/[b-z]*":true,
- 16 "arch/arm/plat*":true,
- 17 "arch/arm/mach-[a-y]*":true,
- 18 "arch/arm/mach-zx":true,
- 19
- 20 /* 屏蔽不用的配置文件 */
- 21 "arch/arm/configs/[a-w]*":true,
- 22 "arch/arm/configs/[y-z]*":true,
- 23
- 24 /* 屏蔽不用的DTB文件 */
- 25 "arch/arm/boot/dts/[a-y]*":true,
- 26 "arch/arm/boot/dts/.*":true,
- 27 },
- 28
- 29 "files.exclude": {
- 30 "**/.git": true,
- 31 "**/.svn": true,
- 32 "**/.hg": true,
- 33 "**/CVS": true,
- 34 "**/.DS_Store": true,
- 35 "**/*.o":true,
- 36 "**/*.su":true,
- 37 "**/*.cmd":true,
- 38 "Documentation":true,
- 39
- 40 /* 屏蔽不用的架构相关的文件 */
- 41 "arch/alpha":true,
- 42 "arch/arc":true,
- 43 "arch/arm64":true,
- 44 "arch/avr32":true,
- 45 "arch/[b-z]*":true,
- 46 "arch/arm/plat*":true,
- 47 "arch/arm/mach-[a-y]*":true,
- 48 "arch/arm/mach-zx":true,
- 49
- 50 /* 屏蔽不用的配置文件 */
- 51 "arch/arm/configs/[a-w]*":true,
- 52 "arch/arm/configs/[y-z]*":true,
- 53
- 54 /* 屏蔽不用的DTB文件 */
- 55 "arch/arm/boot/dts/[a-y]*":true,
- 56 "arch/arm/boot/dts/.*":true,
- 57 }
- 58 }
创建好VSCode工程以后就可以开始分析Linux的顶层Makefile了。
1.5顶层Makefile详解
Linux的顶层Makefile和uboot的顶层Makefile非常相似,因为uboot参考了Linux,前610行几乎一样,所以前面部分我们大致看一下就行了。
1、版本号
顶层Makefile一开始就是Linux内核的版本号,如下所示:
示例代码 顶层Makefile代码段
- 1 # SPDX-License-Identifier: GPL-2.0
- 2 VERSION = 4
- 3 PATCHLEVEL = 14
- 4 SUBLEVEL = 0
- 5 EXTRAVERSION =
可以看出,Linux内核版本号为4.14.0。
2、MAKEFLAGS变量
MAKEFLAGS变量设置如下所示:
示例代码 顶层Makefile代码段
- 17 MAKEFLAGS += -rR --include-dir=$(CURDIR)
3、命令输出
Linux编译的时候也可以通过“V=1”来输出完整的命令,这个和uboot一样,相关代码如下所示:
示例代码 顶层Makefile代码段
- 70 ifeq ("$(origin V)", "command line")
- 71 KBUILD_VERBOSE = $(V)
- 72 endif
- 73 ifndef KBUILD_VERBOSE
- 74 KBUILD_VERBOSE = 0
- 75 endif
- 76
- 77 ifeq ($(KBUILD_VERBOSE),1)
- 78 quiet =
- 79 Q =
- 80 else
- 81 quiet=quiet_
- 82 Q = @
- 83 endif
4、静默输出
Linux编译的时候使用“make -s”就可实现静默编译,编译的时候就不会打印任何的信息,同uboot一样,相关代码如下:
示例代码 顶层Makefile代码段
- 85 # If the user is running make -s (silent mode), suppress echoing of
- 86 # commands
- 87
- 88 ifneq ($(findstring s,$(filter-out --%,$(MAKEFLAGS))),)
- 89 quiet=silent_
- 90 tools_silent=s
- 91 endif
- 92
- 93 export quiet Q KBUILD_VERBOSE
5、设置编译结果输出目录
Linux编译的时候使用“O=xxx”即可将编译产生的过程文件输出到指定的目录中,相关代码如下:
示例代码 顶层Makefile代码段
- 112 ifeq ($(KBUILD_SRC),)
- 113
- 114 # OK, Make called in directory where kernel src resides
- 115 # Do we want to locate output files in a separate directory?
- 116 ifeq ("$(origin O)", "command line")
- 117 KBUILD_OUTPUT := $(O)
- 118 endif
6、代码检查
Linux也支持代码检查,使用命令“make C=1”使能代码检查,检查那些需要重新编译的文件。“make C=2”用于检查所有的源码文件,顶层Makefile中的代码如下:
示例代码 顶层Makefile代码段
- 172 ifeq ("$(origin C)", "command line")
- 173 KBUILD_CHECKSRC = $(C)
- 174 endif
- 175 ifndef KBUILD_CHECKSRC
- 176 KBUILD_CHECKSRC = 0
- 177 endif
7、模块编译
Linux允许单独编译某个模块,使用命令“make M=dir”即可,旧语法“make SUBDIRS=dir”也是支持的。顶层Makefile中的代码如下:
示例代码 顶层Makefile代码段
- 179 # Use make M=dir to specify directory of external module to build
- 180 # Old syntax make ... SUBDIRS=$PWD is still supported
- 181 # Setting the environment variable KBUILD_EXTMOD take precedence
- 182 ifdef SUBDIRS
- 183 KBUILD_EXTMOD ?= $(SUBDIRS)
- 184 endif
- 185
- 186 ifeq ("$(origin M)", "command line")
- 187 KBUILD_EXTMOD := $(M)
- 188 endif
- 189
- 190 # If building an external module we do not care about the all: rule
- 191 # but instead _all depend on modules
- 192 PHONY += all
- 193 ifeq ($(KBUILD_EXTMOD),)
- 194 _all: all
- 195 else
- 196 _all: modules
- 197 endif
- 198
- 199 ifeq ($(KBUILD_SRC),)
- 200 # building in the source tree
- 201 srctree := .
- 202 else
- 203 ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
- 204 # building in a subdirectory of the source tree
- 205 srctree := ..
- 206 else
- 207 srctree := $(KBUILD_SRC)
- 208 endif
- 209 endif
- 210 objtree := .
- 211 src := $(srctree)
- 212 obj := $(objtree)
- 213
- 214 VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
- 215
- 216 export srctree objtree VPATH
外部模块编译过程和uboot也一样,最终导出srctree、objtree和VPATH这三个变量的值,其中srctree=.,也就是当前目录,objtree同样为“.”。
8、设置目标架构和交叉编译器
同uboot一样,Linux编译的时候需要设置目标板架构ARCH和交叉编译器CROSS_COMPILE,在顶层Makefile中代码如下:
示例代码 顶层Makefile代码段
- 251 ARCH ?= $(SUBARCH)
- 252 CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)
有时为了方便,可以直接修改顶层Makefile中的ARCH和CROSS_COMPILE,将其设置为对应的架构和编译器,比如本教程可以将ARCH设置为arm,CROSS_COMPILE设置为arm-linux-gnueabihf-,如下所示:
示例代码 顶层Makefile代码段
- 251 ARCH ?= arm
- 252 CROSS_COMPILE ?= arm-linux-gnueabihf-
设置好以后就可以使用如下命令编译Linux了:
- make xxx_defconfig //使用默认配置文件配置Linux
- make menuconfig //启动图形化配置界面
- make -j16 //编译Linux
9、调用scripts/Kbuild.include文件
同uboot一样,Linux顶层Makefile也会调用文件scripts/Kbuild.include,顶层Makefile相应代码如下:
示例代码 顶层Makefile代码段
- 345 # We need some generic definitions (do not try to remake the file).
- 346 scripts/Kbuild.include: ;
- 347 include scripts/Kbuild.include
10、交叉编译工具变量设置
顶层Makefile中其他和交叉编译器有关的变零设置如下:
示例代码 顶层Makefile代码段
- 349 # Make variables (CC, etc...)
- 350 AS = $(CROSS_COMPILE)as
- 351 LD = $(CROSS_COMPILE)ld
- 352 CC = $(CROSS_COMPILE)gcc
- 353 CPP = $(CC) -E
- 354 AR = $(CROSS_COMPILE)ar
- 355 NM = $(CROSS_COMPILE)nm
- 356 STRIP = $(CROSS_COMPILE)strip
- 357 OBJCOPY = $(CROSS_COMPILE)objcopy
- 358 OBJDUMP = $(CROSS_COMPILE)objdump
LA、LD、CC等这些都是交叉编译器所使用的工具。
11、头文件路径变量
顶层Makefile定义了两个变量保存头文件路径:USERINCLUDE和LINUXINCLUDE,相关代码如下:
示例代码 顶层Makefile代码段
- 380 # Use USERINCLUDE when you must reference the UAPI directories only.
- 381 USERINCLUDE :=
- 382 -I$(srctree)/arch/$(hdr-arch)/include/uapi
- 383 -I$(objtree)/arch/$(hdr-arch)/include/generated/uapi
- 384 -I$(srctree)/include/uapi
- 385 -I$(objtree)/include/generated/uapi
- 386 -include $(srctree)/include/linux/kconfig.h
- 387
- 388 # Use LINUXINCLUDE when you must reference the include/ directory.
- 389 # Needed to be compatible with the O= option
- 390 LINUXINCLUDE :=
- 391 -I$(srctree)/arch/$(hdr-arch)/include
- 392 -I$(objtree)/arch/$(hdr-arch)/include/generated
- 393 $(if $(KBUILD_SRC), -I$(srctree)/include)
- 394 -I$(objtree)/include
- 395 $(USERINCLUDE)
第380~386行的USERINCLUDE是UAPI相关的头文件路径,第390~395行的LINUXINCLUDE是Linux内核源码的头文件路径。重点来看下LINUXINCLUDE,其中srctree=.,objtree=.,hdr-arch=arm,KBUILD_SRC为空,因此,将USERINCLUDE和LINUXINCLUDE展开以后为:
- USERINCLUDE :=
- -I./arch/arm/include/uapi
- -I./arch/arm/include/generated/uapi
- -I./include/uapi
- -I./include/generated/uapi
- -include ./include/linux/kconfig.h
- LINUXINCLUDE :=
- -I./arch/arm/include
- -I./arch/arm/include/generated
- -I./include
- -I./arch/arm/include/uapi
- -I./arch/arm/include/generated/uapi
- -I./include/uapi
- -I./include/generated/uapi
- -include ./include/linux/kconfig.h
12、导出变量
顶层Makefile会导出很多变量给子Makefile使用,导出的这些变量如下:
示例代码 顶层Makefile代码段
- 417 export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION
- 418 export ARCH SRCARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC
- 419 export CPP AR NM STRIP OBJCOPY OBJDUMP HOSTLDFLAGS HOST_LOADLIBES
- 420 export MAKE AWK GENKSYMS INSTALLKERNEL PERL PYTHON UTS_MACHINE
- 421 export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS
- 422
- 423 export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS
- 424 export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_GCOV CFLAGS_KCOV CFLAGS_KASAN CFLAGS_UBSAN
- 425 export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
- 426 export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE
- 427 export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
- 428 export KBUILD_ARFLAGS
1.5.1 make xxx_defconfig过程
第一次编译Linux之前都要先使用“make xxx_defconfig”配置Linux内核,在顶层Makefile中有“%config”这个目标,如下所示:
示例代码 27.5.117.5.1顶层Makefile代码段
- 491 config-targets := 0
- 492 mixed-targets := 0
- 493 dot-config := 1
- 494
- 495 ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
- 496 ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
- 497 dot-config := 0
- 498 endif
- 499 endif
- 500
- 501 ifeq ($(KBUILD_EXTMOD),)
- 502 ifneq ($(filter config %config,$(MAKECMDGOALS)),)
- 503 config-targets := 1
- 504 ifneq ($(words $(MAKECMDGOALS)),1)
- 505 mixed-targets := 1
- 506 endif
- 507 endif
- 508 endif
- 509 # install and modules_install need also be processed one by one
- 510 ifneq ($(filter install,$(MAKECMDGOALS)),)
- 511 ifneq ($(filter modules_install,$(MAKECMDGOALS)),)
- 512 mixed-targets := 1
- 513 endif
- 514 endif
- 515
- 516 ifeq ($(mixed-targets),1)
- 517 # ===========================================================================
- 518 # We're called with mixed targets (*config and build targets).
- 519 # Handle them one by one.
- 520
- 521 PHONY += $(MAKECMDGOALS) __build_one_by_one
- 522
- 523 $(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one
- 524 @:
- 525
- 526 __build_one_by_one:
- 527 $(Q)set -e;
- 528 for i in $(MAKECMDGOALS); do
- 529 $(MAKE) -f $(srctree)/Makefile $i;
- 530 done
- 531
- 532 else
- 533 ifeq ($(config-targets),1)
- 534 # ===========================================================================
- 535 # *config targets only - make sure prerequisites are updated, and descend
- 536 # in scripts/kconfig to make the *config target
- 537
- 538 # Read arch specific Makefile to set KBUILD_DEFCONFIG as needed.
- 539 # KBUILD_DEFCONFIG may point out an alternative default configuration
- 540 # used for 'make defconfig'
- 541 include arch/$(SRCARCH)/Makefile
- 542 export KBUILD_DEFCONFIG KBUILD_KCONFIG
- 543
- 544 config: scripts_basic outputmakefile FORCE
- 545 $(Q)$(MAKE) $(build)=scripts/kconfig $@
- 546
- 547 %config: scripts_basic outputmakefile FORCE
- 548 $(Q)$(MAKE) $(build)=scripts/kconfig $@
- 549
- 550 else
- ......
- 571 endif # KBUILD_EXTMOD
第491~514行和uboot一样,都是设置定义变量config-targets、mixed-targets和dot-config的值,最终这三个变量的值为:
- config-targets= 1
- mixed-targets= 0
- dot-config= 1
因为config-targets=1,因此第541行~548行成立。第541行引用arch/arm/Makefile这个文件,这个文件很重要,以为zImage、uImage等这些文件就是由arch/arm/Makefile来生成的。
第542行导出变量KBUILD_DEFCONFIG KBUILD_KCONFIG。
第544行,没有目标与之匹配,因此不执行。
第547行,“make xxx_defconfig”与目标“%config”匹配,因此被执行。“%config”依赖scripts_basic、outputmakefile和FORCE,真正有意义的依赖是scripts_basic。scripts_basic的规则如下:
示例代码 27.5.217.5.2 顶层Makefile代码段
- 448 scripts_basic:
- 449 $(Q)$(MAKE) $(build)=scripts/basic
- 450 $(Q)rm -f .tmp_quiet_recordmcount
build定义在文件scripts/Kbuild.include中,值为build := -f $(srctree)/scripts/Makefile.build obj,因此将上述示例代码展开就是:
- scripts_basic:
- @make -f ./scripts/Makefile.build obj=scripts/basic //也可以没有@,视配置而定
- @rm -f . tmp_quiet_recordmcount //也可以没有@
接着回到Makefile的目标“%config”处,内容如下:
- %config: scripts_basic outputmakefile FORCE
- $(Q)$(MAKE) $(build)=scripts/kconfig $@
将命令展开就是:
- @make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
1.5.2Makefile.build脚本分析
从上一小节可知,“make xxx_defconfig“配置Linux的时候如下两行命令会执行脚本scripts/Makefile.build:
- @make -f ./scripts/Makefile.build obj=scripts/basic
- @make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
我们依次来分析一下:
1、scripts_basic目标对应的命令
scripts_basic目标对应的命令为:@make -f ./scripts/Makefile.build obj=scripts/basic。打开文件scripts/Makefile.build,有如下代码:
示例代码 27.5.317.5.3 Makefile.build代码段
- 42 # The filename Kbuild has precedence over Makefile
- 43 kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
- 44 kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
- 45 include $(kbuild-file)
将kbuild-dir展开后为:
- kbuild-dir=./scripts/basic
将kbuild-file展开后为:
- kbuild-file= ./scripts/basic/Makefile
最后将45行展开,即:
- include ./scripts/basic/Makefile
继续分析scripts/Makefile.build,如下代码:
示例代码 27.5.417.5.4 Makefile.build代码段
- 96 __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y))
- 97 $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target))
- 98 $(subdir-ym) $(always)
- 99 @:
__build是默认目标,因为命令“@make -f ./scripts/Makefile.build obj=scripts/basic”没有指定目标,所以会使用到默认目标__build。在顶层Makefile中,KBUILD_BUILTIN为1,KBUILD_MODULES为空,因此展开后目标__build为:
- __build:$(builtin-target) $(lib-target) $(extra-y)) $(subdir-ym) $(always)
- @:
可以看出目标__build有5个依赖:builtin-target、lib-target、extra-y、subdir-ym和always。这5个依赖的具体内容如下:
- builtin-target =
- lib-target =
- extra-y =
- subdir-ym =
- always = scripts/basic/fixdep scripts/basic/bin2c
只有always有效,因此__build最终为:
- __build: scripts/basic/fixdep scripts/basic/bin2c
- @:
__build依赖于scripts/basic/fixdep和scripts/basic/bin2c,所以要先将scripts/basic/fixdep.c和scripts/basic/bin2c.c这两个文件编译成fixdep和bin2c。
综上所述,scripts_basic目标的作用就是编译scripts/basic/fixdep和scripts/basic/bin2c这两个文件。
2、 %config目标对应的命令
%config目标对应的命令为:@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig,此命令会使用到的各个变量值如下:
- src= scripts/kconfig
- kbuild-dir = ./scripts/kconfig
- kbuild-file = ./scripts/kconfig/Makefile
- include ./scripts/kconfig/Makefile
可以看出,Makefilke.build会读取scripts/kconfig/Makefile中的内容,此文件有如下所示内容:
示例代码 27.5.517.5.5 scripts/kconfig/Makefile代码段
- 112 %_defconfig: $(obj)/conf
- 113 $(Q)[ DISCUZ_CODE_983 ]lt; $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
目标%_defconfig与 xxx_defconfig匹配,所以会执行这条规则,将其展开就是:
- %_defconfig: scripts/kconfig/conf
- @ scripts/kconfig/conf --defconfig=arch/arm/configs/%_defconfig Kconfig
%_defconfig依赖scripts/kconfig/conf,所以会编译scripts/kconfig/conf.c生成conf这个软件。此软件就会将%_defconfig中的配置输出到.config文件中,最终生成Linux kernel根目录下的.config文件。