From 8468028c23d48c700375f49833242d58ceb0e67a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Boutemy?= Date: Sat, 4 Mar 2023 19:01:17 +0100 Subject: [PATCH 1/2] [SHADE-420] create IT: 2 runs with different TZ give different jars --- src/it/projects/MSHADE-420/invoker.properties | 25 ++ src/it/projects/MSHADE-420/pom.xml | 83 +++++++ src/it/projects/MSHADE-420/verify.groovy | 59 +++++ src/it/projects/MSHADE-420/zipdetails.txt | 215 ++++++++++++++++++ 4 files changed, 382 insertions(+) create mode 100644 src/it/projects/MSHADE-420/invoker.properties create mode 100644 src/it/projects/MSHADE-420/pom.xml create mode 100644 src/it/projects/MSHADE-420/verify.groovy create mode 100644 src/it/projects/MSHADE-420/zipdetails.txt diff --git a/src/it/projects/MSHADE-420/invoker.properties b/src/it/projects/MSHADE-420/invoker.properties new file mode 100644 index 00000000..4ab37c16 --- /dev/null +++ b/src/it/projects/MSHADE-420/invoker.properties @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.goals.1 = clean package -DfinalName=current-OS dependency:copy-dependencies + +invoker.mavenOpts.2=-Duser.timezone=Etc/UTC +invoker.goals.2 = package -DfinalName=UTC +invoker.mavenOpts.3=-Duser.timezone=Asia/Tokyo +invoker.goals.3 = package -DfinalName=Tokyo +invoker.mavenOpts.4=-Duser.timezone=Canada/Yukon +invoker.goals.4 = package -DfinalName=Yukon diff --git a/src/it/projects/MSHADE-420/pom.xml b/src/it/projects/MSHADE-420/pom.xml new file mode 100644 index 00000000..1a610c7d --- /dev/null +++ b/src/it/projects/MSHADE-420/pom.xml @@ -0,0 +1,83 @@ + + + + + + 4.0.0 + + org.apache.maven.its.shade.parallel + mshade420 + 1.0 + + MSHADE-420 + + Some jar entries are timezone-sensitive. + + + + + net.java.dev.jna + jna + 5.13.0 + + + + + + + org.apache.maven.plugins + maven-shade-plugin + @project.version@ + + + + shade + + + ${finalName} + + + net.java.dev.jna:jna + + META-INF/* + **/*.class + com/sun/jna/win32**/* + + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.5.0 + + + + copy-dependencies + + + + + + + diff --git a/src/it/projects/MSHADE-420/verify.groovy b/src/it/projects/MSHADE-420/verify.groovy new file mode 100644 index 00000000..215f42c7 --- /dev/null +++ b/src/it/projects/MSHADE-420/verify.groovy @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.security.MessageDigest +import java.util.jar.JarFile + +String describeEntry(JarFile jar, String name, long time) { + def entry = jar.getEntry(name) + return String.format(" - %-47s: time = %d, lastModified = %s = diff %d min., extra = %b", name, entry.getTime(), entry.getLastModifiedTime().toString(), + (long)((entry.getTime() / 1000L - time)/60L), entry.getExtra() != null) +} + +String describeJar(JarFile jar) { + return describeEntry(jar, "com/sun/jna/openbsd-x86-64/libjnidispatch.so", 1671223758L) + '\n'\ + + describeEntry(jar, "com/sun/jna/linux-loongarch64/libjnidispatch.so", 1671223358L) +} + +String describe(String name) { + def file = new File(basedir, "target/" + name) + def jar = new JarFile(file) + + println(name) + return "sha1 = " + MessageDigest.getInstance("SHA1").digest(file.bytes).encodeHex().toString()\ + + "\n" + describeJar(jar) +} + +void describeTz(TimeZone tz) { + println("TZ = " + tz.getID() + ", raw offset = " + tz.getRawOffset() / 60000 + " min., offset to current TZ = " + (tz.getRawOffset() - TimeZone.getDefault().getRawOffset()) / 60000 + " min.") +} + +describeTz(TimeZone.getDefault()) +println(describe("dependency/jna-5.13.0.jar")) +println(describe("current-OS.jar")) +def utcDescription = describe("UTC.jar") +describeTz(TimeZone.getTimeZone("Etc/UTC")) +println(utcDescription) +def tokyoDescription = describe("Tokyo.jar") +describeTz(TimeZone.getTimeZone("Asia/Tokyo")) +println(tokyoDescription) +describeTz(TimeZone.getTimeZone("Canada/Yukon")) +println(describe("Yukon.jar")) + +assert utcDescription == tokyoDescription \ No newline at end of file diff --git a/src/it/projects/MSHADE-420/zipdetails.txt b/src/it/projects/MSHADE-420/zipdetails.txt new file mode 100644 index 00000000..1fa0db79 --- /dev/null +++ b/src/it/projects/MSHADE-420/zipdetails.txt @@ -0,0 +1,215 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# extract of zipdetails tool (https://perldoc.perl.org/zipdetails) output on target/dependency/jna-5.13.0.jar +# and on UTC.jar and Tokyo.jar +# to compare details of a problematic zip entry vs non-problematic in origin jar +# vs in shaded jars +# + +# extracts from *.zipdetails.txt and *.zipdetails-stderr.txt files generated by: +$ for n in UTC Tokyo dependency/jna-5.13.0 ; do sha1sum $n.jar ; zipdetails $n.jar > $n-zipdetails.txt 2> $n-zipdetails-stderr.txt ; done +2f9bd2808b7f93058abd8a66e160b699e2ff53f1 UTC.jar +bc0b043cbcdda98b53d9aa8f8ef3a4e7f6624708 Tokyo.jar +1200e7ebeedbe0d10062093f32925a912020e747 dependency/jna-5.13.0.jar +# done on my own Linux box with Paris TZ in January 2024 (I don't know if other OS TZ may have an impact, or daylight savings, +# recording details for comparing with anybody getting different results on SHA1s) +# focused on problematic zip entries com/sun/jna/openbsd-x86-64/libjnidispatch.so vs non-problematic com/sun/jna/linux-loongarch64/libjnidispatch.so + +# initial zipdetails contains 2 "Extra ID" fields + +## +## jna-5.13.0.jar +## +# zip entry in jna-5.13.0.jar causing the issue +ADD 1600309 1648805 CENTRAL HEADER ref Local #B5: com/sun/jna/openbsd-x86-64/libjnidispatch.so +1CAAA1 CENTRAL HEADER #B5 02014B50 +1CAAA5 Created Zip Spec 14 '2.0' +1CAAA6 Created OS 03 'Unix' +1CAAA7 Extract Zip Spec 0A '1.0' +1CAAA8 Extract OS 00 'MS-DOS' +1CAAA9 General Purpose Flag 0800 + [Bits 1-2] 0 'Normal Compression' + [Bit 11] 1 'Language Encoding' +1CAAAB Compression Method 0008 'Deflated' +1CAAAD Last Mod Time 5590AE29 'Fri Dec 16 21:49:18 2022' +1CAAB1 CRC 9D24CF73 +1CAAB5 Compressed Length 0000BD70 +1CAAB9 Uncompressed Length 0001E03E +1CAABD Filename Length 002C +1CAABF Extra Length 0018 +1CAAC1 Comment Length 0000 +1CAAC3 Disk Start 0000 +1CAAC5 Int File Attributes 0000 + [Bit 0] 0 'Binary Data' +1CAAC7 Ext File Attributes 81ED0000 +1CAACB Local Header Offset 00186B35 +1CAACF Filename 'com/sun/jna/openbsd-x86- + 64/libjnidispatch.so' +1CAAFB Extra ID #0001 5455 'UT: Extended Timestamp' +1CAAFD Length 0005 +1CAAFF Flags '03 mod access' +1CAB00 Mod Time 639CD9CE 'Fri Dec 16 21:49:18 2022' +1CAB04 Extra ID #0002 7875 'ux: Unix Extra Type 3' +1CAB06 Length 000B +1CAB08 Version 01 +1CAB09 UID Size 04 +1CAB0A UID 00000000 +1CAB0E GID Size 04 +1CAB0F GID 00000000 + +# usual zip entry jna-5.13.0.jar, not causing any issue +ADD 1059307 1109553 CENTRAL HEADER ref Local #A1: com/sun/jna/linux-loongarch64/libjnidispatch.so +1CA392 CENTRAL HEADER #A1 02014B50 +1CA396 Created Zip Spec 14 '2.0' +1CA397 Created OS 03 'Unix' +1CA398 Extract Zip Spec 0A '1.0' +1CA399 Extract OS 00 'MS-DOS' +1CA39A General Purpose Flag 0800 + [Bits 1-2] 0 'Normal Compression' + [Bit 11] 1 'Language Encoding' +1CA39C Compression Method 0008 'Deflated' +1CA39E Last Mod Time 5590AD53 'Fri Dec 16 21:42:38 2022' +1CA3A2 CRC 1AA0D033 +1CA3A6 Compressed Length 0000C446 +1CA3AA Uncompressed Length 0005B768 +1CA3AE Filename Length 002F +1CA3B0 Extra Length 0000 +1CA3B2 Comment Length 0000 +1CA3B4 Disk Start 0000 +1CA3B6 Int File Attributes 0000 + [Bit 0] 0 'Binary Data' +1CA3B8 Ext File Attributes 81A40000 +1CA3BC Local Header Offset 001029EB +1CA3C0 Filename 'com/sun/jna/linux- + loongarch64/libjnidispatch.so' + + +## +## UTC.jar = shaded content with JVM UTC TZ +## +# zip entry in UTC.jar having an issue +ADD 1317115 1365611 CENTRAL HEADER ref Local #38: com/sun/jna/openbsd-x86-64/libjnidispatch.so +14E7BF CENTRAL HEADER #38 02014B50 +14E7C3 Created Zip Spec 14 '2.0' +14E7C4 Created OS 00 'MS-DOS' +14E7C5 Extract Zip Spec 14 '2.0' +14E7C6 Extract OS 00 'MS-DOS' +14E7C7 General Purpose Flag 0808 + [Bits 1-2] 0 'Normal Compression' + [Bit 3] 1 'Streamed' + [Bit 11] 1 'Language Encoding' +14E7C9 Compression Method 0008 'Deflated' +14E7CB Last Mod Time 5590A629 'Fri Dec 16 20:49:18 2022' +14E7CF CRC 9D24CF73 +14E7D3 Compressed Length 0000BD70 +14E7D7 Uncompressed Length 0001E03E +14E7DB Filename Length 002C +14E7DD Extra Length 0000 +14E7DF Comment Length 0000 +14E7E1 Disk Start 0000 +14E7E3 Int File Attributes 0000 + [Bit 0] 0 'Binary Data' +14E7E5 Ext File Attributes 00000000 +14E7E9 Local Header Offset 001418FB +14E7ED Filename 'com/sun/jna/openbsd-x86- + 64/libjnidispatch.so' + +# zip entry in UTC.jar not having an issue +ADD 776025 826271 CENTRAL HEADER ref Local #24: com/sun/jna/linux-loongarch64/libjnidispatch.so +14E188 CENTRAL HEADER #24 02014B50 +14E18C Created Zip Spec 14 '2.0' +14E18D Created OS 00 'MS-DOS' +14E18E Extract Zip Spec 14 '2.0' +14E18F Extract OS 00 'MS-DOS' +14E190 General Purpose Flag 0808 + [Bits 1-2] 0 'Normal Compression' + [Bit 3] 1 'Streamed' + [Bit 11] 1 'Language Encoding' +14E192 Compression Method 0008 'Deflated' +14E194 Last Mod Time 5590AD53 'Fri Dec 16 21:42:38 2022' +14E198 CRC 1AA0D033 +14E19C Compressed Length 0000C446 +14E1A0 Uncompressed Length 0005B768 +14E1A4 Filename Length 002F +14E1A6 Extra Length 0000 +14E1A8 Comment Length 0000 +14E1AA Disk Start 0000 +14E1AC Int File Attributes 0000 + [Bit 0] 0 'Binary Data' +14E1AE Ext File Attributes 00000000 +14E1B2 Local Header Offset 000BD759 +14E1B6 Filename 'com/sun/jna/linux- + loongarch64/libjnidispatch.so' + + +## +## Tokyo.jar = shaded content with JVM Asia/Tokyo TZ +## +# zip entry in Tokyo.jar having an issue +ADD 1317115 1365611 CENTRAL HEADER ref Local #38: com/sun/jna/openbsd-x86-64/libjnidispatch.so +14E7BF CENTRAL HEADER #38 02014B50 +14E7C3 Created Zip Spec 14 '2.0' +14E7C4 Created OS 00 'MS-DOS' +14E7C5 Extract Zip Spec 14 '2.0' +14E7C6 Extract OS 00 'MS-DOS' +14E7C7 General Purpose Flag 0808 + [Bits 1-2] 0 'Normal Compression' + [Bit 3] 1 'Streamed' + [Bit 11] 1 'Language Encoding' +14E7C9 Compression Method 0008 'Deflated' +14E7CB Last Mod Time 55912E29 'Sat Dec 17 05:49:18 2022' +14E7CF CRC 9D24CF73 +14E7D3 Compressed Length 0000BD70 +14E7D7 Uncompressed Length 0001E03E +14E7DB Filename Length 002C +14E7DD Extra Length 0000 +14E7DF Comment Length 0000 +14E7E1 Disk Start 0000 +14E7E3 Int File Attributes 0000 + [Bit 0] 0 'Binary Data' +14E7E5 Ext File Attributes 00000000 +14E7E9 Local Header Offset 001418FB +14E7ED Filename 'com/sun/jna/openbsd-x86- + 64/libjnidispatch.so' + +# zip entry in Tokyo.jar not having an issue +ADD 776025 826271 CENTRAL HEADER ref Local #24: com/sun/jna/linux-loongarch64/libjnidispatch.so +14E188 CENTRAL HEADER #24 02014B50 +14E18C Created Zip Spec 14 '2.0' +14E18D Created OS 00 'MS-DOS' +14E18E Extract Zip Spec 14 '2.0' +14E18F Extract OS 00 'MS-DOS' +14E190 General Purpose Flag 0808 + [Bits 1-2] 0 'Normal Compression' + [Bit 3] 1 'Streamed' + [Bit 11] 1 'Language Encoding' +14E192 Compression Method 0008 'Deflated' +14E194 Last Mod Time 5590AD53 'Fri Dec 16 21:42:38 2022' +14E198 CRC 1AA0D033 +14E19C Compressed Length 0000C446 +14E1A0 Uncompressed Length 0005B768 +14E1A4 Filename Length 002F +14E1A6 Extra Length 0000 +14E1A8 Comment Length 0000 +14E1AA Disk Start 0000 +14E1AC Int File Attributes 0000 + [Bit 0] 0 'Binary Data' +14E1AE Ext File Attributes 00000000 +14E1B2 Local Header Offset 000BD759 +14E1B6 Filename 'com/sun/jna/linux- + loongarch64/libjnidispatch.so' From 8f02adbf9d5a88b3db9a89da36c30ce3384fd07e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Boutemy?= Date: Sun, 21 Jan 2024 17:54:12 +0100 Subject: [PATCH 2/2] [MSHADE-420] fix time when read from extra field --- pom.xml | 5 ++++ .../maven/plugins/shade/DefaultShader.java | 27 +++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2bfe4b65..0431d4c8 100644 --- a/pom.xml +++ b/pom.xml @@ -255,6 +255,11 @@ 3.3.0 test + + org.apache.commons + commons-compress + 1.23.0 + diff --git a/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java b/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java index 3d14fd21..c4b22bf1 100644 --- a/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java +++ b/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java @@ -43,6 +43,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.TimeZone; import java.util.concurrent.Callable; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -55,6 +56,9 @@ import org.apache.commons.collections4.MultiValuedMap; import org.apache.commons.collections4.multimap.HashSetValuedHashMap; +import org.apache.commons.compress.archivers.zip.ExtraFieldUtils; +import org.apache.commons.compress.archivers.zip.X5455_ExtendedTimestamp; +import org.apache.commons.compress.archivers.zip.ZipExtraField; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.shade.filter.Filter; import org.apache.maven.plugins.shade.relocation.Relocator; @@ -89,6 +93,25 @@ public DefaultShader(final Logger logger) { this.logger = Objects.requireNonNull(logger); } + // workaround for MSHADE-420 + private long getTime(ZipEntry entry) { + if (entry.getExtra() != null) { + try { + ZipExtraField[] fields = + ExtraFieldUtils.parse(entry.getExtra(), true, ExtraFieldUtils.UnparseableExtraField.SKIP); + for (ZipExtraField field : fields) { + if (X5455_ExtendedTimestamp.HEADER_ID.equals(field.getHeaderId())) { + // extended timestamp extra field: need to translate UTC to local time for Reproducible Builds + return entry.getTime() - TimeZone.getDefault().getRawOffset(); + } + } + } catch (ZipException ze) { + // ignore + } + } + return entry.getTime(); + } + public void shade(ShadeRequest shadeRequest) throws IOException, MojoExecutionException { Set resources = new HashSet<>(); @@ -332,7 +355,7 @@ public InputStream call() throws Exception { } }, name, - entry.getTime(), + getTime(entry), entry.getMethod()); } catch (Exception e) { throw new IOException(String.format("Problem shading JAR %s entry %s: %s", jar, name, e), e); @@ -423,7 +446,7 @@ private void goThroughAllJarEntriesForManifestTransformer( resources.add(resource); try (InputStream inputStream = jarFile.getInputStream(entry)) { manifestTransformer.processResource( - resource, inputStream, shadeRequest.getRelocators(), entry.getTime()); + resource, inputStream, shadeRequest.getRelocators(), getTime(entry)); } break; }