Uboot顶层Makefile分析

1、选项和参数配置

1.1、版本号
VERSION = 2016        // 主版本号
PATCHLEVEL = 03       // 补丁版本号
SUBLEVEL =            // 次版本号
EXTRAVERSION =        // 附加信息
NAME =                // uboot名称
1.2、MAKEFLAGS

MAKEFLAGS在make执行的过程中传递给子make,用于make命令执行时指定选项和标志,-r 选项用于禁止使用内置的隐含规则,-R 选项用于禁止使用内置的变量定义,--include-dir选项制定搜索路径,$(CURDIR)表示当前目录

MAKEFLAGS += -rR --include-dir=$(CURDIR)
1.3、显示详细编译信息

执行make的时候添加V=1选项可以显示更详细的编译信息。 

ifeq ("$(origin V)", "command line")
  KBUILD_VERBOSE = $(V)
endif
ifndef KBUILD_VERBOSE
  KBUILD_VERBOSE = 0
endif

ifeq ($(KBUILD_VERBOSE),1)
  quiet =
  Q =
else
  quiet=quiet_
  Q = @
endif

添加该选项后 quiet 和Q均为空,下图所示命令执行的时候就可以输出信息,否则Q值为@就不会输出编译信息:

1.4、静默编译

编译的时候使用make -s可以实现静默编译,本质是quiet=silent_:

# If the user is running make -s (silent mode), suppress echoing of
# commands

ifneq ($(filter 4.%,$(MAKE_VERSION)),)	# make-4
ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
  quiet=silent_
endif
else					# make-3.8x
ifneq ($(filter s% -s%,$(MAKEFLAGS)),)
  quiet=silent_
endif
endif
1.5、设置编译结果输出路径

该选项很少用到,编译的时候make  O=filepath,可以指定输出路径KBUILD_OUTPUT 变量保存filepath并供makefile使用

ifeq ($(KBUILD_SRC),)

# OK, Make called in directory where kernel src resides
# Do we want to locate output files in a separate directory?
ifeq ("$(origin O)", "command line")
  KBUILD_OUTPUT := $(O)
endif

# That's our default target when none is given on the command line
PHONY := _all
_all:
# Cancel implicit rules on top Makefile
$(CURDIR)/Makefile Makefile: ;

ifneq ($(KBUILD_OUTPUT),)
# Invoke a second make in the output directory, passing relevant variables
# check that the output directory actually exists
saved-output := $(KBUILD_OUTPUT)
KBUILD_OUTPUT := $(shell mkdir -p $(KBUILD_OUTPUT) && cd $(KBUILD_OUTPUT) \
								&& /bin/pwd)
$(if $(KBUILD_OUTPUT),, \
     $(error failed to create output directory "$(saved-output)"))

PHONY += $(MAKECMDGOALS) sub-make

$(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make
	@:

sub-make: FORCE
	$(Q)$(MAKE) -C $(KBUILD_OUTPUT) KBUILD_SRC=$(CURDIR) \
	-f $(CURDIR)/Makefile $(filter-out _all sub-make,$(MAKECMDGOALS))

# Leave processing to above invocation of make
skip-makefile := 1
endif # ifneq ($(KBUILD_OUTPUT),)
endif # ifeq ($(KBUILD_SRC),)
1.6、源文件检查

该选项很少用到,编译的时候make  C=1,或者C=2,1表示检查需要重新编译的文件,2表示检查所有文件,该值会赋值给变量KBUILD_CHECKSRC,变量在顶层makefile中export KBUILD_CHECKSRC导出给其他文件使用

ifeq ("$(origin C)", "command line")
  KBUILD_CHECKSRC = $(C)
endif
ifndef KBUILD_CHECKSRC
  KBUILD_CHECKSRC = 0
endif
1.7、模块编译

该选项很少用到,编译的时候make  M=modle;KBUILD_EXTMOD变量赋值为modle,_all依赖于modules,makefile会执行相应命令生成模块:

ifdef SUBDIRS
  KBUILD_EXTMOD ?= $(SUBDIRS)
endif

ifeq ("$(origin M)", "command line")
  KBUILD_EXTMOD := $(M)
endif

# If building an external module we do not care about the all: rule
# but instead _all depend on modules
PHONY += all
ifeq ($(KBUILD_EXTMOD),)
_all: all
else
_all: modules
endif
1.8、获取编译平台信息

通过执行shell命令并通过管道进行处理得到HOSTARCH HOSTOS导出:

HOSTARCH := $(shell uname -m | \
	sed -e s/i.86/x86/ \
	    -e s/sun4u/sparc64/ \
	    -e s/arm.*/arm/ \
	    -e s/sa110/arm/ \
	    -e s/ppc64/powerpc/ \
	    -e s/ppc/powerpc/ \
	    -e s/macppc/powerpc/\
	    -e s/sh.*/sh/)

HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
	    sed -e 's/\(cygwin\).*/cygwin/')

