[projects/pld-builder.new] Rework PHP version handling: repo-based detection, chroot cleanup, split concerns

arekm arekm at pld-linux.org
Wed Apr 15 22:43:16 CEST 2026


commit 859f89b844a5e817445563c32c6c83efcf700e52
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date:   Wed Apr 15 22:42:08 2026 +0200

    Rework PHP version handling: repo-based detection, chroot cleanup, split concerns
    
    Replace rpm --eval '%{php_name}' with poldek repo queries for PHP
    version detection.  The macro resolves via php-config --sysconfdir
    which reflects whatever PHP is currently installed in the chroot,
    not what should be installed — on a dirty chroot with php53, it
    would return 'php53' and keep the wrong version.
    
    Instead, discover phpNN-program packages from the repo via poldek
    (with network access) and cache the result per build.
    
    Split the ignore mechanism into two concerns:
    
    cleanup_patterns() — what to remove from the dirty chroot before
      checking BRs.  When -D php_suffix is set, removes all PHP except
      that version.  Without php_suffix, removes ALL versioned PHP
      (clean slate) so poldek resolves from scratch using its EVR
      tiebreaker and the spec's version constraints.
    
    ignore_patterns() — what to pass as --ignore to poldek.  Only
      active when -D php_suffix is set (explicit version request from
      client scripts like php82.sh).  Without php_suffix, returns empty
      so poldek resolves freely.
    
    This correctly handles specs with version ranges (e.g.
    php(core) >= 5.6 and php(core) < 8.0) — poldek picks the newest
    valid provider instead of being forced to a single version.
    
    Chroot cleanup uses uninstall() for safety (hold list, crucial
    package checks) instead of raw rpm -e.  Call is placed in
    rpm_builder.py between uninstall_self_conflict and install_br.
    
    Also add log.notice() calls for build step visibility in syslog.

 PLD_Builder/install.py     | 39 ++++++++++++++++----------------
 PLD_Builder/request.py     | 56 +++++++++++++++++++++++++---------------------
 PLD_Builder/rpm_builder.py |  2 +-
 3 files changed, 52 insertions(+), 45 deletions(-)
---
diff --git a/PLD_Builder/install.py b/PLD_Builder/install.py
index e304503..ea5e90d 100644
--- a/PLD_Builder/install.py
+++ b/PLD_Builder/install.py
@@ -100,6 +100,7 @@ def uninstall(conflicting, b):
     return True
 
 def uninstall_self_conflict(b):
