在上节我们完成了通过make编译多个项目,但是我们发现,两个项目里其实有非常多的重复部分,并且每个项目的makefile都非常长。
于是想到能不能把公共部分都提取出来,自定义一个makefile配置
于是本章节通过在项目中包含一个公共的makefile来实现makefile的简化
(一)成果预览
首先看看简化后的Test_s_lib的makefile
ifeq ($(STATIC),1)
LIBTYPE=.a
else
LIBTYPE=.so
endif
include ../makefile.mk
这是简化后的Test_d_lib的makefile
LIBTYPE=.so
include ../makefile.mk
是的一共就这么几行!!!比起原来的少了非常多
(二)编写配置用的makefile
(1)提取TARGET
再每个makefile项目里都需要目标项和目标依赖项,而且基本每个目标和缪表依赖项都和目录名一致,除了多了后缀名,于是我们可以将其提取出来
通过ifndef来判断是否已经确定目标
ifndef TARGET
TARGET:=$(notdir $(shell pwd)) #test_include
endif
在上一章节中提到过可以通过$(shell pwd)获取到当前路径
这里再引入一个新的makefile函数 :notdir
1、notdir函数:
作用是从文件路径中提取文件名部分,去掉路径部分。
当有一个文件路径/home/user/documents/file.txt,
使用notdir函数后,$(notir /home/user/documents/file.txt) 返回的结果是file.txt
(2)提取编译器环境变量
变量的意思已经再第二章节解释过,这就不在解释一遍啦
CXXFLAGS:=$(CXXFLAGS) -I../../include -std=c++17
LDFLAGS:=$(LDFLAGS)
LDLIBS:=$(LDLIBS) -lpthread
-std=c++17是告诉编译器要使用 C++ 17 标准来编译代码。
这里只是引入原项目中的变量,后面还会对变量进一步设置
(3)提取目标依赖项
SRCS:=$(wildcard *.cpp *.cc *.c)
OBJS:=$(patsubst %.cpp,%.o,$(SRCS))
OBJS:=$(patsubst %.cc,%.o,$(OBJS))
OBJS:=$(patsubst %.c,%.o,$(OBJS))
1、wildcard 函数:
用于获取符合特定模式的文件名列表。它的主要作用是在构建过程中自动发现源文件或其他相关文件,从而方便地管理文件依赖关系。
$(wildcard PATTERN),其中PATTERN是一个包含通配符的文件名模式。通配符通常有*(匹配零个或多个任意字符)例如,$(wildcard *.c)会返回当前目录下所有以.c结尾的文件列表。
当Makefile中的文件列表依赖于wildcard函数获取的结果时,添加或删除符合模式的文件后,make命令会自动重新构建相关的目标。这对于管理项目中的文件更新非常方便,
2、patsubst函数:
在 Makefile 中,patsubst是一个文本处理函数,用于模式替换。它的主要作用是将文本中的一种模式替换为另一种模式。
语法格式为:$(patsubst <pattern>,<replacement>,<text>)。
其中<pattern>是要匹配的模式,<replacement>是替换后的内容,<text>是要进行处理的原始文本,可以是变量或者字符串列表。
即我们这里第一个patsubst是把SRCS里的.cpp文件替换成对应的.o文件
后面的也都一样
(4)区分动态静态库
ifeq ($(LIBTYPE),.so)
TARGET:=lib$(strip $(TARGET)).so
LDLIBS:=$(LDLIBS) -shared
CXXFLAGS:=$(CXXFLAGS) -fPIC
endif
ifeq ($(LIBTYPE),.a)
TARGET:=lib$(strip $(TARGET)).a
endif
变量LIBTYPE是在原项目中设定变量,用于确定要编译为什么库
ifeq的功能是对比两个字符是否相等,在上一章节也已经提到过
当传进来的LIBTYPE是.so,说明要编译的是动态库
动态库和静态库用到的变量和参数也都已经在上一章节讲解过,这里说下用到的新函数strip
1、strip函数
它的主要作用是去除字符串开头和结尾的空格(包括空格、制表符等空白字符)。这在处理文件路径、变量值等可能包含多余空白字符的字符串时非常有用。
(5)目标生成
$(TARGET):$(OBJS)
ifeq ($(LIBTYPE),.a)
$(AR) -cvr $@ $^
else
$(CXX) $(LDFLAGS) $^ -o $@ $(LDLIBS)
endif
这里也通过LIBTYPE来决定是生成动态库路还是静态库
变量和参数也都在上一章节提到过啦
(6)完整配置makefile
最后的clean和伪目标的设置也已经用过很多次,就不再提了
ifndef TARGET
TARGET:=$(notdir $(shell pwd)) #test_include
endif
CXXFLAGS:=$(CXXFLAGS) -I../../include -std=c++17
LDFLAGS:=$(LDFLAGS)
LDLIBS:=$(LDLIBS) -lpthread
SRCS:=$(wildcard *.cpp *.cc *.c)
OBJS:=$(patsubst %.cpp,%.o,$(SRCS))
OBJS:=$(patsubst %.cc,%.o,$(OBJS))
OBJS:=$(patsubst %.c,%.o,$(OBJS))
ifeq ($(LIBTYPE),.so)
TARGET:=lib$(strip $(TARGET)).so
LDLIBS:=$(LDLIBS) -shared
CXXFLAGS:=$(CXXFLAGS) -fPIC
endif
ifeq ($(LIBTYPE),.a)
TARGET:=lib$(strip $(TARGET)).a
endif
$(TARGET):$(OBJS)
ifeq ($(LIBTYPE),.a)
$(AR) -cvr $@ $^
else
$(CXX) $(LDFLAGS) $^ -o $@ $(LDLIBS)
endif
#rm -r test.o test
clean:
$(RM) $(OBJS) $(TARGET)
.PHONY: clean
(三)使用配置的makefile
把上一章节Test_s_lib的makefile和Test_d_lib的makefile都换成这章的makefile
ifeq ($(STATIC),1)
LIBTYPE=.a
else
LIBTYPE=.so
endif
include ../makefile.mk
LIBTYPE=.so
include ../makefile.mk
然后
把makefile.mk放在外面那个目录
再执行上章节写好的make就可以直接编译啦