系统的Makefile文件
本文以某一类linux系统为例,分析该类linux系统中make的流程和原理。
打开makefile文件,在文件头部包含了变量定义和头文件包含,如下所示;
TOPDIR := ${shell pwd | sed -e 's/ /\\ /g'}
MEMSTATS =
-include $(TOPDIR)/.config
include $(TOPDIR)/tools/Config.mk
-include $(TOPDIR)/Make.defs
TOPDIR := ${shell pwd | sed -e 's/ /\\ /g'}:定义一个TOPDIR 的变量,该变量的值为{shell pwd | sed -e 's/ /\\ /g'}的执行结果,sed -e 's/ /\\ /g'该表达式为sed流编辑器的表达式,意思是跳过pwd执行后字符串里的空格;
sed替换命令格式为sed -i "s/oldstring/goalstring/g" file, -i为直接修改源文件,如果被替换的字符里包含有特殊字符,比如“/”、“\”等,这种情况下可以将sed语句里的分隔符‘/’换成‘#’,比如:sed -i 's#/#$(DELIM)#g' test;或者是在特殊符号之前加上转义字符‘\’,例如sed -i 's/\//\\/g' test;
关于sed流编辑器的详细用法,可参考http://www.cnblogs.com/cbscan/articles/2277351.html;
2-5行为当前Makefile所依赖的头文件,这里需要说明下include和-include的区别;
使用“include FILENAMES...”,make程序处理时,如果“FILENAMES”列表中的任何一个文件不能正常读取而且不存在一个创建此文件的规则时make程序将会提示错误并退出。
使用“-include FILENAMES...”的情况是,当所包含的文件不存在或者不存在一个规则去创建它,make程序会继续执行,只有真正由于不能正确完成终极目标的重建时(某些必需的目标无法在当前已读取的makefile文件内容中找到正确的重建规则),才会提示致命错误并退出。
(TOPDIR)/.config文件定义了一些系统和CPU等的宏定义和开关,比如;
CONFIG_HOST_LINUX=y
CONFIG_ARCH_ARM=y
CONFIG_ARCH="arm"CONFIG_ARCH_CORTEXR4=y
CONFIG_ARCH_FAMILY="armv7-r"
CONFIG_ARCH_CHIP="s5j"
CONFIG_ARCH_HAVE_FPU=y
打开(TOPDIR)/tools/Config.mk文件,可以看到里面定义的东西;
OBJEXT ?= .o
LIBEXT ?= .a
此处需要强调一下Makefile文件里=, :=, ?=, +=的区别:
= 是最基本的赋值
:= 是覆盖之前的值
?= 是如果没有被赋值过就赋予等号后面的值
+= 是添加等号后面的值
define COMPILEXX
@echo "CXX: $1"
$(Q) $(CXX) -c $(CXXFLAGS) $1 -o $2
endef
define COMPILE
@echo "CC: $1"
$(Q) $(CC) -c $(CFLAGS) $1 -o $2
endef
定义c++和c交叉编译工具,通过CXX变量将CXXFLAGS的文件编译生成对应的.0文件;通过CC变量将CFLAGS的文件编译生成对应的.0文件;
$(TOPDIR)/Make.defs文件除了定义一些OS配置相关的变量外,还定义了交叉编译类型;
CC = $(CROSSDEV)gcc
CXX = $(CROSSDEV)g++
CPP = $(CROSSDEV)gcc -E
LD = $(CROSSDEV)ld
AR = $(CROSSDEV)ar rcs
NM = $(CROSSDEV)nm
OBJCOPY = $(CROSSDEV)objcopy
OBJDUMP = $(CROSSDEV)objdump
同时还定义了各个交叉编译类型的条件,比如是否使用内建函数,对编译警告的处理,对编译优化的处理等等;
CFLAGS = $(ARCHCFLAGS) $(ARCHWARNINGS) $(ARCHOPTIMIZATION) $(ARCHCPUFLAGS) $(ARCHINCLUDES) $(ARCHDEFINES) $(EXTRADEFINES) -pipe -ffunction-sections -fda ta-sections
CPICFLAGS = $(ARCHPICFLAGS) $(CFLAGS)
CXXFLAGS = $(ARCHCXXFLAGS) $(ARCHWARNINGSXX) $(ARCHOPTIMIZATION) $(ARCHCPUFLAGS) $(ARCHXXINCLUDES) $(ARCHDEFINES) $(EXTRADEFINES) -pipe
CXXPICFLAGS = $(ARCHPICFLAGS) $(CXXFLAGS)
CPPFLAGS = $(ARCHINCLUDES) $(ARCHDEFINES) $(EXTRADEFINES)
AFLAGS = $(CFLAGS) -D__ASSEMBLY__
all:所有需要生成的最终文件;memstat为查看共享库的内存占用;
.PHONY:目标并非实际的文件名:只是在显式请求时执行命令的名字。有两种理由需要使用PHONY 目标:避免和同名文件冲突,改善性能。phony 目标并非是由其它文件生成的实际文件,make 会跳过隐含规则搜索;这就是声明phony 目标会改善性能的原因,即使你并不担心实际文件存在与否。
关于PHONY的更多知识,可以参考http://blog.chinaunix.net/uid-28458801-id-3452277.html;
# This is the name of the final target (relative to the top level directorty)
BIN_EXE = tinyara$(EXEEXT)
BIN = $(BIN_DIR)/$(BIN_EXE)
all: $(BIN) memstat
.PHONY: context clean_context check_context export subdir_clean clean subdir_distclean distclean apps_clean apps_distclean force_build
此处是对库文件的替换,如果CONFIG_ARCH_FLOAT_H=y,则用include/tinyara/float.h替换掉include/float.h;
ifeq ($(CONFIG_ARCH_FLOAT_H),y)
include/float.h: include/tinyara/float.h
$(Q) cp -f include/tinyara/float.h include/float.h
else
include/float.h:
endif
force_build为一个伪目标,其作用是保证伪目标的执行命令执行;然后进入tools/mkversion$(HOSTEXEEXT)下执行make指令;然后进入$(TOPDIR)/.version路径下,常见.version文件,写入版本号,修改文件权限;
force_build:
tools/mkversion$(HOSTEXEEXT):
$(Q) $(MAKE) -C tools -f Makefile.host TOPDIR="$(TOPDIR)" mkversion$(HOSTEXEEXT)$(TOPDIR)/.version: force_build
echo "create .version file"; \
tools/version.sh -v 1.0 .version; \
chmod 755 .version
include/apps: Make.defs包含apps路径下的Make.defs文件,如果APPDIR不为空,log输出相关的include路径;
include/apps: Make.defs
ifneq ($(APPDIR),)
@echo "LN: include/apps to $(APPDIR)/include"
$(Q) if [ -d $(TOPDIR)/$(APPDIR)/include ]; then \
$(DIRLINK) $(TOPDIR)/$(APPDIR)/include include/apps; \
fi
endif
context:上下文目标和目标库文件;if语句的意思是如果BIN目标文件存在,则删除;for语句为对CONTEXTDIRS变量的值(路径)开始逐一进行$(MAKE) -C $$dir TOPDIR="$(TOPDIR)" context语句;即在TOPDIR路径下执行make命令,context为所依赖的上下文文件;最后通过$(call DELFILE, $(APPDIR)/builtin/registry/*.?dat)删除临时文件;
context: check_context iotivity_context include/tinyara/config.h include/tinyara/version.h include/math.h include/float.h include/stdarg.h dirlinks
$(Q)if [ -e ${BIN} ]; then \
echo "Previous Build Outputs - $(BIN_DIR) - are deleted"; \
rm -rf ${BIN_DIR}/*; \
fi
$(Q) for dir in $(CONTEXTDIRS) ; do \
$(MAKE) -C $$dir TOPDIR="$(TOPDIR)" context; \
done
$(call DELFILE, $(APPDIR)/builtin/registry/*.?dat)
clean:当执行makefile clean的时候,会执行clean伪指令下的语句,call函数是唯一一个可以创建定制参数化的函数的引用函数。我们可以将一个变量定义为一个复杂的表达式,用“call”函数根据不同的参数对它进行展开来获得不同的结果,这里是指删除与后面的表达式相关的文件;
clean: subdir_clean
$(call DELFILE, $(BIN_DIR)/*)
$(call DELFILE, _SAVED_APPS_config)
$(call DELFILE, tinyara-export*)
$(call DELFILE, tinyara_user*)
$(call CLEAN)
系统makefile所包含的makefile
LibTargets.mk为系统makefile所include的一个makefile文件之一,该文件定义了生成APP所依赖的驱动文件和顶层库文件,此处列举一个驱动为例;当执行系统makefile文件时,运行到“include LibTargets.mk”语句,此时会进入LibTargets.mk文件进行语句展开和执行; 如果此时定义了CONFIG_ZIGBEE宏定义,则会进入到与该驱动相关的路径下执行“$(Q) $(MAKE) -C drivers$(DELIM)zigbee TOPDIR="$(TOPDIR)" libzigbee$(LIBEXT) KERNEL=y EXTRADEFINES=$(KDEFINE)”语句,即运行该驱动路劲下的makefile文件;
include LibTargets.mk
展开后为:
ifeq ($(CONFIG_ZIGBEE),y)
os$(DELIM)drivers$(DELIM)zigbee$(DELIM)libzigbee$(LIBEXT): context
$(Q) $(MAKE) -C drivers$(DELIM)zigbee TOPDIR="$(TOPDIR)" libzigbee$(LIBEXT) KERNEL=y EXTRADEFINES=$(KDEFINE)
进入drivers/zigbee路径下的makefile之后,可以看到include src$(DELIM)components$(DELIM)Make.defs,查看对应路径的Make.defs文件;
CSRCS 为系统定义的源文件变量,此处将需要编译的.c文件添加到CSRCS 变量即可;下面是对编译库变量的添加,只需要添加当前路径就OK了;
CSRCS += af.c
CSRCS += af_ep.c
CSRCS += af_tx.c# Include zigbee devices build support
DEPPATH += --dep-path src$(DELIM)components$(DELIM)stack$(DELIM)af
VPATH += :src$(DELIM)components$(DELIM)stack$(DELIM)af
CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)drivers$(DELIM)zigbee$(DELIM)src$(DELIM)components$(DELIM)stack$(DELIM)af}
回归到driver/zigbee下的makefile文件;all为目标文件,depend后为需要编译的源文件,$(Q) $(MKDEP) $(DEPPATH) "$(CC)" -- $(CFLAGS) -- $(SRCS) >Make.dep 为编译当前路径下所链接到的所有的C文件,将结果输出到对应的Make.dep文件里。
all: $(BIN)
$(AOBJS): %$(OBJEXT): %.S
$(call ASSEMBLE, $<, $@)$(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS)).depend: Makefile $(SRCS)
$(Q) $(MKDEP) $(DEPPATH) "$(CC)" -- $(CFLAGS) -- $(SRCS) >Make.dep
$(Q) touch $@depend: .depend
clean:
$(call DELFILE, $(BIN))
$(call CLEAN)distclean: clean
$(call DELFILE, Make.dep)
$(call DELFILE, .depend)
网友评论