Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
56 changes: 56 additions & 0 deletions src/main/java/net/minecraftforge/gradle/MavenizerInstance.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.gradle;

import org.gradle.api.artifacts.ExternalModuleDependency;
import org.gradle.api.provider.Provider;
import org.gradle.api.provider.ProviderConvertible;

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<ExternalModuleDependency> {
/// Gets the dependency (used by Gradle) generated by the Mavenizer instance.
///
/// @return The dependency generated by this instance
@Override Provider<ExternalModuleDependency> asProvider();

/// Gets the mappings version used by the Mavenizer instance.
///
/// @return The mappings version used
/// @see MinecraftMappings#getVersion()
Provider<String> getMappingVersion();

/// Gets the artifact coordinates for the SRG mappings used by the Mavenizer instance.
///
/// @return The SRG mapping artifact coordinate
Provider<String> 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<File> getToSrgFile();

/// Gets the artifact coordinates for the SRG mappings used by the Mavenizer instance.
///
/// @return The SRG mapping artifact coordinate
Provider<String> 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<File> getToObfFile();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -28,7 +25,26 @@ public interface MinecraftExtensionForProject extends MinecraftExtension, Minecr
/// @return The dependency
/// @see <a href="https://docs.gradle.org/current/userguide/declaring_dependencies.html">Declaring Dependencies
/// in Gradle</a>
Provider<ExternalModuleDependency> dependency(
default MavenizerInstance dependency(
Object value,
@DelegatesTo(ExternalModuleDependency.class)
@ClosureParams(value = SimpleType.class, options = "net.minecraftforge.gradle.ClosureOwner.MinecraftDependency")
Closure<?> closure
) {
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 <a href="https://docs.gradle.org/current/userguide/declaring_dependencies.html">Declaring Dependencies
/// in Gradle</a>
MavenizerInstance dependency(
String name,
Object value,
@DelegatesTo(ExternalModuleDependency.class)
@ClosureParams(value = SimpleType.class, options = "net.minecraftforge.gradle.ClosureOwner.MinecraftDependency")
Expand All @@ -43,17 +59,56 @@ Provider<ExternalModuleDependency> dependency(
/// @return The dependency
/// @see <a href="https://docs.gradle.org/current/userguide/declaring_dependencies.html">Declaring Dependencies
/// in Gradle</a>
default Provider<ExternalModuleDependency> dependency(Object value, Action<? super ClosureOwner.MinecraftDependency> action) {
return this.dependency(value, Closures.action(this, action));
default MavenizerInstance dependency(Object value, Action<? super ClosureOwner.MinecraftDependency> 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 <a href="https://docs.gradle.org/current/userguide/declaring_dependencies.html">Declaring Dependencies
/// in Gradle</a>
default MavenizerInstance dependency(String name, Object value, Action<? super ClosureOwner.MinecraftDependency> action) {
return this.dependency(name, value, Closures.action(this, action));
}

/// Creates (or marks if existing) the given dependency as a Minecraft dependency.
///
/// @param value The dependency
/// @return The dependency
/// @see <a href="https://docs.gradle.org/current/userguide/declaring_dependencies.html">Declaring Dependencies
/// in Gradle</a>
default MavenizerInstance dependency(Object value) {
return this.dependency("default", value);
}

/// Creates (or marks if existing) the given dependency as a Minecraft dependency.
///
/// @param name The name to give the Mavenizer instance used to generate the dependency
/// @param value The dependency
/// @return The dependency
/// @see <a href="https://docs.gradle.org/current/userguide/declaring_dependencies.html">Declaring Dependencies
/// in Gradle</a>
default Provider<ExternalModuleDependency> dependency(Object value) {
return this.dependency(value, Closures.empty(this));
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
/// @throws java.util.NoSuchElementException If the default Minecraft dependency has not yet been created.
/// @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
/// @throws java.util.NoSuchElementException If the named Minecraft dependency has not yet been created.
MavenizerInstance getDependency(String name);
}
12 changes: 0 additions & 12 deletions src/main/java/net/minecraftforge/gradle/internal/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 = """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import javax.inject.Inject;
import java.io.File;
import java.util.NoSuchElementException;

import static net.minecraftforge.gradle.internal.ForgeGradlePlugin.LOGGER;

Expand Down Expand Up @@ -213,6 +214,33 @@ void reportForgeMavenNotDeclared() {
.solution(HELP_MESSAGE)
);
}

void reportDuplicateMavenizerNames(String name) {
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.
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)
);
}

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

Expand Down
Original file line number Diff line number Diff line change
@@ -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.MapProperty;
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<Boolean> valueSource;
private final ExternalModuleDependency dependency;
private final File jsonFile;

private final MapProperty<String, String> invoke;
private @Nullable Map<String, String> map;

MavenizerInstanceImpl(
MinecraftExtensionImpl.ForProjectImpl extension,
Provider<Boolean> valueSource,
ExternalModuleDependency dependency,
File jsonFile
) {
this.extension = extension;
this.dependency = dependency;
this.valueSource = valueSource;
this.jsonFile = jsonFile;
this.invoke = this.extension.getObjects().mapProperty(String.class, String.class)
.convention(this.extension.getProviders().provider(this::invoke));
}

@SuppressWarnings("unchecked")
private Map<String, String> invoke() {
if (this.map == null) {
valueSource.get(); // Execute Mavenizer, probably called before, but just be sure.
this.map = (Map<String, String>) new JsonSlurper().parse(this.jsonFile, "UTF-8");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are using Gson in Mavenizer, we should prefer using it in here as well. This is fine for now, though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didnt want to add extra deps to FG as we are not using GSON for anything else currently.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FG already bundles Gson as it is a transitive dependency of JSON Data Utils. But not a big deal, we can revisit this as needed.

//this.map.forEach((k, v) -> this.extension.getProject().getLogger().lifecycle(k + " => " + v));
}
return this.map;
}

private Provider<String> 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
public Provider<ExternalModuleDependency> asProvider() {
return this.invoke.map(m -> this.dependency);
}

@Override
public Provider<String> getMappingVersion() {
return get("mappings.version");
}

@Override
public Provider<String> getToSrg() {
return get("mappings.srg.artifact");
}

@Override
public Provider<File> getToSrgFile() {
return get("mappings.srg.file").map(this.extension.getProject()::file);
}

@Override
public Provider<String> getToObf() {
return get("mappings.obf.artifact");
}

@Override
public Provider<File> getToObfFile() {
return get("mappings.obf.file").map(this.extension.getProject()::file);
}
}
Loading