一、概述
笔者已经写了一篇实现目标文件与源码分开的makefile测试实验,但是觉得不够完美,没有更多的体现u-boot Makefile的工作原理和特点。所以,决定重新修订,使之更加充分的接近u-boot Makfefile的原貌。
至于,u-boot Makfefile的整体论述和特点,笔者在u-boot Makefile整体解析文中已经详细论述,不在多言。
二、测试源码构成
整个测试源码,由四个文件夹构成,三个是源文件夹,分别包含源程序(.c)和Makefile。顶层目录中包含config.mk和rules.mk文件。
三、测试源码注释
1、顶层Makfile
ifneq ($(BUILD_DIR),) #此变量在命令行选择定义,定义了就是目标文件和源码文件分开,否则是同一个 saved-output := $(BUILD_DIR) # Attempt to create a output directory. $(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}) #创建目录BUILD_DIR # Verify if it was successful. BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd) $(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist)) endif # ifneq ($(BUILD_DIR),) OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR)) #目标文件生成的根目录,可以认为是一个常量,\ 区别于obj SRCTREE := $(CURDIR) #CURDIR是当前目录的路径,目录改变,它随之改变 TOPDIR := $(SRCTREE) LNDIR := $(OBJTREE) export SRCTREE OBJTREE TOPDIR ifneq ((OBJTREE),(SRCTREE)) REMOTE_BUILD := 1 export REMOTE_BUILD #此变量会被config.mk所调用 endif include $(TOPDIR)/config.mk #config.mk中定义了obj、CC等变量,下边的内容需要调用 CROSS_COMPILE = export CROSS_COMPILE OBJS = add/add.o sub/sub.o test/test.o #需要生成的目标文件 OBJS := $(addprefix $(obj),$(OBJS)) #需要生成目标文件的完整路径 .PHONY : $(OBJS) #确保源文件修改了,目标文件能够自动重建 ALL = $(obj)program #在program前边也加上了前缀obj all: $(ALL) $(obj)program: $(OBJS) $(CC) $^ -o $@ $(OBJS): make -C $(dir $(subst $(obj),,$@)) #依次执行各个子目录中的makefile SUBDIRS = add/ sub/ test/ .PHONY: clean distclean clean: for f in $(SUBDIRS); do make -C $$f clean; done //依次在各个子目录中删除 rm -f $(ALL) distclean: for f in $(SUBDIRS); do make -C $$f distclean; done rm -f $(ALL)
2、config.mk
ifneq ($(OBJTREE),$(SRCTREE)) #判断是否选择目标文件生成目录和源码目录分开 ifeq ($(CURDIR),$(SRCTREE)) #以下内容是目标文件生成目录和源码目录分开的情况 dir := else dir := $(subst $(SRCTREE)/,,$(CURDIR)) endif obj := $(if $(dir),$(OBJTREE)/$(dir)/,$(OBJTREE)/) #确定要生成目标文件的完整路径, #注意这是个变量,在不同的子目录中,执行makefile,obj的值相应的发生变化 src := $(if $(dir),$(SRCTREE)/$(dir)/,$(SRCTREE)/) #确定当前源文件的目录 $(shell mkdir -p $(obj)) #创建目标文件的完整路径,注意gcc命令并不能自动生成缺少的路径,所以需要事先创建 else #以下内容是目标文件生成目录和源码目录同一的情况 obj := #目标文件与源文件目录相同时,obj没有必要赋值 src := endif #以下的规则将用来编译子目录中的源文件 # Include the make variables (CC, etc...) # AS = $(CROSS_COMPILE)as LD = $(CROSS_COMPILE)ld CC = $(CROSS_COMPILE)gcc CPP = $(CC) -E AR = $(CROSS_COMPILE)ar OBJCOPY = $(CROSS_COMPILE)objcopy OBJDUMP = $(CROSS_COMPILE)objdump ######################################################################### ifndef REMOTE_BUILD %.s: %.S $(CPP) $(AFLAGS) -o $@ $< %.o: %.S $(CC) $(AFLAGS) -c -o $@ $< %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< else $(obj)%.s: %.S $(CPP) $(AFLAGS) -o $@ $< $(obj)%.o: %.S $(CC) $(AFLAGS) -c -o $@ $< $(obj)%.o: %.c $(CC) $(CFLAGS) -c -o $@ $< endif #########################################################################
3、rules.mk
#########################################################################
$(obj).depend: $(src)Makefile $(TOPDIR)/config.mk $(SRCS) #SRCS在子目录Makefile中定义
@rm -f $@
@for f in $(SRCS); do \
g=`basename $$f | sed -e 's/\(.*\)\.\w/\1.o/'`; \
$(CC) -M $(HOST_CFLAGS) $(CPPFLAGS) -MQ $(obj)$$g $$f >> $@ ; \
done
#########################################################################
4、子目录Makefile
include $(TOPDIR)/config.mk #包含config.mk COBJS := add.o #.c文件生成的目标文件 SOBJS := #.S文件生成的目标文件 SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) #所有参与编译的源文件(.c、.S) OBJS := $(addprefix $(obj),$(COBJS))#添加目录obj SOBJS := $(addprefix $(obj),$(SOBJS)) all: $(obj).depend $(OBJS) #先生成依赖文件,然后才能编译源程序 clean: rm -f $(SOBJS) $(OBJS) distclean: clean rm -f $(LIB) $(obj).depend ######################################################################### # defines $(obj).depend target include $(SRCTREE)/rules.mk #包含rules.mk sinclude $(obj).depend #包含.depend文件 #########################################################################
四、编译方法
1、目标文件与源码混在一起
直接在顶层目录中执行:#make
2、目标文件与源码分开
方法一:
先声明个环境变量:#exprort BUILD_DIR=/tmp/build //这里以/tmp/build为例
然后在顶层目录中执行:#make
方法二:
直接在顶层目录中执行:#make BUILD_DIR=/tmp/build