makefile学习五
2011-09-20 22:21
246 查看
前面两篇都是在说配置是怎么解析的,以及配置参数是怎么得到了。有了这些配置信息,接下来就是根据这些配置信息编译了。首先需要的是建立适当的环境,包括交叉编译环境,然后确定要编译的objs,最后就是编译objs。还是参考源码来看:
OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
SPLTREE := $(OBJTREE)/spl
SRCTREE := $(CURDIR)
TOPDIR := $(SRCTREE)
LNDIR := $(OBJTREE)
首先是建立源码,目标等路径。接下来是生产的config.mk包含进来,# load ARCH, BOARD, and CPU configuration
include $(obj)include/config.mk
export ARCH CPU BOARD VENDOR SOC
至此确定了编译需要的关键几个变量ARCH、CPU、BOARD、VENDOR、SOC。然后就是包含include $(TOPDIR)/config.mk,这个文件非常重要,主要任务是建立环境。
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
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
RANLIB = $(CROSS_COMPILE)RANLIB
这几个确定了常用的编译、链接。CROSS_COMPILE是跟cpu arch所决定的,在x86上面为“”,其他平台都会加个前zui,如arm-linux-none-等,因此需要包含特定arch、cpu下的配置:
sinclude $(TOPDIR)/arch/$(ARCH)/config.mk # include architecture dependend rules
sinclude $(TOPDIR)/$(CPUDIR)/config.mk # include CPU specific rules
我们以arm为例看看,到底里面需要做些什么?
CROSS_COMPILE ?= arm-linux-
ifndef CONFIG_STANDALONE_LOAD_ADDR
ifeq ($(SOC),omap3)
CONFIG_STANDALONE_LOAD_ADDR = 0x80300000
else
CONFIG_STANDALONE_LOAD_ADDR = 0xc100000
endif
endif
PLATFORM_CPPFLAGS += -DCONFIG_ARM -D__ARM__
# Explicitly specifiy 32-bit ARM ISA since toolchain default can be -mthumb:
PLATFORM_CPPFLAGS += $(call cc-option,-marm,)
# Try if EABI is supported, else fall back to old API,
# i. e. for example:
# - with ELDK 4.2 (EABI supported), use:
# -mabi=aapcs-linux -mno-thumb-interwork
# - with ELDK 4.1 (gcc 4.x, no EABI), use:
# -mabi=apcs-gnu -mno-thumb-interwork
# - with ELDK 3.1 (gcc 3.x), use:
# -mapcs-32 -mno-thumb-interwork
PLATFORM_CPPFLAGS += $(call cc-option,\
-mabi=aapcs-linux -mno-thumb-interwork,\
$(call cc-option,\
-mapcs-32,\
$(call cc-option,\
-mabi=apcs-gnu,\
)\
) $(call cc-option,-mno-thumb-interwork,)\
)
# For EABI, make sure to provide raise()
ifneq (,$(findstring -mabi=aapcs-linux,$(PLATFORM_CPPFLAGS)))
# This file is parsed many times, so the string may get added multiple
# times. Also, the prefix needs to be different based on whether
# CONFIG_SPL_BUILD is defined or not. 'filter-out' the existing entry
# before adding the correct one.
ifdef CONFIG_SPL_BUILD
PLATFORM_LIBS := $(SPLTREE)/arch/arm/lib/eabi_compat.o \
$(filter-out %/arch/arm/lib/eabi_compat.o, $(PLATFORM_LIBS))
else
PLATFORM_LIBS := $(OBJTREE)/arch/arm/lib/eabi_compat.o \
$(filter-out %/arch/arm/lib/eabi_compat.o, $(PLATFORM_LIBS))
endif
endif
ifdef CONFIG_SYS_LDSCRIPT
# need to strip off double quotes
LDSCRIPT := $(subst ",,$(CONFIG_SYS_LDSCRIPT))
else
LDSCRIPT := $(SRCTREE)/$(CPUDIR)/u-boot.lds
endif
# needed for relocation
ifndef CONFIG_NAND_SPL
LDFLAGS_u-boot += -pie
endif
主要是交叉编译器服了初值,以及添加arch相关的编译、链接选项。另外还包含soc、board等相关的mk
ifdef SOC
sinclude $(TOPDIR)/$(CPUDIR)/$(SOC)/config.mk # include SoC specific rules
endif
ifdef VENDOR
BOARDDIR = $(VENDOR)/$(BOARD)
else
BOARDDIR = $(BOARD)
endif
ifdef BOARD
sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk # include board specific rules
endif
接下来是确定需要编译那些obj:主要包括文件系统、常用lib函数,drivers、架构相关的库函数等。这里uboot 只是简单的把各个目录下的目标包含了下。以lib库为例,LIBS包含了drivers、lib以及文件系统等obj。它的依赖关系如下:
$(LIBS): depend $(SUBDIRS)
$(MAKE) -C $(dir $(subst $(obj),,$@))
这里生成LIBS库的方法是去到相应的lib目录下执行makefile。我们以一个driver的例子说明,以drivers下fpga的生成为例
include $(TOPDIR)/config.mk
LIB := $(obj)libfpga.o
ifdef CONFIG_FPGA
COBJS-y += fpga.o
COBJS-$(CONFIG_FPGA_SPARTAN2) += spartan2.o
COBJS-$(CONFIG_FPGA_SPARTAN3) += spartan3.o
COBJS-$(CONFIG_FPGA_VIRTEX2) += virtex2.o
COBJS-$(CONFIG_FPGA_XILINX) += xilinx.o
COBJS-$(CONFIG_FPGA_LATTICE) += ivm_core.o lattice.o
ifdef CONFIG_FPGA_ALTERA
COBJS-y += altera.o
COBJS-$(CONFIG_FPGA_ACEX1K) += ACEX1K.o
COBJS-$(CONFIG_FPGA_CYCLON2) += cyclon2.o
COBJS-$(CONFIG_FPGA_STRATIX_II) += stratixII.o
endif
endif
由于uboot每个lib都会可以独立编译的,所以第一句就是先把交叉编译环境建立起来。接下来定义要编译的lib名字,uboot采用了一种非常简单的命名规则,即lib明是lib+目录名组成的。接下来把根据配置信息把需要编译的c目标文件包含进来。
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
同时获取相应.c和目标文件。
接下来就是如何编译了:
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
这里调用了$TOP/config.mk,完成生成库。
下面是编译规则和依赖
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
rules里面的定义了依赖关系是怎么产生的:
$(obj).depend: $(src)Makefile $(TOPDIR)/config.mk $(SRCS) $(HOSTSRCS)
@rm -f $@
@touch $@
@for f in $(SRCS); do \
g=`basename $$f | sed -e 's/\(.*\)\.[[:alnum:]_]/\1.o/'`; \
$(CC) -M $(CPPFLAGS) -MQ $(obj)$$g $$f >> $@ ; \
done
@for f in $(HOSTSRCS); do \
g=`basename $$f | sed -e 's/\(.*\)\.[[:alnum:]_]/\1.o/'`; \
$(HOSTCC) -M $(HOSTCPPFLAGS) -MQ $(obj)$$g $$f >> $@ ; \
done
这里主要是使用了gcc的一个特性-M -MQ生成依赖关系。至于.c怎么生成.o等规则在$TOP/config.mk中已经定义了。
这里在学习一下依赖关系是怎么生成的,在uboot里面有两种方式:第一种是直接使用depend这个关键字。像前面LIBS生成那样,由编译器自动生成相关的依赖;第二种是自己生成依赖文件,make会在编译的时候自己去寻找对应的规则。在上面fpga下的depend是由用户自己生成的。当然整个工程也可以只生成一个.d文件,只要把相关的依赖输入到这个文件即可,make会自动找。这里创建依赖关系是通过gcc的编译选项来完成的,以hello.c为例:
alloc@linux-erlo:~/test> cc -M -MQ a hello.c
a: hello.c /usr/include/stdio.h /usr/include/features.h \
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
/usr/lib64/gcc/x86_64-suse-linux/4.5/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib64/gcc/x86_64-suse-linux/4.5/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h file1.h
这里列出了所有的依赖关系,MQ后面是obj,接下来是源文件。接下来我们再看看这个依赖关系在uboot下面是怎么完成的。首先它需要知道生成的目标,它通过源文件而得到。首先通过basename函数得到了.c部分,然后通过sed将前面部分取出来作为参数,然后调用gcc命令得到依赖关系。其实获取这个名字也可以使用makefile的函数实现:
o= $(addsuffix .o,$(notdir $(basename $(SRCS))))
OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
SPLTREE := $(OBJTREE)/spl
SRCTREE := $(CURDIR)
TOPDIR := $(SRCTREE)
LNDIR := $(OBJTREE)
首先是建立源码,目标等路径。接下来是生产的config.mk包含进来,# load ARCH, BOARD, and CPU configuration
include $(obj)include/config.mk
export ARCH CPU BOARD VENDOR SOC
至此确定了编译需要的关键几个变量ARCH、CPU、BOARD、VENDOR、SOC。然后就是包含include $(TOPDIR)/config.mk,这个文件非常重要,主要任务是建立环境。
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
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
RANLIB = $(CROSS_COMPILE)RANLIB
这几个确定了常用的编译、链接。CROSS_COMPILE是跟cpu arch所决定的,在x86上面为“”,其他平台都会加个前zui,如arm-linux-none-等,因此需要包含特定arch、cpu下的配置:
sinclude $(TOPDIR)/arch/$(ARCH)/config.mk # include architecture dependend rules
sinclude $(TOPDIR)/$(CPUDIR)/config.mk # include CPU specific rules
我们以arm为例看看,到底里面需要做些什么?
CROSS_COMPILE ?= arm-linux-
ifndef CONFIG_STANDALONE_LOAD_ADDR
ifeq ($(SOC),omap3)
CONFIG_STANDALONE_LOAD_ADDR = 0x80300000
else
CONFIG_STANDALONE_LOAD_ADDR = 0xc100000
endif
endif
PLATFORM_CPPFLAGS += -DCONFIG_ARM -D__ARM__
# Explicitly specifiy 32-bit ARM ISA since toolchain default can be -mthumb:
PLATFORM_CPPFLAGS += $(call cc-option,-marm,)
# Try if EABI is supported, else fall back to old API,
# i. e. for example:
# - with ELDK 4.2 (EABI supported), use:
# -mabi=aapcs-linux -mno-thumb-interwork
# - with ELDK 4.1 (gcc 4.x, no EABI), use:
# -mabi=apcs-gnu -mno-thumb-interwork
# - with ELDK 3.1 (gcc 3.x), use:
# -mapcs-32 -mno-thumb-interwork
PLATFORM_CPPFLAGS += $(call cc-option,\
-mabi=aapcs-linux -mno-thumb-interwork,\
$(call cc-option,\
-mapcs-32,\
$(call cc-option,\
-mabi=apcs-gnu,\
)\
) $(call cc-option,-mno-thumb-interwork,)\
)
# For EABI, make sure to provide raise()
ifneq (,$(findstring -mabi=aapcs-linux,$(PLATFORM_CPPFLAGS)))
# This file is parsed many times, so the string may get added multiple
# times. Also, the prefix needs to be different based on whether
# CONFIG_SPL_BUILD is defined or not. 'filter-out' the existing entry
# before adding the correct one.
ifdef CONFIG_SPL_BUILD
PLATFORM_LIBS := $(SPLTREE)/arch/arm/lib/eabi_compat.o \
$(filter-out %/arch/arm/lib/eabi_compat.o, $(PLATFORM_LIBS))
else
PLATFORM_LIBS := $(OBJTREE)/arch/arm/lib/eabi_compat.o \
$(filter-out %/arch/arm/lib/eabi_compat.o, $(PLATFORM_LIBS))
endif
endif
ifdef CONFIG_SYS_LDSCRIPT
# need to strip off double quotes
LDSCRIPT := $(subst ",,$(CONFIG_SYS_LDSCRIPT))
else
LDSCRIPT := $(SRCTREE)/$(CPUDIR)/u-boot.lds
endif
# needed for relocation
ifndef CONFIG_NAND_SPL
LDFLAGS_u-boot += -pie
endif
主要是交叉编译器服了初值,以及添加arch相关的编译、链接选项。另外还包含soc、board等相关的mk
ifdef SOC
sinclude $(TOPDIR)/$(CPUDIR)/$(SOC)/config.mk # include SoC specific rules
endif
ifdef VENDOR
BOARDDIR = $(VENDOR)/$(BOARD)
else
BOARDDIR = $(BOARD)
endif
ifdef BOARD
sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk # include board specific rules
endif
接下来是确定需要编译那些obj:主要包括文件系统、常用lib函数,drivers、架构相关的库函数等。这里uboot 只是简单的把各个目录下的目标包含了下。以lib库为例,LIBS包含了drivers、lib以及文件系统等obj。它的依赖关系如下:
$(LIBS): depend $(SUBDIRS)
$(MAKE) -C $(dir $(subst $(obj),,$@))
这里生成LIBS库的方法是去到相应的lib目录下执行makefile。我们以一个driver的例子说明,以drivers下fpga的生成为例
include $(TOPDIR)/config.mk
LIB := $(obj)libfpga.o
ifdef CONFIG_FPGA
COBJS-y += fpga.o
COBJS-$(CONFIG_FPGA_SPARTAN2) += spartan2.o
COBJS-$(CONFIG_FPGA_SPARTAN3) += spartan3.o
COBJS-$(CONFIG_FPGA_VIRTEX2) += virtex2.o
COBJS-$(CONFIG_FPGA_XILINX) += xilinx.o
COBJS-$(CONFIG_FPGA_LATTICE) += ivm_core.o lattice.o
ifdef CONFIG_FPGA_ALTERA
COBJS-y += altera.o
COBJS-$(CONFIG_FPGA_ACEX1K) += ACEX1K.o
COBJS-$(CONFIG_FPGA_CYCLON2) += cyclon2.o
COBJS-$(CONFIG_FPGA_STRATIX_II) += stratixII.o
endif
endif
由于uboot每个lib都会可以独立编译的,所以第一句就是先把交叉编译环境建立起来。接下来定义要编译的lib名字,uboot采用了一种非常简单的命名规则,即lib明是lib+目录名组成的。接下来把根据配置信息把需要编译的c目标文件包含进来。
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
同时获取相应.c和目标文件。
接下来就是如何编译了:
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
这里调用了$TOP/config.mk,完成生成库。
下面是编译规则和依赖
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
rules里面的定义了依赖关系是怎么产生的:
$(obj).depend: $(src)Makefile $(TOPDIR)/config.mk $(SRCS) $(HOSTSRCS)
@rm -f $@
@touch $@
@for f in $(SRCS); do \
g=`basename $$f | sed -e 's/\(.*\)\.[[:alnum:]_]/\1.o/'`; \
$(CC) -M $(CPPFLAGS) -MQ $(obj)$$g $$f >> $@ ; \
done
@for f in $(HOSTSRCS); do \
g=`basename $$f | sed -e 's/\(.*\)\.[[:alnum:]_]/\1.o/'`; \
$(HOSTCC) -M $(HOSTCPPFLAGS) -MQ $(obj)$$g $$f >> $@ ; \
done
这里主要是使用了gcc的一个特性-M -MQ生成依赖关系。至于.c怎么生成.o等规则在$TOP/config.mk中已经定义了。
这里在学习一下依赖关系是怎么生成的,在uboot里面有两种方式:第一种是直接使用depend这个关键字。像前面LIBS生成那样,由编译器自动生成相关的依赖;第二种是自己生成依赖文件,make会在编译的时候自己去寻找对应的规则。在上面fpga下的depend是由用户自己生成的。当然整个工程也可以只生成一个.d文件,只要把相关的依赖输入到这个文件即可,make会自动找。这里创建依赖关系是通过gcc的编译选项来完成的,以hello.c为例:
alloc@linux-erlo:~/test> cc -M -MQ a hello.c
a: hello.c /usr/include/stdio.h /usr/include/features.h \
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
/usr/lib64/gcc/x86_64-suse-linux/4.5/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib64/gcc/x86_64-suse-linux/4.5/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h file1.h
这里列出了所有的依赖关系,MQ后面是obj,接下来是源文件。接下来我们再看看这个依赖关系在uboot下面是怎么完成的。首先它需要知道生成的目标,它通过源文件而得到。首先通过basename函数得到了.c部分,然后通过sed将前面部分取出来作为参数,然后调用gcc命令得到依赖关系。其实获取这个名字也可以使用makefile的函数实现:
o= $(addsuffix .o,$(notdir $(basename $(SRCS))))
相关文章推荐
- Makefile的学习
- Makefile 学习笔记(三)实例
- Makefile学习教程: 跟我一起写 Makefile
- 一个工程实例来学习 Makefile
- Linux Make(Makefile)由浅入深的学习与示例剖析
- 我的学习之旅(31) schedule和tools目录下的Makefile
- Makefile学习笔记2:入门篇-学习点滴之Makefile基础 (zz)
- Makefile 学习日记(四)——makefile的基本规则
- qt和makefile学习网址
- 运用Autoconf和Automake生成Makefile的学习之路
- 对 makefile 中 override 优先级的学习体会
- linux下Makefile学习
- Makefile学习
- gcc gdb makefile学习笔记
- 【Linux学习】Makefile学习(一)
- MakeFile学习
- Makefile 学习总结
- 移植中Makefile学习 关键字理解
- Linux学习笔记——例说makefile 多个文件
- MakeFile的学习