上层 Makefile 控制下层 Makefile 的方法
在复杂的项目中,通常会将项目划分为多个模块或子项目,每个模块都有自己的 Makefile
。上层 Makefile
的作用是协调和控制这些下层 Makefile
的构建过程。下面是几种常见的示例,实现上层 Makefile
对下层 Makefile
的控制。
-
直接调用:通过
$(MAKE)
或make
命令直接调用下层Makefile
。 -
传递变量:通过环境变量或命令行参数将变量传递给下层
Makefile
。 -
递归构建:通过递归调用
make
命令构建所有模块。 -
导入规则:通过
include
指令导入下层Makefile
的规则和变量。
目录
一个实际例子
1.工程目录结构
2.主目录Makefile内容
3.Service目录的Makefile
4.Client目录的Makefile
方法 1:直接调用下层 Makefile
示例项目结构
上层 Makefile 示例
下层 Makefile 示例(module1/Makefile)
使用说明
方法 2:传递变量给下层 Makefile
上层 Makefile 示例
下层 Makefile 示例(module1/Makefile)
使用说明
方法 3:使用递归构建
上层 Makefile 示例
下层 Makefile 示例(module1/Makefile)
使用说明
方法 4:使用 include 导入下层 Makefile
上层 Makefile 示例
下层 Makefile 示例(module1/Makefile)
使用说明
总结
一个实际例子
1.工程目录结构
├── ChatRoom
│ ├── Client
│ │ ├── Client.c
│ │ ├── Makefile
│ │ ├── Tcp_Sock.c
│ │ ├── Tcp_Sock.h
│ │ ├── main
│ │ └── obj
│ └── Service
│ ├── Makefile
│ ├── Service.c
│ ├── Tcp_Sock.c
│ ├── Tcp_Sock.h
│ ├── main
│ └── obj
├── Makefile
2.主目录Makefile内容
简单版本:
SUBDIR1 = ChatRoom/Service
SUBDIR2 = ChatRoom/Client
SUBDIRS = $(SUBDIR1) $(SUBDIR2)
.PHONY: all clean $(SUBDIRS)
all: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $@
clean:
for dir in $(SUBDIRS); \
do \
$(MAKE) -C $$dir clean; \
done
# 定义模块路径
##################################
########用户配置区#################
MODULE1 = ChatRoom/Client
MODULE2 = ChatRoom/Service
MODULE1_TARGET = $(MODULE1)/main
MODULE2_TARGET = $(MODULE2)/main
MODULES = $(MODULE1) $(MODULE2)
MODULES_TARGET = $(MODULE1_TARGET) $(MODULE2_TARGET)
##################################
# 默认目标
all: $(MODULES_TARGET)
# 构建模块
$(MODULE1_TARGET): $(MODULE1)
@echo "正在编译模块:$(MODULE1)"
$(MAKE) -C $(MODULE1) all
$(MODULE2_TARGET): $(MODULE2)
@echo "正在编译模块:$(MODULE2)"
$(MAKE) -C $(MODULE2) all
# 清理所有模块
clean:
$(MAKE) -C $(MODULE1) clean
$(MAKE) -C $(MODULE2) clean
rm -f $(MODULE1_TARGET) $(MODULE2_TARGET)
# 帮助信息
help:
@echo "可用命令:"
@echo " make 编译所有模块"
@echo " make clean 清理所有模块"
@echo " make help 查看帮助信息"
3.Service目录的Makefile
CC = gcc
SCRC = $(wildcard *.c)
OBJS_DIR = ./obj
OBJ = $(SCRC:%.c=$(OBJS_DIR)/%.o)
CFLAGS = -Wall -O2
LDFLAGS = -lpthread
TARGET = main
# 默认目标
all: $(TARGET)
$(TARGET):$(OBJ)
$(CC) $^ -o $@ $(LDFLAGS)
$(OBJS_DIR)/%o:%c
@mkdir -p $(OBJS_DIR)
$(CC) $< -o $@ $(CFLAGS) -c
echo:
@echo $(OBJ)
clean:
rm -rf ./obj
.PHONY:clean
.PHONY:echo
4.Client目录的Makefile
CC = gcc
SCRC = $(wildcard *.c)
OBJS_DIR = ./obj
OBJ = $(SCRC:%.c=$(OBJS_DIR)/%.o)
CFLAGS = -Wall -O2
LDFLAGS = -lpthread
TARGET = main
# 默认目标
all: $(TARGET)
$(TARGET):$(OBJ)
$(CC) $^ -o $@ $(LDFLAGS)
$(OBJS_DIR)/%o:%c
@mkdir -p $(OBJS_DIR)
$(CC) $< -o $@ $(CFLAGS) -c
echo:
@echo $(OBJ)
clean:
rm -rf ./obj
.PHONY:clean
.PHONY:echo
方法 1:直接调用下层 Makefile
上层 Makefile
可以通过 $(MAKE)
或 make
命令直接调用下层 Makefile
。这种方法简单且灵活。
示例项目结构
project/
├── Makefile # 上层 Makefile
├── module1/
│ ├── Makefile # 下层 Makefile
│ └── *.c
├── module2/
│ ├── Makefile # 下层 Makefile
│ └── *.c
└── main.c
上层 Makefile
示例
# 定义下层模块的路径
MODULE1 = module1
MODULE2 = module2
# 定义所有模块
MODULES = $(MODULE1) $(MODULE2)
# 默认目标
all: $(MODULES)
# 构建模块
$(MODULES):
$(MAKE) -C $@
# 清理所有模块
clean:
$(MAKE) -C $(MODULE1) clean
$(MAKE) -C $(MODULE2) clean
rm -f main
# 帮助信息
help:
@echo "可用命令:"
@echo " make 编译所有模块"
@echo " make clean 清理所有模块"
@echo " make help 查看帮助信息"
下层 Makefile
示例(module1/Makefile
)
# 定义编译器
CC = gcc
# 定义目标文件
TARGET = module1.o
# 定义源文件
SRC = module1.c
# 编译选项
CFLAGS = -g -Wall
# 默认目标
all: $(TARGET)
# 生成目标文件
$(TARGET):
$(CC) $(CFLAGS) -c $(SRC) -o $(TARGET)
# 清理
clean:
rm -f $(TARGET)
使用说明
-
编译所有模块
上层 Makefile
会依次调用 module1/Makefile
和 module2/Makefile
进行编译。
make
-
清理所有模块
make clean
上层
Makefile
会调用每个模块的clean
目标,并清理上层生成的文件。 -
查看帮助
make help
方法 2:传递变量给下层 Makefile
上层 Makefile
可以通过环境变量或命令行参数将变量传递给下层 Makefile
。
上层 Makefile
示例
# 定义构建类型
BUILD_TYPE ?= Debug
# 定义下层模块的路径
MODULE1 = module1
MODULE2 = module2
# 定义所有模块
MODULES = $(MODULE1) $(MODULE2)
# 默认目标
all: $(MODULES)
# 构建模块
$(MODULES):
$(MAKE) -C $@ BUILD_TYPE=$(BUILD_TYPE)
# 清理所有模块
clean:
$(MAKE) -C $(MODULE1) clean
$(MAKE) -C $(MODULE2) clean
rm -f main
# 帮助信息
help:
@echo "可用命令:"
@echo " make 编译所有模块"
@echo " make clean 清理所有模块"
@echo " make help 查看帮助信息"
下层 Makefile
示例(module1/Makefile
)
# 定义编译器
CC = gcc
# 定义目标文件
TARGET = module1.o
# 定义源文件
SRC = module1.c
# 从环境变量中获取构建类型
ifeq ($(BUILD_TYPE), Debug)
CFLAGS = -g -Wall
else
CFLAGS = -O2
endif
# 默认目标
all: $(TARGET)
# 生成目标文件
$(TARGET):
$(CC) $(CFLAGS) -c $(SRC) -o $(TARGET)
# 清理
clean:
rm -f $(TARGET)
使用说明
-
编译所有模块(Debug 模式)
make
-
编译所有模块(Release 模式)
make BUILD_TYPE=Release
-
清理所有模块
make clean
方法 3:使用递归构建
上层 Makefile
可以通过递归调用 make
命令来构建所有下层模块。
上层 Makefile
示例
# 定义模块路径
MODULES = module1 module2
# 默认目标
all: $(MODULES)
# 构建模块
$(MODULES):
@echo "构建模块: $@"
$(MAKE) -C $@
# 清理
clean:
@echo "清理模块..."
$(MAKE) -C module1 clean
$(MAKE) -C module2 clean
rm -f main
# 帮助信息
help:
@echo "可用命令:"
@echo " make 编译所有模块"
@echo " make clean 清理所有模块"
@echo " make help 查看帮助信息"
下层 Makefile
示例(module1/Makefile
)
# 定义编译器
CC = gcc
# 定义目标文件
TARGET = module1.o
# 定义源文件
SRC = module1.c
# 编译选项
CFLAGS = -g -Wall
# 默认目标
all: $(TARGET)
# 生成目标文件
$(TARGET):
$(CC) $(CFLAGS) -c $(SRC) -o $(TARGET)
# 清理
clean:
rm -f $(TARGET)
使用说明
-
编译所有模块
make
-
清理所有模块
make clean
方法 4:使用 include
导入下层 Makefile
上层 Makefile
可以通过 include
指令导入下层 Makefile
的规则和变量。
上层 Makefile
示例
# 定义模块路径
MODULES = module1 module2
# 导入下层 Makefile 的变量和规则
include $(MODULES:%=%.mk)
# 默认目标
all: $(MODULES:%=build_%)
# 构建模块
build_%:
$(MAKE) -C $* all
# 清理
clean:
$(MAKE) -C module1 clean
$(MAKE) -C module2 clean
rm -f main
# 帮助信息
help:
@echo "可用命令:"
@echo " make 编译所有模块"
@echo " make clean 清理所有模块"
@echo " make help 查看帮助信息"
下层 Makefile
示例(module1/Makefile
)
# 定义编译器
CC = gcc
# 定义目标文件
TARGET = module1.o
# 定义源文件
SRC = module1.c
# 编译选项
CFLAGS = -g -Wall
# 默认目标
all: $(TARGET)
# 生成目标文件
$(TARGET):
$(CC) $(CFLAGS) -c $(SRC) -o $(TARGET)
# 清理
clean:
rm -f $(TARGET)
使用说明
-
编译所有模块
make
-
清理所有模块
make clean
总结
通过上述方法,上层 Makefile
可以灵活地控制下层 Makefile
的构建过程。以下是关键点的总结:
- 直接调用:通过
$(MAKE)
或make
命令直接调用下层Makefile
。 - 传递变量:通过环境变量或命令行参数将变量传递给下层
Makefile
。 - 递归构建:通过递归调用
make
命令构建所有模块。 - 导入规则:通过
include
指令导入下层Makefile
的规则和变量。