当前位置:文档之家› C++项目的Makefile编写

C++项目的Makefile编写

一个C++项目的Makefile编写-Tony与Alex的对话系列- -Tony : Hey Alex, How are you doing?Alex : 不怎么样。

(显得很消沉的样子)Tony : Oh , Really ? What is the matter?Alex : 事情是这样的。

最近有一个Unix下的C++项目要求我独自完成,以前都是跟着别人做,现在让自己独立完成,还真是不知道该怎么办,就连一个最简单的项目的Makefile都搞不定。

昨晚看了一晚上资料也没有什么头绪。

唉!!Tony : 别急,我曾经有一段时间研究过一些关于Makefile的东西,也许能帮得上忙,来,我们一起来设计这个项目的Makefile。

Alex : So it is a deal。

(一言为定)Tony : 我们现在就开始吧,给我拿把椅子过来。

(Tony坐在Alex电脑的旁边)Tony : 把你的项目情况大概给我讲讲吧。

Alex : No Problem ! 这是一个“半成品”项目,也就是说我将提供一个开发框架供应用开发人员使用,一个类似MFC的东西。

Tony : 继续。

Alex : 我现在头脑中的项目目录结构是这样的:APL (Alex's Programming Library)-Make.properties-Makefile(1)-include //存放头文件-Module1_1.h-Module1_2.h-Module2_1.h-Module2_2.h-src //存放源文件-Makefile(2)-module1-Module1_1.cpp-Module1_2.cpp-Makefile(3)-module2-Module2_1.cpp-Module2_2.cpp-Makefile(3)-...-lib //存放该Project依赖的库文件,型如libxxx.a-dist //存放该Project编译连接后的库文件libapl.a-examples //存放使用该“半成品”搭建的例子应用的源程序Makefile(4)-src //存放应用源代码-include-bin //存放应用可执行程序-appdemo2-Makefile(5)-src //存放应用源代码-include-bin //存放应用可执行程序-...Tony : I got it!Alex : 下面我们该如何做呢?Tony : 我们来分析一下各个Makefile的作用。

你来分析一下各个级别目录下的Makefile的作用是什么呢?Alex : (思考了一会儿)我想应该是这样的吧。

Makefile(3)负责将其module下的.cpp源文件编译为同名.o文件,同时其phony target "clean"负责删除该目录下的所有.o文件;Makefile(2)负责调用src目录下所有module的Makefile文件。

Makefile(1)负责先调用src中的Makefile生成静态库文件,然后调用examples中的Makefile构建基于该框架的应用。

至于Make.properties,定义通用的目录信息变量、编译器参数变量和通用的依赖关系。

Tony : 说得很好。

我们一点一点来,先从src中每个module下的Makefile着手,就如你所说在每个module下的Makefile负责将该module下的.cpp文件编译为同名的.o文件。

Alex : 好的,我来写吧,这个我还是能搞定的。

看下面:module1下的Makefile如下:## Makefile for module1#all : Module1_1.o Module1_2.oModule1_1.o : Module1_1.cppg++ -c $^ -I ../../includeModule1_2.o : Module1_2.cppg++ -c $^ -I ../../includeclean :rm -f *.omodule2下的Makefile如下:## Makefile for module2#all : Module2_1.o Module2_2.og++ -c $^ -I ../../includeModule2_2.o : Module2_2.cppg++ -c $^ -I ../../includeclean :rm -f *.omake一下,顺利产生相应的.o文件。

/*=============================================================Note: 关于$^、$<和$@的用法说明:$@ -- “$@”表示目标的集合,就像一个数组,“$@”依次取出目标,并执于命令。

$^ -- 所有的依赖目标的集合。

以空格分隔。

如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。

$< -- 依赖目标中的第一个目标名字举例: Module1_1.o Module1_2.o : Module1_1.cpp Module1_2.cpp则$@ -- Module1_1.o Module1_2.o$^ -- Module1_1.cpp Module1_2.cpp$< -- Module1_1.cpp==============================================================*/Tony : Well done! 不过发现什么问题了么?Alex : 什么问题?Tony : 存在重复的东西。

在重构中我们知道如果两个子类中都定义相同的接口函数,我们会将其pull up到基类中。

同样我们可以重构我们的Makefile,把一些重复的东西拿到外层去。

Alex : (似乎略微明白了一些)我想有三处重复:a)查找头文件的路径是重复的; b)g++这个字符串可以用一个变量定义代替c)编译器的编译参数可以也定义到一个变量中。

