Make Forge tools compatible with config cache

This commit is contained in:
Juuz
2024-12-06 23:20:04 +02:00
parent 1837cd7d3c
commit 598ef19370
11 changed files with 234 additions and 137 deletions

View File

@@ -0,0 +1,141 @@
package dev.architectury.loom.forge.tool;
import java.util.Collection;
import java.util.List;
import javax.inject.Inject;
import org.apache.commons.io.output.NullOutputStream;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.logging.LogLevel;
import org.gradle.api.logging.configuration.ShowStacktrace;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Classpath;
import org.gradle.api.tasks.Input;
import org.gradle.process.ExecOperations;
import org.gradle.process.ExecResult;
import org.jetbrains.annotations.Nullable;
/**
* Contains helpers for executing Forge's command line tools
* with suppressed output streams to prevent annoying log spam.
*/
public abstract class ForgeToolExecutor {
@Inject
protected abstract ExecOperations getExecOperations();
public static boolean shouldShowVerboseStdout(Project project) {
// if running with INFO or DEBUG logging
return project.getGradle().getStartParameter().getLogLevel().compareTo(LogLevel.LIFECYCLE) < 0;
}
public static boolean shouldShowVerboseStderr(Project project) {
// if stdout is shown or stacktraces are visible so that errors printed to stderr show up
return shouldShowVerboseStdout(project) || project.getGradle().getStartParameter().getShowStacktrace() != ShowStacktrace.INTERNAL_EXCEPTIONS;
}
public static Settings getDefaultSettings(Project project) {
final Settings settings = project.getObjects().newInstance(Settings.class);
settings.getExecutable().set(JavaExecutableFetcher.getJavaToolchainExecutable(project));
settings.getShowVerboseStdout().set(shouldShowVerboseStdout(project));
settings.getShowVerboseStderr().set(shouldShowVerboseStderr(project));
return settings;
}
/**
* Executes an external Java process.
*
* <p>This method cannot be used during configuration.
* Use {@link ForgeToolValueSource#exec(Project, Action)} for those cases.
*
* @param project the project
* @param configurator the {@code javaexec} configuration action
* @return the execution result
*/
public static ExecResult exec(Project project, Action<? super Settings> configurator) {
final Settings settings = getDefaultSettings(project);
configurator.execute(settings);
return project.getObjects().newInstance(ForgeToolExecutor.class).exec(settings);
}
private ExecResult exec(Settings settings) {
return exec(getExecOperations(), settings);
}
public static ExecResult exec(ExecOperations execOperations, Settings settings) {
return execOperations.javaexec(spec -> {
final @Nullable String executable = settings.getExecutable().getOrNull();
if (executable != null) spec.setExecutable(executable);
spec.getMainClass().set(settings.getMainClass());
spec.setArgs(settings.getProgramArgs().get());
spec.setJvmArgs(settings.getJvmArgs().get());
spec.setClasspath(settings.getExecClasspath());
if (settings.getShowVerboseStdout().get()) {
spec.setStandardOutput(System.out);
} else {
spec.setStandardOutput(NullOutputStream.INSTANCE);
}
if (settings.getShowVerboseStderr().get()) {
spec.setErrorOutput(System.err);
} else {
spec.setErrorOutput(NullOutputStream.INSTANCE);
}
});
}
public interface Settings {
@Input
Property<String> getExecutable();
@Input
ListProperty<String> getProgramArgs();
@Input
ListProperty<String> getJvmArgs();
@Input
Property<String> getMainClass();
@Classpath
ConfigurableFileCollection getExecClasspath();
@Input
Property<Boolean> getShowVerboseStdout();
@Input
Property<Boolean> getShowVerboseStderr();
default void classpath(Object... paths) {
getExecClasspath().from(paths);
}
default void setClasspath(Object... paths) {
getExecClasspath().setFrom(paths);
}
default void args(String... args) {
getProgramArgs().addAll(args);
}
default void args(Collection<String> args) {
getProgramArgs().addAll(args);
}
default void setArgs(List<String> args) {
getProgramArgs().set(args);
}
default void jvmArgs(String... args) {
getJvmArgs().addAll(args);
}
default void jvmArgs(Collection<String> args) {
getJvmArgs().addAll(args);
}
}
}

View File

