我正在尝试使用以下脚本在我的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。
答案 0 :(得分:1)
您似乎有执行顺序问题。您希望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
的{{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)
被称为“命令替换”。执行括号内的命令,并捕获并替换其标准输出。使用它就不会对执行顺序产生任何疑问。
没有什么可以说的了,就像这样出来的:
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编写为“编程”(本身)。
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