工作过程中,平时不怎么关注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. Linux防火墙
  2. iOS工作笔记(十四)
  3. winform之权限判断小技巧
  4. Java基础-被final修饰的引用变量的指向
  5. 华为OJ平台——查找组成一个偶数最接近的两个素数
  6. Hadoop-eclipse-plugin插件安装
  7. SqlParameter的用法和好处
  8. .Net Core小技巧 - Swagger适配虚拟目录及二级目录
  9. nexus3.14.0版本linux环境安装、启动、搭建私库
  10. PHP中逻辑运算符的高效用法---&&和||
  11. linode上切换Linux到FreeBSD
  12. Angular 学习笔记 (version 6 小笔记)
  13. WPF 透明窗体
  14. P3084 [USACO13OPEN]照片Photo
  15. bzoj千题计划207:bzoj1879: [Sdoi2009]Bill的挑战
  16. arcgis api for flex 开发入门(一)环境搭建
  17. October 06th 2017 Week 40th Friday
  18. tomcat之组成结构
  19. 成为JavaGC专家(3)—如何监控Java垃圾回收机制(转载)
  20. 前端工程师(JavaScript)在业余时间如何提高自身能力

热门文章

  1. Tensorflow2对GPU内存的分配策略
  2. ssrf漏洞随笔
  3. 【笔记】matplotilb数据可视化基础
  4. 把对象交给spring管理的3种方法及经典应用
  5. Abp vNext 基础篇丨分层架构
  6. 【MySQL】自定义数据库连接池和开源数据库连接池的使用
  7. Docker++:docker运行Tomcat后访问首页报404 (永久解决方式)
  8. SpringBoot集成websocket(java注解方式)
  9. 安装 iperf和服务器之间测速
  10. 8、二进制安装K8s之部署CIN网络