diff mbox series

staging: keep the mtime of files fixed up in the sysroot

Message ID 20260930142231.1818511-1-f_l_k@t-online.de
State New
Headers show
Series staging: keep the mtime of files fixed up in the sysroot | expand

Commit Message

Markus Volk Sept. 30, 2026, 2:22 p.m. UTC
staging_processfixme() replaces the FIXMESTAGINGDIR placeholders with
sed -i, which gives the files the current time. They then look newer
than anything already built against them, and build systems that
compare timestamps consider their outputs out of date. For example, the
Makefile of a cpan recipe depends on perl's Config.pm and config.h; when
perl-native is staged again, make regenerates the Makefile during
do_compile or do_install and fails, asking to be rerun.

Record the times of the files before sed and restore them afterwards.
The list of files is now read in Python instead of being passed through
xargs, and sed is run on chunks of it.

Suggested-by: Paul Barker <paul@pbarker.dev>
AI-Generated: Uses Claude Code (Claude Opus 5.5)
Signed-off-by: Markus Volk <f_l_k@t-online.de>
---
 meta/classes-global/staging.bbclass | 34 +++++++++++++++++++++++++----
 1 file changed, 30 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/meta/classes-global/staging.bbclass b/meta/classes-global/staging.bbclass
index 5833abebc8..f8d476f8f8 100644
--- a/meta/classes-global/staging.bbclass
+++ b/meta/classes-global/staging.bbclass
@@ -176,16 +176,42 @@  def staging_copydir(c, target, dest, seendirs):
         seendirs.add(dest)
 
 def staging_processfixme(fixme, target, recipesysroot, recipesysrootnative, d):
+    import re
     import subprocess
 
     if not fixme:
         return
-    cmd = "sed -e 's:^[^/]*/:%s/:g' %s | xargs sed -i -e 's:FIXMESTAGINGDIRTARGET:%s:g; s:FIXMESTAGINGDIRHOST:%s:g'" % (target, " ".join(fixme), recipesysroot, recipesysrootnative)
+    files = []
+    for fixmefile in fixme:
+        with open(fixmefile) as f:
+            for line in f:
+                line = line.strip()
+                if line:
+                    files.append(re.sub(r"^[^/]*/", target + "/", line))
+    if not files:
+        return
+
+    cmd = ["sed", "-i", "-e", "s:FIXMESTAGINGDIRTARGET:%s:g; s:FIXMESTAGINGDIRHOST:%s:g" % (recipesysroot, recipesysrootnative)]
     for fixmevar in ['PSEUDO_SYSROOT', 'HOSTTOOLS_DIR', 'PKGDATA_DIR', 'PSEUDO_LOCALSTATEDIR', 'LOGFIFO']:
         fixme_path = d.getVar(fixmevar)
-        cmd += " -e 's:FIXME_%s:%s:g'" % (fixmevar, fixme_path)
-    bb.debug(2, cmd)
-    subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
+        cmd += ["-e", "s:FIXME_%s:%s:g" % (fixmevar, fixme_path)]
+
+    # sed -i rewrites the files, which would give them the current time and
+    # make them look newer than anything already built against them. Build
+    # systems that compare timestamps then consider their outputs out of date,
+    # e.g. cpan Makefiles depending on perl's Config.pm. Keep the times the
+    # files had when they were staged.
+    times = {}
+    for f in files:
+        st = os.stat(f)
+        times[f] = (st.st_atime_ns, st.st_mtime_ns)
+
+    bb.debug(2, " ".join(cmd) + " <%d files>" % len(files))
+    for i in range(0, len(files), 1000):
+        subprocess.check_output(cmd + files[i:i + 1000], stderr=subprocess.STDOUT)
+
+    for f, ns in times.items():
+        os.utime(f, ns=ns)
 
 
 def staging_populate_sysroot_dir(targetsysroot, nativesysroot, native, d):