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