u-boot 顶层Makefile 分析

本文详细解读了U-Boot顶层Makefile的关键部分,包括版本号定义、硬件体系结构检测、操作系统识别以及构建路径配置等核心内容。通过解析这些细节,读者能够深入理解U-Boot构建流程的底层逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

U-Boot 顶层Makefile 解析

u-Boot -Makefile(一)  声明 : 以下解析大部分来自网络大神之手 所以可以放心阅读  Good luck

VERSION = 1
PATCHLEVEL = 1
SUBLEVEL = 6
EXTRAVERSION =
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
#版本号定义
VERSION_FILE = $(obj)include/version_autogenerated.h  #不是很理解这句话的意思
# 举例理解$(obj)  obj := a.o b.o  $(obj)指的是obj变量的值  即为a.o b.o 
# makefile中的$(xxx)跟shell中的是不一样的

HOSTARCH := $(shell uname -m | \
 sed -e s/i.86/i386/ \
    -e s/sun4u/sparc64/ \
    -e s/arm.*/arm/ \
    -e s/sa110/arm/ \
    -e s/powerpc/ppc/ \
    -e s/macppc/ppc/)
# uname 命令将正在使用的操作系统名写到标准输出中 
# -m 显示系统运行的硬件机器ID号
#  '| '  为linux中的管道处理符 pipe    而 '\'  自然就是换行符咯
#  sed -e 后面跟着一串(shell?)命令脚本 表示将i.86替换成i386  这样执行一套程序下来就知道硬件的体系结构了  如何知道的?
# 我猜应该会有某个变量用来标记 是哪种硬件体系的吧 只要对应的体系结构名被修改后就标记它 得以知道是哪种结构?
# i386
# sparc64
# arm -ARM
# ppc -Power PC

##下面这句话的意思是定义变量HOSTOS,HOSTOS里面存放的是主机安装的,并且当前正在运行的操作系统
HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
    sed -e 's/\(cygwin\).*/cygwin/')
#sed -e 's/\(cygwin\).*/cygwin/')    
# 这一句的意思是检测出主机安装的,并且当前正在运行的操作系统名,并把这个系统名中的大写字母转换为小写字母
# 然后在通过sed流编辑器匹配所有的,这个系统名中出现的"\(cygwin\).*"模式,然后再用"cygwin"模式替换(用来处理"cygwin"环境下编译的环境变量)
## 可以把shell脚本写到makefile里面,这是make中的shell function,相当于shell中的命令替换!
# uname -s 得到内核系统名称即Linux    
# tr '[:upper:]' '[:lower:]'把大写变成小写然后输出(也就是将Linux--->linux  Linux是通过管道得来的)(tr为shell command)

export HOSTARCH HOSTOS  #导出变量HOSTARCH 和变量HOSTOS 

# Deal with colliding definitions from tcsh etc.处理来自tcsh相互冲突的定义等等
VENDOR= 
#VENDOR 是卖家卖方的意思 这边可以写开发商名字吧

#########################################################################
#
# U-boot build supports producing a object files to the separate external
# directory. Two use cases are supported:
# 可以将 U-Boot的创建指向其他外部路径(可以理解为自定义的directory) 来存放最终生成的目标文件支持以下两种方法:
#
# 1) Add O= to the make command line
# 'make O=/tmp/build all'
# 第一种用法:通过在终端执行命令make O=/dir (/dir即为我们指定的生成的目标文件的存放目录)
# 然后执行 "make O=/dir"

# 2) Set environement variable BUILD_DIR to point to the desired location
# 'export BUILD_DIR=/tmp/build'
# 'make'
# 第二种用法:通过设置环境变量来指定目标文件存放目录
# 'export BUILD_DIR=/tmp/build'
# then excute : 'make'
# The second approach can also be used with a MAKEALL script
# 第二中方式还可以写成一个MAKEALL脚本 然后执行 ./MAKEALL
# 'export BUILD_DIR=/tmp/build'
# './MAKEALL'
#
# Command line 'O=' setting overrides BUILD_DIR environent variable.
# 命令行'O='的设置会覆盖BUILD_DIR环境变量的设置
# When none of the above methods is used the local build is performed and
# the object files are placed in the source directory.
# 当以上两种方式都未被采用时,默认创建路径是当前u-boot所在的目录的也就是存放在u-boot自己的顶层目录下
# 理解了上面一段英文,这里就不难理解了 都是对应着的
ifdef O      #如果定义了变量'O'
ifeq ("$(origin O)", "command line") #如果变量上面那个变量'O'在command line(命令行)里定义过
BUILD_DIR := $(O)   #就把变量'O'的值赋给BUILD_DIR这个环境变量
endif     #endif
endif     #endif

