Использование переменной l oop в оболочке Makefile - PullRequest
0 голосов
/ 18 января 2020

Я пытаюсь создать docker извлечение в моем Makefile, используя следующий скрипт:

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? Должен ли я сделать это каким-то другим способом или некоторые изменения, необходимые здесь? ТИА.

Ответы [ 2 ]

1 голос
/ 18 января 2020

У вас, похоже, проблема с порядком исполнения. Вы ожидаете, что make будет расширять функцию $(shell) каждый раз, когда выполнение l oop в рецепте правила достигает точки, где ссылка $(shell) появляется лексически, но если вы подумаете об этом, это не сработает. make передает каждую команду в рецепте оболочке для выполнения, и в этот момент она не в руках make. Поэтому он должен (и должен) расширять make вызовов функций перед передачей команды в оболочку.

Целое for l oop в вашем рецепте есть и должно быть одной командой для этой цели (это то, о чем идет обратный слеш), поэтому функция $(shell) раскрывается до того, как будет установлена ​​переменная l oop. Внутри этой команды $tag расширяется до нуля.

И кроме порядка выполнения, выполнение кода в функции $(shell) происходит в ее собственной оболочке, где переменная $tag не будет установить в любом случае.

Есть несколько хороших альтернатив:

Альтернатива 1: избавиться от l oop

У вас есть несколько вещей, которые вы хотите построить. Большой! Это верно в 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 автоматическая c переменная $@ расширяется до имени цели, которая в настоящее время создается.

Вызов функции $(shell) здесь все еще раскрывается до запуска команды, но это не проблема, потому что переменные make внутри, включая $@, раскрываются первыми.

Альтернатива 2: использовать подстановку команд оболочки вместо make $(shell) function

Честно говоря, довольно глупо использовать $(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 передает оболочке в этом случае, эквивалентна *

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)

И, думаю, эта альтернатива мне больше всего нравится. Меня не особо волнуют функции make, так как они являются расширением GNU, и многие из них имеют тенденцию приводить к размытой парадигме программирования. Или, может быть, «парадигма программирования» была бы лучше формулировать, поскольку я обычно не думаю о написании make-файлов как о «программировании» per se .

Альтернатива 4: также улучшите команду подстановки команд

sed - это немного излишне просто добавлять строку в несколько других строк, а синтаксис ее выражения немного загадочен. Я на самом деле очень люблю sed, но для использования в make-файле я очень ценю ясность. По этой причине, что-то в этом роде, вероятно, было бы тем, что я сделал бы сам:

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 будет выполнять линия, соединяющая вместо того, чтобы оставить это для оболочки. Вместо этого я представляю версию с разделением строк для удобства чтения.

0 голосов
/ 18 января 2020

Ваш 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...