From 0ccc934f784f4fa557cae5b72b74bd48007b74ce Mon Sep 17 00:00:00 2001
From: Sebastian Hartte <shartte@users.noreply.github.com>
Date: Sat, 11 Nov 2023 19:20:57 +0100
Subject: [PATCH 1/5] Transform decompile step into a task that is customizable
 via specific settings.

---
 .../gradle/common/CommonProjectPlugin.java    |  3 +
 .../subsystems/SubsystemsExtension.java       | 21 ++++++
 .../extensions/subsystems/Decompiler.groovy   | 52 +++++++++++++++
 .../extensions/subsystems/Subsystems.groovy   | 21 ++++++
 .../gradle/dsl/common/tasks/Execute.groovy    |  2 -
 .../extensions/NeoFormRuntimeExtension.java   | 64 +++++++++++++++++--
 6 files changed, 157 insertions(+), 6 deletions(-)
 create mode 100644 common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/SubsystemsExtension.java
 create mode 100644 dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Decompiler.groovy
 create mode 100644 dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Subsystems.groovy

diff --git a/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java b/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java
index 493ee0db1..ed43589e7 100644
--- a/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java
+++ b/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java
@@ -6,6 +6,7 @@
 import net.neoforged.gradle.common.extensions.dependency.creation.ProjectBasedDependencyCreator;
 import net.neoforged.gradle.common.extensions.dependency.replacement.DependencyReplacementsExtension;
 import net.neoforged.gradle.common.extensions.repository.IvyDummyRepositoryExtension;
+import net.neoforged.gradle.common.extensions.subsystems.SubsystemsExtension;
 import net.neoforged.gradle.common.runs.ide.IdeRunIntegrationManager;
 import net.neoforged.gradle.common.runs.run.RunImpl;
 import net.neoforged.gradle.common.runtime.definition.CommonRuntimeDefinition;
@@ -20,6 +21,7 @@
 import net.neoforged.gradle.dsl.common.extensions.*;
 import net.neoforged.gradle.dsl.common.extensions.dependency.replacement.DependencyReplacement;
 import net.neoforged.gradle.dsl.common.extensions.repository.Repository;
+import net.neoforged.gradle.dsl.common.extensions.subsystems.Subsystems;
 import net.neoforged.gradle.dsl.common.runs.run.Run;
 import net.neoforged.gradle.dsl.common.runs.type.RunType;
 import net.neoforged.gradle.dsl.common.util.NamingConstants;
