通用 Makefile 模板

一个简单的Makefile模版

#=============================================================================
# 示例项目结构:
# my_project/
# ├── Makefile
# ├── src/
# │   ├── main.c
# │   └── module1.cpp
# └── include/
#     ├── my_header.h
#     └── module1.h
# └── lib/ (可选)
#     └── my_library.a
#=============================================================================

#=============================================================================
# Makefile 模板: 适用于 C/C++ 项目的通用构建系统
# ----------------------------------------------------------------------------
# 用户需要修改的变量:
#   PROJECT_NAME        : 最终生成的可执行文件或库的名称
#   SRC_DIRS            : 包含源代码的目录列表 (相对路径, 空格分隔)
#   INC_DIRS            : 包含头文件的目录列表 (相对路径, 空格分隔)
#   LIB_DIRS            : 链接库的目录列表 (相对路径, 空格分隔)
#   LIBS                : 需要链接的库列表 (如 -lpthread -lrt 等, 空格分隔)
#   CC                  : C 编译器 (默认为 gcc)
#   CXX                 : C++ 编译器 (默认为 g++)
#   CFLAGS              : C 编译选项 (如 -Wall -Wextra -g)
#   CXXFLAGS            : C++ 编译选项 (如 -Wall -Wextra -g -std=c++17)
#   LDFLAGS             : 链接选项 (如 -static)
#   BUILD_DIR           : 编译中间文件 (.o, .d) 存放目录 (默认为 build)
#=============================================================================

# --- 用户可配置变量 ---
PROJECT_NAME    := my_project
SRC_DIRS        := src include
INC_DIRS        := include
LIB_DIRS        := lib
LIBS            := -lm -lpthread

CC              := gcc
CXX             := g++
CFLAGS          := -Wall -Wextra -O2 -MMD
CXXFLAGS        := -Wall -Wextra -O2 -std=c++17 -MMD
LDFLAGS         :=

BUILD_DIR       := build

# --- 不建议用户修改的内部变量 ---
VPATH           := $(SRC_DIRS) $(INC_DIRS)
INC_FLAGS       := $(addprefix -I,$(INC_DIRS))
LIB_FLAGS       := $(addprefix -L,$(LIB_DIRS))

