diff --git a/include/host-build.mk b/include/host-build.mk index b459c0ab18c..a0419d8e37a 100644 --- a/include/host-build.mk +++ b/include/host-build.mk @@ -109,9 +109,12 @@ HOST_MAKE_FLAGS = HOST_CONFIGURE_CMD = $(BASH) ./configure -ifeq ($(HOST_OS),Darwin) - HOST_CONFIG_SITE:=$(INCLUDE_DIR)/site/darwin -endif +HOST_CONFIG_SITE_BASE:=$(if $(filter Darwin,$(HOST_OS)),$(INCLUDE_DIR)/site/darwin) +HOST_CONFIG_SITE:=$(INCLUDE_DIR)/site/cache +# Set HOST_CONFIGURE_CACHE:=1 to keep the autoconf result cache of this host +# build between builds. Read the note above PKG_CONFIGURE_CACHE in package.mk +# before you opt a package in. +HOST_CONFIGURE_CACHE_FILE = $(CONFIGURE_CACHE_BASE)/$(notdir $(BUILD_DIR_HOST))-$(host_cc_id)/$(notdir $(HOST_BUILD_DIR))/$(call strhash,$(HOST_CONFIGURE_ARGS) $(HOST_CONFIGURE_VARS)).cache define Host/Configure/Default $(if $(HOST_CONFIGURE_PARALLEL),+)(cd $(HOST_BUILD_DIR)/$(3); \ @@ -170,6 +173,7 @@ define Host/Exports/Default $(1) : export PKG_CONFIG_LIBDIR=$$(HOST_BUILD_PREFIX)/lib/pkgconfig $(1) : export GIT_CEILING_DIRECTORIES=$$(BUILD_DIR_HOST) $(if $(HOST_CONFIG_SITE),$(1) : export CONFIG_SITE:=$(HOST_CONFIG_SITE)) + $(if $(HOST_CONFIG_SITE_BASE),$(1) : export CONFIG_SITE_BASE:=$(HOST_CONFIG_SITE_BASE)) $(if $(IS_PACKAGE_BUILD),$(1) : export PATH=$$(TARGET_PATH_PKG)) endef Host/Exports=$(Host/Exports/Default) @@ -192,6 +196,7 @@ ifndef DUMP $(call BuildTimeLog,end,prepare) $(call Host/Exports,$(HOST_STAMP_CONFIGURED)) + $(if $(and $(CONFIGURE_CACHE_BASE),$(HOST_CONFIGURE_CACHE)),$(HOST_STAMP_CONFIGURED) : export CONFIGURE_CACHE_FILE:=$(HOST_CONFIGURE_CACHE_FILE)) $(HOST_STAMP_CONFIGURED): $(HOST_STAMP_PREPARED) $(call BuildTimeLog,begin,configure) $(foreach hook,$(Hooks/HostConfigure/Pre),$(call $(hook))$(sep)) diff --git a/include/package.mk b/include/package.mk index fe213f80411..4ff79658f66 100644 --- a/include/package.mk +++ b/include/package.mk @@ -188,11 +188,19 @@ include $(INCLUDE_DIR)/autotools.mk _pkg_target:=$(if $(QUILT),,.) override MAKEFLAGS= -CONFIG_SITE:=$(INCLUDE_DIR)/site/$(ARCH) +CONFIG_SITE_BASE:=$(INCLUDE_DIR)/site/$(ARCH) +CONFIG_SITE:=$(INCLUDE_DIR)/site/cache +# Set PKG_CONFIGURE_CACHE:=1 to keep the autoconf result cache of this package +# between builds. Opt in only after you check that the package configures the +# same way with a warm cache as it does without one. A configure script that +# changes CPPFLAGS or CFLAGS inside an AC_CACHE_VAL body is not safe: a warm +# cache restores the value and skips the change. Compare the generated +# Makefile, not only config.h, because that is where the difference shows. +PKG_CONFIGURE_CACHE_FILE = $(CONFIGURE_CACHE_BASE)/$(TOOLCHAIN_DIR_NAME)/$(notdir $(PKG_BUILD_DIR))/$(call strhash,$(CONFIGURE_ARGS) $(CONFIGURE_VARS)).cache CUR_MAKEFILE:=$(filter-out Makefile,$(firstword $(MAKEFILE_LIST))) SUBMAKE:=$(NO_TRACE_MAKE) $(if $(CUR_MAKEFILE),-f $(CUR_MAKEFILE)) PKG_CONFIG_PATH=$(STAGING_DIR)/usr/lib/pkgconfig:$(STAGING_DIR)/usr/share/pkgconfig -unexport QUIET CONFIG_SITE +unexport QUIET CONFIG_SITE CONFIG_SITE_BASE ifeq ($(DUMP)$(filter prereq clean refresh update,$(MAKECMDGOALS)),) ifneq ($(if $(QUILT),,$(CONFIG_AUTOREBUILD)),) @@ -224,6 +232,7 @@ define Build/Exports/Default $(1) : export STAGING_PREFIX=$$(STAGING_DIR)/usr $(1) : export PATH=$$(TARGET_PATH_PKG) $(1) : export CONFIG_SITE:=$$(CONFIG_SITE) + $(1) : export CONFIG_SITE_BASE:=$$(CONFIG_SITE_BASE) $(1) : export PKG_CONFIG_PATH:=$$(PKG_CONFIG_PATH) $(1) : export PKG_CONFIG_LIBDIR:=$$(PKG_CONFIG_PATH) $(1) : export GIT_CEILING_DIRECTORIES:=$$(BUILD_DIR) @@ -258,6 +267,7 @@ define Build/CoreTargets $(call BuildTimeLog,end,prepare) $(call Build/Exports,$(STAMP_CONFIGURED)) + $(if $(and $(CONFIGURE_CACHE_BASE),$(PKG_CONFIGURE_CACHE)),$(STAMP_CONFIGURED) : export CONFIGURE_CACHE_FILE:=$(PKG_CONFIGURE_CACHE_FILE)) $(STAMP_CONFIGURED): $(STAMP_PREPARED) $(STAMP_CONFIGURED_DEPENDS) $(call BuildTimeLog,begin,configure) rm -f $(STAMP_CONFIGURED_WILDCARD) diff --git a/include/site/cache b/include/site/cache new file mode 100644 index 00000000000..7a19181c6fe --- /dev/null +++ b/include/site/cache @@ -0,0 +1,44 @@ +#!/bin/sh +# +# Points the autoconf result cache at the file that the build system keeps for +# this package. Set CONFIGURE_CACHE_DIR to switch this on; see rules.mk. +# +# configure sources the site scripts before it loads the cache, so setting +# cache_file here has the same effect as --cache-file, without a configure +# argument that a hand written configure script would choke on. + +# Autoconf before 2.70 reads CONFIG_SITE as a single path name, so the site +# script of the architecture cannot be a second word in it. Load it from here. +if [ -n "$CONFIG_SITE_BASE" ] && [ -r "$CONFIG_SITE_BASE" ]; then + . "$CONFIG_SITE_BASE" +fi + +if [ -n "$CONFIGURE_CACHE_FILE" ]; then + configure_cache_dir=${CONFIGURE_CACHE_FILE%/*} + + mkdir -p "$configure_cache_dir" 2>/dev/null + + # A change of the configure arguments gives a new file name. Drop the + # file of the previous arguments so that the directory holds one cache + # per package. + for configure_cache_stale in "$configure_cache_dir"/*.cache; do + [ -f "$configure_cache_stale" ] || continue + [ "$configure_cache_stale" = "$CONFIGURE_CACHE_FILE" ] || rm -f "$configure_cache_stale" + done + + # A cached program path outlives a wipe of staging_dir, so it can name a + # tool that is gone, or miss one that the build installed since. Drop + # those answers and let configure search PATH again. A search runs no + # compiler, and the compiler is where the time goes. + if [ -f "$CONFIGURE_CACHE_FILE" ]; then + grep -v -e '^ac_cv_path_' -e '^ac_cv_prog_' \ + "$CONFIGURE_CACHE_FILE" > "$CONFIGURE_CACHE_FILE.tmp" + mv -f "$CONFIGURE_CACHE_FILE.tmp" "$CONFIGURE_CACHE_FILE" + fi + + cache_file=$CONFIGURE_CACHE_FILE + + # A configure script that runs further configure scripts must not hand + # this cache to them. Their checks run with different flags. + unset CONFIGURE_CACHE_FILE configure_cache_dir configure_cache_stale +fi diff --git a/rules.mk b/rules.mk index adae18b97fc..db5b2a91c63 100644 --- a/rules.mk +++ b/rules.mk @@ -206,6 +206,33 @@ else BuildTimeLog = endif +# A package that sets PKG_CONFIGURE_CACHE or HOST_CONFIGURE_CACHE keeps the +# autoconf result cache of its configure checks, so a package that is cleaned +# and built again reads the answers instead of running the checks. The cache +# lives in tmp/, which dirclean removes, so a full reset of the tree starts +# from cold answers. +# +# The path covers everything that changes an answer: the toolchain for a target +# package, the host compiler for a host package, and a hash of the configure +# arguments and build flags for both. autoconf refuses a cache that it wrote +# with different flags on top of that. +# +# A package must opt in, because a cache is only correct for a configure script +# that keeps its side effects outside the AC_CACHE_VAL body. ncurses appends +# -D_XOPEN_SOURCE to CPPFLAGS inside that body, so a cached run restores the +# value, skips the append and then fails to compile. Read the note above +# PKG_CONFIGURE_CACHE in package.mk before you opt a package in. +# +# Set CONFIGURE_CACHE_DIR to a path of your own to keep the cache elsewhere, or +# to the empty string to run every check again. +ifeq ($(origin CONFIGURE_CACHE_DIR),undefined) + CONFIGURE_CACHE_DIR:=$(TMP_DIR)/configure-cache +endif +ifneq ($(CONFIGURE_CACHE_DIR),) + CONFIGURE_CACHE_BASE:=$(if $(filter /%,$(CONFIGURE_CACHE_DIR)),$(CONFIGURE_CACHE_DIR),$(TOPDIR)/$(CONFIGURE_CACHE_DIR)) + strhash=$(shell printf '%s' '$(subst ','\'',$(1))' | $(MKHASH) md5) +endif + BUILD_DIR_HOST:=$(if $(IS_PACKAGE_BUILD),$(BUILD_DIR_BASE)/hostpkg,$(BUILD_DIR_BASE)/host) STAGING_DIR_HOST:=$(abspath $(STAGING_DIR)/../host) STAGING_DIR_HOSTPKG:=$(abspath $(STAGING_DIR)/../hostpkg) @@ -359,6 +386,14 @@ export TARGET_CXX_NOCACHE export HOSTCC_NOCACHE export HOSTCXX_NOCACHE +# A new host compiler behind an unchanged name changes what a configure check +# answers, and autoconf does not detect that. gcc 14 turned an implicit function +# declaration into an error, which changed many of those answers. The configure +# cache of a host package is therefore keyed on the compiler as well. prereq +# builds mkhash and links the compiler into staging_dir, so both exist by the +# time a configure recipe asks for this. +host_cc_id = $(if $(filter undefined,$(origin __host_cc_id)),$(eval __host_cc_id:=$(shell $(HOSTCC_NOCACHE) --version 2>/dev/null | head -1 | $(MKHASH) md5)))$(__host_cc_id) + ifneq ($(CONFIG_CCACHE),) TARGET_CC:= ccache $(TARGET_CC) TARGET_CXX:= ccache $(TARGET_CXX)