Fix included none mod libraries not being reproducible. (#923)

Closes #921
This commit is contained in:
modmuss
2023-07-04 17:13:48 +01:00
committed by GitHub
parent 6413a9312c
commit e49a051ea0
3 changed files with 79 additions and 21 deletions

View File

@@ -53,7 +53,7 @@ import org.jetbrains.annotations.Nullable;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.task.RemapTaskConfiguration;
import net.fabricmc.loom.util.ZipUtils;
import net.fabricmc.loom.util.ZipReprocessorUtil;
import net.fabricmc.loom.util.fmj.FabricModJsonFactory;
public final class IncludedJarFactory {
@@ -163,7 +163,7 @@ public final class IncludedJarFactory {
try {
FileUtils.copyFile(input, tempFile);
ZipUtils.add(tempFile.toPath(), "fabric.mod.json", generateModForDependency(metadata).getBytes(StandardCharsets.UTF_8));
ZipReprocessorUtil.appendZipEntry(tempFile, "fabric.mod.json", generateModForDependency(metadata).getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
throw new UncheckedIOException("Failed to add dummy mod while including %s".formatted(input), e);
}

View File

@@ -95,44 +95,80 @@ public class ZipReprocessorUtil {
return;
}
try (ZipFile zipFile = new ZipFile(file)) {
try (var zipFile = new ZipFile(file)) {
ZipEntry[] entries;
if (reproducibleFileOrder) {
entries = zipFile.stream().sorted(Comparator.comparing(ZipEntry::getName, ZipReprocessorUtil::specialOrdering)).toArray(ZipEntry[]::new);
entries = zipFile.stream()
.sorted(Comparator.comparing(ZipEntry::getName, ZipReprocessorUtil::specialOrdering))
.toArray(ZipEntry[]::new);
} else {
entries = zipFile.stream().toArray(ZipEntry[]::new);
entries = zipFile.stream()
.toArray(ZipEntry[]::new);
}
ByteArrayOutputStream outZip = new ByteArrayOutputStream(zipFile.size());
final var outZip = new ByteArrayOutputStream(entries.length);
try (ZipOutputStream zipOutputStream = new ZipOutputStream(outZip)) {
try (var zipOutputStream = new ZipOutputStream(outZip)) {
for (ZipEntry entry : entries) {
ZipEntry newEntry = entry;
if (!preserveFileTimestamps) {
newEntry = new ZipEntry(entry.getName());
newEntry.setTime(CONSTANT_TIME_FOR_ZIP_ENTRIES);
newEntry.setLastModifiedTime(FileTime.fromMillis(CONSTANT_TIME_FOR_ZIP_ENTRIES));
newEntry.setLastAccessTime(FileTime.fromMillis(CONSTANT_TIME_FOR_ZIP_ENTRIES));
setConstantFileTime(newEntry);
}
zipOutputStream.putNextEntry(newEntry);
InputStream inputStream = zipFile.getInputStream(entry);
byte[] buf = new byte[1024];
int length;
while ((length = inputStream.read(buf)) > 0) {
zipOutputStream.write(buf, 0, length);
}
zipOutputStream.closeEntry();
copyZipEntry(zipOutputStream, newEntry, zipFile.getInputStream(entry));
}
}
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
try (var fileOutputStream = new FileOutputStream(file)) {
outZip.writeTo(fileOutputStream);
}
}
}
public static void appendZipEntry(File file, String path, byte[] data) throws IOException {
try (var zipFile = new ZipFile(file)) {
ZipEntry[] entries = zipFile.stream().toArray(ZipEntry[]::new);
final var outZip = new ByteArrayOutputStream(entries.length);
try (var zipOutputStream = new ZipOutputStream(outZip)) {
// Copy existing entries
for (ZipEntry entry : entries) {
copyZipEntry(zipOutputStream, entry, zipFile.getInputStream(entry));
}
// Append the new entry
var entry = new ZipEntry(path);
setConstantFileTime(entry);
zipOutputStream.putNextEntry(entry);
zipOutputStream.write(data, 0, data.length);
zipOutputStream.closeEntry();
}
try (var fileOutputStream = new FileOutputStream(file)) {
outZip.writeTo(fileOutputStream);
}
}
}
private static void copyZipEntry(ZipOutputStream zipOutputStream, ZipEntry entry, InputStream inputStream) throws IOException {
zipOutputStream.putNextEntry(entry);
byte[] buf = new byte[1024];
int length;
while ((length = inputStream.read(buf)) > 0) {
zipOutputStream.write(buf, 0, length);
}
zipOutputStream.closeEntry();
}
private static void setConstantFileTime(ZipEntry entry) {
entry.setTime(ZipReprocessorUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES);
entry.setLastModifiedTime(FileTime.fromMillis(ZipReprocessorUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES));
entry.setLastAccessTime(FileTime.fromMillis(ZipReprocessorUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES));
}
}

View File

@@ -29,7 +29,9 @@ import java.nio.file.Files
import spock.lang.Specification
import net.fabricmc.loom.util.Checksum
import net.fabricmc.loom.util.Pair
import net.fabricmc.loom.util.ZipReprocessorUtil
import net.fabricmc.loom.util.ZipUtils
class ZipUtilsTest extends Specification {
@@ -150,4 +152,24 @@ class ZipUtilsTest extends Specification {
then:
!result
}
def "append zip entry"() {
given:
// Create a reproducible input zip
def dir = Files.createTempDirectory("loom-zip-test")
def zip = Files.createTempFile("loom-zip-test", ".zip")
def fileInside = dir.resolve("text.txt")
Files.writeString(fileInside, "hello world")
ZipUtils.pack(dir, zip)
ZipReprocessorUtil.reprocessZip(zip.toFile(), true, false)
when:
// Add an entry to it
ZipReprocessorUtil.appendZipEntry(zip.toFile(), "fabric.mod.json", "Some text".getBytes(StandardCharsets.UTF_8))
then:
ZipUtils.unpack(zip, "text.txt") == "hello world".bytes
ZipUtils.unpack(zip, "fabric.mod.json") == "Some text".bytes
Checksum.sha1Hex(zip) == "232ecda4c770bde8ba618e7a194a4f7b57928dc5"
}
}