ifneq ($(BUILD_DIR),)   #如果BUILD_DIR被定义过
saved-output := $(BUILD_DIR)  #就把它的值赋给saved-output

# Attempt to create a output directory.   #尝试创建一个输出路径BUILD_DIR
$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}) #创建这个路径的shell命令

# Verify if it was successful.    #检测是否创建成功
BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd) 
$(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist)) #如果BUILD_DIR非空则什么都不做  
endif           #如果为空则用error输出错误信息
# ifneq ($(BUILD_DIR),)    #被注释了?看上去有点怪怪的

OBJTREE  := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR)) #如果BUILD_DIR为非空则将BUILD_DIR路径赋给OBJTREE否则将当前路径CURDIR赋给OBJTREE
SRCTREE  := $(CURDIR)     #把当前源码所在目录 $(CURDIR) 赋给SRCTREE
TOPDIR  := $(SRCTREE)     #你懂的
LNDIR  := $(OBJTREE)     #就是最终设置好的生成目标文件的存放路径
export TOPDIR SRCTREE OBJTREE  #导出这三个路径变量

MKCONFIG := $(SRCTREE)/mkconfig #让MKCONFIG指向U-BOOT源码存放的顶层目录下的mkconfig配置文件
export MKCONFIG    #并且导出MKCONFIG

ifneq ($(OBJTREE),$(SRCTREE))  #当目标存放目录不是U-BOOT顶层目录(源码目录)时
REMOTE_BUILD  := 1   #REMOTE_BUILD=1  只是flag?
export REMOTE_BUILD   #导出REMOTE_BUILD
endif     #endif

# $(obj) and (src) are defined in config.mk but here in main Makefile
# we also need them before config.mk is included which is the case for
# some targets like unconfig, clean, clobber, distclean, etc.
ifneq ($(OBJTREE),$(SRCTREE))  #当目标存放目录不是U-BOOT顶层目录(源码目录)时
obj := $(OBJTREE)/   #定义变量obj让其等于目标存放目录 多了一个/  他们是目录
src := $(SRCTREE)/   #定义变量src让其等于uboot顶层目录
else     #else
obj :=     #obj = 空
src :=     #src = 空
endif     #endif
export obj src    #export them

#########################################################################

ifeq ($(OBJTREE)/include/config.mk,$(wildcard $(OBJTREE)/include/config.mk))
# $(OBJTREE)/include/config.mk            
# $(wildcard $(OBJTREE)/include/config.mk)
# $(wildcard pattern) 参数pattern是一个文件名格式(可使用通配符)
# 这个函数的结果是一列和格式匹配且真实存在的文件的名称 文件名之间用空格符隔开
# 那  这句的意思是啥 

# load ARCH, BOARD, and CPU configuration #加载这些配置
include $(OBJTREE)/include/config.mk  #把/include/config.mk包含进工程中     
export ARCH CPU BOARD VENDOR SOC  #导出ARCH CPU BOARD VENDOR SOC

ifndef CROSS_COMPILE  #如果没有定义交叉编译工具
ifeq ($(HOSTARCH),ppc)  #如果他俩一样
CROSS_COMPILE =   #交叉编译工具为空
else    #else
ifeq ($(ARCH),ppc)  #
CROSS_COMPILE = powerpc-linux- #
endif    #
ifeq ($(ARCH),arm)  #
CROSS_COMPILE = arm-linux- #我们要用的交叉编译工具arm-linux-是前缀
endif    #
ifeq ($(ARCH),i386)  #
ifeq ($(HOSTARCH),i386)  #为啥这两个要这样
CROSS_COMPILE =   #
else    #
CROSS_COMPILE = i386-linux- #
endif    #
endif    #
ifeq ($(ARCH),mips)  #
CROSS_COMPILE = mips_4KC- #
endif    #
ifeq ($(ARCH),nios)  #
CROSS_COMPILE = nios-elf- #
endif    #
ifeq ($(ARCH),nios2)  #
CROSS_COMPILE = nios2-elf- #
endif    #
ifeq ($(ARCH),m68k)  #
CROSS_COMPILE = m68k-elf- #
endif    #
ifeq ($(ARCH),microblaze) #
CROSS_COMPILE = mb-  #
endif    #
ifeq ($(ARCH),blackfin)  #
CROSS_COMPILE = bfin-elf- #
endif    #
ifeq ($(ARCH),avr32)  #
CROSS_COMPILE = avr32-  #avr32都来了?
endif    #
endif    #
endif    #

