[RFC] [PATCH 1/2] cmake-utils.eclass: Add new general purpose CMake manipulation eclass

Andreas Sturmlechner <[email protected]>
Newsgroups gmane.linux.gentoo.devel
Message-ID <[email protected]>
See also: https://github.com/gentoo/kde/pull/1097
---

Overrides no phase functions

New functions:
- cmake_recurse_files()
  loop from cmake.eclass' _cmake_modify-cmakelists(), made reusable
- cmake_minreqver_qainfo()
  QA check part from cmake.eclass' _cmake_modify-cmakelists()
  Now public instead of private _cmake_minreqver-info()
  Previously global arrays made local to this function:
  - _CMAKE_MINREQVER_CMAKE305 -> minreqver305
  - _CMAKE_MINREQVER_CMAKE310 -> minreqver310
  - _CMAKE_MINREQVER_CMAKE316 -> minreqver316

Functions moved from cmake.eclass:
- cmake_comment_add_subdirectory()
- cmake_use_find_package()
- cmake_run_in()
- _cmake_minreqver-get() - merged into cmake_minreqver_qainfo
- _cmake_minreqver-info() - merged into cmake_minreqver_qainfo

Meta variable moved from cmake.eclass: CMAKE_ECM_MODE

Signed-off-by: Andreas Sturmlechner <[email protected]>
---
 eclass/cmake-utils.eclass | 277 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 277 insertions(+)
 create mode 100644 eclass/cmake-utils.eclass

