在Makefile Shell中使用for循环变量

时间:2020-01-18 06:15:09

标签: makefile

我正在尝试使用以下脚本在我的Makefile中创建一个docker pull:

ARCH=amd64
IMAGE=k8s.gcr.io/debian-base
TAG=v1.0.0

all:
        docker pull $(shell echo $(ARCH) | sed -e "s~[^ ]*~$(IMAGE)\-&:$(TAG)~g");

运行make命令时,可以拉出图像:

# make
docker pull k8s.gcr.io/debian-base-amd64:v1.0.0;
v1.0.0: Pulling from debian-base-amd64
39fafc05754f: Pull complete
Digest: sha256:5f25d97ece9076612b64bb551e12f1e39a520176b684e2d663ce1bd53c5d0618
Status: Downloaded newer image for k8s.gcr.io/debian-base-amd64:v1.0.0

我想补充一点。我想为带有多个标签的多个拱形图像创建清单的地方,例如:

ARCH=amd64 ppc64le
IMAGE=k8s.gcr.io/debian-base
export DOCKER_CLI_EXPERIMENTAL=enabled

all:
        for tag in v1.0.0 v1.0.1 ; do \
                docker manifest create $(IMAGE):$$tag $(shell echo $(ARCH) | sed -e "s~[^ ]*~$(IMAGE)\-&:$$tag~g"); \
        done

不幸的是,失败并显示以下错误:

# make
for tag in v1.0.0 v1.0.1 ; do \
    docker manifest create k8s.gcr.io/debian-base:$tag k8s.gcr.io/debian-base-amd64: k8s.gcr.io/debian-base-ppc64le:; \
done
invalid reference format
invalid reference format
make: *** [Makefile:6: all] Error 1

如果您在上方看到标签字段,则无法正确填充。在Makefile中可以这样做吗?我应该以其他方式或在此处进行一些修改吗? TIA。

2 个答案:

答案 0 :(得分:1)

您似乎有执行顺序问题。您希望make每次在规则配方中执行循环达到$(shell)引用按词法出现的点时都会扩展$(shell)函数,但是如果考虑一下,那将无法正常工作。 make将配方中的每个命令移交给外壳程序以使其执行,这时它已由make掌握。因此,在将命令传递给外壳程序之前,它必须(并且确实)扩展make函数调用。

为此,配方中的整个for循环并且必须是单个命令(这是结尾的反斜杠所针对的),因此$(shell)函数在循环变量出现之前就已展开组。在该命令中,$tag扩展为空。

除了执行顺序外,$(shell)函数中的代码执行在其自己的外壳中进行,在该外壳中无论如何都不会设置$tag变量。

有几种不错的选择:

替代方法1:摆脱循环

您有多个要构建的东西。大!没错,make的操舵室。让make为您提供帮助。例如:

# Note: make syntax permits whitespace around the "=" in variable assignments
ARCH = amd64 ppc64le
IMAGE = k8s.gcr.io/debian-base
export DOCKER_CLI_EXPERIMENTAL = enabled

TAGS = v1.0.0 v1.0.1

all: $(TAGS)

$(TAGS):
        docker manifest create $(IMAGE):$@ $(shell echo $(ARCH) | sed -e "s~[^ ]*~$(IMAGE)\-&:$@~g")

.PHONY: $(TAGS)

使用以下事实:将具有多个目标的规则分别应用于构建每个目标。它不需要显式的迭代变量,因为在任何make配方中,自动变量$@都会扩展为当前正在构建的目标的名称。

此处的$(shell)函数调用在命令运行之前仍会扩展,但这不是问题,因为其中的make变量(包括$@)首先被扩展。

替代2:使用shell命令替换代替make的{​​{1}}函数

老实说,除非在食谱中使用$(shell),除非故意 使用执行属性的顺序,否则$(shell)会很钝,因为make的外壳功能函数建模几乎总是一个更简单,更合适的选择。例如:

ARCH = amd64 ppc64le
IMAGE = k8s.gcr.io/debian-base
export DOCKER_CLI_EXPERIMENTAL = enabled

all:
        for tag in v1.0.0 v1.0.1 ; do \
                docker manifest create $(IMAGE):$$tag $$(echo $(ARCH) | sed -e "s~[^ ]*~$(IMAGE)\-&:$$tag~g"); \
        done

扩展后,make传递给shell的命令等效于 *

for tag in v1.0.0 v1.0.1 ; do \
        docker manifest create k8s.gcr.io/debian-base:$tag $(echo amd64 ppc64le | sed -e "s~[^ ]*~k8s.gcr.io/debian-base\-&:$tag~g"); \
done

在Shell代码中,构造$(any command)被称为“命令替换”。执行括号内的命令,并捕获并替换其标准输出。使用它就不会对执行顺序产生任何疑问。

替代3:以上两种情况

没有什么可以说的了,就像这样出来的:

ARCH = amd64 ppc64le
IMAGE = k8s.gcr.io/debian-base
export DOCKER_CLI_EXPERIMENTAL = enabled

TAGS = v1.0.0 v1.0.1

all: $(TAGS)

$(TAGS):
        docker manifest create $(IMAGE):$@ $$(echo $(ARCH) | sed -e "s~[^ ]*~$(IMAGE)\-&:$@~g")

.PHONY: $(TAGS)

而且,我想这是到目前为止我最喜欢的替代方法。我一般并不特别关心make函数,因为它们是GNU扩展,并且许多函数倾向于产生模糊的编程范例。也许“编程范例”的措辞会更好,因为我通常不认为将makefile编写为“编程”(本身)。

替代方法4:还改进了命令替换命令

sed只是将一个字符串追加到多个其他字符串,这有点过头了,其表达语法有点奥秘。我实际上非常喜欢sed,但是对于在makefile中使用,我非常重视清晰度。出于这个原因,我可能会自己做以下事情:

ARCH = amd64 ppc64le
IMAGE = k8s.gcr.io/debian-base
export DOCKER_CLI_EXPERIMENTAL = enabled

TAGS = v1.0.0 v1.0.1

all: $(TAGS)

$(TAGS):
        docker manifest create $(IMAGE):$@ $$(for arch in $(ARCH); do echo "$(IMAGE)-$$arch:$@"; done)

.PHONY: $(TAGS)

* 等效,但不完全相同,因为make将执行行连接,而不是将其留给shell。我提出的是行拆分版本,以便于阅读。

答案 1 :(得分:0)

如果在第二个Makefile前面添加\,您的$$tag应该可以正常工作。这是因为$(shell中的内容两次传递到外壳程序(一次$(shell调用一次,一次docker manifest ...调用一次)。

ARCH=amd64 ppc64le
IMAGE=k8s.gcr.io/debian-base
export DOCKER_CLI_EXPERIMENTAL=enabled

all:
    for tag in v1.0.0 v1.0.1 ; do \
        docker manifest create $(IMAGE):$$tag $(shell echo $(ARCH) | sed -e "s~[^ ]*~$(IMAGE)\-&:\$$tag~g"); \
    done

也许让make建立名字会更简单吗?

ARCH=amd64 ppc64le
IMAGE=k8s.gcr.io/debian-base

all:
    for tag in v1.0.0 v1.0.1 ; do \
        docker manifest create $(IMAGE):$$tag $(foreach a,$(ARCH),$(IMAGE)-$a:$$tag); \
    done
相关问题