diff --git a/src/main/java/net/fabricmc/loom/api/InterfaceInjectionExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/InterfaceInjectionExtensionAPI.java index 5a3cfbd3..e8a5da7b 100644 --- a/src/main/java/net/fabricmc/loom/api/InterfaceInjectionExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/InterfaceInjectionExtensionAPI.java @@ -38,15 +38,15 @@ public interface InterfaceInjectionExtensionAPI { */ Property getEnableDependencyInterfaceInjection(); + Property getIsEnabled(); + /** - * 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} + * @deprecated now uses the source sets defined in {@link LoomGradleExtensionAPI#getMods()} */ + @Deprecated(forRemoval = true) ListProperty getInterfaceInjectionSourceSets(); default boolean isEnabled() { - return getEnableDependencyInterfaceInjection().get() || !getInterfaceInjectionSourceSets().get().isEmpty(); + return getIsEnabled().get(); } } diff --git a/src/main/java/net/fabricmc/loom/api/processor/ProcessorContext.java b/src/main/java/net/fabricmc/loom/api/processor/ProcessorContext.java index 9444e58b..5f934e65 100644 --- a/src/main/java/net/fabricmc/loom/api/processor/ProcessorContext.java +++ b/src/main/java/net/fabricmc/loom/api/processor/ProcessorContext.java @@ -24,7 +24,9 @@ package net.fabricmc.loom.api.processor; +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration; +import net.fabricmc.tinyremapper.TinyRemapper; public interface ProcessorContext { MinecraftJarConfiguration getJarConfiguration(); @@ -34,4 +36,6 @@ public interface ProcessorContext { boolean includesClient(); boolean includesServer(); + + TinyRemapper createRemapper(MappingsNamespace from, MappingsNamespace to); } diff --git a/src/main/java/net/fabricmc/loom/api/processor/SpecContext.java b/src/main/java/net/fabricmc/loom/api/processor/SpecContext.java index 3370ae71..e0480168 100644 --- a/src/main/java/net/fabricmc/loom/api/processor/SpecContext.java +++ b/src/main/java/net/fabricmc/loom/api/processor/SpecContext.java @@ -34,6 +34,9 @@ public interface SpecContext { List localMods(); + // Returns mods that are both on the compile and runtime classpath + List modDependenciesCompileRuntime(); + default List allMods() { return Stream.concat(modDependencies().stream(), localMods().stream()).toList(); } diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index 49413b90..7cdb5846 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -43,6 +43,7 @@ import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.api.tasks.javadoc.Javadoc; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.api.InterfaceInjectionExtensionAPI; import net.fabricmc.loom.build.mixin.GroovyApInvoker; import net.fabricmc.loom.build.mixin.JavaApInvoker; import net.fabricmc.loom.build.mixin.KaptApInvoker; @@ -202,7 +203,7 @@ public final class CompileConfiguration { final IntermediaryMinecraftProvider intermediaryMinecraftProvider = jarConfiguration.getIntermediaryMinecraftProviderBiFunction().apply(configContext, minecraftProvider); NamedMinecraftProvider namedMinecraftProvider = jarConfiguration.getNamedMinecraftProviderBiFunction().apply(configContext, minecraftProvider); - registerGameProcessors(project); + registerGameProcessors(configContext); MinecraftJarProcessorManager minecraftJarProcessorManager = MinecraftJarProcessorManager.create(project); if (minecraftJarProcessorManager != null) { @@ -217,32 +218,30 @@ public final class CompileConfiguration { namedMinecraftProvider.provide(true); } - private static void registerGameProcessors(Project project) { - final LoomGradleExtension extension = LoomGradleExtension.get(project); + private static void registerGameProcessors(ConfigContext configContext) { + final LoomGradleExtension extension = configContext.extension(); if (extension.getAccessWidenerPath().isPresent()) { - extension.getGameJarProcessors().add(new AccessWidenerJarProcessor(project)); + extension.getGameJarProcessors().add(new AccessWidenerJarProcessor(configContext)); } if (extension.getEnableTransitiveAccessWideners().get()) { - TransitiveAccessWidenerJarProcessor transitiveAccessWidenerJarProcessor = new TransitiveAccessWidenerJarProcessor(project); + TransitiveAccessWidenerJarProcessor transitiveAccessWidenerJarProcessor = new TransitiveAccessWidenerJarProcessor(configContext); if (!transitiveAccessWidenerJarProcessor.isEmpty()) { extension.getGameJarProcessors().add(transitiveAccessWidenerJarProcessor); } } - if (extension.getInterfaceInjection().isEnabled()) { - InterfaceInjectionProcessor jarProcessor = new InterfaceInjectionProcessor(project); - - if (!jarProcessor.isEmpty()) { - extension.getGameJarProcessors().add(jarProcessor); - } - } - if (extension.getEnableModProvidedJavadoc().get()) { extension.addMinecraftJarProcessor(ModJavadocProcessor.class, "fabric-loom:mod-javadoc"); } + + final InterfaceInjectionExtensionAPI interfaceInjection = extension.getInterfaceInjection(); + + if (interfaceInjection.isEnabled()) { + extension.addMinecraftJarProcessor(InterfaceInjectionProcessor.class, "fabric-loom:interface-inject", interfaceInjection.getEnableDependencyInterfaceInjection().get()); + } } private static void setupMixinAp(Project project, MixinExtension mixin) { diff --git a/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerJarProcessor.java b/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerJarProcessor.java index e1b23ea3..daacaf4b 100644 --- a/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerJarProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerJarProcessor.java @@ -37,6 +37,7 @@ import org.gradle.api.Project; import net.fabricmc.accesswidener.AccessWidener; import net.fabricmc.accesswidener.AccessWidenerReader; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.configuration.ConfigContext; import net.fabricmc.loom.configuration.processors.JarProcessor; import net.fabricmc.loom.util.Checksum; import net.fabricmc.loom.util.ZipUtils; @@ -44,15 +45,13 @@ import net.fabricmc.loom.util.ZipUtils; public class AccessWidenerJarProcessor implements JarProcessor { // Filename used to store hash of input access widener in processed jar file private static final String HASH_FILENAME = "aw.sha256"; - // The mod's own access widener file - private byte[] modAccessWidener; private final AccessWidener accessWidener = new AccessWidener(); private final Project project; // This is a SHA256 hash across the mod's and all transitive AWs private byte[] inputHash; - public AccessWidenerJarProcessor(Project project) { - this.project = project; + public AccessWidenerJarProcessor(ConfigContext configContext) { + this.project = configContext.project(); } @Override @@ -66,6 +65,9 @@ public class AccessWidenerJarProcessor implements JarProcessor { Path awPath = extension.getAccessWidenerPath().get().getAsFile().toPath(); // Read our own mod's access widener, used later for producing a version remapped to intermediary + // The mod's own access widener file + byte[] modAccessWidener; + try { modAccessWidener = Files.readAllBytes(awPath); } catch (NoSuchFileException e) { diff --git a/src/main/java/net/fabricmc/loom/configuration/accesswidener/TransitiveAccessWidenerJarProcessor.java b/src/main/java/net/fabricmc/loom/configuration/accesswidener/TransitiveAccessWidenerJarProcessor.java index 3da420b5..21b1fd74 100644 --- a/src/main/java/net/fabricmc/loom/configuration/accesswidener/TransitiveAccessWidenerJarProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/accesswidener/TransitiveAccessWidenerJarProcessor.java @@ -48,6 +48,7 @@ import net.fabricmc.accesswidener.TransitiveOnlyFilter; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.RemapConfigurationSettings; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.configuration.ConfigContext; import net.fabricmc.loom.configuration.processors.JarProcessor; import net.fabricmc.loom.util.TinyRemapperHelper; import net.fabricmc.tinyremapper.TinyRemapper; @@ -56,14 +57,16 @@ import net.fabricmc.tinyremapper.TinyRemapper; * Applies transitive access wideners that are inherited from mod and api dependencies. */ public class TransitiveAccessWidenerJarProcessor implements JarProcessor { + private final ConfigContext configContext; private final Project project; private final LoomGradleExtension extension; private final List transitiveAccessWideners; - public TransitiveAccessWidenerJarProcessor(Project project) { - this.project = project; - this.extension = LoomGradleExtension.get(project); + public TransitiveAccessWidenerJarProcessor(ConfigContext configContext) { + this.configContext = configContext; + this.project = configContext.project(); + this.extension = configContext.extension(); transitiveAccessWideners = getTransitiveAccessWideners(); @@ -166,12 +169,7 @@ public class TransitiveAccessWidenerJarProcessor implements JarProcessor { private TinyRemapper createTinyRemapper() { try { - // TODO service manager, via new processor api - if (true) { - throw new UnsupportedOperationException("ToDO"); - } - - TinyRemapper tinyRemapper = TinyRemapperHelper.getTinyRemapper(project, null, "intermediary", "named"); + TinyRemapper tinyRemapper = TinyRemapperHelper.getTinyRemapper(project, configContext.serviceManager(), "intermediary", "named"); tinyRemapper.readClassPath(TinyRemapperHelper.getMinecraftDependencies(project)); 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 e23ebd12..585e2c1f 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java @@ -24,217 +24,157 @@ 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.Path; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Function; import java.util.stream.Collectors; -import java.util.stream.Stream; -import com.google.common.base.Preconditions; -import com.google.common.hash.Hasher; -import com.google.common.hash.Hashing; +import javax.inject.Inject; + 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.jetbrains.annotations.Nullable; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.commons.Remapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.api.InterfaceInjectionExtensionAPI; -import net.fabricmc.loom.api.RemapConfigurationSettings; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; -import net.fabricmc.loom.configuration.processors.JarProcessor; -import net.fabricmc.loom.task.GenerateSourcesTask; -import net.fabricmc.loom.util.Checksum; +import net.fabricmc.loom.api.processor.MinecraftJarProcessor; +import net.fabricmc.loom.api.processor.ProcessorContext; +import net.fabricmc.loom.api.processor.SpecContext; 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.loom.util.fmj.FabricModJson; -import net.fabricmc.loom.util.fmj.FabricModJsonFactory; import net.fabricmc.mappingio.tree.MappingTree; -import net.fabricmc.mappingio.tree.MemoryMappingTree; import net.fabricmc.tinyremapper.TinyRemapper; -public class InterfaceInjectionProcessor implements JarProcessor, GenerateSourcesTask.MappingsProcessor { - private final Map> injectedInterfaces; - private final Project project; - private final LoomGradleExtension extension; - private final InterfaceInjectionExtensionAPI interfaceInjectionExtension; - private final byte[] inputHash; - private Map> remappedInjectedInterfaces; +public abstract class InterfaceInjectionProcessor implements MinecraftJarProcessor { + private static final Logger LOGGER = LoggerFactory.getLogger(InterfaceInjectionProcessor.class); - 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)); + private final String name; + private final boolean fromDependencies; - this.inputHash = hashInjectedInterfaces(); - } - - public boolean isEmpty() { - return injectedInterfaces.isEmpty(); + @Inject + public InterfaceInjectionProcessor(String name, boolean fromDependencies) { + this.name = name; + this.fromDependencies = fromDependencies; } @Override - public String getId() { - Preconditions.checkArgument(!isEmpty()); - return "loom:interface_injection:" + Checksum.toHex(inputHash); + public String getName() { + return name; } @Override - public void setup() { + public @Nullable InterfaceInjectionProcessor.Spec buildSpec(SpecContext context) { + List injectedInterfaces = new ArrayList<>(); + + injectedInterfaces.addAll(InjectedInterface.fromMods(context.localMods())); + // Find the injected interfaces from mods that are both on the compile and runtime classpath. + // Runtime is also required to ensure that the interface and it's impl is present when running the mc jar. + + if (fromDependencies) { + injectedInterfaces.addAll(InjectedInterface.fromMods(context.modDependenciesCompileRuntime())); + } + + if (injectedInterfaces.isEmpty()) { + return null; + } + + return new Spec(injectedInterfaces); + } + + public record Spec(List injectedInterfaces) implements MinecraftJarProcessor.Spec { } @Override - public void process(File jarFile) { - // Lazily remap from intermediary->named - if (remappedInjectedInterfaces == null) { - TinyRemapper tinyRemapper = createTinyRemapper(); - Remapper remapper = tinyRemapper.getEnvironment().getRemapper(); + public void processJar(Path jar, Spec spec, ProcessorContext context) throws IOException { + // Remap from intermediary->named + final TinyRemapper tinyRemapper = context.createRemapper(MappingsNamespace.INTERMEDIARY, MappingsNamespace.NAMED); + final Remapper remapper = tinyRemapper.getEnvironment().getRemapper(); + final List remappedInjectedInterfaces; - try { - remappedInjectedInterfaces = new HashMap<>(injectedInterfaces.size()); - - for (Map.Entry> entry : injectedInterfaces.entrySet()) { - String namedClassName = remapper.map(entry.getKey()); - remappedInjectedInterfaces.put( - namedClassName, - entry.getValue().stream() - .map(injectedInterface -> - new InjectedInterface( - injectedInterface.modId(), - namedClassName, - remapper.map(injectedInterface.ifaceName()) - )) - .toList() - ); - } - } finally { - tinyRemapper.finish(); - } + try { + remappedInjectedInterfaces = spec.injectedInterfaces().stream() + .map(injectedInterface -> remap(injectedInterface, remapper)) + .toList(); + } finally { + tinyRemapper.finish(); } try { - ZipUtils.transform(jarFile.toPath(), getTransformers()); + ZipUtils.transform(jar, getTransformers(remappedInjectedInterfaces)); } catch (IOException e) { - throw new RuntimeException("Failed to apply interface injections to " + jarFile, e); + throw new RuntimeException("Failed to apply interface injections to " + jar, e); } } - private List>> getTransformers() { - return remappedInjectedInterfaces.keySet().stream() - .map(string -> new Pair<>(string.replaceAll("\\.", "/") + ".class", getTransformer(string))) - .collect(Collectors.toList()); + private InjectedInterface remap(InjectedInterface in, Remapper remapper) { + return new InjectedInterface( + in.modId(), + remapper.map(in.className()), + remapper.map(in.ifaceName()) + ); } - private ZipUtils.UnsafeUnaryOperator getTransformer(String className) { + private List>> getTransformers(List injectedInterfaces) { + return injectedInterfaces.stream() + .collect(Collectors.groupingBy(InjectedInterface::className)) + .entrySet() + .stream() + .map(entry -> { + final String zipEntry = entry.getKey().replaceAll("\\.", "/") + ".class"; + return new Pair<>(zipEntry, getTransformer(entry.getValue())); + }).toList(); + } + + private ZipUtils.UnsafeUnaryOperator getTransformer(List injectedInterfaces) { return input -> { - ClassReader reader = new ClassReader(input); - ClassWriter writer = new ClassWriter(0); - List ifaces = remappedInjectedInterfaces.get(className); - ClassVisitor classVisitor = new InjectingClassVisitor(Constants.ASM_VERSION, writer, ifaces); - - // Log which mods add which interface to the class - project.getLogger().info("Injecting interfaces into " + className + ": " - + ifaces.stream().map(i -> i.ifaceName() + " [" + i.modId() + "]" - ).collect(Collectors.joining(", "))); - + final ClassReader reader = new ClassReader(input); + final ClassWriter writer = new ClassWriter(0); + final ClassVisitor classVisitor = new InjectingClassVisitor(Constants.ASM_VERSION, writer, injectedInterfaces); reader.accept(classVisitor, 0); return writer.toByteArray(); }; } - 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; - } - - // Find the injected interfaces from mods that are both on the compile and runtime classpath. - // Runtime is also required to ensure that the interface and it's impl is present when running the mc jar. - private List getDependencyInjectedInterfaces() { - final Function> resolve = settings -> - settings.getSourceConfiguration().get().resolve().stream() - .map(File::toPath); - - final List runtimeEntries = extension.getRuntimeRemapConfigurations().stream() - .flatMap(resolve) - .toList(); - - return extension.getCompileRemapConfigurations().stream() - .flatMap(resolve) - .filter(runtimeEntries::contains) // Use the intersection of the two configurations. - .flatMap(path -> InjectedInterface.fromModJar(path).stream()) - .toList(); - } - - private List getSourceInjectedInterface(SourceSet sourceSet) { - final FabricModJson fabricModJson; - - try { - fabricModJson = FabricModJsonFactory.createFromSourceSetsNullable(sourceSet); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - - if (fabricModJson == null) { - return Collections.emptyList(); - } - - return InjectedInterface.fromFabricModJson(fabricModJson); - } - @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; + public MappingsProcessor processMappings() { + return (mappings, spec, context) -> { + if (!MappingsNamespace.INTERMEDIARY.toString().equals(mappings.getSrcNamespace())) { + throw new IllegalStateException("Mapping tree must have intermediary src mappings not " + mappings.getSrcNamespace()); } - classMapping.setComment(appendComment(classMapping.getComment(), injectedInterfaces)); - } + Map> map = spec.injectedInterfaces().stream() + .collect(Collectors.groupingBy(InjectedInterface::className)); - return true; + for (Map.Entry> entry : map.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(",")); + LOGGER.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) { @@ -254,20 +194,7 @@ public class InterfaceInjectionProcessor implements JarProcessor, GenerateSource } private record InjectedInterface(String modId, String className, String ifaceName) { - /** - * Reads the injected interfaces contained in a mod jar, or returns empty if there is none. - */ - public static List fromModJar(Path modJarPath) { - FabricModJson fabricModJson = FabricModJsonFactory.createFromZipNullable(modJarPath); - - if (fabricModJson == null) { - return Collections.emptyList(); - } - - return fromFabricModJson(fabricModJson); - } - - public static List fromFabricModJson(FabricModJson fabricModJson) { + public static List fromMod(FabricModJson fabricModJson) { final String modId = fabricModJson.getId(); final JsonElement jsonElement = fabricModJson.getCustom(Constants.CustomModJsonKeys.INJECTED_INTERFACE); @@ -289,6 +216,13 @@ public class InterfaceInjectionProcessor implements JarProcessor, GenerateSource return result; } + + public static List fromMods(List fabricModJsons) { + return fabricModJsons.stream() + .map(InjectedInterface::fromMod) + .flatMap(List::stream) + .toList(); + } } private static class InjectingClassVisitor extends ClassVisitor { @@ -326,41 +260,4 @@ public class InterfaceInjectionProcessor implements JarProcessor, GenerateSource super.visit(version, access, name, signature, superName, modifiedInterfaces.toArray(new String[0])); } } - - private TinyRemapper createTinyRemapper() { - try { - // TODO service manager, via new processor api - if (true) { - throw new UnsupportedOperationException("ToDO"); - } - - TinyRemapper tinyRemapper = TinyRemapperHelper.getTinyRemapper(project, null, "intermediary", "named"); - tinyRemapper.readClassPath(TinyRemapperHelper.getMinecraftDependencies(project)); - - for (Path minecraftJar : extension.getMinecraftJars(MappingsNamespace.INTERMEDIARY)) { - tinyRemapper.readClassPath(minecraftJar); - } - - return tinyRemapper; - } catch (IOException e) { - throw new RuntimeException("Failed to create tiny remapper for intermediary->named", e); - } - } - - private byte[] hashInjectedInterfaces() { - // Hash the interfaces we're about to inject to not have to repeat this everytime - Hasher hasher = Hashing.sha256().newHasher(); - - for (Map.Entry> entry : injectedInterfaces.entrySet()) { - hasher.putString("class:", StandardCharsets.UTF_8); - hasher.putString(entry.getKey(), StandardCharsets.UTF_8); - - for (InjectedInterface ifaceName : entry.getValue()) { - hasher.putString("iface:", StandardCharsets.UTF_8); - hasher.putString(ifaceName.ifaceName(), StandardCharsets.UTF_8); - } - } - - return hasher.hash().asBytes(); - } } diff --git a/src/main/java/net/fabricmc/loom/configuration/processors/ProcessorContextImpl.java b/src/main/java/net/fabricmc/loom/configuration/processors/ProcessorContextImpl.java index 382a54c9..f8a2e116 100644 --- a/src/main/java/net/fabricmc/loom/configuration/processors/ProcessorContextImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/processors/ProcessorContextImpl.java @@ -24,21 +24,22 @@ package net.fabricmc.loom.configuration.processors; -import org.gradle.api.Project; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Path; -import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.api.processor.ProcessorContext; +import net.fabricmc.loom.configuration.ConfigContext; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJar; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration; +import net.fabricmc.loom.util.TinyRemapperHelper; +import net.fabricmc.tinyremapper.TinyRemapper; -public record ProcessorContextImpl(MinecraftJarConfiguration jarConfiguration, MinecraftJar minecraftJar) implements ProcessorContext { - public static ProcessorContext create(Project project, MinecraftJar minecraftJar) { - return new ProcessorContextImpl(LoomGradleExtension.get(project).getMinecraftJarConfiguration().get(), minecraftJar); - } - +public record ProcessorContextImpl(ConfigContext configContext, MinecraftJar minecraftJar) implements ProcessorContext { @Override public MinecraftJarConfiguration getJarConfiguration() { - return jarConfiguration; + return configContext.extension().getMinecraftJarConfiguration().get(); } @Override @@ -55,4 +56,20 @@ public record ProcessorContextImpl(MinecraftJarConfiguration jarConfiguration, M public boolean includesServer() { return minecraftJar.includesServer(); } + + @Override + public TinyRemapper createRemapper(MappingsNamespace from, MappingsNamespace to) { + try { + TinyRemapper tinyRemapper = TinyRemapperHelper.getTinyRemapper(configContext().project(), configContext().serviceManager(), from.toString(), to.toString()); + tinyRemapper.readClassPath(TinyRemapperHelper.getMinecraftDependencies(configContext.project())); + + for (Path minecraftJar : configContext.extension().getMinecraftJars(MappingsNamespace.INTERMEDIARY)) { + tinyRemapper.readClassPath(minecraftJar); + } + + return tinyRemapper; + } catch (IOException e) { + throw new UncheckedIOException("Failed to create tiny remapper", e); + } + } } diff --git a/src/main/java/net/fabricmc/loom/configuration/processors/SpecContextImpl.java b/src/main/java/net/fabricmc/loom/configuration/processors/SpecContextImpl.java index 50567c97..996fd8d2 100644 --- a/src/main/java/net/fabricmc/loom/configuration/processors/SpecContextImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/processors/SpecContextImpl.java @@ -27,11 +27,15 @@ package net.fabricmc.loom.configuration.processors; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Optional; import java.util.Set; +import java.util.function.Function; +import java.util.stream.Stream; import org.gradle.api.Project; import org.gradle.api.tasks.SourceSet; @@ -47,9 +51,9 @@ import net.fabricmc.loom.util.gradle.SourceSetHelper; * @param modDependencies External mods that are depended on * @param localMods The main mod being built. In the future this may also include other mods. */ -public record SpecContextImpl(List modDependencies, List localMods) implements SpecContext { +public record SpecContextImpl(List modDependencies, List localMods, List compileRuntimeMods) implements SpecContext { public static SpecContextImpl create(Project project) { - return new SpecContextImpl(getDependentMods(project), getMods(project)); + return new SpecContextImpl(getDependentMods(project), getMods(project), getCompileRuntimeMods(project)); } private static List getDependentMods(Project project) { @@ -95,8 +99,33 @@ public record SpecContextImpl(List modDependencies, List getCompileRuntimeMods(Project project) { + final LoomGradleExtension extension = LoomGradleExtension.get(project); + final Function> resolve = settings -> + settings.getSourceConfiguration().get().resolve().stream() + .map(File::toPath); + + final List runtimeEntries = extension.getRuntimeRemapConfigurations().stream() + .flatMap(resolve) + .toList(); + + return extension.getCompileRemapConfigurations().stream() + .flatMap(resolve) + .filter(runtimeEntries::contains) // Use the intersection of the two configurations. + .map(FabricModJsonFactory::createFromZipOptional) + .filter(Optional::isPresent) + .map(Optional::get) + .sorted(Comparator.comparing(FabricModJson::getId)) + .toList(); + } + // Sort to ensure stable caching private static List sorted(List mods) { return mods.stream().sorted(Comparator.comparing(FabricModJson::getId)).toList(); } + + @Override + public List modDependenciesCompileRuntime() { + return compileRuntimeMods; + } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java index c1ba3920..1b32fed0 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java @@ -220,8 +220,6 @@ public abstract sealed class MinecraftSourceSets permits MinecraftSourceSets.Sin // The client only sources to the combined sources jar. jar.from(clientOnlySourceSet.getAllSource()); }); - - extension.getInterfaceInjection().getInterfaceInjectionSourceSets().add(clientOnlySourceSet); } @Override diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/ProcessedNamedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/ProcessedNamedMinecraftProvider.java index 2a4d8901..18413335 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/ProcessedNamedMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/ProcessedNamedMinecraftProvider.java @@ -92,7 +92,7 @@ public abstract class ProcessedNamedMinecraftProvider createFromZipOptional(Path zipPath) { + return Optional.ofNullable(createFromZipNullable(zipPath)); + } + public static FabricModJson createFromDirectory(Path directory) throws IOException { final Path path = directory.resolve(FABRIC_MOD_JSON);