From 24a7d83c19f1a5c6d0ca18475a5e432ce6647adf Mon Sep 17 00:00:00 2001 From: LexManos Date: Tue, 10 Feb 2026 17:13:42 -0800 Subject: [PATCH 1/8] Add support for mavenizer output json, and expose mappings helper methods --- settings.gradle | 2 +- .../gradle/MavenizerInstance.java | 15 ++++ .../gradle/MinecraftExtensionForProject.java | 25 +++++- .../gradle/internal/Constants.java | 12 --- .../gradle/internal/ForgeGradleProblems.java | 11 +++ .../internal/MavenizerInstanceImpl.java | 87 +++++++++++++++++++ .../internal/MinecraftExtensionImpl.java | 31 ++++--- .../minecraftforge/gradle/internal/Tools.java | 4 +- 8 files changed, 158 insertions(+), 29 deletions(-) create mode 100644 src/main/java/net/minecraftforge/gradle/MavenizerInstance.java create mode 100644 src/main/java/net/minecraftforge/gradle/internal/MavenizerInstanceImpl.java diff --git a/settings.gradle b/settings.gradle index c93bf5a7d..ff5c799c8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -24,7 +24,7 @@ gradle.beforeProject { Project project -> //@formatter:off dependencyResolutionManagement.versionCatalogs.register('libs') { - version 'gradleutils', '3.3.42' + version 'gradleutils', '3.4.3' plugin 'licenser', 'net.minecraftforge.licenser' version '1.2.0' // https://plugins.gradle.org/plugin/net.minecraftforge.licenser plugin 'gradleutils', 'net.minecraftforge.gradleutils' versionRef 'gradleutils' diff --git a/src/main/java/net/minecraftforge/gradle/MavenizerInstance.java b/src/main/java/net/minecraftforge/gradle/MavenizerInstance.java new file mode 100644 index 000000000..9af558870 --- /dev/null +++ b/src/main/java/net/minecraftforge/gradle/MavenizerInstance.java @@ -0,0 +1,15 @@ +package net.minecraftforge.gradle; + +import org.gradle.api.artifacts.ExternalModuleDependency; +import org.gradle.api.provider.Provider; + +import java.io.File; + +public interface MavenizerInstance { + Provider getDependency(); + Provider getMappingVersion(); + Provider getToSrg(); + Provider getToSrgFile(); + Provider getToObf(); + Provider getToObfFile(); +} diff --git a/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java b/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java index 116258821..ea2df9980 100644 --- a/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java +++ b/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java @@ -28,7 +28,17 @@ public interface MinecraftExtensionForProject extends MinecraftExtension, Minecr /// @return The dependency /// @see Declaring Dependencies /// in Gradle + default Provider dependency( + Object value, + @DelegatesTo(ExternalModuleDependency.class) + @ClosureParams(value = SimpleType.class, options = "net.minecraftforge.gradle.ClosureOwner.MinecraftDependency") + Closure closure + ) { + return this.dependency("default", value, closure); + } + Provider dependency( + String name, Object value, @DelegatesTo(ExternalModuleDependency.class) @ClosureParams(value = SimpleType.class, options = "net.minecraftforge.gradle.ClosureOwner.MinecraftDependency") @@ -44,7 +54,10 @@ Provider dependency( /// @see Declaring Dependencies /// in Gradle default Provider dependency(Object value, Action action) { - return this.dependency(value, Closures.action(this, action)); + return this.dependency("default", value, action); + } + default Provider dependency(String name, Object value, Action action) { + return this.dependency(name, value, Closures.action(this, action)); } /// Creates (or marks if existing) the given dependency as a Minecraft dependency. @@ -54,6 +67,14 @@ default Provider dependency(Object value, ActionDeclaring Dependencies /// in Gradle default Provider dependency(Object value) { - return this.dependency(value, Closures.empty(this)); + return this.dependency("default", value); + } + default Provider dependency(String name, Object value) { + return this.dependency(name, value, Closures.empty(this)); + } + + default MavenizerInstance instance() { + return this.instance("default"); } + MavenizerInstance instance(String name); } diff --git a/src/main/java/net/minecraftforge/gradle/internal/Constants.java b/src/main/java/net/minecraftforge/gradle/internal/Constants.java index 93da65625..04adce51a 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/Constants.java +++ b/src/main/java/net/minecraftforge/gradle/internal/Constants.java @@ -11,18 +11,6 @@ final class Constants { static final String FORGE_MAVEN = "https://maven.minecraftforge.net/"; static final String MC_LIBS_MAVEN = "https://libraries.minecraft.net/"; - static final String SLIMELAUNCHER_NAME = "slimelauncher"; - static final String SLIMELAUNCHER_VERSION = "0.1.6"; - static final String SLIMELAUNCHER_DL_URL = "https://maven.minecraftforge.net/net/minecraftforge/slime-launcher/" + SLIMELAUNCHER_VERSION + "/slime-launcher-" + SLIMELAUNCHER_VERSION + ".jar"; - static final int SLIMELAUNCHER_JAVA_VERSION = 8; - static final String SLIMELAUNCHER_MAIN = "net.minecraftforge.launcher.Main"; - - static final String MAVENIZER_NAME = "mavenizer"; - static final String MAVENIZER_VERSION = "0.4.24"; - static final String MAVENIZER_DL_URL = "https://maven.minecraftforge.net/net/minecraftforge/minecraft-mavenizer/" + MAVENIZER_VERSION + "/minecraft-mavenizer-" + MAVENIZER_VERSION + ".jar"; - static final int MAVENIZER_JAVA_VERSION = 25; - static final String MAVENIZER_MAIN = "net.minecraftforge.mcmaven.cli.Main"; - /// Use these with [java.text.MessageFormat#format(String, Object...)]. static final class Messages { static final String WELCOME = """ diff --git a/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java b/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java index 4c784bae4..7b63f8a2a 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java +++ b/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java @@ -213,6 +213,17 @@ void reportForgeMavenNotDeclared() { .solution(HELP_MESSAGE) ); } + + void reportDuplicateMavenizerNames() { + this.report("forge-maven-duplicate-name", "Duplicate minecraft dependencies registered", spec -> spec + .details(""" + In order to manage access to mapping data each deobfuscated dependency needs to have a unique name. + Call the `minecraft.dependency` method with a unique name as the first parameter, the default when not specified is `default`""") + .severity(Severity.WARNING) + .solution("Call `minecraft.dependency` method with a unique name as the first parameter`") + .solution(HELP_MESSAGE) + ); + } //endregion //endregion diff --git a/src/main/java/net/minecraftforge/gradle/internal/MavenizerInstanceImpl.java b/src/main/java/net/minecraftforge/gradle/internal/MavenizerInstanceImpl.java new file mode 100644 index 000000000..a66119dc6 --- /dev/null +++ b/src/main/java/net/minecraftforge/gradle/internal/MavenizerInstanceImpl.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.gradle.internal; + +import groovy.json.JsonSlurper; +import net.minecraftforge.gradle.MavenizerInstance; +import org.gradle.api.Transformer; +import org.gradle.api.artifacts.ExternalModuleDependency; +import org.gradle.api.provider.Provider; +import org.jspecify.annotations.Nullable; + +import java.io.File; +import java.util.Map; + +class MavenizerInstanceImpl implements MavenizerInstance { + private final MinecraftExtensionImpl.ForProjectImpl extension; + private final Provider valueSource; + private final ExternalModuleDependency dependency; + private final File jsonFile; + + private final Provider> invoke; + private @Nullable Map map; + + MavenizerInstanceImpl( + MinecraftExtensionImpl.ForProjectImpl extension, + Provider valueSource, + ExternalModuleDependency dependency, + File jsonFile + ) { + this.extension = extension; + this.dependency = dependency; + this.valueSource = valueSource; + this.jsonFile = jsonFile; + this.invoke = this.extension.getProviders().provider(this::invoke); + } + + @SuppressWarnings("unchecked") + private Map invoke() { + if (this.map == null) { + valueSource.get(); // Execute Mavenizer, probably called before, but just be sure. + this.map = (Map) new JsonSlurper().parse(this.jsonFile, "UTF-8"); + this.map.forEach((k, v) -> this.extension.getProject().getLogger().lifecycle(k + " => " + v)); + } + return this.map; + } + + private Transformer> get(String key) { + return map -> { + var ret = map.get(key); + if (ret == null) + throw new IllegalStateException("Mavenizer did not output expected json data " + key); + return ret; + }; + } + + @Override + public Provider getDependency() { + return this.invoke.map(m -> this.dependency); + } + + @Override + public Provider getMappingVersion() { + return this.invoke.map(get("mappings.vesion")); + } + + @Override + public Provider getToSrg() { + return this.invoke.map(get("mappings.srg.artifact")); + } + + @Override + public Provider getToSrgFile() { + return getToSrg().map(this.extension.getProject()::file); + } + + @Override + public Provider getToObf() { + return this.invoke.map(get("mappings.obf.artifact")); + } + + @Override + public Provider getToObfFile() { + return getToSrg().map(this.extension.getProject()::file); + } +} diff --git a/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java b/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java index b76a21ba1..27ea4175e 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java +++ b/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java @@ -9,16 +9,15 @@ import groovy.transform.NamedVariant; import groovy.transform.stc.ClosureParams; import groovy.transform.stc.SimpleType; +import net.minecraftforge.gradle.MavenizerInstance; import net.minecraftforge.gradle.MinecraftExtension; import net.minecraftforge.gradle.MinecraftExtensionForProject; import net.minecraftforge.gradle.MinecraftMappings; import net.minecraftforge.gradle.SlimeLauncherOptions; -import org.codehaus.groovy.runtime.InvokerHelper; import org.gradle.api.Action; import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.Project; import org.gradle.api.Task; -import org.gradle.api.UnknownTaskException; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ExternalModuleDependency; import org.gradle.api.artifacts.ExternalModuleDependencyBundle; @@ -29,7 +28,6 @@ import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.file.ProjectLayout; -import org.gradle.api.file.RegularFileProperty; import org.gradle.api.flow.FlowProviders; import org.gradle.api.flow.FlowScope; import org.gradle.api.initialization.Settings; @@ -44,18 +42,16 @@ import org.gradle.api.reflect.TypeOf; import org.gradle.api.tasks.TaskProvider; import org.gradle.jvm.toolchain.JavaLauncher; -import org.gradle.jvm.toolchain.JavaToolchainService; import org.gradle.plugins.ide.eclipse.model.EclipseModel; -import org.jetbrains.annotations.UnmodifiableView; -import org.jspecify.annotations.Nullable; import javax.inject.Inject; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Objects; -import java.util.function.Predicate; import java.util.function.ToIntFunction; import java.util.stream.Collectors; @@ -177,6 +173,7 @@ static abstract class ForProjectImpl extends MinecraftExtensionImpl implements F // Dependencies private final List minecraftDependencies = new ArrayList<>(); + private final Map mavenizerRegistry = new HashMap<>(); // Access Transformers private final boolean hasAccessTransformersPlugin; @@ -381,6 +378,7 @@ public NamedDomainObjectContainer getRuns() { @SuppressWarnings({"UnstableApiUsage"}) @Override public Provider dependency( + String name, Object value, @DelegatesTo(ExternalModuleDependency.class) @ClosureParams(value = SimpleType.class, options = "net.minecraftforge.gradle.MinecraftDependency.ClosureOwner") @@ -394,6 +392,8 @@ public Provider dependency( var minecraftDependency = this.getObjects().newInstance(MinecraftDependencyImpl.class, this.getMavenizerOutput()); this.minecraftDependencies.add(minecraftDependency); var dep = minecraftDependency.init(value, closure); + var outputJson = this.plugin.localCaches().file("mavenizer/" + name + ".json").get().getAsFile(); + var mavenizer = this.getProviders().of(MavenizerValueSource.class, spec -> { spec.parameters(params -> { var tool = this.plugin.getTool(Tools.MAVENIZER); @@ -413,7 +413,8 @@ public Provider dependency( "--output", this.getMavenizerOutput().get().getAsFile().getAbsolutePath(), "--artifact", dep.getModule().toString(), "--version", Objects.requireNonNull(dep.getVersion()), - "--global-auxiliary-variants" + "--global-auxiliary-variants", + "--output-json", outputJson.getAbsolutePath() )); // If we are finding the access transformer from sourcesets, just find from any source set @@ -447,10 +448,16 @@ public Provider dependency( }); }); - return this.getProviders().provider(() -> { - mavenizer.get();// Invoke mavenizer, it should be invoked already by gradle config cache, but Force it to be - return dep; - }); + var instance = new MavenizerInstanceImpl(this, mavenizer, dep, outputJson); + if (this.mavenizerRegistry.containsKey(name)) + problems.reportDuplicateMavenizerNames(); + this.mavenizerRegistry.put(name, instance); + return instance.getDependency(); + } + + @Override + public MavenizerInstance instance(String name) { + return this.mavenizerRegistry.get(name); } private void checkRepos(List repos) { diff --git a/src/main/java/net/minecraftforge/gradle/internal/Tools.java b/src/main/java/net/minecraftforge/gradle/internal/Tools.java index 465b52bae..e5df35173 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/Tools.java +++ b/src/main/java/net/minecraftforge/gradle/internal/Tools.java @@ -9,7 +9,7 @@ final class Tools { private Tools() { } - static final Tool SLIMELAUNCHER = Tool.of(Constants.SLIMELAUNCHER_NAME, Constants.SLIMELAUNCHER_VERSION, Constants.SLIMELAUNCHER_DL_URL, Constants.SLIMELAUNCHER_JAVA_VERSION, Constants.SLIMELAUNCHER_MAIN); + static final Tool SLIMELAUNCHER = Tool.ofForge("slimelauncher", "net.minecraftforge:slimelauncher:0.1.6", 8, "net.minecraftforge.launcher.Main"); - static final Tool MAVENIZER = Tool.of(Constants.MAVENIZER_NAME, Constants.MAVENIZER_VERSION, Constants.MAVENIZER_DL_URL, Constants.MAVENIZER_JAVA_VERSION, Constants.MAVENIZER_MAIN); + static final Tool MAVENIZER = Tool.ofForge("mavenizer", "net.minecraftforge:minecraft-mavenizer:0.4.25", 25, "net.minecraftforge.mcmaven.cli.Main"); } From d4ea0e31b0b3507a1c07997e976a48714434f103 Mon Sep 17 00:00:00 2001 From: LexManos Date: Tue, 10 Feb 2026 17:55:25 -0800 Subject: [PATCH 2/8] Fix file variants --- .../java/net/minecraftforge/gradle/MavenizerInstance.java | 4 ++++ .../gradle/internal/MavenizerInstanceImpl.java | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/minecraftforge/gradle/MavenizerInstance.java b/src/main/java/net/minecraftforge/gradle/MavenizerInstance.java index 9af558870..a22d5977a 100644 --- a/src/main/java/net/minecraftforge/gradle/MavenizerInstance.java +++ b/src/main/java/net/minecraftforge/gradle/MavenizerInstance.java @@ -1,3 +1,7 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ package net.minecraftforge.gradle; import org.gradle.api.artifacts.ExternalModuleDependency; diff --git a/src/main/java/net/minecraftforge/gradle/internal/MavenizerInstanceImpl.java b/src/main/java/net/minecraftforge/gradle/internal/MavenizerInstanceImpl.java index a66119dc6..951dd6bd3 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/MavenizerInstanceImpl.java +++ b/src/main/java/net/minecraftforge/gradle/internal/MavenizerInstanceImpl.java @@ -41,7 +41,7 @@ private Map invoke() { if (this.map == null) { valueSource.get(); // Execute Mavenizer, probably called before, but just be sure. this.map = (Map) new JsonSlurper().parse(this.jsonFile, "UTF-8"); - this.map.forEach((k, v) -> this.extension.getProject().getLogger().lifecycle(k + " => " + v)); + //this.map.forEach((k, v) -> this.extension.getProject().getLogger().lifecycle(k + " => " + v)); } return this.map; } @@ -62,7 +62,7 @@ public Provider getDependency() { @Override public Provider getMappingVersion() { - return this.invoke.map(get("mappings.vesion")); + return this.invoke.map(get("mappings.version")); } @Override @@ -72,7 +72,7 @@ public Provider getToSrg() { @Override public Provider getToSrgFile() { - return getToSrg().map(this.extension.getProject()::file); + return this.invoke.map(get("mappings.srg.file")).map(this.extension.getProject()::file); } @Override @@ -82,6 +82,6 @@ public Provider getToObf() { @Override public Provider getToObfFile() { - return getToSrg().map(this.extension.getProject()::file); + return this.invoke.map(get("mappings.obf.file")).map(this.extension.getProject()::file); } } From 693aaee065a7da77341affe9fbbb1748bb50865d Mon Sep 17 00:00:00 2001 From: LexManos Date: Tue, 10 Feb 2026 18:09:13 -0800 Subject: [PATCH 3/8] Move to MapProperty --- .../internal/MavenizerInstanceImpl.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/java/net/minecraftforge/gradle/internal/MavenizerInstanceImpl.java b/src/main/java/net/minecraftforge/gradle/internal/MavenizerInstanceImpl.java index 951dd6bd3..58946c18c 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/MavenizerInstanceImpl.java +++ b/src/main/java/net/minecraftforge/gradle/internal/MavenizerInstanceImpl.java @@ -8,6 +8,7 @@ import net.minecraftforge.gradle.MavenizerInstance; import org.gradle.api.Transformer; import org.gradle.api.artifacts.ExternalModuleDependency; +import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.Provider; import org.jspecify.annotations.Nullable; @@ -20,7 +21,7 @@ class MavenizerInstanceImpl implements MavenizerInstance { private final ExternalModuleDependency dependency; private final File jsonFile; - private final Provider> invoke; + private final MapProperty invoke; private @Nullable Map map; MavenizerInstanceImpl( @@ -33,7 +34,8 @@ class MavenizerInstanceImpl implements MavenizerInstance { this.dependency = dependency; this.valueSource = valueSource; this.jsonFile = jsonFile; - this.invoke = this.extension.getProviders().provider(this::invoke); + this.invoke = this.extension.getObjects().mapProperty(String.class, String.class) + .convention(this.extension.getProviders().provider(this::invoke)); } @SuppressWarnings("unchecked") @@ -46,13 +48,11 @@ private Map invoke() { return this.map; } - private Transformer> get(String key) { - return map -> { - var ret = map.get(key); - if (ret == null) - throw new IllegalStateException("Mavenizer did not output expected json data " + key); - return ret; - }; + private Provider get(String key) { + return this.invoke.getting(key) + .orElse(this.extension.getProviders().provider(() -> { + throw new IllegalStateException("Mavenizer did not output expected json data " + key); + })); } @Override @@ -62,26 +62,26 @@ public Provider getDependency() { @Override public Provider getMappingVersion() { - return this.invoke.map(get("mappings.version")); + return get("mappings.version"); } @Override public Provider getToSrg() { - return this.invoke.map(get("mappings.srg.artifact")); + return get("mappings.srg.artifact"); } @Override public Provider getToSrgFile() { - return this.invoke.map(get("mappings.srg.file")).map(this.extension.getProject()::file); + return get("mappings.srg.file").map(this.extension.getProject()::file); } @Override public Provider getToObf() { - return this.invoke.map(get("mappings.obf.artifact")); + return get("mappings.obf.artifact"); } @Override public Provider getToObfFile() { - return this.invoke.map(get("mappings.obf.file")).map(this.extension.getProject()::file); + return get("mappings.obf.file").map(this.extension.getProject()::file); } } From d9823bb8b220768ae622da5d73b2eba141c68117 Mon Sep 17 00:00:00 2001 From: Jonathing Date: Wed, 11 Feb 2026 15:31:23 -0500 Subject: [PATCH 4/8] Address a couple of minor nitpicks --- .../gradle/internal/ForgeGradleProblems.java | 17 ++++++++++------- .../gradle/internal/MinecraftExtensionImpl.java | 10 ++++------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java b/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java index 7b63f8a2a..919be14a1 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java +++ b/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java @@ -214,14 +214,17 @@ void reportForgeMavenNotDeclared() { ); } - void reportDuplicateMavenizerNames() { - this.report("forge-maven-duplicate-name", "Duplicate minecraft dependencies registered", spec -> spec - .details(""" + void reportDuplicateMavenizerNames(String name) { + this.report("forge-maven-duplicate-name", "Duplicate Minecraft dependencies registered", spec -> spec + .details(""" + A Minecraft dependency was declared with a name already in use! In order to manage access to mapping data each deobfuscated dependency needs to have a unique name. - Call the `minecraft.dependency` method with a unique name as the first parameter, the default when not specified is `default`""") - .severity(Severity.WARNING) - .solution("Call `minecraft.dependency` method with a unique name as the first parameter`") - .solution(HELP_MESSAGE) + Call the `minecraft.dependency` method with a unique name as the first parameter, the default when not specified is `default` + Name used: %s""" + .formatted(name)) + .severity(Severity.WARNING) + .solution("Call `minecraft.dependency` method with a unique name as the first parameter`") + .solution(HELP_MESSAGE) ); } //endregion diff --git a/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java b/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java index 27ea4175e..a751623ca 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java +++ b/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java @@ -405,8 +405,7 @@ public Provider dependency( .map(this.problems.ensureFileLocation()); var cache = toolCache.get().dir("caches").getAsFile().getAbsolutePath(); - var ret = new ArrayList(); - ret.addAll(List.of( + var ret = new ArrayList<>(List.of( "--maven", "--cache", cache, "--jdk-cache", cache, @@ -449,10 +448,9 @@ public Provider dependency( }); var instance = new MavenizerInstanceImpl(this, mavenizer, dep, outputJson); - if (this.mavenizerRegistry.containsKey(name)) - problems.reportDuplicateMavenizerNames(); - this.mavenizerRegistry.put(name, instance); - return instance.getDependency(); + if (this.mavenizerRegistry.put(name, instance) != null) + problems.reportDuplicateMavenizerNames(name); + return instance; } @Override From e36044ef7aefbc211c1cbab86f76a5dcc99ec7e5 Mon Sep 17 00:00:00 2001 From: Jonathing Date: Wed, 11 Feb 2026 15:32:09 -0500 Subject: [PATCH 5/8] Make MavenizerInstance a ProviderConvertible --- .../gradle/MavenizerInstance.java | 5 +++-- .../gradle/MinecraftExtensionForProject.java | 21 ++++++++----------- .../internal/MavenizerInstanceImpl.java | 2 +- .../internal/MinecraftExtensionImpl.java | 4 ++-- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/main/java/net/minecraftforge/gradle/MavenizerInstance.java b/src/main/java/net/minecraftforge/gradle/MavenizerInstance.java index a22d5977a..6edb65294 100644 --- a/src/main/java/net/minecraftforge/gradle/MavenizerInstance.java +++ b/src/main/java/net/minecraftforge/gradle/MavenizerInstance.java @@ -6,11 +6,12 @@ import org.gradle.api.artifacts.ExternalModuleDependency; import org.gradle.api.provider.Provider; +import org.gradle.api.provider.ProviderConvertible; import java.io.File; -public interface MavenizerInstance { - Provider getDependency(); +public interface MavenizerInstance extends ProviderConvertible { + @Override Provider asProvider(); Provider getMappingVersion(); Provider getToSrg(); Provider getToSrgFile(); diff --git a/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java b/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java index ea2df9980..0d821ec42 100644 --- a/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java +++ b/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java @@ -7,13 +7,10 @@ import groovy.lang.Closure; import groovy.lang.DelegatesTo; import groovy.transform.stc.ClosureParams; -import groovy.transform.stc.FromString; import groovy.transform.stc.SimpleType; import net.minecraftforge.gradleutils.shared.Closures; import org.gradle.api.Action; -import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.artifacts.ExternalModuleDependency; -import org.gradle.api.provider.Provider; /// [Project][org.gradle.api.Project]-specific additions for the Minecraft extension. These will be accessible from the /// `minecraft` DSL object within your project's buildscript. @@ -28,7 +25,7 @@ public interface MinecraftExtensionForProject extends MinecraftExtension, Minecr /// @return The dependency /// @see Declaring Dependencies /// in Gradle - default Provider dependency( + default MavenizerInstance dependency( Object value, @DelegatesTo(ExternalModuleDependency.class) @ClosureParams(value = SimpleType.class, options = "net.minecraftforge.gradle.ClosureOwner.MinecraftDependency") @@ -37,7 +34,7 @@ default Provider dependency( return this.dependency("default", value, closure); } - Provider dependency( + MavenizerInstance dependency( String name, Object value, @DelegatesTo(ExternalModuleDependency.class) @@ -53,10 +50,10 @@ Provider dependency( /// @return The dependency /// @see Declaring Dependencies /// in Gradle - default Provider dependency(Object value, Action action) { + default MavenizerInstance dependency(Object value, Action action) { return this.dependency("default", value, action); } - default Provider dependency(String name, Object value, Action action) { + default MavenizerInstance dependency(String name, Object value, Action action) { return this.dependency(name, value, Closures.action(this, action)); } @@ -66,15 +63,15 @@ default Provider dependency(String name, Object value, /// @return The dependency /// @see Declaring Dependencies /// in Gradle - default Provider dependency(Object value) { + default MavenizerInstance dependency(Object value) { return this.dependency("default", value); } - default Provider dependency(String name, Object value) { + default MavenizerInstance dependency(String name, Object value) { return this.dependency(name, value, Closures.empty(this)); } - default MavenizerInstance instance() { - return this.instance("default"); + default MavenizerInstance getDependency() { + return this.getDependency("default"); } - MavenizerInstance instance(String name); + MavenizerInstance getDependency(String name); } diff --git a/src/main/java/net/minecraftforge/gradle/internal/MavenizerInstanceImpl.java b/src/main/java/net/minecraftforge/gradle/internal/MavenizerInstanceImpl.java index 58946c18c..ab57a7dad 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/MavenizerInstanceImpl.java +++ b/src/main/java/net/minecraftforge/gradle/internal/MavenizerInstanceImpl.java @@ -56,7 +56,7 @@ private Provider get(String key) { } @Override - public Provider getDependency() { + public Provider asProvider() { return this.invoke.map(m -> this.dependency); } diff --git a/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java b/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java index a751623ca..974da74bd 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java +++ b/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java @@ -377,7 +377,7 @@ public NamedDomainObjectContainer getRuns() { @SuppressWarnings({"UnstableApiUsage"}) @Override - public Provider dependency( + public MavenizerInstance dependency( String name, Object value, @DelegatesTo(ExternalModuleDependency.class) @@ -454,7 +454,7 @@ public Provider dependency( } @Override - public MavenizerInstance instance(String name) { + public MavenizerInstance getDependency(String name) { return this.mavenizerRegistry.get(name); } From 46aa86094fdd13e2fb218b9a503aec8ba1050577 Mon Sep 17 00:00:00 2001 From: Jonathing Date: Wed, 11 Feb 2026 16:10:47 -0500 Subject: [PATCH 6/8] Document new interfaces and methods --- .../gradle/MavenizerInstance.java | 36 +++++++++++++++++++ .../gradle/MinecraftExtensionForProject.java | 26 ++++++++++++++ .../gradle/internal/ForgeGradleProblems.java | 4 +-- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/minecraftforge/gradle/MavenizerInstance.java b/src/main/java/net/minecraftforge/gradle/MavenizerInstance.java index 6edb65294..7eed9db40 100644 --- a/src/main/java/net/minecraftforge/gradle/MavenizerInstance.java +++ b/src/main/java/net/minecraftforge/gradle/MavenizerInstance.java @@ -10,11 +10,47 @@ import java.io.File; +/// A Mavenizer instance is an abstraction over an invocation of the Minecraft Mavenizer and its output when generating +/// a Minecraft dependency. This is designed to be consumed by one of the various +/// [MinecraftExtensionForProject#dependency] methods as it is a [ProviderConvertible] of a dependency. +/// +/// This is not to be confused with [MinecraftDependency], which is designed to add additional DSL features on top of +/// [ExternalModuleDependency] when configuring a Minecraft dependency in your buildscript. public interface MavenizerInstance extends ProviderConvertible { + /// Gets the dependency (used by Gradle) generated by the Mavenizer instance. + /// + /// @return The dependency generated by this instance @Override Provider asProvider(); + + /// Gets the mappings version used by the Mavenizer instance. + /// + /// @return The mappings version used + /// @see MinecraftMappings#getVersion() Provider getMappingVersion(); + + /// Gets the artifact coordinates for the SRG mappings used by the Mavenizer instance. + /// + /// @return The SRG mapping artifact coordinate Provider getToSrg(); + + /// Gets the SRG mapping file used by the Mavenizer instance. + /// + /// @return The SRG mapping file + /// @apiNote Buildscript authors should prefer to use [#getToSrg()] with + /// [org.gradle.api.artifacts.dsl.DependencyFactory#create(CharSequence)] or + /// [org.gradle.api.artifacts.dsl.DependencyHandler#addProvider(String, Provider)]. Provider getToSrgFile(); + + /// Gets the artifact coordinates for the SRG mappings used by the Mavenizer instance. + /// + /// @return The SRG mapping artifact coordinate Provider getToObf(); + + /// Gets the notch mapping file used by the Mavenizer instance. + /// + /// @return The notch mapping file + /// @apiNote Buildscript authors should prefer to use [#getToObf()] with + /// [org.gradle.api.artifacts.dsl.DependencyFactory#create(CharSequence)] or + /// [org.gradle.api.artifacts.dsl.DependencyHandler#addProvider(String, Provider)]. Provider getToObfFile(); } diff --git a/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java b/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java index 0d821ec42..7b08ea3dc 100644 --- a/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java +++ b/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java @@ -53,6 +53,16 @@ MavenizerInstance dependency( default MavenizerInstance dependency(Object value, Action action) { return this.dependency("default", value, action); } + + /// Creates (or marks if existing) the given dependency as a Minecraft dependency and applies the given action to + /// it. + /// + /// @param name The name to give the Mavenizer instance used to generate the dependency + /// @param value The dependency + /// @param action The action to apply to the dependency attributes + /// @return The dependency + /// @see Declaring Dependencies + /// in Gradle default MavenizerInstance dependency(String name, Object value, Action action) { return this.dependency(name, value, Closures.action(this, action)); } @@ -66,12 +76,28 @@ default MavenizerInstance dependency(String name, Object value, ActionDeclaring Dependencies + /// in Gradle default MavenizerInstance dependency(String name, Object value) { return this.dependency(name, value, Closures.empty(this)); } + /// Gets the default Minecraft dependency that was created using one of the [#dependency] methods. + /// + /// @return The default Minecraft dependency + /// @see #getDependency(String) default MavenizerInstance getDependency() { return this.getDependency("default"); } + + /// Gets the named Minecraft dependency that was created using one of the [#dependency] methods. + /// + /// @return The default Minecraft dependency MavenizerInstance getDependency(String name); } diff --git a/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java b/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java index 919be14a1..898c47918 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java +++ b/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java @@ -215,7 +215,7 @@ void reportForgeMavenNotDeclared() { } void reportDuplicateMavenizerNames(String name) { - this.report("forge-maven-duplicate-name", "Duplicate Minecraft dependencies registered", spec -> spec + this.report("mavenizer-instance-duplicate-name", "Duplicate Minecraft dependencies registered", spec -> spec .details(""" A Minecraft dependency was declared with a name already in use! In order to manage access to mapping data each deobfuscated dependency needs to have a unique name. @@ -223,7 +223,7 @@ void reportDuplicateMavenizerNames(String name) { Name used: %s""" .formatted(name)) .severity(Severity.WARNING) - .solution("Call `minecraft.dependency` method with a unique name as the first parameter`") + .solution("Call `minecraft.dependency` method with a unique name as the first parameter.") .solution(HELP_MESSAGE) ); } From b6fb47e7b8ed6066cec02555f6e2d9d655eb33c4 Mon Sep 17 00:00:00 2001 From: Jonathing Date: Wed, 11 Feb 2026 16:11:06 -0500 Subject: [PATCH 7/8] Throw on `minecraft.getDependency()` if dependency is not found --- .../gradle/MinecraftExtensionForProject.java | 2 ++ .../gradle/internal/ForgeGradleProblems.java | 14 ++++++++++++++ .../gradle/internal/MinecraftExtensionImpl.java | 6 +++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java b/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java index 7b08ea3dc..0d549dfbd 100644 --- a/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java +++ b/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java @@ -91,6 +91,7 @@ default MavenizerInstance dependency(String name, Object value) { /// Gets the default Minecraft dependency that was created using one of the [#dependency] methods. /// /// @return The default Minecraft dependency + /// @throws java.util.NoSuchElementException If the default Minecraft dependency has not yet been created. /// @see #getDependency(String) default MavenizerInstance getDependency() { return this.getDependency("default"); @@ -99,5 +100,6 @@ default MavenizerInstance getDependency() { /// Gets the named Minecraft dependency that was created using one of the [#dependency] methods. /// /// @return The default Minecraft dependency + /// @throws java.util.NoSuchElementException If the named Minecraft dependency has not yet been created. MavenizerInstance getDependency(String name); } diff --git a/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java b/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java index 898c47918..c099a6ec7 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java +++ b/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java @@ -16,6 +16,7 @@ import javax.inject.Inject; import java.io.File; +import java.util.NoSuchElementException; import static net.minecraftforge.gradle.internal.ForgeGradlePlugin.LOGGER; @@ -227,6 +228,19 @@ void reportDuplicateMavenizerNames(String name) { .solution(HELP_MESSAGE) ); } + + RuntimeException mavenizerInstanceNotFound(NoSuchElementException e, String name) { + return this.throwing(e, "mavenizer-instance-not-found", "Minecraft dependency instance not found", spec -> spec + .details(""" + A Minecraft dependency instance was requested but not found! + The Minecraft dependency must be declared using `minecraft.dependency` first before it can be referenced. + Name requested: %s""" + .formatted(name)) + .severity(Severity.ERROR) + .solution("Call `minecraft.dependency` method before getting it using `minecraft.getDependency()`.") + .solution(HELP_MESSAGE) + ); + } //endregion //endregion diff --git a/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java b/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java index 974da74bd..4ff9fa815 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java +++ b/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java @@ -51,6 +51,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.function.ToIntFunction; import java.util.stream.Collectors; @@ -455,7 +456,10 @@ public MavenizerInstance dependency( @Override public MavenizerInstance getDependency(String name) { - return this.mavenizerRegistry.get(name); + var ret = this.mavenizerRegistry.get(name); + if (ret == null) + throw problems.mavenizerInstanceNotFound(new NoSuchElementException("Mavenizer instance not found: " + name), name); + return ret; } private void checkRepos(List repos) { From 8964f9455e03b1845e13799cbc711bf6c0a131dd Mon Sep 17 00:00:00 2001 From: Jonathing Date: Wed, 11 Feb 2026 16:30:13 -0500 Subject: [PATCH 8/8] Forgot to document this method --- .../gradle/MinecraftExtensionForProject.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java b/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java index 0d549dfbd..339e60f01 100644 --- a/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java +++ b/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java @@ -34,6 +34,15 @@ default MavenizerInstance dependency( return this.dependency("default", value, closure); } + /// Creates (or marks if existing) the given dependency as a Minecraft dependency and configures it with the given + /// closure. + /// + /// @param name The name to give the Mavenizer instance used to generate the dependency + /// @param value The dependency + /// @param closure The closure to configure the dependency with + /// @return The dependency + /// @see Declaring Dependencies + /// in Gradle MavenizerInstance dependency( String name, Object value,