diff options
Diffstat (limited to 'src/main')
5 files changed, 180 insertions, 135 deletions
diff --git a/src/main/java/net/fabricmc/loom/build/ModCompileRemapper.java b/src/main/java/net/fabricmc/loom/build/ModCompileRemapper.java index ceccd3b2..76475caa 100644 --- a/src/main/java/net/fabricmc/loom/build/ModCompileRemapper.java +++ b/src/main/java/net/fabricmc/loom/build/ModCompileRemapper.java @@ -144,7 +144,7 @@ public class ModCompileRemapper { } try { - ModProcessor.processMods(project, modDependencies); + new ModProcessor(project).processMods(modDependencies); } catch (IOException e) { // Failed to remap, lets clean up to ensure we try again next time modDependencies.forEach(info -> info.getRemappedOutput().delete()); diff --git a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java index 81ee411f..af080877 100644 --- a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java +++ b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java @@ -25,6 +25,9 @@ package net.fabricmc.loom.configuration; import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -39,13 +42,14 @@ import org.gradle.api.artifacts.ExternalModuleDependency; import org.gradle.api.artifacts.repositories.MavenArtifactRepository; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.LoomRepositoryPlugin; import net.fabricmc.loom.build.ModCompileRemapper; import net.fabricmc.loom.configuration.DependencyProvider.DependencyInfo; -import net.fabricmc.loom.configuration.mods.ModProcessor; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.SourceRemapper; +import net.fabricmc.loom.util.ZipUtils; public class LoomDependencyManager { private static class ProviderList { @@ -148,7 +152,7 @@ public class LoomDependencyManager { for (Dependency dependency : configuration.getAllDependencies()) { for (File input : configuration.files(dependency)) { - JsonObject jsonObject = ModProcessor.readInstallerJson(input, project); + JsonObject jsonObject = readInstallerJson(input); if (jsonObject != null) { if (extension.getInstallerData() != null) { @@ -177,6 +181,20 @@ public class LoomDependencyManager { } } + public static JsonObject readInstallerJson(File file) { + try { + byte[] bytes = ZipUtils.unpackNullable(file.toPath(), "fabric-installer.json"); + + if (bytes == null) { + return null; + } + + return LoomGradlePlugin.GSON.fromJson(new String(bytes, StandardCharsets.UTF_8), JsonObject.class); + } catch (IOException e) { + throw new UncheckedIOException("Failed to try and read installer json from", e); + } + } + private static void handleInstallerJson(JsonObject jsonObject, Project project) { LoomGradleExtension extension = LoomGradleExtension.get(project); diff --git a/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerFile.java b/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerFile.java index 5bc892e9..4961e71d 100644 --- a/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerFile.java +++ b/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerFile.java @@ -45,44 +45,6 @@ public record AccessWidenerFile( * Reads the access-widener contained in a mod jar, or returns null if there is none. */ public static AccessWidenerFile fromModJar(Path modJarPath) { - if (ZipUtils.contains(modJarPath, "architectury.common.json")) { - String awPath = null; - byte[] commonJsonBytes; - - try { - commonJsonBytes = ZipUtils.unpackNullable(modJarPath, "architectury.common.json"); - } catch (IOException e) { - throw new UncheckedIOException("Failed to read access-widener file from: " + modJarPath.toAbsolutePath(), e); - } - - if (commonJsonBytes != null) { - JsonObject jsonObject = new Gson().fromJson(new String(commonJsonBytes, StandardCharsets.UTF_8), JsonObject.class); - - if (jsonObject.has("accessWidener")) { - awPath = jsonObject.get("accessWidener").getAsString(); - } else { - throw new IllegalArgumentException("The architectury.common.json file does not contain an accessWidener field."); - } - } else { - // ??????????? - throw new IllegalArgumentException("The architectury.common.json file does not exist."); - } - - byte[] content; - - try { - content = ZipUtils.unpack(modJarPath, awPath); - } catch (IOException e) { - throw new UncheckedIOException("Could not find access widener file (%s) defined in the architectury.common.json file of %s".formatted(awPath, modJarPath.toAbsolutePath()), e); - } - - return new AccessWidenerFile( - awPath, - modJarPath.getFileName().toString(), - content - ); - } - byte[] modJsonBytes; try { @@ -92,6 +54,44 @@ public record AccessWidenerFile( } if (modJsonBytes == null) { + if (ZipUtils.contains(modJarPath, "architectury.common.json")) { + String awPath = null; + byte[] commonJsonBytes; + + try { + commonJsonBytes = ZipUtils.unpackNullable(modJarPath, "architectury.common.json"); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read access-widener file from: " + modJarPath.toAbsolutePath(), e); + } + + if (commonJsonBytes != null) { + JsonObject jsonObject = new Gson().fromJson(new String(commonJsonBytes, StandardCharsets.UTF_8), JsonObject.class); + + if (jsonObject.has("accessWidener")) { + awPath = jsonObject.get("accessWidener").getAsString(); + } else { + throw new IllegalArgumentException("The architectury.common.json file does not contain an accessWidener field."); + } + } else { + // ??????????? + throw new IllegalArgumentException("The architectury.common.json file does not exist."); + } + + byte[] content; + + try { + content = ZipUtils.unpack(modJarPath, awPath); + } catch (IOException e) { + throw new UncheckedIOException("Could not find access widener file (%s) defined in the architectury.common.json file of %s".formatted(awPath, modJarPath.toAbsolutePath()), e); + } + + return new AccessWidenerFile( + awPath, + modJarPath.getFileName().toString(), + content + ); + } + return null; } diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java index 5cb175c7..0c28cac9 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java @@ -28,9 +28,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -40,11 +38,8 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.jar.Attributes; -import java.util.jar.JarFile; import java.util.jar.Manifest; -import java.util.stream.Collectors; import java.util.stream.Stream; -import java.util.zip.ZipEntry; import com.google.common.base.Stopwatch; import com.google.gson.JsonObject; @@ -59,7 +54,6 @@ import net.fabricmc.accesswidener.AccessWidenerReader; import net.fabricmc.accesswidener.AccessWidenerRemapper; import net.fabricmc.accesswidener.AccessWidenerWriter; import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.RemappedConfigurationEntry; import net.fabricmc.loom.configuration.processors.dependency.ModDependencyInfo; @@ -75,39 +69,54 @@ import net.fabricmc.loom.util.srg.CoreModClassRemapper; import net.fabricmc.mappingio.tree.MemoryMappingTree; public class ModProcessor { - public static void processMods(Project project, List<ModDependencyInfo> processList) throws IOException { - if (processList.stream().noneMatch(ModDependencyInfo::requiresRemapping)) { - return; - } + private static final String fromM = MappingsNamespace.INTERMEDIARY.toString(); + private static final String toM = MappingsNamespace.NAMED.toString(); + + private final Project project; + public ModProcessor(Project project) { + this.project = project; + } + + public void processMods(List<ModDependencyInfo> processList) throws IOException { ArrayList<ModDependencyInfo> remapList = new ArrayList<>(); for (ModDependencyInfo info : processList) { if (info.requiresRemapping()) { - if (info.getRemappedOutput().exists()) { - info.getRemappedOutput().delete(); - } + project.getLogger().debug("{} requires remapping", info.getInputFile()); + Files.deleteIfExists(info.getRemappedOutput().toPath()); remapList.add(info); } } - remapJars(project, processList); + if (remapList.isEmpty()) { + project.getLogger().debug("No mods to remap, skipping"); + return; + } + + try { + remapJars(remapList); + } catch (Exception e) { + project.getLogger().error("Failed to remap %d mods".formatted(remapList.size()), e); + for (ModDependencyInfo info : remapList) { + Files.deleteIfExists(info.getRemappedOutput().toPath()); + } + + throw e; + } + + // Check all the mods we expect exist for (ModDependencyInfo info : processList) { if (!info.getRemappedOutput().exists()) { throw new RuntimeException("Failed to find remapped mod" + info); } } - - for (ModDependencyInfo info : remapList) { - stripNestedJars(info.getRemappedOutput()); - } } - private static void stripNestedJars(File file) { + private void stripNestedJars(File file) { if (!ZipUtils.contains(file.toPath(), "fabric.mod.json")) return; - // Strip out all contained jar info as we dont want loader to try and load the jars contained in dev. try { ZipUtils.transformJson(JsonObject.class, file.toPath(), Map.of("fabric.mod.json", json -> { @@ -122,7 +131,7 @@ public class ModProcessor { /** * Remap another mod's access widener from intermediary to named, so that loader can apply it in our dev-env. */ - private static byte[] remapAccessWidener(byte[] input, Remapper remapper) { + private byte[] remapAccessWidener(byte[] input, Remapper remapper) { int version = AccessWidenerReader.readVersion(input); AccessWidenerWriter writer = new AccessWidenerWriter(version); @@ -137,38 +146,34 @@ public class ModProcessor { return writer.write(); } - private static void remapJars(Project project, List<ModDependencyInfo> processList) throws IOException { - LoomGradleExtension extension = LoomGradleExtension.get(project); + private void remapJars(List<ModDependencyInfo> remapList) throws IOException { + final LoomGradleExtension extension = LoomGradleExtension.get(project); + final MinecraftMappedProvider mappedProvider = extension.getMinecraftMappedProvider(); + final MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); String fromM = extension.isForge() ? MappingsNamespace.SRG.toString() : MappingsNamespace.INTERMEDIARY.toString(); String toM = MappingsNamespace.NAMED.toString(); - MinecraftMappedProvider mappedProvider = extension.getMinecraftMappedProvider(); - MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); - - Path mc = extension.isForge() ? mappedProvider.getSrgJar().toPath() : mappedProvider.getIntermediaryJar().toPath(); + Path intermediaryJar = extension.isForge() ? mappedProvider.getSrgJar().toPath() : mappedProvider.getIntermediaryJar().toPath(); Path[] mcDeps = project.getConfigurations().getByName(Constants.Configurations.LOADER_DEPENDENCIES).getFiles() .stream().map(File::toPath).toArray(Path[]::new); - List<ModDependencyInfo> remapList = processList.stream().filter(ModDependencyInfo::requiresRemapping).collect(Collectors.toList()); - Stopwatch stopwatch = Stopwatch.createStarted(); project.getLogger().lifecycle(":remapping " + remapList.size() + " mods (TinyRemapper, " + fromM + " -> " + toM + ")"); MemoryMappingTree mappings = (fromM.equals("srg") || toM.equals("srg")) && extension.isForge() ? mappingsProvider.getMappingsWithSrg() : mappingsProvider.getMappings(); LoggerFilter.replaceSystemOut(); - TinyRemapper remapper = TinyRemapper.newRemapper() + final TinyRemapper remapper = TinyRemapper.newRemapper() .logger(project.getLogger()::lifecycle) .logUnknownInvokeDynamic(false) .withMappings(TinyRemapperHelper.create(mappings, fromM, toM, false)) .renameInvalidLocals(false) .build(); - remapper.readClassPathAsync(mc); + remapper.readClassPathAsync(intermediaryJar); if (extension.isForgeAndNotOfficial()) { remapper.readClassPathAsync(mappedProvider.getForgeSrgJar().toPath()); } - remapper.readClassPathAsync(mcDeps); final Map<ModDependencyInfo, InputTag> tagMap = new HashMap<>(); @@ -194,29 +199,32 @@ public class ModProcessor { tagMap.put(info, tag); } - // Apply this in a second loop as we need to ensure all the inputs are on the classpath before remapping. - for (ModDependencyInfo info : remapList) { - try { - OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(info.getRemappedOutput().toPath()).build(); + try { + // Apply this in a second loop as we need to ensure all the inputs are on the classpath before remapping. + for (ModDependencyInfo info : remapList) { + try { + OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(info.getRemappedOutput().toPath()).build(); - outputConsumer.addNonClassFiles(info.getInputFile().toPath(), NonClassCopyMode.FIX_META_INF, remapper); - outputConsumerMap.put(info, outputConsumer); - String accessWidener = info.getAccessWidener(); + outputConsumer.addNonClassFiles(info.getInputFile().toPath(), NonClassCopyMode.FIX_META_INF, remapper); + outputConsumerMap.put(info, outputConsumer); - if (accessWidener != null) { - accessWidenerMap.put(info, remapAccessWidener(ZipUtils.unpack(info.inputFile.toPath(), accessWidener), remapper.getRemapper())); - } + final ModDependencyInfo.AccessWidenerData accessWidenerData = info.getAccessWidenerData(); - remapper.apply(outputConsumer, tagMap.get(info)); - } catch (Exception e) { - remapper.finish(); - Files.deleteIfExists(info.getRemappedOutput().toPath()); + if (accessWidenerData != null) { + project.getLogger().debug("Remapping access widener in {}", info.getInputFile()); + byte[] remappedAw = remapAccessWidener(accessWidenerData.content(), remapper.getEnvironment().getRemapper()); + accessWidenerMap.put(info, remappedAw); + } - throw new RuntimeException("Failed to remap: " + info.getRemappedNotation(), e); + remapper.apply(outputConsumer, tagMap.get(info)); + } catch (Exception e) { + throw new RuntimeException("Failed to remap: " + info.getRemappedNotation(), e); + } } + } finally { + remapper.finish(); } - remapper.finish(); project.getLogger().lifecycle(":remapped " + remapList.size() + " mods (TinyRemapper, " + fromM + " -> " + toM + ") in " + stopwatch.stop()); for (ModDependencyInfo info : remapList) { @@ -224,9 +232,12 @@ public class ModProcessor { byte[] accessWidener = accessWidenerMap.get(info); if (accessWidener != null) { - ZipUtils.replace(info.getRemappedOutput().toPath(), info.getAccessWidener(), accessWidener); + assert info.getAccessWidenerData() != null; + ZipUtils.replace(info.getRemappedOutput().toPath(), info.getAccessWidenerData().path(), accessWidener); } + stripNestedJars(info.getRemappedOutput()); + if (extension.isForge()) { AtRemapper.remap(project.getLogger(), info.getRemappedOutput().toPath(), mappings); CoreModClassRemapper.remapJar(info.getRemappedOutput().toPath(), mappings, project.getLogger()); @@ -286,30 +297,4 @@ public class ModProcessor { if (attrs.isEmpty()) it.remove(); } } - - public static JsonObject readInstallerJson(File file, Project project) { - try { - LoomGradleExtension extension = LoomGradleExtension.get(project); - - String jsonStr; - - try (JarFile jarFile = new JarFile(file)) { - ZipEntry entry = jarFile.getEntry("fabric-installer.json"); - - if (entry == null) { - return null; - } - - try (InputStream inputstream = jarFile.getInputStream(entry)) { - jsonStr = new String(inputstream.readAllBytes(), StandardCharsets.UTF_8); - } - } - - return LoomGradlePlugin.GSON.fromJson(jsonStr, JsonObject.class); - } catch (IOException e) { - e.printStackTrace(); - } - - return null; - } } diff --git a/src/main/java/net/fabricmc/loom/configuration/processors/dependency/ModDependencyInfo.java b/src/main/java/net/fabricmc/loom/configuration/processors/dependency/ModDependencyInfo.java index e043ce73..10aa256a 100644 --- a/src/main/java/net/fabricmc/loom/configuration/processors/dependency/ModDependencyInfo.java +++ b/src/main/java/net/fabricmc/loom/configuration/processors/dependency/ModDependencyInfo.java @@ -27,17 +27,19 @@ package net.fabricmc.loom.configuration.processors.dependency; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; +import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; +import java.nio.file.Path; import com.google.gson.JsonObject; import org.apache.commons.io.FileUtils; import org.gradle.api.artifacts.Configuration; import org.jetbrains.annotations.Nullable; +import net.fabricmc.accesswidener.AccessWidenerReader; import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.util.ZipUtils; public class ModDependencyInfo { private final String group; @@ -47,9 +49,11 @@ public class ModDependencyInfo { public final String classifier; public final File inputFile; public final Configuration targetConfig; - public final RemapData remapData; + @Nullable + private final AccessWidenerData accessWidenerData; + private boolean forceRemap = false; public ModDependencyInfo(String group, String name, String version, @Nullable String classifier, File inputFile, Configuration targetConfig, RemapData remapData) { @@ -60,6 +64,12 @@ public class ModDependencyInfo { this.inputFile = inputFile; this.targetConfig = targetConfig; this.remapData = remapData; + + try { + this.accessWidenerData = tryReadAccessWidenerData(getInputFile().toPath()); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read access widener data from" + inputFile, e); + } } public String getRemappedNotation() { @@ -106,13 +116,42 @@ public class ModDependencyInfo { return inputFile; } + private boolean outputHasInvalidAccessWidener() { + if (accessWidenerData == null) { + // This mod doesn't use an AW + return false; + } + + assert getRemappedOutput().exists(); + final AccessWidenerData outputAWData; + + try { + outputAWData = tryReadAccessWidenerData(getRemappedOutput().toPath()); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read output access widener data from " + getRemappedOutput(), e); + } + + if (outputAWData == null) { + // We know for sure the input has an AW, something is wrong if the output hasn't got one. + return true; + } + + // The output jar must have an AW in the "named" namespace. + return !MappingsNamespace.NAMED.toString().equals(outputAWData.header().getNamespace()); + } + public boolean requiresRemapping() { - return !getRemappedOutput().exists() || inputFile.lastModified() <= 0 || inputFile.lastModified() > getRemappedOutput().lastModified() || forceRemap || !getRemappedPom().exists(); + return !getRemappedOutput().exists() || inputFile.lastModified() <= 0 || inputFile.lastModified() > getRemappedOutput().lastModified() || forceRemap || !getRemappedPom().exists() || outputHasInvalidAccessWidener(); } public void finaliseRemapping() { getRemappedOutput().setLastModified(inputFile.lastModified()); savePom(); + + // Validate that the remapped AW is what we want. + if (outputHasInvalidAccessWidener()) { + throw new RuntimeException("Failed to validate remapped access widener in " + getRemappedOutput()); + } } private void savePom() { @@ -147,23 +186,26 @@ public class ModDependencyInfo { return classifier != null && !classifier.isEmpty(); } - public String getAccessWidener() throws IOException { - try (JarFile jarFile = new JarFile(getInputFile())) { - JarEntry modJsonEntry = jarFile.getJarEntry("fabric.mod.json"); + @Nullable + public AccessWidenerData getAccessWidenerData() { + return accessWidenerData; + } + + private static AccessWidenerData tryReadAccessWidenerData(Path inputJar) throws IOException { + byte[] modJsonBytes = ZipUtils.unpack(inputJar, "fabric.mod.json"); + JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(new String(modJsonBytes, StandardCharsets.UTF_8), JsonObject.class); - if (modJsonEntry == null) { - return null; - } + if (!jsonObject.has("accessWidener")) { + return null; + } - try (InputStream inputStream = jarFile.getInputStream(modJsonEntry)) { - JsonObject json = LoomGradlePlugin.GSON.fromJson(new InputStreamReader(inputStream), JsonObject.class); + String accessWidenerPath = jsonObject.get("accessWidener").getAsString(); + byte[] accessWidener = ZipUtils.unpack(inputJar, accessWidenerPath); + AccessWidenerReader.Header header = AccessWidenerReader.readHeader(accessWidener); - if (!json.has("accessWidener")) { - return null; - } + return new AccessWidenerData(accessWidenerPath, header, accessWidener); + } - return json.get("accessWidener").getAsString(); - } - } + public record AccessWidenerData(String path, AccessWidenerReader.Header header, byte[] content) { } } |