Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package java-11-openjdk for openSUSE:Factory 
checked in at 2026-08-25 13:19:46
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/java-11-openjdk (Old)
 and      /work/SRC/openSUSE:Factory/.java-11-openjdk.new.1258 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "java-11-openjdk"

Tue Aug 25 13:19:46 2026 rev:89 rq:1373461 version:11.0.32.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/java-11-openjdk/java-11-openjdk.changes  
2026-07-22 19:08:01.823068952 +0200
+++ 
/work/SRC/openSUSE:Factory/.java-11-openjdk.new.1258/java-11-openjdk.changes    
    2026-08-25 13:19:51.737477100 +0200
@@ -1,0 +2,28 @@
+Mon Aug 24 16:50:27 UTC 2026 - Fridrich Strba <[email protected]>
+
+- Upgrade to upstream tag jdk-11.0.32.1+1 (August 2026 CSPU)
+  * Changes:
+    + JDK-8385390: Update FreeType to 2.14.3
+    + JDK-8384708, bsc#1275778, CVE-2026-61308: Enhance HTTP
+      Connections
+    + JDK-8386205, bsc#1275764, CVE-2026-70907: Enhance TLS server
+    + JDK-8382471, CVE-2026-60589: Improve Resource Resolving
+    + JDK-8389476: Bump update version for OpenJDK: jdk-11.0.32.1
+- Added patch:
+  * tzdata-2026c.patch
+    + backport upcoming upgrade of timezone data (bsc#1275035)
+
+-------------------------------------------------------------------
+Wed Jul 29 09:37:47 UTC 2026 - Bernhard Wiedemann <[email protected]>
+
+- Add patches for reproducible builds:
+  * reproducible-classlist-sort.patch
+  * reproducible-cldr-shortids-order.patch
+  * reproducible-generated-source-dates.patch
+  * reproducible-jar-timestamps.patch
+  * reproducible-jmod-entry-order.patch
+  * reproducible-jmod-timestamps.patch
+  * reproducible-module-descriptor-hashcode.patch
+  * reproducible-source-date-epoch.patch
+
+-------------------------------------------------------------------

Old:
----
  jdk-11.0.32+9.tar.gz

New:
----
  jdk-11.0.32.1+1.tar.gz
  reproducible-classlist-sort.patch
  reproducible-cldr-shortids-order.patch
  reproducible-generated-source-dates.patch
  reproducible-jar-timestamps.patch
  reproducible-jmod-entry-order.patch
  reproducible-jmod-timestamps.patch
  reproducible-module-descriptor-hashcode.patch
  reproducible-source-date-epoch.patch
  tzdata-2026c.patch

----------(New B)----------
  New:- Add patches for reproducible builds:
  * reproducible-classlist-sort.patch
  * reproducible-cldr-shortids-order.patch
  New:  * reproducible-classlist-sort.patch
  * reproducible-cldr-shortids-order.patch
  * reproducible-generated-source-dates.patch
  New:  * reproducible-cldr-shortids-order.patch
  * reproducible-generated-source-dates.patch
  * reproducible-jar-timestamps.patch
  New:  * reproducible-generated-source-dates.patch
  * reproducible-jar-timestamps.patch
  * reproducible-jmod-entry-order.patch
  New:  * reproducible-jar-timestamps.patch
  * reproducible-jmod-entry-order.patch
  * reproducible-jmod-timestamps.patch
  New:  * reproducible-jmod-entry-order.patch
  * reproducible-jmod-timestamps.patch
  * reproducible-module-descriptor-hashcode.patch
  New:  * reproducible-jmod-timestamps.patch
  * reproducible-module-descriptor-hashcode.patch
  * reproducible-source-date-epoch.patch
  New:  * reproducible-module-descriptor-hashcode.patch
  * reproducible-source-date-epoch.patch
  New:- Added patch:
  * tzdata-2026c.patch
    + backport upcoming upgrade of timezone data (bsc#1275035)
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ java-11-openjdk.spec ++++++
--- /var/tmp/diff_new_pack.OIzu2Y/_old  2026-08-25 13:19:58.568717647 +0200
+++ /var/tmp/diff_new_pack.OIzu2Y/_new  2026-08-25 13:19:58.571717753 +0200
@@ -43,7 +43,8 @@
 %global featurever      11
 %global interimver      0
 %global updatever       32
-%global buildver        9
+%global patchver        1
+%global buildver        1
 %global openjdk_repo    jdk11u
 %global openjdk_tag     
jdk-%{featurever}.%{interimver}.%{updatever}%{?patchver:.%{patchver}}+%{buildver}
 %global openjdk_dir     
%{openjdk_repo}-jdk-%{featurever}.%{interimver}.%{updatever}%{?patchver:.%{patchver}}-%{buildver}
@@ -233,9 +234,32 @@
 Patch22:        reproducible-jlink.patch
 Patch23:        reproducible-directory-mtime.patch
 Patch24:        reproducible-search-index-files.patch
+# Honour the SOURCE_DATE_EPOCH from the environment and derive the copyright
+# year from it (adapted from JDK-8282567, and from what JDK 17 already does)
+Patch25:        reproducible-source-date-epoch.patch
+# Sort the classlist, its order otherwise depends on thread scheduling
+# (JDK-8273092, in JDK 18)
+Patch26:        reproducible-classlist-sort.patch
+# Stamp jmod entries with SOURCE_DATE_EPOCH rather than the current time
+Patch27:        reproducible-jmod-timestamps.patch
+# Use SOURCE_DATE_EPOCH for the dates the CLDR converter, GenerateCharacter
+# and EquivMapsGenerator write into the sources they generate
+Patch28:        reproducible-generated-source-dates.patch
+# Write jmod entries in sorted order, not in file system order
+Patch29:        reproducible-jmod-entry-order.patch
 #
 Patch30:        JDK-8208602.patch
 Patch31:        bsc_1255446.patch
+# Stamp the entries of jrt-fs.jar and ct.sym with SOURCE_DATE_EPOCH, so that
+# java.base.jmod and jdk.compiler.jmod stop depending on the build time
+Patch32:        reproducible-jar-timestamps.patch
+# Insert the ZoneId.SHORT_IDS entries into the generated TimeZoneNames bundles
+# in a fixed order, rather than in the per-JVM-run random order of Map.of
+Patch33:        reproducible-cldr-shortids-order.patch
+# Derive ModuleDescriptor.hashCode from the modifier names rather than from
+# enum identity hash codes, which vary per JVM run (JDK-8275509, JDK-8290041).
+# jlink bakes that hash into the SystemModules classes in lib/modules.
+Patch34:        reproducible-module-descriptor-hashcode.patch
 #
 # OpenJDK specific patches
 #
@@ -247,6 +271,8 @@
 #
 Patch302:       disable-doclint-by-default.patch
 #
+Patch400:       tzdata-2026c.patch
+#
 Patch500:       activation-module.patch
 Patch501:       annotation-module.patch
 BuildRequires:  alsa-lib-devel
@@ -277,6 +303,9 @@
 BuildRequires:  libxslt
 BuildRequires:  mozilla-nss-devel >= 3.53
 BuildRequires:  pkgconfig
+%if 0%{?suse_version} >= 1500
+BuildRequires:  strip-nondeterminism
+%endif
 BuildRequires:  unzip
 BuildRequires:  xorg-x11-proto-devel
 BuildRequires:  xprop
@@ -540,9 +569,17 @@
 %patch -P 22 -p1
 %patch -P 23 -p1
 %patch -P 24 -p1
+%patch -P 25 -p1
+%patch -P 26 -p1
+%patch -P 27 -p1
+%patch -P 28 -p1
+%patch -P 29 -p1
 
 %patch -P 30 -p1
 %patch -P 31 -p1
+%patch -P 32 -p1
+%patch -P 33 -p1
+%patch -P 34 -p1
 
 # s390 build fixes
 
@@ -559,6 +596,8 @@
 
 %patch -P 302 -p1
 
+%patch -P 400 -p1
+
 %patch -P 500
 %patch -P 501
 
@@ -983,6 +1022,9 @@
   | sed 's|^|%doc |' \
   >> %{name}-demo.files
 
+%if 0%?have_strip_nondeterminism
+strip-all-nondeterminism %{buildroot}/%{_jvmdir}
+%endif
 # fdupes links the files from JDK to JRE, so it breaks a JRE
 # use it carefully :))
 %fdupes -s %{buildroot}/%{_jvmdir}/%{sdkdir}/

++++++ jdk-11.0.32+9.tar.gz -> jdk-11.0.32.1+1.tar.gz ++++++
/work/SRC/openSUSE:Factory/java-11-openjdk/jdk-11.0.32+9.tar.gz 
/work/SRC/openSUSE:Factory/.java-11-openjdk.new.1258/jdk-11.0.32.1+1.tar.gz 
differ: char 28, line 1

++++++ reproducible-classlist-sort.patch ++++++
# 8273092: Sort classlist in JDK image
#
# Reviewed-by: iklam, erikj
# (cherry picked from commit 1996f649a3a30b7ac4b547a762417f807f5fa414)
#
# Backported to make openSUSE java-11-openjdk reproducible. The classlist
# records the order in which classes happen to be loaded, and threads such
# as Finalizer$FinalizerThread load classes concurrently with the main
# thread, so the order varies between builds. Two openSUSE builds differed
# by jdk/internal/misc/VM moving three lines down in lib/classlist, which
# also changes java.base.jmod, since the classlist is copied into it.
#
# Sorting the classlist removes the ordering dependency entirely.
#
# Adapted for 11:
#   - JDK 11 writes plain class names (classFileParser.cpp does
#     classlist_file->print_cr("%s", ...)), with no "id:" suffixes and no
#     @lambda-form-invoker lines, so the tool sorts one flat list. Its
#     handling of "#" and "@" lines is inert here but is kept so the file
#     stays identical to the one in 17u.
#   - the JDK 11 recipe generates the classlist in a single java run, so
#     the sorting step is appended to that one recipe.
#
# Note that the classlist is also passed to jlink --order-resources, so
# this changes the resource order inside lib/modules. JDK 17 and later
# sort the classlist and feed it to jlink the same way.
#

diff --git a/make/GenerateLinkOptData.gmk b/make/GenerateLinkOptData.gmk
index 7a98cb36a1d..445c1717856 100644
--- a/make/GenerateLinkOptData.gmk
+++ b/make/GenerateLinkOptData.gmk
@@ -66,7 +66,10 @@ $(CLASSLIST_FILE): 
$(INTERIM_IMAGE_DIR)/bin/java$(EXECUTABLE_SUFFIX) $(CLASSLIST
            -cp $(SUPPORT_OUTPUTDIR)/classlist.jar \
            build.tools.classlist.HelloClasslist \
            $(LOG_DEBUG) 2>&1 > $(JLI_TRACE_FILE)
-       $(GREP) -v HelloClasslist [email protected] > $@
+       $(GREP) -v HelloClasslist [email protected] > [email protected]
+       $(FIXPATH) $(INTERIM_IMAGE_DIR)/bin/java \
+           -cp $(SUPPORT_OUTPUTDIR)/classlist.jar \
+           build.tools.classlist.SortClasslist [email protected] > $@
 
 # The jli trace is created by the same recipe as classlist. By declaring these
 # dependencies, make will correctly rebuild both jli trace and classlist
diff --git a/make/jdk/src/classes/build/tools/classlist/SortClasslist.java 
b/make/jdk/src/classes/build/tools/classlist/SortClasslist.java
new file mode 100644
index 00000000000..872d00c6d06
--- /dev/null
+++ b/make/jdk/src/classes/build/tools/classlist/SortClasslist.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2021,2023 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * This application is meant to be run to create a classlist file representing
+ * common use.
+ *
+ * The classlist is produced by adding -XX:DumpLoadedClassList=classlist
+ */
+package build.tools.classlist;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.Scanner;
+
+/**
+ * The classlist generated by build.tools.classlist.HelloClasslist
+ * may have non-deterministic contents, affected by Java thread execution 
order.
+ * SortClasslist sorts the file to make the JDK image's contents more 
deterministic.
+ */
+public class SortClasslist {
+    public static void main(String args[]) throws FileNotFoundException  {
+        ArrayList<String> classes = new ArrayList<>();
+        ArrayList<String> lambdas = new ArrayList<>();
+
+        FileInputStream fis = new FileInputStream(args[0]);
+        Scanner scanner = new Scanner(fis);
+        while (scanner.hasNextLine()) {
+            String line = scanner.nextLine();
+            if (line.startsWith("#")) {
+                // Comments -- print them first without sorting. These appear 
only at the top
+                // of the file.
+                System.out.println(line);
+            } else if (line.startsWith("@")) {
+                // @lambda-form-invoker, @lambda-proxy, etc.
+                lambdas.add(line);
+            } else {
+                // Class name line
+                classes.add(line);
+            }
+        }
+
+        Collections.sort(classes);
+        Collections.sort(lambdas);
+
+        for (String s : classes) {
+            System.out.println(s);
+        }
+        for (String s : lambdas) {
+            System.out.println(s);
+        }
+    }
+}

++++++ reproducible-cldr-shortids-order.patch ++++++
# Insert the ZoneId.SHORT_IDS compatibility entries in a fixed order
#
# ZoneId.SHORT_IDS is built with Map.ofEntries, and the immutable maps
# introduced in JDK 9 randomize their iteration order once per JVM run.
# CLDRConverter.extractZoneNames() collects into a HashMap, so that random
# order decides the relative order of any two short ids that hash to the
# same bucket, and ResourceBundleGenerator then writes the map out in
# iteration order.
#
# The result is that a generated TimeZoneNames bundle differs between two
# otherwise identical builds. TimeZoneNames_kea.java, for instance, has
# "PST" and "AET" in the same bucket and emits them in either order, which
# propagates into lib/src.zip, jdk.localedata.jmod, lib/modules, and - via
# the module hashes recorded in its module-info - java.base.jmod.
#
# Sort the entries by key before inserting them. Still present upstream.
#

diff --git a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java 
b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java
index 068b8701d70..5954462573b 100644
--- a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java
+++ b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java
@@ -718,8 +718,14 @@ public class CLDRConverter {
             names.put("UTC", META_EMPTY_ZONE_NAME);
         }
 
-        // Finally some compatibility stuff
+        // Finally some compatibility stuff.
+        // ZoneId.SHORT_IDS is an immutable map, whose iteration order is
+        // randomized per JVM run. Since "names" is a HashMap, that insertion
+        // order decides the order of any two of these ids that end up in the
+        // same bucket, and with it the order of the entries in the generated
+        // TimeZoneNames bundles. Insert them in a fixed order instead.
         ZoneId.SHORT_IDS.entrySet().stream()
+            .sorted(Map.Entry.comparingByKey())
             .filter(e -> !names.containsKey(e.getKey()) && 
names.containsKey(e.getValue()))
             .forEach(e -> {
                 names.put(e.getKey(), names.get(e.getValue()));

++++++ reproducible-generated-source-dates.patch ++++++
# Use SOURCE_DATE_EPOCH for dates written into generated sources
#
# Three build tools stamp the current date into the sources they generate,
# so the result depends on when the build runs. All three show up in the
# shipped lib/src.zip and in the jmods and lib/modules built from them.
#
#   - CopyrightHeaders.getYear() in the CLDR converter used the current
#     year, which put "Copyright (c) 2012, 2026" into one build and
#     "2012, 2027" into the next, across more than a thousand generated
#     locale sources.
#
#   - GenerateCharacter wrote "This file was generated AUTOMATICALLY from
#     a template file <full timestamp>" into the five CharacterData*.java
#     files.
#
#   - EquivMapsGenerator did the same as the CLDR converter for the
#     copyright header of sun/util/locale/LocaleEquivalentMaps.java.
#
# Take the date from SOURCE_DATE_EPOCH when it is set, and keep the
# current time otherwise. The CLDR converter and EquivMapsGenerator keep
# computing the year in America/Los_Angeles, as before, so only the
# instant changes.
#
# Note that this is separate from the COPYRIGHT_YEAR that configure
# computes: that one already follows SOURCE_DATE_EPOCH and fixed the
# javadoc bundle, but these tools never consulted it.
#

diff --git 
a/make/jdk/src/classes/build/tools/cldrconverter/CopyrightHeaders.java 
b/make/jdk/src/classes/build/tools/cldrconverter/CopyrightHeaders.java
index 89c7e0f41aa..d714dd27292 100644
--- a/make/jdk/src/classes/build/tools/cldrconverter/CopyrightHeaders.java
+++ b/make/jdk/src/classes/build/tools/cldrconverter/CopyrightHeaders.java
@@ -150,8 +150,20 @@ class CopyrightHeaders {
     }
 
     private static int getYear() {
-        return new 
GregorianCalendar(TimeZone.getTimeZone("America/Los_Angeles"),
-                                         Locale.US).get(Calendar.YEAR);
+        GregorianCalendar cal =
+            new GregorianCalendar(TimeZone.getTimeZone("America/Los_Angeles"),
+                                  Locale.US);
+        String sourceDateEpoch = System.getenv("SOURCE_DATE_EPOCH");
+        if (sourceDateEpoch != null && !sourceDateEpoch.isEmpty()) {
+            // Use the source date rather than the current time, so that the
+            // generated files do not depend on when the build is run.
+            try {
+                cal.setTimeInMillis(Long.parseLong(sourceDateEpoch.trim()) * 
1000L);
+            } catch (NumberFormatException e) {
+                // Not a valid timestamp, keep the current time.
+            }
+        }
+        return cal.get(Calendar.YEAR);
     }
 
     // no instantiation
diff --git 
a/make/jdk/src/classes/build/tools/generatecharacter/GenerateCharacter.java 
b/make/jdk/src/classes/build/tools/generatecharacter/GenerateCharacter.java
index 88a8d82f893..fd553f35c42 100644
--- a/make/jdk/src/classes/build/tools/generatecharacter/GenerateCharacter.java
+++ b/make/jdk/src/classes/build/tools/generatecharacter/GenerateCharacter.java
@@ -686,6 +686,22 @@ OUTER:  for (int i = 0; i < n; i += m) {
     *     @see GenerateCharacter#replaceCommand
     */
 
+    /**
+     * The date stamped into the generated files. Use SOURCE_DATE_EPOCH when it
+     * is set, so that the generated files do not depend on when the build 
runs.
+     */
+    static java.util.Date generationDate() {
+        String sourceDateEpoch = System.getenv("SOURCE_DATE_EPOCH");
+        if (sourceDateEpoch != null && !sourceDateEpoch.isEmpty()) {
+            try {
+                return new 
java.util.Date(Long.parseLong(sourceDateEpoch.trim()) * 1000L);
+            } catch (NumberFormatException e) {
+                // Not a valid timestamp, fall through to the current time.
+            }
+        }
+        return new java.util.Date();
+    }
+
     static void generateCharacterClass(String theTemplateFileName,
                                        String theOutputFileName)
         throws FileNotFoundException, IOException {
@@ -693,7 +709,7 @@ OUTER:  for (int i = 0; i < n; i += m) {
         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);
+            generationDate() + commentEnd);
         int marklen = commandMarker.length();
         LOOP: while(true) {
             try {
diff --git 
a/make/jdk/src/classes/build/tools/generatelsrequivmaps/EquivMapsGenerator.java 
b/make/jdk/src/classes/build/tools/generatelsrequivmaps/EquivMapsGenerator.java
index cffaed7186a..c8660c90c15 100644
--- 
a/make/jdk/src/classes/build/tools/generatelsrequivmaps/EquivMapsGenerator.java
+++ 
b/make/jdk/src/classes/build/tools/generatelsrequivmaps/EquivMapsGenerator.java
@@ -30,6 +30,7 @@ import java.io.IOException;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.time.Instant;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.util.ArrayList;
@@ -266,9 +267,27 @@ public class EquivMapsGenerator {
         + "}";
 
     private static String getOpenJDKCopyright() {
-        int year = ZonedDateTime.now(ZoneId
-                .of("America/Los_Angeles")).getYear();
-        return String.format(Locale.US, COPYRIGHT, year);
+        return String.format(Locale.US, COPYRIGHT, getYear());
+    }
+
+    /**
+     * The copyright year written into the generated source file. Use
+     * SOURCE_DATE_EPOCH when it is set, so that the generated file does not
+     * depend on when the build is run.
+     */
+    private static int getYear() {
+        ZoneId zone = ZoneId.of("America/Los_Angeles");
+        String sourceDateEpoch = System.getenv("SOURCE_DATE_EPOCH");
+        if (sourceDateEpoch != null && !sourceDateEpoch.isEmpty()) {
+            try {
+                return ZonedDateTime.ofInstant(
+                        
Instant.ofEpochSecond(Long.parseLong(sourceDateEpoch.trim())),
+                        zone).getYear();
+            } catch (NumberFormatException e) {
+                // Not a valid timestamp, fall back to the current time.
+            }
+        }
+        return ZonedDateTime.now(zone).getYear();
     }
 
     /**

++++++ reproducible-jar-timestamps.patch ++++++
# Stamp jrt-fs.jar and ct.sym entries with SOURCE_DATE_EPOCH
#
# The jar command of this release takes the entry times from the
# modification times of the input files, and stamps the entries it creates
# itself (META-INF/ and META-INF/MANIFEST.MF) with the current time, so
# every archive built by SetupJarArchive differs between builds. Upstream
# solved this in JDK 19 with the jar --date option (JDK-8276766), which the
# boot JDK used to build this release does not have.
#
# The two archives that matter here are lib/jrt-fs.jar and lib/ct.sym,
# because they are also packed into java.base.jmod and jdk.compiler.jmod -
# these were the last two files that still differed between two openSUSE
# builds. Normalising the installed image does not reach the copies inside
# the jmods.
#
# Add a build tool that rewrites the entry timestamps of a finished archive
# to SOURCE_DATE_EPOCH, leaving names, order, contents and compression
# method alone, and a REPRODUCIBLE_TIMESTAMPS argument to SetupJarArchive
# that runs it. It is opt-in because the tool lives in buildtools-jdk,
# which the buildtools-hotspot jars cannot depend on. When SOURCE_DATE_EPOCH
# is unset nothing changes.
#

diff --git a/make/JrtfsJar.gmk b/make/JrtfsJar.gmk
index e35c8220cd0..589eb0f90eb 100644
--- a/make/JrtfsJar.gmk
+++ b/make/JrtfsJar.gmk
@@ -67,6 +67,7 @@ $(eval $(call SetupJarArchive,BUILD_JRTFS_JAR, \
     SRCS := $(SUPPORT_OUTPUTDIR)/jrtfs_classes, \
     JAR := $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/jrt-fs.jar, \
     MANIFEST := $(SUPPORT_OUTPUTDIR)/java-main-manifest.mf, \
+    REPRODUCIBLE_TIMESTAMPS := true, \
 ))
 
 all: $(BUILD_JRTFS_JAR)
diff --git a/make/Main.gmk b/make/Main.gmk
index 2c08532626a..66451a1e0fc 100644
--- a/make/Main.gmk
+++ b/make/Main.gmk
@@ -894,7 +894,8 @@ else
 
   zip-source: gensrc rmic
 
-  jrtfs-jar: interim-langtools
+  # buildtools-jdk is needed to make the jar entry timestamps reproducible.
+  jrtfs-jar: interim-langtools buildtools-jdk
 
   ifeq ($(ENABLE_GENERATE_CLASSLIST), true)
     ifeq ($(CREATE_BUILDJDK), true)
diff --git a/make/common/JarArchive.gmk b/make/common/JarArchive.gmk
index e1ec1427a88..0e39a377fdb 100644
--- a/make/common/JarArchive.gmk
+++ b/make/common/JarArchive.gmk
@@ -57,6 +57,9 @@ FALSE_FIND_PATTERN:=-name FILE_NAME_THAT_DOESNT_EXIST
 #   EXTRA_MANIFEST_ATTR:=Extra attribute to add to manifest.
 #   CHECK_COMPRESS_JAR Check the COMPRESS_JAR variable
 #   JAR_CMD:=Optionally override the jar command to use when creating the 
archive.
+#   REPRODUCIBLE_TIMESTAMPS:=true means rewrite the entry timestamps of the
+#       finished archive to SOURCE_DATE_EPOCH. Requires the jdk build tools,
+#       so the target must depend on buildtools-jdk.
 SetupJarArchive = $(NamedParamsMacroTemplate)
 define SetupJarArchiveBody
 
@@ -68,6 +71,20 @@ define SetupJarArchiveBody
   $1_BIN:=$$(dir $$($1_JAR))
   $$(call SetIfEmpty, $1_JAR_CMD, $$(JAR))
 
+  # The jar command takes the entry times from the input files, and stamps the
+  # entries it creates itself (META-INF/ and META-INF/MANIFEST.MF, and the
+  # index) with the current time, so its output differs between builds. Later
+  # releases pass jar --date, which the boot JDK used here does not support,
+  # so rewrite the timestamps once the archive is complete instead.
+  $1_FIXUP_TIMESTAMPS := true
+  ifeq ($$($1_REPRODUCIBLE_TIMESTAMPS), true)
+    ifneq ($(SOURCE_DATE_EPOCH), )
+      $1_FIXUP_TIMESTAMPS := $(JAVA_SMALL) \
+          -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \
+          build.tools.jarreproducible.JarReproducible $$($1_JAR)
+    endif
+  endif
+
   ifeq (,$$($1_SUFFIXES))
     # No suffix was set, default to classes.
     $1_SUFFIXES:=.class
@@ -230,7 +247,8 @@ define SetupJarArchiveBody
   # Include all variables of significance in the vardeps file
   $1_VARDEPS := $$($1_JAR_CMD) $$($1_JAR_CREATE_OPTIONS) $$($1_MANIFEST) \
       $$($1_JARMAIN) $$($1_EXTRA_MANIFEST_ATTR) $$($1_ORIG_DEPS) $$($1_SRCS) \
-      $$($1_INCLUDES) $$($1_EXCLUDES) $$($1_EXCLUDE_FILES) $$($1_EXTRA_FILES)
+      $$($1_INCLUDES) $$($1_EXCLUDES) $$($1_EXCLUDE_FILES) $$($1_EXTRA_FILES) \
+      $$($1_FIXUP_TIMESTAMPS)
   $1_VARDEPS_FILE := $$(call DependOnVariable, $1_VARDEPS, $$(dir 
$$($1_JAR))_the.$$($1_JARNAME).vardeps)
 
   # Here is the rule that creates/updates the jar file.
@@ -270,6 +288,7 @@ define SetupJarArchiveBody
          fi $$(NEWLINE) \
          $$($1_UPDATE_CONTENTS) true $$(NEWLINE) \
          $$($1_JARINDEX) && true )
+       $$($1_FIXUP_TIMESTAMPS)
 
   # Add jar to target list
   $1 += $$($1_JAR)
diff --git a/make/gendata/Gendata-jdk.compiler.gmk 
b/make/gendata/Gendata-jdk.compiler.gmk
index 1d034828598..976ba758a7f 100644
--- a/make/gendata/Gendata-jdk.compiler.gmk
+++ b/make/gendata/Gendata-jdk.compiler.gmk
@@ -91,6 +91,7 @@ $(eval $(call SetupJarArchive, CREATE_CTSYM, \
     SRCS := $(SUPPORT_OUTPUTDIR)/symbols/ct.sym-files, \
     SUFFIXES := .sig system-modules, \
     JAR := $(SUPPORT_OUTPUTDIR)/symbols/ct.sym, \
+    REPRODUCIBLE_TIMESTAMPS := true, \
 ))
 
 # Copy ct.sym to the modules libs dir
diff --git 
a/make/jdk/src/classes/build/tools/jarreproducible/JarReproducible.java 
b/make/jdk/src/classes/build/tools/jarreproducible/JarReproducible.java
new file mode 100644
index 00000000000..87a39525abc
--- /dev/null
+++ b/make/jdk/src/classes/build/tools/jarreproducible/JarReproducible.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2026, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package build.tools.jarreproducible;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * Rewrites the given jar files so that every entry is stamped with the time
+ * given by SOURCE_DATE_EPOCH instead of the time the build happened to run.
+ *
+ * The jar command of this release takes entry times from the modification
+ * times of the input files, and stamps the entries it generates itself
+ * (META-INF/ and META-INF/MANIFEST.MF) with the current time, so the archives
+ * it produces differ between builds. Later releases solve this with the
+ * jar --date option, which is not available in the boot JDK used here.
+ *
+ * Entry names, order, contents and compression method are left untouched, so
+ * this only removes the timestamps as a source of difference. If
+ * SOURCE_DATE_EPOCH is not set the files are left alone.
+ */
+public class JarReproducible {
+
+    public static void main(String[] args) throws IOException {
+        String sourceDateEpoch = System.getenv("SOURCE_DATE_EPOCH");
+        if (sourceDateEpoch == null || sourceDateEpoch.trim().isEmpty()) {
+            return;
+        }
+        long time;
+        try {
+            time = Long.parseLong(sourceDateEpoch.trim()) * 1000L;
+        } catch (NumberFormatException e) {
+            return;
+        }
+        for (String arg : args) {
+            normalize(Paths.get(arg), time);
+        }
+    }
+
+    private static void normalize(Path jar, long time) throws IOException {
+        Path tmp = jar.resolveSibling(jar.getFileName() + ".tmp");
+        try (ZipFile in = new ZipFile(jar.toFile());
+             ZipOutputStream out =
+                 new ZipOutputStream(Files.newOutputStream(tmp))) {
+            Enumeration<? extends ZipEntry> entries = in.entries();
+            while (entries.hasMoreElements()) {
+                ZipEntry entry = entries.nextElement();
+                ZipEntry copy = new ZipEntry(entry.getName());
+                copy.setMethod(entry.getMethod());
+                copy.setTime(time);
+                copy.setComment(entry.getComment());
+                // The extra field is deliberately not carried over. The jar
+                // command does not add any, and the ones ZipFile hands out
+                // from the central directory (zip64, timestamps) would either
+                // be regenerated by ZipOutputStream or reintroduce a time.
+                if (entry.getMethod() == ZipEntry.STORED) {
+                    copy.setSize(entry.getSize());
+                    copy.setCompressedSize(entry.getCompressedSize());
+                    copy.setCrc(entry.getCrc());
+                }
+                out.putNextEntry(copy);
+                try (InputStream is = in.getInputStream(entry)) {
+                    is.transferTo(out);
+                }
+                out.closeEntry();
+            }
+            out.setComment(in.getComment());
+        }
+        Files.move(tmp, jar, StandardCopyOption.REPLACE_EXISTING);
+    }
+}

++++++ reproducible-jmod-entry-order.patch ++++++
# Write jmod entries in sorted order
#
# JmodTask walks the input directories with Files.walkFileTree and writes
# each file as it is visited, so the order of the entries in the resulting
# jmod is whatever order the file system returned the directory entries
# in. Eight of the shipped jmods differed between two openSUSE builds for
# this reason: the entry timestamps and the contents matched, but the
# entries appeared at different offsets.
#
# Collect the files first, sort them by their relative path, and then
# write them out.
#
# The jar files handled by processClasses keep their existing order, which
# comes from the jar itself and is already deterministic.
#

diff --git a/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java 
b/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java
index 0f6b488bf5e..68c9faf73d0 100644
--- a/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java
+++ b/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java
@@ -773,33 +773,40 @@ public class JmodTask {
         void processSection(JmodOutputStream out, Section section, Path path)
             throws IOException
         {
+            // Collect the files first and sort them, so that the entry order 
in
+            // the resulting jmod does not depend on the order in which the 
file
+            // system happens to return directory entries.
+            List<Path> files = new ArrayList<>();
             Files.walkFileTree(path, Set.of(FileVisitOption.FOLLOW_LINKS),
                 Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
                     @Override
-                    public FileVisitResult visitFile(Path file, 
BasicFileAttributes attrs)
-                        throws IOException
-                    {
-                        Path relPath = path.relativize(file);
-                        if (relPath.toString().equals(MODULE_INFO)
-                                && !Section.CLASSES.equals(section))
-                            warning("warn.ignore.entry", MODULE_INFO, section);
-
-                        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;
-                            }
-                        }
+                    public FileVisitResult visitFile(Path file, 
BasicFileAttributes attrs) {
+                        files.add(file);
                         return FileVisitResult.CONTINUE;
                     }
                 });
+            files.sort(Comparator.comparing(f -> 
path.relativize(f).toString()));
+
+            for (Path file : files) {
+                Path relPath = path.relativize(file);
+                if (relPath.toString().equals(MODULE_INFO)
+                        && !Section.CLASSES.equals(section))
+                    warning("warn.ignore.entry", MODULE_INFO, section);
+
+                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);
+                            continue;
+                        }
+                        throw x;
+                    }
+                }
+            }
         }
 
         boolean matches(Path path, List<PathMatcher> matchers) {

++++++ reproducible-jmod-timestamps.patch ++++++
# Stamp jmod entries with SOURCE_DATE_EPOCH instead of the current time
#
# JmodOutputStream.newEntry() returns a ZipEntry without a time, so
# ZipOutputStream.putNextEntry() fills in System.currentTimeMillis() for
# every entry. Each build therefore writes a different DOS timestamp into
# the local file header of every entry, which is why all of the roughly 70
# shipped jmods differ between two openSUSE builds while their contents are
# identical.
#
# Use SOURCE_DATE_EPOCH for the entry time when it is set in the
# environment, and keep the previous behaviour when it is not.
#
# JDK 19 solved this upstream with the jmod --date option (JDK-8276766),
# which also required jmod command line and ZipEntry.setTimeLocal()
# support that JDK 11 does not have, so this is an openSUSE specific
# change rather than a backport.
#
# Note that ZipEntry.setTime() converts to the DOS timestamp using the
# local time zone, so the build still has to run with a fixed TZ, which
# the openSUSE build service already guarantees.
#

--- a/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodOutputStream.java
+++ b/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodOutputStream.java
@@ -101,7 +101,11 @@ class JmodOutputStream extends OutputStream implements 
AutoCloseable {
         String prefix = section.jmodDir();
         String name = Paths.get(prefix, path).toString()
                            .replace(File.separatorChar, '/');
-        return new ZipEntry(name);
+        ZipEntry ze = new ZipEntry(name);
+        String sde = System.getenv("SOURCE_DATE_EPOCH");
+        if (sde != null)
+            ze.setTime(1000 * Long.parseLong(sde));
+        return ze;
     }

     @Override

++++++ reproducible-module-descriptor-hashcode.patch ++++++
# Backport of JDK-8275509 and its follow-up JDK-8290041:
#   8275509: ModuleDescriptor.hashCode isn't reproducible across builds
#   8290041: ModuleDescriptor.hashCode is inconsistent
#
# ModuleDescriptor.hashCode() and the hashCode() of Requires, Exports and
# Opens folded in the hash of their Set of modifiers. Those are sets of
# enums, and Enum.hashCode() is the identity hash code, which depends on
# the state of HotSpot's per-thread PRNG when the constant is first hashed
# - so it varies between JVM runs, and with it the descriptor hash.
#
# jlink bakes ModuleDescriptor.hashCode() into the SystemModules classes it
# generates (SystemModulesPlugin.putModuleDescriptor calls
# mv.visitLdcInsn(md.hashCode())), so this landed in lib/modules as an int
# constant that changed from build to build. It was the last remaining
# difference between two openSUSE builds, and it is sensitive to anything
# that shifts execution order in the jlink JVM - such as the number of
# build cores.
#
# Hash the modifier names instead of the enum instances. JDK-8290041 is the
# follow-up that makes the sum order independent again, which matters here
# because the modifier sets are Set.of instances whose iteration order is
# also randomized per JVM run - without it the first fix alone would leave
# the same symptom.
#
# Note that jlink runs from the JDK being built (BUILD_JDK defaults to
# JDK_OUTPUTDIR for a native build), so patching java.base takes effect for
# the image built in the same run.
#

diff --git a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java 
b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java
index fa359348f6c..f9ebfe1559d 100644
--- a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java
+++ b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java
@@ -333,7 +333,7 @@ public class ModuleDescriptor
          */
         @Override
         public int hashCode() {
-            int hash = name.hashCode() * 43 + mods.hashCode();
+            int hash = name.hashCode() * 43 + modsHashCode(mods);
             if (compiledVersion != null)
                 hash = hash * 43 + compiledVersion.hashCode();
             if (rawCompiledVersion != null)
@@ -514,7 +514,7 @@ public class ModuleDescriptor
          */
         @Override
         public int hashCode() {
-            int hash = mods.hashCode();
+            int hash = modsHashCode(mods);
             hash = hash * 43 + source.hashCode();
             return hash * 43 + targets.hashCode();
         }
@@ -721,7 +721,7 @@ public class ModuleDescriptor
          */
         @Override
         public int hashCode() {
-            int hash = mods.hashCode();
+            int hash = modsHashCode(mods);
             hash = hash * 43 + source.hashCode();
             return hash * 43 + targets.hashCode();
         }
@@ -2283,7 +2283,7 @@ public class ModuleDescriptor
         int hc = hash;
         if (hc == 0) {
             hc = name.hashCode();
-            hc = hc * 43 + Objects.hashCode(modifiers);
+            hc = hc * 43 + modsHashCode(modifiers);
             hc = hc * 43 + requires.hashCode();
             hc = hc * 43 + Objects.hashCode(packages);
             hc = hc * 43 + exports.hashCode();
@@ -2568,6 +2568,18 @@ public class ModuleDescriptor
                 .collect(Collectors.joining(" "));
     }
 
+    /**
+     * Generates and returns a hashcode for the enum instances. The returned 
hashcode
+     * is a value based on the {@link Enum#name() name} of each enum instance.
+     */
+    private static int modsHashCode(Iterable<? extends Enum<?>> enums) {
+        int h = 0;
+        for (Enum<?> e : enums) {
+            h += e.name().hashCode();
+        }
+        return h;
+    }
+
     private static <T extends Object & Comparable<? super T>>
     int compare(T obj1, T obj2) {
         if (obj1 != null) {
diff --git a/test/jdk/java/lang/module/ModuleDescriptorHashCodeTest.java 
b/test/jdk/java/lang/module/ModuleDescriptorHashCodeTest.java
new file mode 100644
index 00000000000..c1269f28c48
--- /dev/null
+++ b/test/jdk/java/lang/module/ModuleDescriptorHashCodeTest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2021, 2022, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleDescriptor.Exports;
+import java.lang.module.ModuleDescriptor.Opens;
+import java.lang.module.ModuleDescriptor.Requires;
+import java.util.Set;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotSame;
+
+/**
+ * @test
+ * @bug 8275509 8290041
+ * @summary Tests the ModuleDescriptor.hashCode()
+ * @run testng ModuleDescriptorHashCodeTest
+ * @run testng/othervm -Xshare:off ModuleDescriptorHashCodeTest
+ */
+public class ModuleDescriptorHashCodeTest {
+
+    /**
+     * Verifies that the ModuleDescriptor.hashCode() returned by a boot layer 
module is
+     * the same as that returned by a ModuleDescriptor constructed from the 
ModuleDescriptor.Builder
+     * for the same module.
+     */
+    @Test
+    public void testBootModuleDescriptor() throws Exception {
+        Set<Module> bootModules = ModuleLayer.boot().modules();
+        for (Module bootModule : bootModules) {
+            System.out.println("Testing module descriptor of boot module " + 
bootModule);
+            ModuleDescriptor bootMD = bootModule.getDescriptor();
+            ModuleDescriptor mdFromBuilder = fromModuleInfoClass(bootModule);
+            // verify that this object is indeed a different object instance 
than the boot module descriptor
+            // to prevent any artificial passing of the test
+            assertNotSame(mdFromBuilder, bootMD, "ModuleDescriptor loaded from 
boot layer and " +
+                    "one created from module-info.class unexpectedly returned 
the same instance");
+            assertEquals(mdFromBuilder.hashCode(), bootMD.hashCode(),
+                    "Unexpected ModuleDescriptor.hashCode() for " + 
mdFromBuilder);
+            assertEquals(mdFromBuilder.compareTo(bootMD), 0,
+                    "Unexpected ModuleDescriptor.compareTo() for " + 
mdFromBuilder);
+        }
+    }
+
+    /**
+     * Verifies that two "equal" module descriptors which only differ in the 
order of
+     * {@link ModuleDescriptor.Opens.Modifier opens modifiers}, that were used 
to construct the
+     * descriptors, have the same hashcode.
+     */
+    @Test
+    public void testOpensModifiersOrdering() throws Exception {
+        // important to use Set.of() (i.e. backed by immutable set) to 
reproduce the issue
+        final Set<Opens.Modifier> mods1 = Set.of(Opens.Modifier.SYNTHETIC, 
Opens.Modifier.MANDATED);
+        final ModuleDescriptor desc1 = createModuleDescriptor(mods1, null, 
null);
+
+        // create the same module descriptor again and this time just change 
the order of the
+        // "opens" modifiers' Set.
+
+        // important to use Set.of() (i.e. backed by immutable set) to 
reproduce the issue
+        final Set<Opens.Modifier> mods2 = Set.of(Opens.Modifier.MANDATED, 
Opens.Modifier.SYNTHETIC);
+        final ModuleDescriptor desc2 = createModuleDescriptor(mods2, null, 
null);
+
+        // basic verification of the modifiers themselves before we check the 
module descriptors
+        assertEquals(mods1, mods2, "Modifiers were expected to be equal");
+
+        // now verify the module descriptors
+        assertEquals(desc1, desc2, "Module descriptors were expected to be 
equal");
+        assertEquals(desc1.compareTo(desc2), 0, "compareTo was expected to 
return" +
+                " 0 for module descriptors that are equal");
+        System.out.println(desc1 + " hashcode = " + desc1.hashCode());
+        System.out.println(desc2 + " hashcode = " + desc2.hashCode());
+        assertEquals(desc1.hashCode(), desc2.hashCode(), "Module descriptor 
hashcodes" +
+                " were expected to be equal");
+    }
+
+    /**
+     * Verifies that two "equal" module descriptors which only differ in the 
order of
+     * {@link ModuleDescriptor.Exports.Modifier exports modifiers}, that were 
used to construct the
+     * descriptors, have the same hashcode.
+     */
+    @Test
+    public void testExportsModifiersOrdering() throws Exception {
+        // important to use Set.of() (i.e. backed by immutable set) to 
reproduce the issue
+        final Set<Exports.Modifier> mods1 = Set.of(Exports.Modifier.SYNTHETIC, 
Exports.Modifier.MANDATED);
+        final ModuleDescriptor desc1 = createModuleDescriptor(null, null, 
mods1);
+
+        // create the same module descriptor again and this time just change 
the order of the
+        // "exports" modifiers' Set.
+
+        // important to use Set.of() (i.e. backed by immutable set) to 
reproduce the issue
+        final Set<Exports.Modifier> mods2 = Set.of(Exports.Modifier.MANDATED, 
Exports.Modifier.SYNTHETIC);
+        final ModuleDescriptor desc2 = createModuleDescriptor(null, null, 
mods2);
+
+        // basic verification of the modifiers themselves before we check the 
module descriptors
+        assertEquals(mods1, mods2, "Modifiers were expected to be equal");
+
+        // now verify the module descriptors
+        assertEquals(desc1, desc2, "Module descriptors were expected to be 
equal");
+        assertEquals(desc1.compareTo(desc2), 0, "compareTo was expected to 
return" +
+                " 0 for module descriptors that are equal");
+        System.out.println(desc1 + " hashcode = " + desc1.hashCode());
+        System.out.println(desc2 + " hashcode = " + desc2.hashCode());
+        assertEquals(desc1.hashCode(), desc2.hashCode(), "Module descriptor 
hashcodes" +
+                " were expected to be equal");
+    }
+
+    /**
+     * Verifies that two "equal" module descriptors which only differ in the 
order of
+     * {@link ModuleDescriptor.Requires.Modifier requires modifiers}, that 
were used to construct the
+     * descriptors, have the same hashcode.
+     */
+    @Test
+    public void testRequiresModifiersOrdering() throws Exception {
+        // important to use Set.of() (i.e. backed by immutable set) to 
reproduce the issue
+        final Set<Requires.Modifier> mods1 = 
Set.of(Requires.Modifier.SYNTHETIC, Requires.Modifier.MANDATED);
+        final ModuleDescriptor desc1 = createModuleDescriptor(null, mods1, 
null);
+
+        // create the same module descriptor again and this time just change 
the order of the
+        // "exports" modifiers' Set.
+
+        // important to use Set.of() (i.e. backed by immutable set) to 
reproduce the issue
+        final Set<Requires.Modifier> mods2 = 
Set.of(Requires.Modifier.MANDATED, Requires.Modifier.SYNTHETIC);
+        final ModuleDescriptor desc2 = createModuleDescriptor(null, mods2, 
null);
+
+        // basic verification of the modifiers themselves before we check the 
module descriptors
+        assertEquals(mods1, mods2, "Modifiers were expected to be equal");
+
+        // now verify the module descriptors
+        assertEquals(desc1, desc2, "Module descriptors were expected to be 
equal");
+        assertEquals(desc1.compareTo(desc2), 0, "compareTo was expected to 
return" +
+                " 0 for module descriptors that are equal");
+        System.out.println(desc1 + " hashcode = " + desc1.hashCode());
+        System.out.println(desc2 + " hashcode = " + desc2.hashCode());
+        assertEquals(desc1.hashCode(), desc2.hashCode(), "Module descriptor 
hashcodes" +
+                " were expected to be equal");
+    }
+
+    // Returns a ModuleDescriptor parsed out of the module-info.class of the 
passed Module
+    private static ModuleDescriptor fromModuleInfoClass(Module module) throws 
IOException {
+        try (InputStream moduleInfo = 
module.getResourceAsStream("module-info.class")) {
+            if (moduleInfo == null) {
+                throw new RuntimeException("Could not locate module-info.class 
in " + module);
+            }
+            // internally calls ModuleDescriptor.Builder
+            return ModuleDescriptor.read(moduleInfo);
+        }
+    }
+
+    // creates a module descriptor with passed (optional) 
opens/exports/requires modifiers
+    private static ModuleDescriptor createModuleDescriptor(
+            Set<Opens.Modifier> opensModifiers,
+            Set<Requires.Modifier> reqsModifiers,
+            Set<Exports.Modifier> expsModifiers) {
+
+        final ModuleDescriptor.Builder builder = 
ModuleDescriptor.newModule("foobar");
+        if (opensModifiers != null) {
+            builder.opens(opensModifiers, "a.p1", Set.of("a.m1"));
+        }
+        if (reqsModifiers != null) {
+            builder.requires(reqsModifiers, "a.m2");
+        }
+        if (expsModifiers != null) {
+            builder.exports(expsModifiers, "a.b.c", Set.of("a.m3"));
+        }
+        return builder.build();
+    }
+}

++++++ reproducible-source-date-epoch.patch ++++++
# Derive the source date and the copyright year from SOURCE_DATE_EPOCH
#
# Adapted from JDK-8282567 ("Improve source-date handling in build system",
# JDK 20) and from the COPYRIGHT_YEAR handling that JDK 17 already has.
#
# Two independent sources of non-determinism in openSUSE java-11-openjdk:
#
# 1. configure ignored the SOURCE_DATE_EPOCH exported by the build service
#    and left SOURCE_DATE at "updated", so SetupReproducibleBuild in
#    make/InitSupport.gmk replaced the inherited SOURCE_DATE_EPOCH with the
#    output of "date +%s" at make start. Since gcc derives __DATE__ and
#    __TIME__ from SOURCE_DATE_EPOCH, every build embedded a different
#    timestamp in libjvm.so ("built on Sep 11 2025 22:44:08" versus
#    "built on Oct 18 2026 12:53:30" in two openSUSE builds).
#
# 2. COPYRIGHT_YEAR was taken from the wall clock, so builds run in
#    different years differ in every generated file that carries a
#    copyright header: the whole javadoc bundle and more than 1200
#    generated CLDR sources in lib/src.zip.
#
# Honour SOURCE_DATE_EPOCH from the environment when --with-source-date is
# not given, and derive COPYRIGHT_YEAR from it, exactly like JDK 17.
#
# Note that setting with_source_date_present flips ENABLE_REPRODUCIBLE_BUILD
# to true, which on JDK 11 only affects the Microsoft toolchain
# (flags-cflags.m4 and flags-ldflags.m4 gate -pathmap and
# -experimental:deterministic on it), so this is a no-op elsewhere. Unlike
# JDK 17, JDK 11 has no HOTSPOT_BUILD_TIME and no jar/jmod --date, so those
# parts of JDK-8282567 have no counterpart here.
#
# JDK 11 also has no IS_GNU_DATE (make/InitSupport.gmk references it, but
# nothing ever sets it), so the epoch to year conversion tries the GNU
# syntax first and falls back to the BSD one.
#

diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4
index cc45f3c0267..00e0ea22878 100644
--- a/make/autoconf/jdk-options.m4
+++ b/make/autoconf/jdk-options.m4
@@ -255,6 +255,17 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_JDK_OPTIONS],
     AC_MSG_ERROR([Copyright year must have a value])
   elif test "x$with_copyright_year" != x; then
     COPYRIGHT_YEAR="$with_copyright_year"
+  elif test "x$SOURCE_DATE_EPOCH" != x; then
+    # Derive the copyright year from SOURCE_DATE_EPOCH, so that it does not
+    # depend on when the build happens to run. JDK 17 and later do the same.
+    # JDK 11 has no IS_GNU_DATE, so try the GNU syntax and fall back to BSD.
+    COPYRIGHT_YEAR=`$DATE --utc --date=@$SOURCE_DATE_EPOCH +%Y 2> /dev/null`
+    if test "x$COPYRIGHT_YEAR" = x; then
+      COPYRIGHT_YEAR=`$DATE -u -j -f %s $SOURCE_DATE_EPOCH +%Y 2> /dev/null`
+    fi
+    if test "x$COPYRIGHT_YEAR" = x; then
+      AC_MSG_ERROR([Cannot convert SOURCE_DATE_EPOCH to a copyright year])
+    fi
   else
     COPYRIGHT_YEAR=`$DATE +'%Y'`
   fi
@@ -659,7 +670,17 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_REPRODUCIBLE_BUILD],
 
   if test "x$with_source_date" = xyes; then
     AC_MSG_ERROR([--with-source-date must have a value])
-  elif test "x$with_source_date" = xupdated || test "x$with_source_date" = x; 
then
+  elif test "x$with_source_date" = x; then
+    if test "x$SOURCE_DATE_EPOCH" != x; then
+      SOURCE_DATE=$SOURCE_DATE_EPOCH
+      with_source_date_present=true
+      AC_MSG_RESULT([$SOURCE_DATE, from SOURCE_DATE_EPOCH])
+    else
+      # Tell the makefiles to update at each build
+      SOURCE_DATE=updated
+      AC_MSG_RESULT([determined at build time (default)])
+    fi
+  elif test "x$with_source_date" = xupdated; then
     # Tell the makefiles to update at each build
     SOURCE_DATE=updated
     AC_MSG_RESULT([determined at build time, from 'updated'])

++++++ tzdata-2026c.patch ++++++
++++ 805 lines (skipped)

Reply via email to