linux新建文件夹_【正点原子FPGA连载】第十六章内核顶层Makefile详解-领航者ZYNQ之linux开发指南...

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

图 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内核。进入到下载到内核目录处,查看内核目录结构,命令如下:

  1. cd ~/work/petalinux/linux-4.14/
  2. ls


可以看到Linux源码根目录如下图所示:

2b0ab5cf89cf38c2fb813e0b8aa079c7.png

图 27.2.1正点原子提供的Linux源码根目录


其中oe-logs和oe-workdir是Petalinux工具生成的链接目录,不是linux内核的源码目录。
现在我们讲解一下如何编译出对应的Linux镜像文件。新建名为“zynq.sh”的shell脚本,然后在这个shell脚本里面输入如下所示内容:
示例代码 zynq.sh文件内容

  1. 1 #!/bin/sh
  2. 2 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
  3. 3 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- xilinx_zynq_defconfig
  4. 4 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
  5. 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脚本,命令如下:

  1. ./zynq.sh


编译的时候会弹出Linux图形配置界面,如下图所示:

3e7ca4d1ec219d9bbef2b6df96f9a843.png

图 27.2.2 Linux图形配置界面


Linux的图行界面配置和uboot是一样的,这里我们不需要做任何的配置,直接按两下ESC键退出,退出图形界面以后会自动开始编译Linux。等待编译完成,完成以后如下图所示:

00631d3752d05cf29b20fdb2128c5fd8.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

图 27.3.1未编译的Linux源码目录


上图就是正点原子提供的未编译的Linux源码目录文件,我们在分析Linux之前一定要先在Ubuntu中编译一下Linux,因为编译过程会生成一些文件,而生成的这些恰恰是分析Linux不可或缺的文件。编译后的Linux目录如下图所示:

edb98e2733e0513de42852e69f2ebc76.png

图 27.3.2编译后的Linux目录


上图中重要的文件夹或文件的含义如下表所示:

表 26.2.2.1 Linux目录

279c4b68333d56d1aac35f0c9294ff7e.png

d30192e81d498600289d66e118fd4104.png


上表中的很多文件夹和文件我们都不需要去关心,我们要关注的文件夹或文件如下:
1、arch目录
这个目录是和架构有关的目录,比如arm、arm64、avr32、x86等等架构。每种架构都对应一个目录,在这些目录中又有很多子目录,比如boot、common、configs等等,以arch/arm为例,其子目录如下图所示:

e92ee62162948376d5caafda690042b9.png

图 27.3.3 arch/arm子目录


上图是arch/arm的一部分子目录,这些子目录用于控制系统引导、系统调用、动态调频、主频设置等。arch/arm/configs目录是不同平台的默认配置文件:xxx_defconfig,如下图所示:

5592bbcb63f368e5dcd374341bb01123.png

图 27.3.4配置文件


在arch/arm/configs中就包含有领航者开发板的默认配置文件:xilinx_zynq_defconfig,执行“make xilinx_zynq_defconfig”即可完成配置。arch/arm/boot/dts目录里面是对应开发平台的设备树文件,正点原子领航者开发板对应的设备树文件如下图所示:

60ce3bcca9267ad6658d2ab02907c34f.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. 1 {
  2. 2 "search.exclude": {
  3. 3 "**/node_modules": true,
  4. 4 "**/bower_components": true,
  5. 5 "**/*.o":true,
  6. 6 "**/*.su":true,
  7. 7 "**/*.cmd":true,
  8. 8 "Documentation":true,
  9. 9
  10. 10 /* 屏蔽不用的架构相关的文件 */
  11. 11 "arch/alpha":true,
  12. 12 "arch/arc":true,
  13. 13 "arch/arm64":true,
  14. 14 "arch/avr32":true,
  15. 15 "arch/[b-z]*":true,
  16. 16 "arch/arm/plat*":true,
  17. 17 "arch/arm/mach-[a-y]*":true,
  18. 18 "arch/arm/mach-zx":true,
  19. 19
  20. 20 /* 屏蔽不用的配置文件 */
  21. 21 "arch/arm/configs/[a-w]*":true,
  22. 22 "arch/arm/configs/[y-z]*":true,
  23. 23
  24. 24 /* 屏蔽不用的DTB文件 */
  25. 25 "arch/arm/boot/dts/[a-y]*":true,
  26. 26 "arch/arm/boot/dts/.*":true,
  27. 27 },
  28. 28
  29. 29 "files.exclude": {
  30. 30 "**/.git": true,
  31. 31 "**/.svn": true,
  32. 32 "**/.hg": true,
  33. 33 "**/CVS": true,
  34. 34 "**/.DS_Store": true,
  35. 35 "**/*.o":true,
  36. 36 "**/*.su":true,
  37. 37 "**/*.cmd":true,
  38. 38 "Documentation":true,
  39. 39
  40. 40 /* 屏蔽不用的架构相关的文件 */
  41. 41 "arch/alpha":true,
  42. 42 "arch/arc":true,
  43. 43 "arch/arm64":true,
  44. 44 "arch/avr32":true,
  45. 45 "arch/[b-z]*":true,
  46. 46 "arch/arm/plat*":true,
  47. 47 "arch/arm/mach-[a-y]*":true,
  48. 48 "arch/arm/mach-zx":true,
  49. 49
  50. 50 /* 屏蔽不用的配置文件 */
  51. 51 "arch/arm/configs/[a-w]*":true,
  52. 52 "arch/arm/configs/[y-z]*":true,
  53. 53
  54. 54 /* 屏蔽不用的DTB文件 */
  55. 55 "arch/arm/boot/dts/[a-y]*":true,
  56. 56 "arch/arm/boot/dts/.*":true,
  57. 57 }
  58. 58 }


