mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-30 13:05:27 -05:00
Mod provided javadoc (#627)
This commit is contained in:
@@ -140,6 +140,13 @@ public interface LoomGradleExtensionAPI {
|
||||
*/
|
||||
Property<Boolean> getEnableTransitiveAccessWideners();
|
||||
|
||||
/**
|
||||
* When true loom will apply mod provided javadoc from dependencies.
|
||||
*
|
||||
* @return the property controlling the mod provided javadoc
|
||||
*/
|
||||
Property<Boolean> getEnableModProvidedJavadoc();
|
||||
|
||||
@ApiStatus.Experimental
|
||||
IntermediateMappingsProvider getIntermediateMappingsProvider();
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ import net.fabricmc.loom.build.mixin.ScalaApInvoker;
|
||||
import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor;
|
||||
import net.fabricmc.loom.configuration.accesswidener.TransitiveAccessWidenerJarProcessor;
|
||||
import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor;
|
||||
import net.fabricmc.loom.configuration.mods.ModJavadocProcessor;
|
||||
import net.fabricmc.loom.configuration.processors.JarProcessorManager;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration;
|
||||
@@ -239,6 +240,15 @@ public final class CompileConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
if (extension.getEnableModProvidedJavadoc().get()) {
|
||||
// This doesn't do any processing on the compiled jar, but it does have an effect on the generated sources.
|
||||
final ModJavadocProcessor javadocProcessor = ModJavadocProcessor.create(project);
|
||||
|
||||
if (javadocProcessor != null) {
|
||||
extension.getGameJarProcessors().add(javadocProcessor);
|
||||
}
|
||||
}
|
||||
|
||||
JarProcessorManager processorManager = new JarProcessorManager(extension.getGameJarProcessors().get());
|
||||
extension.setJarProcessorManager(processorManager);
|
||||
processorManager.setupProcessors();
|
||||
|
||||
@@ -61,6 +61,7 @@ import net.fabricmc.loom.configuration.processors.JarProcessor;
|
||||
import net.fabricmc.loom.task.GenerateSourcesTask;
|
||||
import net.fabricmc.loom.util.Checksum;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.ModUtils;
|
||||
import net.fabricmc.loom.util.Pair;
|
||||
import net.fabricmc.loom.util.TinyRemapperHelper;
|
||||
import net.fabricmc.loom.util.ZipUtils;
|
||||
@@ -270,23 +271,15 @@ 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 null if there is none.
|
||||
* Reads the injected interfaces contained in a mod jar, or returns empty if there is none.
|
||||
*/
|
||||
public static List<InjectedInterface> fromModJar(Path modJarPath) {
|
||||
final byte[] modJsonBytes;
|
||||
final JsonObject jsonObject = ModUtils.getFabricModJson(modJarPath);
|
||||
|
||||
try {
|
||||
modJsonBytes = ZipUtils.unpackNullable(modJarPath, "fabric.mod.json");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to extract fabric.mod.json from " + modJarPath);
|
||||
}
|
||||
|
||||
if (modJsonBytes == null) {
|
||||
if (jsonObject == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(new String(modJsonBytes, StandardCharsets.UTF_8), JsonObject.class);
|
||||
|
||||
return fromJson(jsonObject);
|
||||
}
|
||||
|
||||
@@ -299,11 +292,11 @@ public class InterfaceInjectionProcessor implements JarProcessor, GenerateSource
|
||||
|
||||
final JsonObject custom = jsonObject.getAsJsonObject("custom");
|
||||
|
||||
if (!custom.has("loom:injected_interfaces")) {
|
||||
if (!custom.has(Constants.CustomModJsonKeys.INJECTED_INTERFACE)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final JsonObject addedIfaces = custom.getAsJsonObject("loom:injected_interfaces");
|
||||
final JsonObject addedIfaces = custom.getAsJsonObject(Constants.CustomModJsonKeys.INJECTED_INTERFACE);
|
||||
|
||||
final List<InjectedInterface> result = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2022 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.fabricmc.loom.configuration.mods;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import org.gradle.api.Project;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.configuration.RemappedConfigurationEntry;
|
||||
import net.fabricmc.loom.configuration.processors.JarProcessor;
|
||||
import net.fabricmc.loom.task.GenerateSourcesTask;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.ModUtils;
|
||||
import net.fabricmc.loom.util.ZipUtils;
|
||||
import net.fabricmc.mappingio.MappingReader;
|
||||
import net.fabricmc.mappingio.tree.MappingTree;
|
||||
import net.fabricmc.mappingio.tree.MemoryMappingTree;
|
||||
|
||||
public final class ModJavadocProcessor implements JarProcessor, GenerateSourcesTask.MappingsProcessor {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ModJavadocProcessor.class);
|
||||
|
||||
private final List<ModJavadoc> javadocs;
|
||||
|
||||
private ModJavadocProcessor(List<ModJavadoc> javadocs) {
|
||||
this.javadocs = javadocs;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ModJavadocProcessor create(Project project) {
|
||||
final LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
final List<ModJavadoc> javadocs = new ArrayList<>();
|
||||
|
||||
for (RemappedConfigurationEntry entry : Constants.MOD_COMPILE_ENTRIES) {
|
||||
Set<File> artifacts = extension.getLazyConfigurationProvider(entry.sourceConfiguration())
|
||||
.get()
|
||||
.resolve();
|
||||
|
||||
for (File artifact : artifacts) {
|
||||
if (!ModUtils.isMod(artifact.toPath())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final ModJavadoc modJavadoc;
|
||||
|
||||
try {
|
||||
modJavadoc = ModJavadoc.fromModJar(artifact.toPath());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to read mod jar (%s)".formatted(artifact), e);
|
||||
}
|
||||
|
||||
if (modJavadoc != null) {
|
||||
javadocs.add(modJavadoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (javadocs.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ModJavadocProcessor(javadocs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transform(MemoryMappingTree mappings) {
|
||||
for (ModJavadoc javadoc : javadocs) {
|
||||
javadoc.apply(mappings);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "loom:interface_injection:" + javadocs.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(File file) {
|
||||
// No need to actually process anything, we need to be a JarProcessor to ensure that the jar is cached correctly.
|
||||
}
|
||||
|
||||
public record ModJavadoc(String modId, MemoryMappingTree mappingTree) {
|
||||
@Nullable
|
||||
public static ModJavadoc fromModJar(Path path) throws IOException {
|
||||
JsonObject jsonObject = ModUtils.getFabricModJson(path);
|
||||
|
||||
if (jsonObject == null || !jsonObject.has("custom")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String modId = jsonObject.get("id").getAsString();
|
||||
final JsonObject custom = jsonObject.getAsJsonObject("custom");
|
||||
|
||||
if (!custom.has(Constants.CustomModJsonKeys.PROVIDED_JAVADOC)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String javaDocPath = custom.getAsJsonPrimitive(Constants.CustomModJsonKeys.PROVIDED_JAVADOC).getAsString();
|
||||
final byte[] data = ZipUtils.unpack(path, javaDocPath);
|
||||
final MemoryMappingTree mappings = new MemoryMappingTree();
|
||||
|
||||
try (Reader reader = new InputStreamReader(new ByteArrayInputStream(data))) {
|
||||
MappingReader.read(reader, mappings);
|
||||
}
|
||||
|
||||
if (!mappings.getSrcNamespace().equals(MappingsNamespace.INTERMEDIARY.toString())) {
|
||||
throw new IllegalStateException("Javadoc provided by mod (%s) must be have an intermediary source namespace".formatted(modId));
|
||||
}
|
||||
|
||||
if (!mappings.getDstNamespaces().isEmpty()) {
|
||||
throw new IllegalStateException("Javadoc provided by mod (%s) must not contain any dst names".formatted(modId));
|
||||
}
|
||||
|
||||
return new ModJavadoc(modId, mappings);
|
||||
}
|
||||
|
||||
public void apply(MemoryMappingTree target) {
|
||||
if (!mappingTree.getSrcNamespace().equals(target.getSrcNamespace())) {
|
||||
throw new IllegalStateException("Cannot apply mappings to differing namespaces. source: %s target: %s".formatted(mappingTree.getSrcNamespace(), target.getSrcNamespace()));
|
||||
}
|
||||
|
||||
for (MappingTree.ClassMapping sourceClass : mappingTree.getClasses()) {
|
||||
final MappingTree.ClassMapping targetClass = target.getClass(sourceClass.getSrcName());
|
||||
|
||||
if (targetClass == null) {
|
||||
LOGGER.warn("Could not find provided javadoc target class {} from mod {}", sourceClass.getSrcName(), modId);
|
||||
continue;
|
||||
}
|
||||
|
||||
applyComment(sourceClass, targetClass);
|
||||
|
||||
for (MappingTree.FieldMapping sourceField : sourceClass.getFields()) {
|
||||
final MappingTree.FieldMapping targetField = targetClass.getField(sourceField.getSrcName(), sourceField.getSrcDesc());
|
||||
|
||||
if (targetField == null) {
|
||||
LOGGER.warn("Could not find provided javadoc target field {}{} from mod {}", sourceField.getSrcName(), sourceField.getSrcDesc(), modId);
|
||||
continue;
|
||||
}
|
||||
|
||||
applyComment(sourceField, targetField);
|
||||
}
|
||||
|
||||
for (MappingTree.MethodMapping sourceMethod : sourceClass.getMethods()) {
|
||||
final MappingTree.MethodMapping targetMethod = targetClass.getMethod(sourceMethod.getSrcName(), sourceMethod.getSrcDesc());
|
||||
|
||||
if (targetMethod == null) {
|
||||
LOGGER.warn("Could not find provided javadoc target method {}{} from mod {}", sourceMethod.getSrcName(), sourceMethod.getSrcDesc(), modId);
|
||||
continue;
|
||||
}
|
||||
|
||||
applyComment(sourceMethod, targetMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends MappingTree.ElementMapping> void applyComment(T source, T target) {
|
||||
String sourceComment = source.getComment();
|
||||
|
||||
if (sourceComment == null) {
|
||||
LOGGER.warn("Mod {} provided javadoc has mapping for {}, without comment", modId, source);
|
||||
return;
|
||||
}
|
||||
|
||||
String targetComment = target.getComment();
|
||||
|
||||
if (targetComment == null) {
|
||||
targetComment = "";
|
||||
} else {
|
||||
targetComment += "\n";
|
||||
}
|
||||
|
||||
targetComment += sourceComment;
|
||||
target.setComment(targetComment);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
|
||||
protected final Property<String> customManifest;
|
||||
protected final Property<Boolean> setupRemappedVariants;
|
||||
protected final Property<Boolean> transitiveAccessWideners;
|
||||
protected final Property<Boolean> modProvidedJavadoc;
|
||||
protected final Property<String> intermediary;
|
||||
protected final Property<IntermediateMappingsProvider> intermediateMappingsProvider;
|
||||
private final Property<Boolean> runtimeOnlyLog4j;
|
||||
@@ -98,6 +99,9 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
|
||||
this.transitiveAccessWideners = project.getObjects().property(Boolean.class)
|
||||
.convention(true);
|
||||
this.transitiveAccessWideners.finalizeValueOnRead();
|
||||
this.modProvidedJavadoc = project.getObjects().property(Boolean.class)
|
||||
.convention(true);
|
||||
this.modProvidedJavadoc.finalizeValueOnRead();
|
||||
this.intermediary = project.getObjects().property(String.class)
|
||||
.convention("https://maven.fabricmc.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar");
|
||||
|
||||
@@ -234,6 +238,11 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
|
||||
return transitiveAccessWideners;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property<Boolean> getEnableModProvidedJavadoc() {
|
||||
return modProvidedJavadoc;
|
||||
}
|
||||
|
||||
protected abstract Project getProject();
|
||||
|
||||
protected abstract LoomFiles getFiles();
|
||||
|
||||
@@ -64,6 +64,7 @@ import net.fabricmc.loom.api.decompilers.LoomDecompiler;
|
||||
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
|
||||
import net.fabricmc.loom.configuration.accesswidener.TransitiveAccessWidenerMappingsProcessor;
|
||||
import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor;
|
||||
import net.fabricmc.loom.configuration.mods.ModJavadocProcessor;
|
||||
import net.fabricmc.loom.decompilers.LineNumberRemapper;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.FileSystemUtil;
|
||||
@@ -329,6 +330,12 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
mappingsProcessors.add(new InterfaceInjectionProcessor(getProject()));
|
||||
}
|
||||
|
||||
final ModJavadocProcessor javadocProcessor = ModJavadocProcessor.create(getProject());
|
||||
|
||||
if (javadocProcessor != null) {
|
||||
mappingsProcessors.add(javadocProcessor);
|
||||
}
|
||||
|
||||
if (mappingsProcessors.isEmpty()) {
|
||||
return inputMappings;
|
||||
}
|
||||
|
||||
@@ -142,4 +142,9 @@ public class Constants {
|
||||
private TaskGroup() {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class CustomModJsonKeys {
|
||||
public static final String INJECTED_INTERFACE = "loom:injected_interfaces";
|
||||
public static final String PROVIDED_JAVADOC = "loom:provided_javadoc";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2016-2021 FabricMC
|
||||
* Copyright (c) 2016-2022 FabricMC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,12 +25,42 @@
|
||||
package net.fabricmc.loom.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.LoomGradlePlugin;
|
||||
|
||||
public final class ModUtils {
|
||||
private ModUtils() {
|
||||
}
|
||||
|
||||
public static boolean isMod(File input) {
|
||||
return ZipUtils.contains(input.toPath(), "fabric.mod.json");
|
||||
public static boolean isMod(File file) {
|
||||
return isMod(file.toPath());
|
||||
}
|
||||
|
||||
public static boolean isMod(Path input) {
|
||||
return ZipUtils.contains(input, "fabric.mod.json");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static JsonObject getFabricModJson(Path path) {
|
||||
final byte[] modJsonBytes;
|
||||
|
||||
try {
|
||||
modJsonBytes = ZipUtils.unpackNullable(path, "fabric.mod.json");
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to extract fabric.mod.json from " + path, e);
|
||||
}
|
||||
|
||||
if (modJsonBytes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return LoomGradlePlugin.GSON.fromJson(new String(modJsonBytes, StandardCharsets.UTF_8), JsonObject.class);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user