export	HOSTARCH HOSTOS

可以打印出信息查看:

1.9、设置编译工具等

HOSTARCH和ARCH一致的话为将CROSS_COMPILE 变量设置为空,KCONFIG_CONFIG 变量未指定的话指定为.config,判断是否存在bash或者/bin/bash,存在的话赋值给变量CONFIG_SHELL 。

# set default to nothing for native builds
ifeq ($(HOSTARCH),$(ARCH))
CROSS_COMPILE ?=
endif

KCONFIG_CONFIG	?= .config
export KCONFIG_CONFIG

# SHELL used by kbuild
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
	  else if [ -x /bin/bash ]; then echo /bin/bash; \
	  else echo sh; fi ; fi)

HOSTCC       = cc
HOSTCXX      = c++
HOSTCFLAGS   = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
HOSTCXXFLAGS = -O2

ifeq ($(HOSTOS),cygwin)
HOSTCFLAGS	+= -ansi
endif

在执行make的时候通常需要指定ARCH和CROSS_COMPILE,可以在此处直接赋值,后续只需要执行make即可:

ARCH ?= arm 
CROSS_COMPILE ?= arm-linux-gnueabihf-

2、make xxx_defconfig执行过程

2.1、目的

执行命令查看详细信息,make xxx_defconfig的最终目的是将配置写入.config中:

2.2 、规则分析

使用以下命令将make规则信息输出到makedata.log中:

make mx6ull_14x14_ddr256_nand_defconfig V=1 -p > makedata.log

查找mx6ull_14x14_ddr256_nand_defconfig,其依赖于scripts/kconfig/conf,规则来源于cripts/kconfig/Makefile的114行:

mx6ull_14x14_ddr256_nand_defconfig: scripts/kconfig/conf
#  recipe to execute (from 'scripts/kconfig/Makefile', line 114):
        $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)

继续查找scripts/kconfig/conf工具的依赖:

scripts/kconfig/conf: FORCE scripts/kconfig/conf.o scripts/kconfig/zconf.tab.o

进一步查找可以发现这些.o文件来源于一些.c文件。

scripts/kconfig/conf.o: scripts/kconfig/conf.c FORCE
scripts/kconfig/zconf.tab.o: scripts/kconfig/zconf.tab.c FORCE scripts/kconfig/zconf.lex.c scripts/kconfig/zconf.hash.c

因此整个过程是通过一些.c文件生成.o最终.o文件被链接成scripts/kconfig/conf,使用该工具执行$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)命令即为make xxxdefconfig的作用,在命令中$(Q)为是否编译输出,无需关注;$<表示第一个依赖,即scripts/kconfig/conf;silent没有设置,无需关注;SRCARCH为“..”;$@为第一个目标,即mx6ull_14x14_ddr256_nand_defconfig;$(Kconfig)为Kconfig,最终执行的命令为:

scripts/kconfig/conf --defconfig=configs/mx6ull_14x14_ddr256_nand_defconfig Kconfig
2.3、工具代码分析

要分析上述命令具体的实现细节则需要查看conf这个工具的源码,即2.2中分析出依赖的几个.c文件,工具的主函数位于scripts/kconfig/conf.c中。

首先通过getopt_long函数处理入参,实现input_mode和defconfig_file的赋值,其中input_mode赋值为defconfig,defconfig_file赋值为configs/mx6ull_14x14_ddr256_nand_defconfig。

	while ((opt = getopt_long(ac, av, "s", long_opts, NULL)) != -1) {
		if (opt == 's') {
			conf_set_message_callback(NULL);
			continue;
		}
		input_mode = (enum input_mode)opt;
		switch (opt) {
		case silentoldconfig:
			sync_kconfig = 1;
			break;
		case defconfig:
		case savedefconfig:
			defconfig_file = optarg;
			break;
        ……
        ……
        ……
		case '?':
			conf_usage(progname);
			exit(1);
			break;
		}
	}

