U-Boot Makefile分析(2) config.mk分析

  浏览一下U-Boot各个子目录下的Makefile可以看到,几乎他们都会包含$(TOPDIR)/config.mk,那么这个文件进行了什么操作呢?简单概括:读入include/config.mk、include/autoconf.mk,指定ARCH CPU SoC Board等重要信息,并且加入各个层次上的编译选项;初始化编译处理选项、链接选项;最后有一个很重要的变量定义:

  cmd_link_o_target = $(if $(strip $1),\

            $(LD) $(LDFLAGS) -r -o $@ $1, \

            rm -f $@; $(AR) rcs $@)

  其实就是将输入的参数$1部分链接成目标,如果输入目标无效,则删除相关的目标,并且建立一个新的归档文件,与目标同名。

  该文件中涉及大量编译、链接选项,篇幅较大,因此不深究它们,只是分析关键步骤。

 1 ifeq ($(CURDIR),$(SRCTREE))
 2 dir :=
 3 else
 4 dir := $(subst $(SRCTREE)/,,$(CURDIR))
 5 endif
 6 
 7 ifneq ($(OBJTREE),$(SRCTREE))
 8 # Create object files for SPL in a separate directory
 9 ifeq ($(CONFIG_SPL_BUILD),y)
10 obj := $(if $(dir),$(SPLTREE)/$(dir)/,$(SPLTREE)/)
11 else
12 obj := $(if $(dir),$(OBJTREE)/$(dir)/,$(OBJTREE)/)
13 endif
14 src := $(if $(dir),$(SRCTREE)/$(dir)/,$(SRCTREE)/)
15 
16 $(shell mkdir -p $(obj))
17 else
18 # Create object files for SPL in a separate directory
19 ifeq ($(CONFIG_SPL_BUILD),y)
20 obj := $(if $(dir),$(SPLTREE)/$(dir)/,$(SPLTREE)/)
21 
22 $(shell mkdir -p $(obj))
23 else
24 obj :=
25 endif
26 src :=
27 endif

  CURDIR是Makefile内置变量,表示该Makefile的绝对路径,相当于执行pwd得到的输出,这里通过subst函数将CURDIR中的SRCTREE部分去掉,这样dir就表示当前路径相对于源码根目录的相对路径。

  接下来是obj和src的处理,这里SRCTREE和OBJTREE相同,而且CONFIG_SPL_BUILD没定义,因此上述两个变量是空字符串。

 1 CC_OPTIONS_CACHE_FILE := $(OBJTREE)/include/generated/cc_options.mk
 2 CC_TEST_OFILE := $(OBJTREE)/include/generated/cc_test_file.o
 3 
 4 -include $(CC_OPTIONS_CACHE_FILE)
 5 
 6 cc-option-sys = $(shell mkdir -p $(dir $(CC_TEST_OFILE)); \
 7         if $(CC) $(CFLAGS) $(1) -S -xc /dev/null -o $(CC_TEST_OFILE) \
 8         > /dev/null 2>&1; then \
 9         echo 'CC_OPTIONS += $(strip $1)' >> $(CC_OPTIONS_CACHE_FILE); \
10         echo "$(1)"; fi)
11 
12 ifeq ($(CONFIG_CC_OPT_CACHE_DISABLE),y)
13 cc-option = $(strip $(if $(call cc-option-sys,$1),$1,$2))
14 else
15 cc-option = $(strip $(if $(findstring $1,$(CC_OPTIONS)),$1,\
16         $(if $(call cc-option-sys,$1),$1,$2)))
17 endif
18 
19 # cc-version
20 # Usage gcc-ver := $(call cc-version)
21 cc-version = $(shell $(SHELL) $(SRCTREE)/tools/gcc-version.sh $(CC))
22 binutils-version = $(shell $(SHELL) $(SRCTREE)/tools/binutils-version.sh $(AS))

  这里相当于定义了两个小函数cc-option-sys,cc-option,其中后者调用前者,因此虽然前者提供了接口,但是并不使用,我不知道CONFIG_CC_OPT_CACHE_DISABLE是不是y,但是这里的核心操作就是,将要添加的CC编译选项以参数的形式传进去,然后尝试加入该选项来编译/dev/null,丢弃所有stdout/stderr输出。这里比较有意思的是,编译的源文件是/dev/null,估计作者的意思是,只要加入的这个选项存在,是编译器支持的就可以,而不是要求达到何种效果。如果选项有效,则向include/generated/cc_options.mk中写入该选项。

  接着又定义了两个函数cc-version、binutils-version,两者都是通过执行shell脚本得到的,没用到,暂不分析。

 1 AS  = $(CROSS_COMPILE)as
 2 
 3 # Always use GNU ld
 4 LD  = $(shell if $(CROSS_COMPILE)ld.bfd -v > /dev/null 2>&1; \
 5         then echo "$(CROSS_COMPILE)ld.bfd"; else echo "$(CROSS_COMPILE)ld"; fi;)
 6 
 7 CC  = $(CROSS_COMPILE)gcc
 8 CPP = $(CC) -E
 9 AR  = $(CROSS_COMPILE)ar
10 NM  = $(CROSS_COMPILE)nm
11 LDR = $(CROSS_COMPILE)ldr
12 STRIP   = $(CROSS_COMPILE)strip
13 OBJCOPY = $(CROSS_COMPILE)objcopy
14 OBJDUMP = $(CROSS_COMPILE)objdump
15 RANLIB  = $(CROSS_COMPILE)RANLIB
16 DTC = dtc

  上面就很简单了,指明使用的各个工具,这是每个子Makefile都要包含config.mk的重要原因之一。

