Move Iface injection to new MinecraftJarProcessor API

This commit is contained in:
modmuss50
2022-10-09 14:08:23 +01:00
parent 47987b2aa3
commit 00a3b7ff4e
14 changed files with 202 additions and 254 deletions

View File

@@ -38,15 +38,15 @@ public interface InterfaceInjectionExtensionAPI {
*/
Property<Boolean> getEnableDependencyInterfaceInjection();
Property<Boolean> 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<SourceSet> getInterfaceInjectionSourceSets();
default boolean isEnabled() {
return getEnableDependencyInterfaceInjection().get() || !getInterfaceInjectionSourceSets().get().isEmpty();
return getIsEnabled().get();
}
}

View File

@@ -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);
}

View File

@@ -34,6 +34,9 @@ public interface SpecContext {
List<FabricModJson> localMods();
// Returns mods that are both on the compile and runtime classpath
List<FabricModJson> modDependenciesCompileRuntime();
default List<FabricModJson> allMods() {
return Stream.concat(modDependencies().stream(), localMods().stream()).toList();
}

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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<AccessWidenerFile> 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));

View File

@@ -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<String, List<InjectedInterface>> injectedInterfaces;
private final Project project;
private final LoomGradleExtension extension;
private final InterfaceInjectionExtensionAPI interfaceInjectionExtension;
private final byte[] inputHash;
private Map<String, List<InjectedInterface>> remappedInjectedInterfaces;
public abstract class InterfaceInjectionProcessor implements MinecraftJarProcessor<InterfaceInjectionProcessor.Spec> {
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<InjectedInterface> 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<InjectedInterface> 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<InjectedInterface> remappedInjectedInterfaces;
try {
remappedInjectedInterfaces = new HashMap<>(injectedInterfaces.size());
for (Map.Entry<String, List<InjectedInterface>> 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<Pair<String, ZipUtils.UnsafeUnaryOperator<byte[]>>> 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<byte[]> getTransformer(String className) {
private List<Pair<String, ZipUtils.UnsafeUnaryOperator<byte[]>>> getTransformers(List<InjectedInterface> 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<byte[]> getTransformer(List<InjectedInterface> injectedInterfaces) {
return input -> {
ClassReader reader = new ClassReader(input);
ClassWriter writer = new ClassWriter(0);
List<InjectedInterface> 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<InjectedInterface> getInjectedInterfaces() {
List<InjectedInterface> 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<InjectedInterface> getDependencyInjectedInterfaces() {
final Function<RemapConfigurationSettings, Stream<Path>> resolve = settings ->
settings.getSourceConfiguration().get().resolve().stream()
.map(File::toPath);
final List<Path> 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<InjectedInterface> 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<String, List<InjectedInterface>> entry : injectedInterfaces.entrySet()) {
final String className = entry.getKey();
final List<InjectedInterface> 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<Spec> 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<String, List<InjectedInterface>> map = spec.injectedInterfaces().stream()
.collect(Collectors.groupingBy(InjectedInterface::className));
return true;
for (Map.Entry<String, List<InjectedInterface>> entry : map.entrySet()) {
final String className = entry.getKey();
final List<InjectedInterface> 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<InjectedInterface> 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<InjectedInterface> fromModJar(Path modJarPath) {
FabricModJson fabricModJson = FabricModJsonFactory.createFromZipNullable(modJarPath);
if (fabricModJson == null) {
return Collections.emptyList();
}
return fromFabricModJson(fabricModJson);
}
public static List<InjectedInterface> fromFabricModJson(FabricModJson fabricModJson) {
public static List<InjectedInterface> 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<InjectedInterface> fromMods(List<FabricModJson> 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<String, List<InjectedInterface>> 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();
}
}

View File

@@ -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);
}
}
}

View File

@@ -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<FabricModJson> modDependencies, List<FabricModJson> localMods) implements SpecContext {
public record SpecContextImpl(List<FabricModJson> modDependencies, List<FabricModJson> localMods, List<FabricModJson> 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<FabricModJson> getDependentMods(Project project) {
@@ -95,8 +99,33 @@ public record SpecContextImpl(List<FabricModJson> modDependencies, List<FabricMo
return Collections.emptyList();
}
private static List<FabricModJson> getCompileRuntimeMods(Project project) {
final LoomGradleExtension extension = LoomGradleExtension.get(project);
final Function<RemapConfigurationSettings, Stream<Path>> resolve = settings ->
settings.getSourceConfiguration().get().resolve().stream()
.map(File::toPath);
final List<Path> 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<FabricModJson> sorted(List<FabricModJson> mods) {
return mods.stream().sorted(Comparator.comparing(FabricModJson::getId)).toList();
}
@Override
public List<FabricModJson> modDependenciesCompileRuntime() {
return compileRuntimeMods;
}
}

View File

@@ -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

View File

@@ -92,7 +92,7 @@ public abstract class ProcessedNamedMinecraftProvider<M extends MinecraftProvide
Files.copy(inputJar, outputJar, StandardCopyOption.REPLACE_EXISTING);
jarProcessorManager.processJar(outputJar, ProcessorContextImpl.create(getProject(), minecraftJar));
jarProcessorManager.processJar(outputJar, new ProcessorContextImpl(configContext, minecraftJar));
}
}

View File

@@ -136,6 +136,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
this.splitModDependencies.finalizeValueOnRead();
this.interfaceInjectionExtension = project.getObjects().newInstance(InterfaceInjectionExtensionAPI.class);
this.interfaceInjectionExtension.getIsEnabled().convention(true);
this.splitEnvironmentalSourceSet = project.getObjects().property(Boolean.class).convention(false);
this.splitEnvironmentalSourceSet.finalizeValueOnRead();

View File

@@ -66,7 +66,6 @@ import net.fabricmc.loom.api.decompilers.LoomDecompiler;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
import net.fabricmc.loom.api.processor.MappingProcessorContext;
import net.fabricmc.loom.configuration.accesswidener.TransitiveAccessWidenerMappingsProcessor;
import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor;
import net.fabricmc.loom.configuration.processors.MinecraftJarProcessorManager;
import net.fabricmc.loom.decompilers.LineNumberRemapper;
import net.fabricmc.loom.util.Constants;
@@ -335,10 +334,6 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
mappingsProcessors.add(new TransitiveAccessWidenerMappingsProcessor(getProject()));
}
if (getExtension().getInterfaceInjection().isEnabled()) {
mappingsProcessors.add(new InterfaceInjectionProcessor(getProject()));
}
MinecraftJarProcessorManager minecraftJarProcessorManager = MinecraftJarProcessorManager.create(getProject());
if (minecraftJarProcessorManager != null) {

View File

@@ -33,6 +33,7 @@ import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import com.google.gson.JsonObject;
import org.gradle.api.tasks.SourceSet;
@@ -91,6 +92,10 @@ public final class FabricModJsonFactory {
return create(jsonObject, new FabricModJsonSource.ZipSource(zipPath));
}
public static Optional<FabricModJson> createFromZipOptional(Path zipPath) {
return Optional.ofNullable(createFromZipNullable(zipPath));
}
public static FabricModJson createFromDirectory(Path directory) throws IOException {
final Path path = directory.resolve(FABRIC_MOD_JSON);