mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-04-02 21:47:42 -05:00
Move Iface injection to new MinecraftJarProcessor API
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user