龙空技术网

「正点原子Linux连载」第三十一章U-Boot顶层Makefile详解(二)

正点原子原子哥 147

前言:

此时各位老铁们对“uboot命令行截取字符串”大体比较关心,我们都想要了解一些“uboot命令行截取字符串”的相关知识。那么小编同时在网上汇集了一些对于“uboot命令行截取字符串””的相关文章,希望朋友们能喜欢,你们快快来学习一下吧!

1)实验平台:正点原子Linux开发板

2)摘自《正点原子I.MX6U嵌入式Linux驱动开发指南》

关注官方微信号公众号,获取更多资料:正点原子

31.3.11 交叉编译工具变量设置

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

示例代码31.3.11.1 顶层Makefile代码

331 # Make variables (CC, etc...)

332

333 AS = $(CROSS_COMPILE)as

334 # Always use GNU ld

335 ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2>/dev/null),)

336 LD = $(CROSS_COMPILE)ld.bfd

337else

338 LD = $(CROSS_COMPILE)ld

339 endif

340 CC = $(CROSS_COMPILE)gcc

341 CPP = $(CC)-E

342 AR = $(CROSS_COMPILE)ar

343 NM = $(CROSS_COMPILE)nm

344 LDR = $(CROSS_COMPILE)ldr

345 STRIP = $(CROSS_COMPILE)strip

346 OBJCOPY = $(CROSS_COMPILE)objcopy

347 OBJDUMP = $(CROSS_COMPILE)objdump

31.3.12 导出其他变量

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

示例代码31.3.12.1 顶层Makefile代码

368export VERSION PATCHLEVEL SUBLEVEL UBOOTRELEASE UBOOTVERSION

369export ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR

370export CONFIG_SHELL HOSTCC HOSTCFLAGS HOSTLDFLAGS CROSS_COMPILE AS LD CC

371export CPP AR NM LDR STRIP OBJCOPY OBJDUMP

372export MAKE AWK PERL PYTHON

373export HOSTCXX HOSTCXXFLAGS DTC CHECK CHECKFLAGS

374

375export KBUILD_CPPFLAGS NOSTDINC_FLAGS UBOOTINCLUDE OBJCOPYFLAGS LDFLAGS

376export KBUILD_CFLAGS KBUILD_AFLAGS

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

ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR

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

图31.3.12.1 输出变量值

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

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mytest

结果如图31.3.12.2所示:

图31.3.12.2 变量结果

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

示例代码31.3.12.2 config.mk代码

1 #

2 # (C) Copyright 2000-2013

3 # Wolfgang Denk, DENX Software Engineering, wd@denx.de.

4 #

5 # SPDX-License-Identifier: GPL-2.0+

6 #

7 #########################################################################

8

9 # This file is included from ./Makefile and spl/Makefile.

10 # Clean the state to avoid the same flags added twice.

11 #

12 # (Tegra needs different flags for SPL.

13 # That's the reason why this file must be included from spl/Makefile too.

14 # If we did not have Tegra SoCs, build system would be much simpler...)

15 PLATFORM_RELFLAGS :=

16 PLATFORM_CPPFLAGS :=

17 PLATFORM_LDFLAGS :=

18 LDFLAGS :=

19 LDFLAGS_FINAL :=

20 OBJCOPYFLAGS :=

21 # clear VENDOR for tcsh

22 VENDOR :=

23 #########################################################################

24

25 ARCH := $(CONFIG_SYS_ARCH:"%"=%)

26 CPU := $(CONFIG_SYS_CPU:"%"=%)

27 ifdef CONFIG_SPL_BUILD

28 ifdef CONFIG_TEGRA

29 CPU := arm720t

30 endif

31 endif

32 BOARD := $(CONFIG_SYS_BOARD:"%"=%)

33 ifneq ($(CONFIG_SYS_VENDOR),)

34 VENDOR := $(CONFIG_SYS_VENDOR:"%"=%)

35 endif

36 ifneq ($(CONFIG_SYS_SOC),)

37 SOC := $(CONFIG_SYS_SOC:"%"=%)

38 endif

39

40 # Some architecture config.mk files need to know what CPUDIR is set to,

41 # so calculate CPUDIR before including ARCH/SOC/CPU config.mk files.

42 # Check if arch/$ARCH/cpu/$CPU exists, otherwise assume arch/$ARCH/cpu contains

43 # CPU-specific code.

44 CPUDIR=arch/$(ARCH)/cpu$(if $(CPU),/$(CPU),)

45

46 sinclude $(srctree)/arch/$(ARCH)/config.mk

47 sinclude $(srctree)/$(CPUDIR)/config.mk

48

49 ifdef SOC

