diff --git a/src/main/java/net/fabricmc/loom/api/InterfaceInjectionExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/InterfaceInjectionExtensionAPI.java new file mode 100644 index 0000000..5a3cfbd --- /dev/null +++ b/src/main/java/net/fabricmc/loom/api/InterfaceInjectionExtensionAPI.java @@ -0,0 +1,52 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.api; + +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.SourceSet; + +public interface InterfaceInjectionExtensionAPI { + /** + * When true loom will inject interfaces declared in dependency mod manifests into the minecraft jar file. + * This is used to expose interfaces that are implemented on Minecraft classes by mixins at runtime + * in the dev environment. + * + * @return the property controlling interface injection. + */ + Property getEnableDependencyInterfaceInjection(); + + /** + * Contains a list of {@link SourceSet} that may contain a fabric.mod.json file with interfaces to inject. + * By default, this list contains only the main {@link SourceSet}. + * + * @return the list property containing the {@link SourceSet} + */ + ListProperty getInterfaceInjectionSourceSets(); + + default boolean isEnabled() { + return getEnableDependencyInterfaceInjection().get() || !getInterfaceInjectionSourceSets().get().isEmpty(); + } +} diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index 6fc8721..f993e37 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 FabricMC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -86,6 +86,12 @@ public interface LoomGradleExtensionAPI { // TODO: move this from LoomGradleExtensionAPI to LoomGradleExtension once getRefmapName & setRefmapName is removed. MixinExtensionAPI getMixin(); + default void interfaceInjection(Action action) { + action.execute(getInterfaceInjection()); + } + + InterfaceInjectionExtensionAPI getInterfaceInjection(); + Property getCustomMinecraftManifest(); /** @@ -131,15 +137,6 @@ public interface LoomGradleExtensionAPI { */ Property getIntermediaryUrl(); - /** - * When true loom will inject interfaces declared in mod manifests into the minecraft jar file. - * This is used to expose interfaces that are implemented on Minecraft classes by mixins at runtime - * in the dev environment. - * - * @return the property controlling interface injection. - */ - Property getEnableInterfaceInjection(); - @ApiStatus.Experimental Property getMinecraftJarConfiguration(); diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index 9e6f859..ee36469 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2016-2021 FabricMC + * Copyright (c) 2016-2022 FabricMC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -233,7 +233,7 @@ public final class CompileConfiguration { } } - if (extension.getEnableInterfaceInjection().get()) { + if (extension.getInterfaceInjection().isEnabled()) { InterfaceInjectionProcessor jarProcessor = new InterfaceInjectionProcessor(project); if (!jarProcessor.isEmpty()) { diff --git a/src/main/java/net/fabricmc/loom/configuration/accesswidener/TransitiveAccessWidenerMappingsProcessor.java b/src/main/java/net/fabricmc/loom/configuration/accesswidener/TransitiveAccessWidenerMappingsProcessor.java index 549200a..e82ea83 100644 --- a/src/main/java/net/fabricmc/loom/configuration/accesswidener/TransitiveAccessWidenerMappingsProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/accesswidener/TransitiveAccessWidenerMappingsProcessor.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 FabricMC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,58 +24,44 @@ package net.fabricmc.loom.configuration.accesswidener; -import java.io.IOException; -import java.io.Reader; -import java.io.Writer; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.List; +import org.gradle.api.Project; import org.gradle.api.logging.Logger; import net.fabricmc.accesswidener.AccessWidenerReader; import net.fabricmc.accesswidener.AccessWidenerVisitor; import net.fabricmc.accesswidener.TransitiveOnlyFilter; +import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; -import net.fabricmc.mappingio.MappingReader; -import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; -import net.fabricmc.mappingio.format.Tiny2Writer; +import net.fabricmc.loom.task.GenerateSourcesTask; import net.fabricmc.mappingio.tree.MappingTree; import net.fabricmc.mappingio.tree.MemoryMappingTree; -public final class TransitiveAccessWidenerMappingsProcessor { - private TransitiveAccessWidenerMappingsProcessor() { - } +public record TransitiveAccessWidenerMappingsProcessor(Project project) implements GenerateSourcesTask.MappingsProcessor { + @Override + public boolean transform(MemoryMappingTree mappings) { + final LoomGradleExtension extension = LoomGradleExtension.get(project); + List accessWideners = extension.getTransitiveAccessWideners(); - public static void process(Path inputMappings, Path outputMappings, List accessWideners, Logger logger) { - MemoryMappingTree mappingTree = new MemoryMappingTree(); - - try (Reader reader = Files.newBufferedReader(inputMappings, StandardCharsets.UTF_8)) { - MappingReader.read(reader, new MappingSourceNsSwitch(mappingTree, MappingsNamespace.INTERMEDIARY.toString())); - } catch (IOException e) { - throw new RuntimeException("Failed to read mappings", e); + if (accessWideners.isEmpty()) { + return false; } - if (!MappingsNamespace.INTERMEDIARY.toString().equals(mappingTree.getSrcNamespace())) { - throw new IllegalStateException("Mapping tree must have intermediary src mappings not " + mappingTree.getSrcNamespace()); + if (!MappingsNamespace.INTERMEDIARY.toString().equals(mappings.getSrcNamespace())) { + throw new IllegalStateException("Mapping tree must have intermediary src mappings not " + mappings.getSrcNamespace()); } for (AccessWidenerFile accessWidener : accessWideners) { - MappingCommentVisitor mappingCommentVisitor = new MappingCommentVisitor(accessWidener.modId(), mappingTree, logger); + MappingCommentVisitor mappingCommentVisitor = new MappingCommentVisitor(accessWidener.modId(), mappings, project.getLogger()); AccessWidenerReader accessWidenerReader = new AccessWidenerReader(new TransitiveOnlyFilter(mappingCommentVisitor)); accessWidenerReader.read(accessWidener.content()); } - try (Writer writer = Files.newBufferedWriter(outputMappings, StandardCharsets.UTF_8)) { - Tiny2Writer tiny2Writer = new Tiny2Writer(writer, false); - mappingTree.accept(new MappingSourceNsSwitch(tiny2Writer, MappingsNamespace.NAMED.toString())); - } catch (IOException e) { - throw new RuntimeException("Failed to write mappings", e); - } + return true; } - private static record MappingCommentVisitor(String modId, MemoryMappingTree mappingTree, Logger logger) implements AccessWidenerVisitor { + private record MappingCommentVisitor(String modId, MemoryMappingTree mappingTree, Logger logger) implements AccessWidenerVisitor { @Override public void visitClass(String name, AccessWidenerReader.AccessType access, boolean transitive) { MappingTree.ClassMapping classMapping = mappingTree.getClass(name); diff --git a/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java b/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java index bdf1473..bad920f 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 FabricMC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,7 +26,9 @@ package net.fabricmc.loom.configuration.ifaceinject; import java.io.File; import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; @@ -40,40 +42,47 @@ import java.util.stream.Collectors; import com.google.common.base.Preconditions; import com.google.common.hash.Hasher; import com.google.common.hash.Hashing; -import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import org.gradle.api.Project; +import org.gradle.api.tasks.SourceSet; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.commons.Remapper; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.api.InterfaceInjectionExtensionAPI; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.RemappedConfigurationEntry; import net.fabricmc.loom.configuration.processors.JarProcessor; +import net.fabricmc.loom.task.GenerateSourcesTask; import net.fabricmc.loom.util.Checksum; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.Pair; import net.fabricmc.loom.util.TinyRemapperHelper; import net.fabricmc.loom.util.ZipUtils; +import net.fabricmc.mappingio.tree.MappingTree; +import net.fabricmc.mappingio.tree.MemoryMappingTree; import net.fabricmc.tinyremapper.TinyRemapper; -public class InterfaceInjectionProcessor implements JarProcessor { +public class InterfaceInjectionProcessor implements JarProcessor, GenerateSourcesTask.MappingsProcessor { // Filename used to store hash of injected interfaces in processed jar file private static final String HASH_FILENAME = "injected_interfaces.sha256"; private final Map> injectedInterfaces; private final Project project; private final LoomGradleExtension extension; + private final InterfaceInjectionExtensionAPI interfaceInjectionExtension; private final byte[] inputHash; private Map> remappedInjectedInterfaces; public InterfaceInjectionProcessor(Project project) { this.project = project; this.extension = LoomGradleExtension.get(project); + this.interfaceInjectionExtension = this.extension.getInterfaceInjection(); this.injectedInterfaces = getInjectedInterfaces().stream() .collect(Collectors.groupingBy(InjectedInterface::className)); @@ -158,6 +167,20 @@ public class InterfaceInjectionProcessor implements JarProcessor { private List getInjectedInterfaces() { List result = new ArrayList<>(); + if (interfaceInjectionExtension.getEnableDependencyInterfaceInjection().get()) { + result.addAll(getDependencyInjectedInterfaces()); + } + + for (SourceSet sourceSet : interfaceInjectionExtension.getInterfaceInjectionSourceSets().get()) { + result.addAll(getSourceInjectedInterface(sourceSet)); + } + + return result; + } + + private List getDependencyInjectedInterfaces() { + List result = new ArrayList<>(); + for (RemappedConfigurationEntry entry : Constants.MOD_COMPILE_ENTRIES) { // Only apply injected interfaces from mods that are part of the compile classpath if (!entry.compileClasspath()) { @@ -176,12 +199,81 @@ public class InterfaceInjectionProcessor implements JarProcessor { return result; } + private List getSourceInjectedInterface(SourceSet sourceSet) { + final File fabricModJson; + + try { + fabricModJson = sourceSet.getResources() + .matching(patternFilterable -> patternFilterable.include("fabric.mod.json")) + .getSingleFile(); + } catch (IllegalStateException e) { + // File not found + return Collections.emptyList(); + } + + final String jsonString; + + try { + jsonString = Files.readString(fabricModJson.toPath(), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read fabric.mod.json", e); + } + + final JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(jsonString, JsonObject.class); + + return InjectedInterface.fromJson(jsonObject); + } + + @Override + public boolean transform(MemoryMappingTree mappings) { + if (injectedInterfaces.isEmpty()) { + return false; + } + + if (!MappingsNamespace.INTERMEDIARY.toString().equals(mappings.getSrcNamespace())) { + throw new IllegalStateException("Mapping tree must have intermediary src mappings not " + mappings.getSrcNamespace()); + } + + for (Map.Entry> entry : injectedInterfaces.entrySet()) { + final String className = entry.getKey(); + final List injectedInterfaces = entry.getValue(); + + MappingTree.ClassMapping classMapping = mappings.getClass(className); + + if (classMapping == null) { + final String modIds = injectedInterfaces.stream().map(InjectedInterface::modId).distinct().collect(Collectors.joining(",")); + project.getLogger().warn("Failed to find class ({}) to add injected interfaces from mod(s) ({})", className, modIds); + continue; + } + + classMapping.setComment(appendComment(classMapping.getComment(), injectedInterfaces)); + } + + return true; + } + + private static String appendComment(String comment, List injectedInterfaces) { + for (InjectedInterface injectedInterface : injectedInterfaces) { + String iiComment = "Interface {@link %s} injected by mod %s".formatted(injectedInterface.ifaceName.substring(injectedInterface.ifaceName.lastIndexOf("/") + 1), injectedInterface.modId); + + if (comment == null || !comment.contains(iiComment)) { + if (comment == null) { + comment = iiComment; + } else { + comment += "\n" + iiComment; + } + } + } + + return comment; + } + private record InjectedInterface(String modId, String className, String ifaceName) { /** * Reads the injected interfaces contained in a mod jar, or returns null if there is none. */ public static List fromModJar(Path modJarPath) { - byte[] modJsonBytes; + final byte[] modJsonBytes; try { modJsonBytes = ZipUtils.unpackNullable(modJarPath, "fabric.mod.json"); @@ -193,26 +285,30 @@ public class InterfaceInjectionProcessor implements JarProcessor { return Collections.emptyList(); } - JsonObject jsonObject = new Gson().fromJson(new String(modJsonBytes, StandardCharsets.UTF_8), JsonObject.class); + final JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(new String(modJsonBytes, StandardCharsets.UTF_8), JsonObject.class); - String modId = jsonObject.get("id").getAsString(); + return fromJson(jsonObject); + } + + public static List fromJson(JsonObject jsonObject) { + final String modId = jsonObject.get("id").getAsString(); if (!jsonObject.has("custom")) { return Collections.emptyList(); } - JsonObject custom = jsonObject.getAsJsonObject("custom"); + final JsonObject custom = jsonObject.getAsJsonObject("custom"); if (!custom.has("loom:injected_interfaces")) { return Collections.emptyList(); } - JsonObject addedIfaces = custom.getAsJsonObject("loom:injected_interfaces"); + final JsonObject addedIfaces = custom.getAsJsonObject("loom:injected_interfaces"); - List result = new ArrayList<>(); + final List result = new ArrayList<>(); for (String className : addedIfaces.keySet()) { - JsonArray ifaceNames = addedIfaces.getAsJsonArray(className); + final JsonArray ifaceNames = addedIfaces.getAsJsonArray(className); for (JsonElement ifaceName : ifaceNames) { result.add(new InjectedInterface(modId, className, ifaceName.getAsString())); diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index 09e4a63..02b23e2 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 FabricMC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,10 +30,13 @@ import org.gradle.api.Project; import org.gradle.api.artifacts.Dependency; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; import org.gradle.api.publish.maven.MavenPublication; +import org.gradle.api.tasks.SourceSet; +import net.fabricmc.loom.api.InterfaceInjectionExtensionAPI; import net.fabricmc.loom.api.LoomGradleExtensionAPI; import net.fabricmc.loom.api.MixinExtensionAPI; import net.fabricmc.loom.api.decompilers.DecompilerOptions; @@ -62,9 +65,9 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA protected final Property setupRemappedVariants; protected final Property transitiveAccessWideners; protected final Property intermediary; - protected final Property enableInterfaceInjection; private final Property runtimeOnlyLog4j; private final Property minecraftJarConfiguration; + private final InterfaceInjectionExtensionAPI interfaceInjectionExtension; private final ModVersionParser versionParser; @@ -88,9 +91,6 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA this.transitiveAccessWideners.finalizeValueOnRead(); this.intermediary = project.getObjects().property(String.class) .convention("https://maven.fabricmc.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar"); - this.enableInterfaceInjection = project.getObjects().property(Boolean.class) - .convention(true); - this.enableInterfaceInjection.finalizeValueOnRead(); this.versionParser = new ModVersionParser(project); @@ -108,6 +108,18 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA this.runtimeOnlyLog4j = project.getObjects().property(Boolean.class).convention(false); this.runtimeOnlyLog4j.finalizeValueOnRead(); + + this.interfaceInjectionExtension = project.getObjects().newInstance(InterfaceInjectionExtensionAPI.class); + + // Add main source set by default + interfaceInjection(interfaceInjection -> { + final JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class); + final SourceSet main = javaPluginExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME); + interfaceInjection.getInterfaceInjectionSourceSets().add(main); + + interfaceInjection.getInterfaceInjectionSourceSets().finalizeValueOnRead(); + interfaceInjection.getEnableDependencyInterfaceInjection().convention(true).finalizeValueOnRead(); + }); } @Override @@ -202,11 +214,6 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA return intermediary; } - @Override - public Property getEnableInterfaceInjection() { - return enableInterfaceInjection; - } - @Override public void disableDeprecatedPomGeneration(MavenPublication publication) { net.fabricmc.loom.configuration.MavenPublication.excludePublication(publication); @@ -222,6 +229,11 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA return runtimeOnlyLog4j; } + @Override + public InterfaceInjectionExtensionAPI getInterfaceInjection() { + return interfaceInjectionExtension; + } + // This is here to ensure that LoomGradleExtensionApiImpl compiles without any unimplemented methods private final class EnsureCompile extends LoomGradleExtensionApiImpl { private EnsureCompile() { diff --git a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java index d29fe3b..cb03b25 100644 --- a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java +++ b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2016-2021 FabricMC + * Copyright (c) 2016-2022 FabricMC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,12 +26,16 @@ package net.fabricmc.loom.task; import java.io.File; import java.io.IOException; +import java.io.Reader; import java.io.UncheckedIOException; +import java.io.Writer; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Locale; @@ -57,8 +61,9 @@ import org.jetbrains.annotations.Nullable; import net.fabricmc.loom.api.decompilers.DecompilationMetadata; import net.fabricmc.loom.api.decompilers.DecompilerOptions; import net.fabricmc.loom.api.decompilers.LoomDecompiler; -import net.fabricmc.loom.configuration.accesswidener.AccessWidenerFile; +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.accesswidener.TransitiveAccessWidenerMappingsProcessor; +import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor; import net.fabricmc.loom.decompilers.LineNumberRemapper; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.FileSystemUtil; @@ -69,6 +74,10 @@ import net.fabricmc.loom.util.gradle.ThreadedSimpleProgressLogger; import net.fabricmc.loom.util.gradle.WorkerDaemonClientsManagerHelper; import net.fabricmc.loom.util.ipc.IPCClient; import net.fabricmc.loom.util.ipc.IPCServer; +import net.fabricmc.mappingio.MappingReader; +import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; +import net.fabricmc.mappingio.format.Tiny2Writer; +import net.fabricmc.mappingio.tree.MemoryMappingTree; public abstract class GenerateSourcesTask extends AbstractLoomTask { private final DecompilerOptions decompilerOptions; @@ -300,29 +309,62 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { } private Path getMappings() { - Path baseMappings = getExtension().getMappingsProvider().tinyMappings; + Path inputMappings = getExtension().getMappingsProvider().tinyMappings; - if (getExtension().getEnableTransitiveAccessWideners().get()) { - List accessWideners = getExtension().getTransitiveAccessWideners(); + MemoryMappingTree mappingTree = new MemoryMappingTree(); - if (accessWideners.isEmpty()) { - return baseMappings; - } - - Path outputMappings; - - try { - outputMappings = Files.createTempFile("loom-transitive-mappings", ".tiny"); - } catch (IOException e) { - throw new RuntimeException("Failed to create temp file", e); - } - - TransitiveAccessWidenerMappingsProcessor.process(baseMappings, outputMappings, accessWideners, getProject().getLogger()); - - return outputMappings; + try (Reader reader = Files.newBufferedReader(inputMappings, StandardCharsets.UTF_8)) { + MappingReader.read(reader, new MappingSourceNsSwitch(mappingTree, MappingsNamespace.INTERMEDIARY.toString())); + } catch (IOException e) { + throw new RuntimeException("Failed to read mappings", e); } - return baseMappings; + final List mappingsProcessors = new ArrayList<>(); + + if (getExtension().getEnableTransitiveAccessWideners().get()) { + mappingsProcessors.add(new TransitiveAccessWidenerMappingsProcessor(getProject())); + } + + if (getExtension().getInterfaceInjection().isEnabled()) { + mappingsProcessors.add(new InterfaceInjectionProcessor(getProject())); + } + + if (mappingsProcessors.isEmpty()) { + return inputMappings; + } + + boolean transformed = false; + + for (MappingsProcessor mappingsProcessor : mappingsProcessors) { + if (mappingsProcessor.transform(mappingTree)) { + transformed = true; + } + } + + if (!transformed) { + return inputMappings; + } + + final Path outputMappings; + + try { + outputMappings = Files.createTempFile("loom-transitive-mappings", ".tiny"); + } catch (IOException e) { + throw new RuntimeException("Failed to create temp file", e); + } + + try (Writer writer = Files.newBufferedWriter(outputMappings, StandardCharsets.UTF_8)) { + Tiny2Writer tiny2Writer = new Tiny2Writer(writer, false); + mappingTree.accept(new MappingSourceNsSwitch(tiny2Writer, MappingsNamespace.NAMED.toString())); + } catch (IOException e) { + throw new RuntimeException("Failed to write mappings", e); + } + + return outputMappings; + } + + public interface MappingsProcessor { + boolean transform(MemoryMappingTree mappings); } private static Constructor getDecompilerConstructor(String clazz) { diff --git a/src/test/resources/projects/interfaceInjection/dummyDependency/fabric.mod.json b/src/test/resources/projects/interfaceInjection/dummyDependency/fabric.mod.json index 0d61ca3..66d3b71 100644 --- a/src/test/resources/projects/interfaceInjection/dummyDependency/fabric.mod.json +++ b/src/test/resources/projects/interfaceInjection/dummyDependency/fabric.mod.json @@ -4,7 +4,6 @@ "version": "1", "name": "Dummy Mod", "custom": { - "fabric-api:module-lifecycle": "stable", "loom:injected_interfaces": { "net/minecraft/class_2248": ["InjectedInterface"] } diff --git a/src/test/resources/projects/interfaceInjection/src/main/java/ExampleMod.java b/src/test/resources/projects/interfaceInjection/src/main/java/ExampleMod.java index c73a730..d808f6f 100644 --- a/src/test/resources/projects/interfaceInjection/src/main/java/ExampleMod.java +++ b/src/test/resources/projects/interfaceInjection/src/main/java/ExampleMod.java @@ -6,5 +6,6 @@ public class ExampleMod implements ModInitializer { @Override public void onInitialize() { Blocks.AIR.newMethodThatDidNotExist(); + Blocks.AIR.anotherNewMethodThatDidNotExist(); } } diff --git a/src/test/resources/projects/interfaceInjection/src/main/java/OwnInjectedInterface.java b/src/test/resources/projects/interfaceInjection/src/main/java/OwnInjectedInterface.java new file mode 100644 index 0000000..4a96812 --- /dev/null +++ b/src/test/resources/projects/interfaceInjection/src/main/java/OwnInjectedInterface.java @@ -0,0 +1,5 @@ + +public interface OwnInjectedInterface { + default void anotherNewMethodThatDidNotExist() { + } +} diff --git a/src/test/resources/projects/interfaceInjection/src/main/resources/fabric.mod.json b/src/test/resources/projects/interfaceInjection/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..c5d78c1 --- /dev/null +++ b/src/test/resources/projects/interfaceInjection/src/main/resources/fabric.mod.json @@ -0,0 +1,11 @@ +{ + "schemaVersion": 1, + "id": "owndummy", + "version": "1", + "name": "Own Dummy Mod", + "custom": { + "loom:injected_interfaces": { + "net/minecraft/class_2248": ["OwnInjectedInterface"] + } + } +}