diff --git a/include/image-commands.mk b/include/image-commands.mk index e6b526dc56a..8badecf1d63 100644 --- a/include/image-commands.mk +++ b/include/image-commands.mk @@ -466,6 +466,10 @@ define Build/fit $(call Build/fit-image,$(1)) endef +# A slot name from the basename alone collides between two targets that share +# it in different directories below $(KDIR). +cache_slot = $(KDIR)/cache/$(subst /,_,$(patsubst $(KDIR)/%,%,$(1))) + define Build/libdeflate-gzip $(STAGING_DIR_HOST)/bin/libdeflate-gzip -f -12 -c $@ $(1) > $@.new @mv $@.new $@ @@ -593,7 +597,8 @@ define Build/lzma endef define Build/lzma-no-dict - $(STAGING_DIR_HOST)/bin/lzma e $@ $(1) $@.new + $(CACHE_RUN) $(call cache_slot,$@).lzma $@ $@.new \ + $(STAGING_DIR_HOST)/bin/lzma e $@ $(1) $@.new @mv $@.new $@ endef diff --git a/include/kernel-defaults.mk b/include/kernel-defaults.mk index 02a4f9cde64..ffb8a5a3e0b 100644 --- a/include/kernel-defaults.mk +++ b/include/kernel-defaults.mk @@ -204,6 +204,11 @@ define Kernel/PrepareConfigPerRootfs endef ifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),) +# $1: Per Device Rootfs ID +# $2: suffix of the compressed initrd +Kernel/CacheInitrd = $(CACHE_RUN) $(KERNEL_BUILD_DIR)/cache/initrd$(1).cpio.$(2) \ + $(KERNEL_BUILD_DIR)/initrd$(1).cpio $(KERNEL_BUILD_DIR)/initrd$(1).cpio.$(2) + # $1: Custom TARGET_DIR. If omitted TARGET_DIR is used. # $2: If defined Generate Per Rootfs Kernel Directory and use it # For Separate Initramfs, the regular kernel is used as is, as its config @@ -226,16 +231,21 @@ define Kernel/CompileImage/Initramfs $(if $(CONFIG_TARGET_INITRAMFS_COMPRESSION_BZIP2), \ $(STAGING_DIR_HOST)/bin/bzip2 -9 -c < $(KERNEL_BUILD_DIR)/initrd$(2).cpio > $(KERNEL_BUILD_DIR)/initrd$(2).cpio.bzip2;) \ $(if $(CONFIG_TARGET_INITRAMFS_COMPRESSION_GZIP), \ - $(STAGING_DIR_HOST)/bin/libdeflate-gzip -n -f -S .gzip -12 $(KERNEL_BUILD_DIR)/initrd$(2).cpio;) \ + $(call Kernel/CacheInitrd,$(2),gzip) \ + $(STAGING_DIR_HOST)/bin/libdeflate-gzip -n -k -f -S .gzip -12 $(KERNEL_BUILD_DIR)/initrd$(2).cpio;) \ $(if $(CONFIG_TARGET_INITRAMFS_COMPRESSION_LZ4), \ + $(call Kernel/CacheInitrd,$(2),lz4) \ $(STAGING_DIR_HOST)/bin/lz4c -l -c1 -fz --favor-decSpeed $(KERNEL_BUILD_DIR)/initrd$(2).cpio $(KERNEL_BUILD_DIR)/initrd$(2).cpio.lz4;) \ $(if $(CONFIG_TARGET_INITRAMFS_COMPRESSION_LZMA), \ + $(call Kernel/CacheInitrd,$(2),lzma) \ $(STAGING_DIR_HOST)/bin/lzma e -lc1 -lp2 -pb2 $(KERNEL_BUILD_DIR)/initrd$(2).cpio $(KERNEL_BUILD_DIR)/initrd$(2).cpio.lzma;) \ $(if $(CONFIG_TARGET_INITRAMFS_COMPRESSION_LZO), \ $(STAGING_DIR_HOST)/bin/lzop -9 -f $(KERNEL_BUILD_DIR)/initrd$(2).cpio;) \ $(if $(CONFIG_TARGET_INITRAMFS_COMPRESSION_XZ), \ - $(STAGING_DIR_HOST)/bin/xz -T$(if $(filter 1,$(NPROC)),2,0) -9 -fz --check=crc32 $(KERNEL_BUILD_DIR)/initrd$(2).cpio;) \ + $(call Kernel/CacheInitrd,$(2),xz) \ + $(STAGING_DIR_HOST)/bin/xz -T$(if $(filter 1,$(NPROC)),2,0) -9 -fzk --check=crc32 $(KERNEL_BUILD_DIR)/initrd$(2).cpio;) \ $(if $(CONFIG_TARGET_INITRAMFS_COMPRESSION_ZSTD), \ + $(call Kernel/CacheInitrd,$(2),zstd) \ $(STAGING_DIR_HOST)/bin/zstd -T0 -f -o $(KERNEL_BUILD_DIR)/initrd$(2).cpio.zstd $(KERNEL_BUILD_DIR)/initrd$(2).cpio;) \ }, gen-cpio$(2));,\ $(KERNEL_MAKE) $(if $(2),-C $(LINUX_DIR)$(2)) $(KERNEL_MAKEOPTS_IMAGE) $(if $(KERNELNAME),$(KERNELNAME),all);) \ diff --git a/rules.mk b/rules.mk index 0fd2ccf5123..adae18b97fc 100644 --- a/rules.mk +++ b/rules.mk @@ -319,6 +319,7 @@ TARGET_CC:=$(TARGET_CROSS)gcc TARGET_CXX:=$(TARGET_CROSS)g++ TARGET_LD:=$(TARGET_CROSS)ld.$(TARGET_LINKER) KPATCH:=$(SCRIPT_DIR)/patch-kernel.sh +CACHE_RUN:=$(SCRIPT_DIR)/cache-run.sh FILECMD:=$(STAGING_DIR_HOST)/bin/file SED:=$(STAGING_DIR_HOST)/bin/sed -i -e ESED:=$(STAGING_DIR_HOST)/bin/sed -E -i -e diff --git a/scripts/cache-run.sh b/scripts/cache-run.sh new file mode 100755 index 00000000000..9c1cc87815b --- /dev/null +++ b/scripts/cache-run.sh @@ -0,0 +1,55 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-only +# +# Usage: cache-run.sh [...] +# +# Run a command that creates from . If the content of the +# input, the command line and the command binary are the same as in the +# last run for , copy the stored output instead of running the +# command again. Only use it for deterministic commands, such as the +# compression of kernel and initramfs images, which are slow and get the +# same input on most rebuilds. + +[ $# -ge 4 ] || { + echo "Usage: $0 [...]" >&2 + exit 1 +} +[ -n "$MKHASH" ] || { + echo "$0: MKHASH is not set" >&2 + exit 1 +} + +slot="$1" +input="$2" +output="$3" +shift 3 + +[ -f "$input" ] || { + echo "$0: $input does not exist" >&2 + exit 1 +} +tool="$(command -v "$1")" || { + echo "$0: $1 not found" >&2 + exit 1 +} + +input_hash="$("$MKHASH" sha256 "$input")" || exit 1 +tool_hash="$("$MKHASH" sha256 "$tool")" || exit 1 +key="$(printf '%s\n' "$input_hash" "$tool_hash" "$@" | "$MKHASH" sha256)" || exit 1 + +mkdir -p "$(dirname "$slot")" || exit 1 + +# Two image recipes can hold the same slot at the same time. Hold a lock over +# the whole slot, so that a reader never sees a half written pair. +exec 9>"$slot.lock" || exit 1 +flock 9 || exit 1 + +if [ -f "$slot.data" ] && [ "$(cat "$slot.key" 2>/dev/null)" = "$key" ]; then + cmp -s "$slot.data" "$output" && exit 0 + exec cp "$slot.data" "$output" +fi + +rm -f "$slot.key" +"$@" || exit +cp "$output" "$slot.data.new" && mv -f "$slot.data.new" "$slot.data" && + echo "$key" > "$slot.key"