When neither docker nor podman is available and usable, `make build`, `make lint.yaml`, and `lint.markdown` used to fail with confusing unhelpful error messages like `run: command not found` or `build: command not found` and in the case of build fail rather late in the process. This change lets make fail imuch more early and with a clear error message that a working installation of container runtime with podman or docker is required. The new logic is this: If docker is present and usable, use it otherwise, if podman is present and usable, use it otherwise, if DOCKERCMD is explicitly set to a usable command, use it. otherwise, error out at corresponding targets. In order to avoid adding new confusions, `make clean` and `make build` are additionally made robust to gracefully skip the image cleanup and image cleanuop steps, respectively, with an informational message instead of failingi hard when no usable container command is provided or detected. Assisted-by: GitHub Copilot Signed-off-by: Michael Adam <obnox@samba.org>
191 lines
6.3 KiB
Makefile
191 lines
6.3 KiB
Makefile
# Copyright 2016 The Rook Authors. All rights reserved.
|
|
#
|
|
# 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.
|
|
|
|
# remove default suffixes as we dont use them
|
|
.SUFFIXES:
|
|
|
|
override GOOS=linux
|
|
|
|
# include the common make file
|
|
SELF_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
|
|
include $(SELF_DIR)/../build/makelib/common.mk
|
|
|
|
# common.mk may define targets (e.g., directory creation). Since GNU Make uses the first target
|
|
# it sees as the default goal, this behavior may unintentionally mask the intended default goal
|
|
# of all (AKA build).
|
|
# Explicitly set the default goal for image builds to override this masking.
|
|
.DEFAULT_GOAL := all
|
|
|
|
# the registry used for cached images
|
|
CACHE_REGISTRY := cache
|
|
|
|
ifeq ($(GOARCH),amd64)
|
|
PLATFORM_ARCH = x86_64
|
|
else ifeq ($(GOARCH),arm64)
|
|
PLATFORM_ARCH = aarch64
|
|
else
|
|
$(error Unknown go architecture $(GOARCH))
|
|
endif
|
|
|
|
# if we are running inside the container get our own cid
|
|
ifeq ($(UNAME_S),Linux)
|
|
SELF_CID := $(shell cat /proc/self/cgroup | grep docker | grep -o -E '[0-9a-f]{64}' | head -n 1)
|
|
endif
|
|
|
|
CACHEBUST ?= 0
|
|
ifeq ($(CACHEBUST),1)
|
|
BUILD_ARGS += --no-cache
|
|
endif
|
|
|
|
V ?= 0
|
|
ifeq ($(V),1)
|
|
MAKEFLAGS += VERBOSE=1
|
|
else
|
|
MAKEFLAGS += --no-print-directory
|
|
BUILD_ARGS ?= -q
|
|
endif
|
|
|
|
PULL ?= 1
|
|
ifeq ($(PULL),1)
|
|
BUILD_BASE_ARGS += --pull
|
|
endif
|
|
export PULL
|
|
|
|
ifeq ($(origin IMAGE_OUTPUT_DIR),undefined)
|
|
IMAGE_OUTPUT_DIR := $(OUTPUT_DIR)/images/$(PLATFORM)
|
|
endif
|
|
|
|
BUILD_BASE_ARGS += $(BUILD_ARGS)
|
|
|
|
# =====================================================================================
|
|
# Targets
|
|
#
|
|
.PHONY: all build publish clean
|
|
all: build
|
|
|
|
build: do.build ## Build images for the host platform.
|
|
@if [ "$(BUILD_CONTAINER_IMAGE)" != "false" ] && $(DOCKERCMD) version >/dev/null 2>&1; then \
|
|
$(MAKE) cache.images; \
|
|
fi
|
|
|
|
clean: ## Remove all images created from the current build.
|
|
@if $(DOCKERCMD) version >/dev/null 2>&1; then \
|
|
$(MAKE) clean.build; \
|
|
else \
|
|
echo "=== skipping image cleanup because no usable container runtime was found"; \
|
|
fi
|
|
|
|
prune: cache.prune ## Prunes orphaned and cached images at the host level.
|
|
|
|
clean.images:
|
|
@for i in $(CLEAN_IMAGES); do \
|
|
if [ -n "$$($(DOCKERCMD) images -q $$i)" ]; then \
|
|
for c in $$($(DOCKERCMD) ps -a -q --no-trunc --filter=ancestor=$$i); do \
|
|
if [ "$$c" != "$(SELF_CID)" ]; then \
|
|
echo stopping and removing container $${c} referencing image $$i; \
|
|
$(DOCKERCMD) stop $${c}; \
|
|
$(DOCKERCMD) rm $${c}; \
|
|
fi; \
|
|
done; \
|
|
echo cleaning image $$i; \
|
|
$(DOCKERCMD) rmi $$i > /dev/null 2>&1 || true; \
|
|
fi; \
|
|
done
|
|
|
|
# this will clean everything for this build
|
|
clean.build:
|
|
@echo === cleaning images for $(BUILD_REGISTRY)
|
|
@$(MAKE) clean.images CLEAN_IMAGES="$(shell $(DOCKERCMD) images | grep -E '^$(BUILD_REGISTRY)/' | awk '{print $$1":"$$2}')"
|
|
|
|
# =====================================================================================
|
|
# Caching
|
|
#
|
|
|
|
# NOTE: in order to reduce built time, we maintain a cache
|
|
# of already built images. This cache contains images that can be used to help speed
|
|
# future docker build commands using docker's content addressable schemes. And also
|
|
# to avoid running builds like ceph when the contents have not changed.
|
|
# All cached images go in in a 'cache/' local registry and we follow an MRU caching
|
|
# policy -- keeping images that have been referenced around and evicting images
|
|
# that have to been referenced in a while (and according to a policy). Note we can
|
|
# not rely on the image's .CreatedAt date since docker only updates then when the
|
|
# image is created and not referenced. Instead we keep a date in the Tag.
|
|
|
|
# prune images that are at least this many hours old
|
|
PRUNE_HOURS ?= 48
|
|
|
|
# prune keeps at least this many images regardless of how old they are
|
|
PRUNE_KEEP ?= 24
|
|
|
|
PRUNE_DRYRUN ?= 0
|
|
|
|
CACHE_DATE_FORMAT := "%Y-%m-%d.%H%M%S"
|
|
CACHE_PRUNE_DATE := $(shell export TZ="UTC+$(PRUNE_HOURS)"; date +"$(CACHE_DATE_FORMAT)")
|
|
CACHE_TAG := $(shell date -u +"$(CACHE_DATE_FORMAT)")
|
|
|
|
cache.lookup:
|
|
@IMAGE_NAME=$${LOOKUP_IMAGE#*/} ;\
|
|
if [ -n "$$($(DOCKERCMD) images -q $(LOOKUP_IMAGE))" ]; then exit 0; fi; \
|
|
if [ -z "$$($(DOCKERCMD) images -q $(CACHE_REGISTRY)/$${IMAGE_NAME})" ]; then \
|
|
$(MAKE) $(MISS_TARGET); \
|
|
else \
|
|
$(DOCKERCMD) tag $$($(DOCKERCMD) images -q $(CACHE_REGISTRY)/$${IMAGE_NAME}) $(LOOKUP_IMAGE); \
|
|
fi;
|
|
|
|
cache.images:
|
|
@for i in $(CACHE_IMAGES); do \
|
|
IMGID=$$($(DOCKERCMD) images -q $$i); \
|
|
if [ -n "$$IMGID" ]; then \
|
|
echo === caching image $$i; \
|
|
CACHE_IMAGE=$(CACHE_REGISTRY)/$${i#*/}; \
|
|
$(DOCKERCMD) tag $$i $${CACHE_IMAGE}:$(CACHE_TAG); \
|
|
for r in $$($(DOCKERCMD) images --format "{{.ID}}#{{.Repository}}:{{.Tag}}" | grep $$IMGID | grep $(CACHE_REGISTRY)/ | grep -v $${CACHE_IMAGE}:$(CACHE_TAG)); do \
|
|
$(DOCKERCMD) rmi $${r#*#} > /dev/null 2>&1 || true; \
|
|
done; \
|
|
fi; \
|
|
done
|
|
|
|
# prune removes old cached images
|
|
cache.prune:
|
|
@echo === pruning images older than $(PRUNE_HOURS) hours
|
|
@echo === keeping a minimum of $(PRUNE_KEEP) images
|
|
@EXPIRED=$$($(DOCKERCMD) images --format "{{.Tag}}#{{.Repository}}:{{.Tag}}" \
|
|
| grep -E '$(CACHE_REGISTRY)/' \
|
|
| sort -r \
|
|
| awk -v i=0 -v cd="$(CACHE_PRUNE_DATE)" -F "#" '{if ($$1 <= cd && i >= $(PRUNE_KEEP)) print $$2; i++ }') &&\
|
|
for i in $$EXPIRED; do \
|
|
echo removing expired cache image $$i; \
|
|
[ $(PRUNE_DRYRUN) = 1 ] || $(DOCKERCMD) rmi $$i > /dev/null 2>&1 || true; \
|
|
done
|
|
@for i in $$($(DOCKERCMD) images -q -f dangling=true); do \
|
|
echo removing dangling image $$i; \
|
|
$(DOCKERCMD) rmi $$i > /dev/null 2>&1 || true; \
|
|
done
|
|
|
|
# =====================================================================================
|
|
# Debugging nukes all images
|
|
#
|
|
debug.nuke:
|
|
@for c in $$($(DOCKERCMD) ps -a -q --no-trunc); do \
|
|
if [ "$$c" != "$(SELF_CID)" ]; then \
|
|
echo stopping and removing container $${c}; \
|
|
$(DOCKERCMD) stop $${c}; \
|
|
$(DOCKERCMD) rm $${c}; \
|
|
fi; \
|
|
done
|
|
@for i in $$($(DOCKERCMD) images -q); do \
|
|
echo removing image $$i; \
|
|
$(DOCKERCMD) rmi -f $$i > /dev/null 2>&1; \
|
|
done
|