diff --git a/settings.gradle b/settings.gradle index bf5205009..301fa6c0c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,7 +13,7 @@ gradle.beforeProject { Project project -> mavenCentral() gradlePluginPortal() maven project.gradleutils.forgeMaven - //mavenLocal() + mavenLocal() } } } @@ -45,9 +45,10 @@ dependencyResolutionManagement.versionCatalogs.register('libs') { // https://plugins.gradle.org/plugin/net.minecraftforge.accesstransformers library 'accesstransformers-gradle', 'net.minecraftforge.accesstransformers', 'net.minecraftforge.accesstransformers.gradle.plugin' version '5.0.1' - library 'utils-data', 'net.minecraftforge', 'json-data-utils' version '0.3.0' // https://files.minecraftforge.net/net/minecraftforge/json-data-utils/index.html + library 'utils-data', 'net.minecraftforge', 'json-data-utils' version '0.4.0' // https://files.minecraftforge.net/net/minecraftforge/json-data-utils/index.html + library 'utils-os', 'net.minecraftforge', 'os-utils' version '0.1.0' // https://files.minecraftforge.net/net/minecraftforge/os-utils/index.html library 'utils-hash', 'net.minecraftforge', 'hash-utils' version '0.1.10' // https://files.minecraftforge.net/net/minecraftforge/hash-utils/index.html library 'utils-download', 'net.minecraftforge', 'download-utils' version '0.3.2' // https://files.minecraftforge.net/net/minecraftforge/download-utils/index.html - bundle 'utils', ['utils-data', 'utils-hash', 'utils-download'] + bundle 'utils', ['utils-data', 'utils-os', 'utils-hash', 'utils-download'] } //@formatter:on diff --git a/src/main/java/net/minecraftforge/gradle/ForgeGradleProblems.java b/src/main/java/net/minecraftforge/gradle/ForgeGradleProblems.java index a229170b2..4c1401481 100644 --- a/src/main/java/net/minecraftforge/gradle/ForgeGradleProblems.java +++ b/src/main/java/net/minecraftforge/gradle/ForgeGradleProblems.java @@ -124,7 +124,7 @@ RuntimeException changingMinecraftDependency(Dependency dependency) { //endregion //region Minecraft Maven - RuntimeException mavenizerOutOfDateCompile(Dependency dependency) { + RuntimeException mavenizerOutOfDateCompile(Object dependency) { return this.throwing(new IllegalStateException(), "mavenizer-out-of-date", "Minecraft Mavenizer is out-of-date", spec -> spec .details(""" Gradle cannot compile your sources because the Minecraft Mavenizer is out-of-date. diff --git a/src/main/java/net/minecraftforge/gradle/MinecraftDependencyImpl.java b/src/main/java/net/minecraftforge/gradle/MinecraftDependencyImpl.java index 334266231..93a42a40d 100644 --- a/src/main/java/net/minecraftforge/gradle/MinecraftDependencyImpl.java +++ b/src/main/java/net/minecraftforge/gradle/MinecraftDependencyImpl.java @@ -10,12 +10,14 @@ import groovy.transform.NamedVariant; import net.minecraftforge.accesstransformers.gradle.ArtifactAccessTransformer; import net.minecraftforge.gradleutils.shared.Closures; +import net.minecraftforge.util.os.OS; import org.gradle.api.Action; import org.gradle.api.InvalidUserCodeException; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.ExternalModuleDependency; +import org.gradle.api.artifacts.ModuleIdentifier; import org.gradle.api.artifacts.type.ArtifactTypeDefinition; import org.gradle.api.attributes.Attribute; import org.gradle.api.attributes.AttributeContainer; @@ -23,7 +25,6 @@ import org.gradle.api.file.Directory; import org.gradle.api.file.ProjectLayout; import org.gradle.api.file.RegularFileProperty; -import org.gradle.api.logging.LogLevel; import org.gradle.api.model.ObjectFactory; import org.gradle.api.plugins.ExtensionAware; import org.gradle.api.plugins.JavaPluginExtension; @@ -33,19 +34,22 @@ import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.TaskProvider; import org.gradle.api.tasks.compile.JavaCompile; -import org.gradle.internal.os.OperatingSystem; -import org.gradle.nativeplatform.OperatingSystemFamily; import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; import javax.inject.Inject; import java.io.File; import java.util.Map; import java.util.Objects; +import java.util.Set; abstract class MinecraftDependencyImpl implements MinecraftDependencyInternal { - private @UnknownNullability ExternalModuleDependency delegate; - private @UnknownNullability TaskProvider mavenizer; + private transient @Nullable("configuration cache") ExternalModuleDependency delegate; + private transient @Nullable("configuration cache") TaskProvider mavenizer; + + final Property asString = getObjects().property(String.class); + final Property asPath = getObjects().property(String.class); + final Property module = getObjects().property(ModuleIdentifier.class); + final Property version = getObjects().property(String.class); private final Provider mavenizerOutput; private final Property mappings; @@ -70,12 +74,12 @@ public MinecraftDependencyImpl(Provider mavenizerOutput) { } @Override - public ExternalModuleDependency asDependency() { + public @Nullable("configuration cache") ExternalModuleDependency asDependency() { return this.delegate; } @Override - public TaskProvider asTask() { + public @Nullable("configuration cache") TaskProvider asTask() { return this.mavenizer; } @@ -97,13 +101,18 @@ public ExternalModuleDependency init(Object dependencyNotation, Closure closu this.mavenizer = SyncMavenizer.register(getProject(), dependency, this.mappings, mavenizerOutput); + this.asString.set(dependency.toString()); + this.asPath.set(Util.pathify(dependency)); + this.module.set(dependency.getModule()); + this.version.set(dependency.getVersion()); + return this.delegate = dependency; } @Override public Action addAttributes() { return attributes -> { - attributes.attribute(MinecraftExtension.Attributes.os, this.getObjects().named(OperatingSystemFamily.class, OperatingSystem.current().getFamilyName())); + attributes.attributeProvider(MinecraftExtension.Attributes.os, getProviders().of(OperatingSystemName.class, spec -> spec.parameters(parameters -> parameters.getAllowedOperatingSystems().set(Set.of(OS.WINDOWS, OS.MACOS, OS.LINUX))))); attributes.attributeProvider(MinecraftExtension.Attributes.mappingsChannel, mappings.map(MinecraftMappings::channel)); attributes.attributeProvider(MinecraftExtension.Attributes.mappingsVersion, mappings.map(MinecraftMappings::version)); }; @@ -114,9 +123,8 @@ public void handle(Configuration configuration) { if (!configuration.isCanBeResolved()) return; this.mappings.finalizeValue(); - var dependency = this.asDependency(); configuration.getResolutionStrategy().dependencySubstitution(s -> { - var moduleSelector = "%s:%s".formatted(dependency.getModule(), dependency.getVersion()); + var moduleSelector = "%s:%s".formatted(this.module.get(), this.version.get()); var module = s.module(moduleSelector); try { s.substitute(module) @@ -127,12 +135,15 @@ public void handle(Configuration configuration) { } }); + var asDependency = this.asDependency(); + if (asDependency == null) return; + var hierarchy = configuration.getHierarchy(); var sourceSet = Util.getSourceSet( getProject().getConfigurations().matching(hierarchy::contains), getProject().getExtensions().getByType(JavaPluginExtension.class).getSourceSets(), - this.asDependency() + asDependency ); // Hope that this is handled elsewhere, and that we are transitive. @@ -145,9 +156,9 @@ public void handle(Configuration configuration) { public void handle(SourceSet sourceSet) { getProject().getTasks().named(sourceSet.getCompileJavaTaskName(), JavaCompile.class, task -> { task.doFirst(t -> { - var file = this.mavenizerOutput.map(dir -> dir.dir(Util.pathify(asDependency()))).get().getAsFile(); + var file = this.mavenizerOutput.map(dir -> dir.dir(this.asPath)).get().get().getAsFile(); if (!file.exists()) - throw this.problems.mavenizerOutOfDateCompile(asDependency()); + throw this.problems.mavenizerOutOfDateCompile(this.asString.get()); }); }); @@ -223,7 +234,10 @@ public Action addAttributes() { public void handle(SourceSet sourceSet) { super.handle(sourceSet); - if (!Util.contains(getProject().getConfigurations(), sourceSet, false, this.asDependency())) return; + var asDependency = this.asDependency(); + if (asDependency == null) return; + + if (!Util.contains(getProject().getConfigurations(), sourceSet, false, asDependency)) return; if (!this.atPath.isPresent()) return; var itor = sourceSet.getResources().getSrcDirs().iterator(); @@ -235,7 +249,7 @@ public void handle(SourceSet sourceSet) { this.atFile.convention(this.getProjectLayout().getProjectDirectory().file(this.getProviders().provider(() -> "src/%s/resources/%s".formatted(sourceSet.getName(), this.atPath.get()))).get()); } - ArtifactAccessTransformer.validateConfig(getProject(), this.asDependency(), this.atFile); + ArtifactAccessTransformer.validateConfig(getProject(), asDependency, this.atFile); } private Attribute registerTransform() { diff --git a/src/main/java/net/minecraftforge/gradle/MinecraftDependencyInternal.java b/src/main/java/net/minecraftforge/gradle/MinecraftDependencyInternal.java index 655a8b37c..e789cd458 100644 --- a/src/main/java/net/minecraftforge/gradle/MinecraftDependencyInternal.java +++ b/src/main/java/net/minecraftforge/gradle/MinecraftDependencyInternal.java @@ -46,9 +46,9 @@ static boolean is(Dependency dependency) { ExternalModuleDependency init(Object dependencyNotation, Closure closure); - ExternalModuleDependency asDependency(); + @Nullable("configuration cache") ExternalModuleDependency asDependency(); - TaskProvider asTask(); + @Nullable("configuration cache") TaskProvider asTask(); Action addAttributes(); diff --git a/src/main/java/net/minecraftforge/gradle/MinecraftExtension.java b/src/main/java/net/minecraftforge/gradle/MinecraftExtension.java index d163829c3..51e10366a 100644 --- a/src/main/java/net/minecraftforge/gradle/MinecraftExtension.java +++ b/src/main/java/net/minecraftforge/gradle/MinecraftExtension.java @@ -80,7 +80,7 @@ sealed interface Attributes permits MinecraftExtensionInternal.AttributesInterna /// The [operating system family][OperatingSystemFamily] of the project's host. /// /// This is used to filter natives from the Minecraft repo. - Attribute os = Attribute.of("net.minecraftforge.native.operatingSystem", OperatingSystemFamily.class); + Attribute os = Attribute.of("net.minecraftforge.native.operatingSystem", String.class); /// The requested mappings channel of the project. /// /// This is determined using [MinecraftMappings#channel()] via [#getMappings()] diff --git a/src/main/java/net/minecraftforge/gradle/MinecraftExtensionImpl.java b/src/main/java/net/minecraftforge/gradle/MinecraftExtensionImpl.java index 2d6988a50..7a9aecbf0 100644 --- a/src/main/java/net/minecraftforge/gradle/MinecraftExtensionImpl.java +++ b/src/main/java/net/minecraftforge/gradle/MinecraftExtensionImpl.java @@ -271,7 +271,10 @@ private void finish(Project project) { if (!this.minecraftDependencies.isEmpty()) { var syncMavenizer = project.getTasks().register("syncMavenizer", task -> task.setGroup("Build Setup")); for (var minecraftDependency : this.minecraftDependencies) { - syncMavenizer.configure(task -> task.dependsOn(minecraftDependency.asTask())); + var mavenizer = minecraftDependency.asTask(); + if (mavenizer == null) continue; + + syncMavenizer.configure(task -> task.dependsOn(mavenizer)); } project.getPluginManager().withPlugin("eclipse", eclipsePlugin -> { @@ -302,8 +305,8 @@ private void finish(Project project) { // This can never be null in production and is only here to make the IDE happy. assert minecraftDependency != null; - var dependency = minecraftDependency.asDependency(); - this.runs.forEach(options -> SlimeLauncherExec.register(project, sourceSet, options, dependency, single)); + var impl = (MinecraftDependencyImpl) minecraftDependency; + this.runs.forEach(options -> SlimeLauncherExec.register(project, sourceSet, options, impl.module.get(), impl.version.get(), impl.asPath.get(), impl.asString.get(), single)); } }); } diff --git a/src/main/java/net/minecraftforge/gradle/OperatingSystemName.java b/src/main/java/net/minecraftforge/gradle/OperatingSystemName.java new file mode 100644 index 000000000..2ca56a4bd --- /dev/null +++ b/src/main/java/net/minecraftforge/gradle/OperatingSystemName.java @@ -0,0 +1,21 @@ +package net.minecraftforge.gradle; + +import net.minecraftforge.util.os.OS; +import org.gradle.api.provider.SetProperty; +import org.gradle.api.provider.ValueSource; +import org.gradle.api.provider.ValueSourceParameters; +import org.jspecify.annotations.Nullable; + +abstract class OperatingSystemName implements ValueSource { + interface Parameters extends ValueSourceParameters { + SetProperty getAllowedOperatingSystems(); + } + + @Override + public @Nullable String obtain() { + var allowed = getParameters().getAllowedOperatingSystems().get().toArray(new OS[0]); + return allowed.length > 0 + ? OS.current(allowed).key() + : OS.current().key(); + } +} diff --git a/src/main/java/net/minecraftforge/gradle/SlimeLauncherExec.java b/src/main/java/net/minecraftforge/gradle/SlimeLauncherExec.java index 12ae5ee70..edaf8d978 100644 --- a/src/main/java/net/minecraftforge/gradle/SlimeLauncherExec.java +++ b/src/main/java/net/minecraftforge/gradle/SlimeLauncherExec.java @@ -12,6 +12,8 @@ import org.gradle.api.UnknownDomainObjectException; import org.gradle.api.UnknownTaskException; import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.attributes.Usage; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.ListProperty; @@ -38,22 +40,27 @@ import java.util.Map; abstract class SlimeLauncherExec extends JavaExec implements ForgeGradleTask, HasPublicType { - static void register(Project project, SourceSet sourceSet, SlimeLauncherOptionsImpl options, Dependency dependency, boolean single) { + static void register(Project project, SourceSet sourceSet, SlimeLauncherOptionsImpl options, ModuleIdentifier module, String version, String asPath, String asString, boolean single) { TaskProvider metadata; { TaskProvider t; - var taskName = "slimeLauncherMetadata" + (single ? "" : "for" + Util.dependencyToCamelCase(dependency)); + var taskName = "slimeLauncherMetadata" + (single ? "" : "for" + Util.dependencyToCamelCase(module)); try { t = project.getTasks().named(taskName, SlimeLauncherMetadata.class); } catch (UnknownDomainObjectException e) { - var metadataZip = project.getObjects().fileProperty().fileProvider(project.getProviders().provider(() -> - project.getConfigurations().detachedConfiguration( - project.getDependencyFactory().create(dependency.getGroup(), dependency.getName(), dependency.getVersion(), "metadata", "zip") - ).getSingleFile() - )); + var metadataDep = project.getDependencyFactory().create(module.getGroup(), module.getName(), version, "metadata", "zip"); + var metadataAttr = project.getObjects().named(Usage.class, "metadata"); + var metadataZip = project.getObjects().fileProperty().fileProvider(project.getProviders().provider(() -> { + var configuration = project.getConfigurations().detachedConfiguration( + metadataDep + ); + configuration.setTransitive(false); + configuration.attributes(a -> a.attribute(Usage.USAGE_ATTRIBUTE, metadataAttr)); + return configuration.getSingleFile(); + })); t = project.getTasks().register(taskName, SlimeLauncherMetadata.class, task -> { - task.setDescription("Extracts the Slime Launcher metadata%s.".formatted(single ? "" : " for '%s'".formatted(Util.toString(dependency)))); + task.setDescription("Extracts the Slime Launcher metadata%s.".formatted(single ? "" : " for '%s'".formatted(asString))); task.getMetadataZip().set(metadataZip); }); @@ -62,7 +69,7 @@ static void register(Project project, SourceSet sourceSet, SlimeLauncherOptionsI metadata = t; } - var taskName = sourceSet.getTaskName("run", options.getName()) + (single ? "" : "for" + Util.dependencyToCamelCase(dependency)); + var taskName = sourceSet.getTaskName("run", options.getName()) + (single ? "" : "for" + Util.dependencyToCamelCase(module)); project.getTasks().register(taskName, SlimeLauncherExec.class, task -> { task.getRunName().set(options.getName()); task.setDescription("Runs the '%s' Slime Launcher run configuration.".formatted(options.getName())); @@ -70,8 +77,9 @@ static void register(Project project, SourceSet sourceSet, SlimeLauncherOptionsI task.classpath(task.getObjectFactory().fileCollection().from(task.getProviderFactory().provider(sourceSet::getRuntimeClasspath))); task.getJavaLauncher().unset(); - var caches = task.getObjectFactory().directoryProperty().value(task.globalCaches().dir("slime-launcher/cache/%s".formatted(Util.pathify(dependency)))); + var caches = task.getObjectFactory().directoryProperty().value(task.globalCaches().dir("slime-launcher/cache/%s".formatted(asPath))); task.getCacheDir().set(caches.map(task.problems.ensureFileLocation())); + task.getMetadataZip().set(metadata.flatMap(SlimeLauncherMetadata::getMetadataZip)); task.getRunsJson().set(metadata.flatMap(SlimeLauncherMetadata::getRunsJson)); task.getOptions().set(options); @@ -84,9 +92,11 @@ static void register(Project project, SourceSet sourceSet, SlimeLauncherOptionsI protected abstract @Internal DirectoryProperty getCacheDir(); + protected abstract @InputFile RegularFileProperty getMetadataZip(); + protected abstract @InputFile RegularFileProperty getRunsJson(); - protected abstract @Input Property getDelegateMainClass(); + protected abstract @Input @Optional Property getDelegateMainClass(); protected abstract @Input @Optional ListProperty getDelegateArgs(); @@ -116,7 +126,7 @@ public SlimeLauncherExec() { @Override public void exec() { Provider mainClass; - Provider> args; + List args; //region Launcher Metadata Inheritance Map configs = Map.of(); @@ -129,12 +139,12 @@ public void exec() { // continue } - var options = (SlimeLauncherOptionsImpl) this.getOptions().get(); - options.inherit(configs); + var options = ((SlimeLauncherOptionsInternal) this.getOptions().get()).inherit(configs); mainClass = this.getDelegateMainClass().orElse(options.getMainClass().filter(Util::isPresent)); - args = this.getDelegateArgs().orElse(options.getArgs()); - this.jvmArgs(options.getJvmArgs()); + args = new ArrayList<>(options.getArgs().getOrElse(List.of())); + args.addAll(this.getDelegateArgs().getOrElse(List.of())); + this.jvmArgs(options.getJvmArgs().get()); if (!options.getClasspath().isEmpty()) this.setClasspath(options.getClasspath()); if (options.getMinHeapSize().filter(Util::isPresent).isPresent()) @@ -151,11 +161,11 @@ public void exec() { } else { this.args("--main", mainClass.get(), "--cache", this.getCacheDir().get().getAsFile().getAbsolutePath(), - "--metadata", this.getRunsJson().get().getAsFile().getAbsolutePath(), + "--metadata", this.getMetadataZip().get().getAsFile().getAbsolutePath(), "--"); } - this.args(args.get().toArray()); + this.args(args); if (!this.getClient().getOrElse(false)) this.setStandardInput(System.in); @@ -167,6 +177,13 @@ public void exec() { } this.getLogger().info("{} {}", this.getClasspath().getAsPath(), String.join(" ", this.getArgs())); - super.exec(); + try { + super.exec(); + } catch (Exception e) { + this.getLogger().error("Something went wrong! Here is some debug info."); + this.getLogger().error("Args: {}", this.getArgs()); + this.getLogger().error("Options: {}", options); + throw e; + } } } diff --git a/src/main/java/net/minecraftforge/gradle/SlimeLauncherMetadata.java b/src/main/java/net/minecraftforge/gradle/SlimeLauncherMetadata.java index 34a799840..22b4cae6b 100644 --- a/src/main/java/net/minecraftforge/gradle/SlimeLauncherMetadata.java +++ b/src/main/java/net/minecraftforge/gradle/SlimeLauncherMetadata.java @@ -10,10 +10,11 @@ import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.OutputFile; import org.gradle.api.tasks.TaskAction; -import org.gradle.internal.impldep.com.google.common.io.Files; import javax.inject.Inject; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; abstract class SlimeLauncherMetadata extends DefaultTask implements ForgeGradleTask { protected abstract @InputFile RegularFileProperty getMetadataZip(); @@ -33,7 +34,12 @@ protected void exec() { .visit(file -> { try { if (file.getPath().equals("launcher/runs.json")) { - Files.copy(file.getFile(), this.getRunsJson().getAsFile().get()); + Files.copy( + file.getFile().toPath(), + this.getRunsJson().getAsFile().get().toPath(), + StandardCopyOption.REPLACE_EXISTING, + StandardCopyOption.COPY_ATTRIBUTES + ); } } catch (IOException e) { throw new RuntimeException(e); diff --git a/src/main/java/net/minecraftforge/gradle/SlimeLauncherOptions.java b/src/main/java/net/minecraftforge/gradle/SlimeLauncherOptions.java index e58d6c6d6..ab072aca7 100644 --- a/src/main/java/net/minecraftforge/gradle/SlimeLauncherOptions.java +++ b/src/main/java/net/minecraftforge/gradle/SlimeLauncherOptions.java @@ -12,6 +12,11 @@ import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.Optional; import java.io.Serializable; import java.util.Map; @@ -34,13 +39,16 @@ /// public-facing interface APIs in ForgeGradle, this class remains sealed and is implemented by a package-private class /// that cannot be directly accessed. public sealed interface SlimeLauncherOptions extends Named permits SlimeLauncherOptionsInternal { + @Override + @Input String getName(); + /// The main class for Slime Launcher to use. /// /// This is the class that will be invoked by Slime Launcher, **not** the main class of the /// [org.gradle.api.tasks.JavaExec] task that will be produced from these options. /// /// @return A property for the main class - Property getMainClass(); + @Input @Optional Property getMainClass(); /// The arguments to pass to the main class. /// @@ -48,7 +56,7 @@ public sealed interface SlimeLauncherOptions extends Named permits SlimeLauncher /// Slime Launcher itself. /// /// @return A property for the arguments to pass to the main class - ListProperty getArgs(); + @Input @Optional ListProperty getArgs(); /// The JVM arguments to use. /// @@ -56,7 +64,7 @@ public sealed interface SlimeLauncherOptions extends Named permits SlimeLauncher /// re-launcher but a dev-environment bootstrapper. /// /// @return A property for the JVM arguments - ListProperty getJvmArgs(); + @Input @Optional ListProperty getJvmArgs(); /// The classpath to use. /// @@ -68,7 +76,7 @@ public sealed interface SlimeLauncherOptions extends Named permits SlimeLauncher /// class][org.gradle.api.tasks.JavaExec#getMainClass()], the classpath does not need to include Slime Launcher. /// /// @return The classpath to use - ConfigurableFileCollection getClasspath(); + @InputFiles @Classpath @Optional ConfigurableFileCollection getClasspath(); /// The minimum memory heap size to use. /// @@ -76,7 +84,7 @@ public sealed interface SlimeLauncherOptions extends Named permits SlimeLauncher /// arguments][#getJvmArgs()]. /// /// @return A property for the minimum heap size - Property getMinHeapSize(); + @Input @Optional Property getMinHeapSize(); /// The maximum memory heap size to use. /// @@ -84,17 +92,17 @@ public sealed interface SlimeLauncherOptions extends Named permits SlimeLauncher /// arguments][#getJvmArgs()]. /// /// @return A property for the maximum heap size - Property getMaxHeapSize(); + @Input @Optional Property getMaxHeapSize(); /// The system properties to use. /// /// @return A property for the system properties - MapProperty getSystemProperties(); + @Input @Optional MapProperty getSystemProperties(); /// The environment variables to use. /// /// @return A property for the environment variables - MapProperty getEnvironment(); + @Input @Optional MapProperty getEnvironment(); /// The working directory to use. /// @@ -104,7 +112,7 @@ public sealed interface SlimeLauncherOptions extends Named permits SlimeLauncher /// place its caches and metadata, which do not interfere with the working directory. /// /// @return A property for the working directory - DirectoryProperty getWorkingDir(); + @Internal DirectoryProperty getWorkingDir(); /// Adds to the arguments to pass to the [main class][#getMainClass()]. /// diff --git a/src/main/java/net/minecraftforge/gradle/SlimeLauncherOptionsImpl.java b/src/main/java/net/minecraftforge/gradle/SlimeLauncherOptionsImpl.java index d42aae2e5..5e6d19bb9 100644 --- a/src/main/java/net/minecraftforge/gradle/SlimeLauncherOptionsImpl.java +++ b/src/main/java/net/minecraftforge/gradle/SlimeLauncherOptionsImpl.java @@ -4,6 +4,7 @@ */ package net.minecraftforge.gradle; +import net.minecraftforge.util.data.json.RunConfig; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.model.ObjectFactory; @@ -18,6 +19,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; abstract class SlimeLauncherOptionsImpl implements SlimeLauncherOptionsInternal { @@ -234,4 +236,70 @@ public void environment(Provider> properties) { })); } + + /* INHERITANCE */ + + @Override + public SlimeLauncherOptionsInternal inherit(Map configs, String name) { + var target = getObjects().newInstance(SlimeLauncherOptionsImpl.class, name); + target.getMainClass().convention(this.getMainClass()); + target.getArgs().convention(this.getArgs()); + target.getJvmArgs().convention(this.getJvmArgs()); + target.getClasspath().convention(this.getClasspath()); + target.getMinHeapSize().convention(this.getMinHeapSize()); + target.getMaxHeapSize().convention(this.getMaxHeapSize()); + target.getSystemProperties().convention(this.getSystemProperties()); + target.getEnvironment().convention(this.getEnvironment()); + target.getWorkingDir().convention(this.getWorkingDir()); + target.getClient().convention(this.getClient()); + return this.inherit(target, configs, name); + } + + private SlimeLauncherOptionsInternal inherit(SlimeLauncherOptionsInternal target, Map configs, String name) { + var config = configs.get(name); + if (config == null) return target; + + if (config.parents != null && !config.parents.isEmpty()) + config.parents.forEach(parent -> this.inherit(target, configs, parent)); + + if (config.main != null) + target.getMainClass().convention(config.main); + + if (config.args != null && !config.args.isEmpty()) + target.getArgs().convention(List.copyOf(config.args)); + + if (config.jvmArgs != null && !config.jvmArgs.isEmpty()) + target.jvmArgs(config.jvmArgs); + + target.getClient().set(config.client); + + if (config.buildAllProjects) + LOGGER.warn("WARNING: ForgeGradle 7 does not support the buildAllProjects feature."); + + if (config.env != null && !config.env.isEmpty()) + target.environment(config.env); + + if (config.props != null && !config.props.isEmpty()) + target.systemProperties(config.props); + + return target; + } + + /* DEBUGGING */ + + @Override public String toString() { + return "SlimeLauncherOptionsImpl{" + + "name='" + name + '\'' + + ", mainClass=" + mainClass.getOrNull() + + ", args=[" + String.join(", ", args.getOrElse(List.of())) + ']' + + ", jvmArgs=[" + String.join(", ", jvmArgs.getOrElse(List.of())) + ']' + + ", classpath=[" + classpath.getAsPath() + ']' + + ", minHeapSize=" + minHeapSize.getOrNull() + + ", maxHeapSize=" + maxHeapSize.getOrNull() + + ", systemProperties=" + systemProperties.getOrNull() + + ", environment=" + environment.getOrNull() + + ", workingDir=" + workingDir.getOrNull() + + ", client=" + client.getOrNull() + + '}'; + } } diff --git a/src/main/java/net/minecraftforge/gradle/SlimeLauncherOptionsInternal.java b/src/main/java/net/minecraftforge/gradle/SlimeLauncherOptionsInternal.java index 4e64db3a0..da59c539e 100644 --- a/src/main/java/net/minecraftforge/gradle/SlimeLauncherOptionsInternal.java +++ b/src/main/java/net/minecraftforge/gradle/SlimeLauncherOptionsInternal.java @@ -12,6 +12,9 @@ import org.gradle.api.provider.ProviderConvertible; import org.gradle.api.reflect.HasPublicType; import org.gradle.api.reflect.TypeOf; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.Optional; import java.util.ArrayList; import java.util.List; @@ -21,41 +24,15 @@ non-sealed interface SlimeLauncherOptionsInternal extends SlimeLauncherOptions, Logger LOGGER = Logging.getLogger(SlimeLauncherOptions.class); @Override - default TypeOf getPublicType() { + default @Internal TypeOf getPublicType() { return TypeOf.typeOf(SlimeLauncherOptions.class); } - Property getClient(); + @Input @Optional Property getClient(); - default void inherit(Map configs) { - this.inherit(configs, this.getName()); + default SlimeLauncherOptionsInternal inherit(Map configs) { + return this.inherit(configs, this.getName()); } - default void inherit(Map configs, String name) { - var config = configs.get(name); - if (config == null) return; - - if (config.parents != null && !config.parents.isEmpty()) - config.parents.forEach(parent -> this.inherit(configs, parent)); - - if (config.main != null) - this.getMainClass().convention(config.main); - - if (config.args != null && !config.args.isEmpty()) - this.getArgs().convention(List.copyOf(config.args)); - - if (config.jvmArgs != null && !config.jvmArgs.isEmpty()) - this.jvmArgs(config.jvmArgs); - - this.getClient().set(config.client); - - if (config.buildAllProjects) - LOGGER.warn("WARNING: ForgeGradle 7 does not support the buildAllProjects feature."); - - if (config.env != null && !config.env.isEmpty()) - this.environment(config.env); - - if (config.props != null && !config.props.isEmpty()) - this.systemProperties(config.props); - } + SlimeLauncherOptionsInternal inherit(Map configs, String name); } diff --git a/src/main/java/net/minecraftforge/gradle/Util.java b/src/main/java/net/minecraftforge/gradle/Util.java index 249b4a757..61384764e 100644 --- a/src/main/java/net/minecraftforge/gradle/Util.java +++ b/src/main/java/net/minecraftforge/gradle/Util.java @@ -5,11 +5,11 @@ package net.minecraftforge.gradle; import net.minecraftforge.gradleutils.shared.SharedUtil; -import org.codehaus.groovy.runtime.InvokerHelper; import org.codehaus.groovy.runtime.StringGroovyMethods; import org.gradle.api.NamedDomainObjectSet; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.ModuleIdentifier; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.SourceSetContainer; import org.jetbrains.annotations.Nullable; @@ -24,19 +24,20 @@ static boolean isPresent(String c) { } static String dependencyToCamelCase(Dependency dependency) { + return dependencyToCamelCase(dependency.getGroup(), dependency.getName()); + } + + static String dependencyToCamelCase(ModuleIdentifier dependency) { + return dependencyToCamelCase(dependency.getGroup(), dependency.getName()); + } + + static String dependencyToCamelCase(@Nullable String group, String name) { var list = new ArrayList(3); - var group = dependency.getGroup(); if (group != null) list.addAll(Arrays.asList(group.split("\\."))); - list.add(dependency.getName()); - - try { - list.add(InvokerHelper.getProperty(dependency, "classifer").toString()); - } catch (Exception ignored) { - // No classifier, not a problem - } + list.add(name); var builder = new StringBuilder(64); for (var s : list) {