kehuadong

uboot之顶层Makefile

uboot版本信息

VERSION = 2006 主版本号

PATCHLEVEL = 03 补丁版本号

SUBLEVEL = 次此版本号
EXTRAVERSION =  附加版本
NAME 
 
 
 

MAKEFLAGS变量

$(MAKE) -C subdir $(MAKE)就是调用“make”命令,-C 指定子目录
export VARIABLE …… //导出变量给子 make 。
unexport VARIABLE…… //不导出变量给子 make。
MAKEFLAGS += -rR --include-dir=$(CURDIR) 上述代码使用“+=”来给变量 MAKEFLAGS 追加了一些值,“-rR”表示禁止使用内置的隐含规则和变量定义,“--include-dir”指明搜索路径,”$(CURDIR)”表示当前目录。
 
 
命令输出
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
 
 
V=1 的话:
KBUILD_VERBOSE=1
quiet= 空 。
Q= 空。
V=0 或者命令行不定义 V 的话:
KBUILD_VERBOSE=0
quiet= quiet_。
Q= @。
 
make V=1  变量V=1
 
静默输出
 
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
 
export quiet Q KBUILD_VERBOSE
 
make -s  选项中的第一个单词
 
 
设置编译结果输出目录
 make O=out 生成的文件和源文件分开, 否则生成的文件在源文件的目录
 
 
代码检查
使用命令“make C=1”使能代码检查,检查那些需要重新编译的文
件。“make C=2”用于检查所有的源码文件
 
 
模块编译
使用命令“make M=dir”即可,旧语法“make
SUBDIRS=dir”也是支持的
 
ifeq ("$(origin M)", "command line")
  KBUILD_EXTMOD := $(M)
endif
 
ifeq ($(KBUILD_SRC),)
# building in the source tree
  srctree := .
else
  ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
    # building in a subdirectory of the source tree
    srctree := ..
  else
    srctree := $(KBUILD_SRC)
  endif
endif
objtree := .
src := $(srctree)
obj := $(objtree)
 
VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
export srctree objtree VPATH
 
 
获取主机架构和系统
 
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
 
设置目标架构、交叉编译器和配置文件 
# set default to nothing for native builds
ifeq ($(HOSTARCH),$(ARCH))
  CROSS_COMPILE ?=
endif
 
KCONFIG_CONFIG ?= .config
export KCONFIG_CONFIG
 
调用 scripts/Kbuild.include
scripts/Kbuild.include: ;
include scripts/Kbuild.include
 
交叉编译工具变量设置
# Make variables (CC, etc...)
AS  = $(CROSS_COMPILE)as
# Always use GNU ld
ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2> /dev/null),)
  LD = $(CROSS_COMPILE)ld.bfd
else
  LD= $(CROSS_COMPILE)ld
endif
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
LDR = $(CROSS_COMPILE)ldr
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
 
 
导出其他变量
export VERSION PATCHLEVEL SUBLEVEL UBOOTRELEASE UBOOTVERSION
export ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR
export CONFIG_SHELL HOSTCC HOSTCFLAGS HOSTLDFLAGS CROSS_COMPILE AS LD CC
export CPP AR NM LDR STRIP OBJCOPY OBJDUMP
export MAKE AWK PERL PYTHON
export HOSTCXX HOSTCXXFLAGS DTC CHECK CHECKFLAGS
 
export KBUILD_CPPFLAGS NOSTDINC_FLAGS UBOOTINCLUDE OBJCOPYFLAGS LDFLAGS
export KBUILD_CFLAGS KBUILD_AFLAGS
 
 
config.mk 配置了ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR等
 
make xxx_defconfig 过程
 
version_h := include/generated/version_autogenerated.h
timestamp_h := include/generated/timestamp_autogenerated.h
 
no-dot-config-targets := clean clobber mrproper distclean \
  help %docs check% coccicheck \
  ubootversion backup
 
config-targets := 0
mixed-targets := 0
dot-config := 1
 
ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
  ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
    dot-config := 0
  endif
endif
 
ifeq ($(KBUILD_EXTMOD),)
  ifneq ($(filter config %config,$(MAKECMDGOALS)),)
    config-targets := 1
    ifneq ($(words $(MAKECMDGOALS)),1)
      mixed-targets := 1
    endif
  endif
endif 
 
%config: scripts_basic outputmakefile FORCE
  $(Q)$(MAKE) $(build)=scripts/kconfig $@
 
 
 
MAKECMDGOALS 是 make 的一个环境变量,这个变量会保存你所指定的终极目标列表,比如执行“make mx6ull_alientek_emmc_defconfig”
ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)    保留$(MAKECMDGOALS)符合$(no-dot-config-targets)的部分,
这里得到了空,ifneq不成立, dot-config := 1保持不变
 