@@ -0,0 +1,46 @@
package dev.architectury.loom.forge.tool;
import javax.inject.Inject;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.provider.ValueSource;
import org.gradle.api.provider.ValueSourceParameters;
import org.gradle.api.tasks.Nested;
import org.gradle.process.ExecOperations;
import org.gradle.process.ExecResult;
public abstract class ForgeToolValueSource implements ValueSource<ExecResult, ForgeToolValueSource.Parameters> {
@Inject
protected abstract ExecOperations getExecOperations();
public static Provider<ExecResult> create(Project project, Action<? super ForgeToolExecutor.Settings> configurator) {
return project.getProviders().of(ForgeToolValueSource.class, spec -> {
ForgeToolExecutor.Settings settings = ForgeToolExecutor.getDefaultSettings(project);
configurator.execute(settings);
spec.getParameters().getSettings().set(settings);
});
}
/**
* Executes an external Java process during project configuration.
*
* @param project the project
* @param configurator an action that configures the exec settings
*/
public static void exec(Project project, Action<? super ForgeToolExecutor.Settings> configurator) {
create(project, configurator).get().rethrowFailure().assertNormalExitValue();
}
@Override
public ExecResult obtain() {
return ForgeToolExecutor.exec(getExecOperations(), getParameters().getSettings().get());
}
public interface Parameters extends ValueSourceParameters {
@Nested
Property<ForgeToolExecutor.Settings> getSettings();
}
}

View File

