1. 本示例演示的是需链接动态库静态库,但不需编译库的makefile的编写方式(动态库是jsoncpp的使用,静态库是tinyxml的使用)
2. 目的是帮助那些新接触makefile的新手如何快速写出可用的makefile,下载本例后完全可以稍作修改就可以满足自己的需要
3. 本篇博客逐条语句分析了万能makefile的实现,尽可能多的添加了注释,也在一些地方进行了修改,以用于不同情况下makefile的编写
4. 所有示例都在centos上亲测编译,运行通过的,附上完整示例下载链接下载解压后,根目录中有介绍文档,请按照里面的步骤操作,保证可以运行成功
5. 如有任何疑问
以下是makefile的内容,如发现错误,欢迎拍砖
# 工程根目录
PROJECT_ROOT = /home/make_test/make2
# 最终生成的目标文件(可执行程序)的名字
EXECUTABLE := makefile_test_exe
# 库文件所在的目录
LIBDIR:= $(PROJECT_ROOT)/lib
# 库文件所在目录的扩展,不需扩展时不写内容即可
LIBDIR+=
# 所需库文件的名字(包括静态库和动态库,这里静态库为libtinyxml.a,动态库为libjson_linux-gcc-4.1.2_libmt.so)
# 注意名字:比如需要链接 libmysql.a 时只需写 mysql 即可
# 所以这里的tinyxml表示需要链接的库名字为 libtinyxml.a,json_linux-gcc-4.1.2_libmt表示需要链接的库名字为 libjson_linux-gcc-4.1.2_libmt.so
# 注意libjson_linux-gcc-4.1.2_libmt名字不要改,这个是在linux下默认编译出来的,改名字后会运行出错
LIBS := tinyxml json_linux-gcc-4.1.2_libmt
# 本工程所有的头文件所在的目录
INCLUDES:= $(PROJECT_ROOT)/include
# 所有的头文件所在目录的扩展,不需扩展时不写内容即可
INCLUDES +=
# 本工程所有源文件所在的目录
SRCDIR:= $(PROJECT_ROOT)/src
# 所有的源文件所在目录的扩展,不需扩展时不写内容即可
SRCDIR +=
# 生成的最终目标文件所在的目录,会作为伪目标,如果该路径不存在,则新建该目录
EXECUTEDIR := $(PROJECT_ROOT)
# 生成的最终目标文件的全名(带全路径的)
EXECUTABLE := $(EXECUTEDIR)/$(EXECUTABLE)
# 指定编译器,编译c++时,必须使用 g++
CC:=g++
# 编译参数,-g 表带上调试信息,这样可以调试程序 -Wall 表输出警告信息 -O1表示优化级别为1级
CFLAGS := -g -Wall -O1
CPPFLAGS := $(CFLAGS)
# 在所有的头文件目录前面加上 -I ,这是make包含头文件目录的语法要求
CPPFLAGS += $(addprefix -I,$(INCLUDES))
# 可以自动推导生成源文件对头文件的依赖关系
CPPFLAGS += -MMD
# 删除操作
RM-F := rm -f
# 函数介绍
# 函数 wildcard : 扩展通配符,比如下面的*符号在变量和函数中会失效,有了 wildcard 时,通配符就不会失效,比如可以使用 $(wildcard *.c) 来获取当前目录下的所有的.c文件名字列表
# 函数 addsuffix : 加后缀函数,示例:$(addsuffix .c,foo bar)返回值是“foo.c bar.c”
# 函数 addprefix : 加前缀函数,示例:$(addprefix lib, mysql.a ACE.so)返回值是 "libmysql.a libACE.so"
# 函数 patsubst : 模式字符串替换函数,$(patsubst %.c,%.o,x.c.c bar.c) 把字符串“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.o bar.o”
# 函数 filter-out : 反过滤函数,即过滤掉模式符合的文件后剩下的文件名字,示例 objects=main1.o foo.o main2.o bar.o
# : mains=main1.o main2.o
# : 则$(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”
# 以下内容不需修改
SRCS := $(wildcard *.cpp) $(wildcard $(addsuffix /*.cpp, $(SRCDIR))) # 表当前目录的所有.cpp文件名字列表和SRCDIR所有目录下的所有.cpp文件名字列表,即本工程的所有.cpp文件
OBJS := $(patsubst %.cpp,%.o,$(SRCS)) # 表上面所有.cpp编译产生的.o文件
DEPS := $(patsubst %.o,%.d,$(OBJS)) # 表上面所有.cpp编译产生的中间文件.d文件
MISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS)) # 不清楚,打印结果是所有的.d文件
MISSING_DEPS_SOURCES := $(wildcard $(patsubst %.d,%.cpp,$(MISSING_DEPS)))# 不清楚,打印结果是所有的.cpp文件
# 所有的伪目标
.PHONY : all deps objs clean veryclean rebuild info
# 最终的要生成的目标文件
all: $(EXECUTABLE)
# 生成所有的.d中间文件
deps : $(DEPS)
# 生成所有的.o中间文件
objs : $(OBJS)
# 一般清空操作,删除所有的.o 和 .d文件
clean :
@$(RM-F) $(OBJS)
@$(RM-F) $(DEPS)
# 彻底清空操作,注意其依赖于伪目标 clean,所以必定会先执行clean操作,再执行下面的命令(删除最终的目标文件)
veryclean: clean
@$(RM-F) $(EXECUTABLE)
# 重新生成,其依赖于伪目标veryclean 和 all ,即先执行veryclean操作,再执行all操作
rebuild: veryclean all
# 若MISSING_DEPS为空,则产生一个伪目标,删除所有的.o文件
ifneq ($(MISSING_DEPS),)
$(MISSING_DEPS) :
@$(RM-F) $(patsubst %.d,%.o,$@)
endif
# 将DEPS中的文件包含进来,"-"表示忽略文件不存在的错误
-include $(DEPS)
# 打印出所有的源文件,.o文件,.d文件,
info:
@echo $(SRCS)
@echo $(OBJS)
@echo $(DEPS)
@echo $(MISSING_DEPS)
@echo $(MISSING_DEPS_SOURCES)
# 下面是必定要执行的目标,依赖于所有的.o文件和伪目标EXECUTEDIR(生成存放最终目标的目录)
# 下面操作表示把所有的.o文件链接成可执行文件,-L指定链接库的路径,-l指定链接库的名字
# 注意:所有的.o文件并没有指明依赖于谁,也没有指明哪条命令生成它们
# 这里是make的隐含规则,对于每个.o文件,make会自动调用: $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@,来生成.o文件
# 之所以生成.d文件,是因为$(CPPFLAGS)含有-MMD,编译器会自动推导依赖信息,放在.d文件中
# 一个.cpp文件一定会生成一个.d文件和.o文件
$(EXECUTABLE) : $(OBJS) $(EXECUTEDIR)
$(CC) -o $(EXECUTABLE) $(OBJS) $(addprefix -L,$(LIBDIR)) $(addprefix -l,$(LIBS))
# 伪目标,用于生成存放最终目标的文件目录,-p 参数用于允许一次性创建多层次的目录
$(EXECUTEDIR) :
mkdir -p $(EXECUTEDIR)
标签:文件,%.,深入浅出,makefile,DEPS,cpp,第二篇,所有 From: https://blog.51cto.com/u_15912066/5936283