diff options
author | Adin Scannell <ascannell@google.com> | 2020-12-09 09:31:44 -0800 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2020-12-09 15:53:23 -0800 |
commit | a855a814d601a4c30f26743ef1bf016df956e042 (patch) | |
tree | b017a831f6eff9c9734663114bbdf39fc4b6ffc7 /tools/images.mk | |
parent | f6cb96bd57dec4e3baa8c57ccdeb0f1d8706b682 (diff) |
Refactor the Makefile to avoid recursive Make.
Recursive make is difficult to follow and debug. Drop this by using
internal functions, which, while difficult, are easier than trying to
following recursive invokations.
Further simplify the Makefile by collapsing the image bits and removing
the tools/vm directory, which is effectively unused.
Fixes #4952
PiperOrigin-RevId: 346569133
Diffstat (limited to 'tools/images.mk')
-rw-r--r-- | tools/images.mk | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/tools/images.mk b/tools/images.mk new file mode 100644 index 000000000..ef72f2b80 --- /dev/null +++ b/tools/images.mk @@ -0,0 +1,165 @@ +#!/usr/bin/make -f + +# Copyright 2018 The gVisor Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +## +## Docker image targets. +## +## Images used by the tests must also be built and available locally. +## The canonical test targets defined below will automatically load +## relevant images. These can be loaded or built manually via these +## targets. +## +## (*) Note that you may provide an ARCH parameter in order to build +## and load images from an alternate archiecture (using qemu). When +## bazel is run as a server, this has the effect of running an full +## cross-architecture chain, and can produce cross-compiled binaries. +## + +# ARCH is the architecture used for the build. This may be overriden at the +# command line in order to perform a cross-build (in a limited capacity). +ARCH := $(shell uname -m) +ifneq ($(ARCH),$(shell uname -m)) +DOCKER_PLATFORM_ARGS := --platform=$(ARCH) +else +DOCKER_PLATFORM_ARGS := +endif + +# Note that the image prefixes used here must match the image mangling in +# runsc/testutil.MangleImage. Names are mangled in this way to ensure that all +# tests are using locally-defined images (that are consistent and idempotent). +REMOTE_IMAGE_PREFIX ?= gcr.io/gvisor-presubmit +LOCAL_IMAGE_PREFIX ?= gvisor.dev/images +ALL_IMAGES := $(subst /,_,$(subst images/,,$(shell find images/ -name Dockerfile -o -name Dockerfile.$(ARCH) | xargs -n 1 dirname | uniq))) +SUB_IMAGES := $(foreach image,$(ALL_IMAGES),$(if $(findstring _,$(image)),$(image),)) +IMAGE_GROUPS := $(sort $(foreach image,$(SUB_IMAGES),$(firstword $(subst _, ,$(image))))) + +define expand_group = +load-$(1): $$(patsubst $(1)_%, load-$(1)_%, $$(filter $(1)_%,$$(ALL_IMAGES))) + @ +.PHONY: load-$(1) +push-$(1): $$(patsubst $(1)_%, push-$(1)_%, $$(filter $(1)_%,$$(ALL_IMAGES))) + @ +.PHONY: push-$(1) +endef +$(foreach group,$(IMAGE_GROUPS),$(eval $(call expand_group,$(group)))) + +list-all-images: ## List all images. + @for image in $(ALL_IMAGES); do echo $${image}; done +.PHONY: list-all-images + +load-all-images: ## Load all images. +load-all-images: $(patsubst %,load-%,$(ALL_IMAGES)) +.PHONY: load-all-images + +push-all-images: ## Push all images. +push-all-images: $(patsubst %,push-%,$(ALL_IMAGES)) +.PHONY: push-all-images + +# path and dockerfile are used to extract the relevant path and dockerfile +# (depending on what's available for the given architecture). +path = images/$(subst _,/,$(1)) +dockerfile = $$(if [ -f "$(call path,$(1))/Dockerfile.$(ARCH)" ]; then echo Dockerfile.$(ARCH); else echo Dockerfile; fi) + +# The tag construct is used to memoize the image generated (see README.md). +# This scheme is used to enable aggressive caching in a central repository, but +# ensuring that images will always be sourced using the local files. +tag = $(shell cd images && find $(subst _,/,$(1)) -type f | sort | xargs -n 1 sha256sum | sha256sum - | cut -c 1-16) +remote_image = $(REMOTE_IMAGE_PREFIX)/$(subst _,/,$(1))_$(ARCH) +local_image = $(LOCAL_IMAGE_PREFIX)/$(subst _,/,$(1)) + +# Include all existing images as targets here. +# +# Note that we use a _ for the tag separator, instead of :, as the latter is +# interpreted by Make, unfortunately. tag_expand expands the generic rules to +# tag-specific targets. These is needed to provide sensible targets for load +# below, with caching. Basically, if there is a rule generated here, then the +# load will be skipped. If there is no load generated here, then the default +# rule for load will kick in. +# +# Note that if this rule does not successfully rule, we will simply have +# additional Docker pull commands that run for all images that are already +# pulled. No real harm done. +EXISTING_IMAGES = $(shell docker images --format '{{.Repository}}_{{.Tag}}' | grep -v '<none>') +define existing_image_rule = +loaded0_$(1)=load-$$(1): tag-$$(1) # Already available. +loaded1_$(1)=.PHONY: load-$$(1) +endef +$(foreach image, $(EXISTING_IMAGES), $(eval $(call existing_image_rule,$(image)))) +define tag_expand_rule = +$(eval $(loaded0_$(call local_image,$(1))_$(call tag,$(1)))) +$(eval $(loaded1_$(call local_image,$(1))_$(call tag,$(1)))) +endef +$(foreach image, $(ALL_IMAGES), $(eval $(call tag_expand_rule,$(image)))) + +# tag tags a local image. This applies both the hash-based tag from above to +# ensure that caching works as expected, as well as the "latest" tag that is +# used by the tests. +local_tag = \ + docker tag $(call remote_image,$(1)):$(call tag,$(1)) $(call local_image,$(1)):$(call tag,$(1)) && \ + docker tag $(call remote_image,$(1)):$(call tag,$(1)) $(call local_image,$(1)) +tag-%: ## Tag a local image. + @$(call local_tag,$*) + +# pull forces the image to be pulled. +pull = \ + $(call header,PULL $(1)) && \ + docker pull $(DOCKER_PLATFORM_ARGS) $(call remote_image,$(1)):$(call tag,$(1)) && \ + $(call local_tag,$(1)) +pull-%: register-cross ## Force a repull of the image. + @$(call pull,$*) + +# rebuild builds the image locally. Only the "remote" tag will be applied. Note +# we need to explicitly repull the base layer in order to ensure that the +# architecture is correct. Note that we use the term "rebuild" here to avoid +# conflicting with the bazel "build" terminology, which is used elsewhere. +rebuild = \ + $(call header,REBUILD $(1)) && \ + (T=$$(mktemp -d) && cp -a $(call path,$(1))/* $$T && \ + $(foreach image,$(shell grep FROM "$(call path,$(1))/$(call dockerfile,$(1))" 2>/dev/null | cut -d' ' -f2),docker pull $(DOCKER_PLATFORM_ARGS) $(image) &&) \ + docker build $(DOCKER_PLATFORM_ARGS) \ + -f "$$T/$(call dockerfile,$(1))" \ + -t "$(call remote_image,$(1)):$(call tag,$(1))" \ + $$T && \ + rm -rf $$T) && \ + $(call local_tag,$(1)) +rebuild-%: register-cross ## Force rebuild an image locally. + @$(call rebuild,$*) + +# load will either pull the "remote" or build it locally. This is the preferred +# entrypoint, as it should never fail. The local tag should always be set after +# this returns (either by the pull or the build). +load-%: register-cross ## Pull or build an image locally. + @($(call pull,$*)) || ($(call rebuild,$*)) + +# push pushes the remote image, after either pulling (to validate that the tag +# already exists) or building manually. Note that this generic rule will match +# the fully-expanded remote image tag. +push-%: load-% ## Push a given image. + @docker push $(call remote_image,$*):$(call tag,$*) + +# register-cross registers the necessary qemu binaries for cross-compilation. +# This may be used by any target that may execute containers that are not the +# native format. Note that this will only apply on the first execution. +register-cross: +ifneq ($(ARCH),$(shell uname -m)) +ifeq (,$(wildcard /proc/sys/fs/binfmt_misc/qemu-*)) + @docker run --rm --privileged multiarch/qemu-user-static --reset --persistent yes +else + @ +endif +else + @ +endif |