diff --git a/src/centpkg/__init__.py b/src/centpkg/__init__.py index 661a62b..f614d33 100644 --- a/src/centpkg/__init__.py +++ b/src/centpkg/__init__.py @@ -16,10 +16,12 @@ # the full text of the license. +import os import re import warnings import git +import rpm from pyrpkg import Commands, rpkgError from pyrpkg.utils import cached_property @@ -201,6 +203,55 @@ class Commands(Commands): self.branch_merge, ) + def _define_patchn_compatiblity_macros(self): + """ + RPM 4.19 deprecated the %patchN macro. RPM 4.20 removed it completely. + The macro works on c8s, c9s, c10s, but does not work on Fedora 41+. + We can no longer even parse RPM spec files with the %patchN macros. + When we build for old streams, we define the %patchN macros manually as %patch -P N. + Since N can be any number including zero-prefixed numbers, + we regex-search the spec file for %patchN uses and define only the macros found. + """ + # Only do this on RPM 4.19.90+ (4.19.9x were pre-releases of 4.20) + if tuple(int(i) for i in rpm.__version_info__) < (4, 19, 90): + return + # Only do this when building for CentOS Stream version with RPM < 4.20 + try: + if int(self._distval.split("_")[0]) > 10: + return + except ValueError as e: + self.log.debug( + "Cannot parse major dist version as int: %s", + self._distval.split("_")[0], + exc_info=e, + ) + return + defined_patchn = False + try: + specfile_path = os.path.join(self.layout.specdir, self.spec) + with open(specfile_path, "rb") as specfile: + # Find all uses of %patchN in the spec files + # Using a benevolent regex: commented out macros, etc. match as well + for patch in re.findall(rb"%{?patch(\d+)\b", specfile.read()): + # We operate on bytes becasue we don't know the spec encoding + # but the matched part only includes ASCII digits + patch = patch.decode("ascii") + self._rpmdefines.extend( + [ + "--define", + # defines parametric macro %patchN which passes all arguments to %patch -P N + "patch%s(-) %%patch -P %s %%{?**}" % (patch, patch), + ] + ) + defined_patchn = True + except OSError as e: + self.log.debug("Cannot read spec.", exc_info=e) + if defined_patchn: + self.log.warn( + "centpkg defined %patchN compatibility shims to parse the spec file. " + "%patchN is obsolete, use %patch -P N instead." + ) + # redefined loaders def load_rpmdefines(self): """ @@ -245,6 +296,7 @@ class Commands(Commands): "--eval", "%%undefine %s" % self._distunset, ] + self._define_patchn_compatiblity_macros() self.log.debug("RPMDefines: %s" % self._rpmdefines) def construct_build_url(self, *args, **kwargs):