build: cache the autoconf results of a package that opts in

A configure run spends its time on the compiler invocations behind its
checks, not on the shell. tools/m4 runs the compiler 633 times and takes
24 seconds. ccache does not help, because it refuses to cache an
autoconf conftest compile at all; it counts these separately as
"Autoconf compile/link" under its uncacheable calls.

A package that sets PKG_CONFIGURE_CACHE or HOST_CONFIGURE_CACHE keeps a
config.cache under tmp/, so a package that is cleaned and built again
reads the answers instead of running the checks. The configure stage of
tools/m4 drops from 24.5 s to 1.5 s. dirclean removes tmp/, so a full
reset of the tree starts from cold answers.

A package has to 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 and -D_DEFAULT_SOURCE to CPPFLAGS inside
that body, so a cached run restores the value, skips the append, and
then fails to compile wcwidth. ncurses_cfg.h is identical in both cases;
the difference is in the generated Makefile.

The path covers everything else that changes an answer. A target package
is keyed on the toolchain directory, which carries the gcc version. A
host package is keyed on the version string of the host compiler,
because autoconf cannot detect a new compiler behind an unchanged name;
gcc 14 turned an implicit function declaration into an error and changed
many answers that way. Both are keyed on a hash of the configure
arguments and the build flags, and autoconf refuses a cache written with
different flags on top of that.

A cached program path outlives a wipe of staging_dir, so ac_cv_path_ and
ac_cv_prog_ answers are dropped when the cache loads and configure
searches PATH again. Without that, a cache written before staging_dir
held sed pins /usr/bin/sed for good. A search runs no compiler, so this
costs nothing that matters.

The cache arrives through a site script rather than --cache-file, so a
hand written configure script never sees a new argument. The site script
removes the file of the previous arguments, so a different configuration
gets a cold cache instead of an autoconf error. It also unsets the
variable, and make exports it for the configure stage alone, so a nested
configure run keeps its own cache.

Set CONFIGURE_CACHE_DIR to keep the cache elsewhere, or to the empty
string to run every check again.

Host builds now read a site script on Linux as well. Before this change
only Darwin had one.

CONFIG_SITE holds one path name: autoconf before 2.70 reads the whole
value as that one name, so a second path would silence both scripts. It
names the cache script alone, and the cache script loads the site script
of the architecture from CONFIG_SITE_BASE.

Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
John Crispin committed 2026-09-22 18:18:58 +02:00
1 parent 48c1e36bf2
commit a0b036985a
4 files changed
+99 -5

No files matched your search

+8 -3
View File
@@ -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))
+12 -2
View File
@@ -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)
+44
View File
@@ -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