export CROSS_COMPILE  #export CROSS_COMPILE

# load other configuration #加载其他配置
include $(TOPDIR)/config.mk #位于$(TOPDIR)/config.mk

# U-Boot objects....order is important (i.e. start must be first)
# 目标文件的顺序很重要 start要排第一

OBJS  = cpu/$(CPU)/start.o #start.o必须放在目标文件的第一位 因为uboot执行的第一段代码就是start.S
# # 某些架构的CPU需要另外的一些目标文件
ifeq ($(CPU),i386)
OBJS += cpu/$(CPU)/start16.o
OBJS += cpu/$(CPU)/reset.o
endif
ifeq ($(CPU),ppc4xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),mpc83xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),mpc85xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),mpc86xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),bf533)
OBJS += cpu/$(CPU)/start1.o cpu/$(CPU)/interrupt.o cpu/$(CPU)/cache.o
OBJS += cpu/$(CPU)/cplbhdlr.o cpu/$(CPU)/cplbmgr.o cpu/$(CPU)/flush.o
endif

OBJS := $(addprefix $(obj),$(OBJS)) #将$(obj)做为前缀添加在$(OBJS)中每个文件的前面
# $(addprefix prefix,names)参数prefix是前缀  将其添加在names(names代表文件名 之间用空格隔开)的前面
# 最后生成的每个文件也用空格隔开来
# 编译U-BOOT所需库文件
LIBS  = lib_generic/libgeneric.a
LIBS += board/$(BOARDDIR)/lib$(BOARD).a
LIBS += cpu/$(CPU)/lib$(CPU).a
ifdef SOC
LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a
endif
LIBS += lib_$(ARCH)/lib$(ARCH).a
LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
 fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
LIBS += net/libnet.a
LIBS += disk/libdisk.a
LIBS += rtc/librtc.a
LIBS += dtt/libdtt.a
LIBS += drivers/libdrivers.a
LIBS += drivers/nand/libnand.a
LIBS += drivers/nand_legacy/libnand_legacy.a
LIBS += drivers/sk98lin/libsk98lin.a
LIBS += post/libpost.a post/cpu/libcpu.a
LIBS += common/libcommon.a
LIBS += $(BOARDLIBS)

LIBS := $(addprefix $(obj),$(LIBS))
.PHONY : $(LIBS)  # .PHONY声明伪目标

# Add GCC lib #添加GCC库
PLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc

# The "tools" are needed early, so put this first
# Don't include stuff already done in $(LIBS)
SUBDIRS = tools \  #伪目标SUBDIRS 用于执行toolsexamples、post、post\cpu子目录下的make文件
  examples \
  post \
  post/cpu
.PHONY : $(SUBDIRS)  # .PHONY声明伪目标

ifeq ($(CONFIG_NAND_U_BOOT),y)
NAND_SPL = nand_spl
U_BOOT_NAND = $(obj)u-boot-nand.bin
endif

__OBJS := $(subst $(obj),,$(OBJS)) #把$(obj)从$(OBJS)$(LIBS)中删除
__LIBS := $(subst $(obj),,$(LIBS)) #$(obj)是$(OBJTREE)/  或者为  空
     #$(OBJTREE)是BUILD_DIR(自定义目录或者是源码目录)
# $(subst from,to,text)  在文本"text"中用"to"替换所有的"from"

#########################################################################
#########################################################################
# u-boot.srec u-boot.bin ...an so on

ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)

all:  $(ALL)

