3535import org .objectweb .asm .Opcodes ;
3636
3737import javax .annotation .Nullable ;
38+ import java .io .BufferedOutputStream ;
3839import java .io .BufferedReader ;
3940import java .io .ByteArrayInputStream ;
4041import java .io .ByteArrayOutputStream ;
4647import java .nio .charset .StandardCharsets ;
4748import java .nio .file .Files ;
4849import java .util .ArrayList ;
50+ import java .util .Calendar ;
4951import java .util .Collection ;
5052import java .util .Collections ;
53+ import java .util .GregorianCalendar ;
5154import java .util .LinkedHashMap ;
5255import java .util .List ;
5356import java .util .Map ;
5457import java .util .Optional ;
5558import java .util .Set ;
5659import java .util .TreeSet ;
5760import java .util .jar .JarEntry ;
61+ import java .util .jar .JarFile ;
5862import java .util .jar .JarInputStream ;
5963import java .util .jar .JarOutputStream ;
6064import java .util .jar .Manifest ;
@@ -82,6 +86,9 @@ public abstract class ExtraJavaModuleInfoTransform implements TransformAction<Ex
8286 private static final Pattern JAR_SIGNATURE_PATH = Pattern .compile ("^META-INF/[^/]+\\ .(SF|RSA|DSA|sf|rsa|dsa)$" );
8387 private static final String SERVICES_PREFIX = "META-INF/services/" ;
8488
89+ // See: org.gradle.api.internal.file.archive.ZipCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES
90+ private static final long CONSTANT_TIME_FOR_ZIP_ENTRIES = new GregorianCalendar (1980 , Calendar .FEBRUARY , 1 , 0 , 0 , 0 ).getTimeInMillis ();
91+
8592 public interface Parameter extends TransformParameters {
8693 @ Input
8794 MapProperty <String , ModuleSpec > getModuleSpecs ();
@@ -260,7 +267,7 @@ private void addModuleDescriptor(File originalJar, File moduleJar, ModuleInfo mo
260267 Set <String > packages = new TreeSet <>();
261268 copyAndExtractProviders (inputStream , outputStream , !moduleInfo .getMergedJars ().isEmpty (), providers , packages );
262269 mergeJars (moduleInfo , outputStream , providers , packages );
263- outputStream .putNextEntry (new JarEntry ("module-info.class" ));
270+ outputStream .putNextEntry (newReproducibleEntry ("module-info.class" ));
264271 outputStream .write (addModuleInfo (moduleInfo , providers , versionFromFilePath (originalJar .toPath ()),
265272 moduleInfo .exportAllPackages ? packages : Collections .emptySet ()));
266273 outputStream .closeEntry ();
@@ -271,7 +278,14 @@ private void addModuleDescriptor(File originalJar, File moduleJar, ModuleInfo mo
271278 }
272279
273280 private JarOutputStream newJarOutputStream (OutputStream out , @ Nullable Manifest manifest ) throws IOException {
274- return manifest == null ? new JarOutputStream (out ) : new JarOutputStream (out , manifest );
281+ JarOutputStream jar = new JarOutputStream (out );
282+ if (manifest != null ) {
283+ ZipEntry e = newReproducibleEntry (JarFile .MANIFEST_NAME );
284+ jar .putNextEntry (e );
285+ manifest .write (new BufferedOutputStream (jar ));
286+ jar .closeEntry ();
287+ }
288+ return jar ;
275289 }
276290
277291 private void copyAndExtractProviders (JarInputStream inputStream , JarOutputStream outputStream , boolean willMergeJars , Map <String , List <String >> providers , Set <String > packages ) throws IOException {
@@ -450,8 +464,7 @@ private void mergeJars(ModuleSpec moduleSpec, JarOutputStream outputStream, Map<
450464
451465 private void mergeServiceProviderFiles (JarOutputStream outputStream , Map <String , List <String >> providers ) throws IOException {
452466 for (Map .Entry <String , List <String >> provider : providers .entrySet ()) {
453- JarEntry jarEntry = new JarEntry (SERVICES_PREFIX + provider .getKey ());
454- outputStream .putNextEntry (jarEntry );
467+ outputStream .putNextEntry (newReproducibleEntry (SERVICES_PREFIX + provider .getKey ()));
455468 for (String implementation : provider .getValue ()) {
456469 outputStream .write (implementation .getBytes ());
457470 outputStream .write ("\n " .getBytes ());
@@ -460,6 +473,12 @@ private void mergeServiceProviderFiles(JarOutputStream outputStream, Map<String,
460473 }
461474 }
462475
476+ private JarEntry newReproducibleEntry (String name ) {
477+ JarEntry jarEntry = new JarEntry (name );
478+ jarEntry .setTime (CONSTANT_TIME_FOR_ZIP_ENTRIES );
479+ return jarEntry ;
480+ }
481+
463482 private byte [] readAllBytes (InputStream inputStream ) throws IOException {
464483 final int bufLen = 4 * 0x400 ;
465484 byte [] buf = new byte [bufLen ];
0 commit comments