在源码中添加了几行打印信息方便查看赋值,执行到if (ac == optind)时,我们后续还有一个参数Kconfig所以判断不成立不会执行,如果我们调用工具时不添加kconfig参数就会打印错误信息:

	printf("defconfig_file : %s\n",defconfig_file);

	if (ac == optind) {
		printf(_("%s: Kconfig file missing\n"), av[0]);
		conf_usage(progname);
		exit(1);
	}
	name = av[optind];

	printf("name = %s\n", name);
	conf_parse(name);

后续name被赋值为Kconfig,然后调佣conf_parse进行解析处理。处理完Kconfig后调用conf_read处理我们指定的默认配置文件。

处理完Kconfiug和xxx_defconfig后调用conf_write将处理结果写入.config文件:

执行完整过程可以查看添加的打印信息:

3、make执行过程

在顶层目录直接执行make,在Makefile中会查找第一个目标去生成,第一个目标为_all,依赖于all:

ifeq ($(KBUILD_EXTMOD),)
_all: all
else
_all: modules

all依赖于$(ALL-y):

all:		$(ALL-y)

$(ALL-y)的值如下,对于其中的ALL-$(CONFIG_ONENAND_U_BOOT) += u-boot-onenand.bin这样的语句,如果.config中对CONFIG_ONENAND_U_BOOT进行配置的话会有CONFIG_ONENAND_U_BOOT=y的设置,此时就是ALL-y += u-boot-onenand.bin。

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

ALL-$(CONFIG_ONENAND_U_BOOT) += u-boot-onenand.bin
ifeq ($(CONFIG_SPL_FSL_PBL),y)
ALL-$(CONFIG_RAMBOOT_PBL) += u-boot-with-spl-pbl.bin
else
ifneq ($(CONFIG_SECURE_BOOT), y)
# For Secure Boot The Image needs to be signed and Header must also
# be included. So The image has to be built explicitly
ALL-$(CONFIG_RAMBOOT_PBL) += u-boot.pbl
endif
endif
ALL-$(CONFIG_SPL) += spl/u-boot-spl.bin
…………
…………
…………

# Add optional build target if defined in board/cpu/soc headers
ifneq ($(CONFIG_BUILD_TARGET),)
ALL-y += $(CONFIG_BUILD_TARGET:"%"=%)
endif

u-boot.bin为例,在第一行有给$(ALL-y)累加u-boot.bin,查找u-boot.bin的依赖:

ifeq ($(CONFIG_OF_SEPARATE),y)
u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
	$(call if_changed,cat)

u-boot.bin: u-boot-dtb.bin FORCE
	$(call if_changed,copy)
else
u-boot.bin: u-boot-nodtb.bin FORCE
	$(call if_changed,copy)
endif

u-boot.bin依赖于u-boot-nodtb.bin,继续查找:

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

u-boot-nodtb.bin依赖于u-boot,继续查找:

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

其中三项依赖如下,大概意思是通过连接脚本将头部和多个库链接生成u-boot-nodtb.bin:

u-boot-init := $(head-y)
u-boot-main := $(libs-y)
u-boot.lds: $(LDSCRIPT) prepare FORCE

//对于$(head-y)在arch/arm/Makefile指定:
head-y := arch/arm/cpu/$(CPU)/start.o 
//替换后:
head-y := arch/arm/cpu/armv7/start.o

//对于$(libs-y)在顶层Makefile中指定:
libs-y += lib/
libs-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/
libs-$(CONFIG_OF_EMBED) += dts/
libs-y += fs/
libs-y += net/
libs-y += disk/
libs-y += drivers/
libs-y += drivers/dma/
libs-y += drivers/gpio/
libs-y += drivers/i2c/
libs-y += drivers/mmc/
…………
…………
…………

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

对于ALL-y其他依赖可以依次分析。

4、总结

简单介绍了uboot顶层makefile,给初学者提供一些参考。

文章参考:

正点原子左盟主:《I.MX6U嵌入式Linux驱动开发指南》

百文网韦东山老师课程:《u-boot完全分析与移植》

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值