$(obj)u-boot.hex: $(obj)u-boot
  $(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@

$(obj)u-boot.srec: $(obj)u-boot
  $(OBJCOPY) ${OBJCFLAGS} -O srec $< $@

$(obj)u-boot.bin: $(obj)u-boot
  $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@

$(obj)u-boot.img: $(obj)u-boot.bin
  ./tools/mkimage -A $(ARCH) -T firmware -C none \
  -a $(TEXT_BASE) -e 0 \
  -n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
   sed -e 's/"[  ]*$$/ for $(BOARD) board"/') \
  -d $< $@

$(obj)u-boot.dis: $(obj)u-boot
  $(OBJDUMP) -d $< > $@

$(obj)u-boot:  depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
  UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
  cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
   --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
   -Map u-boot.map -o u-boot

# $(OBJS) cpu/start.o
$(OBJS):
  $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))

# $(LIBS)为每个子目录下的*.a 通过自动变量$@ 使得每个子目录下的Makefile得以执行 完成库文件的创建
$(LIBS):
  $(MAKE) -C $(dir $(subst $(obj),,$@))

$(SUBDIRS):
  $(MAKE) -C $@ all

$(NAND_SPL): version
  $(MAKE) -C nand_spl/board/$(BOARDDIR) all

$(U_BOOT_NAND): $(NAND_SPL) $(obj)u-boot.bin
  cat $(obj)nand_spl/u-boot-spl-16k.bin $(obj)u-boot.bin > $(obj)u-boot-nand.bin

version:
  @echo -n "#define U_BOOT_VERSION \"U-Boot " > $(VERSION_FILE); \
  echo -n "$(U_BOOT_VERSION)" >> $(VERSION_FILE); \
  echo -n $(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion \
    $(TOPDIR)) >> $(VERSION_FILE); \
  echo "\"" >> $(VERSION_FILE)
# >> $(VERSION_FILE) 追加版本信息到VERSION_FILE中

gdbtools:
  $(MAKE) -C tools/gdb all || exit 1

updater:
  $(MAKE) -C tools/updater all || exit 1

env:
  $(MAKE) -C tools/env all || exit 1

depend dep:
  for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done
# 依赖目标depend:生成各个子目录.depend文件.  .depend列出每个目标文件的依赖文件
# 生成方法:调用每个子目录的make_depend
# it's  .d??
# 以下:.d 文件
# GNU组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中,为每一个“name.c”的文件都生成一个“name.d”的Makefile文件,[.d]文件中就存放对应[.c]文件的依赖关系。
tags ctags:
  ctags -w -o $(OBJTREE)/ctags `find $(SUBDIRS) include \
    lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \
    fs/cramfs fs/fat fs/fdos fs/jffs2 \
    net disk rtc dtt drivers drivers/sk98lin common \
   \( -name CVS -prune \) -o \( -name '*.[ch]' -print \)`

etags:
  etags -a -o $(OBJTREE)/etags `find $(SUBDIRS) include \
    lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \
    fs/cramfs fs/fat fs/fdos fs/jffs2 \
    net disk rtc dtt drivers drivers/sk98lin common \
   \( -name CVS -prune \) -o \( -name '*.[ch]' -print \)`

$(obj)System.map: $(obj)u-boot
  @$(NM) $< | \
  grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \
  sort > $(obj)System.map

#########################################################################
else
all $(obj)u-boot.hex $(obj)u-boot.srec $(obj)u-boot.bin \
$(obj)u-boot.img $(obj)u-boot.dis $(obj)u-boot \
$(SUBDIRS) version gdbtools updater env depend \
dep tags ctags etags $(obj)System.map:
 @echo "System not configured - see README" >&2
 @ exit 1
endif

.PHONY : CHANGELOG
CHANGELOG:
 git log --no-merges U-Boot-1_1_5.. | \
 unexpand -a | sed -e 's/\s\s*$$//' > $@

#########################################################################

# 清除上次生成的include/config.h和include/config.mk以及开发板目录下的一些临时配置文件
unconfig:
 @rm -f $(obj)include/config.h $(obj)include/config.mk \
  $(obj)board/*/config.tmp $(obj)board/*/*/config.tmp

#========================================================================
# PowerPC
#========================================================================

其实只要掌握Makefile的语法规则 这些Makefile文件都是一堆大同小异的small case了骂人  http://www.linuxidc.com/Linux/2011-02/32261.htm

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值