[PATCH 08/10] nginx{,-module}.eclass: save NGINX build config by storing ./configure flags

Zurab Kvachadze <[email protected]> Sat, 18 Jul 2026 19:37:05 +0300
Newsgroups gmane.linux.gentoo.devel
Message-ID <[email protected]>
Background
==========

NGINX's ABI and API can change depending on supplied ./configure flags
(most notoriously, enabling/disabling many first-party modules can
change ABI and API of NGINX as a whole). Perhaps unrelated, but
third-party modules are expected to be built in-tree, at the same time
NGINX is built. It is because of these two factors that we have to
freeze/preserve NGINX configuration.

Configuration-saving methods
============================

In general, I have identified two approaches as to how to preserve NGINX
configuration for building third-party modules out-of-tree.

The current implementation saves the generated headers:
ngx_auto_config.h and ngx_auto_headers.h. When third-party modules are
built, the ./configure flags are mostly ignored. It is straightforward
and easy and allowed us to cut some parts of configuration out.
Unfortunately, due to headers not being first-class configuration in the
build system, it has proven to be quite difficult to change some parts
of the build using headers alone.

For instance, my initial header-mangling-based implementation of
ngx_force_module (see the following commit) proved to be quite limited
and not really robust. The reason why I used this approach initially is
because when nginx-module.eclass was written, none of the
ngx_force_module fancy crossed my mind.

Second method is saving the ./configure flags themselves. Although
increasing the src_configure time (since we no longer skip phases), this
allows to build modules and edit the build configuration more easily and
correctly. This also gives users/ebuild authors more control since, as
stated above, most ./configure flags are ignored in the header
implementation.

Compatibility
=============

While nginx.eclass completely removes support for emitting
ngx_auto_{config,headers}.h, generating only ./configure flags,
nginx-module.eclass will support both configuration-saving methods for
the time being, allowing users to update www-servers/nginx.

Notes
=====

Passing ./configure flags around makes modules transitively depend on
NGINX's dependencies.

-Wl,--as-needed is added to LDFLAGS since NGINX links external
libraries needed by first-party modules also to third-party modules.

Signed-off-by: Zurab Kvachadze <[email protected]>
---
 eclass/nginx-module.eclass                    | 185 +++++++++++-------
 eclass/nginx.eclass                           |  36 ++--
 ....29.5-r1.ebuild => nginx-1.29.5-r2.ebuild} |   0
 ....29.7-r2.ebuild => nginx-1.29.7-r3.ebuild} |   0
 ....30.0-r1.ebuild => nginx-1.30.0-r2.ebuild} |   0
 ....30.2-r1.ebuild => nginx-1.30.2-r2.ebuild} |   0
 ....30.3-r1.ebuild => nginx-1.30.3-r2.ebuild} |   0
 ...x-1.30.4.ebuild => nginx-1.30.4-r1.ebuild} |   0
 ....31.1-r1.ebuild => nginx-1.31.1-r2.ebuild} |   0
 ....31.2-r1.ebuild => nginx-1.31.2-r2.ebuild} |   0
 ...x-1.31.3.ebuild => nginx-1.31.3-r1.ebuild} |   0
 11 files changed, 130 insertions(+), 91 deletions(-)
 rename www-servers/nginx/{nginx-1.29.5-r1.ebuild => nginx-1.29.5-r2.ebuild} (100%)
 rename www-servers/nginx/{nginx-1.29.7-r2.ebuild => nginx-1.29.7-r3.ebuild} (100%)
 rename www-servers/nginx/{nginx-1.30.0-r1.ebuild => nginx-1.30.0-r2.ebuild} (100%)
 rename www-servers/nginx/{nginx-1.30.2-r1.ebuild => nginx-1.30.2-r2.ebuild} (100%)
 rename www-servers/nginx/{nginx-1.30.3-r1.ebuild => nginx-1.30.3-r2.ebuild} (100%)
 rename www-servers/nginx/{nginx-1.30.4.ebuild => nginx-1.30.4-r1.ebuild} (100%)
 rename www-servers/nginx/{nginx-1.31.1-r1.ebuild => nginx-1.31.1-r2.ebuild} (100%)
 rename www-servers/nginx/{nginx-1.31.2-r1.ebuild => nginx-1.31.2-r2.ebuild} (100%)
 rename www-servers/nginx/{nginx-1.31.3.ebuild => nginx-1.31.3-r1.ebuild} (100%)