50 sinclude $(srctree)/$(CPUDIR)/$(SOC)/config.mk

51 endif

52 ifneq ($(BOARD),)

53 ifdef VENDOR

54 BOARDDIR = $(VENDOR)/$(BOARD)

55else

56 BOARDDIR = $(BOARD)

57 endif

58 endif

59 ifdef BOARD

60 sinclude $(srctree)/board/$(BOARDDIR)/config.mk # include board specific rules

61 endif

62

63 ifdef FTRACE

64 PLATFORM_CPPFLAGS +=-finstrument-functions -DFTRACE

65 endif

66

67 # Allow use of stdint.h if available

68 ifneq ($(USE_STDINT),)

69 PLATFORM_CPPFLAGS +=-DCONFIG_USE_STDINT

70 endif

71

72 #########################################################################

73

74 RELFLAGS := $(PLATFORM_RELFLAGS)

75

76 PLATFORM_CPPFLAGS += $(RELFLAGS)

77 PLATFORM_CPPFLAGS +=-pipe

78

79 LDFLAGS += $(PLATFORM_LDFLAGS)

80 LDFLAGS_FINAL +=-Bstatic

81

82export PLATFORM_CPPFLAGS

83export RELFLAGS

84export LDFLAGS_FINAL

第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/$(ARCH)/config.mk的内容。sinclude读取的文件如果不存在的话不会报错。

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

第50行读取文件$(srctree)/$(CPUDIR)/$(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文件中有定义,定义如下:

示例代码31.3.12.3 .config文件代码

23 CONFIG_SYS_ARCH="arm"

24 CONFIG_SYS_CPU="armv7"

25 CONFIG_SYS_SOC="mx6"

26 CONFIG_SYS_VENDOR="freescale"

27 CONFIG_SYS_BOARD="mx6ullevk "

28 CONFIG_SYS_CONFIG_NAME="mx6ullevk"

根据示例代码31.3.12.3可知:

ARCH = arm

CPU = armv7

BOARD = mx6ullevk

VENDOR = freescale

SOC = mx6

CPUDIR = arch/arm/cpu/armv7

BOARDDIR = freescale/mx6ullevk

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

arch/arm/config.mk

arch/arm/cpu/armv7/config.mk

arch/arm/cpu/armv7/mx6/config.mk (此文件不存在)

board/ freescale/mx6ullevk/config.mk (此文件不存在)

31.3.13 make xxx_defconfig过程

在编译uboot之前要使用“makexxx_defconfig”命令来配置uboot,那么这个配置过程是如何运行的呢?在顶层Makefile中有如下代码:

示例代码31.3.13.1 顶层Makefile代码段

414 # To make sure we donot include .config for any of the *config targets

415 # catch them early,and hand them over to scripts/kconfig/Makefile

416 # It is allowed to specify more targets when calling make, including

417 # mixing *config targets and build targets.

418 # For example 'make oldconfig all'.

419 # Detect when mixed targets is specified,and make a second invocation

420 # of make so .config is not included in thiscase either (for*config).

421

422 version_h := include/generated/version_autogenerated.h

423 timestamp_h := include/generated/timestamp_autogenerated.h

424

425 no-dot-config-targets := clean clobber mrproper distclean \

426 help %docs check% coccicheck \

427 ubootversion backup

428

429 config-targets :=0

430 mixed-targets :=0

431 dot-config :=1

432

433 ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)

434 ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)

435 dot-config :=0

436 endif

437 endif

438

439 ifeq ($(KBUILD_EXTMOD),)

440 ifneq ($(filter config %config,$(MAKECMDGOALS)),)

441 config-targets :=1

442 ifneq ($(words $(MAKECMDGOALS)),1)

443 mixed-targets :=1

444 endif

445 endif

446 endif

447

448 ifeq ($(mixed-targets),1)

449 # ================================================================

450 # We're called with mixed targets (*config and build targets).

451 # Handle them one by one.

452

453 PHONY += $(MAKECMDGOALS) __build_one_by_one

454

