多文件Makefile编写

工作过程中,平时不怎么关注Makefile的书写规则,对于遇到的编译错误一般能看懂Makefile的基本规则也能解决。但如果想要编写Makefile文件还是有相当的难度的,更不用说包含多个目录和文件的功程了。于是在调试了一下午的情况下,终于把一个包含多个目录的基本Makefile给实现了,特此记录下来。 其实编写这个Makefile的目的很简单:便于集中分类管理平时的小程序。 (文章不是用来描述Makefile规则)

1. Makefile多目录结构

在windows上可以使用tree命令查看目录的树状信息(下图是在Windows下通过tree /f目录查看的)
在这里插入图片描述
简单的说明:
该工程目前包含有四个源代码目录和一个编译目录target,编写该功能的目的是为了方便对平时编写的小程序的管理和测试。

目录名说明
main存放main函数的C文件,用来测试运行其他接口程序
process进程和线程相关
scheduler任务调度代码实现
sleep延时函数实现:select, pselect等实现
target存放编译过程中生成的.o文件,因此不包含makefile

2. 多目录Makefile编写思路

  • 首先编译各个子目录的文件,生成一系列目标文件(.o文件)
  • 然后将各个子目录编译生成的目标文件链接为最终的可执行文件

3. 工程根目录下的总控Makefile

#Makefile
#author : sunzd
#date  :  2020-02-23
#location: Beijing

CC = gcc
MAKE = make
CFLAGS = -g -W -O2
LFLAGS = 

#directory
TOP_DIR := $(PWD)

OBJ_DIR := $(TOP_DIR)/target

BIN_DIR := $(TOP_DIR)

SRC_SCHEDULE_DIR := $(TOP_DIR)/scheduler
SRC_SLEEP_DIR    := $(TOP_DIR)/sleep
SRC_PROCESS_DIR  := $(TOP_DIR)/process
SRC_MAIN_DIR     := $(TOP_DIR)/main
#这里指定要编译的子目录信息
SUB_DIR := $(SRC_SCHEDULE_DIR) $(SRC_SCHEDULE_DIR) $(SRC_SLEEP_DIR) $(SRC_MAIN_DIR)
 
TARGET := demo.out

#方法一:手动指定所有的目标文件 
#OBJS ?= $(OBJ_DIR)/scheduler.o 
#OBJS +=	$(OBJ_DIR)/scheduler_demo.o 
#OBJS +=	$(OBJ_DIR)/mySleep.o 
#OBJS +=	$(OBJ_DIR)/main.o

#方法二:自动获取指定目录下的目标文件 
#OBJS  = $(shell ls $(OBJ_DIR)/*.o)  works
#OBJS := $(shell ls $(OBJ_DIR)/*.o)  can't work
#OBJS ?= $(OBJ_DIR)/$(wildcard *.o)  can't work
OBJS  ?= $(shell ls $(OBJ_DIR)/*.o)

#将重要的变量export,供子目录中的Makefile使用
export CC CFLAGS TOP_DIR OBJ_DIR BIN_DIR SRC_PROCESS_DIR SRC_SCHEDULE_DIR SRC_SLEEP_DIR OBJS \
		SRC_MAIN_DIR
 
all : $(SUB_DIR) $(TARGET)

#依次进入各个子目录进行编译
$(SUB_DIR):ECHO
	$(MAKE) -C $@

ECHO:
	@echo sub directorys: $(SUB_DIR) 
	@echo begin compile::::

#最终要生成的目标文件规则	
$(TARGET):$(OBJS)	
	#@echo ======== OBJS: $(OBJS) =======
	$(CC) $(LFLAGS) $(CFLAGS) $(OBJS) -o $@

.PHNOY: clean
clean:
	rm  -rf $(OBJ_DIR)/*.o  $(TARGET)

说明

变量定义方式说明
=延时变量
?=延时变量
:=立即变量
延时变量在定义时不计算变量的值,在用到时再计算变量的值
立即变量在定义时便计算出变量的值

这里由于不想通过手动指定所有的.o文件,而这些文件只有在子目录完全编译成功后会生成,因此在定义时不能采用立即变量,必须采用延时变量,等到最后编译生成可执行文件时在计算获取target目录下的所有.o文件。主要是为了方便后续扩展。

4. 子目录下的Makefile

工程中四个子目录的Makefile文件都一样(确实很方便),如果后续需要提前新的功能创建新的目录,如果没有特殊需求,可以直接将Makefile拿来使用。

#获取当前目录下所有的.c文件
src := $(shell ls *.c)

#获取要编译的目标文件(.o文件): 使用patsubst函数做后缀替换实现
objs := $(patsubst %.c,%.o,$(src))

#OBJS += $(OBJ_DIR)/$(objs)

#目标: 1)编译.o文件  2)将.o文件移动到指定目录
all : $(objs) MOVE
 
#1)编译.o文件
%.o:%.c
	$(CC) -c $^ -o $@
	
#2)将.o文件移动到指定目录
MOVE:
	mv *.o $(OBJ_DIR)
 

4. 疑问

在Makefile规则的最后,一般是链接各个目录的目标文件生成最终的可执行文件。可以想到的方式有三种:

  • 手动在中控Makefile中指定所有的.o文件(可能还有链接库,头文件等)。这种方式就是不方便扩展,每次增删文件需要修改总控的Makefile
  • 将所有的目标文件放到一个特定的目录,最后读取对应的目录文件完成最后的链接操作。缺点很明显:对于需要链接特殊库和搜索指定路径的程序没有办法实现,还需要手动指定。
  • 在总控Makefile中预先定义变量,每一个子目录的变量将各自的目标文件、链接文件、搜索路径添加到特定的全局变量中,最终由总控Makefile在生成最后的可执行文件时引用相应的全局变量即可。

在尝试使用第三种方式时,子目录虽然成功修改了全局变量OBJS,但是在总控的Makefile中,依然为空,尚不清楚哪个原因。留着后续处理

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叨陪鲤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值