mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
IJI-772 reproducibility test: build date time supplied externally for JPS project artifacts
GitOrigin-RevId: b3e778ab6ba2ecb547c988bc267939884427031c
This commit is contained in:
committed by
intellij-monorepo-bot
parent
505c26d0aa
commit
cec73c0390
@@ -50,4 +50,9 @@ public interface GlobalOptions {
|
||||
* This will allow JPS process to access bundle's resources and provide localized error/warning/diagnostic messages
|
||||
*/
|
||||
String LANGUAGE_BUNDLE = "jps.language.bundle";
|
||||
|
||||
/**
|
||||
* See https://reproducible-builds.org/specs/source-date-epoch/
|
||||
*/
|
||||
String BUILD_DATE_IN_SECONDS = "SOURCE_DATE_EPOCH";
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.intellij.util.io.ZipUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.api.GlobalOptions;
|
||||
import org.jetbrains.jps.builders.BuildOutputConsumer;
|
||||
import org.jetbrains.jps.builders.JpsBuildBundle;
|
||||
import org.jetbrains.jps.builders.logging.ProjectBuilderLogger;
|
||||
@@ -44,6 +45,7 @@ public final class JarsBuilder {
|
||||
private Map<JarInfo, File> myBuiltJars;
|
||||
private final BuildOutputConsumer myOutputConsumer;
|
||||
private final ArtifactOutputToSourceMapping myOutSrcMapping;
|
||||
private final @Nullable Long buildDateInMillis;
|
||||
|
||||
public JarsBuilder(Set<JarInfo> jarsToBuild, CompileContext context, BuildOutputConsumer outputConsumer,
|
||||
ArtifactOutputToSourceMapping outSrcMapping) {
|
||||
@@ -55,6 +57,11 @@ public final class JarsBuilder {
|
||||
}
|
||||
myJarsToBuild = evaluator.getJars();
|
||||
myContext = context;
|
||||
String buildDateInSeconds = context.getBuilderParameter(GlobalOptions.BUILD_DATE_IN_SECONDS);
|
||||
if (buildDateInSeconds == null) {
|
||||
buildDateInSeconds = System.getenv(GlobalOptions.BUILD_DATE_IN_SECONDS);
|
||||
}
|
||||
buildDateInMillis = buildDateInSeconds != null ? Long.valueOf(buildDateInSeconds) * 1000 : null;
|
||||
}
|
||||
|
||||
public boolean buildJars() throws IOException, ProjectBuildException {
|
||||
@@ -282,7 +289,7 @@ public final class JarsBuilder {
|
||||
private void extractFileAndAddToJar(final JarOutputStream jarOutputStream, final JarBasedArtifactRootDescriptor root,
|
||||
final String relativeOutputPath, final Set<? super String> writtenPaths)
|
||||
throws IOException {
|
||||
final long timestamp = FSOperations.lastModified(root.getRootFile());
|
||||
final long timestamp = buildDateInMillis != null ? buildDateInMillis : FSOperations.lastModified(root.getRootFile());
|
||||
root.processEntries(new JarBasedArtifactRootDescriptor.EntryProcessor() {
|
||||
@Override
|
||||
public void process(@Nullable InputStream inputStream, @NotNull String relativePath, ZipEntry entry) throws IOException {
|
||||
@@ -369,7 +376,7 @@ public final class JarsBuilder {
|
||||
}
|
||||
|
||||
|
||||
private static String addParentDirectories(JarOutputStream jarOutputStream, Set<? super String> writtenPaths, String relativePath) throws IOException {
|
||||
private String addParentDirectories(JarOutputStream jarOutputStream, Set<? super String> writtenPaths, String relativePath) throws IOException {
|
||||
while (StringUtil.startsWithChar(relativePath, '/')) {
|
||||
relativePath = relativePath.substring(1);
|
||||
}
|
||||
@@ -384,10 +391,13 @@ public final class JarsBuilder {
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
private static void addDirectoryEntry(final ZipOutputStream output, @NonNls final String relativePath, Set<? super String> writtenPaths) throws IOException {
|
||||
private void addDirectoryEntry(final ZipOutputStream output, @NonNls final String relativePath, Set<? super String> writtenPaths) throws IOException {
|
||||
if (!writtenPaths.add(relativePath)) return;
|
||||
|
||||
ZipEntry e = new ZipEntry(relativePath);
|
||||
if (buildDateInMillis != null) {
|
||||
e.setTime(buildDateInMillis);
|
||||
}
|
||||
e.setMethod(ZipEntry.STORED);
|
||||
e.setSize(0);
|
||||
e.setCrc(0);
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.intellij.util.PathUtil
|
||||
import com.intellij.util.io.directoryContent
|
||||
import com.intellij.util.io.systemIndependentPath
|
||||
import com.intellij.util.io.zipFile
|
||||
import org.jetbrains.jps.api.GlobalOptions
|
||||
import org.jetbrains.jps.builders.CompileScopeTestBuilder
|
||||
import org.jetbrains.jps.incremental.artifacts.LayoutElementTestUtil.archive
|
||||
import org.jetbrains.jps.incremental.artifacts.LayoutElementTestUtil.root
|
||||
@@ -18,11 +19,19 @@ import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.security.DigestInputStream
|
||||
import java.security.MessageDigest
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.jar.JarFile
|
||||
import java.util.zip.CRC32
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
import java.util.zip.ZipOutputStream
|
||||
import kotlin.io.path.inputStream
|
||||
|
||||
class ArtifactBuilderTest : ArtifactBuilderTestCase() {
|
||||
fun testFileCopy() {
|
||||
@@ -306,6 +315,38 @@ class ArtifactBuilderTest : ArtifactBuilderTestCase() {
|
||||
}})
|
||||
}
|
||||
|
||||
private fun Path.checksum(): String = inputStream().buffered().use { input ->
|
||||
val digest = MessageDigest.getInstance("SHA-256")
|
||||
DigestInputStream(input, digest).use {
|
||||
var bytesRead = 0
|
||||
val buffer = ByteArray(1024 * 8)
|
||||
while (bytesRead != -1) {
|
||||
bytesRead = it.read(buffer)
|
||||
}
|
||||
}
|
||||
Base64.getEncoder().encodeToString(digest.digest())
|
||||
}
|
||||
|
||||
fun `test jars build reproducibility`() {
|
||||
myBuildParams[GlobalOptions.BUILD_DATE_IN_SECONDS] = (System.currentTimeMillis() / 1000).toString()
|
||||
val jar = root().archive("a.jar")
|
||||
.extractedDir(createXJarFile(), "/dir")
|
||||
.let { addArtifact("a", it).outputPath }
|
||||
?.let { Paths.get(it).resolve("a.jar") }
|
||||
requireNotNull(jar)
|
||||
val checksums = (1..2).map {
|
||||
// sleeping more than a second ensures different last modification time
|
||||
// for the next iteration jar if the build date isn't provided or ignored
|
||||
TimeUnit.SECONDS.sleep(2)
|
||||
FileUtil.delete(jar.parent)
|
||||
assert(!Files.exists(jar))
|
||||
buildAll()
|
||||
assert(Files.exists(jar))
|
||||
jar.checksum()
|
||||
}.distinct()
|
||||
assert(checksums.count() == 1)
|
||||
}
|
||||
|
||||
fun `test no duplicated directory entries for extracted directory packed into JAR file`() {
|
||||
val zipPath = createXJarFile()
|
||||
val a = addArtifact("a", root().archive("a.jar").extractedDir(zipPath, ""))
|
||||
|
||||
Reference in New Issue
Block a user