Makefile先决条件中的复杂模式替换

时间:2012-05-07 14:35:41

标签: dependencies makefile build-automation

我有以下目录结构:

./
 |- obj/
 |--- debug/
 |--- release/
 |- bin/
 |--- debug/
 |--- release/
 |- src/
 |--- main.c
 |--- libb/
 |----- foo.c
 |- include/
 |--- libb/
 |----- foo.h

此外,我以下列格式(示例)手动指定Makefile中各个对象的依赖关系:

 main_DEPS = libb/foo

现在我希望能够输入make bin/debug/main,这会生成必要的依赖项和目标文件:

  • obj/debug/libb/foo.o
  • obj/debug/main.o
  • bin/debug/main

我该怎么做?我目前的做法非常错综复杂:

  • 以下内容创建目标文件。但是,如果它们尚不存在,则无法创建任何必要的子文件夹(libb)。它也不会跟踪任何已更改的头文件。可以调整Makefile tutorial trick to generate prerequisites automatically吗?

    CFLAGS+=-I include
    
    obj/*/%.o: src/%.cpp
        $(CC) $(CFLAGS) $< -c -o $@
    
  • 以下尝试解析二进制文件的依赖关系并从所有必需的目标文件构建它。但我不知道如何从*_DEPS变量中检索这些目标文件。

    .SECONDEXPANSION:
    
    bin/debug/%: obj/debug/%.o $(patsubst %,obj/debug/%.o,$($(patsubst bin/debug/%,%,$@)_DEPS))
        $(LD) $(LDFLAGS) $< $(patsubst %,obj/debug/%.o,$($(patsubst bin/debug/%,%,$@)_DEPS)) -o $@
    
    bin/release/%: obj/release/%.o $(patsubst %,obj/release/%.o,$($(patsubst bin/release/%,%,$@)_DEPS))
        $(CXX) $(LDFLAGS) $< $(patsubst %,obj/release/%.o,$($(patsubst bin/release/%,%,$@)_DEPS)) -o $@
    

    不幸的是,这不会自动构建依赖项:它只是抱怨“obj / debug / libb / foo.o:没有这样的文件或目录” - 但如果我使用{{1}手动构建此文件,它就足够奇怪了}。

    它也无可救药地复杂并使用无偿代码重复(我无法摆脱它而不会使它变得更复杂)。

对此最简单的解决方案是什么?原则上我并不反对使用除mkdir obj/debug/libb; make obj/debug/libb/foo.o以外的其他东西(如果它可以广泛使用)(autoconf ...)但是我更喜欢将努力(以及所需的新学习)保持在最低限度并且从目前为止我所看到的其他构建系统似乎比make复杂得多。

1 个答案:

答案 0 :(得分:2)

更新以回应chat message

  

@sehe它让它稍微容易一些,是的......但复制仍然存在,而且还没有构建依赖关系的问题

我找到了一种冗长但有效(非复杂)的方法,使其可以使用submake调用(单个Makefile中的 )。

我们有'外部制作',它只包含一个规则和依赖关系定义

A_DEPS=dep1 dep2
C_DEPS=dep2

##########################
# "outer" Make
bin/%/A bin/%/B bin/%/C:
    $(MAKE) BUILD="$*" TARGET=$(@F) DEPS="$($(@F)_DEPS)" GENRULE

实际上,我们将'推导'信息转换为子调用make中的显式变量。 Makefile的其余部分包含我们的'通用目标规则'的定义,GENRULE

##########################
# "inner" Make

%/*.o:
    mkdir -p "$(@D)" && touch "$@"

GENRULE: obj/$(BUILD)/$(TARGET).o $(DEPS:%=obj/$(BUILD)/%.o) 
    echo gcc -o "bin/$(BUILD)/$(TARGET)" $^
    mkdir -p "bin/$(BUILD)" && touch "bin/$(BUILD)/$(TARGET)"
.PHONY: GENRULE

再次,测试运行:

$ for target in bin/{debug,release}/{A,B,C}; do make -Bs "$target"; done

gcc -o bin/debug/A obj/debug/A.o obj/debug/dep1.o obj/debug/dep2.o
gcc -o bin/debug/B obj/debug/B.o
gcc -o bin/debug/C obj/debug/C.o obj/debug/dep2.o
gcc -o bin/release/A obj/release/A.o obj/release/dep1.o obj/release/dep2.o
gcc -o bin/release/B obj/release/B.o
gcc -o bin/release/C obj/release/C.o obj/release/dep2.o

所有对象都已创建:

$ find bin obj

bin
bin/debug
bin/debug/A
bin/debug/B
bin/debug/C
bin/release
bin/release/A
bin/release/B
bin/release/C
obj
obj/debug
obj/debug/A.o
obj/debug/B.o
obj/debug/C.o
obj/debug/dep1.o
obj/debug/dep2.o
obj/release
obj/release/A.o
obj/release/B.o
obj/release/C.o
obj/release/dep1.o
obj/release/dep2.o

对于简单变量(非依赖性),这是一个使用$ @和$(@ F)自动变量的简单示例

EXTRA="--standardopts"
A_FLAGS="--a-opts"
B_FLAGS="--b-opts"

.SECONDEXPANSION:

debug/% release/%: EXTRA+=$($(@F)_FLAGS)

%/A %/B %/C:
    echo building $@ with $(EXTRA)