diff --git a/eclass/nginx-module.eclass b/eclass/nginx-module.eclass
index 51a0a54d3bf0..0bb3f207ed39 100644
--- a/eclass/nginx-module.eclass
+++ b/eclass/nginx-module.eclass
@@ -411,6 +411,13 @@ fi
 # NGINX's headers are created in the nginx-module_src_prepare() phase function.
 NGINX_S="${WORKDIR}/nginx"
 
+# @ECLASS_VARIABLE: _NGX_MOD_CONFIG_FLAGS_FILE
+# @INTERNAL
+# @DESCRIPTION:
+# Holds the path to the file containing NUL-separated ./configure flags used to
+# build www-servers/nginx.
+_NGX_MOD_CONFIG_FLAGS_FILE="${BROOT}/usr/src/nginx/configure-flags"
+
 # @ECLASS_VARIABLE: NGINX_MOD_SHARED_OBJECTS
 # @OUTPUT_VARIABLE
 # @DESCRIPTION:
@@ -634,8 +641,7 @@ unset -f _ngx_mod_set_test_env
 
 # @FUNCTION: nginx-module_src_prepare
 # @DESCRIPTION:
-# Creates a fake build environment and patches the module's initialisation code
-# to play nicely with it.
+# Creates a fake build environment.
 #
 # In the build environment initialisation part, the following symbolic links are
 # created (to not copy files over):
@@ -645,12 +651,7 @@ unset -f _ngx_mod_set_test_env
 # For additional information of what resides under linked paths, see the
 # nginx.eclass source, namely the nginx_src_install() function.
 #