455 $(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one

456 @:

457

458 __build_one_by_one:

459 $(Q)set -e; \

460for i in $(MAKECMDGOALS);do \

461 $(MAKE)-f $(srctree)/Makefile $$i; \

462 done

463

464else

465 ifeq ($(config-targets),1)

466 # ================================================================

467 # *config targets only - make sure prerequisites are updated,and descend

468 # in scripts/kconfig to make the *config target

469

470 KBUILD_DEFCONFIG := sandbox_defconfig

471export KBUILD_DEFCONFIG KBUILD_KCONFIG

472

473 config: scripts_basic outputmakefile FORCE

474 $(Q)$(MAKE) $(build)=scripts/kconfig $@

475

476%config: scripts_basic outputmakefile FORCE

477 $(Q)$(MAKE) $(build)=scripts/kconfig $@

478

479else

480 #==================================================================

481 # Build targets only -this includes vmlinux, arch specific targets, clean

482 # targets and others. In general all targets except *config targets.

483

484 ifeq ($(dot-config),1)

485 # Read in config

486-include include/config/auto.conf

......

第422行定义了变量version_h,这变量保存版本号文件,此文件是自动生成的。文件include/generated/version_autogenerated.h内容如图31.3.13.1所示:

图31.3.13.1 版本号文件

第423行定义了变量timestamp_h,此变量保存时间戳文件,此文件也是自动生成的。文件include/generated/timestamp_autogenerated.h内容如图31.3.13.2所示:

图31.3.13.2 时间戳文件

第425行定义了变量no-dot-config-targets。

第429行定义了变量config-targets,初始值为0。

第430行定义了变量mixed-targets,初始值为0。

第431行定义了变量dot-config,初始值为1。

第433行将MAKECMDGOALS中不符合no-dot-config-targets的部分过滤掉,剩下的如果不为空的话条件就成立。MAKECMDGOALS是make的一个环境变量,这个变量会保存你所指定的终极目标列表,比如执行“make mx6ull_alientek_emmc_defconfig”,那么MAKECMDGOALS就为mx6ull_alientek_emmc_defconfig。很明显过滤后为空,所以条件不成立,变量dot-config依旧为1。

第439行判断KBUILD_EXTMOD是否为空,如果KBUILD_EXTMOD为空的话条件成立,经过前面的分析,我们知道KBUILD_EXTMOD为空,所以条件成立。

第440行将MAKECMDGOALS中不符合“config”和“%config”的部分过滤掉,如果剩下的部分不为空条件就成立,很明显此处条件成立,变量config-targets=1。

第442行统计MAKECMDGOALS中的单词个数,如果不为1的话条件成立。此处调用Makefile中的words函数来统计单词个数,words函数格式如下:

$(words <text>)

很明显,MAKECMDGOALS的单词个数是1个,所以条件不成立,mixed-targets继续为0。综上所述,这些变量值如下:

config-targets =1

mixed-targets = 0

dot-config = 1

第448行如果变量mixed-targets为1的话条件成立,很明显,条件不成立。

第465行如果变量config-targets为1的话条件成立,很明显,条件成立,执行这个分支。

第473行,没有目标与之匹配,所以不执行。

第476行,有目标与之匹配,当输入“makexxx_defconfig”的时候就会匹配到%config目标,目标“%config”依赖于scripts_basic、outputmakefile和FORCE。FORCE在顶层Makefile的1610行有如下定义:

示例代码31.3.13.2 顶层Makefile代码段

1610 PHONY += FORCE

1611 FORCE:

可以看出FORCE是没有规则和依赖的,所以每次都会重新生成FORCE。当FORCE作为其他目标的依赖时,由于FORCE总是被更新过的,因此依赖所在的规则总是会执行的。

依赖scripts_basic和outputmakefile在顶层Makefile中的内容如下:

示例代码31.3.13.3 顶层Makefile代码段

394 # Basic helpers built in scripts/

395 PHONY += scripts_basic

396 scripts_basic:

397 $(Q)$(MAKE) $(build)=scripts/basic

398 $(Q)rm -f .tmp_quiet_recordmcount

399

400 # To avoid any implicit rule to kick in, define an empty command.

401 scripts/basic/%: scripts_basic ;

402

403 PHONY += outputmakefile

404 # outputmakefile generates a Makefile in the output directory,if

405 # using a separate output directory. This allows convenient use of

406 # make in the output directory.

407 outputmakefile:

408 ifneq ($(KBUILD_SRC),)

409 $(Q)ln -fsn $(srctree) source

410 $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \

411 $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)

412 endif

第408行,判断KBUILD_SRC是否为空,只有变量KBUILD_SRC不为空的时候outputmakefile才有意义,经过我们前面的分析KBUILD_SRC为空,所以outputmakefile无效。只有scripts_basic是有效的。

第396~398行是scripts_basic的规则,其对应的命令用到了变量Q、MAKE和build,其中:

Q=@或为空

MAKE=make

变量build是在scripts/Kbuild.include文件中有定义,定义如下:

示例代码31.3.13.3 Kbuild.include代码段

177 ###

178 # Shorthand for $(Q)$(MAKE)-f scripts/Makefile.build obj=

179 # Usage:

180 # $(Q)$(MAKE) $(build)=dir

181 build :=-f $(srctree)/scripts/Makefile.build obj

