【问题标题】:Gnu make: build target only if not building another targetGnu make:仅在不构建另一个目标时才构建目标
【发布时间】:2015-06-02 19:55:06
【问题描述】:

我正在尝试从*.cc 类型的源文件构建一个makefile,以优化某些文件的构建。有两组目标,%.o%.vd。构建%.o 构建%.vd 作为副作用,所以如果需要构建%.o,我不希望显式构建%.vd。但是,单独构建*.vd 有一个更有效的过程,因此如果不需要构建%.o,我希望只构建%.vd 而不是%.o

这是我开始的(作为指导):

all:

CC_FILES := $(wildcard *.cc)
O_FILES := $(patsubst %.cc,%.o,$(CC_FILES))
VD_FILES := $(patsubst %.cc,%.vd,$(CC_FILES))

all: $(O_FILES) $(VD_FILES)

# rule to make %.o that makes %.vd as side-effect; relatively expensive
%.vd %.o: %.cc
    # shell commands

# rule to make only %.vd, relatively fast
%.vd: %.cc
    # different set of shell commands

但是,这总是会同时生成 *.vd*.o 文件,并且当 *.o 已经是最新的时,不会自动采用仅构建 *.vd 的有效路线,但会冗余地重新生成构建*.o,我不想发生这种情况。

【问题讨论】:

  • @EtanReisner,你想试试这个吗?

标签: makefile gnu-make


【解决方案1】:

这反映了 make 的一项常见任务,但可以通过两种不同的方式发生。

文件链

有时构建会经历一个序列的文件类型,但并不总是希望重新生成每个文件,而是在中途停止。举个例子,当从 lex 和 yacc 构建一些东西时,我们可能会得到一些模糊的东西:

%.l -> %.y -> %.c -> %.o -> ....

或者从 LaTex 构建输出文件时:

%.tex -> %.dvi -> %.ps -> %.pdf   (maybe)

我们只是放了必要的Pattern Rules 来显示依赖关系:

%.y: %.c
        bison -y $<
        mv y.tab.c $@
%.c: %.o
        $(CC) $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@ 
        # This rule is actually built into make

现在我们可以随时制作中间文件:

$ make mycompiler.c

并且 make 只会从源 mycompiler.y 重新构建文件 mycompiler.c 构建所需的任何早期 *.l 文件,但不会构建 mycompiler.o 或 mycompiler.exe。它只是制作您要求的文件。

请注意,这只是一个 GNU make 配方。还有一个Traditional Suffix rule 用于其他类似的制作系统。应该是这样写的:

.y.c:
       bison -y $<
       mv y.tab.c $@
       ... etc...

如果您想构建 all *.vd,下一个问题是您如何指定它。您可以像这样传递 *.vd 文件的名称(如果它们存在):

make *.vd

或在 make 文件中有一个 phony 目标,将它们包含在依赖项列表中:

VD_FILES= 1.vd 2.vd ... etc ...
.PHONY: VD
VD: $(VD_FILES)

文件选择

在您的示例中,您有一个选择,而不是一个序列。您可以从源文件中创建一个或两个目标文件。 make 中有一个语法,它是double colon rule。不幸的是,使用了double colons in patterns means something different,所以我们必须使用双冒号规则和虚假目标来表达我们想要的构建。我们还必须注意规则的顺序,因为较早的规则优先于后面的规则。在您的示例中,这可以通过以下方式实现*

CC_FILES := $(wildcard *.cc)
O_FILES := $(patsubst %.cc,%.o,$(CC_FILES))
VD_FILES := $(patsubst %.cc,%.vd,$(CC_FILES))

.PHONY: all VD

all: $(VD_FILES) $(O_FILES)

VD: $(VD_FILES)

# rule to make only %.vd, relatively fast
$(VD_FILES):: $(CC_FILES)
    # different set of shell commands
    touch $@

# rule to make %.o that makes %.vd as side-effect; relatively expensive
%.vd %.o:: %.cc
    # shell commands
    touch $*.o $*.vd

我们现在可以调用 make 来构建所有 *.vd 文件:

make VD

或者只是一两个单独的:

make 1.vd 2.vd

但如果我们要求一个 *.o 文件,它将同时构建:

make 1.o

但是,由于我们首先在依赖项列表中列出了 VD 文件,因此它将在 *.o 文件之前检查所有 VD 文件,这意味着每次都会采用最佳构建。这意味着您现在有选择;您可以手动请求构建任何文件、仅请求 VD 文件或高效更新所有内容(默认)。

*这已经过测试,并且是一个工作的makefile BTW

【讨论】:

  • 我很想知道您是如何回答这个问题的。这根本不是一个常见的制作场景。我需要构建所有*.o 及其对应的*.vd。但是,如果任何.o,比如foo.o 需要建立在make 的任何特定运行之上,那么foo.vd 的收据不应该运行,否则它应该运行。
  • 好的,我在问题中添加了 Further Details:
  • 不,这不能正常工作。如果只是 %.vd 已过期,则两条规则都会运行。只有第一个应该运行。
  • @ThomasMcLeod 你输入了什么命令?如果您只输入make,您正在命令它生成*.o 文件。正如我解释的那样,如果您命令它执行该子集,它只会生成*.vd,因为*.o 是默认目标all 的一部分。如果您想要默认的*.vd,请更改VDall 规则。
  • 构建的所有逻辑都需要在 Makefile 中。您的解决方案依赖于外部逻辑来确定 make 参数。这需要 100% 自动。
【解决方案2】:

我设计的解决方案将%.o 作为%.vd 的仅限订单的先决条件。在%.o 规则中,配方使用eval 函数设置变量O_RULE 以指示%.o 规则已运行。 %.vd 配方仍在运行。但是配方被封装在一个if 函数中,该函数的测试是O_RULE%.vd 配方仅在 O_RULE 未定义时运行。

CC_FILES = $(wildcare *.cc)
O_FILES = $(patsubst %.cc,%.o,$(wildcard *.cc))
VD_FILES = $(patsubst %.cc,%.vd,$(wildcard *.cc))

.PHONY: all

all: $(O_FILES)

$(info MAKE_RESTARTS is $(MAKE_RESTARTS))

%.vd: | %.o
    @echo run %.vd recipe
    $(if $(DISABLE_$*_VD),,touch $@)

%.o: %.cc
    @echo run %.o recipe
    $(eval DISABLE_$*_VD := 1)
    touch $@
    touch $*.vd

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-01
    • 1970-01-01
    • 2016-12-19
    • 2013-10-05
    • 1970-01-01
    • 2020-11-17
    相关资源
    最近更新 更多