不单独编译模块则$(KBUILD_EXTMOD)为空, 所以ifeq ($(KBUILD_EXTMOD),)成立
ifneq ($(filter config %config,$(MAKECMDGOALS)),)的%config匹配了make mx6ull_alientek_emmc_defconfig
所以config-targets := 1
 
ifneq ($(words $(MAKECMDGOALS)),1) 多个目标, 这里不成立
 
因此最后有
config-targets = 1
mixed-targets = 0
dot-config = 1
 
make mx6ull_alientek_emmc_defconfig 匹配了目标 %config
%config: scripts_basic outputmakefile FORCE
  $(Q)$(MAKE) $(build)=scripts/kconfig $@
 
其中FORCE没有规则和依赖, 因此FORCE总是新的, 因此$(Q)$(MAKE) $(build)=scripts/kconfig $@总是被执行
PHONY += scripts_basic
PHONY += FORCE  
FORCE:
 
PHONY += scripts_basic
scripts_basic:
  $(Q)$(MAKE) $(build)=scripts/basic
  $(Q)rm -f .tmp_quiet_recordmcount
 
scripts/basic/%: scripts_basic ;
 
变量 build 是在 scripts/Kbuild.include 文件中有定义,定义如下:
build := -f $(srctree)/scripts/Makefile.build obj
即build=-f ./scripts/Makefile.build obj
scripts_basic 展开以后如下
scripts_basic:
  @make -f ./scripts/Makefile.build obj=scripts/basic //也可以没有@,视配置而定
  @rm -f . tmp_quiet_recordmcount //也可以没有@
 
同样 %config展开有
%config: scripts_basic outputmakefile FORCE    
  $(Q)$(MAKE) $(build)=scripts/kconfig $@   =>  @make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
 
PHONY += outputmakefile
outputmakefile:
ifneq ($(KBUILD_SRC),)
  $(Q)ln -fsn $(srctree) source
  $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
    $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif
 
 这里$(KBUILD_SRC)为空, ifneq ($(KBUILD_SRC),)不成立
 
最终
scripts_basic 目标对应的命令 @make -f ./scripts/Makefile.build obj=scripts/basic
%config 目标对应的命令 @make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
其中 %config依赖scripts_basic,另外两个outputmakefile, FORCE没有构建命令
 
Makefile.build 脚本分析
 
scripts_basic 目标对应的命令
 
# Modified for U-Boot
prefix := tpl
src := $(patsubst $(prefix)/%,%,$(obj))
ifeq ($(obj),$(src))
prefix := spl
src := $(patsubst $(prefix)/%,%,$(obj))
ifeq ($(obj),$(src))
prefix := .
endif
endif
 
对于@make -f ./scripts/Makefile.build obj=scripts/basic
有src := $(patsubst $(prefix)/%,%,$(obj)) = $(patsubst tpl/%,%, scripts/basic), 其中scripts/basic不符合tpl/%, 没有东西被替换
所以src := scripts/basic    prefix  := .
 
 
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
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
 
最后为 include ./scripts/basic/Makefile
 
 
 
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
$(subdir-ym) $(always)
  @:
 
__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)
  @:
 
只有 always 有效,因此__build 最终为 __build: scripts/basic/fixdep
scripts_basic 目标的作用就是编译出 scripts/basic/fixdep 这个软件
 
%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 中的内容,此文件有如下所示内容
%_defconfig: $(obj)/conf
  $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
 
 # Added for U-Boot (backward compatibility)
%_config: %_defconfig
  @:
 
$(obj)/conf 为编译出来的主机工具
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)最终是@ scripts/kconfig/conf --defconfig=arch/../configs/xxx_defconfig Kconfig
 
最后make xxx_defconfig 执行流程总结
make xxx_defconfig -> 顶层Makefile中的%config
依赖  
  scripts_basic
    make -f ./scripts/Makefile.build obj=scripts/basic  -> 生成 script/basic/fixdep
  outputmakefile
  FORCE
 
命令 make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig 生成  scripts/kconfig/conf 
命令 scripts/kconfig/conf --defconfig=arch/../configs/xxx_defconfig Kconfig 生成 .config
 
 
 
make 过程
PHONY := _all
_all:
 
PHONY += all
ifeq ($(KBUILD_EXTMOD),)
_all: all
else
_all: modules
endif
 
在主 Makefile 中 all 目标规则如下
all: $(ALL-y)
ifneq ($(CONFIG_SYS_GENERIC_BOARD),y)
  @echo "===================== WARNING ======================"
  @echo "Please convert this board to generic board."
  @echo "Otherwise it will be removed by the end of 2014."
  @echo "See doc/README.generic-board for further information"
  @echo "===================================================="