从示例代码31.3.13.3可以看出build=-f $(srctree)/scripts/Makefile.build obj,经过前面的分析可知,变量srctree为”.”,因此:

build=-f ./scripts/Makefile.build obj

scripts_basic展开以后如下:

scripts_basic:

@make -f ./scripts/Makefile.build obj=scripts/basic //也可以没有@,视配置而定

@rm -f . tmp_quiet_recordmcount //也可以没有@

scripts_basic会调用文件./scripts/Makefile.build,这个我们后面在分析。

接着回到示例代码31.3.13.1中的%config处,内容如下:

%config: scripts_basic outputmakefile FORCE

$(Q)$(MAKE) $(build)=scripts/kconfig $@

将命令展开就是:

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

同样也跟文件./scripts/Makefile.build有关,我们后面再分析此文件。使用如下命令配置uboot,并观察其配置过程:

make mx6ull_14x14_ddr512_emmc_defconfig V=1

配置过程如图31.3.13.1所示:

图31.3.13.1 uboot配置过程

从图31.3.13.1可以看出,我们的分析是正确的,接下来就要结合下面两行命令重点分析一下文件scripts/Makefile.build。

①、scripts_basic目标对应的命令

@make -f ./scripts/Makefile.build obj=scripts/basic

②、%config目标对应的命令

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

31.3.14 Makefile.build脚本分析

从上一小节可知,“makexxx_defconfig“配置uboot的时候如下两行命令会执行脚本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,有如下代码:

示例代码31.3.14.1 Makefile.build代码段

8 # Modified for U-Boot

9 prefix := tpl

10 src := $(patsubst $(prefix)/%,%,$(obj))

11 ifeq ($(obj),$(src))

12 prefix := spl

13 src := $(patsubst $(prefix)/%,%,$(obj))

14 ifeq ($(obj),$(src))

15 prefix :=.

16 endif

17 endif

第9行定义了变量prefix值为tpl。

第10行定义了变量src,这里用到了函数patsubst,此行代码展开后为:

$(patsubst tpl/%,%, scripts/basic)

patsubst是替换函数,格式如下:

$(patsubst <pattern>,<replacement>,<text>)

此函数用于在text中查找符合pattern的部分,如果匹配的话就用replacement替换掉。pattenr是可以包含通配符“%”,如果replacement中也包含通配符“%”,那么replacement中的这个“%”将是pattern中的那个“%”所代表的字符串。函数的返回值为替换后的字符串。因此,第10行就是在“scripts/basic”中查找符合“tpl/%”的部分,然后将“tpl/”取消掉,但是“scripts/basic”没有“tpl/”,所以src= scripts/basic。

第11行判断变量obj和src是否相等,相等的话条件成立,很明显,此处条件成立。

第12行和第9行一样,只是这里处理的是“spl”,“scripts/basic”里面也没有“spl/”,所以src继续为scripts/basic。

第15行因为变量obj和src相等,所以prefix=.。

继续分析scripts/Makefile.build,有如下代码:

示例代码31.3.14.2 Makefile.build代码段

56 # The filename Kbuild has precedence over Makefile

57 kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))

58 kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)

59 include $(kbuild-file)

将kbuild-dir展开后为:

$(if $(filter /%, scripts/basic), scripts/basic, ./scripts/basic),

因为没有以“/”为开头的单词,所以$(filter /%, scripts/basic)的结果为空,kbuild-dir=./scripts/basic。

将kbuild-file展开后为:

$(if $(wildcard ./scripts/basic/Kbuild), ./scripts/basic/Kbuild, ./scripts/basic/Makefile)

因为scrpts/basic目录中没有Kbuild这个文件,所以kbuild-file= ./scripts/basic/Makefile。最后将59行展开,即:

include./scripts/basic/Makefile

也就是读取scripts/basic下面的Makefile文件。

继续分析scripts/Makefile.build,如下代码:

示例代码31.3.14.3 Makefile.build代码段

116 __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \

117 $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \

118 $(subdir-ym) $(always)

119 @:

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

__build:$(builtin-target) $(lib-target) $(extra-y)) $(subdir-ym) $(always)

@:

可以看出目标__build有5个依赖:builtin-target、lib-target、extra-y、subdir-ym和always。这5个依赖的具体内容我们就不通过源码来分析了,直接在scripts/Makefile.build中输入图31.3.14.1所示内容,将这5个变量的值打印出来:

图31.3.14.1 输出变量

执行如下命令:

make mx6ull_14x14_ddr512_emmc_defconfig V=1

结果如图31.3.14.2所示:

图31.3.14.2 输出结果

从上图可以看出,只有always有效,因此__build最终为:

