Skip to content

Commit

Permalink
Add "zeroEntryTimestamps()" option
Browse files Browse the repository at this point in the history
See javadoc on the method for details on why and how. This
is basically the half-assed deterministic build from #229,
which is to say still extremely helpful when dealing with rsync.
  • Loading branch information
blendmaster committed Sep 9, 2016
1 parent c10038d commit 8d16c02
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ import java.util.zip.ZipException

@Slf4j
public class ShadowCopyAction implements CopyAction {
/**
* Jan 1st, 1980. The day the zip timestamps stood still.
*/
public static final int DOS_TIMESTAMP_ZERO = 315558000000L

private final File zipFile
private final ZipCompressor compressor
Expand All @@ -47,18 +51,19 @@ public class ShadowCopyAction implements CopyAction {
private final List<Relocator> relocators
private final PatternSet patternSet
private final ShadowStats stats
private final boolean zeroEntryTimestamps

public ShadowCopyAction(File zipFile, ZipCompressor compressor, DocumentationRegistry documentationRegistry,
List<Transformer> transformers, List<Relocator> relocators, PatternSet patternSet,
ShadowStats stats) {

ShadowStats stats, boolean zeroEntryTimestamps) {
this.zipFile = zipFile
this.compressor = compressor
this.documentationRegistry = documentationRegistry
this.transformers = transformers
this.relocators = relocators
this.patternSet = patternSet
this.stats = stats
this.zeroEntryTimestamps = zeroEntryTimestamps
}

@Override
Expand Down Expand Up @@ -170,7 +175,7 @@ public class ShadowCopyAction implements CopyAction {
ZipEntry archiveEntry = new ZipEntry(mappedPath)
archiveEntry.setTime(fileDetails.lastModified)
archiveEntry.unixMode = (UnixStat.FILE_FLAG | fileDetails.mode)
zipOutStr.putNextEntry(archiveEntry)
putNextEntry(archiveEntry)
fileDetails.copyTo(zipOutStr)
zipOutStr.closeEntry()
} else {
Expand Down Expand Up @@ -209,7 +214,7 @@ public class ShadowCopyAction implements CopyAction {

private void visitArchiveDirectory(RelativeArchivePath archiveDir) {
if (recordVisit(archiveDir)) {
zipOutStr.putNextEntry(archiveDir.entry)
putNextEntry(archiveDir.entry)
zipOutStr.closeEntry()
}
}
Expand Down Expand Up @@ -277,7 +282,7 @@ public class ShadowCopyAction implements CopyAction {

try {
// Now we put it back on so the class file is written out with the right extension.
zipOutStr.putNextEntry(new ZipEntry(mappedName + ".class"))
putNextEntry(new ZipEntry(mappedName + ".class"))
IOUtils.copyLarge(new ByteArrayInputStream(renamedClass), zipOutStr)
zipOutStr.closeEntry()
} catch (ZipException e) {
Expand All @@ -289,7 +294,7 @@ public class ShadowCopyAction implements CopyAction {
String mappedPath = remapper.map(archiveFile.entry.name)
RelativeArchivePath mappedFile = new RelativeArchivePath(new ZipEntry(mappedPath), archiveFile.details)
addParentDirectories(mappedFile)
zipOutStr.putNextEntry(mappedFile.entry)
putNextEntry(mappedFile.entry)
IOUtils.copyLarge(archive.getInputStream(archiveFile.entry), zipOutStr)
zipOutStr.closeEntry()
}
Expand All @@ -301,7 +306,7 @@ public class ShadowCopyAction implements CopyAction {
ZipEntry archiveEntry = new ZipEntry(path)
archiveEntry.setTime(dirDetails.lastModified)
archiveEntry.unixMode = (UnixStat.DIR_FLAG | dirDetails.mode)
zipOutStr.putNextEntry(archiveEntry)
putNextEntry(archiveEntry)
zipOutStr.closeEntry()
recordVisit(dirDetails.relativePath)
} catch (Exception e) {
Expand All @@ -326,6 +331,13 @@ public class ShadowCopyAction implements CopyAction {
return transformers.any { it.canTransformResource(element) }
}

// manually intercept `putNextEntry` to zero out timestamps if requested.
private void putNextEntry(ZipEntry entry) {
if (zeroEntryTimestamps)
entry.setTime(DOS_TIMESTAMP_ZERO)
zipOutStr.putNextEntry(entry)
}

}

class RelativeArchivePath extends RelativePath {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import org.gradle.api.internal.DocumentationRegistry;
import org.gradle.api.internal.file.FileResolver;
import org.gradle.api.internal.file.copy.CopyAction;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.TaskAction;
Expand All @@ -36,6 +35,8 @@ public class ShadowJar extends Jar implements ShadowSpec {
private final ShadowStats shadowStats = new ShadowStats();
private final GradleVersionUtil versionUtil;

private boolean zeroEntryTimestamps = false;

public ShadowJar() {
versionUtil = new GradleVersionUtil(getProject().getGradle().getGradleVersion());
dependencyFilter = new DefaultDependencyFilter(getProject());
Expand All @@ -54,7 +55,27 @@ public InheritManifest getManifest() {
protected CopyAction createCopyAction() {
DocumentationRegistry documentationRegistry = getServices().get(DocumentationRegistry.class);
return new ShadowCopyAction(getArchivePath(), getInternalCompressor(), documentationRegistry,
transformers, relocators, getRootPatternSet(), shadowStats);
transformers, relocators, getRootPatternSet(), shadowStats, zeroEntryTimestamps);
}

/**
* Configure the shadow jar such that all timestamps on entries
* inside are fixed to Jan 1st 1980.
*
* Why would you want to do this? The timestamps usually are just set to the
* time the jar was built. However, those timestamps are repeated across every
* file inside the jar, which scatters byte-level changes throughout the
* shadow jar, which breaks any efforts to calculate efficient deltas between
* shadow jar builds (e.g. rsync). With static timestamps though, rsync just works.
*
* This is generally safe to turn on because nothing cares about
* internal timestamps, but it's off by default.
*
* @return this
*/
public ShadowJar zeroEntryTimestamps() {
this.zeroEntryTimestamps = true;
return this;
}

protected ZipCompressor getInternalCompressor() {
Expand Down Expand Up @@ -278,6 +299,14 @@ public <R extends Relocator> ShadowJar relocate(Class<R> relocatorClass, Action<
return this;
}

public boolean isZeroEntryTimestamps() {
return zeroEntryTimestamps;
}

public void setZeroEntryTimestamps(boolean zeroEntryTimestamps) {
this.zeroEntryTimestamps = zeroEntryTimestamps;
}

public List<Transformer> getTransformers() {
return this.transformers;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.jengelman.gradle.plugins.shadow

import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenFileRepository
import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification
Expand Down Expand Up @@ -61,7 +62,7 @@ class ShadowPluginSpec extends PluginSpecification {
.withArguments('--stacktrace')
.withProjectDir(dir.root)
.forwardOutput()
.withDebug(true)
// .withDebug(true)
.withTestKitDir(getTestKitDir())


Expand Down Expand Up @@ -484,6 +485,36 @@ class ShadowPluginSpec extends PluginSpecification {

}

def 'zeroes zip entry timestamps if requested'() {
given:
file('src/main/java/shadow/Passed.java') << '''
package shadow;
public class Passed {}
'''.stripIndent()

buildFile << """
dependencies { compile 'junit:junit:3.8.2' }
// tag::rename[]
shadowJar {
baseName = 'shadow'
classifier = null
version = null
zeroEntryTimestamps()
}
// end::rename[]
""".stripIndent()

when:
runner.withArguments('-S', 'shadowJar').build()

then:
getZipEntries(output("shadow.jar")).each {
assert it.getTime() == ShadowCopyAction.DOS_TIMESTAMP_ZERO
}
}

private String escapedPath(File file) {
file.path.replaceAll('\\\\', '\\\\\\\\')
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package com.github.jengelman.gradle.plugins.shadow.util

import com.github.jengelman.gradle.plugins.shadow.util.file.TestFile
import com.google.common.base.StandardSystemProperty
import org.apache.tools.zip.ZipEntry
import org.apache.tools.zip.ZipFile
import org.codehaus.plexus.util.IOUtil
import org.gradle.testkit.runner.GradleRunner
import org.junit.Rule
Expand Down Expand Up @@ -56,7 +58,7 @@ class PluginSpecification extends Specification {
GradleRunner.create()
.withProjectDir(dir.root)
.forwardOutput()
// .withDebug(true)
.withDebug(true)
.withTestKitDir(getTestKitDir())
}

Expand Down Expand Up @@ -100,6 +102,10 @@ class PluginSpecification extends Specification {
return sw.toString()
}

List<ZipEntry> getZipEntries(File f) {
new ZipFile(f).entries.toList()
}

void contains(File f, List<String> paths) {
JarFile jar = new JarFile(f)
paths.each { path ->
Expand Down

0 comments on commit 8d16c02

Please sign in to comment.