From 48ea1a2b3b8bc4c8b2247972401ab5a6f7f4ecc7 Mon Sep 17 00:00:00 2001 From: Danny Milosavljevic Date: Mon, 31 Jul 2023 20:09:14 +0200 Subject: [PATCH] gnu: openjdk10: Make more reproducible. * gnu/packages/patches/openjdk-10-char-reproducibility.patch: New file. * gnu/packages/patches/openjdk-10-classlist-reproducibility.patch: New file. * gnu/packages/patches/openjdk-10-corba-reproducibility.patch: New file. * gnu/packages/patches/openjdk-10-jar-reproducibility.patch: New file. * gnu/packages/patches/openjdk-10-jtask-reproducibility.patch: New file. * gnu/packages/patches/openjdk-10-module-reproducibility.patch: New file. * gnu/packages/patches/openjdk-10-module3-reproducibility.patch: New file. * gnu/packages/patches/openjdk-10-module4-reproducibility.patch: New file. * gnu/packages/java.scm (openjdk10)[source]: Add patches. [arguments]<#:phases>[remove-timestamping]: New phase. * gnu/local.mk (dist_patch_DATA): Add patches. Signed-off-by: Maxim Cournoyer --- gnu/local.mk | 8 + gnu/packages/java.scm | 13 + .../openjdk-10-char-reproducibility.patch | 12 + ...openjdk-10-classlist-reproducibility.patch | 27 ++ .../openjdk-10-corba-reproducibility.patch | 12 + .../openjdk-10-jar-reproducibility.patch | 103 ++++++ .../openjdk-10-jtask-reproducibility.patch | 53 +++ .../openjdk-10-module-reproducibility.patch | 305 ++++++++++++++++++ .../openjdk-10-module3-reproducibility.patch | 34 ++ .../openjdk-10-module4-reproducibility.patch | 14 + 10 files changed, 581 insertions(+) create mode 100644 gnu/packages/patches/openjdk-10-char-reproducibility.patch create mode 100644 gnu/packages/patches/openjdk-10-classlist-reproducibility.patch create mode 100644 gnu/packages/patches/openjdk-10-corba-reproducibility.patch create mode 100644 gnu/packages/patches/openjdk-10-jar-reproducibility.patch create mode 100644 gnu/packages/patches/openjdk-10-jtask-reproducibility.patch create mode 100644 gnu/packages/patches/openjdk-10-module-reproducibility.patch create mode 100644 gnu/packages/patches/openjdk-10-module3-reproducibility.patch create mode 100644 gnu/packages/patches/openjdk-10-module4-reproducibility.patch diff --git a/gnu/local.mk b/gnu/local.mk index b0104619be..d8966dd016 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -1829,7 +1829,15 @@ dist_patch_DATA = \ %D%/packages/patches/openjdk-9-module-reproducibility.patch \ %D%/packages/patches/openjdk-9-module2-reproducibility.patch \ %D%/packages/patches/openjdk-9-module3-reproducibility.patch \ + %D%/packages/patches/openjdk-10-char-reproducibility.patch \ + %D%/packages/patches/openjdk-10-classlist-reproducibility.patch \ + %D%/packages/patches/openjdk-10-corba-reproducibility.patch \ %D%/packages/patches/openjdk-10-idlj-reproducibility.patch \ + %D%/packages/patches/openjdk-10-jar-reproducibility.patch \ + %D%/packages/patches/openjdk-10-jtask-reproducibility.patch \ + %D%/packages/patches/openjdk-10-module-reproducibility.patch \ + %D%/packages/patches/openjdk-10-module3-reproducibility.patch \ + %D%/packages/patches/openjdk-10-module4-reproducibility.patch \ %D%/packages/patches/openjdk-10-pointer-comparison.patch \ %D%/packages/patches/openjdk-10-setsignalhandler.patch \ %D%/packages/patches/openjdk-15-xcursor-no-dynamic.patch \ diff --git a/gnu/packages/java.scm b/gnu/packages/java.scm index ace06977c9..91ab61ad13 100644 --- a/gnu/packages/java.scm +++ b/gnu/packages/java.scm @@ -1215,7 +1215,15 @@ (define-public openjdk10 (base32 "0i47ar8lxzjrkkiwbzybfxs473390h4jq9ahm3xqdvy5zpchxy3y")) (patches (search-patches + "openjdk-10-char-reproducibility.patch" + "openjdk-10-classlist-reproducibility.patch" + "openjdk-10-corba-reproducibility.patch" "openjdk-10-idlj-reproducibility.patch" + "openjdk-10-module-reproducibility.patch" + "openjdk-10-module3-reproducibility.patch" + "openjdk-10-module4-reproducibility.patch" + "openjdk-10-jar-reproducibility.patch" + "openjdk-10-jtask-reproducibility.patch" "openjdk-10-pointer-comparison.patch" "openjdk-10-setsignalhandler.patch" "openjdk-currency-time-bomb2.patch")))) @@ -1241,6 +1249,11 @@ (define-public openjdk10 ;; this exact first line. (substitute* "make/data/blacklistedcertsconverter/blacklisted.certs.pem" (("^#!.*") "#! java BlacklistedCertsConverter SHA-256\n")))) + (add-after 'unpack 'remove-timestamping + (lambda _ + (substitute* "./src/hotspot/share/runtime/vm_version.cpp" + (("__DATE__") "") + (("__TIME__") "")))) (replace 'configure (lambda* (#:key inputs outputs #:allow-other-keys) (invoke "bash" "./configure" diff --git a/gnu/packages/patches/openjdk-10-char-reproducibility.patch b/gnu/packages/patches/openjdk-10-char-reproducibility.patch new file mode 100644 index 0000000000..a7932678af --- /dev/null +++ b/gnu/packages/patches/openjdk-10-char-reproducibility.patch @@ -0,0 +1,12 @@ +Danny +--- orig/jdk-6fa770f9f8ab/make/jdk/src/classes/build/tools/generatecharacter/GenerateCharacter.java 2022-04-13 19:24:10.211683257 +0200 ++++ jdk-6fa770f9f8ab/make/jdk/src/classes/build/tools/generatecharacter/GenerateCharacter.java 2022-04-13 22:51:50.680487330 +0200 +@@ -693,7 +693,7 @@ + PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(theOutputFileName))); + out.println(commentStart + + " This file was generated AUTOMATICALLY from a template file " + +- new java.util.Date() + commentEnd); ++ (System.getenv("SOURCE_DATE_EPOCH") == null ? new java.util.Date() : new java.util.Date(1000 * Long.parseLong(System.getenv("SOURCE_DATE_EPOCH")))) + commentEnd); + int marklen = commandMarker.length(); + LOOP: while(true) { + try { diff --git a/gnu/packages/patches/openjdk-10-classlist-reproducibility.patch b/gnu/packages/patches/openjdk-10-classlist-reproducibility.patch new file mode 100644 index 0000000000..e1292ba82d --- /dev/null +++ b/gnu/packages/patches/openjdk-10-classlist-reproducibility.patch @@ -0,0 +1,27 @@ +--- orig/jdk-6fa770f9f8ab/make/GenerateLinkOptData.gmk 2022-04-04 17:16:29.365930149 +0200 ++++ jdk-6fa770f9f8ab/make/GenerateLinkOptData.gmk 2022-04-04 17:16:54.954624358 +0200 +@@ -61,11 +61,12 @@ + $(call MakeDir, $(LINK_OPT_DIR)) + $(call LogInfo, Generating $(patsubst $(OUTPUTDIR)/%, %, $@)) + $(call LogInfo, Generating $(patsubst $(OUTPUTDIR)/%, %, $(JLI_TRACE_FILE))) +- $(FIXPATH) $(INTERIM_IMAGE_DIR)/bin/java -XX:DumpLoadedClassList=$@ \ ++ $(FIXPATH) $(INTERIM_IMAGE_DIR)/bin/java -XX:DumpLoadedClassList=$@.tmp \ + -Djava.lang.invoke.MethodHandle.TRACE_RESOLVE=true \ + -cp $(SUPPORT_OUTPUTDIR)/classlist.jar \ + build.tools.classlist.HelloClasslist \ + $(LOG_DEBUG) 2>&1 > $(JLI_TRACE_FILE) ++ sort $@.tmp > $@ + + # The jli trace is created by the same recipe as classlist. By declaring these + # dependencies, make will correctly rebuild both jli trace and classlist +--- orig/jdk-6fa770f9f8ab/make/gendata/Gendata-jdk.compiler.gmk 2022-04-13 19:24:10.191682716 +0200 ++++ jdk-6fa770f9f8ab/make/gendata/Gendata-jdk.compiler.gmk 2022-04-13 20:58:57.891368216 +0200 +@@ -83,6 +83,8 @@ + $(CT_MODULESOURCEPATH) \ + $(CT_MODULES) \ + >$(@D)/A/system-modules ++ # Make files reproducible ++ find $(@D) -exec $(TOUCH) -h -c -t 197001010000.01 {} \; + $(TOUCH) $@ + + # Can't generate ct.sym directly into modules libs as the SetupJarArchive macro diff --git a/gnu/packages/patches/openjdk-10-corba-reproducibility.patch b/gnu/packages/patches/openjdk-10-corba-reproducibility.patch new file mode 100644 index 0000000000..bd5ce1fd2b --- /dev/null +++ b/gnu/packages/patches/openjdk-10-corba-reproducibility.patch @@ -0,0 +1,12 @@ +Danny +--- orig/jdk-6fa770f9f8ab/make/corba/src/classes/build/tools/logutil/MC.java 2022-04-13 19:24:10.111680549 +0200 ++++ jdk-6fa770f9f8ab/make/corba/src/classes/build/tools/logutil/MC.java 2022-04-13 22:51:13.399462259 +0200 +@@ -154,7 +154,7 @@ + groupName); + pw.println("//"); + pw.printMsg("// Generated by MC.java version @, DO NOT EDIT BY HAND!", VERSION); +- pw.printMsg("// Generated from input file @ on @", inFile, new Date()); ++ pw.printMsg("// Generated from input file @ on @", inFile, System.getenv("SOURCE_DATE_EPOCH") == null ? new Date() : new Date(1000 * Long.parseLong(System.getenv("SOURCE_DATE_EPOCH")))); + pw.println(); + } + diff --git a/gnu/packages/patches/openjdk-10-jar-reproducibility.patch b/gnu/packages/patches/openjdk-10-jar-reproducibility.patch new file mode 100644 index 0000000000..176eedfce6 --- /dev/null +++ b/gnu/packages/patches/openjdk-10-jar-reproducibility.patch @@ -0,0 +1,103 @@ +diff -ru orig/jdk-6fa770f9f8ab/make/common/JarArchive.gmk jdk-6fa770f9f8ab/make/common/JarArchive.gmk +--- orig/jdk-6fa770f9f8ab/make/common/JarArchive.gmk 2022-04-13 19:24:10.107680441 +0200 ++++ jdk-6fa770f9f8ab/make/common/JarArchive.gmk 2022-04-13 19:31:18.031271019 +0200 +@@ -251,12 +251,14 @@ + $(ECHO) "Main-Class: $$(strip $$($1_JARMAIN))" >> $$($1_MANIFEST_FILE) $$(NEWLINE)) \ + $$(if $$($1_EXTRA_MANIFEST_ATTR), \ + $(PRINTF) "$$($1_EXTRA_MANIFEST_ATTR)\n" >> $$($1_MANIFEST_FILE) $$(NEWLINE)) \ ++ $(TOUCH) -h -c -t 197001010000.00 $$($1_MANIFEST_FILE) $$(NEWLINE) \ + $(ECHO) Creating $$($1_NAME) $$(NEWLINE) \ + $$($1_JAR_CMD) $$($1_JAR_CREATE_OPTIONS) $$@ $$($1_MANIFEST_FILE) $$(NEWLINE) \ + $$($1_SCAPTURE_CONTENTS) \ + $$($1_SCAPTURE_METAINF) \ + $$($1_SUPDATE_CONTENTS) \ +- $$($1_JARINDEX) && true \ ++ $$($1_JARINDEX) && true $$(NEWLINE) \ ++ d="`mktemp -d`" && $(CP) -f $$@ "$$$$d/a.jar" && (cd "$$$$d" && unzip a.jar META-INF/MANIFEST.MF && $(TOUCH) -h -c -t 197001010000.00 META-INF && $(TOUCH) -h -c -t 197001010000.00 META-INF/MANIFEST.MF && (zip --symlinks -0 -X a.jar META-INF META-INF/MANIFEST.MF; zip --symlinks -0 -X a.jar META-INF META-INF/MANIFEST.MF)) && $(CP) -f "$$$$d/a.jar" $$@ \ + , \ + $(ECHO) Modifying $$($1_NAME) $$(NEWLINE) \ + $$($1_CAPTURE_CONTENTS) \ +diff -ru orig/jdk-6fa770f9f8ab/make/JrtfsJar.gmk jdk-6fa770f9f8ab/make/JrtfsJar.gmk +--- orig/jdk-6fa770f9f8ab/make/JrtfsJar.gmk 2022-04-13 19:24:10.091680007 +0200 ++++ jdk-6fa770f9f8ab/make/JrtfsJar.gmk 2022-04-13 19:29:30.044346222 +0200 +@@ -57,13 +57,18 @@ + # file will not be copied unless META-INF/services would also be added to the INCLUDES. + # Adding META-INF/services would include all files in that directory when only the one + # is needed, which is why this explicit copy is defined instead. +-$(eval $(call SetupCopyFiles, COPY_JIMAGE_SERVICE_PROVIDER, \ ++$(eval $(call SetupCopyFiles, COPY_JIMAGE_SERVICE_PROVIDER, \ + SRC := $(TOPDIR)/src/java.base/share/classes, \ + DEST := $(SUPPORT_OUTPUTDIR)/jrtfs_classes, \ + FILES := META-INF/services/java.nio.file.spi.FileSystemProvider)) + ++.PHONY: $(COPY_JIMAGE_SERVICE_PROVIDER)_fix ++$(COPY_JIMAGE_SERVICE_PROVIDER)_fix: $(COPY_JIMAGE_SERVICE_PROVIDER) ++ find $(SUPPORT_OUTPUTDIR)/jrtfs_classes -exec $(TOUCH) -h -c -t 197001010000.00 {} \; ++ $(TOUCH) -h -c -t 197001010000.00 $(SUPPORT_OUTPUTDIR)/java-main-manifest.mf ++ + $(eval $(call SetupJarArchive,BUILD_JRTFS_JAR, \ +- DEPENDENCIES := $(BUILD_JRTFS) $(COPY_JIMAGE_SERVICE_PROVIDER), \ ++ DEPENDENCIES := $(BUILD_JRTFS) $(COPY_JIMAGE_SERVICE_PROVIDER)_fix, \ + SRCS := $(SUPPORT_OUTPUTDIR)/jrtfs_classes, \ + JAR := $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/jrt-fs.jar, \ + MANIFEST := $(SUPPORT_OUTPUTDIR)/java-main-manifest.mf, \ +diff -ru orig/jdk-6fa770f9f8ab/src/jdk.jartool/share/classes/sun/tools/jar/Main.java jdk-6fa770f9f8ab/src/jdk.jartool/share/classes/sun/tools/jar/Main.java +--- orig/jdk-6fa770f9f8ab/src/jdk.jartool/share/classes/sun/tools/jar/Main.java 2022-04-13 19:24:12.555746751 +0200 ++++ jdk-6fa770f9f8ab/src/jdk.jartool/share/classes/sun/tools/jar/Main.java 2022-04-13 19:25:34.117955999 +0200 +@@ -849,12 +849,18 @@ + output(getMsg("out.added.manifest")); + } + ZipEntry e = new ZipEntry(MANIFEST_DIR); +- e.setTime(System.currentTimeMillis()); ++ if (System.getenv("SOURCE_DATE_EPOCH") != null) ++ e.setTime(1000 * Long.parseLong(System.getenv("SOURCE_DATE_EPOCH"))); ++ else ++ e.setTime(System.currentTimeMillis()); + e.setSize(0); + e.setCrc(0); + zos.putNextEntry(e); + e = new ZipEntry(MANIFEST_NAME); +- e.setTime(System.currentTimeMillis()); ++ if (System.getenv("SOURCE_DATE_EPOCH") != null) ++ e.setTime(1000 * Long.parseLong(System.getenv("SOURCE_DATE_EPOCH"))); ++ else ++ e.setTime(System.currentTimeMillis()); + if (flag0) { + crc32Manifest(e, manifest); + } +@@ -1021,7 +1027,10 @@ + throws IOException + { + ZipEntry e = new ZipEntry(INDEX_NAME); +- e.setTime(System.currentTimeMillis()); ++ if (System.getenv("SOURCE_DATE_EPOCH") != null) ++ e.setTime(1000 * Long.parseLong(System.getenv("SOURCE_DATE_EPOCH"))); ++ else ++ e.setTime(System.currentTimeMillis()); + if (flag0) { + CRC32OutputStream os = new CRC32OutputStream(); + index.write(os); +@@ -1040,7 +1049,10 @@ + String name = mi.getKey(); + byte[] bytes = mi.getValue(); + ZipEntry e = new ZipEntry(name); +- e.setTime(System.currentTimeMillis()); ++ if (System.getenv("SOURCE_DATE_EPOCH") != null) ++ e.setTime(1000 * Long.parseLong(System.getenv("SOURCE_DATE_EPOCH"))); ++ else ++ e.setTime(System.currentTimeMillis()); + if (flag0) { + crc32ModuleInfo(e, bytes); + } +@@ -1065,7 +1077,10 @@ + addMultiRelease(m); + } + ZipEntry e = new ZipEntry(MANIFEST_NAME); +- e.setTime(System.currentTimeMillis()); ++ if (System.getenv("SOURCE_DATE_EPOCH") != null) ++ e.setTime(1000 * Long.parseLong(System.getenv("SOURCE_DATE_EPOCH"))); ++ else ++ e.setTime(System.currentTimeMillis()); + if (flag0) { + crc32Manifest(e, m); + } diff --git a/gnu/packages/patches/openjdk-10-jtask-reproducibility.patch b/gnu/packages/patches/openjdk-10-jtask-reproducibility.patch new file mode 100644 index 0000000000..3411ca12ae --- /dev/null +++ b/gnu/packages/patches/openjdk-10-jtask-reproducibility.patch @@ -0,0 +1,53 @@ +--- jdk-10/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java.orig 2022-04-04 11:18:52.760626467 +0200 ++++ jdk-10/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java 2022-04-04 12:03:40.645325687 +0200 +@@ -105,6 +105,7 @@ + import jdk.internal.module.ModuleTarget; + import jdk.internal.module.Resources; + import jdk.tools.jlink.internal.Utils; ++import java.util.TreeSet; + + import static java.util.stream.Collectors.joining; + +@@ -768,6 +769,7 @@ + void processSection(JmodOutputStream out, Section section, Path path) + throws IOException + { ++ TreeSet paths = new TreeSet<>(); + Files.walkFileTree(path, Set.of(FileVisitOption.FOLLOW_LINKS), + Integer.MAX_VALUE, new SimpleFileVisitor() { + @Override +@@ -781,20 +783,24 @@ + + if (!relPath.toString().equals(MODULE_INFO) + && !matches(relPath, excludes)) { +- try (InputStream in = Files.newInputStream(file)) { +- out.writeEntry(in, section, relPath.toString()); +- } catch (IOException x) { +- if (x.getMessage().contains("duplicate entry")) { +- warning("warn.ignore.duplicate.entry", +- relPath.toString(), section); +- return FileVisitResult.CONTINUE; +- } +- throw x; +- } ++ paths.add(file); + } + return FileVisitResult.CONTINUE; + } + }); ++ for (Path file : paths) { ++ Path relPath = path.relativize(file); ++ try (InputStream in = Files.newInputStream(file)) { ++ out.writeEntry(in, section, relPath.toString()); ++ } catch (IOException x) { ++ if (x.getMessage().contains("duplicate entry")) { ++ warning("warn.ignore.duplicate.entry", ++ relPath.toString(), section); ++ continue; ++ } ++ throw x; ++ } ++ } + } + + boolean matches(Path path, List matchers) { diff --git a/gnu/packages/patches/openjdk-10-module-reproducibility.patch b/gnu/packages/patches/openjdk-10-module-reproducibility.patch new file mode 100644 index 0000000000..165edd3b4a --- /dev/null +++ b/gnu/packages/patches/openjdk-10-module-reproducibility.patch @@ -0,0 +1,305 @@ +From a52c4ef44c0553a399a8a47e528db92e3bf51c6c Mon Sep 17 00:00:00 2001 +From: Alan Bateman +Date: Wed, 29 Apr 2020 08:38:28 +0100 +Subject: [PATCH] 8243666: ModuleHashes attribute generated for JMOD and JAR + files depends on timestamps + +Reviewed-by: mchung +--- + +--- orig/jdk-3cc80be736f2/src/java.base/share/classes/jdk/internal/module/ModuleHashesBuilder.java 2022-04-13 19:24:10.655695284 +0200 ++++ jdk-3cc80be736f2/src/java.base/share/classes/jdk/internal/module/ModuleHashesBuilder.java 2022-04-14 02:43:48.610326492 +0200 +@@ -27,9 +27,8 @@ + + import java.io.PrintStream; + import java.lang.module.Configuration; ++import java.lang.module.ModuleReference; + import java.lang.module.ResolvedModule; +-import java.net.URI; +-import java.nio.file.Path; + import java.nio.file.Paths; + import java.util.ArrayDeque; + import java.util.Collections; +@@ -39,8 +38,8 @@ + import java.util.LinkedList; + import java.util.Map; + import java.util.Set; ++import java.util.TreeMap; + import java.util.function.Consumer; +-import java.util.function.Function; + import java.util.stream.Stream; + import static java.util.stream.Collectors.*; + +@@ -101,7 +100,7 @@ + // the modules to record the hashes - it is the first matching + // module and has not been hashed during the traversal. + Set mods = new HashSet<>(); +- Map hashes = new HashMap<>(); ++ Map hashes = new TreeMap<>(); + builder.build() + .orderedNodes() + .filter(mn -> roots.contains(mn) && !mods.contains(mn)) +@@ -116,27 +115,17 @@ + mods.addAll(ns); + + if (!ns.isEmpty()) { +- Map moduleToPath = ns.stream() +- .collect(toMap(Function.identity(), this::moduleToPath)); +- hashes.put(mn, ModuleHashes.generate(moduleToPath, "SHA-256")); ++ Set mrefs = ns.stream() ++ .map(name -> configuration.findModule(name) ++ .orElseThrow(InternalError::new)) ++ .map(ResolvedModule::reference) ++ .collect(toSet()); ++ hashes.put(mn, ModuleHashes.generate(mrefs, "SHA-256")); + } + }); + return hashes; + } + +- private Path moduleToPath(String name) { +- ResolvedModule rm = configuration.findModule(name).orElseThrow( +- () -> new InternalError("Selected module " + name + " not on module path")); +- +- URI uri = rm.reference().location().get(); +- Path path = Paths.get(uri); +- String fn = path.getFileName().toString(); +- if (!fn.endsWith(".jar") && !fn.endsWith(".jmod")) { +- throw new UnsupportedOperationException(path + " is not a modular JAR or jmod file"); +- } +- return path; +- } +- + /* + * Utility class + */diff -ru orig/jdk-3cc80be736f2/src/java.base/share/classes/jdk/internal/module/ModuleHashes.java jdk-3cc80be736f2/src/java.base/share/classes/jdk/internal/module/ModuleHashes.java +--- orig/jdk-3cc80be736f2/src/java.base/share/classes/jdk/internal/module/ModuleHashes.java 1970-01-01 01:00:01.000000000 +0100 ++++ jdk-3cc80be736f2/src/java.base/share/classes/jdk/internal/module/ModuleHashes.java 2022-04-12 16:58:05.639985936 +0200 +@@ -26,17 +26,21 @@ + package jdk.internal.module; + + import java.io.IOException; ++import java.io.InputStream; + import java.io.UncheckedIOException; +-import java.nio.ByteBuffer; +-import java.nio.channels.FileChannel; +-import java.nio.file.Path; ++import java.lang.module.ModuleReader; ++import java.lang.module.ModuleReference; ++import java.nio.charset.StandardCharsets; + import java.security.MessageDigest; + import java.security.NoSuchAlgorithmException; ++import java.util.Arrays; + import java.util.Collections; + import java.util.HashMap; + import java.util.Map; + import java.util.Objects; + import java.util.Set; ++import java.util.TreeMap; ++import java.util.function.Supplier; + + /** + * The result of hashing the contents of a number of module artifacts. +@@ -60,8 +64,8 @@ + * @param algorithm the algorithm used to create the hashes + * @param nameToHash the map of module name to hash value + */ +- public ModuleHashes(String algorithm, Map nameToHash) { +- this.algorithm = algorithm; ++ ModuleHashes(String algorithm, Map nameToHash) { ++ this.algorithm = Objects.requireNonNull(algorithm); + this.nameToHash = Collections.unmodifiableMap(nameToHash); + } + +@@ -95,54 +99,125 @@ + } + + /** +- * Computes the hash for the given file with the given message digest +- * algorithm. ++ * Computes a hash from the names and content of a module. + * ++ * @param reader the module reader to access the module content ++ * @param algorithm the name of the message digest algorithm to use ++ * @return the hash ++ * @throws IllegalArgumentException if digest algorithm is not supported + * @throws UncheckedIOException if an I/O error occurs + * @throws RuntimeException if the algorithm is not available + */ +- public static byte[] computeHash(Path file, String algorithm) { ++ private static byte[] computeHash(ModuleReader reader, String algorithm) { ++ MessageDigest md; + try { +- MessageDigest md = MessageDigest.getInstance(algorithm); +- +- // Ideally we would just mmap the file but this consumes too much +- // memory when jlink is running concurrently on very large jmods +- try (FileChannel fc = FileChannel.open(file)) { +- ByteBuffer bb = ByteBuffer.allocate(32*1024); +- while (fc.read(bb) > 0) { +- bb.flip(); +- md.update(bb); +- assert bb.remaining() == 0; +- bb.clear(); +- } +- } +- +- return md.digest(); ++ md = MessageDigest.getInstance(algorithm); + } catch (NoSuchAlgorithmException e) { +- throw new RuntimeException(e); ++ throw new IllegalArgumentException(e); ++ } ++ try { ++ byte[] buf = new byte[32*1024]; ++ reader.list().sorted().forEach(rn -> { ++ md.update(rn.getBytes(StandardCharsets.UTF_8)); ++ try (InputStream in = reader.open(rn).orElseThrow(java.util.NoSuchElementException::new)) { ++ int n; ++ while ((n = in.read(buf)) > 0) { ++ md.update(buf, 0, n); ++ } ++ } catch (IOException ioe) { ++ throw new UncheckedIOException(ioe); ++ } ++ }); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } ++ return md.digest(); + } + + /** +- * Computes the hash for every entry in the given map, returning a +- * {@code ModuleHashes} to encapsulate the result. The map key is +- * the entry name, typically the module name. The map value is the file +- * path to the entry (module artifact). ++ * Computes a hash from the names and content of a module. + * ++ * @param supplier supplies the module reader to access the module content ++ * @param algorithm the name of the message digest algorithm to use ++ * @return the hash ++ * @throws IllegalArgumentException if digest algorithm is not supported ++ * @throws UncheckedIOException if an I/O error occurs ++ */ ++ static byte[] computeHash(Supplier supplier, String algorithm) { ++ try (ModuleReader reader = supplier.get()) { ++ return computeHash(reader, algorithm); ++ } catch (IOException ioe) { ++ throw new UncheckedIOException(ioe); ++ } ++ } ++ ++ /** ++ * Computes the hash from the names and content of a set of modules. Returns ++ * a {@code ModuleHashes} to encapsulate the result. ++ * @param mrefs the set of modules ++ * @param algorithm the name of the message digest algorithm to use + * @return ModuleHashes that encapsulates the hashes ++ * @throws IllegalArgumentException if digest algorithm is not supported ++ * @throws UncheckedIOException if an I/O error occurs + */ +- public static ModuleHashes generate(Map map, String algorithm) { ++ static ModuleHashes generate(Set mrefs, String algorithm) { + Map nameToHash = new HashMap<>(); +- for (Map.Entry entry: map.entrySet()) { +- String name = entry.getKey(); +- Path path = entry.getValue(); +- nameToHash.put(name, computeHash(path, algorithm)); ++ for (ModuleReference mref : mrefs) { ++ try (ModuleReader reader = mref.open()) { ++ byte[] hash = computeHash(reader, algorithm); ++ nameToHash.put(mref.descriptor().name(), hash); ++ } catch (IOException ioe) { ++ throw new UncheckedIOException(ioe); ++ } + } + return new ModuleHashes(algorithm, nameToHash); + } + ++ @Override ++ public int hashCode() { ++ int h = algorithm.hashCode(); ++ for (Map.Entry e : nameToHash.entrySet()) { ++ h = h * 31 + e.getKey().hashCode(); ++ h = h * 31 + Arrays.hashCode(e.getValue()); ++ } ++ return h; ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (!(obj instanceof ModuleHashes)) ++ return false; ++ ModuleHashes other = (ModuleHashes) obj; ++ if (!algorithm.equals(other.algorithm) ++ || nameToHash.size() != other.nameToHash.size()) ++ return false; ++ for (Map.Entry e : nameToHash.entrySet()) { ++ String name = e.getKey(); ++ byte[] hash = e.getValue(); ++ if (!Arrays.equals(hash, other.nameToHash.get(name))) ++ return false; ++ } ++ return true; ++ } ++ ++ @Override ++ public String toString() { ++ StringBuilder sb = new StringBuilder(algorithm); ++ sb.append(" "); ++ nameToHash.entrySet() ++ .stream() ++ .sorted(Map.Entry.comparingByKey()) ++ .forEach(e -> { ++ sb.append(e.getKey()); ++ sb.append("="); ++ byte[] ba = e.getValue(); ++ for (byte b : ba) { ++ sb.append(String.format("%02x", b & 0xff)); ++ } ++ }); ++ return sb.toString(); ++ } ++ + /** + * This is used by jdk.internal.module.SystemModules class + * generated at link time. +diff -ru orig/jdk-3cc80be736f2/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java jdk-3cc80be736f2/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java +--- orig/jdk-3cc80be736f2/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java 1970-01-01 01:00:01.000000000 +0100 ++++ jdk-3cc80be736f2/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java 2022-04-12 16:43:12.967868689 +0200 +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +diff -ru orig/jdk-3cc80be736f2/src/java.base/share/classes/jdk/internal/module/ModuleReferences.java jdk-3cc80be736f2/src/java.base/share/classes/jdk/internal/module/ModuleReferences.java +--- orig/jdk-3cc80be736f2/src/java.base/share/classes/jdk/internal/module/ModuleReferences.java 1970-01-01 01:00:01.000000000 +0100 ++++ jdk-3cc80be736f2/src/java.base/share/classes/jdk/internal/module/ModuleReferences.java 2022-04-12 16:43:12.971868797 +0200 +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -95,7 +95,7 @@ + Path file) { + URI uri = file.toUri(); + Supplier supplier = () -> new JarModuleReader(file, uri); +- HashSupplier hasher = (a) -> ModuleHashes.computeHash(file, a); ++ HashSupplier hasher = (a) -> ModuleHashes.computeHash(supplier, a); + return newModule(attrs, uri, supplier, patcher, hasher); + } + +@@ -105,7 +105,7 @@ + static ModuleReference newJModModule(ModuleInfo.Attributes attrs, Path file) { + URI uri = file.toUri(); + Supplier supplier = () -> new JModModuleReader(file, uri); +- HashSupplier hasher = (a) -> ModuleHashes.computeHash(file, a); ++ HashSupplier hasher = (a) -> ModuleHashes.computeHash(supplier, a); + return newModule(attrs, uri, supplier, null, hasher); + } + diff --git a/gnu/packages/patches/openjdk-10-module3-reproducibility.patch b/gnu/packages/patches/openjdk-10-module3-reproducibility.patch new file mode 100644 index 0000000000..bc54803bea --- /dev/null +++ b/gnu/packages/patches/openjdk-10-module3-reproducibility.patch @@ -0,0 +1,34 @@ +Danny wrote. + +--- orig/jdk-3cc80be736f2/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java 1970-01-01 01:00:01.000000000 +0100 ++++ jdk-3cc80be736f2/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java 2022-04-13 17:30:37.242775977 +0200 +@@ -43,6 +43,7 @@ + import java.util.Objects; + import java.util.Optional; + import java.util.Set; ++import java.util.TreeSet; + import java.util.function.Supplier; + import java.util.stream.Collectors; + import java.util.stream.Stream; +@@ -2155,9 +2156,9 @@ + * @return The module descriptor + */ + public ModuleDescriptor build() { +- Set requires = new HashSet<>(this.requires.values()); +- Set exports = new HashSet<>(this.exports.values()); +- Set opens = new HashSet<>(this.opens.values()); ++ Set requires = new TreeSet<>(this.requires.values()); ++ Set exports = new TreeSet<>(this.exports.values()); ++ Set opens = new TreeSet<>(this.opens.values()); + + // add dependency on java.base + if (strict +@@ -2169,7 +2170,7 @@ + null)); + } + +- Set provides = new HashSet<>(this.provides.values()); ++ Set provides = new TreeSet<>(this.provides.values()); + + return new ModuleDescriptor(name, + version, diff --git a/gnu/packages/patches/openjdk-10-module4-reproducibility.patch b/gnu/packages/patches/openjdk-10-module4-reproducibility.patch new file mode 100644 index 0000000000..051c9344eb --- /dev/null +++ b/gnu/packages/patches/openjdk-10-module4-reproducibility.patch @@ -0,0 +1,14 @@ +Danny wrote it + +--- orig/jdk-6fa770f9f8ab/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java 2022-04-13 19:24:12.655749459 +0200 ++++ jdk-6fa770f9f8ab/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java 2022-04-14 01:53:23.555465018 +0200 +@@ -861,7 +861,8 @@ + */ + private void genModuleReads(ClassWriter cw, Configuration cf) { + // module name -> names of modules that it reads +- Map> map = cf.modules().stream() ++ Map> map = cf.modules().stream() ++ .sorted(java.util.Comparator.comparing(ResolvedModule::name)) + .collect(Collectors.toMap( + ResolvedModule::name, + m -> m.reads().stream()