1 # Load generated board configuration
2 sinclude $(OBJTREE)/include/autoconf.mk
3 sinclude $(OBJTREE)/include/config.mk

  这里虽然就两行,但是很重要,经过上一篇文章的分析,我们知道mkconfig s5p_goni_config的重要输出文件有两个:include/config.mk include/config.h,这里使用第一个,它指明了ARCH CPU SOC VENDOR BOARD,autoconf.mk目前还不知道是如何产生的,但是浏览以下可以知道里面的信息很多很重要,很多功能配置,这是子Makefile要包含config.mk的另一个重要原因。

 1 CPUDIR=arch/$(ARCH)/cpu/$(CPU)
 2 ifneq ($(SRCTREE)/$(CPUDIR),$(wildcard $(SRCTREE)/$(CPUDIR)))
 3 CPUDIR=arch/$(ARCH)/cpu
 4 endif
 5 
 6 sinclude $(TOPDIR)/arch/$(ARCH)/config.mk   # include architecture dependend rules
 7 sinclude $(TOPDIR)/$(CPUDIR)/config.mk      # include  CPU  specific rules
 8 
 9 ifdef   SOC
10 sinclude $(TOPDIR)/$(CPUDIR)/$(SOC)/config.mk   # include  SoC  specific rules
11 endif
12 ifdef   VENDOR
13 BOARDDIR = $(VENDOR)/$(BOARD)
14 else
15 BOARDDIR = $(BOARD)
16 endif
17 ifdef   BOARD
18 sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk  # include board specific rules
19 endif

  概括一下,上面的语句就是读入了几个config.mk:

sinclude arch/arm/config.mk  这是加入架构层次上的配置,比如设定CROSS_COMPILE ?= arm-linux-,比如通过上面的cc-option函数加入arm架构的编译选项:

              $(call cc-option -marm)

sinclude arch/arm/cpu/armv7/config.mk  这是加入cpu层次上的配置,加入指令集上的配置选项,如$(call cc-option, -march=armv7-a, -march=armv5)

sinclude arch/arm/cpu/armv7/s5pc1xx/config.mk  这是加入soc层次上的配置,检查发现目录下没有此文件,但并不报错,sinclude和-include用法一样,当被包含的文件不

                   存在的时候,不报错,继续执行。

sinclude board/samsung/goni/config.mk   这是在board层次上加入配置,因为板卡资源决定了我们的内存容量和起始地址,所以这里指定了u-boot镜像的链接地址CONFIG_SYS_TEXT_BASE = 0x34800000,这个信息是board-specific。

 1 ARFLAGS = $(error update your Makefile to use cmd_link_o_target and not AR)
 2 RELFLAGS= $(PLATFORM_RELFLAGS)
 3 DBGFLAGS= -g # -DDEBUG
 4 OPTFLAGS= -Os #-fomit-frame-pointer
 5 
 6 OBJCFLAGS += --gap-fill=0xff
 7 
 8 gccincdir := $(shell $(CC) -print-file-name=include)
 9 
10 CPPFLAGS := $(DBGFLAGS) $(OPTFLAGS) $(RELFLAGS)     \   
11     -D__KERNEL__

  注意ARFLAGS被设置成了makefile的error函数输出错误信息,这是提醒用户,在对目标文件归档的时候不要使用$(AR)了,要使用$(LD)。

  接下来就是编译链接选项的设置,有太多选项了,什么SPL启动、VENDOR address等等,不细细分析了。

1 export  HOSTCC HOSTCFLAGS HOSTLDFLAGS PEDCFLAGS HOSTSTRIP CROSS_COMPILE \
2     AS LD CC CPP AR NM STRIP OBJCOPY OBJDUMP MAKE
3 export  CONFIG_SYS_TEXT_BASE PLATFORM_CPPFLAGS PLATFORM_RELFLAGS CPPFLAGS CFLAGS AFLAGS

  export到全局的变量如上所示。

  终于到了最后!

 1 # The _DEP version uses the $< file target (for dependency generation)
 2 # See rules.mk
 3 EXTRA_CPPFLAGS_DEP = $(CPPFLAGS_$(BCURDIR)/$(addsuffix .o,$(basename $<))) \
 4         $(CPPFLAGS_$(BCURDIR))
 5 $(obj)%.s:  %.S
 6     $(CPP) $(ALL_AFLAGS) -o $@ $<
 7 $(obj)%.o:  %.S
 8     $(CC)  $(ALL_AFLAGS) -o $@ $< -c
 9 $(obj)%.o:  %.c
10     $(CC)  $(ALL_CFLAGS) -o $@ $< -c
11 $(obj)%.i:  %.c
12     $(CPP) $(ALL_CFLAGS) -o $@ $< -c
13 $(obj)%.s:  %.c
14     $(CC)  $(ALL_CFLAGS) -o $@ $< -c -S
15 
16 #########################################################################
17 
18 # If the list of objects to link is empty, just create an empty built-in.o
19 cmd_link_o_target = $(if $(strip $1),\
20               $(LD) $(LDFLAGS) -r -o $@ $1,\
21               rm -f $@; $(AR) rcs $@ )

  这里很重要,是一些静态模式描述,分析这里的时候,着实让我纠结了一阵子。以前自己写Makefile的时候也会用静态模式,但理解是很不准确的,写依赖的时候理所当然地写$<,不知原因。举例,test.o处理依赖test.c以外,还依赖它所包含的头文件,所以该文件的完整约束描述为:

test.o: test.c  \

header1path/header1.h

header2path/header2.h

...

  上述依赖格式也正是CC -M的输出格式,而在编译生成test.o的时候,只需要test.c就够了,这就是$<的含义:依赖集中的第一个依赖。

  最后那个函数在文章开始的时候已经分析过了。

  ok.

posted @ 2018-02-02 20:17  Moosee  阅读(648)  评论(0编辑  收藏  举报