动态库源码分散在多个目录时,gcc -shared 不能直接跨目录编译;必须先用 -I 指定所有头文件路径、按源文件所在目录分别编译为 .o(推荐统一输出到 build/obj/),再集中链接生成 .so。

动态库源码分散在多个目录时,gcc -shared 不能直接跨目录编译
你不能写 gcc -shared -fPIC dir1/a.c dir2/b.c -o libfoo.so 就完事——gcc 默认不递归搜索头文件或自动处理跨目录依赖。错误现象通常是:fatal error: xxx.h: No such file or directory,或者链接时报 undefined reference(因为某个目录下的 .o 根本没参与构建)。
真正可行的做法是:先统一生成所有目标文件(.o),再集中链接。关键在于路径控制和头文件包含逻辑。
-
-I必须覆盖所有头文件所在路径,可多次使用,比如-Iinclude -Ilib/utils/include -Ithird_party/headers - 每个
.c文件需用其**所在目录的相对路径**调用gcc -c,否则#include "xxx.h"会失效 - 建议把所有
.o输出到同一临时目录(如build/obj/),避免混杂和清理困难
Makefile 中组织多目录动态库的典型结构
如果你用 Makefile 管理,不要试图在一个规则里“猜”所有源文件位置。显式列出或用 wildcard + addprefix 更可靠。
例如,假设源码分布在 src/core/ 和 src/platform/linux/:
SRCS := $(wildcard src/core/*.c) $(wildcard src/platform/linux/*.c) OBJS := $(SRCS:.c=.o) BUILD_DIR := build/obj OBJS_IN_BUILD := $(addprefix $(BUILD_DIR)/, $(notdir $(OBJS))) $(BUILD_DIR)/%.o: %.c @mkdir -p $(BUILD_DIR) gcc -c $< -o $@ -Iinclude -Isrc/core -Isrc/platform/linux -fPIC libmylib.so: $(OBJS_IN_BUILD) gcc -shared -o $@ $^
注意两点:
-
$(BUILD_DIR)/%.o: %.c是模式规则,但$<仍按原始路径读取.c,所以#include路径必须和源文件位置对齐 - 如果
src/platform/linux/下有#include "../core/log.h",那-Isrc/core就比-Iinclude更优先,顺序影响查找结果
用 cmake 处理更省心,但得避开 aux_source_directory 的坑
aux_source_directory 看起来方便,但它**不会递归子目录**,且不保证编译顺序。多目录项目应改用 file(GLOB_RECURSE ...) 或显式列举。
正确写法示例(在 lib/CMakeLists.txt 中):
file(GLOB_RECURSE LIB_SRCS
"${CMAKE_CURRENT_SOURCE_DIR}/src/core/*.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/platform/*.c"
)
add_library(mylib SHARED ${LIB_SRCS})
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
${CMAKE_CURRENT_SOURCE_DIR}/src/core
${CMAKE_CURRENT_SOURCE_DIR}/src/platform
)
这样能确保:
- 头文件路径对所有源文件生效(
PUBLIC会让下游也继承) -
GLOB_RECURSE真正穿透子目录,比如src/platform/linux/epoll.c会被捕获 - 不会因文件名排序导致符号定义顺序错乱(
GLOB结果是字典序,但现代 GCC 对顺序不敏感;若真有强依赖,还是手动列set(LIB_SRCS ...))
运行时找不到 .so?别只往 /usr/lib 硬塞
多目录项目生成的 .so 通常不在系统路径下,硬拷贝到 /usr/lib 是反模式,破坏可重现性和权限管理。
推荐三种实际可用方式:
- 运行前设置
LD_LIBRARY_PATH:LD_LIBRARY_PATH=$(pwd)/lib ./main - 编译可执行文件时用
-Wl,-rpath,$ORIGIN/../lib,让程序自己知道去哪找库($ORIGIN指可执行文件所在目录) - 安装后运行
sudo ldconfig -n /path/to/your/lib临时注册,不改系统配置
最容易被忽略的是:-rpath 中的 $ORIGIN 是链接器特性,不是 shell 变量,不能写成 "$ORIGIN"(引号会导致字面量传递失败);也不要在 Makefile 中用 $(ORIGIN)——那是 make 变量语法,会提前展开为空。


















