跨三目录的GNU Makefile依赖配置方案求助
问题描述
目录结构
libFoo/foo.c libFoo/bar.c StandAlone/Foo/foo.c
编译产物:
- 静态库
.out/libFoo.a(由libFoo/下源码生成) - 可执行文件
.out/Foo(由StandAlone/Foo/foo.c生成)
核心需求
让StandAlone/Foo/Makefile识别以下依赖链:.out/Foo → .out/libFoo.a → libFoo/foo.c、libFoo/bar.c
修改libFoo/下源码时,仅重新编译libFoo.a和Foo,且不能调整现有目录结构。
已尝试方案与现有代码
使用-MD/-MMD参数仅能生成源码与头文件的依赖,无法识别库与源码、可执行文件与库的跨层级依赖。现有代码如下:
StandAlone/Foo/Makefile
PROGS=Foo include ../../Makefile.macros include ../../Makefile.libs include ../Makefile.inc check: $(call check_libraries) DEPENDS = .makedepend SRC := *.c *.ec $(DEPENDS): @makedepend -- $(CFLAGS) -- $(SRC) $(INC_FLAGS) -f- 2>/dev/null >$(DEPENDS) -include $(DEPENDS)
check_libraries宏
define check_libraries @for a in .obj ; do \ build_flag=0 ; \ lib="$${a}/`basename $$a`.a" ; \ if [ ! -f ".out/$$lib" ] ; then \ build_flag=1 ; \ else \ cd $$a; \ for obj in `ar -t $$lib` ; do \ fullpath_object="$${a}/$${obj}"; \ if [ ! -f "$$fullpath_object" ] ; then \ build_flag=1 ; \ fi \ done \ fi; \ if [ "$$build_flag" -eq 1 ] ; then \ echo "Building library: $$lib"; \ cd $$a; \ make; \ fi; \ done endef
解决方案
GNU Make的核心是显式依赖关系声明,原check_libraries用shell循环判断的方式不符合Make的原生依赖机制,以下是标准实现方案:
1. 声明可执行文件与库的依赖
在StandAlone/Foo/Makefile中,直接为.out/Foo添加库依赖:
# 若原有生成.out/Foo的规则未明确依赖,补充或修改规则 .out/Foo: $(SRC) .out/libFoo.a $(CC) $(CFLAGS) $(INC_FLAGS) -o $@ $^ $(LDFLAGS) # 或者针对PROGS批量声明依赖 $(addprefix .out/,$(PROGS)): .out/libFoo.a
2. 声明库与源码的依赖
在libFoo/目录的Makefile中,明确.out/libFoo.a的源码依赖:
# libFoo/Makefile示例 .out/libFoo.a: libFoo/foo.o libFoo/bar.o $(AR) rcs $@ $^ libFoo/foo.o: libFoo/foo.c $(CC) $(CFLAGS) $(INC_FLAGS) -c -o $@ $< libFoo/bar.o: libFoo/bar.c $(CC) $(CFLAGS) $(INC_FLAGS) -c -o $@ $<
3. 触发库的递归构建
替换原check目标和check_libraries宏,改用Make的递归调用自动处理依赖:
# 让Make自动检查并构建libFoo.a .out/libFoo.a: $(MAKE) -C ../../libFoo # 确保可执行文件构建前先处理库依赖 check: $(addprefix .out/,$(PROGS))
关于-MD/-MMD的说明
-MD/-MMD仅用于自动生成源码与头文件的依赖,无法处理“可执行文件→库→库源码”这种跨目录的构建依赖,这类依赖必须手动声明,这是GNU Make手册中明确支持的多目录构建方式。
兼容原有宏的修改方案(非推荐)
若因历史限制无法替换宏,可修改宏添加源码更新检查:
define check_libraries @LIB=../../libFoo; \ LIB_OUT=.out/libFoo.a; \ # 检查库是否存在,或源码是否比库新 if [ ! -f $$LIB_OUT ] || [ `find $$LIB -name "*.c" -newer $$LIB_OUT | wc -l` -gt 0 ]; then \ echo "Building library: $$LIB_OUT"; \ $(MAKE) -C $$LIB; \ fi; \ # 保留原有.obj相关检查逻辑 for a in .obj ; do \ build_flag=0 ; \ lib="$${a}/`basename $$a`.a" ; \ if [ ! -f ".out/$$lib" ] ; then \ build_flag=1 ; \ else \ cd $$a; \ for obj in `ar -t $$lib` ; do \ fullpath_object="$${a}/$${obj}"; \ if [ ! -f "$$fullpath_object" ] ; then \ build_flag=1 ; \ fi \ done \ fi; \ if [ "$$build_flag" -eq 1 ] ; then \ echo "Building library: $$lib"; \ cd $$a; \ make; \ fi; \ done endef
注:此shell判断方式可靠性低于Make原生依赖机制,仅作兼容过渡使用。
内容的提问来源于stack exchange,提问作者Justin Banks
相关产品推荐
相关产品推荐

