我正在尝试使用以下脚本在我的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。
如果您在第二个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
您似乎有执行顺序问题。您希望make
会在每次在规则配方中执行循环达到$(shell)
引用按词法出现的点时展开$(shell)
函数,但是如果您考虑一下,那将无法正常工作。 make
将配方中的每个命令移交给外壳程序以使其执行,这时它已由make
掌握。因此,在将命令传递给外壳程序之前,它必须(并且确实)扩展make
函数调用。
为此,配方中的整个for
循环是并且必须是单个命令(这是尾随反斜杠所针对的),因此$(shell)
函数在设置循环变量之前就已展开。在该命令中,$tag
扩展为空。
除了执行顺序外,$(shell)
函数中的代码执行在其自己的外壳中进行,在该外壳中无论如何都不会设置$tag
变量。
有几种不错的选择:
您有多个要构建的东西。大!就是在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
变量(包括$@
首先被扩展。
make
的$(shell)
函数老实说,除非在[[有意地中使用执行属性的顺序,否则在配方中使用$(shell)
实在太钝了,因为建模make
函数的shell功能几乎总是比较简单”和更合适的选择。例如:
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
传递给外壳的命令等效于*
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
在外壳程序代码中,构造$(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)
而且,我想这是到目前为止我最喜欢的替代方法。我一般不关心替代4:还改进了命令替换命令make
函数,因为它们是GNU扩展,并且它们中的许多趋向于产生模糊的编程范例。也许“编程范例”的措辞会更好,因为我通常不认为将makefile写为“编程”本身]。
sed
只是将一个字符串追加到其他多个字符串而已,这有点矫kill过正,其表达语法有点奥秘。我实际上非常喜欢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
将执行线连接,而不是将其留给外壳程序。我提供的是行拆分版本,以便于阅读。