在Makefile
s中编写可重用代码的最佳做法是什么?
假设我有一个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
部分 - 它很吵,很恶心,而且不是右。
我该如何避免?我可以用模板重新定义所有任务,以便在一行中添加新的任务组吗?
答案 0 :(得分:9)
由于问题是关于在Makefile中编写可重用代码,我将举例说明如何在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
目标的模式规则;我们通过在这些规则前加上目标列表和第二个冒号(例如$(TASKS):
)来告诉Make明确地这样做。
由于task01-run
需要task01
才能发挥作用,我们会使%-run
个目标取决于%
。此外,您的Makefile显示您希望每次都运行干净,因此我们%
依赖于%-clean
。由于%-clean
实际上不会产生任何输出,我们将其设为“仅限订单”依赖项 - Make不会查找时间戳或任何内容,它将首先运行%-clean
规则任何时候它需要运行%
规则。 “仅订购”依赖项放在|
:
%: %.ext | %-clean
值得一提的是,Make的最大优势之一是它可以通过不重复不需要重复的工作来节省时间 - 即,如果依赖项比目标更新,它只会运行规则。因此,您可以不依赖于%-clean
,如果compiler $< -O2 --make
比%.ext
更新,则会导致make仅运行%
:
%: %.ext
compiler $< -O2 --make
然后,您可以添加规则以运行所有%-clean
目标:
.PHONY: all $(TASKS) $(TASKS:run=clean) clean
clean: $(TASKS:run=clean)
最后一件事:我在食谱中使用了一些特殊的变量。 $@
代表正在构建的目标。 $<
代表第一个依赖项。 $*
代表模式规则的“主干”(即与%
匹配的部分)。
答案 1 :(得分: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=}