__build: scripts/basic/fixdep

@:

__build依赖于scripts/basic/fixdep,所以要先scripts/basic/fixdep.c编译,生成fixdep,前面已经读取了scripts/basic/Makefile文件。

综上所述,scripts_basic目标的作用就是编译出scripts/basic/fixdep这个软件。

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中的内容,此文件有如下所示内容:

示例代码31.3.14.4 scripts/kconfig/Makefile代码段

113%_defconfig: $(obj)/conf

114 $(Q)$< $(silent)--defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)

115

116 # Added for U-Boot (backward compatibility)

117%_config:%_defconfig

118 @:

目标%_defconfig刚好和我们输入的xxx_defconfig匹配,所以会执行这条规则。依赖为$(obj)/conf,展开后就是scripts/kconfig/conf。接下来就是检查并生成依赖scripts/kconfig/conf。conf是主机软件,到这里我们就打住,不要纠结conf是怎么编译出来的,否则就越陷越深,太绕了,像conf这种主机所使用的工具类软件我们一般不关心它是如何编译产生的。如果一定要看是conf是怎么生成的,可以输入如下命令重新配置uboot,在重新配置uboot的过程中就会输出conf编译信息。

make distclean

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_

defconfig V=1

结果如图31.3.14.3所示:

图31.3.14.3 编译过程

得到scripts/kconfig/conf以后就要执行目标%_defconfig的命令:

$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)

相关的变量值如下:

silent=-s或为空

SRCARCH=..

Kconfig=Kconfig

将其展开就是:

@ scripts/kconfig/conf --defconfig=arch/../configs/xxx_defconfig Kconfig

上述命令用到了xxx_defconfig文件,比如mx6ull_alientek_emmc_defconfig。这里会将mx6ull_alientek_emmc_defconfig中的配置输出到.config文件中,最终生成uboot根目录下的.config文件。

这个就是命令makexxx_defconfig执行流程,总结一下如图31.3.14.4所示:

图31.3.14.4 makexxx_defconfig执行流程图

至此,makexxx_defconfig就分析完了,接下来就要分析一下u-boot.bin是怎么生成的了。

31.3.15 make过程

配置好uboot以后就可以直接make编译了,因为没有指明目标,所以会使用默认目标,主Makefile中的默认目标如下:

示例代码31.3.15.1 顶层Makefile代码段

128 # That's our default target when none is given on the command line

129 PHONY := _all

130 _all:

目标_all又依赖于all,如下所示:

示例代码31.3.15.2 顶层Makefile代码段

194 # If building an external module we donot care about the all: rule

195 # but instead _all depend on modules

196 PHONY += all

197 ifeq ($(KBUILD_EXTMOD),)

198 _all: all

199else

200 _all: modules

201 endif

如果KBUILD_EXTMOD为空的话_all依赖于all。这里不编译模块,所以KBUILD_EXTMOD肯定为空,_all的依赖就是all。在主Makefile中all目标规则如下:

示例代码31.3.15.2 顶层Makefile代码段

802 all: $(ALL-y)

803 ifneq ($(CONFIG_SYS_GENERIC_BOARD),y)

804 @echo "===================== WARNING ======================"

805 @echo "Please convert this board to generic board."

806 @echo "Otherwise it will be removed by the end of 2014."

807 @echo "See doc/README.generic-board for further information"

808 @echo "===================================================="

809 endif

810 ifeq ($(CONFIG_DM_I2C_COMPAT),y)

811 @echo "===================== WARNING ======================"

812 @echo "This board uses CONFIG_DM_I2C_COMPAT. Please remove"

813 @echo "(possibly in a subsequent patch in your series)"

814 @echo "before sending patches to the mailing list."

815 @echo "===================================================="

816 endif

从802行可以看出,all目标依赖$(ALL-y),而在顶层Makefile中,ALL-y如下:

示例代码31.3.15.3 顶层Makefile代码段

730 # Always append ALL so that arch config.mk's can add custom ones

731 ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map u-boot.cfg binary_size_check

732

733 ALL-$(CONFIG_ONENAND_U_BOOT)+= u-boot-onenand.bin

734 ifeq ($(CONFIG_SPL_FSL_PBL),y)

735 ALL-$(CONFIG_RAMBOOT_PBL)+= u-boot-with-spl-pbl.bin

736else

737 ifneq ($(CONFIG_SECURE_BOOT), y)

738 # For Secure Boot The Image needs to be signedand Header must also

739 # be included. So The image has to be built explicitly

740 ALL-$(CONFIG_RAMBOOT_PBL)+= u-boot.pbl

741 endif

742 endif

743 ALL-$(CONFIG_SPL)+= spl/u-boot-spl.bin

