同事想看开源项目contiki,无奈被其复杂的Makefile搞晕而找我帮忙。我大致看了一些,由于这个项目涉及到多个目标平台,结构复杂可想而知啦-_-!!!。这里用相对简单的原生平台(native)来剖析一下。
大致上有四个重要的Makefile文件:
-
自己的应用程序Makefile,如examples/email/Makefile:
这个定义了最终的目标应用程序名,如email:
CONTIKI_PROJECT = email-client all: $(CONTIKI_PROJECT) APPS = email CONTIKI = ../.. include $(CONTIKI)/Makefile.include
-
根目录的Makefile.include:
这个提供了统一的Makefile框架,提供一般的编译选项和功能,各个平台可以覆盖这些默认规则。
-
platform/native/Makefile.native:
这个是平台相关的硬件,如串口、led等,以及定义最终链接生成的目标文件%.$(Target)的编译规则。原生平台(native)直接在Makefile.include里定义了最终目标%.$(Target)的规则了。其他的平台要定义宏CUSTOM_RULE_LINK来进行覆盖。
-
cpu/native/Makefile.native
这个定义目标文件和库的编译。如果这里不是native平台,要定义自己的链接和编译规则,要预先定义几个宏覆盖系统默认的规则:
CUSTOM_RULE_LINK=1 CUSTOM_RULE_C_TO_OBJECTDIR_O=1 CUSTOM_RULE_ALLOBJS_TO_TARGETLIB=1
上面分别对应链接、编译和打包成库。编译和打包成库直接在这个Makefile里,最终的链接在platform//Makefile.里。
讲了这么多,来看一下最技巧性的地方。
CONTIKI_PROJECT = email-client
all: $(CONTIKI_PROJECT)
这里只指定了一个email-client,不带任何扩展名。那make如果知道依赖什么能?这里就要用到make的隐式规则了。
隐式规则
make看到email-client这个目标会采用两条隐式规则:
%:%.c
gcc $(CFLAGS) %< -o $@
或者
# 这个会生成中间文件xxx.o,make会在完成时删除。
%:%.o
gcc %< -o $@
%.o:%.c
gcc $(CFLAGS) -c %< -o $@
那如果要让make执行我预定义的依赖项:
# @ 表示占位符,如果没有则代表删除这条规则
%:%.$(TAGET)
@
则要删除%:%.c这条隐式规则才能执行我的%:%.$(TAGET)。至于第二条隐式规则相比我的%:%.$(TAGET)要复杂,make是比较聪明的不回绕远路的。
删除%:%.c规则(不加命令项):
%:%.c
重看Makefile.include
有了上面的知识,下面就比较好理解了,最终的目标是email-client.native,链接规则是:
ifndef CUSTOM_RULE_LINK
%.$(TARGET): %.co $(PROJECT_OBJECTFILES) $(PROJECT_LIBRARIES) contiki-$(TARGET).a
$(LD) $(LDFLAGS) $(TARGET_STARTFILES) ${filter-out %.a,$^} ${filter %.a,$^} $(TARGET_LIBFILES) -o $@
endif
编译规则是:
ifndef CUSTOM_RULE_C_TO_OBJECTDIR_O
$(OBJECTDIR)/%.o: %.c
$(CC) $(CFLAGS) -MMD -c $< -o $@
@$(FINALIZE_DEPENDENCY)
endif
打包生产库规则:
ifndef CUSTOM_RULE_ALLOBJS_TO_TARGETLIB
contiki-$(TARGET).a: $(CONTIKI_OBJECTFILES)
$(AR) $(AROPTS) $@ $^
endif
其他平台的类似,只是中间有很多步骤,这里不细讲。