# 查找所有源文件 (根据扩展名自动识别 C 或 C++ 源文件)
C_SRCS          := $(foreach dir, $(SRC_DIRS), $(wildcard $(dir)/*.c))
CPP_SRCS        := $(foreach dir, $(SRC_DIRS), $(wildcard $(dir)/*.cpp) $(wildcard $(dir)/*.cc) $(wildcard $(dir)/*.cxx))
SRCS            := $(C_SRCS) $(CPP_SRCS)

# 生成对应的目标文件 (.o) 和依赖文件 (.d) 路径
# 将源文件路径中的目录部分去除,然后加上 BUILD_DIR 前缀和 .o/.d 后缀
OBJS            := $(patsubst %.c,$(BUILD_DIR)/%.o,$(notdir $(C_SRCS))) \
                   $(patsubst %.cpp,$(BUILD_DIR)/%.o,$(notdir $(CPP_SRCS))) \
                   $(patsubst %.cc,$(BUILD_DIR)/%.o,$(notdir $(CPP_SRCS))) \
                   $(patsubst %.cxx,$(BUILD_DIR)/%.o,$(notdir $(CPP_SRCS)))

DEPS            := $(OBJS:.o=.d)

# 默认目标
.PHONY: all clean rebuild

all: $(BUILD_DIR) $(PROJECT_NAME)

# 创建构建目录
$(BUILD_DIR):
	@mkdir -p $(BUILD_DIR)

# 链接可执行文件
$(PROJECT_NAME): $(OBJS)
	@echo "Linking $(PROJECT_NAME)..."
	$(CXX) $(OBJS) $(LIB_FLAGS) $(LIBS) $(LDFLAGS) -o $@

# C 源文件编译规则
$(BUILD_DIR)/%.o: %.c
	@echo "Compiling C: $<"
	$(CC) $(CFLAGS) $(INC_FLAGS) -c $< -o $@ -MF $(@:.o=.d)

# C++ 源文件编译规则
$(BUILD_DIR)/%.o: %.cpp
	@echo "Compiling C++: $<"
	$(CXX) $(CXXFLAGS) $(INC_FLAGS) -c $< -o $@ -MF $(@:.o=.d)

$(BUILD_DIR)/%.o: %.cc
	@echo "Compiling C++: $<"
	$(CXX) $(CXXFLAGS) $(INC_FLAGS) -c $< -o $@ -MF $(@:.o=.d)

$(BUILD_DIR)/%.o: %.cxx
	@echo "Compiling C++: $<"
	$(CXX) $(CXXFLAGS) $(INC_FLAGS) -c $< -o $@ -MF $(@:.o=.d)

# 清理目标文件和可执行文件
clean:
	@echo "Cleaning build directory and project executable..."
	$(RM) -r $(BUILD_DIR) $(PROJECT_NAME)

# 重新构建
rebuild: clean all

# 包含自动生成的依赖文件 (.d 文件)
# `-include` 表示如果文件不存在也不会报错,这在第一次编译时很有用
-include $(DEPS)


如何使用这个 Makefile

保存为 Makefile: 将上述代码保存为名为 Makefile 的文件,放在你的项目根目录下。

配置变量:

PROJECT_NAME: 修改为你的可执行文件(或库)的名称。

SRC_DIRS: 列出所有包含 .c, .cpp, .cc, .cxx 源文件的目录。例如,如果你的源文件在 src/test/ 目录下,那么 SRC_DIRS := src test

INC_DIRS: 列出所有包含 .h 头文件的目录。这些目录会通过 -I 选项添加到编译器的头文件搜索路径中。

LIB_DIRS: 如果你的项目需要链接非标准路径下的库,请在这里添加库文件所在的目录。这些目录会通过 -L 选项添加到链接器的库搜索路径中。

LIBS: 添加你需要链接的库名称,例如 LIBS := -lm -lpthread

CC, CXX: 如果你需要使用特定的 C/C++ 编译器(例如 clang, arm-none-eabi-gcc),在这里修改。

CFLAGS, CXXFLAGS: 添加 C 和 C++ 的编译选项。常用的有 -Wall -Wextra (开启所有警告), -O2 (优化级别), -g (生成调试信息), -std=c++17 (C++ 标准)。注意:务必保留 -MMD,它用于自动生成 .d 依赖文件。

LDFLAGS: 添加链接选项,例如 -static。

BUILD_DIR: 编译中间文件 (.o, .d) 的存放目录。默认是 build/。

运行 make 命令:

编译项目: 在终端中进入项目根目录,然后简单地运行 make 或 make all。

清理中间文件: 运行 make clean 会删除 BUILD_DIR 目录和最终的可执行文件。

重新编译: 运行 make rebuild 会先清理再重新编译整个项目。

Makefile 模板说明

  1. VPATH:这是一个 make 特性,告诉 make 在哪里寻找源文件。当目标文件依赖于一个没有明确路径的源文件时,make 会在 VPATH 中指定的目录里搜索。

  2. INC_FLAGS 和 LIB_FLAGS:这两个变量将 INC_DIRS 和 LIB_DIRS 中的路径转换为编译器和链接器所需的 -I 和 -L 选项格式。

  3. wildcard 和 foreach:用于在指定目录中查找所有符合特定模式的源文件。

  4. patsubst 和 notdir:这些 make 函数用于处理文件名。notdir 提取文件名(不带路径),patsubst 用于替换字符串,这里是将 .c/.cpp 后缀替换为 $(BUILD_DIR)/%.o,从而生成中间目录下的目标文件路径。

  5. .PHONY:声明 all, clean, rebuild 为伪目标。这意味着它们不是实际的文件,make 不会尝试去查找同名文件来判断是否需要执行。

  6. -MMD 编译选项:这是 gcc/g++ 的一个重要选项。它告诉编译器在编译源文件时,同时生成一个 .d 文件,其中包含了该源文件所依赖的所有头文件。

  7. -MF ( @ : . o = . d ) : − M F 指定生成的依赖文件的名称。 (@:.o=.d):-MF 指定生成的依赖文件的名称。 (@:.o=.d)MF指定生成的依赖文件的名称。(@:.o=.d) 是一个 make 字符串替换,例如,如果目标文件是 build/main.o,那么依赖文件就是 build/main.d。一个 .d 文件的内容可能看起来像这样:main.o: main.c my_header.h common.h /usr/include/stdio.h

  8. -include $(DEPS):这是关键!它告诉 make 包含所有自动生成的 .d 文件。这样,当任何一个头文件被修改时,make 就能知道哪些源文件需要重新编译。_ 前缀的 - 表示即使 .d 文件在第一次编译时不存在,make 也不会报错。

你可能感兴趣的:(小Tips,开发语言,linux)