通用makefile是如何炼成的(IV)

来源:互联网 发布:袜子品牌 知乎 编辑:程序博客网 时间:2024/06/04 04:46

现在我们的主makefile已经是这样的

# main.mk#include build/systems/system.mk## 定义产品信息。这里BUILD_SPECS设置为spec.mkPRODUCT_SPECS :=build/spec.mkinclude build/products/product.mk## 定义平台相关的编译命令。这里PLATFORM_SPECS为空,表示我们不做额外定制化。PLATFORM_SPECS :=include build/platforms/platform.mk## 将产品信息中的定义的feature与平台定义的编译选项混合CXXFLAGS+=$(OPTIONS)OBJS:=hello.o main.oTARGET:=hello.exe## 定义obj文件的编译规则hello.o : hello.cpp hello.h$(CXX) $(CXXFLAGS) -c $< -o $@main.o : main.cpp hello.h$(CXX) $(CXXFLAGS) -c $< -o $@## 定义可执行文件hello.exe的编译规则$(TARGET):$(OBJS)$(CXX) $(CXXFLAGS)  -o $@ $^all: ...


在此之际,回顾一下一开始提到的最原始的makefile的几个毛病

地方

1. makefile内容依赖于源文件。如果再增加一个world.cpp, 则必须再修改makefile后才能编译。只好期望源文件不要太多了

2. 生成obj文件时,依赖的源文件和头文件是手动添加的,不会自动推导。这就导致以后修改源文件时,必须同步维护makefile中头文件依赖关系。太累了

3. g++ 以及相应的编译选项都是写死的,如果想要交叉编译一个arm平台上运行的可执行文件,哦,又要修改makefile

4. 没有编译产品的信息,好歹给个立牌坊的机会

想现在毛病3,4已经基本漂亮的解决了(“基本”的含义是?,请看后文),地也圈了,楼也盖了不少(products目录,platforms目录,systems目录),本来坐等发财,结果发现就因为毛病2,集团内容沟通不利。随便改改源文件,可以就要同步调整makefile了。要解决这个头文件依赖关系的自动推导问题,呵呵,GNU中直接有尚方宝剑MMD,MF, MT。

SRC_FILES:= hello.cpp main.cppOBJS:=hello.o main.oTARGET:=hello.exe## -MMD 可以用于自动生成头文件依赖关系%.o : %.cpp$(CXX) $(CXXFLAGS) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$@" -MT"$(@:%.o=%.d)" -c "$<" -o "$@" DEPS := hello.d main.difneq "$(MAKECMDGOALS)" "clean"-include $(DEPS)endif## 定义可执行文件hello.exe的编译规则$(TARGET):$(OBJS)$(CXX) $(CXXFLAGS)  -o $@ $^

关于hello.d, main.d,他们的内容就是具体的依赖关系,然后通过include将依赖关系导入,如此就大功告成。

## hello.dhello.o hello.d: hello.cpp hello.hhello.h:

详细的,hello.d具体怎么生成的,或者其他更多自动生成头文件依赖关系的,就“不要问我从哪里来,我的答案在度娘”


现在依赖关系是自动生成了,不过obj文件列表,还有那些.d文件列表,都是手动填写的,可以自动化吗?

答案是肯定的,要不然通用makefile就炼不成了。(大笑就像是看电视剧或者动漫的,都知道主角光环,无论前面如何复杂艰险,主角总是能化险为夷的)

makefile中有一种类似于shell脚本中的变量替换一样,我们可以直接从源文件列表导出obj文件列表,以及.d文件列表。不多说,直接上代码

SRC_FILES:= hello.cpp main.cppOBJS:=$(SRC_FILES:.cpp=.o)DEPS:=$(OBJS:.o=.d)TARGET:=hello.exe## -MMD 可以用于自动生成头文件依赖关系%.o : %.cpp$(CXX) $(CXXFLAGS) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$@" -MT"$(@:%.o=%.d)" -c "$<" -o "$@" ifneq "$(MAKECMDGOALS)" "clean"-include $(DEPS)endif## 定义可执行文件hello.exe的编译规则$(TARGET):$(OBJS)$(CXX) $(CXXFLAGS)  -o $@ $^

如果你愿意,源文件列表也是可以自动推导的:给定源文件路径,通过$(foreach dir, $(LOCAL_SRC_PATHS), $(wildcard $(dir)/*.cpp))命令,可以自动将指定目录下的.cpp文件全部找出来。


今天的工作做得很漂亮,以后只要配置好makefile,以及列出源文件列表,就可以直接编译了,其他什么也不用改。甚至对于源文件列表,也可以通过指定目录以及命令wildcard自动推导出来。

走到这里,感觉人生已经无欲无求了,通用makefile真的已经通用了。However,我又是看得太远,看这段makefile,我想到两个问题

1. 上面的生成obj文件和.exe文件的代码是很形式化的,如果可以应该同product一样,单独归类到某个文件夹中,防止在修改makefile时不小心改了这些代码

2. 如果我是多模块编译,怎么办呢?

路漫漫其修远兮,吾将上下而求索...


0 0
原创粉丝点击