From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp10.migadu.com ([2001:41d0:403:478a::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms9.migadu.com with LMTPS id 8PMsFwn5x2SusgAASxT56A (envelope-from ) for ; Mon, 31 Jul 2023 20:10:17 +0200 Received: from aspmx1.migadu.com ([2001:41d0:403:478a::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp10.migadu.com with LMTPS id iLEyFgn5x2TGjAAAG6o9tA (envelope-from ) for ; Mon, 31 Jul 2023 20:10:17 +0200 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 8B48254B9C for ; Mon, 31 Jul 2023 20:10:16 +0200 (CEST) Authentication-Results: aspmx1.migadu.com; dkim=fail ("headers rsa verify failed") header.d=scratchpost.org header.s=kas202303142049 header.b=k+tHX1Fj; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org"; dmarc=none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1690827017; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding:resent-cc: resent-from:resent-sender:resent-message-id:in-reply-to:in-reply-to: references:references:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=RQlx7eMJEJZbkvfyWgsqNKpZxx3MC00ox7zBzgrzp+Q=; b=elE5EXg/mwJ/5HXLn+5bra0g2ZIUumCpwO1FfkEqfAV5+UP1MWoYY3++8J8Z5FZenvyxJ3 BjCi3qXGtcvIRkPFa8ZehoilgzGsSgH8KKNHO5hQ0RErSZVZzlRVIZMKER3ZXYSP2QTTIW KYrvT6JFobEUNliYZ3pUuAY7/wJB2w0reKxSs454SR7GMwdG3iwfsFA4Skny7UEDkiPLcP yyuNrG9mB2Oh9riEMqObVtxHnGy7jeUi3NHKJXwWR6tIKH3GfhCOKNZLy9/sVNklh9WhHN 2Ynw40SL9pMlPjEcKoEreSBAT7XBESC54h5JABgk1oMG9eJrOqgkm9SDNMlOqA== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=fail ("headers rsa verify failed") header.d=scratchpost.org header.s=kas202303142049 header.b=k+tHX1Fj; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org"; dmarc=none ARC-Seal: i=1; s=key1; d=yhetil.org; t=1690827017; a=rsa-sha256; cv=none; b=N6c6U9/WlmbDhOL1JnSHBAr+9RumQYI//Sgko/0fd/0WQu0aCG3Ltg3dVGGU02ldmnN86R Aa6ACiJ2c2+caNPTUDUT5JpaP+a+RyD0ranoe+iycQBMujUUjsPsF2PcG7D+3BnOseLXW7 ex0LO/CF9rdd0nkriV/z06J1sh2NH27jYSf2qspInewSLVuC4ZVditKuqhBGsp5+vpU5Ug GakJjB7OWJxQfs81lUgC4aMz1N9bm/M7ZsFHikDdx6C2CnzWKCyUWsKNIx7pBKBXpC2hA4 enVtlQGf0g00zXHUCu7bNn9y63Tbq7bgPzBpzyumInMGnFNysoJzsopP5g79Qg== Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1qQXLL-0003T6-4N; Mon, 31 Jul 2023 14:10:07 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qQXLI-0003Sh-Jb for guix-patches@gnu.org; Mon, 31 Jul 2023 14:10:04 -0400 Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1qQXLH-0003g8-Uy for guix-patches@gnu.org; Mon, 31 Jul 2023 14:10:04 -0400 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1qQXLH-0007LM-N3 for guix-patches@gnu.org; Mon, 31 Jul 2023 14:10:03 -0400 X-Loop: help-debbugs@gnu.org Subject: [bug#55751] [PATCH v2 1/7] gnu: openjdk9: Make build reproducible. Resent-From: Danny Milosavljevic Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Mon, 31 Jul 2023 18:10:03 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 55751 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: 55751@debbugs.gnu.org Cc: Danny Milosavljevic Received: via spool by 55751-submit@debbugs.gnu.org id=B55751.169082699028189 (code B ref 55751); Mon, 31 Jul 2023 18:10:03 +0000 Received: (at 55751) by debbugs.gnu.org; 31 Jul 2023 18:09:50 +0000 Received: from localhost ([127.0.0.1]:53525 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qQXL3-0007KY-0Q for submit@debbugs.gnu.org; Mon, 31 Jul 2023 14:09:50 -0400 Received: from dd30410.kasserver.com ([85.13.145.193]:47610) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qQXKz-0007KP-RY for 55751@debbugs.gnu.org; Mon, 31 Jul 2023 14:09:47 -0400 Received: from nova.lan (84-115-230-178.cable.dynamic.surfer.at [84.115.230.178]) by dd30410.kasserver.com (Postfix) with ESMTPSA id DA00A11211F7; Mon, 31 Jul 2023 20:09:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=scratchpost.org; s=kas202303142049; t=1690826972; bh=RQlx7eMJEJZbkvfyWgsqNKpZxx3MC00ox7zBzgrzp+Q=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=k+tHX1FjZcGdqqBkKle9JIRqZQ5PX5k16/4/SqbUNko4ZVTKPrZz/yZkz99EJLYdJ 8SXHafvCEL83Sxsv1ybVFUXFdTpiVL3dediL6Un1gdEVIaX94P7HtMrjL5VBExrSW0 f1ypvmPV0RcEGybh43GH6eXO5jbI40/b8FQ/1+OGC50n50+fqqHUems9ECtY10w33M ZAOW0/xmn7YkxYvz6/p4cHjkHxqR/GLWOw5GYy0CRi1TKdMid1pn/qigKeriB+ncw2 sr4171e4bEC+KzCSHY7/Rx6xp6mYgQXkkmm1yGwnf3WbD30nu/KDbNh0eXnkhUgrEo y7YBDY1yynrMg== From: Danny Milosavljevic Date: Mon, 31 Jul 2023 20:09:13 +0200 Message-Id: <20230731180919.28047-2-dannym@scratchpost.org> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20230731180919.28047-1-dannym@scratchpost.org> References: <20230731180919.28047-1-dannym@scratchpost.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spamd-Bar: ------ X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+larch=yhetil.org@gnu.org Sender: guix-patches-bounces+larch=yhetil.org@gnu.org X-Migadu-Flow: FLOW_IN X-Migadu-Country: US X-Spam-Score: 2.26 X-Migadu-Queue-Id: 8B48254B9C X-Migadu-Spam-Score: 2.26 X-Migadu-Scanner: mx0.migadu.com X-TUID: xrzJLtEHVTda * gnu/packages/patches/openjdk-9-classlist-reproducibility.patch: New file. * gnu/packages/patches/openjdk-9-jar-reproducibility.patch: New file. * gnu/packages/patches/openjdk-9-module-reproducibility.patch: New file. * gnu/packages/patches/openjdk-9-module2-reproducibility.patch: New file. * gnu/packages/patches/openjdk-9-module3-reproducibility.patch: New file. * gnu/packages/patches/openjdk-9-idlj-reproducibility.patch: New file. * gnu/packages/java.scm (openjdk9)[source]: Add patches. [arguments]<#:phases>[strip-zip-timestamps]: Modify. [inputs]: Use new-style syntax. * gnu/local/mk (dist_patch_DATA): Add patches. --- gnu/local.mk | 6 + gnu/packages/java.scm | 88 ++++-- .../openjdk-9-classlist-reproducibility.patch | 31 ++ .../openjdk-9-idlj-reproducibility.patch | 37 +++ .../openjdk-9-jar-reproducibility.patch | 107 +++++++ .../openjdk-9-module-reproducibility.patch | 297 ++++++++++++++++++ .../openjdk-9-module2-reproducibility.patch | 125 ++++++++ .../openjdk-9-module3-reproducibility.patch | 36 +++ 8 files changed, 703 insertions(+), 24 deletions(-) create mode 100644 gnu/packages/patches/openjdk-9-classlist-reproducibility.patch create mode 100644 gnu/packages/patches/openjdk-9-idlj-reproducibility.patch create mode 100644 gnu/packages/patches/openjdk-9-jar-reproducibility.patch create mode 100644 gnu/packages/patches/openjdk-9-module-reproducibility.patch create mode 100644 gnu/packages/patches/openjdk-9-module2-reproducibility.patch create mode 100644 gnu/packages/patches/openjdk-9-module3-reproducibility.patch diff --git a/gnu/local.mk b/gnu/local.mk index 00fe7a036c..155b9e7740 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -1658,6 +1658,12 @@ dist_patch_DATA = \ %D%/packages/patches/openfoam-4.1-cleanup.patch \ %D%/packages/patches/openjdk-9-pointer-comparison.patch \ %D%/packages/patches/openjdk-9-setsignalhandler.patch \ + %D%/packages/patches/openjdk-9-classlist-reproducibility.patch \ + %D%/packages/patches/openjdk-9-idlj-reproducibility.patch \ + %D%/packages/patches/openjdk-9-jar-reproducibility.patch \ + %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-idlj-reproducibility.patch \ %D%/packages/patches/openjdk-10-pointer-comparison.patch \ %D%/packages/patches/openjdk-10-setsignalhandler.patch \ diff --git a/gnu/packages/java.scm b/gnu/packages/java.scm index 5a6288581e..ad2af44229 100644 --- a/gnu/packages/java.scm +++ b/gnu/packages/java.scm @@ -865,7 +865,9 @@ (define-public openjdk9 (uri (hg-reference (url "https://hg.openjdk.org/jdk/jdk") (changeset "jdk-9+181"))) (file-name (hg-file-name name version)) - (modules '((guix build utils))) + (modules '((guix build utils) + (srfi srfi-35) + (ice-9 binary-ports))) (snippet `(begin (for-each delete-file (find-files "." ".*.(bin|exe|jar)$")))) @@ -873,6 +875,12 @@ (define-public openjdk9 (base32 "1v92nzdqx07c35x945awzir4yk0fk22vky6fpp8mq9js930sxsz0")) (patches (search-patches "openjdk-9-pointer-comparison.patch" + "openjdk-9-classlist-reproducibility.patch" + "openjdk-9-jar-reproducibility.patch" + "openjdk-9-module-reproducibility.patch" + "openjdk-9-module2-reproducibility.patch" + "openjdk-9-module3-reproducibility.patch" + "openjdk-9-idlj-reproducibility.patch" "openjdk-9-setsignalhandler.patch")))) (build-system gnu-build-system) (outputs '("out" "jdk" "doc")) @@ -995,37 +1003,69 @@ (define (icedtea-or-openjdk? path) (for-each (lambda (zip) (let ((dir (mkdtemp "zip-contents.XXXXXX"))) (with-directory-excursion dir - (invoke "unzip" zip)) + ;; This is an exact copy of the implementation + ;; of invoke, but this accepts exit code 1 + ;; as OK. + (let ((code (system* "unzip" "--" zip))) + ;; jmod files are zip files with an extra header in front. + ;; unzip will warn about that--but otherwise work. + (when (> (status:exit-val code) 1) ; 1 is just a warning + (raise + (condition + (&invoke-error + (program "unzip") + (arguments (list "--" zip)) + (exit-status (status:exit-val code)) + (term-signal (status:term-sig code)) + (stop-signal (status:stop-sig code)))))))) (delete-file zip) (for-each (lambda (file) (let ((s (lstat file))) - (unless (eq? (stat:type s) 'symlink) (format #t "reset ~a~%" file) - (utime file 0 0 0 0)))) + (utime file 1 1 0 0 + AT_SYMLINK_NOFOLLOW))) (find-files dir #:directories? #t)) (with-directory-excursion dir - (let ((files (find-files "." ".*" #:directories? #t))) - (apply invoke "zip" "-0" "-X" zip files))))) - (find-files (assoc-ref outputs "doc") ".*.zip$")) + (let ((files (cons "./META-INF/MANIFEST.MF" + (append + (find-files "./META-INF" ".*") + ;; for jmod: + (list "./classes/module-info.class") + (find-files "." ".*"))))) + (apply invoke "zip" "--symlinks" "-0" "-X" zip files) + (when (string-suffix? ".jmod" zip) + (let ((new-zip (string-append zip "n")) + (contents (call-with-input-file zip + (@ (ice-9 binary-ports) + get-bytevector-all)))) + (call-with-output-file new-zip + (lambda (output-port) + ((@ (ice-9 binary-ports) put-bytevector) + output-port + #vu8(#x4a #x4d #x01 #x00)) ; JM + ((@ (ice-9 binary-ports) put-bytevector) + output-port + contents))) + (rename-file new-zip zip))))))) + (append (find-files (string-append + (assoc-ref outputs "doc") + "/api") + "\\.zip$") + (find-files (assoc-ref outputs "doc") "src\\.zip$") + (find-files (assoc-ref outputs "jdk") "src\\.zip$") + (find-files (assoc-ref outputs "jdk") "\\.jmod$") + (find-files (assoc-ref outputs "jdk") "\\.diz$") + (find-files (assoc-ref outputs "out") "\\.diz$") + + (list (string-append (assoc-ref outputs "jdk") "/lib/jrt-fs.jar")) + (find-files (string-append (assoc-ref outputs "jdk") + "/demo") + "\\.jar$"))) #t))))) (inputs - `(("alsa-lib" ,alsa-lib) - ("cups" ,cups) - ("fontconfig" ,fontconfig) - ("freetype" ,freetype) - ("giflib" ,giflib) - ("lcms" ,lcms) - ("libelf" ,libelf) - ("libjpeg" ,libjpeg-turbo) - ("libice" ,libice) - ("libpng" ,libpng) - ("libx11" ,libx11) - ("libxcomposite" ,libxcomposite) - ("libxi" ,libxi) - ("libxinerama" ,libxinerama) - ("libxrender" ,libxrender) - ("libxt" ,libxt) - ("libxtst" ,libxtst))) + (list alsa-lib cups fontconfig freetype giflib lcms libelf libjpeg-turbo + libice libpng libx11 libxcomposite libxi libxinerama libxrender + libxt libxtst)) (native-inputs `(("icedtea-8" ,icedtea-8) ("icedtea-8:jdk" ,icedtea-8 "jdk") diff --git a/gnu/packages/patches/openjdk-9-classlist-reproducibility.patch b/gnu/packages/patches/openjdk-9-classlist-reproducibility.patch new file mode 100644 index 0000000000..d0ce7bc4d6 --- /dev/null +++ b/gnu/packages/patches/openjdk-9-classlist-reproducibility.patch @@ -0,0 +1,31 @@ +From: Danny Milosavljevic +Date: Wed, 18 Apr 2022 18:38:28 +0100 +Subject: Make classlist reproducible + +--- jdk-09/make/GenerateLinkOptData.gmk.orig 2022-04-05 10:05:35.892134188 +0200 ++++ jdk-09/make/GenerateLinkOptData.gmk 2022-04-05 10:06:07.885003056 +0200 +@@ -61,11 +61,12 @@ + $(call MakeDir, $(LINK_OPT_DIR)) + $(call LogInfo, Generating $(patsubst $(OUTPUT_ROOT)/%, %, $@)) + $(call LogInfo, Generating $(patsubst $(OUTPUT_ROOT)/%, %, $(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 +--- jdk-09/langtools/make/gendata/Gendata-jdk.compiler.gmk.orig 2022-04-08 22:04:05.784424812 +0200 ++++ jdk-09/langtools/make/gendata/Gendata-jdk.compiler.gmk 2022-04-08 22:09:36.333575143 +0200 +@@ -79,6 +79,8 @@ + $(CT_MODULESOURCEPATH) \ + $(CT_MODULES) \ + >$(@D)/9/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-9-idlj-reproducibility.patch b/gnu/packages/patches/openjdk-9-idlj-reproducibility.patch new file mode 100644 index 0000000000..30dcdf2496 --- /dev/null +++ b/gnu/packages/patches/openjdk-9-idlj-reproducibility.patch @@ -0,0 +1,37 @@ +From: Danny Milosavljevic +Date: Wed, 18 Apr 2022 19:28:00 +0100 +Subject: Make IDL reproducible + +--- jdk-09/corba/src/java.corba/share/classes/com/sun/tools/corba/se/idl/toJavaPortable/Util.java.orig 2022-04-05 02:46:26.805340292 +0200 ++++ jdk-09/corba/src/java.corba/share/classes/com/sun/tools/corba/se/idl/toJavaPortable/Util.java 2022-04-05 02:48:23.152494213 +0200 +@@ -1146,7 +1146,7 @@ + else + formatter.setTimeZone (java.util.TimeZone.getDefault ()); + +- stream.println ("* " + formatter.format (new Date ())); ++ stream.println ("* " + formatter.format (System.getenv("SOURCE_DATE_EPOCH") == null ? new Date () : new Date(1000 * Long.parseLong(System.getenv("SOURCE_DATE_EPOCH"))))); + + // + /////////////// +--- jdk-09/corba/make/src/classes/build/tools/logutil/MC.java.orig 2022-04-05 11:09:43.824720493 +0200 ++++ jdk-09/corba/make/src/classes/build/tools/logutil/MC.java 2022-04-05 11:10:46.518435511 +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(); + } + +--- jdk-09/jdk/make/src/classes/build/tools/generatecharacter/GenerateCharacter.java.orig 2022-04-05 11:14:29.228526408 +0200 ++++ jdk-09/jdk/make/src/classes/build/tools/generatecharacter/GenerateCharacter.java 2022-04-05 11:15:32.658260748 +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-9-jar-reproducibility.patch b/gnu/packages/patches/openjdk-9-jar-reproducibility.patch new file mode 100644 index 0000000000..130eacd867 --- /dev/null +++ b/gnu/packages/patches/openjdk-9-jar-reproducibility.patch @@ -0,0 +1,107 @@ +From: Danny Milosavljevic +Date: Wed, 18 Apr 2022 20:10:01 +0100 +Subject: Make JARs reproducible + +--- jdk-09/make/common/JarArchive.gmk.orig 2022-04-08 21:56:04.075111687 +0200 ++++ jdk-09/make/common/JarArchive.gmk 2022-04-11 00:49:16.809140388 +0200 +@@ -249,12 +249,16 @@ + $(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)) \ +- $(ECHO) Creating $$($1_NAME) $$(NEWLINE) \ ++ $(TOUCH) -h -c -t 197001010000.00 $$($1_MANIFEST_FILE) $$(NEWLINE) \ ++ $(ECHO) XCreating $$($1_NAME) $(JAR) $$($1_JAR_CREATE_OPTIONS) $$@ $$($1_MANIFEST_FILE) $$(NEWLINE) \ + $(JAR) $$($1_JAR_CREATE_OPTIONS) $$@ $$($1_MANIFEST_FILE) $$(NEWLINE) \ + $$($1_SCAPTURE_CONTENTS) \ + $$($1_SCAPTURE_METAINF) \ + $$($1_SUPDATE_CONTENTS) \ +- $$($1_JARINDEX) && true \ ++ $$($1_JARINDEX) && true $$(NEWLINE) \ ++ $(ECHO) Kreppel2 $$@ $$(NEWLINE) \ ++ unzip -v $$@ $$(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) \ +--- jdk-09/make/JrtfsJar.gmk.orig 2022-04-10 13:48:57.385120008 +0200 ++++ jdk-09/make/JrtfsJar.gmk 2022-04-10 13:58:04.688158538 +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_PROVIDER1, \ + SRC := $(JDK_TOPDIR)/src/java.base/share/classes, \ + DEST := $(SUPPORT_OUTPUTDIR)/jrtfs_classes, \ + FILES := META-INF/services/java.nio.file.spi.FileSystemProvider)) + ++.PHONY: jrtfsfixtimestamps47 ++jrtfsfixtimestamps47: $(COPY_JIMAGE_SERVICE_PROVIDER1) ++ 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) jrtfsfixtimestamps47, \ + SRCS := $(SUPPORT_OUTPUTDIR)/jrtfs_classes, \ + JAR := $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/jrt-fs.jar, \ + MANIFEST := $(SUPPORT_OUTPUTDIR)/java-main-manifest.mf, \ +--- jdk-09/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java.orig 2022-04-10 02:05:50.983247794 +0200 ++++ jdk-09/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java 2022-04-10 02:13:01.638960337 +0200 +@@ -850,12 +850,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); + } +@@ -1022,7 +1028,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); +@@ -1041,7 +1050,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); + } +@@ -1066,7 +1078,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-9-module-reproducibility.patch b/gnu/packages/patches/openjdk-9-module-reproducibility.patch new file mode 100644 index 0000000000..320bb7806a --- /dev/null +++ b/gnu/packages/patches/openjdk-9-module-reproducibility.patch @@ -0,0 +1,297 @@ +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 +See: https://bugs.openjdk.org/browse/JDK-8243666 + +Reviewed-by: mchung +--- + +diff -ru orig/jdk-3cc80be736f2/jdk/src/java.base/share/classes/jdk/internal/module/ModuleHashesBuilder.java jdk-3cc80be736f2/jdk/src/java.base/share/classes/jdk/internal/module/ModuleHashesBuilder.java +--- orig/jdk-3cc80be736f2/jdk/src/java.base/share/classes/jdk/internal/module/ModuleHashesBuilder.java 1970-01-01 01:00:01.000000000 +0100 ++++ jdk-3cc80be736f2/jdk/src/java.base/share/classes/jdk/internal/module/ModuleHashesBuilder.java 2022-04-12 16:47:15.690423653 +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; +@@ -40,7 +39,6 @@ + import java.util.Map; + import java.util.Set; + import java.util.function.Consumer; +-import java.util.function.Function; + import java.util.stream.Stream; + import static java.util.stream.Collectors.*; + +@@ -116,27 +114,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/jdk/src/java.base/share/classes/jdk/internal/module/ModuleHashes.java jdk-3cc80be736f2/jdk/src/java.base/share/classes/jdk/internal/module/ModuleHashes.java +--- orig/jdk-3cc80be736f2/jdk/src/java.base/share/classes/jdk/internal/module/ModuleHashes.java 1970-01-01 01:00:01.000000000 +0100 ++++ jdk-3cc80be736f2/jdk/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/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java jdk-3cc80be736f2/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java +--- orig/jdk-3cc80be736f2/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java 1970-01-01 01:00:01.000000000 +0100 ++++ jdk-3cc80be736f2/jdk/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/jdk/src/java.base/share/classes/jdk/internal/module/ModuleReferences.java jdk-3cc80be736f2/jdk/src/java.base/share/classes/jdk/internal/module/ModuleReferences.java +--- orig/jdk-3cc80be736f2/jdk/src/java.base/share/classes/jdk/internal/module/ModuleReferences.java 1970-01-01 01:00:01.000000000 +0100 ++++ jdk-3cc80be736f2/jdk/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-9-module2-reproducibility.patch b/gnu/packages/patches/openjdk-9-module2-reproducibility.patch new file mode 100644 index 0000000000..f167d94dc8 --- /dev/null +++ b/gnu/packages/patches/openjdk-9-module2-reproducibility.patch @@ -0,0 +1,125 @@ +Backport from openjdk 10 + +--- orig/jdk-3cc80be736f2/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ModuleSorter.java 1970-01-01 01:00:01.000000000 +0100 ++++ jdk-3cc80be736f2/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ModuleSorter.java 2022-04-12 20:48:04.474353305 +0200 +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2016, 2017, 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 +@@ -30,13 +30,16 @@ + import jdk.tools.jlink.plugin.ResourcePoolModuleView; + + import java.lang.module.ModuleDescriptor; ++import java.lang.module.ModuleDescriptor.Requires; + import java.lang.module.ModuleDescriptor.Requires.Modifier; + + import java.nio.ByteBuffer; +-import java.util.Deque; ++import java.util.ArrayList; ++import java.util.Comparator; + import java.util.HashMap; + import java.util.HashSet; +-import java.util.LinkedList; ++import java.util.LinkedHashSet; ++import java.util.List; + import java.util.Map; + import java.util.Set; + import java.util.stream.Stream; +@@ -45,9 +48,8 @@ + * Helper class to sort modules in topological order + */ + public final class ModuleSorter { +- private final Deque nodes = new LinkedList<>(); +- private final Map> edges = new HashMap<>(); +- private final Deque result = new LinkedList<>(); ++ private final Map> graph = new HashMap<>(); ++ private final List result = new ArrayList<>(); + + private final ResourcePoolModuleView moduleView; + +@@ -69,11 +71,17 @@ + + private ModuleSorter addModule(ResourcePoolModule module) { + addNode(module); +- readModuleDescriptor(module).requires().forEach(req -> { ++ // the module graph will be traversed in a stable order for ++ // the topological sort. So add the dependences in the module name order ++ readModuleDescriptor(module).requires() ++ .stream() ++ .sorted(Comparator.comparing(Requires::name)) ++ .forEach(req -> ++ { + ResourcePoolModule dep = moduleView.findModule(req.name()).orElse(null); + if (dep != null) { + addNode(dep); +- edges.get(module.name()).add(dep); ++ graph.get(module).add(dep); + } else if (!req.modifiers().contains(Modifier.STATIC)) { + throw new PluginException(req.name() + " not found"); + } +@@ -82,22 +90,23 @@ + } + + private void addNode(ResourcePoolModule module) { +- nodes.add(module); +- edges.computeIfAbsent(module.name(), _n -> new HashSet<>()); ++ graph.computeIfAbsent(module, _n -> new LinkedHashSet<>()); + } + ++ /* ++ * The module graph will be traversed in a stable order ++ * (traversing the modules and their dependences in alphabetical order) ++ * so that it will produce the same result of a given module graph. ++ */ + private synchronized void build() { +- if (!result.isEmpty() || nodes.isEmpty()) ++ if (!result.isEmpty() || graph.isEmpty()) + return; + +- Deque visited = new LinkedList<>(); +- Deque done = new LinkedList<>(); +- ResourcePoolModule node; +- while ((node = nodes.poll()) != null) { +- if (!visited.contains(node)) { +- visit(node, visited, done); +- } +- } ++ Set visited = new HashSet<>(); ++ Set done = new HashSet<>(); ++ graph.keySet().stream() ++ .sorted(Comparator.comparing(ResourcePoolModule::name)) ++ .forEach(node -> visit(node, visited, done)); + } + + public Stream sorted() { +@@ -106,19 +115,21 @@ + } + + private void visit(ResourcePoolModule node, +- Deque visited, +- Deque done) { ++ Set visited, ++ Set done) { + if (visited.contains(node)) { + if (!done.contains(node)) { + throw new IllegalArgumentException("Cyclic detected: " + +- node + " " + edges.get(node.name())); ++ node + " " + graph.get(node)); + } + return; + } ++ ++ // traverse the dependences of the given module which are ++ // also sorted in alphabetical order + visited.add(node); +- edges.get(node.name()) +- .forEach(x -> visit(x, visited, done)); ++ graph.get(node).forEach(x -> visit(x, visited, done)); + done.add(node); +- result.addLast(node); ++ result.add(node); + } + } diff --git a/gnu/packages/patches/openjdk-9-module3-reproducibility.patch b/gnu/packages/patches/openjdk-9-module3-reproducibility.patch new file mode 100644 index 0000000000..9db54f5531 --- /dev/null +++ b/gnu/packages/patches/openjdk-9-module3-reproducibility.patch @@ -0,0 +1,36 @@ +From: Danny Milosavljevic +Date: Wed, 18 Apr 2022 21:50:00 +0100 +Subject: Make module descriptor reproducible + +--- orig/jdk-3cc80be736f2/jdk/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java 1970-01-01 01:00:01.000000000 +0100 ++++ jdk-3cc80be736f2/jdk/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, -- 2.39.2