Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transform decompile step into a task that is customizable via specific settings #46

Merged
merged 5 commits into from
Dec 1, 2023
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
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` |

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
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
public SubsystemsExtension(Project project) {
this.project = project;

ProviderFactory providers = project.getProviders();
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
public Project getProject() {
return project;
}
}
Original file line number Diff line number Diff line change
@@ -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: trace, info, warn, error.
* Defaults to {@link DecompilerLogLevel#INFO}.
*/
@Input
@Optional
@DSLProperty
Property<DecompilerLogLevel> getLogLevel();

/**
* Allows additional JVM arguments to be added to the decompiler invocation.
*/
@Input
@Optional
@DSLProperty
ListProperty<String> getJvmArgs();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package net.neoforged.gradle.dsl.common.extensions.subsystems

enum DecompilerLogLevel {
TRACE,
INFO,
WARN,
ERROR
}
Original file line number Diff line number Diff line change
@@ -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();

}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -11,14 +12,21 @@
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.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;
import net.neoforged.gradle.dsl.common.runtime.tasks.RuntimeArguments;
import net.neoforged.gradle.dsl.common.runtime.tasks.tree.TaskTreeAdapter;
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;
Expand All @@ -31,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;
Expand All @@ -43,11 +52,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) {
Expand All @@ -67,6 +82,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");
Expand Down Expand Up @@ -117,7 +134,65 @@ 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 = getDecompilerLogLevelArg(settings.getLogLevel().getOrElse(DecompilerLogLevel.INFO), function.getVersion());

if (settings.getJvmArgs().isPresent()) {
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 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()));
task.getJvmArguments().addAll(function.getJvmArgs());
Expand Down