主要修改点:
定义
BUILD_DIR:指定存放.o文件的目录(这里设为build)。重构
OBJS变量:使用模式替换,将源文件列表(如src/a.c)映射到构建目录(如build/src/a.o)。自动创建目录:在编译规则中增加
mkdir -p,确保在编译.o之前,对应的子目录结构已经存在。修正 Shared Library 标志:既然目标是
.so(动态库),我为你加上了-fPIC(编译期) 和-shared(链接期),这是 Linux 下编译动态库的标准做法(原 Makefile 缺少这些会导致不可用)。
优化后的 Makefile
Makefile
# 目标文件
TARGET = libucp.so
# 构建目录(存放 .o 文件)
BUILD_DIR = build
# 获取所有 .c 源文件
SRC := $(shell find . -type f -name "*.c")
# 将 .c 文件列表转换为 build 目录下的 .o 文件列表
# 例如: ./src/main.c -> build/./src/main.o
OBJS := $(SRC:%.c=$(BUILD_DIR)/%.o)
CC = gcc
# 编译选项
# 注意:编译动态库通常需要 -fPIC
CFLAGS = -O0 -g -fPIC
CFLAGS += -I/usr/include/glib-2.0/ -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
# 保留原本的头文件查找逻辑
CFLAGS += $(shell find . -type f -name "*.h" -exec dirname {} \; | sort -u | xargs printf "-I%s ")
# 链接选项
LDFLAGS = -Lucp_stack/libs/lib
# 生成 .so 必须加上 -shared
LDFLAGS += -shared
LIBS = -lpthread -lglib-2.0
# 伪目标,防止文件名冲突
.PHONY: all clean
all: $(TARGET)
# 链接规则
$(TARGET): $(OBJS)
@echo "Linking $@"
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) $(LIBS)
# 编译规则
# $(BUILD_DIR)/%.o 匹配 build 目录下的目标
# %.c 匹配当前目录下对应的源文件
$(BUILD_DIR)/%.o: %.c
@echo "Compiling $<"
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
# 清理规则
clean:
@echo "Cleaning build directory and target..."
$(RM) -r $(BUILD_DIR) $(TARGET)
代码详解
1. 路径映射的核心逻辑
OBJS := $(SRC:%.c=$(BUILD_DIR)/%.o)
这行代码使用了 Make 的模式替换。假设 SRC 是 src/foo.c,BUILD_DIR 是 build,那么 OBJS 就会自动变成 build/src/foo.o。
2. 编译规则与目录创建
$(BUILD_DIR)/%.o: %.c
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
Target (
$(BUILD_DIR)/%.o): 告诉 Make 如何生成位于build/下的.o文件。Dependency (
%.c): 它依赖于源码树中相同路径的.c文件。@mkdir -p $(dir $@): 这是一个关键技巧。$@是目标文件(例如build/src/net/tcp.o),$(dir $@)取出目录部分(build/src/net/)。mkdir -p会在编译前递归创建这个目录结构,防止编译器报错“目录不存在”。
3. 动态库标志 (Important)
原 Makefile 中编译 .so 文件却没有加 -fPIC 和 -shared。
-fPIC: Position Independent Code,编译.o时必须加,否则无法合并进动态库。-shared: 链接时必须加,告诉 gcc 生成共享库而不是可执行文件。