744 ALL-$(CONFIG_SPL_FRAMEWORK)+= u-boot.img

745 ALL-$(CONFIG_TPL)+= tpl/u-boot-tpl.bin

746 ALL-$(CONFIG_OF_SEPARATE)+= u-boot.dtb

747 ifeq ($(CONFIG_SPL_FRAMEWORK),y)

748 ALL-$(CONFIG_OF_SEPARATE)+= u-boot-dtb.img

749 endif

750 ALL-$(CONFIG_OF_HOSTFILE)+= u-boot.dtb

751 ifneq ($(CONFIG_SPL_TARGET),)

752 ALL-$(CONFIG_SPL)+= $(CONFIG_SPL_TARGET:"%"=%)

753 endif

754 ALL-$(CONFIG_REMAKE_ELF)+= u-boot.elf

755 ALL-$(CONFIG_EFI_APP)+= u-boot-app.efi

756 ALL-$(CONFIG_EFI_STUB)+= u-boot-payload.efi

757

758 ifneq ($(BUILD_ROM),)

759 ALL-$(CONFIG_X86_RESET_VECTOR)+= u-boot.rom

760 endif

761

762 # enable combined SPL/u-boot/dtb rules for tegra

763 ifeq ($(CONFIG_TEGRA)$(CONFIG_SPL),yy)

764 ALL-y += u-boot-tegra.bin u-boot-nodtb-tegra.bin

765 ALL-$(CONFIG_OF_SEPARATE)+= u-boot-dtb-tegra.bin

766 endif

767

768 # Add optional build target if defined in board/cpu/soc headers

769 ifneq ($(CONFIG_BUILD_TARGET),)

770 ALL-y += $(CONFIG_BUILD_TARGET:"%"=%)

771 endif

从示例代码代码31.3.15.3可以看出,ALL-y包含u-boot.srec、u-boot.bin、u-boot.sym、System.map、u-boot.cfg 和binary_size_check这几个文件。根据uboot的配置情况也可能包含其他的文件,比如:

ALL-$(CONFIG_ONENAND_U_BOOT) += u-boot-onenand.bin

CONFIG_ONENAND_U_BOOT就是uboot中跟ONENAND配置有关的,如果我们使能了ONENAND,那么在.config配置文件中就会有“CONFIG_ONENAND_U_BOOT=y”这一句。相当于CONFIG_ONENAND_U_BOOT是个变量,这个变量的值为“y”,所以展开以后就是:

ALL-y += u-boot-onenand.bin

这个就是.config里面的配置参数的含义,这些参数其实都是变量,后面跟着变量值,会在顶层Makefile或者其他Makefile中调用这些变量。

ALL-y里面有个u-boot.bin,这个就是我们最终需要的uboot二进制可执行文件,所作的所有工作就是为了它。在顶层Makefile中找到u-boot.bin目标对应的规则,如下所示:

示例代码31.3.15.4 顶层Makefile代码段

825 ifeq ($(CONFIG_OF_SEPARATE),y)

826 u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE

827 $(call if_changed,cat)

828

829 u-boot.bin: u-boot-dtb.bin FORCE

830 $(call if_changed,copy)

831else

832 u-boot.bin: u-boot-nodtb.bin FORCE

833 $(call if_changed,copy)

834 endif

第825行判断CONFIG_OF_SEPARATE是否等于y,如果相等,那条件就成立,在.config中搜索“CONFIG_OF_SEPARAT”,没有找到,说明条件不成立。

第832行就是目标u-boot.bin的规则,目标u-boot.bin依赖于u-boot-nodtb.bin,命令为$(call if_changed,copy),这里调用了if_changed,if_changed是一个函数,这个函数在scripts/Kbuild.include中有定义,而顶层Makefile中会包含scripts/Kbuild.include文件,这个前面已经说过了。

if_changed在Kbuild.include中的定义如下:

示例代码31.3.15.5 Kbuild.include代码段

226 ###

227 # if_changed - execute command if any prerequisite is newer than

228 # target,or command line has changed

229 # if_changed_dep - as if_changed, but uses fixdep to reveal

230 # dependencies including used config symbols

231 # if_changed_rule - as if_changed but execute rule instead

232 # See Documentation/kbuild/makefiles.txt for more info

233

234 ifneq ($(KBUILD_NOCMDDEP),1)

235 # Check if both arguments has same arguments. Result is empty string if equal.

236 # User may overridethis check using make KBUILD_NOCMDDEP=1

237 arg-check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \

238 $(filter-out $(cmd_$@), $(cmd_$(1))))

239else

240 arg-check = $(if $(strip $(cmd_$@)),,1)

241 endif

