编写可重用代码的最佳实践

时间:2012-12-27 09:36:54

标签: makefile

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部分 - 它很吵,很恶心,而且不是右。

我该如何避免?我可以用模板重新定义所有任务,以便在一行中添加新的任务组吗?

2 个答案:

答案 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=}