Makefile学习笔记系列3:具有子目录层次结构的makefile写法

在系列1和系列2的基础上再次对目录的结构及makefile进行改进。

(1)首先还是介绍下目录结构

(2)和系列2相比较,在主目录下多了lib这个子目录,lib是用来存放其他三个模块(receiver, responser, sender)编译出来的.a静态库。include目录下存放公共的头文件。

三个子模块目录的结构如下:(以receiver目录为例)

(3)接下来先来看下主目录下的makefile

 

XX = g++
AR = ar
ARFLAG = -rcs
CFLAGS = -g 

CLIBS = -L./lib/ -lSender -lReceiver -lResponser  -lpthread

SUBDIRS = ./receiver ./responser ./sender

INCLUDES = $(wildcard ./include/*.h ./sender/*.h ./receiver/*.h ./responser/*.h) # INCLUDE = a.h b.h ... can't be defined like "INCLUDE = ./*.h"
#SOURCES = $(wildcard ./*.cpp ./sender/*.cpp ./receiver/*.cpp ./responser/*.cpp)
INCLUDE_DIRS = -I./include -I./sender/ -I./receiver/ -I./responser/ #指定头文件目录,代码中就不需要把头文件的完整路径写出来了

TARGET = mainApp
#OBJECTS = $(patsubst %.cpp,%.o,$(SOURCES))
OBJECTS = main.o

export XX CFLAGS AR ARFLAG

$(TARGET) : $(OBJECTS) $(SUBDIRS)
	$(XX) $< -o $@ $(CLIBS)     # $< 表示依赖列表的第一个 也就是 $(OBJECTS)
	
$(OBJECTS) : %.o : %.cpp 
	$(XX) -c $(CFLAGS) $< -o $@ $(INCLUDE_DIRS)
	
$(SUBDIRS):ECHO
	+$(MAKE) -C $@

ECHO:
	@echo $(SUBDIRS)
	@echo begin compile

.PHONY : clean
clean:
	for dir in $(SUBDIRS);\
	do $(MAKE) -C $$dir clean||exit 1;\
	done
	rm -rf $(TARGET) $(OBJECTS)  ./lib/*.a

主Makefile分析:

主要以下几个重点:

<1>  AR是编译.a静态库工具,  ARFLAG 为编译静态库参数

<2>  CLIBS 指定了.o文件链接为target所需要加载的库, -L 指定库的目录,  -lSender表示链接的库名称为 libSender.a

<3>  SUBDIRS 指定了编译main.cpp需要关联的子目录

<4>  export操作将主makefile中定义的 几个变量导出,这样在子目录的makefile中就可以用这些变量了。

<5>  注意这句:

$(TARGET) : $(OBJECTS) $(SUBDIRS)

 

TARGET的生成除了需要依赖OBJECTS指定的.o文件外还需要依赖$(SUBDIRS)。再往下看

 

$(SUBDIRS):ECHO
	+$(MAKE) -C $@

在SUBDIRS的依赖关系后面会执行$(MAKE)命令,会执行每个子目录下的makefile。这样就可以保证生成TARGET之前先执行子目录的makefile产生其所需要的静态库。

 

ECHO:仅仅是打印log。

<6> clean虚目标里面多添加了一个for循环来调用子目录的makefile的make clean命令,清除子目录下产生的目标文件。

<7> 先来看下执行完make以后Lib目录下会产生的静态库文件如下图:

(4)主makefile分析完毕,现在来分析子makefile, 以receiver目录下的为例:

receiver目录下的makefile代码如下:

 

LIB_DIR = ./../lib/
TOP_DIR = ./..

SOURCES = $(wildcard ./*.cpp)
INCLUDE_DIRS = -I$(TOP_DIR)/include  -I$(TOP_DIR)/responser/ -I./ 

TARGET = libReceiver.a
OBJECTS = $(patsubst %.cpp,%.o,$(SOURCES))

$(TARGET) : $(OBJECTS)
	$(AR) $(ARFLAG) $@ $^
	cp $@ $(LIB_DIR)
	
$(OBJECTS) : %.o : %.cpp 
	$(XX) -c $(CFLAGS) $< -o $@ $(INCLUDE_DIRS)

.PHONY : clean
clean:
	rm -rf $(TARGET) $(OBJECTS)


说明:

 

<1>  TOP_DIR 指定顶层目录, LIB_DIR指定静态库文件位置,INCLUDE_DIRS指定了receiver.cpp需要Include的头文件。

<2>  TARGET 指定生成为一个静态链接库文件。 命名规则必须为  libXXX.a的形式。

<3> 

$(TARGET) : $(OBJECTS)
	$(AR) $(ARFLAG) $@ $^
	cp $@ $(LIB_DIR)

 

这段代码生成静态库文件,并将静态库文件拷贝到 顶层目录的Lib目录下。

其他没什么说明了,就这三点重点。

编译完会生成的文件如下图:

 

其他目录下的子makefile类似。

 

总结:

这种Makefile的写法主要是编译子模块文件并生成静态链接库,主makefile编译并链接静态库生成可执行程序。

这种写法每个子目录都需要编写自己的makefile, 貌似有点麻烦。是不是可以写一个makefile的模版然后子目录的makefile只要include这个模版并写几条简单的语句就可以了。

接下来序列4和序列5将实现这个目标。

 

谢谢观看,作者水平有限,有错误麻烦指正。

代码github地址:https://github.com/fanchenxinok/Makefile_models

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值