关于如何学习Makefile
成熟工程的Makefile编写会比较复杂,看相关的介绍总会觉得不理解,不清晰
可以尝试自己编写一个简单的Makefile去验证
比如我在一个文件夹下创建a.c
文件,并在同目录下编写一个Makefile,即可测试Makefile的相关语法
#a.c
int main(){
return 0;
}
#Makefile
LIBODIR := /home
GEN_LIBS :=libfreertos.a libmy.a
OLIBS := $(GEN_LIBS:%= $(LIBODIR)/%)
a:a.c
@echo $(GEN_LIBS)
@echo $(LIBODIR)
@echo $(OLIBS)
gcc $^ -o $@
此Makefile可用于验证
A := $(B:%=$(C)/%)
的作用,终端cd
到该目录,并输入make
,将可得到
libfreertos.a libmy.a
/home
/home/libfreertos.a /home/libmy.a
gcc a.c -o a
即此语句的作用为:寻找
$(GEN_LIBS)
中的每一项,并在其前分别加上$(LIBODIR)/
,最后给$(OLIBS)
赋值,而$(GEN_LIBS)
$(LIBODIR)
的值均不变
接下来,开始分析ESP8266的Makefile
如代码段3.2和代码段2.1所示,从子目录 ESP8266_RTOS_SDK-master/third_party/freertos/
中执行Makefile,将会包含上级目录ESP8266_RTOS_SDK-master/third_party/
的Makefile,而该Makefile又包含根目录的Makefile。
$LIBODIR
由此可见,代码段1.1中的$LIBODIR
实际上等于.ouput/eagle/debug/lib
此时的工作目录在ESP8266_RTOS_SDK-master/third_party/freertos/
中,
即为ESP8266_RTOS_SDK-master/third_party/freertos/.ouput/eagle/debug/lib
这是LIB文件生成的目录。
$OLIBS
由代码段3.1可知 $GEN_LIBS
为libfreertos.a
则OLIBS := $(GEN_LIBS:%=$(LIBODIR)/%)
中的%
为 libfreertos
即$OLIBS
为$LIBODIO/libfreertos.a
,为目标库文件的完整文件名(即带路径的文件名)
$ (foreach …,…,…) | $(eval ..)
$(A ...)
的意思是执行函数A
此处是执行foreach和eval函数
foreach用于遍历循环,而eval用于把内容转化成Makefile语句,即让Make执行eval里面的内容
代码段1.4中的语句的意思是取出$(GEN_LIBS)
中的所有元素,对每个元素进行同样的操作,操作内容是让Makefile执行 自定义的命令包
此处命令包的名字叫ShortcutRule
,在代码段1.2中有其定义,
同时把$(lib)
$(LIBODIR)
当作参数传入该命令包
$(lib)
是在foreach
函数中,从$(GEN_LIBS)
得到的每个元素
此处,$(GEN_LIBS)
只有一个元素:libfreertos.a
所以该命令包只执行一次
ShortcutRule
由代码段1.2可得,ShortcutRule的内容为
$(1): .subdirs $(2)/$(1)
即把$(1)
作为目标文件,把.subdirs
、$(2)/$(1)
作为其依赖文件
$(1)、$(2)
分别为第一参数和第二参数
此处$(1)、$(2)
分别为$(lib)
$(LIBODIR)
则此处的代码展开为
libfreertos.a: .subdirs ESP8266_RTOS_SDK-master/third_party/freertos/.ouput/eagle/debug/lib/libfreertos.a
MakeLibrary
由代码段1.4可得,在确定了目标库文件及其依赖文件后,紧接着对MakeLibrary
命令块进行了调用,以生成库文件
传入参数为libfreertos
和libfreertos.a
$$(COMPONENTS_$(1))
没有定义
$$(DEPENDS_$(1))
没有定义
则由代码段1.3可得,
DEP_LIBS_$(1)
为NULL
DEP_OBJS_$(1)
为NULL
$$(LIBODIR)/$(1).a: $$(OBJS) $$(DEP_OBJS_$(1)) $$(DEP_LIBS_$(1)) $$(DEPENDS_$(1))
此处的
$$(LIBODIR)/$(1).a
实际上就是ShortcutRule
中的第二个依赖文件,此处为目标文件
该目标文件依赖$$(OBJS)
由代码段1.1中有$$(OBJS)
的赋值,其中$(CSRCS)、$(CPPSRCS)、$(ASRCs)、$(ASRCS)、
分别为执行Make的文件夹,也就是ESP8266_RTOS_SDK-master/third_party/freertos/
里面的所有.c、.cpp、.s、.S
文件
$OBJ
则代表所有在上述文件夹里有的源文件所需产生的编译文件.o
的完整文件名
相关代码,代码段顺序与文件中顺序一致
以下代码位于 ESP8266_RTOS_SDK-master/ :
#ESP8266_RTOS_SDK-master/Makefile
#代码段1.1
ODIR := .output
#...
OBJS := $(CSRCS:%.c=$(OBJODIR)/%.o) \
$(CPPSRCS:%.cpp=$(OBJODIR)/%.o) \
$(ASRCs:%.s=$(OBJODIR)/%.o) \
$(ASRCS:%.S=$(OBJODIR)/%.o)
DEPS := $(CSRCS:%.c=$(OBJODIR)/%.d) \
$(CPPSRCS:%.cpp=$(OBJODIR)/%.d) \
$(ASRCs:%.s=$(OBJODIR)/%.d) \
$(ASRCS:%.S=$(OBJODIR)/%.d)
LIBODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/lib
OLIBS := $(GEN_LIBS:%=$(LIBODIR)/%)
#ESP8266_RTOS_SDK-master/Makefile
#代码段1.2
define ShortcutRule
$(1): .subdirs $(2)/$(1)
endef
#ESP8266_RTOS_SDK-master/Makefile
#代码段1.3
define MakeLibrary
DEP_LIBS_$(1) = $$(foreach lib,$$(filter %.a,$$(COMPONENTS_$(1))),$$(dir $$(lib))$$(LIBODIR)/$$(notdir $$(lib)))
DEP_OBJS_$(1) = $$(foreach obj,$$(filter %.o,$$(COMPONENTS_$(1))),$$(dir $$(obj))$$(OBJODIR)/$$(notdir $$(obj)))
$$(LIBODIR)/$(1).a: $$(OBJS) $$(DEP_OBJS_$(1)) $$(DEP_LIBS_$(1)) $$(DEPENDS_$(1))
@mkdir -p $$(LIBODIR)
$$(if $$(filter %.a,$$?),mkdir -p $$(EXTRACT_DIR)_$(1))
$$(if $$(filter %.a,$$?),cd $$(EXTRACT_DIR)_$(1); $$(foreach lib,$$(filter %.a,$$?),$$(AR) xo $$(UP_EXTRACT_DIR)/$$(lib);))
$$(AR) ru $$@ $$(filter %.o,$$?) $$(if $$(filter %.a,$$?),$$(EXTRACT_DIR)_$(1)/*.o)
$$(if $$(filter %.a,$$?),$$(RM) -r $$(EXTRACT_DIR)_$(1))
endef
#ESP8266_RTOS_SDK-master/Makefile
#代码段1.4
$(foreach lib,$(GEN_LIBS),$(eval $(call ShortcutRule,$(lib),$(LIBODIR))))
...
$(foreach lib,$(GEN_LIBS),$(eval $(call MakeLibrary,$(basename $(lib)))))
以下代码位于 ESP8266_RTOS_SDK-master/third_party/:
##ESP8266_RTOS_SDK-master/third_party/Makefile
#代码段2.1
TARGET = eagle
#FLAVOR = release
FLAVOR = debug
#EXTRA_CCFLAGS += -u
ifndef PDIR # {
GEN_IMAGES= eagle.app.v6.out
GEN_BINS= eagle.app.v6.bin
SPECIAL_MKTARGETS=$(APP_MKTARGETS)
SUBDIRS= \
user
endif # } PDIR
#...
INCLUDES := $(INCLUDES) -I $(PDIR)include
sinclude $(SDK_PATH)/Makefile
以下代码位于 ESP8266_RTOS_SDK-master/third_party/freertos/:
#ESP8266_RTOS_SDK-master/third_party/freertos/Makefile
#代码段3.1
ifndef PDIR
GEN_LIBS = libfreertos.a
endif
#ESP8266_RTOS_SDK-master/third_party/freertos/Makefile
#代码段3.2
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile
#本文件结束