6060TESTARGS =
6161endif
6262
63- ARCH := $(if $(GOARCH ) ,$(GOARCH ) ,$(shell go env GOARCH) )
64-
6563# Specific packages can be excluded from each of the tests below by setting the *_FILTER_CMD variables
6664# to something like "| grep -v 'github.com/kubernetes-csi/project/pkg/foobar'". See usage below.
6765
68- build-% : check-go-version-go
66+ # BUILD_PLATFORMS contains a set of <os> <arch> <suffix> triplets,
67+ # separated by semicolon. An empty variable or empty entry (= just a
68+ # semicolon) builds for the default platform of the current Go
69+ # toolchain.
70+ BUILD_PLATFORMS =
71+
72+ # This builds each command (= the sub-directories of ./cmd) for the target platform(s)
73+ # defined by BUILD_PLATFORMS.
74+ $(CMDS:% =build-%): build-%: check-go-version-go
6975 mkdir -p bin
70- CGO_ENABLED=0 GOOS=linux go build $(GOFLAGS_VENDOR ) -a -ldflags ' -X main.version=$(REV) -extldflags "-static"' -o ./bin/$* ./cmd/$*
71- if [ " $$ ARCH" = " amd64" ]; then \
72- CGO_ENABLED=0 GOOS=windows go build $(GOFLAGS_VENDOR ) -a -ldflags ' -X main.version=$(REV) -extldflags "-static"' -o ./bin/$* .exe ./cmd/$* ; \
73- CGO_ENABLED=0 GOOS=linux GOARCH=ppc64le go build $(GOFLAGS_VENDOR ) -a -ldflags ' -X main.version=$(REV) -extldflags "-static"' -o ./bin/$* -ppc64le ./cmd/$* ; \
74- fi
76+ echo '$(BUILD_PLATFORMS)' | tr ';' '\n' | while read -r os arch suffix; do \
77+ if ! (set -x; CGO_ENABLED=0 GOOS="$$os" GOARCH="$$arch" go build $(GOFLAGS_VENDOR) -a -ldflags '-X main.version=$(REV) -extldflags "-static"' -o "./bin/$*$$suffix" ./cmd/$*); then \
78+ echo "Building $* for GOOS=$$os GOARCH=$$arch failed, see error(s) above."; \
79+ exit 1; \
80+ fi; \
81+ done
7582
76- container-% : build-%
83+ $(CMDS:% =container-%): container-%: build-%
7784 docker build -t $*:latest -f $(shell if [ -e ./cmd/$*/Dockerfile ]; then echo ./cmd/$*/Dockerfile; else echo Dockerfile; fi) --label revision=$(REV) .
7885
79- push-% : container-%
86+ $(CMDS:% =push-%): push-%: container-%
8087 set -ex; \
8188 push_image () { \
8289 docker tag $*:latest $(IMAGE_NAME):$$tag; \
@@ -98,6 +105,77 @@ build: $(CMDS:%=build-%)
98105container : $(CMDS:%=container-% )
99106push : $(CMDS:%=push-% )
100107
108+ # Additional parameters are needed when pushing to a local registry,
109+ # see https://github.com/docker/buildx/issues/94.
110+ # However, that then runs into https://github.com/docker/cli/issues/2396.
111+ #
112+ # What works for local testing is:
113+ # make push-multiarch PULL_BASE_REF=master REGISTRY_NAME=<your account on dockerhub.io> BUILD_PLATFORMS="linux amd64; windows amd64 .exe; linux ppc64le -ppc64le; linux s390x -s390x"
114+ DOCKER_BUILDX_CREATE_ARGS ?=
115+
116+ # This target builds a multiarch image for one command using Moby BuildKit builder toolkit.
117+ # Docker Buildx is included in Docker 19.03.
118+ #
119+ # ./cmd/<command>/Dockerfile[.Windows] is used if found, otherwise Dockerfile[.Windows].
120+ # It is currently optional: if no such file exists, Windows images are not included,
121+ # even when Windows is listed in BUILD_PLATFORMS. That way, projects can test that
122+ # Windows binaries can be built before adding a Dockerfile for it.
123+ #
124+ # BUILD_PLATFORMS determines which individual images are included in the multiarch image.
125+ # PULL_BASE_REF must be set to 'master', 'release-x.y', or a tag name, and determines
126+ # the tag for the resulting multiarch image.
127+ $(CMDS:% =push-multiarch-%): push-multiarch-%: check-pull-base-ref build-%
128+ set -ex; \
129+ DOCKER_CLI_EXPERIMENTAL=enabled; \
130+ export DOCKER_CLI_EXPERIMENTAL; \
131+ docker buildx create $(DOCKER_BUILDX_CREATE_ARGS) --use --name multiarchimage-buildertest; \
132+ trap "docker buildx rm multiarchimage-buildertest" EXIT; \
133+ dockerfile_linux=$$(if [ -e ./cmd/$*/Dockerfile ]; then echo ./cmd/$*/Dockerfile; else echo Dockerfile; fi); \
134+ dockerfile_windows=$$(if [ -e ./cmd/$*/Dockerfile.Windows ]; then echo ./cmd/$*/Dockerfile.Windows; else echo Dockerfile.Windows; fi); \
135+ if [ '$(BUILD_PLATFORMS)' ]; then build_platforms='$(BUILD_PLATFORMS)'; else build_platforms="linux amd64"; fi; \
136+ if ! [ -f "$$dockerfile_windows" ]; then \
137+ build_platforms="$$(echo "$$build_platforms" | sed -e 's/windows *[^ ]* *.exe//g' -e 's/; *;/;/g')"; \
138+ fi; \
139+ pushMultiArch () { \
140+ tag=$$1; \
141+ echo "$$build_platforms" | tr ';' '\n' | while read -r os arch suffix; do \
142+ docker buildx build --push \
143+ --tag $(IMAGE_NAME):$$arch-$$os-$$tag \
144+ --platform=$$os/$$arch \
145+ --file $$(eval echo \$${dockerfile_$$os}) \
146+ --build-arg binary=./bin/$*$$suffix \
147+ --label revision=$(REV) \
148+ .; \
149+ done; \
150+ images=$$(echo "$$build_platforms" | tr ';' '\n' | while read -r os arch suffix; do echo $(IMAGE_NAME):$$arch-$$os-$$tag; done); \
151+ docker manifest create --amend $(IMAGE_NAME):$$tag $$images; \
152+ docker manifest push -p $(IMAGE_NAME):$$tag; \
153+ }; \
154+ if [ $(PULL_BASE_REF) = "master" ]; then \
155+ : "creating or overwriting canary image"; \
156+ pushMultiArch canary; \
157+ elif echo $(PULL_BASE_REF) | grep -q -e 'release-*' ; then \
158+ : "creating or overwriting canary image for release branch"; \
159+ release_canary_tag=$$(echo $(PULL_BASE_REF) | cut -f2 -d '-')-canary; \
160+ pushMultiArch $$release_canary_tag; \
161+ elif docker pull $(IMAGE_NAME):$(PULL_BASE_REF) 2>&1 | tee /dev/stderr | grep -q "manifest for $(IMAGE_NAME):$(PULL_BASE_REF) not found"; then \
162+ : "creating release image"; \
163+ pushMultiArch $(PULL_BASE_REF); \
164+ else \
165+ : "ERROR: release image $(IMAGE_NAME):$(PULL_BASE_REF) already exists: a new tag is required!"; \
166+ exit 1; \
167+ fi
168+
169+ .PHONY : check-pull-base-ref
170+ check-pull-base-ref :
171+ if ! [ " $( PULL_BASE_REF) " ]; then \
172+ echo >&2 " ERROR: PULL_BASE_REF must be set to 'master', 'release-x.y', or a tag name." ; \
173+ exit 1; \
174+ fi
175+
176+ .PHONY : push-multiarch
177+ push-multiarch : $(CMDS:%=push-multiarch-% )
178+
101179clean :
102180 -rm -rf bin
103181
0 commit comments