242

243 # Replace >$< with >$$< to preserve $ when reloading the .cmd file

244 # (needed for make)

245 # Replace >#< with >\#< to avoid starting a comment in the .cmd file

246 # (needed for make)

247 # Replace >'< with >'\''< to be able to enclose the whole string in '...'

248 # (needed for the shell)

249 make-cmd = $(call escsq,$(subst \#,\\\#,$(subst $$,$$$$,$(cmd_$(1)))))

250

251 # Find any prerequisites that is newer than target or that does not exist.

252 # PHONY targets skipped in both cases.

253 any-prereq = $(filter-out $(PHONY),$?) $(filter-out $(PHONY) $(wildcard $^),$^)

254

255 # Execute command if command has changed or prerequisite(s) are updated.

256 #

257 if_changed = $(if $(strip $(any-prereq) $(arg-check)), \

258 @set -e; \

259 $(echo-cmd) $(cmd_$(1)); \

260 printf '%s\n''cmd_$@ := $(make-cmd)'> $(dot-target).cmd)

261

第227行为 if_changed的描述,根据描述,在一些先决条件比目标新的时候,或者命令行有改变的时候,if_changed就会执行一些命令。

第257行就是函数if_changed,if_changed函数引用的变量比较多,也比较绕,我们只需要知道它可以从u-boot-nodtb.bin生成u-boot.bin就行了。

既然u-boot.bin依赖于u-boot-nodtb.bin,那么肯定要先生成u-boot-nodtb.bin文件,顶层Makefile中相关代码如下:

示例代码31.3.15.6 顶层Makefile代码段

866 u-boot-nodtb.bin: u-boot FORCE

867 $(call if_changed,objcopy)

868 $(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))

869 $(BOARD_SIZE_CHECK)

目标u-boot-nodtb.bin又依赖于u-boot,顶层Makefile中u-boot相关规则如下:

示例代码31.3.15.7 顶层Makefile代码段

1170 u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE

1171 $(call if_changed,u-boot__)

1172 ifeq ($(CONFIG_KALLSYMS),y)

1173 $(call cmd,smap)

1174 $(call cmd,u-boot__) common/system_map.o

1175 endif

目标u-boot依赖于u-boot_init、u-boot-main和u-boot.lds,u-boot_init和u-boot-main是两个变量,在顶层Makefile中有定义,值如下:

示例代码31.3.15.8 顶层Makefile代码段

678 u-boot-init := $(head-y)

679 u-boot-main := $(libs-y)

$(head-y)跟CPU架构有关,我们使用的是ARM芯片,所以head-y在arch/arm/Makefile中被指定为:

head-y := arch/arm/cpu/$(CPU)/start.o

根据31.3.12小节的分析,我们知道CPU=armv7,因此head-y展开以后就是:

head-y := arch/arm/cpu/armv7/start.o

因此:

u-boot-init= arch/arm/cpu/armv7/start.o

$(libs-y)在顶层Makefile中被定义为uboot所有子目录下build-in.o的集合,代码如下:

示例代码31.3.15.9 顶层Makefile代码段

620 libs-y += lib/

621 libs-$(HAVE_VENDOR_COMMON_LIB)+= board/$(VENDOR)/common/

622 libs-$(CONFIG_OF_EMBED)+= dts/

623 libs-y += fs/

624 libs-y += net/

625 libs-y += disk/

626 libs-y += drivers/

627 libs-y += drivers/dma/

628 libs-y += drivers/gpio/

629 libs-y += drivers/i2c/

......

660 libs-y += cmd/

661 libs-y += common/

662 libs-$(CONFIG_API)+= api/

663 libs-$(CONFIG_HAS_POST)+= post/

664 libs-y += test/

665 libs-y += test/dm/

666 libs-$(CONFIG_UT_ENV)+= test/env/

667

668 libs-y += $(if $(BOARDDIR),board/$(BOARDDIR)/)

669

670 libs-y := $(sort $(libs-y))

671

672 u-boot-dirs := $(patsubst %/,%,$(filter %/, $(libs-y))) tools examples

673

674 u-boot-alldirs := $(sort $(u-boot-dirs) $(patsubst %/,%,$(filter %/, $(libs-))))

675

676 libs-y := $(patsubst %/, %/built-in.o, $(libs-y))

从上面的代码可以看出,libs-y都是uboot各子目录的集合,最后:

libs-y := $(patsubst %/, %/built-in.o, $(libs-y))

这里调用了函数patsubst,将libs-y中的“/”替换为”/built-in.o”,比如“drivers/dma/”就变为了“drivers/dma/built-in.o”,相当于将libs-y改为所有子目录中built-in.o文件的集合。那么u-boot-main就等于所有子目录中built-in.o的集合。

这个规则就相当于将以u-boot.lds为链接脚本,将arch/arm/cpu/armv7/start.o和各个子目录下的built-in.o链接在一起生成u-boot。

u-boot.lds的规则如下:

示例代码31.3.15.10 顶层Makefile代码段

u-boot.lds: $(LDSCRIPT) prepare FORCE

$(call if_changed_dep,cpp_lds)

接下来的重点就是各子目录下的built-in.o是怎么生成的,以drivers/gpio/built-in.o为例,在drivers/gpio/目录下会有个名为.built-in.o.cmd的文件,此文件内容如下:

示例代码31.3.15.11 drivers/gpio/.built-in.o.cmd代码

1 cmd_drivers/gpio/built-in.o := arm-linux-gnueabihf-ld.bfd -r -o drivers/gpio/built-in.o drivers/gpio/mxc_gpio.o

从命令“cmd_drivers/gpio/built-in.o”可以看出,drivers/gpio/built-in.o这个文件是使用ld命令由文件drivers/gpio/mxc_gpio.o生成而来的,mxc_gpio.o是mxc_gpio.c编译生成的.o文件,这个是NXP的I.MX系列的GPIO驱动文件。这里用到了ld的“-r”参数,参数含义如下:

-r –relocateable: 产生可重定向的输出,比如,产生一个输出文件它可再次作为‘ld’的输入,这经常被叫做“部分链接”,当我们需要将几个小的.o文件链接成为一个.o文件的时候,需要使用此选项。

最终将各个子目录中的built-in.o文件链接在一起就形成了u-boot,使用如下命令编译uboot就可以看到链接的过程:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_

defconfig V=1

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- V=1

编译的时候会有如图31.3.15.1所示内容输出:

图31.3.15.1 编译内容输出

将其整理一下,内容如下:

arm-linux-gnueabihf-ld.bfd -pie --gc-sections -Bstatic -Ttext 0x87800000 \

-o u-boot -T u-boot.lds \

arch/arm/cpu/armv7/start.o \

--start-group arch/arm/cpu/built-in.o \

arch/arm/cpu/armv7/built-in.o \

arch/arm/imx-common/built-in.o \

arch/arm/lib/built-in.o \

board/freescale/common/built-in.o \

board/freescale/mx6ull_alientek_emmc/built-in.o \

cmd/built-in.o \

common/built-in.o \

disk/built-in.o \

drivers/built-in.o \

drivers/dma/built-in.o \

drivers/gpio/built-in.o \

……

drivers/spi/built-in.o \

drivers/usb/dwc3/built-in.o \

drivers/usb/emul/built-in.o \

drivers/usb/eth/built-in.o \

drivers/usb/gadget/built-in.o \

drivers/usb/gadget/udc/built-in.o \

drivers/usb/host/built-in.o \

drivers/usb/musb-new/built-in.o \

drivers/usb/musb/built-in.o \

drivers/usb/phy/built-in.o \

drivers/usb/ulpi/built-in.o \

fs/built-in.o \

lib/built-in.o \

net/built-in.o \

test/built-in.o \

test/dm/built-in.o \

--end-group arch/arm/lib/eabi_compat.o \

-L /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/../lib/gcc/arm-linux-gnueabihf/4.9.4 -lgcc -Map u-boot.map

可以看出最终是用arm-linux-gnueabihf-ld.bfd命令将arch/arm/cpu/armv7/start.o和其他众多的built_in.o链接在一起,形成u-boot。

目标all除了u-boot.bin以外还有其他的依赖,比如u-boot.srec 、u-boot.sym 、System.map、u-boot.cfg和binary_size_check等等,这些依赖的生成方法和u-boot.bin很类似,大家自行查看一下顶层Makefile,我们就不详细的讲解了。

总结一下“make”命令的流程,如图31.3.15.2所示:

图31.3.15.2 make命令流程

图31.3.15.2就是“make”命令的执行流程,关于uboot的顶层Makefile就分析到这里,重点是“makexxx_defconfig”和“make”这两个命令的执行流程:

makexxx_defconfig:用于配置uboot,这个命令最主要的目的就是生成.config文件。

make:用于编译uboot,这个命令的主要工作就是生成二进制的u-boot.bin文件和其他的一些与uboot有关的文件,比如u-boot.imx等等。

关于uboot的顶层Makefile就分析到这里,有些内容我们没有详细、深入的去研究,因为我们的重点是使用uboot,而不是uboot的研究者,我们要做的是缕清uboot的流程。至于更具体的实现,有兴趣的可以参考一下其他资料。

标签: #uboot命令行截取字符串