diff --git a/README.md b/README.md index 713017258..67c6cbf5e 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,60 @@ our official [Documentation](https://docs.neoforged.net/neogradle/docs/). To see the latest available version of NeoGradle, visit the [NeoForged project page](https://projects.neoforged.net/neoforged/neogradle). +## Apply Parchment Mappings + +To get human-readable parameter names in decompiled Minecraft source-code, as well as Javadocs, crowed-sourced data +from the [Parchment project](https://parchmentmc.org) can be applied to the Minecraft source-code before it is recompiled. + +This is currently only supported when applying the NeoGradle userdev Plugin. + +The most basic configuration is using the following properties in gradle.properties: + +``` +neogradle.subsystems.parchment.minecraftVersion=1.20.2 +neogradle.subsystems.parchment.mappingsVersion=2023.12.10 +``` + +The subsystem also has Gradle DSL and supports more parameters explained in the following Gradle snippet. + +```gradle +subsystems { + parchment { + # The Minecraft version for which the Parchment mappings were created. + # This does not necessarily need to match the Minecraft version your mod targets + # Defaults to the value of Gradle property neogradle.subsystems.parchment.minecraftVersion + minecraftVersion = "1.20.2" + + # The version of Parchment mappings to apply. + # See https://parchmentmc.org/docs/getting-started for a list. + # Defaults to the value of Gradle property neogradle.subsystems.parchment.mappingsVersion + mappingsVersion = "2023.12.10" + + # Overrides the full Maven coordinate of the Parchment artifact to use + # This is computed from the minecraftVersion and mappingsVersion properties by default. + # If you set this property explicitly, minecraftVersion and mappingsVersion will be ignored. + # The built-in default value can also be overriden using the Gradle property neogradle.subsystems.parchment.parchmentArtifact + # parchmentArtifact = "org.parchmentmc.data:parchment-$minecraftVersion:$mappingsVersion:checked@zip" + + # Overrides the full Maven coordinate of the tool used to apply the Parchment mappings + # See https://github.com/neoforged/JavaSourceTransformer + # The built-in default value can also be overriden using the Gradle property neogradle.subsystems.parchment.toolArtifact + # toolArtifact = "net.neoforged.jst:jst-cli-bundle:1.0.29" + + # Set this to false if you don't want the https://maven.parchmentmc.org/ repository to be added automatically when + # applying Parchment mappings is enabled + # The built-in default value can also be overriden using the Gradle property neogradle.subsystems.parchment.addRepository + # addRepository = true + + # Can be used to explicitly disable this subsystem. By default, it will be enabled automatically as soon + # as parchmentArtifact or minecraftVersion and mappingsVersion are set. + # The built-in default value can also be overriden using the Gradle property neogradle.subsystems.parchment.enabled + # enabled = true + } +} +``` + + ## Override Decompiler Settings The settings used by the decompiler when preparing Minecraft dependencies can be overridden diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/SubsystemsExtension.java b/common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/SubsystemsExtension.java index 55cdbc378..7d907b276 100644 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/SubsystemsExtension.java +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/SubsystemsExtension.java @@ -1,51 +1,129 @@ package net.neoforged.gradle.common.extensions.subsystems; import net.minecraftforge.gdi.ConfigurableDSLElement; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Decompiler; import net.neoforged.gradle.dsl.common.extensions.subsystems.DecompilerLogLevel; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Parchment; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Recompiler; import net.neoforged.gradle.dsl.common.extensions.subsystems.Subsystems; import org.gradle.api.GradleException; import org.gradle.api.Project; +import org.gradle.api.artifacts.repositories.MavenArtifactRepository; import org.gradle.api.provider.Provider; import javax.inject.Inject; +import java.net.URI; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; +import static net.neoforged.gradle.dsl.common.util.Constants.DEFAULT_PARCHMENT_ARTIFACT_PREFIX; +import static net.neoforged.gradle.dsl.common.util.Constants.DEFAULT_PARCHMENT_GROUP; +import static net.neoforged.gradle.dsl.common.util.Constants.DEFAULT_PARCHMENT_MAVEN_URL; +import static net.neoforged.gradle.dsl.common.util.Constants.DEFAULT_PARCHMENT_TOOL_ARTIFACT; +import static net.neoforged.gradle.dsl.common.util.Constants.DEFAULT_RECOMPILER_MAX_MEMORY; +import static net.neoforged.gradle.dsl.common.util.Constants.SUBSYSTEM_PROPERTY_PREFIX; + public abstract class SubsystemsExtension implements ConfigurableDSLElement, Subsystems { - private static final String PROPERTY_PREFIX = "neogradle.subsystems."; - private static final String DEFAULT_RECOMPILER_MAX_MEMORY = "1g"; + private final Project project; @Inject public SubsystemsExtension(Project project) { this.project = project; - // Decompiler default settings - getDecompiler().getMaxMemory().convention(getStringProperty("decompiler.maxMemory")); - getDecompiler().getMaxThreads().convention(getStringProperty("decompiler.maxThreads").map(Integer::parseUnsignedInt)); - getDecompiler().getLogLevel().convention(getStringProperty("decompiler.logLevel").map(s -> { + configureDecompilerDefaults(); + configureRecompilerDefaults(); + configureParchmentDefaults(); + } + + private void configureDecompilerDefaults() { + Decompiler decompiler = getDecompiler(); + decompiler.getMaxMemory().convention(getStringProperty("decompiler.maxMemory")); + decompiler.getMaxThreads().convention(getStringProperty("decompiler.maxThreads").map(Integer::parseUnsignedInt)); + decompiler.getLogLevel().convention(getStringProperty("decompiler.logLevel").map(s -> { try { return DecompilerLogLevel.valueOf(s.toUpperCase(Locale.ROOT)); } catch (Exception e) { throw new GradleException("Unknown DecompilerLogLevel: " + s + ". Available options: " + Arrays.toString(DecompilerLogLevel.values())); } })); - getDecompiler().getJvmArgs().convention(getSpaceSeparatedListProperty("decompiler.jvmArgs").orElse(Collections.emptyList())); + decompiler.getJvmArgs().convention(getSpaceSeparatedListProperty("decompiler.jvmArgs").orElse(Collections.emptyList())); + } - // Recompiler default settings - getRecompiler().getArgs().convention(getSpaceSeparatedListProperty("recompiler.args").orElse(Collections.emptyList())); - getRecompiler().getJvmArgs().convention(getSpaceSeparatedListProperty("recompiler.jvmArgs").orElse(Collections.emptyList())); - getRecompiler().getMaxMemory().convention(getStringProperty("recompiler.maxMemory").orElse(DEFAULT_RECOMPILER_MAX_MEMORY)); + private void configureRecompilerDefaults() { + Recompiler recompiler = getRecompiler(); + recompiler.getArgs().convention(getSpaceSeparatedListProperty("recompiler.args").orElse(Collections.emptyList())); + recompiler.getJvmArgs().convention(getSpaceSeparatedListProperty("recompiler.jvmArgs").orElse(Collections.emptyList())); + recompiler.getMaxMemory().convention(getStringProperty("recompiler.maxMemory").orElse(DEFAULT_RECOMPILER_MAX_MEMORY)); + } + + private void configureParchmentDefaults() { + Parchment parchment = getParchment(); + parchment.getParchmentArtifact().convention( + getStringProperty("parchment.parchmentArtifact").orElse( + parchment.getMinecraftVersion() + .zip(parchment.getMappingsVersion(), (minecraftVersion, mappingVersion) -> { + return DEFAULT_PARCHMENT_GROUP + + ":" + DEFAULT_PARCHMENT_ARTIFACT_PREFIX + minecraftVersion + + ":" + mappingVersion + // We need the checked variant for now since it resolves + // parameters conflicting with local variables by prefixing everything with "p" + + ":checked" + + "@zip"; + }) + ) + ); + parchment.getMinecraftVersion().convention( + getStringProperty("parchment.minecraftVersion") + ); + parchment.getMappingsVersion().convention( + getStringProperty("parchment.mappingsVersion") + ); + parchment.getToolArtifact().convention( + getStringProperty("parchment.toolArtifact").orElse(DEFAULT_PARCHMENT_TOOL_ARTIFACT) + ); + parchment.getAddRepository().convention( + getBooleanProperty("parchment.addRepository").orElse(true) + ); + parchment.getEnabled().convention(parchment.getParchmentArtifact() + .map(s -> !s.isEmpty()).orElse(getBooleanProperty("parchment.enabled").orElse(false))); + + // Add a filtered parchment repository automatically if enabled + project.afterEvaluate(p -> { + if (!parchment.getEnabled().get() || !parchment.getAddRepository().get()) { + return; + } + MavenArtifactRepository repo = p.getRepositories().maven(m -> { + m.setName("Parchment Data"); + m.setUrl(URI.create(DEFAULT_PARCHMENT_MAVEN_URL)); + m.mavenContent(mavenContent -> mavenContent.includeGroup(DEFAULT_PARCHMENT_GROUP)); + }); + // Make sure it comes first due to its filtered group, that should speed up resolution + p.getRepositories().remove(repo); + p.getRepositories().addFirst(repo); + }); } private Provider getStringProperty(String propertyName) { - return this.project.getProviders().gradleProperty(PROPERTY_PREFIX + propertyName); + return this.project.getProviders().gradleProperty(SUBSYSTEM_PROPERTY_PREFIX + propertyName); + } + + private Provider getBooleanProperty(String propertyName) { + String fullPropertyName = SUBSYSTEM_PROPERTY_PREFIX + propertyName; + return this.project.getProviders().gradleProperty(fullPropertyName) + .map(value -> { + try { + return Boolean.valueOf(value); + } catch (Exception e) { + throw new GradleException("Gradle Property " + fullPropertyName + " is not set to a boolean value: '" + value + "'"); + } + }); } private Provider> getSpaceSeparatedListProperty(String propertyName) { - return this.project.getProviders().gradleProperty(PROPERTY_PREFIX + propertyName) + return this.project.getProviders().gradleProperty(SUBSYSTEM_PROPERTY_PREFIX + propertyName) .map(s -> Arrays.asList(s.split("\\s+"))); } diff --git a/common/src/main/java/net/neoforged/gradle/common/util/ToolUtilities.java b/common/src/main/java/net/neoforged/gradle/common/util/ToolUtilities.java index df6780adb..813991fb8 100644 --- a/common/src/main/java/net/neoforged/gradle/common/util/ToolUtilities.java +++ b/common/src/main/java/net/neoforged/gradle/common/util/ToolUtilities.java @@ -2,19 +2,24 @@ import net.neoforged.gradle.dsl.common.util.ConfigurationUtils; import org.gradle.api.Project; +import org.gradle.api.provider.Provider; import java.io.File; public class ToolUtilities { - + private ToolUtilities() { throw new IllegalStateException("Tried to create utility class!"); } - + public static File resolveTool(final Project project, final String tool) { return ConfigurationUtils.temporaryUnhandledConfiguration( project.getConfigurations(), project.getDependencies().create(tool) ).getFiles().iterator().next(); } + + public static Provider resolveTool(final Project project, final Provider tool) { + return tool.map(toolArtifactId -> resolveTool(project, toolArtifactId)); + } } diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Parchment.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Parchment.groovy new file mode 100644 index 000000000..a0249daaf --- /dev/null +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Parchment.groovy @@ -0,0 +1,66 @@ +package net.neoforged.gradle.dsl.common.extensions.subsystems + +import groovy.transform.CompileStatic +import net.minecraftforge.gdi.ConfigurableDSLElement +import net.minecraftforge.gdi.annotations.DSLProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Optional + +/** + * Allows configuration of Parchment mappings for userdev. + */ +@CompileStatic +interface Parchment extends ConfigurableDSLElement { + + /** + * Artifact coordinates for parchment mappings. + */ + @Input + @Optional + @DSLProperty + Property getParchmentArtifact(); + + /** + * Minecraft version of parchment to use. This property is + * ignored if {@link #getParchmentArtifact()} is set explicitly. + */ + @Input + @Optional + @DSLProperty + Property getMinecraftVersion(); + + /** + * Mapping version of default parchment to use. This property is + * ignored if {@link #getParchmentArtifact()} is set explicitly. + */ + @Input + @Optional + @DSLProperty + Property getMappingsVersion(); + + /** + * Artifact coordinates for the tool used to apply the mappings. Overriding this is an advanced use case. + */ + @Input + @Optional + @DSLProperty + Property getToolArtifact(); + + /** + * If enabled (the default), the parchment repository will automatically be added to the project, + * if {@link #getEnabled()} is true. + */ + @Internal + @DSLProperty + Property getAddRepository(); + + /** + * Enables or disables the system. It is enabled by default if a {@link #getParchmentArtifact()} is specified. + */ + @Input + @DSLProperty + Property getEnabled(); + +} diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Subsystems.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Subsystems.groovy index b3a5c4305..2e2b73a3f 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Subsystems.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Subsystems.groovy @@ -25,4 +25,11 @@ interface Subsystems extends BaseDSLElement { @DSLProperty Recompiler getRecompiler(); + /** + * @return settings for applying Parchment mappings. + */ + @Nested + @DSLProperty + Parchment getParchment(); + } diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy index bee4c536f..7f00c9b49 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy @@ -15,11 +15,17 @@ class Constants { public static final String FART_VERSION = "1.0.2"; public static final String FART_ARTIFACT_INTERPOLATION = "net.minecraftforge:ForgeAutoRenamingTool:%s:all"; public static final String FART = String.format(FART_ARTIFACT_INTERPOLATION, FART_VERSION); - public static final String SRG2SOURCE = "net.minecraftforge:Srg2Source:8.+:fatjar"; - public static final String SIDESTRIPPER = "net.minecraftforge:mergetool:1.1.5:fatjar"; - public static final String INSTALLERTOOLS = "net.minecraftforge:installertools:1.3.2:fatjar"; - public static final String JARCOMPATIBILITYCHECKER = "net.minecraftforge:JarCompatibilityChecker:0.1.+:all"; + public static final String VINEFLOWER_VERSION = "1.9.3"; public static final String VINEFLOWER_ARTIFACT_INTERPOLATION = "org.vineflower:vineflower:%s"; public static final String VINEFLOWER = String.format(VINEFLOWER_ARTIFACT_INTERPOLATION, VINEFLOWER_VERSION); + + public static final String DEFAULT_PARCHMENT_GROUP = "org.parchmentmc.data" + public static final String DEFAULT_PARCHMENT_ARTIFACT_PREFIX = "parchment-" + public static final String DEFAULT_PARCHMENT_MAVEN_URL = "https://maven.parchmentmc.org/" + public static final String DEFAULT_PARCHMENT_TOOL_ARTIFACT = "net.neoforged.jst:jst-cli-bundle:1.0.29" + + public static final String DEFAULT_RECOMPILER_MAX_MEMORY = "1g" + + public static final String SUBSYSTEM_PROPERTY_PREFIX = "neogradle.subsystems." } diff --git a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/extensions/NeoFormRuntimeExtension.java b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/extensions/NeoFormRuntimeExtension.java index c4c00374e..e28617c93 100644 --- a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/extensions/NeoFormRuntimeExtension.java +++ b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/extensions/NeoFormRuntimeExtension.java @@ -14,6 +14,7 @@ import net.neoforged.gradle.dsl.common.extensions.MinecraftArtifactCache; import net.neoforged.gradle.dsl.common.extensions.subsystems.Decompiler; import net.neoforged.gradle.dsl.common.extensions.subsystems.DecompilerLogLevel; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Parchment; import net.neoforged.gradle.dsl.common.extensions.subsystems.Recompiler; import net.neoforged.gradle.dsl.common.extensions.subsystems.Subsystems; import net.neoforged.gradle.dsl.common.runtime.naming.TaskBuildingContext; @@ -47,6 +48,8 @@ import org.gradle.api.artifacts.ResolvedConfiguration; import org.gradle.api.file.FileCollection; import org.gradle.api.file.FileTree; +import org.gradle.api.file.RegularFile; +import org.gradle.api.provider.Provider; import org.gradle.api.tasks.TaskProvider; import org.gradle.api.tasks.compile.ForkOptions; import org.jetbrains.annotations.NotNull; @@ -434,10 +437,18 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) { additionalRuntimeTasks.forEach(taskProvider -> taskProvider.configure(task -> configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, task))); remapTask.configure(task -> configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, task)); + Provider recompileInput = maybeApplyParchment( + spec, + remapTask.flatMap(WithOutput::getOutput), + symbolicDataSources, + neoFormDirectory, + context.getLibrariesTask().flatMap(WithOutput::getOutput) + ); + final FileCollection recompileDependencies = spec.getAdditionalRecompileDependencies().plus(spec.getProject().files(definition.getMinecraftDependenciesConfiguration())); final TaskProvider recompileTask = spec.getProject() .getTasks().register(CommonRuntimeUtils.buildTaskName(spec, "recompile"), RecompileSourceJar.class, task -> { - task.getInputJar().set(remapTask.flatMap(WithOutput::getOutput)); + task.getInputJar().set(recompileInput); task.getCompileClasspath().setFrom(recompileDependencies); task.getStepName().set("recompile"); @@ -454,7 +465,7 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) { taskOutputs.put(recompileTask.getName(), recompileTask); definition.getSourceJarTask().configure(task -> { - task.getInput().set(remapTask.flatMap(WithOutput::getOutput)); + task.getInput().set(recompileInput); task.dependsOn(remapTask); }); definition.getRawJarTask().configure(task -> { @@ -462,4 +473,35 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) { task.dependsOn(recompileTask); }); } + + private static Provider maybeApplyParchment(NeoFormRuntimeSpecification spec, + Provider recompileInput, + Map symbolicDataSources, + File neoFormDirectory, + Provider listLibrariesOutput) { + Project project = spec.getProject(); + Parchment parchment = project.getExtensions().getByType(Subsystems.class).getParchment(); + if (!parchment.getEnabled().get()) { + return recompileInput; + } + + // Provide the mappings via artifact + Provider mappingFile = ToolUtilities.resolveTool(project, parchment.getParchmentArtifact()); + Provider toolExecutable = ToolUtilities.resolveTool(project, parchment.getToolArtifact()); + + TaskProvider applyParchmentTask = project.getTasks().register(CommonRuntimeUtils.buildTaskName(spec, "applyParchment"), Execute.class, task -> { + task.getInputs().file(mappingFile); + task.getExecutingJar().fileProvider(toolExecutable); + task.getProgramArguments().add(listLibrariesOutput.map(f -> "--libraries-list=" + f.getAsFile().getAbsolutePath())); + task.getProgramArguments().add("--enable-parchment"); + task.getProgramArguments().add(mappingFile.map(f -> "--parchment-mappings=" + f.getAbsolutePath())); + task.getProgramArguments().add("--in-format=archive"); + task.getProgramArguments().add("--out-format=archive"); + task.getProgramArguments().add(recompileInput.map(f -> f.getAsFile().getAbsolutePath())); + task.getProgramArguments().add("{output}"); + configureCommonRuntimeTaskParameters(task, symbolicDataSources, "applyParchment", spec, neoFormDirectory); + }); + + return applyParchmentTask.flatMap(WithOutput::getOutput); + } }