如何为Make驱动的C库项目搭建多示例子目录结构?
问题描述
我日常主要使用C++,目前正在开发一个小型C库项目来提升C语言能力。
当前项目结构
project/ ├─ bin/ │ ├─ libproject.a ├─ include/ ├─ src/ ├─ Makefile
当前顶层Makefile内容
SRC_DIR := src OBJ_DIR := obj BIN_DIR := bin LIB_DIR := lib EXE := $(BIN_DIR)/libproject.a SRC := $(wildcard $(SRC_DIR)/*.c) OBJ := $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o) CPPFLAGS := -Iinclude -MMD -MP CFLAGS := -Wall LDFLAGS := -L$(LIB_DIR) LDLIBS := -lm .PHONY: all clean all: $(EXE) $(EXE): $(OBJ) | $(BIN_DIR) $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@ $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR) $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ $(BIN_DIR) $(OBJ_DIR): mkdir -p $@ clean: @$(RM) -rv $(BIN_DIR) $(OBJ_DIR) -include $(OBJ:.o=.d)
需求
目前我通过单个入口文件main.c迭代测试新功能,现希望通过examples目录以更独立的方式展示库的用法,每个子目录对应一个要演示的功能,预期项目结构如下:
project/ ├─ bin/ │ ├─ examples/ │ ├─ libproject.a ├─ examples/ │ ├─ foo/ │ │ ├─ include/ │ │ ├─ src/ │ │ ├─ Makefile │ ├─ bar/ │ │ ├─ include/ │ │ ├─ src/ │ │ ├─ Makefile │ ├─ Makefile ├─ include/ ├─ src/ ├─ Makefile
我刚接触Make(主要使用CMake),希望获得编写可选择性构建examples目录下所有项目的Makefile的优化建议,同时也欢迎针对*nix风格库的注意事项建议。
解决方案与优化建议
一、Makefile构建方案
1. 顶层Makefile修改
修改顶层Makefile,添加examples目标,支持单独构建库或示例,同时修正原Makefile中静态库构建的错误:
SRC_DIR := src OBJ_DIR := obj BIN_DIR := bin LIB_DIR := lib EXAMPLES_DIR := examples # 库文件目标 LIB := $(BIN_DIR)/libproject.a SRC := $(wildcard $(SRC_DIR)/*.c) OBJ := $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o) # 示例相关目标 EXAMPLES := $(wildcard $(EXAMPLES_DIR)/*) EXAMPLES_BIN := $(addprefix $(BIN_DIR)/examples/,$(notdir $(EXAMPLES))) CPPFLAGS := -Iinclude -MMD -MP CFLAGS := -Wall -Wextra -pedantic LDFLAGS := -L$(BIN_DIR) LDLIBS := -lproject -lm .PHONY: all clean lib examples $(EXAMPLES) # 默认只构建库 all: lib # 单独构建库目标 lib: $(LIB) # 构建所有示例 examples: lib $(EXAMPLES_BIN) # 单独构建某个示例(比如make foo) $(EXAMPLES): lib $(MAKE) -C $@ BIN_DIR=$(abspath $(BIN_DIR)/examples/$(notdir $@)) LIB_DIR=$(abspath $(BIN_DIR)) # 示例二进制文件的依赖规则(确保目录存在) $(BIN_DIR)/examples/%: mkdir -p $@ $(LIB): $(OBJ) | $(BIN_DIR) ar rcs $@ $^ # 静态库必须用ar命令创建,原Makefile用cc链接是错误的 $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR) $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ $(BIN_DIR) $(OBJ_DIR): mkdir -p $@ clean: @$(RM) -rv $(BIN_DIR) $(OBJ_DIR) @for dir in $(EXAMPLES); do $(MAKE) -C $$dir clean; done -include $(OBJ:.o=.d)
2. 示例子目录Makefile(通用模板)
每个示例子目录的Makefile可以采用统一模板,便于维护,以foo目录为例:
SRC_DIR := src OBJ_DIR := obj # 从顶层传递的参数,默认值用于单独构建示例 BIN_DIR ?= bin LIB_DIR ?= ../../bin EXE := $(BIN_DIR)/foo SRC := $(wildcard $(SRC_DIR)/*.c) OBJ := $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o) CPPFLAGS := -Iinclude -I../../include -MMD -MP CFLAGS := -Wall -Wextra -pedantic LDFLAGS := -L$(LIB_DIR) LDLIBS := -lproject -lm .PHONY: all clean all: $(EXE) $(EXE): $(OBJ) | $(BIN_DIR) $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@ $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR) $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ $(BIN_DIR) $(OBJ_DIR): mkdir -p $@ clean: @$(RM) -rv $(BIN_DIR) $(OBJ_DIR) -include $(OBJ:.o=.d)
3. 常用构建命令
- 只构建库:
make或make lib - 构建所有示例:
make examples - 单独构建某个示例:
make foo(在顶层执行,自动进入对应目录构建) - 清理所有产物:
make clean
二、*nix风格C库注意事项
- 静态库构建规范:必须用
ar rcs命令创建静态库,rcs分别表示替换旧文件、创建新库、生成索引表,这是*nix下的标准做法。 - 头文件组织:
include目录只放对外暴露的公共头文件,内部实现头文件放在src目录,避免用户引入无关文件。 - 编译选项:添加
-Wextra -pedantic开启严格编译检查,帮助发现C语言潜在问题,符合*nix严谨编码风格。 - 安装路径:如果后续支持
make install,遵循*nix标准路径:头文件放/usr/local/include,库文件放/usr/local/lib,可通过PREFIX变量自定义路径。 - 版本管理:静态库可添加版本号,比如命名为
libproject.a.1.0,再用软链接指向最新版本:ln -s libproject.a.1.0 libproject.a。 - 文档要求:根目录添加
README.md和MANUAL.md,说明库的功能、编译方法、示例用法,*nix生态非常看重清晰的文档。 - 测试机制:可添加
tests目录编写单元测试,用make test目标执行,保障库的稳定性。
内容的提问来源于stack exchange,提问作者Mitch War
相关产品推荐
相关产品推荐