@@ -74,6 +76,7 @@ public void apply(Project project) {
 
         extensionManager.registerExtension("minecraft", Minecraft.class, (p) -> p.getObjects().newInstance(MinecraftExtension.class, p));
         extensionManager.registerExtension("mappings", Mappings.class, (p) -> p.getObjects().newInstance(MappingsExtension.class, p));
+        extensionManager.registerExtension("subsystems", Subsystems.class, (p) -> p.getObjects().newInstance(SubsystemsExtension.class, p));
 
         OfficialNamingChannelConfigurator.getInstance().configure(project);
 
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
new file mode 100644
index 000000000..eb3d9fa92
--- /dev/null
+++ b/common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/SubsystemsExtension.java
@@ -0,0 +1,21 @@
+package net.neoforged.gradle.common.extensions.subsystems;
+
+import net.minecraftforge.gdi.ConfigurableDSLElement;
+import net.neoforged.gradle.dsl.common.extensions.subsystems.Subsystems;
+import org.gradle.api.Project;
+
+import javax.inject.Inject;
+
+public abstract class SubsystemsExtension implements ConfigurableDSLElement<Subsystems>, Subsystems {
+    private final Project project;
+
+    @Inject
+    public SubsystemsExtension(Project project) {
+        this.project = project;
+    }
+
+    @Override
+    public Project getProject() {
+        return project;
+    }
+}
diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Decompiler.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Decompiler.groovy
new file mode 100644
index 000000000..41f8c4a72
--- /dev/null
+++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Decompiler.groovy
@@ -0,0 +1,52 @@
+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.ListProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.Optional
+
+/**
+ * Allows configuration of the decompiler used by NeoGradle.
+ */
+@CompileStatic
+interface Decompiler extends ConfigurableDSLElement<Decompiler> {
+
+    /**
+     * Allows the maximum memory provided to the decompiler to be overridden. Must be specified
+     * in the "123g" or "123m" form.
+     */
+    @Input
+    @Optional
+    @DSLProperty
+    Property<String> getMaxMemory();
+
+    /**
+     * Allows the maximum number of threads used by the decompiler to be constrained. By default, it will
+     * use all available threads.
+     */
+    @Input
+    @Optional
+    @DSLProperty
+    Property<Integer> getMaxThreads();
+
+    /**
+     * The log-level to use for the decompiler. Supported values: info, debug, warn, error.
+     * Defaults to info.
+     */
+    @Input
+    @Optional
+    @DSLProperty
+    Property<String> getLogLevel();
+
+    /**
+     * Allows additional JVM arguments to be added to the decompiler invocation.
+     */
+    @Input
+    @Optional
+    @DSLProperty
+    ListProperty<String> getJvmArgs();
+
+}
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
new file mode 100644
index 000000000..eddf2078e
--- /dev/null
+++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Subsystems.groovy
@@ -0,0 +1,21 @@
+package net.neoforged.gradle.dsl.common.extensions.subsystems
+
+import groovy.transform.CompileStatic
+import net.minecraftforge.gdi.BaseDSLElement
+import net.minecraftforge.gdi.annotations.DSLProperty
+import org.gradle.api.tasks.Nested
+
+/**
+ * Allows configuration of various NeoGradle subsystems.
+ */
+@CompileStatic
+interface Subsystems extends BaseDSLElement<Subsystems> {
+
+    /**
+     * @return Settings for the decompiler subsystem.
+     */
+    @Nested
+    @DSLProperty
+    Decompiler getDecompiler();
+
+}
diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/Execute.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/Execute.groovy
index 01a62a840..59875cb41 100644
--- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/Execute.groovy
+++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/Execute.groovy
@@ -129,8 +129,6 @@ interface Execute extends WithWorkspace, WithOutput, WithJavaVersion, ExecuteSpe
                 java.setWorkingDir(me.getOutputDirectory().get())
                 java.getMainClass().set(mainClass)
                 java.setStandardOutput(log_out)
-
-                java.setMaxHeapSize(String.format("%dm", Runtime.getRuntime().maxMemory().intdiv(1024 * 1024)))
             }).rethrowFailure().assertNormalExitValue()
         }
     }
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 ff754647b..7e84d99ba 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
@@ -3,6 +3,7 @@
 import com.google.common.collect.Iterators;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import net.minecraftforge.gdi.ConfigurableDSLElement;
 import net.neoforged.gradle.common.runtime.extensions.CommonRuntimeExtension;
 import net.neoforged.gradle.common.runtime.tasks.Execute;
 import net.neoforged.gradle.common.runtime.tasks.ListLibraries;
@@ -11,6 +12,8 @@
 import net.neoforged.gradle.dsl.common.extensions.Mappings;
 import net.neoforged.gradle.dsl.common.extensions.Minecraft;
 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.Subsystems;
 import net.neoforged.gradle.dsl.common.runtime.naming.TaskBuildingContext;
 import net.neoforged.gradle.dsl.common.runtime.tasks.Runtime;
 import net.neoforged.gradle.dsl.common.runtime.tasks.RuntimeArguments;
@@ -18,7 +21,11 @@
 import net.neoforged.gradle.dsl.common.tasks.ArtifactProvider;
 import net.neoforged.gradle.dsl.common.tasks.WithOutput;
 import net.neoforged.gradle.dsl.common.tasks.specifications.OutputSpecification;
-import net.neoforged.gradle.dsl.common.util.*;
+import net.neoforged.gradle.dsl.common.util.CommonRuntimeUtils;
+import net.neoforged.gradle.dsl.common.util.ConfigurationUtils;
+import net.neoforged.gradle.dsl.common.util.DistributionType;
+import net.neoforged.gradle.dsl.common.util.GameArtifact;
+import net.neoforged.gradle.dsl.common.util.NamingConstants;
 import net.neoforged.gradle.dsl.neoform.configuration.NeoFormConfigConfigurationSpecV1;
 import net.neoforged.gradle.dsl.neoform.configuration.NeoFormConfigConfigurationSpecV2;
 import net.neoforged.gradle.neoform.runtime.definition.NeoFormRuntimeDefinition;
@@ -43,11 +50,17 @@
 import javax.annotation.Nullable;
 import java.io.File;
 import java.io.FileNotFoundException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 @SuppressWarnings({"OptionalUsedAsFieldOrParameterType", "unused"}) // API Design
