You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

跨三目录的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 07:54:56