+    log.notice("checking BuildConflicts for %s" % b.spec)
     b.log_line("checking BuildConflict-ing packages")
     rpmcommand = "rpmbuild --nobuild -br"
     cmd = "set -e; TMPDIR=%(tmpdir)s %(rpmcommand)s %(rpmdefs)s %(topdir)s/%(spec)s 2>&1" % {
@@ -126,40 +127,40 @@ def uninstall_self_conflict(b):
     b.log_line("no BuildConflicts found")
     return True
 
-def remove_ignored_packages(b):
-    """Remove installed packages that match the ignore patterns.
+def cleanup_stale_packages(b):
+    """Remove stale multi-version packages from the chroot.
 
     The builder reuses chroots across builds, so a previous build may
-    have installed packages that should be ignored (e.g. php53-* when
-    the default PHP is php85).  These satisfy virtual deps in the rpm
-    db, preventing poldek from installing the correct version.  Remove
-    them so that get_missing_br() reports the deps as unmet and poldek
-    installs the right packages.
+    have installed packages from the wrong version family (e.g. php53-*
+    when the current build needs php85).  These satisfy virtual deps in
+    the rpm db, preventing poldek from installing the correct version.
+
+    When -D php_suffix is set, only that version is kept.  Otherwise,
+    all versioned PHP packages are removed — poldek will reinstall
+    whatever the spec actually needs, guided by its EVR tiebreaker and
+    the spec's version constraints.
     """
-    patterns = b.ignore_patterns()
+    log.notice("cleaning up stale packages for %s" % b.spec)
+    patterns = b.cleanup_patterns()
     if not patterns:
         return
 
     query = ' '.join("'%s'" % p for p in patterns)
     f = chroot.popen("rpm -qa %s --queryformat '%%{NAME}\\n'" % query, user = "root", encoding = "utf-8")
-    seen = {}
+    stale = {}
     for l in f:
         name = l.strip()
         if name:
-            seen[name] = 1
+            stale[name] = 1
     f.close()
 
-    if not seen:
-        return
-
-    to_remove = ' '.join(seen.keys())
-    b.log_line("removing ignored packages from chroot: %s" % to_remove)
-    res = chroot.run("rpm --allmatches -e %s" % to_remove,
-                     logfile = b.logfile, user = "root")
-    if res != 0:
-        b.log_line("warning: removal of ignored packages failed")
+    if stale:
+        b.log_line("removing stale packages from chroot: %s" % ' '.join(stale.keys()))
+        uninstall(stale, b)
 
 def install_br(r, b):
+    log.notice("installing BuildRequires for %s" % b.spec)
+
     def get_missing_br(r, b):
         # ignore internal rpm dependencies, see lib/rpmns.c for list
         ignore_br = re.compile(r'^\s*(rpmlib|cpuinfo|getconf|uname|soname|user|group|mounted|diskspace|digest|gnupg|macro|envvar|running|sanitycheck|vcheck|signature|verify|exists|executable|readable|writable)\(.*')
diff --git a/PLD_Builder/request.py b/PLD_Builder/request.py
index a014a6a..08a243d 100644
--- a/PLD_Builder/request.py
+++ b/PLD_Builder/request.py
@@ -400,48 +400,54 @@ class Batch:
             "--define '_builddir %{_topdir}/BUILD' "
         return rpmdefs + rpmopts
 
-    def php_ignores(self):
-        # Determine which PHP version to keep.  If -D php_suffix was
-        # passed in the build request, use that.  Otherwise, ask the
-        # chroot what rpm --eval '%{php_name}' returns — this is the
-        # system-default PHP set by rpmbuild macros and stays current
-        # without any hardcoded version lists.
-        if 'php_suffix' in self.defines:
-            keep_prefix = "php%s" % self.defines['php_suffix']
-        else:
-            f = chroot.popen("rpm --eval '%{php_name}'", encoding = "utf-8")
-            keep_prefix = f.read().strip()
-            f.close()
-            if not re.match(r'^php\d+$', keep_prefix):
-                log.notice("php_ignores: unexpected %%{php_name} value: '%s', skipping php ignores" % keep_prefix)
-                return []
-
-        # Discover all phpNN-* version prefixes available in the repo
-        # by looking at php*-program packages.  Ignore every prefix
-        # except the one we want to keep.
+    def _php_repo_prefixes(self):
+        """Discover all phpNN version prefixes available in the repo (cached)."""
+        if hasattr(self, '_cached_php_prefixes'):
+            return set(self._cached_php_prefixes)
+
         rx = re.compile(r'^(php\d+)-program-')
         prefixes = set()
-        f = chroot.popen("poldek -q --shcmd='ls -q php*-program'", encoding = "utf-8")
+        f = chroot.popen("poldek -q --shcmd='ls -q php*-program'", encoding = "utf-8", nonet = False)
         for l in f:
             m = rx.match(l.strip())
             if m:
                 prefixes.add(m.group(1))
         f.close()
+        self._cached_php_prefixes = prefixes
+        return set(prefixes)
 
-        if len(prefixes) == 0:
-            log.notice("php_ignores: no php*-program packages found in repo, skipping php ignores")
+    def _php_patterns(self, require_suffix=False):
+        """Build glob patterns for unwanted PHP version packages.
+
+        When require_suffix is True, return [] if php_suffix is not set
+        (used for --ignore: let poldek resolve freely).  When False,
+        return patterns for all versioned PHP except the explicitly
+        requested one (used for chroot cleanup).
+        """
+        if require_suffix and 'php_suffix' not in self.defines:
             return []
 
-        prefixes.discard(keep_prefix)
+        prefixes = self._php_repo_prefixes()
+        if not prefixes:
+            return []
+
+        if 'php_suffix' in self.defines:
+            prefixes.discard("php%s" % self.defines['php_suffix'])
 
         res = ['hhvm-*']
         for p in sorted(prefixes):
             res.append("%s-*" % p)
-
         return res
 
+    def cleanup_patterns(self):
+        """Patterns for removing stale multi-version packages from the chroot."""
+        return self._php_patterns(require_suffix=False)
+
     def ignore_patterns(self):
-        return self.php_ignores()
+        """Patterns for --ignore flags passed to poldek.
+        Only active when -D php_suffix explicitly requests a version.
+        """
+        return self._php_patterns(require_suffix=True)
 
     # build ignore package list for poldek
     def ignores(self):
diff --git a/PLD_Builder/rpm_builder.py b/PLD_Builder/rpm_builder.py
index 14ed8ce..e5c8ddb 100644
--- a/PLD_Builder/rpm_builder.py
+++ b/PLD_Builder/rpm_builder.py
@@ -232,7 +232,7 @@ def build_rpm(r, b):
             if ("no-install-br" not in r.flags) and not install.uninstall_self_conflict(b):
                 res = "FAIL_DEPS_UNINSTALL"
             if ("no-install-br" not in r.flags):
-                install.remove_ignored_packages(b)
+                install.cleanup_stale_packages(b)
             if ("no-install-br" not in r.flags) and not install.install_br(r, b):
                 res = "FAIL_DEPS_INSTALL"
             if not res:
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/projects/pld-builder.new.git/commitdiff/859f89b844a5e817445563c32c6c83efcf700e52



More information about the pld-cvs-commit mailing list