-public abstract class NeoFormRuntimeExtension extends CommonRuntimeExtension<NeoFormRuntimeSpecification, NeoFormRuntimeSpecification.Builder, NeoFormRuntimeDefinition> {
+public abstract class NeoFormRuntimeExtension extends CommonRuntimeExtension<NeoFormRuntimeSpecification, NeoFormRuntimeSpecification.Builder, NeoFormRuntimeDefinition> implements ConfigurableDSLElement<NeoFormRuntimeExtension> {
 
     @javax.inject.Inject
     public NeoFormRuntimeExtension(Project project) {
@@ -67,6 +80,8 @@ private static void configureMcpRuntimeTaskWithDefaults(NeoFormRuntimeSpecificat
     @Nullable
     private static TaskProvider<? extends WithOutput> createBuiltIn(final NeoFormRuntimeSpecification spec, NeoFormConfigConfigurationSpecV2 neoFormConfigV2, NeoFormConfigConfigurationSpecV1.Step step, final Map<String, TaskProvider<? extends WithOutput>> tasks, final Map<GameArtifact, TaskProvider<? extends WithOutput>> gameArtifactTaskProviders, final Optional<TaskProvider<? extends WithOutput>> adaptedInput) {
         switch (step.getType()) {
+            case "decompile":
+                return createDecompile(spec, step, neoFormConfigV2);
             case "downloadManifest":
                 return gameArtifactTaskProviders.computeIfAbsent(GameArtifact.LAUNCHER_MANIFEST, a -> {
                     throw new IllegalStateException("Launcher Manifest is required for this step, but was not provided");
@@ -117,7 +132,48 @@ private static TaskProvider<? extends WithOutput> createBuiltIn(final NeoFormRun
         return null;
     }
 
-    private static TaskProvider<? extends Runtime> createExecute(final NeoFormRuntimeSpecification spec, final NeoFormConfigConfigurationSpecV1.Step step, final NeoFormConfigConfigurationSpecV1.Function function) {
+    @NotNull
+    private static TaskProvider<? extends Runtime> createDecompile(NeoFormRuntimeSpecification spec, NeoFormConfigConfigurationSpecV1.Step step, NeoFormConfigConfigurationSpecV2 neoFormConfig) {
+        NeoFormConfigConfigurationSpecV1.Function function = neoFormConfig.getFunction(step.getType());
+        if (function == null) {
+            throw new IllegalArgumentException(String.format("Invalid NeoForm Config, Unknown function step type: %s File: %s", step.getType(), neoFormConfig));
+        }
+
+        // Filter out decompiler arguments that aren't related to its output (log-level and thread-count)
+        List<String> decompilerArgs = new ArrayList<>(function.getArgs());
+        decompilerArgs.removeIf(arg -> arg.startsWith("-log=") || arg.startsWith("-thr="));
+
+        // Retrieve the default memory size from the JVM arguments configured in NeoForm
+        String defaultMaxMemory = "4g";
+        List<String> jvmArgs = new ArrayList<>(function.getJvmArgs());
+        for (int i = jvmArgs.size() - 1; i >= 0; i--) {
+            if (jvmArgs.get(i).startsWith("-Xmx")) {
+                defaultMaxMemory = jvmArgs.get(i).substring("-Xmx".length());
+                jvmArgs.remove(i);
+            }
+        }
+
+        // Consider user-settings
+        Decompiler settings = spec.getProject().getExtensions().getByType(Subsystems.class).getDecompiler();
+        String maxMemory = settings.getMaxMemory().getOrElse(defaultMaxMemory);
+        int maxThreads = settings.getMaxThreads().getOrElse(0);
+        String logLevel = settings.getLogLevel().getOrElse("INFO");
+
+        jvmArgs.addAll(settings.getJvmArgs().get());
+        jvmArgs.add("-Xmx" + maxMemory);
+        if (maxThreads > 0) {
+            decompilerArgs.add(0, "-thr=" + maxThreads);
+        }
+        decompilerArgs.add(0, "-log=" + logLevel);
+
+        return spec.getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(spec, step.getName()), Execute.class, task -> {
+            task.getExecutingJar().set(ToolUtilities.resolveTool(task.getProject(), function.getVersion()));
+            task.getJvmArguments().addAll(jvmArgs);
+            task.getProgramArguments().addAll(decompilerArgs);
+        });
+    }
+
+    private TaskProvider<? extends Runtime> createExecute(final NeoFormRuntimeSpecification spec, final NeoFormConfigConfigurationSpecV1.Step step, final NeoFormConfigConfigurationSpecV1.Function function) {
         return spec.getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(spec, step.getName()), Execute.class, task -> {
             task.getExecutingJar().set(ToolUtilities.resolveTool(task.getProject(), function.getVersion()));
             task.getJvmArguments().addAll(function.getJvmArgs());

From df9326e4ea5be6dfac3fd286c086a89f041d5cc5 Mon Sep 17 00:00:00 2001
From: Sebastian Hartte <shartte@users.noreply.github.com>
Date: Sat, 25 Nov 2023 00:16:15 +0100
Subject: [PATCH 2/5] Add property binding and docs for it.

---
 README.md                                       | 17 ++++++++++++++++-
 .../subsystems/SubsystemsExtension.java         |  8 ++++++++
 .../extensions/NeoFormRuntimeExtension.java     |  4 +++-
 3 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index c91efc7e0..b852afb2c 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,21 @@ NeoGradle
 
 Minecraft mod development framework used by NeoForge and FML for the Gradle build system.
 
-For a quick start, see how the [NeoForge Mod Development Kit](https://github.com/neoforged/MDK) uses NeoGradle, or see our official [Documentation](https://docs.neoforged.net/neogradle/docs/).
+For a quick start, see how the [NeoForge Mod Development Kit](https://github.com/neoforged/MDK) uses NeoGradle, or see
+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).
+
+## Override Decompiler Settings
+
+The settings used by the decompiler when preparing Minecraft dependencies can be overridden
+using [Gradle properties](https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties).
+This can be useful to trade slower build-times for being able to run NeoGradle on lower-end machines.
+
+| Property                                     | Description                                                                                                                |
+|----------------------------------------------|----------------------------------------------------------------------------------------------------------------------------|
+| `neogradle.subsystems.decompiler.maxMemory`  | How much heap memory is given to the decompiler. Can be specified either in gigabyte (`4g`) or megabyte (`4096m`).         |
+| `neogradle.subsystems.decompiler.maxThreads` | By default the decompiler uses all available CPU cores. This setting can be used to limit it to a given number of threads. |
+| `neogradle.subsystems.decompiler.logLevel`   | Can be used to override the [decompiler loglevel](https://vineflower.org/usage/#cmdoption-log).                            |
+| `neogradle.subsystems.decompiler.jvmArgs`    | Pass arbitrary JVM arguments to the decompiler. I.e. `-XX:+HeapDumpOnOutOfMemoryError`                                     |
+
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 eb3d9fa92..2a2f008a5 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
@@ -3,8 +3,10 @@
 import net.minecraftforge.gdi.ConfigurableDSLElement;
 import net.neoforged.gradle.dsl.common.extensions.subsystems.Subsystems;
 import org.gradle.api.Project;
+import org.gradle.api.provider.ProviderFactory;
 
 import javax.inject.Inject;
+import java.util.Arrays;
 
 public abstract class SubsystemsExtension implements ConfigurableDSLElement<Subsystems>, Subsystems {
     private final Project project;
@@ -12,6 +14,12 @@ public abstract class SubsystemsExtension implements ConfigurableDSLElement<Subs
     @Inject
     public SubsystemsExtension(Project project) {
         this.project = project;
+
+        ProviderFactory providers = project.getProviders();
+        getDecompiler().getMaxMemory().convention(providers.gradleProperty("neogradle.subsystems.decompiler.maxMemory"));
+        getDecompiler().getMaxThreads().convention(providers.gradleProperty("neogradle.subsystems.decompiler.maxThreads").map(Integer::parseUnsignedInt));
+        getDecompiler().getLogLevel().convention(providers.gradleProperty("neogradle.subsystems.decompiler.logLevel"));
+        getDecompiler().getJvmArgs().convention(providers.gradleProperty("neogradle.subsystems.decompiler.jvmArgs").map(s -> Arrays.asList(s.split("\\s+"))));
     }
 
     @Override
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 7e84d99ba..4b4c06d77 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
@@ -159,7 +159,9 @@ private static TaskProvider<? extends Runtime> createDecompile(NeoFormRuntimeSpe
         int maxThreads = settings.getMaxThreads().getOrElse(0);
         String logLevel = settings.getLogLevel().getOrElse("INFO");
 
-        jvmArgs.addAll(settings.getJvmArgs().get());
+        if (settings.getJvmArgs().isPresent()) {
+            jvmArgs.addAll(settings.getJvmArgs().get());
+        }
         jvmArgs.add("-Xmx" + maxMemory);
         if (maxThreads > 0) {
             decompilerArgs.add(0, "-thr=" + maxThreads);

From b84c84185c76754f257a2f8f6f766d31b4ddfb6b Mon Sep 17 00:00:00 2001
From: Sebastian Hartte <shartte@users.noreply.github.com>
Date: Wed, 29 Nov 2023 22:29:50 +0100
Subject: [PATCH 3/5] Extract property prefix, use an enum for loglevel.

---
 .../subsystems/SubsystemsExtension.java       | 18 ++++++++++++++----
 .../extensions/subsystems/Decompiler.groovy   |  4 ++--
 .../subsystems/DecompilerLogLevel.groovy      |  8 ++++++++
 .../extensions/NeoFormRuntimeExtension.java   | 19 ++++++++++++++++++-
 4 files changed, 42 insertions(+), 7 deletions(-)
 create mode 100644 dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/DecompilerLogLevel.groovy

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 2a2f008a5..4864fafd8 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,14 +1,18 @@
 package net.neoforged.gradle.common.extensions.subsystems;
 
 import net.minecraftforge.gdi.ConfigurableDSLElement;
+import net.neoforged.gradle.dsl.common.extensions.subsystems.DecompilerLogLevel;
 import net.neoforged.gradle.dsl.common.extensions.subsystems.Subsystems;
+import org.gradle.api.GradleException;
 import org.gradle.api.Project;
 import org.gradle.api.provider.ProviderFactory;
 
 import javax.inject.Inject;
 import java.util.Arrays;
+import java.util.Locale;
 
 public abstract class SubsystemsExtension implements ConfigurableDSLElement<Subsystems>, Subsystems {
+    private static final String PROPERTY_PREFIX = "neogradle.subsystems.";
     private final Project project;
 
     @Inject
@@ -16,10 +20,16 @@ public SubsystemsExtension(Project project) {
         this.project = project;
 
         ProviderFactory providers = project.getProviders();
-        getDecompiler().getMaxMemory().convention(providers.gradleProperty("neogradle.subsystems.decompiler.maxMemory"));
-        getDecompiler().getMaxThreads().convention(providers.gradleProperty("neogradle.subsystems.decompiler.maxThreads").map(Integer::parseUnsignedInt));
-        getDecompiler().getLogLevel().convention(providers.gradleProperty("neogradle.subsystems.decompiler.logLevel"));
-        getDecompiler().getJvmArgs().convention(providers.gradleProperty("neogradle.subsystems.decompiler.jvmArgs").map(s -> Arrays.asList(s.split("\\s+"))));
+        getDecompiler().getMaxMemory().convention(providers.gradleProperty(PROPERTY_PREFIX + "decompiler.maxMemory"));
+        getDecompiler().getMaxThreads().convention(providers.gradleProperty(PROPERTY_PREFIX + "decompiler.maxThreads").map(Integer::parseUnsignedInt));
+        getDecompiler().getLogLevel().convention(providers.gradleProperty(PROPERTY_PREFIX + "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(providers.gradleProperty(PROPERTY_PREFIX + "decompiler.jvmArgs").map(s -> Arrays.asList(s.split("\\s+"))));
     }
 
     @Override
diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Decompiler.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Decompiler.groovy
index 41f8c4a72..952630cdc 100644
--- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Decompiler.groovy
+++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Decompiler.groovy
@@ -34,12 +34,12 @@ interface Decompiler extends ConfigurableDSLElement<Decompiler> {
 
     /**
      * The log-level to use for the decompiler. Supported values: info, debug, warn, error.
-     * Defaults to info.
+     * Defaults to {@link DecompilerLogLevel#INFO}.
      */
     @Input
     @Optional
     @DSLProperty
-    Property<String> getLogLevel();
+    Property<DecompilerLogLevel> getLogLevel();
 
     /**
      * Allows additional JVM arguments to be added to the decompiler invocation.
diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/DecompilerLogLevel.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/DecompilerLogLevel.groovy
new file mode 100644
index 000000000..9e42283fe
--- /dev/null
+++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/DecompilerLogLevel.groovy
@@ -0,0 +1,8 @@
+package net.neoforged.gradle.dsl.common.extensions.subsystems
+
+enum DecompilerLogLevel {
+    TRACE,
+    INFO,
+    WARN,
+    ERROR
+}
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 4b4c06d77..f2d01227f 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
@@ -13,6 +13,7 @@
 import net.neoforged.gradle.dsl.common.extensions.Minecraft;
 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.Subsystems;
 import net.neoforged.gradle.dsl.common.runtime.naming.TaskBuildingContext;
 import net.neoforged.gradle.dsl.common.runtime.tasks.Runtime;
@@ -38,6 +39,7 @@
 import net.neoforged.gradle.neoform.util.NeoFormRuntimeUtils;
 import net.neoforged.gradle.util.CopyingFileTreeVisitor;
 import org.apache.commons.lang3.StringUtils;
+import org.gradle.api.GradleException;
 import org.gradle.api.Project;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.Dependency;
@@ -157,7 +159,7 @@ private static TaskProvider<? extends Runtime> createDecompile(NeoFormRuntimeSpe
         Decompiler settings = spec.getProject().getExtensions().getByType(Subsystems.class).getDecompiler();
         String maxMemory = settings.getMaxMemory().getOrElse(defaultMaxMemory);
         int maxThreads = settings.getMaxThreads().getOrElse(0);
-        String logLevel = settings.getLogLevel().getOrElse("INFO");
+        String logLevel = getDecompilerLogLevelArg(settings.getLogLevel().getOrElse(DecompilerLogLevel.INFO), function.getVersion());
 
         if (settings.getJvmArgs().isPresent()) {
             jvmArgs.addAll(settings.getJvmArgs().get());
@@ -175,6 +177,21 @@ private static TaskProvider<? extends Runtime> createDecompile(NeoFormRuntimeSpe
         });
     }
 
+    private static String getDecompilerLogLevelArg(DecompilerLogLevel logLevel, String version) {
+        switch (logLevel) {
+            case TRACE:
+                return "trace";
+            case INFO:
+                return "info";
+            case WARN:
+                return "warn";
+            case ERROR:
+                return "error";
+            default:
+                throw new GradleException("LogLevel " + logLevel + " not supported by " + version);
+        }
+    }
+
     private TaskProvider<? extends Runtime> createExecute(final NeoFormRuntimeSpecification spec, final NeoFormConfigConfigurationSpecV1.Step step, final NeoFormConfigConfigurationSpecV1.Function function) {
         return spec.getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(spec, step.getName()), Execute.class, task -> {
             task.getExecutingJar().set(ToolUtilities.resolveTool(task.getProject(), function.getVersion()));

From 661a421ce507751715553d4e9e00a3c1edcf63e5 Mon Sep 17 00:00:00 2001
From: shartte <shartte@users.noreply.github.com>
Date: Wed, 29 Nov 2023 22:46:14 +0100
Subject: [PATCH 4/5] Update
 dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Decompiler.groovy

Co-authored-by: Matyrobbrt <65940752+Matyrobbrt@users.noreply.github.com>
---
 .../gradle/dsl/common/extensions/subsystems/Decompiler.groovy   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Decompiler.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Decompiler.groovy
index 952630cdc..7afd7420b 100644
--- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Decompiler.groovy
+++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Decompiler.groovy
@@ -33,7 +33,7 @@ interface Decompiler extends ConfigurableDSLElement<Decompiler> {
     Property<Integer> getMaxThreads();
 
     /**
-     * The log-level to use for the decompiler. Supported values: info, debug, warn, error.
+     * The log-level to use for the decompiler. Supported values: trace, info, warn, error.
      * Defaults to {@link DecompilerLogLevel#INFO}.
      */
     @Input

From 07f75a0eaa05c7cd7e0a17cbde4b1d0b5f64f385 Mon Sep 17 00:00:00 2001
From: shartte <shartte@users.noreply.github.com>
Date: Wed, 29 Nov 2023 22:46:18 +0100
Subject: [PATCH 5/5] Update
 dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Subsystems.groovy

Co-authored-by: Matyrobbrt <65940752+Matyrobbrt@users.noreply.github.com>
---
 .../gradle/dsl/common/extensions/subsystems/Subsystems.groovy   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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 eddf2078e..ead229a57 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
@@ -12,7 +12,7 @@ import org.gradle.api.tasks.Nested
 interface Subsystems extends BaseDSLElement<Subsystems> {
 
     /**
-     * @return Settings for the decompiler subsystem.
+     * @return settings for the decompiler subsystem
      */
     @Nested
     @DSLProperty