创建好VSCode工程以后就可以开始分析Linux的顶层Makefile了。
1.5顶层Makefile详解
Linux的顶层Makefile和uboot的顶层Makefile非常相似,因为uboot参考了Linux,前610行几乎一样,所以前面部分我们大致看一下就行了。
1、版本号
顶层Makefile一开始就是Linux内核的版本号,如下所示:
示例代码 顶层Makefile代码段

  1. 1 # SPDX-License-Identifier: GPL-2.0
  2. 2 VERSION = 4
  3. 3 PATCHLEVEL = 14
  4. 4 SUBLEVEL = 0
  5. 5 EXTRAVERSION =


可以看出,Linux内核版本号为4.14.0。
2、MAKEFLAGS变量
MAKEFLAGS变量设置如下所示:
示例代码 顶层Makefile代码段

  1. 17 MAKEFLAGS += -rR --include-dir=$(CURDIR)


3、命令输出
Linux编译的时候也可以通过“V=1”来输出完整的命令,这个和uboot一样,相关代码如下所示:
示例代码 顶层Makefile代码段

  1. 70 ifeq ("$(origin V)", "command line")
  2. 71 KBUILD_VERBOSE = $(V)
  3. 72 endif
  4. 73 ifndef KBUILD_VERBOSE
  5. 74 KBUILD_VERBOSE = 0
  6. 75 endif
  7. 76
  8. 77 ifeq ($(KBUILD_VERBOSE),1)
  9. 78 quiet =
  10. 79 Q =
  11. 80 else
  12. 81 quiet=quiet_
  13. 82 Q = @
  14. 83 endif


4、静默输出
Linux编译的时候使用“make -s”就可实现静默编译,编译的时候就不会打印任何的信息,同uboot一样,相关代码如下:
示例代码 顶层Makefile代码段

  1. 85 # If the user is running make -s (silent mode), suppress echoing of
  2. 86 # commands
  3. 87
  4. 88 ifneq ($(findstring s,$(filter-out --%,$(MAKEFLAGS))),)
  5. 89 quiet=silent_
  6. 90 tools_silent=s
  7. 91 endif
  8. 92
  9. 93 export quiet Q KBUILD_VERBOSE


5、设置编译结果输出目录
Linux编译的时候使用“O=xxx”即可将编译产生的过程文件输出到指定的目录中,相关代码如下:
示例代码 顶层Makefile代码段

  1. 112 ifeq ($(KBUILD_SRC),)
  2. 113
  3. 114 # OK, Make called in directory where kernel src resides
  4. 115 # Do we want to locate output files in a separate directory?
  5. 116 ifeq ("$(origin O)", "command line")
  6. 117 KBUILD_OUTPUT := $(O)
  7. 118 endif


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

  1. 172 ifeq ("$(origin C)", "command line")
  2. 173 KBUILD_CHECKSRC = $(C)
  3. 174 endif
  4. 175 ifndef KBUILD_CHECKSRC
  5. 176 KBUILD_CHECKSRC = 0
  6. 177 endif