我知道Make工具支持include一个文件,我们就建立一个公用的文件来存放一些通用的东西吧。

Tony : 没错,Just do it.Alex : 就按我原先的想法,把这些公共的部分放到Make.properties中吧。

## Properties for demo's Makefile#MAKEFILE = MakefileBASEDIR = $(HOME)/proj/demo##################### Directory layout #####################SRCDIR = $(BASEDIR)/srcINCLUDEDIR = $(BASEDIR)/includeLIBDIR = $(BASEDIRE)/lib##################### Compiler options ## F_ -- FLAG #####################CC = g++# Compiler search optionsF_INCLUDE = -I$(INCLUDEDIR)F_LIB = -L $(LIBDIR)CFLAGS =CPPFLAGS = $(CFLAGS) $(F_INCLUDE)然后修改一下,各个module中的Makefile文件,以module1为例,修改后如下:## Makefile for module1#include ../../Make.propertiesall : Module1_1.o Module1_2.oModule1_1.o : Module1_1.cpp$(CC) -c $^ $(CPPFLAGS)Module1_2.o : Module1_2.cpp$(CC) -c $^ $(CPPFLAGS)clean :rm -f *.oTony : 其实这两个Makefile中还有一个隐含的重复的地方Alex : 你是指依赖规则么?Tony : 嗯,这个依赖规则在src中的各个module中都会用得到的。

Alex : 没错,我也是这么想的,我现在就把这个规则抽取出来,然后你来评审一下。

我想利用make 工具的传统的“后缀规则”来定义通用依赖规则,我在Make.properties加入下面的变量定义:##################### Common depends #####################DEPS = .cpp.o然后还是以module1为例,修改module1的Makefile后如下:## Makefile for module1#include ../../Make.properties$(CC) -c $^ $(CPPFLAGS)clean :rm -f *.oTony : 基本满足需求。

我们可以进行上一个层次的Makefile的设计了。

我们来设计Makefile(2)。

Alex,你来回顾一下Makefile(2)的作用。

/*=============================================================Note: 关于后缀规则的说明后缀规则中所定义的后缀应该是make 所认识的,如果一个后缀是make 所认识的,那么这个规则就是单后缀规则,而如果两个连在一起的后缀都被make 所认识,那就是双后缀规则。

例如:".c"和".o"都是make 所知道。

因而,如果你定义了一个规则是".c.o"那么其就是双后缀规则,意义就是".c"是源文件的后缀,".o"是目标文件的后缀, ".c.o"意为利用.c 文件构造同名.o文件。

==============================================================*/Alex : No Problem! 正如前面说过的Makefile(2)负责调用src目录下所有module子目录下的Makefile 文件,并负责将各个module下的.o文件打包为libdemo.a文件放到dist目录中。

所以存在简单的依赖关系就是libdemo.a依赖各个module子目录下的.o文件,而前面的Makefile(3)已经帮我们解决了.o 文件的生成问题了,即我们只需要逐个在各module子目录下make即可。

我的Makefile(2)文件设计如下:## Makefile for src directory#include ../Make.propertiesTARGET = libdemo.a##################### Subdirs define #####################MODULE1_PA TH = module1MODULE2_PA TH = module2SUBDIRS = $(MODULE1_PA TH) $(MODULE2_PA TH)##################### Objects define #####################MODULE1_OBJS = $(MODULE1_PA TH)/Module1_1.o $(MODULE1_PA TH)/Module1_2.oMODULE2_OBJS = $(MODULE2_PA TH)/Module2_1.o $(MODULE2_PA TH)/Module2_2.oDEMO_OBJS = $(MODULE1_OBJS) $(MODULE2_OBJS)all : subdirs $(TARGET)@for i in $(SUBDIRS); do \echo "===>$$i"; \(cd $$i &&$(MAKE) -f $(MAKEFILE)) || exit 1; \echo "<===$$i"; \done$(TARGET) : $(DEMO_OBJS)ar -r $@ $^clean:@for i in $(SUBDIRS); do \echo "===>$$i"; \(cd $$i &&$(MAKE) clean -f $(MAKEFILE)) || exit 1; \echo "<===$$i"; \donerm -f $(DISTDIR)/$(TARGET)Tony : Alex你的进步真的是很大,分析问题的能力提高的很快,方法也不错。

相关主题