diff --git a/eclass/cmake-utils.eclass b/eclass/cmake-utils.eclass
new file mode 100644
index 0000000000..82c3fc23fe
--- /dev/null
+++ b/eclass/cmake-utils.eclass
@@ -0,0 +1,277 @@
+# Copyright 1999-2026 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: cmake-utils.eclass
+# @MAINTAINER:
+# [email protected]
+# [email protected]
+# @SUPPORTED_EAPIS: 8 9
+# @BLURB: General purpose functions for manipulating CMake files.
+# @DESCRIPTION:
+# A utility eclass providing functions to query and modify CMake files.
+#
+# This eclass does not export any phase functions. It can be inherited safely.
+
+case ${EAPI} in
+	8|9) ;;
+	*) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
+esac
+
+if [[ -z ${_CMAKE_UTILS_ECLASS} ]]; then
+_CMAKE_UTILS_ECLASS=1
+
+# @ECLASS_VARIABLE: CMAKE_ECM_MODE
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# Default value is "auto", which means _cmake_modify-cmakelists will make an
+# effort to detect find_package(ECM) in CMakeLists.txt.  If set to true, make
+# extra checks and add common config settings related to ECM (KDE Extra CMake
+# Modules).  If set to false, do nothing.
+: "${CMAKE_ECM_MODE:=auto}"
+
+# @ECLASS_VARIABLE: CMAKE_QA_COMPAT_SKIP
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# If set, skip detection of CMakeLists.txt unsupported in CMake 4 in case of
+# false positives (e.g. unused outdated bundled libs).
+
+case ${CMAKE_ECM_MODE} in
+	auto|true|false) ;;
+	*)
+		eerror "Unknown value for \${CMAKE_ECM_MODE}"
+		die "Value ${CMAKE_ECM_MODE} is not supported"
+		;;
+esac
+
+# @FUNCTION: cmake_comment_add_subdirectory
+# @USAGE: [-f <filename or directory>] <subdirectory> [<subdirectories>]
+# @DESCRIPTION:
+# Comment out one or more add_subdirectory calls with #DONOTBUILD in
+# a) a given file path (error out on nonexisting path)
+# b) a CMakeLists.txt file inside a given directory (ewarn if not found)
+# c) CMakeLists.txt in current directory (do nothing if not found).
+cmake_comment_add_subdirectory() {
+	local d filename="CMakeLists.txt"
+	if [[ $# -lt 1 ]]; then
+		die "${FUNCNAME[0]} must be passed at least one subdirectory name to comment"
+	fi
+	case ${1} in
+		-f)
+			if [[ $# -ge 3 ]]; then
+				filename="${2}"
+				if [[ -d ${filename} ]]; then
+					filename+="/CMakeLists.txt"
+					if [[ ! -e ${filename} ]]; then
+						ewarn "You've given me nothing to work with in ${filename}!"
+						return
+					fi
+				elif [[ ! -e ${filename} ]]; then
+					die "${FUNCNAME}: called on non-existing ${filename}"
+				fi
+			else
+				die "${FUNCNAME[0]}: bad number of arguments: -f <filename or directory> <subdirectory> expected"
+			fi
+			shift 2
+			;;
+		*)
+			[[ -e ${filename} ]] || return
+			;;
+	esac
+
+	for d in "$@"; do
+		d=${d//\//\\/}
+		sed -e "/add_subdirectory[[:space:]]*([[:space:]]*${d}\([[:space:]][a-Z_ ]*\|[[:space:]]*\))/I s/^/#DONOTBUILD /" \
+			-i ${filename} || die "failed to comment add_subdirectory(${d})"
+	done
+}
+
+# @FUNCTION: cmake_use_find_package
+# @USAGE: <USE flag> <package name>
+# @DESCRIPTION:
+# Based on use_enable. See ebuild(5).
+#
+# `cmake_use_find_package foo LibFoo` echoes -DCMAKE_DISABLE_FIND_PACKAGE_LibFoo=OFF
+# if foo is enabled and -DCMAKE_DISABLE_FIND_PACKAGE_LibFoo=ON if it is disabled.
+# This can be used to make find_package optional.
+cmake_use_find_package() {
+	debug-print-function ${FUNCNAME} "$@"
+
+	if [[ "$#" != 2 || -z $1 ]] ; then
+		die "Usage: cmake_use_find_package <USE flag> <package name>"
+	fi
+
+	echo "-DCMAKE_DISABLE_FIND_PACKAGE_$2=$(use $1 && echo OFF || echo ON)"
+}
+
+# @FUNCTION: cmake_minreqver_qainfo
+# @DESCRIPTION:
+# QA Notice and file listings for any CMake file not meeting various minimum
+# standards for cmake_minimum_required.  May be called from prepare or install
+# phase, adjusts QA notice accordingly (build or installed files warning).
+cmake_minreqver_qainfo() {
+	# Arrays containing <file>:<version> tuples for any CMake file with
+	# cmake_minimum_required version lower than defined.
+	local minreqver305=() # -lt 3.5  (CMake-4 error)
+	local minreqver310=() # -lt 3.10 (CMake-4 warning)
+	local minreqver316=() # -lt 3.16 (ECM-5.100 warning)
+
+	local contextdir
+	# Flag unsupported minimum CMake version unless CMAKE_QA_COMPAT_SKIP is set
+	cmake_chk_minreqver() {
+		debug-print-function ${FUNCNAME} "$@"
+		if [[ $# -ne 1 ]]; then
+			die "${FUNCNAME[0]} must be passed exactly one argument"
+		fi
+		if [[ ${CMAKE_QA_COMPAT_SKIP} ]]; then
+			return
+		fi
+		local file="$1" ver
+
+		ver=$(sed -ne "/^\s*cmake_minimum_required/I{s/.*\(\.\.\.*\|\s\)\([0-9][0-9.]*\)\([)]\|\s\).*$/\2/p;q}" \
+			"${1}" 2>/dev/null \
+		)
+		if [[ -n "${ver}" ]]; then
+			# we don't want duplicates that were already flagged
+			if ver_test "${ver}" -lt "3.5"; then
+				minreqver305+=( "${file#"${contextdir}/"}":"${ver}" )
+			elif ver_test "${ver}" -lt "3.10"; then
+				minreqver310+=( "${file#"${contextdir}/"}":"${ver}" )
+			elif ver_test "${ver}" -lt "3.16"; then
+				minreqver316+=( "${file#"${contextdir}/"}":"${ver}" )
+			fi
+		fi
+	}
+
+	case ${EBUILD_PHASE} in
+		prepare)
+			contextdir="${CMAKE_USE_DIR}"
+			cmake_recurse_files "${contextdir}" "CMakeLists.txt" cmake_chk_minreqver
+			;;
+		install)
+			contextdir="${D}"
+			cmake_recurse_files "${contextdir}" "*.cmake" cmake_chk_minreqver
+			;;
+		*)
+			die "${FUNCNAME[0]} called from unsupported phase function"
+			;;
+	esac
+
+	local warnlvl
+	[[ ${#minreqver305[@]} != 0 ]] && warnlvl=305
+	[[ ${#minreqver310[@]} != 0 ]] || [[ -n ${warnlvl} ]] && warnlvl=310
+	[[ ${CMAKE_ECM_MODE} == true ]] &&
+		{ [[ ${#minreqver316[@]} != 0 ]] || [[ -n ${warnlvl} ]]; } && warnlvl=316
+
+	local weak_qaw="QA Notice: "
+	minreqver_qanotice() {
+		bug() {
+			case ${1} in
+				305) echo "951350" ;;
+				310) echo "964405" ;;
+				316) echo "964407" ;;
+			esac
+		}
+		minreqver_qanotice_prepare() {
+			case ${1} in
+				305)
+					eqawarn "${weak_qaw}Compatibility with CMake < 3.5 has been removed from CMake 4,"
+					eqawarn "${CATEGORY}/${PN} will fail to build w/o a fix."
+					;;
+				310) eqawarn "${weak_qaw}Compatibility with CMake < 3.10 will be removed in a future release." ;;
+				316) eqawarn "${weak_qaw}Compatibility w/ CMake < 3.16 will be removed in future ECM release." ;;
+			esac
+		}
+		minreqver_qanotice_install() {
+			case ${1} in
+				305)
+					eqawarn "${weak_qaw}Package installs CMake module(s) incompatible with CMake 4,"
+					eqawarn "breaking any packages relying on it."
+					;;
+				31[06])
+					eqawarn "${weak_qaw}Package installs CMake module(s) w/ <${1/3/3.} minimum version that will"
+					eqawarn "be unsupported by future releases and is going to break any packages relying on it."
+					;;
+			esac
+		}
+		minreqver_qanotice_${EBUILD_PHASE} ${1}
+		eqawarn "See also tracker bug #$(bug ${1}); check existing or file a new bug for this package."
+		case ${1} in
+			305)	eqawarn "Please also take it upstream." ;;
+			31[06])	eqawarn "If not fixed in upstream's code repository, we should make sure they are aware." ;;
+		esac
+		eqawarn
+		weak_qaw="" # weak notice: no "QA Notice" starting with second call
+	}
+
+	local info
+	# <eqawarn msg> <_CMAKE_MINREQVER_* array>
+	minreqver_listing() {
+		[[ ${#@} -gt 1 ]] || return
+		eqawarn "${1}"
+		shift
+		for info in "${@}"; do
+			eqawarn "  ${info}";
+		done
+		eqawarn
+	}
+
+	# CMake 4-caused error is highest priority and must always be shown
+	if [[ ${#minreqver305[@]} != 0 ]]; then
+		minreqver_qanotice 305
+		minreqver_listing "The following files are causing errors:" ${minreqver305[*]}
+	fi
+	# for warnings, we only want the latest relevant one, but list all flagged files
+	if [[ ${warnlvl} -ge 310 ]]; then
+		minreqver_qanotice ${warnlvl}
+		minreqver_listing "The following files are causing warnings:" ${minreqver310[*]}
+		[[ ${warnlvl} == 316 ]] &&
+			minreqver_listing "The following files are causing warnings:" ${minreqver316[*]}
+	fi
+	if [[ ${warnlvl} ]]; then
+		if [[ ${EBUILD_PHASE} == prepare && ${#minreqver305[@]} != 0 ]] && has_version -b ">=dev-build/cmake-4"; then
+			eqawarn "CMake 4 detected; building with -DCMAKE_POLICY_VERSION_MINIMUM=3.5"
+			eqawarn "This is merely a workaround to avoid CMake Error and *not* a permanent fix;"
+			eqawarn "there may be new build or runtime bugs as a result."
+			eqawarn
+		fi
+		eqawarn "An upstreamable patch should take any resulting CMake policy changes"
+		eqawarn "into account. See also:"
+		eqawarn "  https://cmake.org/cmake/help/latest/manual/cmake-policies.7.html"
+	fi
+}
+
+# @FUNCTION: cmake_recurse_files
+# @USAGE: <rootdir> <filename> <run command>
+# @DESCRIPTION:
+# Recurse over all <filename(s) (case-insensitive)> below <rootdir> and
+# call a given <run command> with <filename> as argument.
+cmake_recurse_files() {
+	if [[ $# -lt 3 ]]; then
+		die "${FUNCNAME[0]} must be passed at least three arguments"
+	fi
+	local file rootdir="${1}" iname="${2}" runcmd="${3}"
+
+	[[ -e ${rootdir} ]] || die "${FUNCNAME[0]}: Nonexistent path: ${rootdir}"
+
+	while read -d '' -r file ; do
+		"${runcmd}" "${file}"
+	done < <(find "${rootdir}" -type f -iname "${iname}" -print0 || die)
+}
+
+# @FUNCTION: cmake_run_in
+# @USAGE: <working dir> <run command>
+# @DESCRIPTION:
+# Set the desired working dir for a function or command.
+cmake_run_in() {
+	if [[ -z ${2} ]]; then
+		die "${FUNCNAME[0]} must be passed at least two arguments"
+	fi
+
+	[[ -e ${1} ]] || die "${FUNCNAME[0]}: Nonexistent path: ${1}"
+
+	pushd ${1} > /dev/null || die
+		"${@:2}"
+	popd > /dev/null || die
+}
+
+fi
-- 
2.54.0
signature.asc (application/pgp-signature, 829 B)
-----BEGIN PGP SIGNATURE-----

iQIvBAABCgCZFiEE34gXUSooizdYNDu3S5FW4z0PgBkFAmoIPx0bFIAAAAAABAAO
bWFudTIsMi41KzEuMTIsMiwyXxSAAAAAAC4AKGlzc3Vlci1mcHJAbm90YXRpb25z
Lm9wZW5wZ3AuZmlmdGhob3JzZW1hbi5uZXRERjg4MTc1MTJBMjg4QjM3NTgzNDNC
Qjc0QjkxNTZFMzNEMEY4MDE5AAoJEEuRVuM9D4AZV3sL/iFMUZBPiUiO+rjNw7ov
Dps1eXSRQIwP4DMgtVjopU2S8pQ4ppRiWP2rV6Q5qG7pzYOQSBwrXVzZr9Evbvhl
LK1yQnrNUKVYxwFBxczbWnXxXPFDNXApFheDC4tpyh/kPb/fnV/Xf41Pl87p1wG+
o7+Al5hHsTvfW7izvVqIsRWDy1KMtp1XDYBDgLZkSMQPhRS44Jsau/X77CDaSVei
jhNN3qG4YDJwiVc9gAlvK/KzLhv+AueCkOf+StfLFKsCJlTu3dGdN9Kodg3vyBno
tLc6DIAieNWwCtWRlDuqkM9R+iTi2BNZTXiJFzP5CuUgMfpicexpuj5WMn4nUUEF
a1/dKI/DSXQjCYM03z99eVT9COayyC+LtqNS54ngXcbE/dZqLnm6ld4bAM+uu2r8
P97lhr6JhEnKJEuIRhx14FdNA3tttyrcmyPqa9pBTeS02cwk21Xi72c3pwjlZMBM
Yu7sxMTj6mK9W/dxZ/+oduq96H+qAnsd1F0Rdajk7zR2DQ==
=Wfxj
-----END PGP SIGNATURE-----
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.