自建工程makefile文件
前言:本人是linux开发C++工程师,最近心血来潮从0学习了Makefile。在网上搜索了N篇资料。终于写了一个符合自己需求的Makefile。
需求:
自建一个工程,工程目录树:
. ├── bin │ └── libPubClass.so ├── Makefile ├── objs │ ├── pubclass │ │ └── deamon │ └── util │ └── deamon │ ├── deamon.d │ └── deamon.o ├── shell │ └── home └── src ├── business ├── pubclass ├── startApp.cpp ├── test │ └── deamon ├── third-part └── util └── deamon ├── deamon.cpp └── deamon.h
代码主要放在src目录。objs目录 用来保存编译目录文件,bin目录存放生成的可执行文件及动态库。其中src/business用于业务相关目录,src/third-part用于第三 方代码,如boost,src/util用于公共代码,pubclass用于公共类。
正式动手之前应该了解下下面的知识。
1、makefile中的三个重要函数
wildcard
在目录下根据正则表达式遍历文件。
用法:wildcard $(dir).cpp
在dir目录下遍历所有cpp后缀文件,并以空格作为分割返回。
foreach
用法:foreach dir $(DIRS) action
将DIRS的依次赋于dir(不一定是目录),并执行action动作。action可以根据dir运行表达式得到想要的结果。
patsubst
用法:patsubst %.cpp, %.o $(FILES)
将FILES所有以cpp结尾的字符串替换成以o结尾。
根据上面三个函数,可以得到我们要的东西,如:
FILES=$(foreach dir, $(DIRS),$(wildcard $(dir).cpp)
可以得到DIRS定义目录下的所有cpp文件,以目录+文件的形式赋值给FILES
OBJS=$(patsubst src%, objs%, $(patsubst %.cpp, %.o, $(FILES)))
得到objs文件
DEPS=$(patsubst %.o, %.d, $(OBJS))
得到DEPS文件
思路:
src 下的代码app文件先用g++ -MM生成DEPS文件(放到objs对应目录,一个文件一个独立DEPS文件),后利用include加载到Makefile中,则可编译生成对应 obj文件,放到objs目录下。后根据规则将目标文件统一编译生成可执行文件及动态库到bin目录下。
思路分解:
将上面思路分解,先生成DEPS,将DEPS加载到makefile中,根据makefile规则生成obj文件,再将obj编译成动态库,最后将主文件链接动态库生成最终可执行文件。
思路清析了,下面正式动手:
1、生成DEPS:
$(DEPS):$(FILES)
g++ -MM $< -o $@ |
sed "$ s+$(patsubst %.d,%.o, $(notdir $@))+$(patsubst %.d,%.o,$@)+g" |
sed '$$ a\tg++ -fPIC -shared -c -o $(patsubst %.d, %.o, $@) $<' > $@
这里因为g++ -MM $< -o $@将如下结果输出到屏幕。
deamon.o: src/util/deamon/deamon.cpp src/util/deamon/deamon.h
所以我们要作一些处理,并重定向。刚加了两个sed命令。最终结果为:
objs/util/deamon/deamon.o:
src/util/deamon/deamon.cpp src/util/deamon/deamon.h
g++ -fPIC -shared -c -o objs/util/deamon/deamon.o
src/util/deamon/deamon.cpp
这就是我们想要的。
2、将DEPS加载到makefile中。
这个很简单,只要用include命令就可以了,看下面代码
-include $(DEPS)
这跟c中的#include效果是一样的。相当于在makefile中增加了一行代码
3、现在已经将DEPS加载到makefile,但怎样利用这段代码呢?
只需要在makefile加如下依赖关系就可以了:
$(TARGET):$(OBJS)
g++ -o $(TARGET) $^
这样,所有的objs目标文件就编译生成了我们想要的APP文件了。
如果想生成动态库,则加上-shared和-fPIC选项就可以了。
$(LIB):$(OBJS)
g++ -shared -fPIC -o $(LIB) $^
如果生成可执行文件时要加上动态库
$(TARGET):$(OBJS)
g++ -o $(TARGET) $^ -LLIBPATH -lyourlibname
4、你以为只是这样吗?
makefile文件执行时,是以第一个目标为入口的。也就是说我们要把之前生成的依赖关系通过第一个目标建立依赖关系。
现在我们理一理我们生成的文件。
1、DEPS文件,要加载到makefile中
2、objs文件,用来生成动态库或可执行文件
3、可执行文件或动态库
首先要生成的是DEPS文件,然后通过DEPS加载到makefile中的命令生成objs,最后生成可执行文件或库文件当然库文件要先于可执行文件生成
所以所有的关系总结如下:
可执行是最终的产物,依赖于库文件和objs文件,库文件依赖objs文件,objs依赖include也就是DEPS要最先生成。
all:$(DEPS) $(OBJS) $(LIB) $(TARGET)
#这里可以什么都不用写
$(DEPS):$(FILES)
g++命令
-include $(DEPS) #这里建立了objs的依赖关系,是自动加载进makefile中的
$(LIB):$(OBJS)
g++命令
$(TARGET):$(OBJS) $(LIB)
g++命令
如此,当有一个cpp文件,或h头文件有修改时,则依赖它的所有文件都会重新编译生成,最终也就更新了可执行文件及库文件。
一个完整的makefile文件就大功告成了。