7、模块编译
Linux允许单独编译某个模块,使用命令“make M=dir”即可,旧语法“make SUBDIRS=dir”也是支持的。顶层Makefile中的代码如下:
示例代码 顶层Makefile代码段

  1. 179 # Use make M=dir to specify directory of external module to build
  2. 180 # Old syntax make ... SUBDIRS=$PWD is still supported
  3. 181 # Setting the environment variable KBUILD_EXTMOD take precedence
  4. 182 ifdef SUBDIRS
  5. 183 KBUILD_EXTMOD ?= $(SUBDIRS)
  6. 184 endif
  7. 185
  8. 186 ifeq ("$(origin M)", "command line")
  9. 187 KBUILD_EXTMOD := $(M)
  10. 188 endif
  11. 189
  12. 190 # If building an external module we do not care about the all: rule
  13. 191 # but instead _all depend on modules
  14. 192 PHONY += all
  15. 193 ifeq ($(KBUILD_EXTMOD),)
  16. 194 _all: all
  17. 195 else
  18. 196 _all: modules
  19. 197 endif
  20. 198
  21. 199 ifeq ($(KBUILD_SRC),)
  22. 200 # building in the source tree
  23. 201 srctree := .
  24. 202 else
  25. 203 ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
  26. 204 # building in a subdirectory of the source tree
  27. 205 srctree := ..
  28. 206 else
  29. 207 srctree := $(KBUILD_SRC)
  30. 208 endif
  31. 209 endif
  32. 210 objtree := .
  33. 211 src := $(srctree)
  34. 212 obj := $(objtree)
  35. 213
  36. 214 VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
  37. 215
  38. 216 export srctree objtree VPATH


外部模块编译过程和uboot也一样,最终导出srctree、objtree和VPATH这三个变量的值,其中srctree=.,也就是当前目录,objtree同样为“.”。
8、设置目标架构和交叉编译器
同uboot一样,Linux编译的时候需要设置目标板架构ARCH和交叉编译器CROSS_COMPILE,在顶层Makefile中代码如下:
示例代码 顶层Makefile代码段

  1. 251 ARCH ?= $(SUBARCH)
  2. 252 CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)


有时为了方便,可以直接修改顶层Makefile中的ARCH和CROSS_COMPILE,将其设置为对应的架构和编译器,比如本教程可以将ARCH设置为arm,CROSS_COMPILE设置为arm-linux-gnueabihf-,如下所示:
示例代码 顶层Makefile代码段

  1. 251 ARCH ?= arm
  2. 252 CROSS_COMPILE ?= arm-linux-gnueabihf-


设置好以后就可以使用如下命令编译Linux了:

  1. make xxx_defconfig //使用默认配置文件配置Linux
  2. make menuconfig //启动图形化配置界面
  3. make -j16 //编译Linux


9、调用scripts/Kbuild.include文件
同uboot一样,Linux顶层Makefile也会调用文件scripts/Kbuild.include,顶层Makefile相应代码如下:
示例代码 顶层Makefile代码段

  1. 345 # We need some generic definitions (do not try to remake the file).
  2. 346 scripts/Kbuild.include: ;
  3. 347 include scripts/Kbuild.include


10、交叉编译工具变量设置
顶层Makefile中其他和交叉编译器有关的变零设置如下:
示例代码 顶层Makefile代码段

  1. 349 # Make variables (CC, etc...)
  2. 350 AS = $(CROSS_COMPILE)as
  3. 351 LD = $(CROSS_COMPILE)ld
  4. 352 CC = $(CROSS_COMPILE)gcc
  5. 353 CPP = $(CC) -E
  6. 354 AR = $(CROSS_COMPILE)ar
  7. 355 NM = $(CROSS_COMPILE)nm
  8. 356 STRIP = $(CROSS_COMPILE)strip
  9. 357 OBJCOPY = $(CROSS_COMPILE)objcopy
  10. 358 OBJDUMP = $(CROSS_COMPILE)objdump


LA、LD、CC等这些都是交叉编译器所使用的工具。
11、头文件路径变量
顶层Makefile定义了两个变量保存头文件路径:USERINCLUDE和LINUXINCLUDE,相关代码如下:
示例代码 顶层Makefile代码段

  1. 380 # Use USERINCLUDE when you must reference the UAPI directories only.
  2. 381 USERINCLUDE :=
  3. 382 -I$(srctree)/arch/$(hdr-arch)/include/uapi
  4. 383 -I$(objtree)/arch/$(hdr-arch)/include/generated/uapi
  5. 384 -I$(srctree)/include/uapi
  6. 385 -I$(objtree)/include/generated/uapi
  7. 386 -include $(srctree)/include/linux/kconfig.h
  8. 387
  9. 388 # Use LINUXINCLUDE when you must reference the include/ directory.
  10. 389 # Needed to be compatible with the O= option
  11. 390 LINUXINCLUDE :=
  12. 391 -I$(srctree)/arch/$(hdr-arch)/include
  13. 392 -I$(objtree)/arch/$(hdr-arch)/include/generated
  14. 393 $(if $(KBUILD_SRC), -I$(srctree)/include)
  15. 394 -I$(objtree)/include
  16. 395 $(USERINCLUDE)