-# The second part of the function patches module's initialisation code so that
-# any module's preprocessor definitions appear in the separate
-# '__ngx_gentoo_mod_config.h' file inside the 'build' directory.  This function
-# also makes module's "config" script clear whatever content
-# build/ngx_auto_config.h may have at the time of invocation. Then,
-# default_src_prepare() is called.
+# In the end, default_src_prepare() is called.
 nginx-module_src_prepare() {
 	debug-print-function "${FUNCNAME[0]}" "$@"
 
@@ -665,45 +666,54 @@ nginx-module_src_prepare() {
 	popd >/dev/null || die "popd failed"
 	eend 0
 
-	pushd "${S}/${NGINX_MOD_CONFIG_DIR}" >/dev/null ||
-		die "pushd failed"
-
-	ebegin "Patching module's config"
-	# Since NGINX does not guarantee ABI or API stability, we utilise
-	# preprocessor macros that were used to compile NGINX itself, to build third
-	# party modules. As such, we do not want for the dummy preprocessor macros
-	# produced by NGINX build system during module compilation to leak into the
-	# building environment. However, we do need to "capture" preprocessor macros
-	# set by the module itself, so we are required to somehow get these
-	# separately.
-	#
-	# To achieve that, the following sed script inserts ': >
-	# build/ngx_auto_config.h' line at the start of a module's 'config' shell
-	# script which gets sourced by NGINX build system midway during
-	# configuration. It has an effect of truncating the file containing NGINX
-	# preprocessor macros. This results in the file containing only module's
-	# macros at the end of the module's configuration.
-	#
-	# The following command renames the file with module's preprocessor macros
-	# to __ngx_gentoo_mod_config.h to be later merged with the system NGINX
-	# header into the actual header used during compilation. Due to the fact
-	# that executing the config shell script is not the last thing that NGINX
-	# build system does during configuration, we can not simply rename the
-	# header after the whole configuration, as it may contain other preprocessor
-	# macros than only the module's ones.
-	sed -i -e '1i\' -e ': > build/ngx_auto_config.h' config ||
-		{ eend $? || die "sed failed"; }
-
-	# Add one extra LF before the command in case the 'config' script does not
-	# have a trailing newline already.
-	printf "\n%s\n" 'mv build/ngx_auto_config.h build/__ngx_gentoo_mod_config.h' \
-		>> config
-	# We specifically need the $? of printf.
-	# shellcheck disable=SC2320
-	eend $? || die "printf failed"
-
-	# Get back into the module root and apply patches.
-	popd >/dev/null || die "popd failed"
+	ebegin "Determining NGINX configuration on-disk format"
+
+	if [[ -f "${_NGX_MOD_CONFIG_FLAGS_FILE}" ]]; then
+		eend 0
+		einfo "Using ./configure flags file"
+	else
+		eend 0
+		einfo "Using saved ngx_auto_{config,headers}.h headers"
+		pushd "${S}/${NGINX_MOD_CONFIG_DIR}" >/dev/null ||
+			die "pushd failed"
+
+		ebegin "Patching module's config"
+		# Since NGINX does not guarantee ABI or API stability, we utilise
+		# preprocessor macros that were used to compile NGINX itself, to build
+		# third party modules. As such, we do not want for the dummy
+		# preprocessor macros produced by NGINX build system during module
+		# compilation to leak into the building environment. However, we do need
+		# to "capture" preprocessor macros set by the module itself, so we are
+		# required to somehow get these separately.
+		#
+		# To achieve that, the following sed script inserts ': >
+		# build/ngx_auto_config.h' line at the start of a module's 'config'
+		# shell script which gets sourced by NGINX build system midway during
+		# configuration. It has an effect of truncating the file containing
+		# NGINX preprocessor macros. This results in the file containing only
+		# module's macros at the end of the module's configuration.
+		#
+		# The following command renames the file with module's preprocessor
+		# macros to __ngx_gentoo_mod_config.h to be later merged with the system
+		# NGINX header into the actual header used during compilation. Due to
+		# the fact that executing the config shell script is not the last thing
+		# that NGINX build system does during configuration, we can not simply
+		# rename the header after the whole configuration, as it may contain
+		# other preprocessor macros than only the module's ones.
+		sed -i -e '1i\' -e ': > build/ngx_auto_config.h' config ||
+			{ eend $? || die "sed failed"; }
+
+		# Add one extra LF before the command in case the 'config' script does
+		# not have a trailing newline already.
+		printf "\n%s\n" 'mv build/ngx_auto_config.h build/__ngx_gentoo_mod_config.h' \
+			>> config
+		# We specifically need the $? of printf.
+		# shellcheck disable=SC2320
+		eend $? || die "printf failed"
+		# Get back into the module root and apply patches.
+		popd >/dev/null || die "popd failed"
+	fi
+
 	default_src_prepare
 }
 
@@ -712,38 +722,59 @@ nginx-module_src_prepare() {
 # Configures the dynamic module by calling NGINX's ./configure script.
 # Custom flags can be supplied as arguments to the function, taking precedence
 # over eclass's flags.
-# This assembles ngx_auto_config.h from the system ngx_auto_config.h and
-# __ngx_gentoo_mod_config.h (see nginx-module_src_prepare()), and
-# ngx_auto_headers.h from the system ngx_auto_headers.h.
+#
+# This restores ./configure flags, if the respective file is present.
+# Otherwise, this function assembles ngx_auto_config.h from the system
+# ngx_auto_config.h and __ngx_gentoo_mod_config.h (see
+# nginx-module_src_prepare()), and ngx_auto_headers.h from the system
+# ngx_auto_headers.h.
+#
 # Also, sets environment variables and appends necessary libraries if
 # NGINX_MOD_LINK_MODULES is set.
 nginx-module_src_configure() {
 	debug-print-function "${FUNCNAME[0]}" "$@"
 	pushd "${NGINX_S}" >/dev/null || die "pushd failed"
 
-	local ngx_mod_flags
-	ngx_mod_flags=(
-		--with-cc="$(tc-getCC)"
-		--with-cpp="$(tc-getCPP)"
-		# The '-isystem' flag is used instead of '-I', so as for the installed
-		# (system) modules' headers to be of lower priority than the headers of
-		# the currently built module. This only affects the modules that both
-		# come with and install their own headers, e.g. ngx_devel_kit.
-		--with-cc-opt="-isystem src/modules"
-		--with-ld-opt="${LDFLAGS}"
-		--builddir=build
+	local ngx_mod_flags=()
+	if [[ -f "${_NGX_MOD_CONFIG_FLAGS_FILE}" ]]; then
+		# Restore the stored configure flags into ngx_mod_flags.
+		mapfile -d '' ngx_mod_flags < "${_NGX_MOD_CONFIG_FLAGS_FILE}"
+
+		# When we save compilation flags, NGINX passes all the -l flags to
+		# modules too, including stuff like -lperl -lcrypt etc. I am not sure
+		# what to do with this yet so for now we just pass the following to
+		# limit unnecessary linkage.
+		ngx_mod_append_libs "$(test-flags-CC '-Wl,--as-needed')"
+	else
+		# Otherwise, just replicate a sane subset of configure flags for
+		# backwards compatibility.
+		ngx_mod_flags=(
+			--with-cc="$(tc-getCC)"
+			--with-cpp="$(tc-getCPP)"
+			--with-ld-opt="${LDFLAGS}"
+			--builddir=build
+		)
+
+		# NGINX build system adds directories under src/ to the include path based
+		# on the specified configuration flags. Since this is the branch where
+		# we do not use ./configure flags we have to add the directories to the
+		# include path manually.
+		#
+		# The src/os is added automatically by the auto/unix script and the
+		# src/modules directory is included below.
+		append-cflags "$(find -H src -mindepth 1 -type d \! \( \( -path 'src/os' -o \
+							-path 'src/modules' \) -prune \) -printf '-I %p ')"
+	fi
+
+	ngx_mod_flags+=(
 		--add-dynamic-module="${S}/${NGINX_MOD_CONFIG_DIR}"
 	)
 
-	# NGINX build system adds directories under src/ to the include path based
-	# on the specified configuration flags. Since nginx.eclass does not
-	# save/restore the configuration flags, we have to add the directories to
-	# the include path manually.
-	# The src/os is added automatically by the auto/unix script and the
-	# src/modules directory is included by the '--with-cc-opt' configuration
-	# flag.
-	append-cflags "$(find -H src -mindepth 1 -type d \! \( \( -path 'src/os' -o \
-						-path 'src/modules' \) -prune \) -printf '-I %p ')"
+	# The '-isystem' flag is used instead of '-I', so as for the installed
+	# (system) modules' headers to be of lower priority than the headers of
+	# the currently built module. This only affects the modules that both
+	# come with and install their own headers, e.g. ngx_devel_kit.
+	append-cflags "-isystem src/modules"
 
 	# Some NGINX modules that depend on ngx_devel_kit (NDK) check whether the
 	# NDK_SRCS variable is non-empty and error out if it is empty or not
@@ -771,6 +802,7 @@ nginx-module_src_configure() {
 
 	eval "local -a EXTRA_ECONF=( ${EXTRA_ECONF} )"
 
+	# Backwards compatibility shim for header saving setups:
 	# Setting the required environment variable to skip the unnecessary
 	# execution of certain scripts (see nginx_src_install() in nginx.eclass).
 	_NGINX_GENTOO_SKIP_PHASES=1 econf_ngx \
@@ -778,11 +810,14 @@ nginx-module_src_configure() {
 		"$@"					\
 		"${EXTRA_ECONF[@]}"
 
-	cat "${ESYSROOT}/usr/include/nginx/ngx_auto_config.h" \
-		build/__ngx_gentoo_mod_config.h > build/ngx_auto_config.h ||
-		die "cat failed"
-	cp "${ESYSROOT}/usr/include/nginx/ngx_auto_headers.h" build ||
-		die "cp failed"
+	# Backwards compatibility.
+	if [[ ! -f "${_NGX_MOD_CONFIG_FLAGS_FILE}" ]]; then
+		cat "${ESYSROOT}/usr/include/nginx/ngx_auto_config.h" \
+			build/__ngx_gentoo_mod_config.h > build/ngx_auto_config.h ||
+			die "cat failed"
+		cp "${ESYSROOT}/usr/include/nginx/ngx_auto_headers.h" build ||
+				die "cp failed"
+	fi
 
 	popd >/dev/null || die "popd failed"
 }
diff --git a/eclass/nginx.eclass b/eclass/nginx.eclass
index 1075fa6da23f..9783f7c7fa25 100644
--- a/eclass/nginx.eclass
+++ b/eclass/nginx.eclass
@@ -237,6 +237,16 @@ esac
 # In EAPI 9, the functionality is unconditionally enabled.
 [[ ${EAPI} != 8 ]] && readonly NGINX_SUPPORT_MODULE_STUBS=1
 
+#-----> Internal variables <-----
+
+# @ECLASS_VARIABLE: _NGX_CONFIG_FLAGS_FILE
+# @INTERNAL
+# @DESCRIPTION:
+# Holds the path to the temporary copy of ./configure flags used to configure
+# NGINX.  Installed to /usr/src/nginx.  Used in nginx_src_configure() and
+# nginx_src_install().
+_NGX_CONFIG_FLAGS_FILE="${T}/nginx-configure-flags"
+
 #-----> ebuild setup <-----
 
 # NGINX does not guarantee ABI stability (required by dynamic modules), subslot is
@@ -673,6 +683,13 @@ nginx_src_configure() {
 		"$@"					\
 		"${EXTRA_ECONF[@]}"
 
+	# Store the configuration flags to install to /usr/src/nginx later for
+	# module building.
+	if use modules; then
+		printf '%s\0' "${nginx_flags[@]}" "$@" "${EXTRA_ECONF[@]}" \
+			>> "${_NGX_CONFIG_FLAGS_FILE}"
+	fi
+
 	sed -E -i \
 		-e '/^\s*LIB= \\$/ d' \
 		-e '/^\s*INSTALLSITEMAN3DIR= \\$/ d' \
@@ -880,23 +897,10 @@ nginx_src_install() {
 		# Copy the build system of NGINX to /usr/src/nginx.
 		insinto /usr/src/nginx
 		doins -r auto
+		# Save the configure flags so that modules have easier time manipulating
+		# the build environment.
+		newins "${_NGX_CONFIG_FLAGS_FILE}" configure-flags
 
-		# Disable several checks if the _NGINX_GENTOO_SKIP_PHASES variable is
-		# set to a non-empty value during the invocation of ./configure script.
-		# This is done since (1) these scripts do not have any effect on the
-		# build process of third-party modules and (2) they considerably
-		# increase configuration time.
-		sed -E -i \
-			's#^\s*\. auto/(unix|summary)$# \
-			[ -z "${_NGINX_GENTOO_SKIP_PHASES}" ] \&\& &#' \
-			configure || die "sed failed"
-
-		# The last statement in ./configure is [ -z "${_NGINX_GENTOO... ]. If
-		# _NGINX_GENTOO_SKIP_PHASES is non-empty, it evaluates to false and the
-		# whole ./configure script exits with a non-zero exit status. 'exit 0'
-		# is appended to the end of the script to always exit with a zero exit
-		# status, regardless of what the last statement evaluates to.
-		echo 'exit 0' >> configure || die "echo failed"
 		exeinto /usr/src/nginx
 		doexe configure
 
diff --git a/www-servers/nginx/nginx-1.29.5-r1.ebuild b/www-servers/nginx/nginx-1.29.5-r2.ebuild
similarity index 100%
rename from www-servers/nginx/nginx-1.29.5-r1.ebuild
rename to www-servers/nginx/nginx-1.29.5-r2.ebuild
diff --git a/www-servers/nginx/nginx-1.29.7-r2.ebuild b/www-servers/nginx/nginx-1.29.7-r3.ebuild
similarity index 100%
rename from www-servers/nginx/nginx-1.29.7-r2.ebuild
rename to www-servers/nginx/nginx-1.29.7-r3.ebuild
diff --git a/www-servers/nginx/nginx-1.30.0-r1.ebuild b/www-servers/nginx/nginx-1.30.0-r2.ebuild
similarity index 100%
rename from www-servers/nginx/nginx-1.30.0-r1.ebuild
rename to www-servers/nginx/nginx-1.30.0-r2.ebuild
diff --git a/www-servers/nginx/nginx-1.30.2-r1.ebuild b/www-servers/nginx/nginx-1.30.2-r2.ebuild
similarity index 100%
rename from www-servers/nginx/nginx-1.30.2-r1.ebuild
rename to www-servers/nginx/nginx-1.30.2-r2.ebuild
diff --git a/www-servers/nginx/nginx-1.30.3-r1.ebuild b/www-servers/nginx/nginx-1.30.3-r2.ebuild
similarity index 100%
rename from www-servers/nginx/nginx-1.30.3-r1.ebuild
rename to www-servers/nginx/nginx-1.30.3-r2.ebuild
diff --git a/www-servers/nginx/nginx-1.30.4.ebuild b/www-servers/nginx/nginx-1.30.4-r1.ebuild
similarity index 100%
rename from www-servers/nginx/nginx-1.30.4.ebuild
rename to www-servers/nginx/nginx-1.30.4-r1.ebuild
diff --git a/www-servers/nginx/nginx-1.31.1-r1.ebuild b/www-servers/nginx/nginx-1.31.1-r2.ebuild
similarity index 100%
rename from www-servers/nginx/nginx-1.31.1-r1.ebuild
rename to www-servers/nginx/nginx-1.31.1-r2.ebuild
diff --git a/www-servers/nginx/nginx-1.31.2-r1.ebuild b/www-servers/nginx/nginx-1.31.2-r2.ebuild
similarity index 100%
rename from www-servers/nginx/nginx-1.31.2-r1.ebuild
rename to www-servers/nginx/nginx-1.31.2-r2.ebuild
diff --git a/www-servers/nginx/nginx-1.31.3.ebuild b/www-servers/nginx/nginx-1.31.3-r1.ebuild
similarity index 100%
rename from www-servers/nginx/nginx-1.31.3.ebuild
rename to www-servers/nginx/nginx-1.31.3-r1.ebuild
-- 
2.54.0