最近在做boost相关的服务端开发,感觉程序多了之后文件夹里面很乱,就想着把他们分在不同的文件夹里面,这样看起来也很舒服,而且也不会很low,毕竟要上传到github上保存版本。给别人看也方便许多。
多目录的Makefile网上有许多教程大多类似,但是实际上应用的时候还是遇见了许多问题。
首先就是从src目录里面搜寻cpp文件,在include目录下面放头文件。找到相应objs目录下的.o文件,最后编译出来我们的主程序下面上我的代码。
CXX = g++
#src obj dep 文件的查找/生成路径
SRC_DIR = src
OBJ_DIR = objs
DEPS_DIR = deps
#对cpp文件 头文件进行查找
SRC = $(wildcard $(SRC_DIR)/*.cpp)
LIBS = -lpthread -lboost_system
OBJS = $(addprefix $(OBJ_DIR)/,$(patsubst %.cpp,%.o,$(notdir $(SRC))))
DEPS = $(addprefix $(DEPS_DIR)/,$(patsubst %.cpp,%.d,$(notdir $(SRC))))
#MySql编译时需要的依赖
SQL_DEPENDS = `mysql_config --cflags --libs`
BOOST_LIB_DIR = /home/mjf/lib/lib
BOOST_INCLUDE_DIR = /home/mjf/lib/include
HEADER_DIR = -I ./include -I/usr/include/mysql -I$(BOOST_INCLUDE_DIR)
CXXFLAG = $(HEADER_DIR) -o2 -g -Wall -std=c++11
#目标
TARGET = myserver
$(TARGET):$(OBJS)
$(CXX) -g $^ $(SQL_DEPENDS) -o $(TARGET) $(LIBS) -L$(BOOST_LIB_DIR)
#@在makefile里面表达的是解析shell命令 fi表示结束吧和if搭配?
$(OBJ_DIR)/%.o:$(SRC_DIR)/%.cpp
@if [ ! -d $(OBJ_DIR) ]; then mkdir -p $(OBJ_DIR); fi; \
$(CXX) -c $(HEADER_DIR) $(SQL_DEPENDS) $(LIBS) -o $@ $<
$(DEPS_DIR)/%.d:$(SRC_DIR)/%.cpp
@if [ ! -d $(DEFS_DIR) ]; then mkdir -p $(DEFS_DIR); fi; \
set -e;rm -f $@;\
$(CXX) -MM $(CXXFLAG) $< > $@.$$$$ ;\
sed 's,\($(notdir$*)\)\.o[ :]*,$(OBJ_DIR$*)\1.o$@ : ,g' < $@.$$$$ > $@;\
rm -f $@.$$$$
-include $(DEPS)
$(OBJS):
.PHONY:clean
clean:
rm -f $(TARGET)
rm -f $(OBJ_DIR)/*
下面就对上面的这段makefile解释一下吧
- addprefix指令是在后一项的前面加上前缀,比如说 addprefix(he,llo.h) 其实就等于 hello.h
- notdir把展开的文件的路径去掉,只显示文件名而不包含其路径信息
- patsubst进行替换的指令,替换的内容就是将.cpp文件替换成相应相应的.d文件,当然这个.cpp文件的替换范围是在 SRC里面规定的。
- wildcard是进行通配符匹配的函数,因为在makefile里面通配符都被用了赋予了其他的含义,所以要在makefile里面使用通配符匹配的话就需要使用到wildcard 搜索 SRC_DIR/下所有cpp文件
SRC = $(wildcard $(SRC_DIR)/*.cpp)
LIBS = -lpthread -lboost_system
OBJS = $(addprefix $(OBJ_DIR)/,$(patsubst %.cpp,%.o,$(notdir $(SRC))))
DEPS = $(addprefix $(DEPS_DIR)/,$(patsubst %.cpp,%.d,$(notdir $(SRC))))
下面的编译内容以及指令
首先我们得知道为什么需要编译这个.d文件,这个文件是做什么的。
根据GNU Make项目管理第二章规则的内容来看。我们可以使用makefile自动产生依存关系的功能,gcc -M指令会帮助我们查看某一个文件的依存有哪些,但是手动添加会给我们带来麻烦,我们为每一个文件生成它的依存关系,并且把它放入相应的d文件之中。
所以为它创建一个工作目标,这样make就会知道cpp文件改变了,这时候就要更新.d文件。那么下面的的代码怎么解释?
首先就是 在创建.d文件之前使用shell指令检查一下要生成的文件存不存在。@表示提醒make后面的指令是shell的
下面就是需要生成一个临时文件,这个文件就是
$@.$$$$,
$$$$会返回当前的运行的shell的进程号,就是说明这个文件不会与其他的重复,没什么具体的意义,保证不会覆盖而已。
-MM就是查找依存项
下面使用sed表达式进行搜索搜索部分是\($(notdir$*)*\) 其中 \(\代表正则表达式的分组。.o[ :]*表示工作目标文件之后可能会有一个或者多个空格或者冒号。后面是要替换的.o文件放在OBJ_DIR目录下面,最后查找到所有的依赖之后就将它保存在$@之中,最后删掉$@.$$$$
$(DEPS_DIR)/%.d:$(SRC_DIR)/%.cpp
@if [ ! -d $(DEFS_DIR) ]; then mkdir -p $(DEFS_DIR); fi; \
set -e;rm -f $@;\
$(CXX) -MM $(CXXFLAG) $< > $@.$$$$ ;\
sed 's,\($(notdir$*)\)\.o[ :]*,$(OBJ_DIR$*)\1.o$@ : ,g' < $@.$$$$ > $@;\
rm -f $@.$$$$
-include $(DEPS)
遇到问题:之前遇到过.o文件生成到根目录下去了,后来发现是我的变量$(OBJ_DIR )后面有个空格就直接变成了 /%.o
还有出现过更新头文件之后make没有反应,查了一波发现.d文件也没有更新。所以最后在sed那里都加上了路径避免了问
我贴一下d文件里面大致有什么东西好了,就是这种类似的路径和依赖
service_main.o: src/service_main.cpp include/server_main.h \
/home/mjf/lib/include/boost/asio.hpp \
/home/mjf/lib/include/boost/asio/async_result.hpp \
/home/mjf/lib/include/boost/asio/detail/config.hpp \
/home/mjf/lib/include/boost/config.hpp \
/home/mjf/lib/include/boost/config/user.hpp \