7

在 s 中编写可重用代码的最佳实践是Makefile什么?

假设我有一个 Makefile:

.PHONY: all task01-all task01-clean task01-run task02-all task02-clean task02-run

all: task01-all task02-all

###############################################################################
task01-all: task01-clean task01 task01-run

task01-clean:
     rm task01 task01.{exi,o} -f

task01:
    compiler task01.ext -O2 --make

task01-run:
    ./task01

###############################################################################
task02-all: task02-clean task02 task02-run

task02-clean:
    rm task02 task02.{exi,o} -f

task02:
    compiler task02.ext -O2 --make

task02-run:
    ./task02

现在我想添加新的任务系列(task03),我需要复制粘贴整个部分,制作s/02/03/它并将它们添加到.PHONY部分 - 它很吵,令人作呕而且不正确。

我怎样才能避免这种情况?我可以用模板重新定义所有任务,以便在一行中添加新的任务组吗?

4

2 回答 2

11

由于问题是关于在 Makefiles 中编写可重用的代码,我将举例说明如何在 GNU Make 中使用模式规则(看起来这就是你在使用的,因为你提到了.PHONY目标)。但是,如果您没有使用 Make 的任何依赖项检查,则使用 shell 脚本执行此操作可能会更简单——例如:

#!/bin/sh
TASKS="task01 task02 task03"

for i in $TASKS; do
    rm $i $i.ext $i.o -f;
    compiler $i.ext -O2 --make;
    ./$i;
done

但是,要将原则扩展到 Make,我们还有另一个问题需要解决。表格行:

task01-all: task01-clean task01 task01-run

不要告诉 Make 以什么顺序构建先决条件——只是它们都需要在task01-all构建之前完成。相反,要运行的每个步骤都应取决于它之前的步骤。像这样的东西:

TASKS=task01-run task02-run task03-run

.PHONY: all $(TASKS) $(TASKS:run=clean)

all: $(TASKS)

$(TASKS:run=clean): %-clean:
    rm $* $*.ext $*.o -f

%: %.ext | %-clean
    compiler $< -O2 --make

$(TASKS): %-run: %
    ./$<

规则%被称为“模式规则”,它们是避免为不同目标多次重写相同规则的好工具。一个警告是 Make 通常不会检查.PHONY目标的模式规则。我们通过在这些规则前面加上目标列表和第二个冒号(例如,)来明确地告诉 Make 这样做$(TASKS):

由于工作task01-run需要task01,我们使%-run目标依赖于%. 此外,您的 Makefile 显示您希望每次都运行 clean,因此我们%依赖%-clean. 由于%-clean实际上不产生任何输出,因此我们将其设为“仅订单”依赖项——Make 不会查找时间戳或任何内容,它只会在%-clean需要运行规则时首先运行%规则。“仅订购”依赖项放在 a 之后|

%: %.ext | %-clean

值得一提的是,Make 的最大优势之一是它可以通过不重复不需要重复的工作来节省时间——即,它只在依赖项比目标更新时运行规则。因此,您可以取消对 的依赖%-clean,这将导致 make 仅在compiler $< -O2 --make更新%.ext时运行%

%: %.ext
    compiler $< -O2 --make

然后,您可以添加一条规则来运行所有%-clean目标:

.PHONY: all $(TASKS) $(TASKS:run=clean) clean

clean: $(TASKS:run=clean)

最后一件事:我在食谱中使用了一些特殊的变量。$@代表正在构建的目标。$<代表第一个依赖。$*代表模式规则的“词干”(即,由 匹配的部分%)。

于 2012-12-27T15:04:35.900 回答
2

看起来像我正在寻找的东西:

ALLS=task01-all task02-all
BUILDS=${ALLS:-all=-build}
CLEANS=${ALLS:-all=-clean}
RUNS=${ALLS:-all=-run}

.PHONY: all $(ALLS) $(CLEANS) $(BUILDS) $(RUNS)

all: $(ALLS)

###############################################################################
$(ALLS): $(CLEANS) $(BUILDS) $(RUNS)

$(CLEANS):
        rm ${@:-clean=} ${@:-clean=}.{ext,o} -f

$(BUILDS):
        compiler ${@:-build=}.ext -O2 --make

$(RUNS):
        ./${@:-run=}
于 2012-12-27T14:22:47.553 回答