第380~386行的USERINCLUDE是UAPI相关的头文件路径,第390~395行的LINUXINCLUDE是Linux内核源码的头文件路径。重点来看下LINUXINCLUDE,其中srctree=.,objtree=.,hdr-arch=arm,KBUILD_SRC为空,因此,将USERINCLUDE和LINUXINCLUDE展开以后为:

  1. USERINCLUDE :=
  2. -I./arch/arm/include/uapi
  3. -I./arch/arm/include/generated/uapi
  4. -I./include/uapi
  5. -I./include/generated/uapi
  6. -include ./include/linux/kconfig.h
  7. LINUXINCLUDE :=
  8. -I./arch/arm/include
  9. -I./arch/arm/include/generated
  10. -I./include
  11. -I./arch/arm/include/uapi
  12. -I./arch/arm/include/generated/uapi
  13. -I./include/uapi
  14. -I./include/generated/uapi
  15. -include ./include/linux/kconfig.h


12、导出变量
顶层Makefile会导出很多变量给子Makefile使用,导出的这些变量如下:
示例代码 顶层Makefile代码段

  1. 417 export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION
  2. 418 export ARCH SRCARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC
  3. 419 export CPP AR NM STRIP OBJCOPY OBJDUMP HOSTLDFLAGS HOST_LOADLIBES
  4. 420 export MAKE AWK GENKSYMS INSTALLKERNEL PERL PYTHON UTS_MACHINE
  5. 421 export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS
  6. 422
  7. 423 export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS
  8. 424 export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_GCOV CFLAGS_KCOV CFLAGS_KASAN CFLAGS_UBSAN
  9. 425 export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
  10. 426 export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE
  11. 427 export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
  12. 428 export KBUILD_ARFLAGS