endif
ifeq ($(CONFIG_DM_I2C_COMPAT),y)
  @echo "===================== WARNING ======================"
  @echo "This board uses CONFIG_DM_I2C_COMPAT. Please remove"
  @echo "(possibly in a subsequent patch in your series)"
  @echo "before sending patches to the mailing list."
  @echo "===================================================="
endif
 
all 目标依赖$(ALL-y),而在顶层 Makefile 中,ALL-y 如下
# Always append ALL so that arch config.mk's can add custom ones
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
ALL-$(CONFIG_SPL_FRAMEWORK) += u-boot.img
ALL-$(CONFIG_TPL) += tpl/u-boot-tpl.bin
ALL-$(CONFIG_OF_SEPARATE) += u-boot.dtb
ifeq ($(CONFIG_SPL_FRAMEWORK),y)
  ALL-$(CONFIG_OF_SEPARATE) += u-boot-dtb.img
endif
ALL-$(CONFIG_OF_HOSTFILE) += u-boot.dtb
ifneq ($(CONFIG_SPL_TARGET),)
  ALL-$(CONFIG_SPL) += $(CONFIG_SPL_TARGET:"%"=%)
endif
ALL-$(CONFIG_REMAKE_ELF) += u-boot.elf
ALL-$(CONFIG_EFI_APP) += u-boot-app.efi
ALL-$(CONFIG_EFI_STUB) += u-boot-payload.efi
 
ifneq ($(BUILD_ROM),)
  ALL-$(CONFIG_X86_RESET_VECTOR) += u-boot.rom
endif
 
# enable combined SPL/u-boot/dtb rules for tegra
ifeq ($(CONFIG_TEGRA)$(CONFIG_SPL),yy)
  ALL-y += u-boot-tegra.bin u-boot-nodtb-tegra.bin
  ALL-$(CONFIG_OF_SEPARATE) += u-boot-dtb-tegra.bin
endif
 
# Add optional build target if defined in board/cpu/soc headers
ifneq ($(CONFIG_BUILD_TARGET),)
  ALL-y += $(CONFIG_BUILD_TARGET:"%"=%)
endif
 
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
 
 
ALL-y 里面有个 u-boot.bin,这个就是我们最终需要的 uboot 二进制可执行文件,所作的所有工作就是为了它。在顶层 Makefile 中找到 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
 
if_changed 是 一 个 函 数 , 这 个 函 数 在scripts/Kbuild.include 中有定义,而顶层 Makefile 中会包含 scripts/Kbuild.include 文件
ifneq ($(KBUILD_NOCMDDEP),1)
  arg-check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) $(filter-out $(cmd_$@), $(cmd_$(1))) )
else  
  arg-check = $(if $(strip $(cmd_$@)),,1)
endif
 
any-prereq = $(filter-out $(PHONY),$?) $(filter-out $(PHONY) $(wildcard $^),$^)
 
if_changed = $(if $(strip $(any-prereq) $(arg-check)), \
   @set -e;  \
  $(echo-cmd) $(cmd_$(1));  \
  printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd)
 
if_change引用变量太多, 只要知道它从 u-boot-nodtb.bin 生成 u-boot.bin 就行了
 
 
目标 u-boot 依赖于 u-boot_init、u-boot-main 和 u-boot.lds
u-boot-init := $(head-y)
u-boot-main := $(libs-y)
 
head-y := arch/arm/cpu/$(CPU)/start.o
=> head-y := arch/arm/cpu/armv7/start.o
=> u-boot-init= arch/arm/cpu/armv7/start.o
 
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))
 
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 的文件
cmd_drivers/gpio/built-in.o := arm-linux-gnueabihf-ld.bfd -r -o drivers/gpio/built-in.o drivers/gpio/mxc_gpio.o
 -r =>  -r –relocateable: 产生可重定向的输出,比如,产生一个输出文件它可再次作为‘ld’的输入,这经常被叫做“部分链接”,当我们需要将几个小的.o 文件链接成为一个.o 文件的时候,需要使用此选项
 
 
最终
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
 
 
最后的总结
make xxx_defconfig:用于配置 uboot,这个命令最主要的目的就是生成.config 文件
make:用于编译 uboot,这个命令的主要工作就是生成二进制的 u-boot.bin 文件和其他的一些与 uboot 有关的文件,比如 u-boot.imx 等等。
 

posted on 2020-11-24 00:03  kehuadong  阅读(430)  评论(0编辑  收藏  举报

导航