@@ -1,4 +1,4 @@
package dev.architectury.loom.forge;
package dev.architectury.loom.forge.tool;
public final class ForgeTools {
public static final String SIDE_STRIPPER = "net.minecraftforge:mergetool:1.1.6:fatjar";

View File

@@ -0,0 +1,31 @@
package dev.architectury.loom.forge.tool;
import javax.inject.Inject;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.Provider;
import org.gradle.jvm.toolchain.JavaLauncher;
import org.gradle.jvm.toolchain.JavaToolchainService;
import org.gradle.jvm.toolchain.JavaToolchainSpec;
public abstract class JavaExecutableFetcher {
@Inject
protected abstract JavaToolchainService getToolchainService();
public static Provider<String> getJavaToolchainExecutable(Project project) {
return project.provider(() -> {
final JavaExecutableFetcher fetcher = project.getObjects().newInstance(JavaExecutableFetcher.class);
final JavaPluginExtension java = project.getExtensions().getByType(JavaPluginExtension.class);
final JavaToolchainSpec toolchain = java.getToolchain();
if (!toolchain.getLanguageVersion().isPresent()) {
// Toolchain not configured, we'll use the runtime Java version.
return null;
}
final JavaLauncher launcher = fetcher.getToolchainService().launcherFor(toolchain).get();
return launcher.getExecutablePath().getAsFile().getAbsolutePath();
});
}
}

View File

@@ -42,6 +42,7 @@ import com.google.common.hash.Hashing;
import com.google.common.io.MoreFiles;
import dev.architectury.at.AccessTransformSet;
import dev.architectury.at.io.AccessTransformFormats;
import dev.architectury.loom.forge.tool.ForgeToolValueSource;
import dev.architectury.loom.util.TempFiles;
import org.gradle.api.Project;
import org.gradle.api.file.FileCollection;
@@ -59,7 +60,6 @@ import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.DependencyDownloader;
import net.fabricmc.loom.util.ExceptionUtil;
import net.fabricmc.loom.util.ForgeToolExecutor;
import net.fabricmc.loom.util.LoomVersions;
import net.fabricmc.loom.util.fmj.FabricModJson;
@@ -174,11 +174,11 @@ public class AccessTransformerJarProcessor implements MinecraftJarProcessor<Acce
configuration.apply(args);
ForgeToolExecutor.exec(project, spec -> {
ForgeToolValueSource.exec(project, spec -> {
spec.getMainClass().set(mainClass);
spec.setArgs(args);
spec.setClasspath(classpath);
}).rethrowFailure().assertNormalExitValue();
});
}
private static LoomVersions chooseAccessTransformer(Project project) {

View File

@@ -53,6 +53,7 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import de.oceanlabs.mcp.mcinjector.adaptors.ParameterAnnotationFixer;
import dev.architectury.loom.forge.UserdevConfig;
import dev.architectury.loom.forge.tool.ForgeToolValueSource;
import dev.architectury.loom.util.MappingOption;
import dev.architectury.loom.util.TempFiles;
import org.gradle.api.Project;
@@ -78,7 +79,6 @@ import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.DependencyDownloader;
import net.fabricmc.loom.util.FileSystemUtil;
import net.fabricmc.loom.util.ForgeToolExecutor;
import net.fabricmc.loom.util.MappingsProviderVerbose;
import net.fabricmc.loom.util.ThreadingUtils;
import net.fabricmc.loom.util.TinyRemapperHelper;
@@ -459,7 +459,7 @@ public class MinecraftPatchedProvider {
}
private void patchJars(Path clean, Path output, Path patches) {
ForgeToolExecutor.exec(project, spec -> {
ForgeToolValueSource.exec(project, spec -> {
UserdevConfig.BinaryPatcherConfig config = getExtension().getForgeUserdevProvider().getConfig().binpatcher();
final FileCollection download = DependencyDownloader.download(project, config.dependency());
spec.classpath(download);

View File

@@ -44,13 +44,14 @@ import com.google.common.base.Stopwatch;
import com.google.common.hash.Hashing;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import dev.architectury.loom.forge.tool.ForgeToolExecutor;
import dev.architectury.loom.forge.tool.ForgeToolValueSource;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.logging.LogLevel;
import org.gradle.api.logging.Logger;
import org.gradle.process.JavaExecSpec;
import org.jetbrains.annotations.Nullable;
import net.fabricmc.loom.LoomGradleExtension;
@@ -67,7 +68,6 @@ import net.fabricmc.loom.configuration.providers.forge.mcpconfig.steplogic.StepL
import net.fabricmc.loom.configuration.providers.forge.mcpconfig.steplogic.StripLogic;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.ForgeToolExecutor;
import net.fabricmc.loom.util.download.DownloadBuilder;
import net.fabricmc.loom.util.function.CollectionUtil;
import net.fabricmc.loom.util.gradle.GradleUtils;
@@ -368,8 +368,8 @@ public final class McpExecutor {
}
@Override
public void javaexec(Action<? super JavaExecSpec> configurator) {
ForgeToolExecutor.exec(project, configurator).rethrowFailure().assertNormalExitValue();
public void javaexec(Action<? super ForgeToolExecutor.Settings> configurator) {
ForgeToolValueSource.exec(project, configurator);
}
@Override

View File

@@ -31,9 +31,9 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import dev.architectury.loom.forge.tool.ForgeToolExecutor;
import org.gradle.api.Action;
import org.gradle.api.logging.Logger;
import org.gradle.process.JavaExecSpec;
import net.fabricmc.loom.configuration.providers.forge.ConfigValue;
import net.fabricmc.loom.util.download.DownloadBuilder;
@@ -64,7 +64,7 @@ public interface StepLogic {
Path downloadFile(String url) throws IOException;
Path downloadDependency(String notation);
DownloadBuilder downloadBuilder(String url);
void javaexec(Action<? super JavaExecSpec> configurator);
void javaexec(Action<? super ForgeToolExecutor.Settings> configurator);
Set<File> getMinecraftLibraries();
default List<String> resolve(List<ConfigValue> configValues) {

View File

@@ -41,6 +41,7 @@ import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import dev.architectury.loom.forge.tool.ForgeToolExecutor;
import dev.architectury.loom.util.MappingOption;
import org.apache.commons.io.output.NullOutputStream;
import org.cadixdev.lorenz.MappingSet;
@@ -57,7 +58,6 @@ import net.fabricmc.loom.task.GenerateSourcesTask;
import net.fabricmc.loom.util.DeletingFileVisitor;
import net.fabricmc.loom.util.DependencyDownloader;
import net.fabricmc.loom.util.FileSystemUtil;
import net.fabricmc.loom.util.ForgeToolExecutor;
import net.fabricmc.loom.util.LoomVersions;
import net.fabricmc.loom.util.SourceRemapper;
import net.fabricmc.loom.util.ThreadingUtils;

View File

@@ -37,7 +37,8 @@ import codechicken.diffpatch.cli.PatchOperation;
import codechicken.diffpatch.util.LoggingOutputStream;
import codechicken.diffpatch.util.PatchMode;
import com.google.common.base.Stopwatch;
import dev.architectury.loom.forge.ForgeTools;
import dev.architectury.loom.forge.tool.ForgeToolValueSource;
import dev.architectury.loom.forge.tool.ForgeTools;
import dev.architectury.loom.util.TempFiles;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.RegularFileProperty;
@@ -55,7 +56,6 @@ import net.fabricmc.loom.configuration.providers.forge.mcpconfig.steplogic.Const
import net.fabricmc.loom.configuration.sources.ForgeSourcesRemapper;
import net.fabricmc.loom.util.DependencyDownloader;
import net.fabricmc.loom.util.FileSystemUtil;
import net.fabricmc.loom.util.ForgeToolExecutor;
import net.fabricmc.loom.util.SourceRemapper;
import net.fabricmc.loom.util.service.ScopedServiceFactory;
import net.fabricmc.loom.util.service.ServiceFactory;
@@ -191,7 +191,7 @@ public abstract class GenerateForgePatchedSourcesTask extends AbstractLoomTask {
final FileCollection classpath = DependencyDownloader.download(getProject(), ForgeTools.SIDE_STRIPPER, false, true);
ForgeToolExecutor.exec(getProject(), spec -> {
ForgeToolValueSource.exec(getProject(), spec -> {
spec.setClasspath(classpath);
spec.args(
"--strip",

View File

@@ -1,121 +0,0 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2022-2024 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.util;
import javax.inject.Inject;
import org.apache.commons.io.output.NullOutputStream;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.logging.LogLevel;
import org.gradle.api.logging.configuration.ShowStacktrace;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.jvm.toolchain.JavaLauncher;
import org.gradle.jvm.toolchain.JavaToolchainService;
import org.gradle.jvm.toolchain.JavaToolchainSpec;
import org.gradle.process.ExecOperations;
import org.gradle.process.ExecResult;
import org.gradle.process.JavaExecSpec;
import org.jetbrains.annotations.Nullable;
/**
* Contains helpers for executing Forge's command line tools
* with suppressed output streams to prevent annoying log spam.
*/
public abstract class ForgeToolExecutor {
@Inject
protected abstract JavaToolchainService getToolchainService();
@Inject
protected abstract ExecOperations getExecOperations();
@Inject
protected abstract Project getProject();
public static boolean shouldShowVerboseStdout(Project project) {
// if running with INFO or DEBUG logging
return project.getGradle().getStartParameter().getLogLevel().compareTo(LogLevel.LIFECYCLE) < 0;
}
public static boolean shouldShowVerboseStderr(Project project) {
// if stdout is shown or stacktraces are visible so that errors printed to stderr show up
return shouldShowVerboseStdout(project) || project.getGradle().getStartParameter().getShowStacktrace() != ShowStacktrace.INTERNAL_EXCEPTIONS;
}
/**
* Executes a {@link ExecOperations#javaexec(Action) javaexec} action with suppressed output.
*
* @param project the project
* @param configurator the {@code javaexec} configuration action
* @return the execution result
*/
public static ExecResult exec(Project project, Action<? super JavaExecSpec> configurator) {
return project.getObjects().newInstance(ForgeToolExecutor.class)
.exec(configurator);
}
private ExecResult exec(Action<? super JavaExecSpec> configurator) {
final Project project = getProject();
return getExecOperations().javaexec(spec -> {
configurator.execute(spec);
if (shouldShowVerboseStdout(project)) {
spec.setStandardOutput(System.out);
} else {
spec.setStandardOutput(NullOutputStream.INSTANCE);
}
if (shouldShowVerboseStderr(project)) {
spec.setErrorOutput(System.err);
} else {
spec.setErrorOutput(NullOutputStream.INSTANCE);
}
// Use project toolchain for executing if possible.
// Note: This feature cannot be tested using the test kit since
// - Gradle disables native services in test kit environments.
// - The only resolver plugin I could find, foojay-resolver,
// requires the services for finding the OS architecture.
final @Nullable String executable = findJavaToolchainExecutable(project);
if (executable != null) {
spec.setExecutable(executable);
}
});
}
private @Nullable String findJavaToolchainExecutable(Project project) {
final JavaPluginExtension java = project.getExtensions().getByType(JavaPluginExtension.class);
final JavaToolchainSpec toolchain = java.getToolchain();
if (!toolchain.getLanguageVersion().isPresent()) {
// Toolchain not configured, we'll use the runtime Java version.
return null;
}
final JavaLauncher launcher = getToolchainService().launcherFor(toolchain).get();
return launcher.getExecutablePath().getAsFile().getAbsolutePath();
}
}