1.5.1 make xxx_defconfig过程
第一次编译Linux之前都要先使用“make xxx_defconfig”配置Linux内核,在顶层Makefile中有“%config”这个目标,如下所示:
示例代码 27.5.117.5.1顶层Makefile代码段

  1. 491 config-targets := 0
  2. 492 mixed-targets := 0
  3. 493 dot-config := 1
  4. 494
  5. 495 ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
  6. 496 ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
  7. 497 dot-config := 0
  8. 498 endif
  9. 499 endif
  10. 500
  11. 501 ifeq ($(KBUILD_EXTMOD),)
  12. 502 ifneq ($(filter config %config,$(MAKECMDGOALS)),)
  13. 503 config-targets := 1
  14. 504 ifneq ($(words $(MAKECMDGOALS)),1)
  15. 505 mixed-targets := 1
  16. 506 endif
  17. 507 endif
  18. 508 endif
  19. 509 # install and modules_install need also be processed one by one
  20. 510 ifneq ($(filter install,$(MAKECMDGOALS)),)
  21. 511 ifneq ($(filter modules_install,$(MAKECMDGOALS)),)
  22. 512 mixed-targets := 1
  23. 513 endif
  24. 514 endif
  25. 515
  26. 516 ifeq ($(mixed-targets),1)
  27. 517 # ===========================================================================
  28. 518 # We're called with mixed targets (*config and build targets).
  29. 519 # Handle them one by one.
  30. 520
  31. 521 PHONY += $(MAKECMDGOALS) __build_one_by_one
  32. 522
  33. 523 $(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one
  34. 524 @:
  35. 525
  36. 526 __build_one_by_one:
  37. 527 $(Q)set -e;
  38. 528 for i in $(MAKECMDGOALS); do
  39. 529 $(MAKE) -f $(srctree)/Makefile $i;
  40. 530 done
  41. 531
  42. 532 else
  43. 533 ifeq ($(config-targets),1)
  44. 534 # ===========================================================================
  45. 535 # *config targets only - make sure prerequisites are updated, and descend
  46. 536 # in scripts/kconfig to make the *config target
  47. 537
  48. 538 # Read arch specific Makefile to set KBUILD_DEFCONFIG as needed.
  49. 539 # KBUILD_DEFCONFIG may point out an alternative default configuration
  50. 540 # used for 'make defconfig'
  51. 541 include arch/$(SRCARCH)/Makefile
  52. 542 export KBUILD_DEFCONFIG KBUILD_KCONFIG
  53. 543
  54. 544 config: scripts_basic outputmakefile FORCE
  55. 545 $(Q)$(MAKE) $(build)=scripts/kconfig $@
  56. 546
  57. 547 %config: scripts_basic outputmakefile FORCE
  58. 548 $(Q)$(MAKE) $(build)=scripts/kconfig $@
  59. 549
  60. 550 else
  61. ......
  62. 571 endif # KBUILD_EXTMOD


第491~514行和uboot一样,都是设置定义变量config-targets、mixed-targets和dot-config的值,最终这三个变量的值为:

  1. config-targets= 1
  2. mixed-targets= 0
  3. 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代码段

  1. 448 scripts_basic:
  2. 449 $(Q)$(MAKE) $(build)=scripts/basic
  3. 450 $(Q)rm -f .tmp_quiet_recordmcount


build定义在文件scripts/Kbuild.include中,值为build := -f $(srctree)/scripts/Makefile.build obj,因此将上述示例代码展开就是:

  1. scripts_basic:
  2. @make -f ./scripts/Makefile.build obj=scripts/basic //也可以没有@,视配置而定
  3. @rm -f . tmp_quiet_recordmcount //也可以没有@


接着回到Makefile的目标“%config”处,内容如下:

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


将命令展开就是:

  1. @make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig


1.5.2Makefile.build脚本分析
从上一小节可知,“make xxx_defconfig“配置Linux的时候如下两行命令会执行脚本scripts/Makefile.build:

  1. @make -f ./scripts/Makefile.build obj=scripts/basic
  2. @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代码段

  1. 42 # The filename Kbuild has precedence over Makefile
  2. 43 kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
  3. 44 kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
  4. 45 include $(kbuild-file)


将kbuild-dir展开后为:

  1. kbuild-dir=./scripts/basic


将kbuild-file展开后为:

  1. kbuild-file= ./scripts/basic/Makefile


最后将45行展开,即:

  1. include ./scripts/basic/Makefile


继续分析scripts/Makefile.build,如下代码:
示例代码 27.5.417.5.4 Makefile.build代码段

  1. 96 __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y))
  2. 97 $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target))
  3. 98 $(subdir-ym) $(always)
  4. 99 @:


__build是默认目标,因为命令“@make -f ./scripts/Makefile.build obj=scripts/basic”没有指定目标,所以会使用到默认目标__build。在顶层Makefile中,KBUILD_BUILTIN为1,KBUILD_MODULES为空,因此展开后目标__build为:

  1. __build:$(builtin-target) $(lib-target) $(extra-y)) $(subdir-ym) $(always)
  2. @:


可以看出目标__build有5个依赖:builtin-target、lib-target、extra-y、subdir-ym和always。这5个依赖的具体内容如下:

  1. builtin-target =
  2. lib-target =
  3. extra-y =
  4. subdir-ym =
  5. always = scripts/basic/fixdep scripts/basic/bin2c


只有always有效,因此__build最终为:

  1. __build: scripts/basic/fixdep scripts/basic/bin2c
  2. @:


__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,此命令会使用到的各个变量值如下:

  1. src= scripts/kconfig
  2. kbuild-dir = ./scripts/kconfig
  3. kbuild-file = ./scripts/kconfig/Makefile
  4. include ./scripts/kconfig/Makefile


可以看出,Makefilke.build会读取scripts/kconfig/Makefile中的内容,此文件有如下所示内容:
示例代码 27.5.517.5.5 scripts/kconfig/Makefile代码段

  1. 112 %_defconfig: $(obj)/conf
  2. 113 $(Q)[ DISCUZ_CODE_983 ]lt; $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)


目标%_defconfig与 xxx_defconfig匹配,所以会执行这条规则,将其展开就是:

  1. %_defconfig: scripts/kconfig/conf
  2. @ scripts/kconfig/conf --defconfig=arch/arm/configs/%_defconfig Kconfig


%_defconfig依赖scripts/kconfig/conf,所以会编译scripts/kconfig/conf.c生成conf这个软件。此软件就会将%_defconfig中的配置输出到.config文件中,最终生成Linux kernel根目录下的.config文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值