From 89d4fd8af05fdf56a9e9503d5045b255ba28ab48 Mon Sep 17 00:00:00 2001 From: Marc Hermans Date: Sun, 3 Nov 2024 22:03:47 +0100 Subject: [PATCH] Adapt to dynamically configurable tools --- .../subsystems/SubsystemsExtension.java | 15 + .../ApplyOfficialMappingsToCompiledJar.java | 3 +- .../UnapplyOfficialMappingsToCompiledJar.java | 39 -- .../tasks/BinaryAccessTransformer.java | 4 +- .../gradle/common/util/ToolUtilities.java | 38 ++ .../common/extensions/subsystems/Tools.groovy | 54 +++ .../gradle/dsl/common/util/Constants.groovy | 25 +- .../platform/extensions/LibraryManager.groovy | 42 ++ .../platform/model/InstallerProfile.groovy | 98 ++--- .../gradle/dsl/platform/model/Library.groovy | 8 +- .../runtime/spec/VanillaSpecification.java | 24 -- .../platform/InstallerProfileTests.groovy | 377 ++++++++++++++++++ .../platform/PlatformProjectPlugin.java | 5 +- .../extensions/DynamicProjectExtension.java | 29 +- .../extensions/LibraryManagerImpl.java | 135 +++++++ .../runtime/tasks/GenerateBinaryPatches.java | 4 +- .../dependency/VanillaDependencyManager.java | 3 - .../extensions/VanillaRuntimeExtension.java | 10 - .../spec/VanillaRuntimeSpecification.java | 120 +----- .../vanilla/runtime/steps/DecompileStep.java | 7 +- 20 files changed, 732 insertions(+), 308 deletions(-) delete mode 100644 common/src/main/java/net/neoforged/gradle/common/runtime/naming/tasks/UnapplyOfficialMappingsToCompiledJar.java create mode 100644 dsl/platform/src/main/groovy/net/neoforged/gradle/dsl/platform/extensions/LibraryManager.groovy create mode 100644 platform/src/functionalTest/groovy/net/neoforged/gradle/platform/InstallerProfileTests.groovy create mode 100644 platform/src/main/java/net/neoforged/gradle/platform/extensions/LibraryManagerImpl.java 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 80cf153b9..3f8eef7aa 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 @@ -67,6 +67,21 @@ private void configureToolsDefaults() { tools.getDevLogin().convention( getStringProperty("tools.devLogin", DEVLOGIN_TOOL_ARTIFACT) ); + tools.getBinaryPatcher().convention( + getStringProperty("tools.binaryPatcher", BINPARCHER_TOOL_ARTIFACT) + ); + tools.getAccessTransformer().convention( + getStringProperty("tools.accessTransformer", ACCESSTRANSFORMER_TOOL_ARTIFACT) + ); + tools.getAutoRenamingTool().convention( + getStringProperty("tools.autoRenamingTool", FART_TOOL_ARTIFACT) + ); + tools.getInstallerTools().convention( + getStringProperty("tools.installerTools", INSTALLERTOOLS_TOOL_ARTIFACT) + ); + tools.getJarSplitter().convention( + getStringProperty("tools.jarSplitter", JARSPLITTER_TOOL_ARTIFACT) + ); RenderDocTools renderDocTools = tools.getRenderDoc(); renderDocTools.getRenderDocPath().convention( diff --git a/common/src/main/java/net/neoforged/gradle/common/runtime/naming/tasks/ApplyOfficialMappingsToCompiledJar.java b/common/src/main/java/net/neoforged/gradle/common/runtime/naming/tasks/ApplyOfficialMappingsToCompiledJar.java index 0d01d0915..5b90e4963 100644 --- a/common/src/main/java/net/neoforged/gradle/common/runtime/naming/tasks/ApplyOfficialMappingsToCompiledJar.java +++ b/common/src/main/java/net/neoforged/gradle/common/runtime/naming/tasks/ApplyOfficialMappingsToCompiledJar.java @@ -4,6 +4,7 @@ import net.neoforged.gradle.common.runtime.tasks.DefaultExecute; import net.neoforged.gradle.common.util.ToolUtilities; import net.neoforged.gradle.dsl.common.extensions.MinecraftArtifactCache; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Tools; import net.neoforged.gradle.dsl.common.tasks.WithOutput; import net.neoforged.gradle.dsl.common.util.Constants; import net.neoforged.gradle.dsl.common.util.DistributionType; @@ -24,7 +25,7 @@ public abstract class ApplyOfficialMappingsToCompiledJar extends DefaultExecute public ApplyOfficialMappingsToCompiledJar() { super(); - getExecutingJar().set(ToolUtilities.resolveTool(getProject(), Constants.FART)); + getExecutingJar().fileProvider(ToolUtilities.resolveTool(getProject(), Tools::getAutoRenamingTool)); getProgramArguments().set(getShouldReverseMappings().map(shouldReverse -> { final List result = Lists.newArrayList(RenameConstants.DEFAULT_PROGRAMM_ARGS); if (shouldReverse) { diff --git a/common/src/main/java/net/neoforged/gradle/common/runtime/naming/tasks/UnapplyOfficialMappingsToCompiledJar.java b/common/src/main/java/net/neoforged/gradle/common/runtime/naming/tasks/UnapplyOfficialMappingsToCompiledJar.java deleted file mode 100644 index 787df5b50..000000000 --- a/common/src/main/java/net/neoforged/gradle/common/runtime/naming/tasks/UnapplyOfficialMappingsToCompiledJar.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.neoforged.gradle.common.runtime.naming.tasks; - -import com.google.common.collect.Lists; -import net.neoforged.gradle.common.runtime.tasks.DefaultExecute; -import net.neoforged.gradle.common.util.ToolUtilities; -import net.neoforged.gradle.dsl.common.extensions.MinecraftArtifactCache; -import net.neoforged.gradle.dsl.common.util.Constants; -import net.neoforged.gradle.dsl.common.util.DistributionType; -import org.gradle.api.file.RegularFileProperty; -import org.gradle.api.provider.Property; -import org.gradle.api.tasks.CacheableTask; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputFile; -import org.gradle.api.tasks.PathSensitive; -import org.gradle.api.tasks.PathSensitivity; - -@CacheableTask -public abstract class UnapplyOfficialMappingsToCompiledJar extends DefaultExecute { - - public UnapplyOfficialMappingsToCompiledJar() { - getExecutingJar().set(ToolUtilities.resolveTool(getProject(), Constants.SPECIALSOURCE)); - getProgramArguments().set(Lists.newArrayList("--in-jar", "{input}", "--out-jar", "{output}", "--srg-in", "{mappings}", "--live", "-r")); - getMappings().fileProvider(getMinecraftVersion().map(minecraftVersion -> getProject().getExtensions().getByType(MinecraftArtifactCache.class).cacheVersionMappings(minecraftVersion, DistributionType.CLIENT))); - - getArguments().putRegularFile("input", getInput()); - getArguments().putRegularFile("mappings", getMappings()); - } - - @Input - public abstract Property getMinecraftVersion(); - - @InputFile - @PathSensitive(PathSensitivity.NONE) - public abstract RegularFileProperty getMappings(); - - @InputFile - @PathSensitive(PathSensitivity.NONE) - public abstract RegularFileProperty getInput(); -} diff --git a/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/BinaryAccessTransformer.java b/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/BinaryAccessTransformer.java index f69a70c71..a0be14593 100644 --- a/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/BinaryAccessTransformer.java +++ b/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/BinaryAccessTransformer.java @@ -2,6 +2,8 @@ import com.google.common.collect.Lists; import net.neoforged.gradle.common.util.ToolUtilities; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Subsystems; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Tools; import net.neoforged.gradle.dsl.common.util.Constants; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; @@ -22,7 +24,7 @@ public BinaryAccessTransformer() { setDescription("Runs the access transformer on the decompiled sources."); - getExecutingJar().set(ToolUtilities.resolveTool(getProject(), Constants.ACCESSTRANSFORMER)); + getExecutingJar().fileProvider(ToolUtilities.resolveTool(getProject(), Tools::getAccessTransformer)); getRuntimeProgramArguments().convention( getInputFile().map(inputFile -> { final List args = Lists.newArrayList(); diff --git a/common/src/main/java/net/neoforged/gradle/common/util/ToolUtilities.java b/common/src/main/java/net/neoforged/gradle/common/util/ToolUtilities.java index df57348cb..de8c5befb 100644 --- a/common/src/main/java/net/neoforged/gradle/common/util/ToolUtilities.java +++ b/common/src/main/java/net/neoforged/gradle/common/util/ToolUtilities.java @@ -1,11 +1,23 @@ package net.neoforged.gradle.common.util; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Subsystems; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Tools; import net.neoforged.gradle.util.ModuleDependencyUtils; import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.ResolvedArtifact; +import org.gradle.api.artifacts.dsl.Dependencies; +import org.gradle.api.artifacts.dsl.DependencyCollector; +import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.artifacts.result.ResolvedArtifactResult; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Provider; import java.io.File; +import java.util.List; +import java.util.function.Function; import java.util.function.Supplier; public class ToolUtilities { @@ -14,6 +26,32 @@ private ToolUtilities() { throw new IllegalStateException("Tried to create utility class!"); } + public static Provider resolveTool(final Project project, final Function> tool) { + return resolveTool( + project, + tool.apply( + project.getExtensions().getByType(Subsystems.class).getTools() + ) + ); + } + + public static Provider resolveTool(final Project project, final Provider tool) { + //We use an anonymous dependency collector here so that we can convert the tool coordinate + //to a file outside the scope of the provider itself. + //If we were to use the provider directly, for example via map, the lambda would need to capture + //the project that converts the string to a dependency. + //This breaks the configuration cache as Projects can not be serialized. + final DependencyCollector collector = project.getObjects().dependencyCollector(); + collector.add(tool.map(project.getDependencies()::create)); + final Configuration config = ConfigurationUtils.temporaryUnhandledConfiguration( + project.getConfigurations(), + "Tool" + ); + config.fromDependencyCollector(collector); + return config.getIncoming().getArtifacts().getResolvedArtifacts().map(a -> a.iterator().next()) + .map(ResolvedArtifactResult::getFile); + } + public static File resolveTool(final Project project, final String tool) { return resolveTool(() -> ConfigurationUtils.temporaryUnhandledConfiguration( project.getConfigurations(), diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Tools.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Tools.groovy index 5536a44b4..c97e37987 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Tools.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Tools.groovy @@ -40,4 +40,58 @@ interface Tools extends ConfigurableDSLElement { */ @Nested RenderDocTools getRenderDoc(); + + /** + * Artifact coordinates for the binary patcher. + * Used by dynamic projects to create and use binary patches. + */ + @Input + @Optional + @DSLProperty + Property getBinaryPatcher(); + + /** + * Artifact coordinates for the binary access transformer tool. + * Used by the vanilla subsystem to apply access transformers to binary classes. + */ + @Input + @Optional + @DSLProperty + Property getAccessTransformer(); + + /** + * Artifact coordinates for the AutoRenamingTool. + * Used by different subsystems to rename classes in jars. + */ + @Input + @Optional + @DSLProperty + Property getAutoRenamingTool(); + + /** + * Artifact coordinates for the NeoGradle decompiler. + * Used by the vanilla subsystem to decompile the game. + */ + @Input + @Optional + @DSLProperty + Property getDecompiler(); + + /** + * Artifact coordinates for the installer tools. + * Used by the platform to configure different tooling in production and userdev. + */ + @Input + @Optional + @DSLProperty + Property getInstallerTools(); + + /** + * Artifact coordinates for the jar splitter. + * Used by the platform to split jars into smaller jars. + */ + @Input + @Optional + @DSLProperty + Property getJarSplitter(); } \ No newline at end of file diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy index a511811e2..c23f3952b 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy @@ -4,26 +4,6 @@ import groovy.transform.CompileStatic @CompileStatic class Constants { - - public static final String BINPATCHER_VERSION = "1.1.1"; - public static final String BINPATCHER_VERSION_INTERPOLATION = "net.minecraftforge:binarypatcher:%s:fatjar"; - public static final String BINPATCHER = String.format(BINPATCHER_VERSION_INTERPOLATION, BINPATCHER_VERSION); - public static final String ACCESSTRANSFORMER_VERSION = "10.0.+"; - public static final String ACCESSTRANSFORMER_VERSION_INTERPOLATION = "net.neoforged.accesstransformers:at-cli:%s:fatjar"; - public static final String ACCESSTRANSFORMER = String.format(ACCESSTRANSFORMER_VERSION_INTERPOLATION, ACCESSTRANSFORMER_VERSION); - public static final String SPECIALSOURCE = "net.md-5:SpecialSource:1.11.0:shaded"; - public static final String FART_VERSION = "2.0.3"; - public static final String FART_ARTIFACT_INTERPOLATION = "net.neoforged:AutoRenamingTool:%s:all"; - public static final String FART = String.format(FART_ARTIFACT_INTERPOLATION, FART_VERSION); - public static final String INSTALLERTOOLS_VERSION = '2.1.2' - public static final String INSTALLERTOOLS = "net.neoforged.installertools:installertools:${INSTALLERTOOLS_VERSION}" - public static final String JARSPLITTER = "net.neoforged.installertools:jarsplitter:${INSTALLERTOOLS_VERSION}" - public static final String BINARYPATCHER = "net.neoforged.installertools:binarypatcher:${INSTALLERTOOLS_VERSION}" - - public static final String VINEFLOWER_VERSION = "1.9.3"; - public static final String VINEFLOWER_ARTIFACT_INTERPOLATION = "org.vineflower:vineflower:%s"; - public static final String VINEFLOWER = String.format(VINEFLOWER_ARTIFACT_INTERPOLATION, VINEFLOWER_VERSION); - public static final String DEFAULT_PARCHMENT_GROUP = "org.parchmentmc.data" public static final String DEFAULT_PARCHMENT_ARTIFACT_PREFIX = "parchment-" public static final String DEFAULT_PARCHMENT_MAVEN_URL = "https://maven.parchmentmc.org/" @@ -31,6 +11,11 @@ class Constants { public static final String DEVLOGIN_TOOL_ARTIFACT = "net.covers1624:DevLogin:0.1.0.4" public static final String RENDERNURSE_TOOL_ARTIFACT = "net.neoforged:render-nurse:0.0.12"; public static final String DEVLOGIN_MAIN_CLASS = "net.covers1624.devlogin.DevLogin" + public static final String BINPARCHER_TOOL_ARTIFACT = "net.neoforged.installertools:binarypatcher:2.1.7:fatjar" + public static final String ACCESSTRANSFORMER_TOOL_ARTIFACT = "net.neoforged.accesstransformers:at-cli:11.0.1:fatjar" + public static final String FART_TOOL_ARTIFACT = "net.neoforged:AutoRenamingTool:2.0.4:all" + public static final String INSTALLERTOOLS_TOOL_ARTIFACT = "net.neoforged.installertools:installertools:2.1.7" + public static final String JARSPLITTER_TOOL_ARTIFACT = "net.neoforged.installertools:jarsplitter:2.1.7" public static final String DEFAULT_RECOMPILER_MAX_MEMORY = "1g" diff --git a/dsl/platform/src/main/groovy/net/neoforged/gradle/dsl/platform/extensions/LibraryManager.groovy b/dsl/platform/src/main/groovy/net/neoforged/gradle/dsl/platform/extensions/LibraryManager.groovy new file mode 100644 index 000000000..aa1a434f5 --- /dev/null +++ b/dsl/platform/src/main/groovy/net/neoforged/gradle/dsl/platform/extensions/LibraryManager.groovy @@ -0,0 +1,42 @@ +package net.neoforged.gradle.dsl.platform.extensions + +import net.neoforged.gradle.dsl.platform.model.Library +import org.gradle.api.provider.Provider + +/** + * Defines a manager for libraries. + */ +interface LibraryManager { + + /** + * Gets the classpath of a library. + * + * @param library The library to get the classpath of. + * @return The classpath of the library. + */ + Provider> getClasspathOf(Provider library); + + /** + * Gets the libraries that a library depends on. + * + * @param library The library to get the dependencies of. + * @return The dependencies of the library. + */ + Provider> getLibrariesOf(Provider library); + + /** + * Gets the classpath of a library. + * + * @param library The library to get the classpath of. + * @return The classpath of the library. + */ + Provider> getClasspathOf(String library); + + /** + * Gets the libraries that a library depends on. + * + * @param library The library to get the dependencies of. + * @return The dependencies of the library. + */ + Provider> getLibrariesOf(String library); +} \ No newline at end of file diff --git a/dsl/platform/src/main/groovy/net/neoforged/gradle/dsl/platform/model/InstallerProfile.groovy b/dsl/platform/src/main/groovy/net/neoforged/gradle/dsl/platform/model/InstallerProfile.groovy index 827f7fd5f..5fe27841e 100644 --- a/dsl/platform/src/main/groovy/net/neoforged/gradle/dsl/platform/model/InstallerProfile.groovy +++ b/dsl/platform/src/main/groovy/net/neoforged/gradle/dsl/platform/model/InstallerProfile.groovy @@ -12,6 +12,9 @@ import groovy.transform.CompileStatic import net.minecraftforge.gdi.ConfigurableDSLElement import net.minecraftforge.gdi.annotations.ClosureEquivalent import net.minecraftforge.gdi.annotations.DSLProperty +import net.neoforged.gradle.dsl.common.extensions.subsystems.Subsystems +import net.neoforged.gradle.dsl.common.extensions.subsystems.Tools +import net.neoforged.gradle.dsl.platform.extensions.LibraryManager import net.neoforged.gradle.dsl.platform.util.LibraryCollector import net.neoforged.gradle.util.ModuleDependencyUtils import org.apache.commons.io.FilenameUtils @@ -36,6 +39,7 @@ import org.jetbrains.annotations.Nullable import javax.inject.Inject import java.lang.reflect.Type +import java.util.function.Function import java.util.stream.Collectors import static net.neoforged.gradle.dsl.common.util.PropertyUtils.deserializeBool @@ -142,7 +146,7 @@ abstract class InstallerProfile implements ConfigurableDSLElement toolLibrariesAdded = new HashSet<>(); + private final Set toolLibrariesAdded = new HashSet<>() void data(String key, @Nullable String client, @Nullable String server) { getData().put(key, getObjectFactory().newInstance(DataFile.class).configure { DataFile it -> @@ -167,86 +171,32 @@ abstract class InstallerProfile implements ConfigurableDSLElement getProcessors(); - private NamedDomainObjectProvider getOrCreateConfigurationForTool(Project project, String tool) { - var configName = "neoForgeInstallerTool" + ModuleDependencyUtils.toConfigurationName(tool) - try { - return project.configurations.named(configName) - } catch (UnknownDomainObjectException ignored) { - return project.configurations.register(configName, (Configuration spec) -> { - spec.canBeConsumed = false - spec.canBeResolved = true - spec.dependencies.add(project.getDependencies().create(tool)) - }) - } - } - - private static Provider> gatherLibrariesFromConfiguration(Project project, Provider configurationProvider) { - var repositoryUrls = project.getRepositories() - .withType(MavenArtifactRepository).stream().map { it.url }.collect(Collectors.toList()) - var logger = project.logger - - var objectFactory = project.objects - - // We use a property because it is *not* re-evaluated when queried, while a normal provider is - var property = project.objects.setProperty(Library.class) - property.set(configurationProvider.flatMap { config -> - logger.info("Finding download URLs for configuration ${config.name}") - config.incoming.artifacts.resolvedArtifacts.map { artifacts -> - var libraryCollector = new LibraryCollector(objectFactory, repositoryUrls, logger) - - for (ResolvedArtifactResult resolvedArtifact in artifacts) { - libraryCollector.visit(resolvedArtifact) - } - - libraryCollector.getLibraries() - } - }) - property.finalizeValueOnRead() - property.disallowChanges() - return property - } + @ClosureEquivalent + void processor(Project project, String tool, Action configurator) { + var processor = getObjectFactory().newInstance(Processor.class).configure(configurator) + processor.jar.set(tool) + getProcessors().add(processor) - private static Provider> gatherLibraryIdsFromConfiguration(Project project, Provider configurationProvider) { - // We use a property because it is *not* re-evaluated when queried, while a normal provider is - var property = project.objects.setProperty(String.class) - def logger = project.logger - property.set(configurationProvider.flatMap { config -> - config.incoming.artifacts.resolvedArtifacts.map { artifacts -> - artifacts.collect { - def componentId = it.id.componentIdentifier - if (componentId instanceof ModuleComponentIdentifier) { - var group = componentId.getGroup() - var module = componentId.getModule() - var version = componentId.getVersion() - var classifier = LibraryCollector.guessMavenClassifier(it.file, componentId) - var extension = FilenameUtils.getExtension(it.file.name) - if (classifier != "") { - version += ":" + classifier - } - return "$group:$module:$version@$extension".toString() - } else { - logger.warn("Cannot handle component: " + componentId) - return null - } - } - } - }) - property.finalizeValueOnRead() - property.disallowChanges() - return property + var libraryManager = project.getExtensions().getByType(LibraryManager.class) + processor.classpath.set(libraryManager.getClasspathOf(tool)) + getLibraries().addAll(libraryManager.getLibrariesOf(tool)) } @ClosureEquivalent - void processor(Project project, String tool, Action configurator) { - var processor = getObjectFactory().newInstance(Processor.class).configure(configurator); + void processor(Project project, Provider tool, Action configurator) { + var processor = getObjectFactory().newInstance(Processor.class).configure(configurator) processor.jar.set(tool) getProcessors().add(processor) - var toolConfiguration = getOrCreateConfigurationForTool(project, tool) - processor.classpath.set(gatherLibraryIdsFromConfiguration(project, toolConfiguration)) - if (toolLibrariesAdded.add(tool)) { - getLibraries().addAll gatherLibrariesFromConfiguration(project, toolConfiguration) - } + var libraryManager = project.getExtensions().getByType(LibraryManager.class) + processor.classpath.set(libraryManager.getClasspathOf(tool)) + getLibraries().addAll(libraryManager.getLibrariesOf(tool)) + } + + @ClosureEquivalent + void processor(Project project, Function> toolSelector, Action configurator) { + var tool = toolSelector.apply(project.getExtensions().getByType(Subsystems).getTools()) + processor(project, tool, configurator); } @Nested diff --git a/dsl/platform/src/main/groovy/net/neoforged/gradle/dsl/platform/model/Library.groovy b/dsl/platform/src/main/groovy/net/neoforged/gradle/dsl/platform/model/Library.groovy index 3814e3ed5..7a2a8f494 100644 --- a/dsl/platform/src/main/groovy/net/neoforged/gradle/dsl/platform/model/Library.groovy +++ b/dsl/platform/src/main/groovy/net/neoforged/gradle/dsl/platform/model/Library.groovy @@ -64,8 +64,8 @@ abstract class Library extends WithRules { @Override int hashCode() { def result = super.hashCode() - result = 31 * result + (getName() != null ? getName().hashCode() : 0) - result = 31 * result + (getDownload() != null ? getDownload().hashCode() : 0) + result = 31 * result + (getName().get() != null ? getName().get().hashCode() : 0) + result = 31 * result + (getDownload().get() != null ? getDownload().get().hashCode() : 0) return result } @@ -75,8 +75,8 @@ abstract class Library extends WithRules { if (!(obj instanceof Library)) return false; final Library other = (Library) obj; return super.equals(obj) && - (getName() != null ? getName() == other.getName() : other.getName() == null) && - (getDownload() != null ? getDownload() == other.getDownload() : other.getDownload() == null); + (getName().get() != null ? getName().get() == other.getName().get() : other.getName().get() == null) && + (getDownload().get() != null ? getDownload().get() == other.getDownload().get() : other.getDownload().get() == null); } @CompileStatic diff --git a/dsl/vanilla/src/main/groovy/net/neoforged/gradle/dsl/vanilla/runtime/spec/VanillaSpecification.java b/dsl/vanilla/src/main/groovy/net/neoforged/gradle/dsl/vanilla/runtime/spec/VanillaSpecification.java index 498832e68..fb677e437 100644 --- a/dsl/vanilla/src/main/groovy/net/neoforged/gradle/dsl/vanilla/runtime/spec/VanillaSpecification.java +++ b/dsl/vanilla/src/main/groovy/net/neoforged/gradle/dsl/vanilla/runtime/spec/VanillaSpecification.java @@ -9,28 +9,4 @@ */ @CompileStatic public interface VanillaSpecification extends Specification { - - /** - * Gets the version of FART to use. - * - * @return The version of FART to use. - */ - @NotNull - String getFartVersion(); - - /** - * Gets the version of ForgeFlower to use. - * - * @return The version of ForgeFlower to use. - */ - @NotNull - String getForgeFlowerVersion(); - - /** - * Gets the version of AccessTransformerApplier to use. - * - * @return The version of AccessTransformerApplier to use. - */ - @NotNull - String getAccessTransformerApplierVersion(); } diff --git a/platform/src/functionalTest/groovy/net/neoforged/gradle/platform/InstallerProfileTests.groovy b/platform/src/functionalTest/groovy/net/neoforged/gradle/platform/InstallerProfileTests.groovy new file mode 100644 index 000000000..3c9cf4dc4 --- /dev/null +++ b/platform/src/functionalTest/groovy/net/neoforged/gradle/platform/InstallerProfileTests.groovy @@ -0,0 +1,377 @@ +package net.neoforged.gradle.platform + +import net.neoforged.gradle.common.services.caching.CachedExecutionService +import net.neoforged.trainingwheels.gradle.functional.BuilderBasedTestSpecification +import net.neoforged.trainingwheels.gradle.functional.builder.Runtime +import org.gradle.testkit.runner.TaskOutcome + +import java.nio.file.Files + +class InstallerProfileTests extends BuilderBasedTestSpecification { + + private static final String TEST_NEOFORM_VERSION = "1.21-20240613.152323" + + private static final String PATCH_TARGET_PATH = "src/main/java/net/minecraft/client/Minecraft.java" + private static final String PATCH_RESULT_PATH = "patches/net/minecraft/client/Minecraft.java.patch" + private static final String ICON_PATH = "docs/assets/neoforged.ico" + private static final String INSTALLER_LOGO_PATH = "src/main/resources/neoforged_logo.png" + private static final String INSTALLER_URL_LOGO_PATH = "src/main/resources/url.png" + + @Override + protected void configurePluginUnderTest() { + pluginUnderTest = "net.neoforged.gradle.platform" + injectIntoAllProject = false + injectIntoRootProject = false + } + + private final class PublishingProjectSetup { + private Runtime rootProject + private Runtime baseProject + private Runtime patchedProject + } + + private PublishingProjectSetup createPublishingProject(String projectId, String patchedBuildConfiguration) { + def rootProject = create(projectId, { + it.property(CachedExecutionService.DEBUG_CACHE_PROPERTY, 'true') + it.settingsPlugin(pluginUnderTest) + it.settings(""" + dynamicProjects { + include ':base' + include ':neoforge' + + project(":base").projectDir = file("projects/base") + project(":neoforge").projectDir = file("projects/neoforge") + } + """.stripMargin()) + it.build(""" + group = 'net.neoforged' + version = "1.0.0-${projectId}" + + allprojects { + version rootProject.version + group 'net.neoforged' + repositories { + mavenLocal() + } + } + + subprojects { + apply plugin: 'java' + + java.toolchain.languageVersion.set(JavaLanguageVersion.of(21)) + } + """) + it.file("server_files/args.txt", """ + Something to Inject into + """) + //The following properties are needed as we do not have an abstract layer over the tokens needed. + it.property("fancy_mod_loader_version", "1.0.0") + it.enableGradleParallelRunning() + it.enableConfigurationCache() + it.enableLocalBuildCache() + it.withGlobalCacheDirectory(tempDir) + }) + + def baseProject = create("${projectId}/projects/base", { + it.build(""" + dynamicProject { + neoform("${TEST_NEOFORM_VERSION}") + } + """) + }) + + //TODO: We need better handling for neoforged dependency detection. Right now this is limited to this exact ga: net.neoforged:neoforge so we are limited to that setup + def patchedProject = create("${projectId}/projects/neoforge", { + it.plugin("maven-publish") + it.build(""" + dynamicProject { + runtime("${TEST_NEOFORM_VERSION}", + rootProject.layout.projectDirectory.dir('patches'), + rootProject.layout.projectDirectory.dir('rejects')) + } + + installerProfile { + profile = 'NeoGradle-Tests' + } + + minecraft { + modIdentifier 'minecraft' + } + + ${patchedBuildConfiguration} + + sourceSets { + main { + java { + srcDirs rootProject.file('src/main/java') + } + resources { + srcDirs rootProject.file('src/main/resources'), rootProject.file('src/generated/resources') + } + } + } + + AdhocComponentWithVariants javaComponent = (AdhocComponentWithVariants) project.components.findByName("java") + // Ensure the two default variants are not published, since they + // contain Minecraft classes + javaComponent.withVariantsFromConfiguration(configurations.apiElements) { + it.skip() + } + javaComponent.withVariantsFromConfiguration(configurations.runtimeElements) { + it.skip() + } + + //TODO: Move these to platform so that they can be centrally round tripped tested. + configurations { + modDevBundle { + canBeResolved = false + canBeConsumed = true + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, "data")) + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL)) + } + outgoing { + capability("net.neoforged:neoforge-moddev-bundle:" + project.version) + } + javaComponent.addVariantsFromConfiguration(it) {} // Publish it + } + modDevConfig { + canBeResolved = false + canBeConsumed = true + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, "data")) + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL)) + } + outgoing { + capability("net.neoforged:neoforge-moddev-config:" + project.version) + } + javaComponent.addVariantsFromConfiguration(it) {} // Publish it + } + installerJar { + canBeResolved = false + canBeConsumed = true + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY)) + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EMBEDDED)) + // The installer targets JDK 8 + attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 8) + } + outgoing { + capability("net.neoforged:neoforge-installer:" + project.version) + } + // Publish it + javaComponent.addVariantsFromConfiguration(it) {} + } + universalJar { + canBeResolved = false + canBeConsumed = true + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY)) + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL)) + attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, JavaVersion.current().majorVersion.toInteger()) + attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements, LibraryElements.JAR)) + } + // Publish it + javaComponent.addVariantsFromConfiguration(it) {} + } + modDevApiElements { + canBeResolved = false + canBeConsumed = true + afterEvaluate { + extendsFrom userdevCompileOnly, installerLibraries, moduleOnly + } + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY)) + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL)) + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_API)) + } + outgoing { + capability("net.neoforged:neoforge-dependencies:" + project.version) + } + javaComponent.addVariantsFromConfiguration(it) {} + } + modDevRuntimeElements { + canBeResolved = false + canBeConsumed = true + afterEvaluate { + extendsFrom installerLibraries, moduleOnly + } + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY)) + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL)) + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) + } + outgoing { + capability("net.neoforged:neoforge-dependencies:" + project.version) + } + javaComponent.addVariantsFromConfiguration(it) {} + } + modDevModulePath { + canBeResolved = false + canBeConsumed = true + afterEvaluate { + extendsFrom moduleOnly + } + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY)) + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL)) + } + outgoing { + capability("net.neoforged:neoforge-moddev-module-path:" + project.version) + } + javaComponent.addVariantsFromConfiguration(it) {} + } + modDevTestFixtures { + canBeResolved = false + canBeConsumed = true + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY)) + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL)) + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) + } + outgoing { + capability("net.neoforged:neoforge-moddev-test-fixtures:" + project.version) + } + javaComponent.addVariantsFromConfiguration(it) {} + } + } + + dependencies { + modDevBundle("net.neoforged:neoform:${TEST_NEOFORM_VERSION}") { + capabilities { + requireCapability 'net.neoforged:neoform' + } + endorseStrictVersions() + } + modDevApiElements("net.neoforged:neoform:${TEST_NEOFORM_VERSION}") { + capabilities { + requireCapability 'net.neoforged:neoform-dependencies' + } + endorseStrictVersions() + } + modDevRuntimeElements("net.neoforged:neoform:${TEST_NEOFORM_VERSION}") { + capabilities { + requireCapability 'net.neoforged:neoform-dependencies' + } + endorseStrictVersions() + } + } + + afterEvaluate { + artifacts { + modDevBundle(userdevJar) { + setClassifier("userdev") // Legacy + } + modDevConfig(createUserdevJson.output) { + builtBy(createUserdevJson) + setClassifier("moddev-config") + } + universalJar(signUniversalJar.output) { + builtBy(signUniversalJar) + setClassifier("universal") + } + installerJar(signInstallerJar.output) { + builtBy(signInstallerJar) + setClassifier("installer") + } + } + } + + publishing { + publications { + maven(MavenPublication) { + groupId = project.group + artifactId = project.name + version = project.version + + from components.java + + versionMapping { + usage('java-api') { + fromResolutionOf('runtimeClasspath') + } + usage('java-runtime') { + fromResolutionResult() + } + } + } + } + repositories { + maven { + name = 'test' + url = "file://${getTestTempDirectory().absolutePath.replace(File.separator, "/")}/maven" + } + } + } + """) + }) + + def iconPath = rootProject.file(ICON_PATH) + iconPath.parentFile.mkdirs() + Files.copy(new File("src/functionalTest/resources/icon.ico").toPath(), iconPath.toPath()) + + def installerLogoPath = rootProject.file(INSTALLER_LOGO_PATH) + installerLogoPath.parentFile.mkdirs() + Files.copy(new File("src/functionalTest/resources/icon.png").toPath(), installerLogoPath.toPath()) + + def installerUrlLogoPath = rootProject.file(INSTALLER_URL_LOGO_PATH) + installerUrlLogoPath.parentFile.mkdirs() + Files.copy(new File("src/functionalTest/resources/icon.png").toPath(), installerUrlLogoPath.toPath()) + + def result = new PublishingProjectSetup() + result.rootProject = rootProject + result.baseProject = baseProject + result.patchedProject = patchedProject + + return result + } + + private void patch(PublishingProjectSetup setup) { + def minecraftClassSourceFile = setup.patchedProject.file(PATCH_TARGET_PATH) + def minecraftClassContent = new ArrayList<>(minecraftClassSourceFile.readLines()) + def minecraftClassContentIndex = minecraftClassContent.findIndexOf { String line -> line.startsWith("public class Minecraft") } + def insertedComment = " // This is a comment inserted by the test" + minecraftClassContent.add(minecraftClassContentIndex, insertedComment) //Insert the comment before the class statement + + minecraftClassSourceFile.delete() + minecraftClassSourceFile.write(minecraftClassContent.join("\n")) + } + + def "a published installer artifact has properly reconfigured tooling"() { + given: + def project = createPublishingProject("published-userdev", """ + subsystems { + tools { + binaryPatcher = "net.neoforged.installertools:binarypatcher:2.1.5:fatjar" + } + } + """) + + project.rootProject.run { it.tasks ':neoforge:setup' } + patch(project) + project.rootProject.run { it.tasks ':neoforge:unpackSourcePatches'} + project.rootProject.run { it.tasks ':neoforge:assemble' } + + when: + def publishingRun = project.rootProject.run { + it.tasks ':neoforge:publishAllPublicationsToTestRepository' + } + + then: + publishingRun.task(":neoforge:publishAllPublicationsToTestRepository").outcome == TaskOutcome.SUCCESS + + and: + def buildPlatformDir = project.patchedProject.file("build/platform") + def platformSpecDir = buildPlatformDir.listFiles()[0]; + def createLegacyInstaller = new File(platformSpecDir, "steps/createLegacyInstallerJson") + def jsonFile = new File(createLegacyInstaller, "install_profile.json") + + then: + jsonFile.exists() + jsonFile.text.contains("net.neoforged.installertools:binarypatcher:2.1.5:fatjar") + !jsonFile.text.contains("net.neoforged.installertools:binarypatcher:2.1.7:fatjar") + } +} + diff --git a/platform/src/main/java/net/neoforged/gradle/platform/PlatformProjectPlugin.java b/platform/src/main/java/net/neoforged/gradle/platform/PlatformProjectPlugin.java index 4d4a66ee9..65612cf84 100644 --- a/platform/src/main/java/net/neoforged/gradle/platform/PlatformProjectPlugin.java +++ b/platform/src/main/java/net/neoforged/gradle/platform/PlatformProjectPlugin.java @@ -5,7 +5,9 @@ import net.neoforged.gradle.dsl.common.runs.ide.extensions.IdeaRunExtension; import net.neoforged.gradle.dsl.common.runs.run.Run; import net.neoforged.gradle.dsl.common.runs.run.RunManager; +import net.neoforged.gradle.dsl.platform.extensions.LibraryManager; import net.neoforged.gradle.platform.extensions.DynamicProjectExtension; +import net.neoforged.gradle.platform.extensions.LibraryManagerImpl; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.plugins.JavaPluginExtension; @@ -18,7 +20,8 @@ public class PlatformProjectPlugin implements Plugin { public void apply(@NotNull Project target) { target.getPlugins().apply(CommonPlugin.class); target.getExtensions().create("dynamicProject", DynamicProjectExtension.class, target); - + target.getExtensions().create(LibraryManager.class, "libraryManager", LibraryManagerImpl.class, target); + target.getExtensions().configure(RunManager.class, runs -> runs.configureAll(run -> configureRun(target, run))); } diff --git a/platform/src/main/java/net/neoforged/gradle/platform/extensions/DynamicProjectExtension.java b/platform/src/main/java/net/neoforged/gradle/platform/extensions/DynamicProjectExtension.java index 84e247d9f..7dfaf7fc3 100644 --- a/platform/src/main/java/net/neoforged/gradle/platform/extensions/DynamicProjectExtension.java +++ b/platform/src/main/java/net/neoforged/gradle/platform/extensions/DynamicProjectExtension.java @@ -22,6 +22,8 @@ import net.neoforged.gradle.common.util.ToolUtilities; import net.neoforged.gradle.dsl.common.extensions.AccessTransformers; import net.neoforged.gradle.dsl.common.extensions.Mappings; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Subsystems; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Tools; import net.neoforged.gradle.dsl.common.runs.run.Run; import net.neoforged.gradle.dsl.common.runs.run.RunManager; import net.neoforged.gradle.dsl.common.runs.type.RunType; @@ -125,7 +127,7 @@ public void clean(final String minecraftVersion) { final SourceSet mainSource = javaPluginExtension.getSourceSets().getByName("main"); final VanillaRuntimeExtension vanillaRuntimeExtension = project.getExtensions().getByType(VanillaRuntimeExtension.class); - final VanillaRuntimeDefinition runtimeDefinition = vanillaRuntimeExtension.create(builder -> builder.withMinecraftVersion(minecraftVersion).withDistributionType(DistributionType.CLIENT).withFartVersion(vanillaRuntimeExtension.getFartVersion()).withForgeFlowerVersion(vanillaRuntimeExtension.getVineFlowerVersion()).withAccessTransformerApplierVersion(vanillaRuntimeExtension.getAccessTransformerApplierVersion())); + final VanillaRuntimeDefinition runtimeDefinition = vanillaRuntimeExtension.create(builder -> builder.withMinecraftVersion(minecraftVersion).withDistributionType(DistributionType.CLIENT)); project.getTasks().named(mainSource.getCompileJavaTaskName()).configure(task -> task.setEnabled(false)); @@ -439,6 +441,7 @@ public void runtime(final String neoFormVersion, Directory patches, Directory re task.dependsOn(universalJar); }); + final Tools tools = project.getExtensions().getByType(Subsystems.class).getTools(); installerProfile.configure((Consumer) profile -> { profile.getProfile().convention(project.getName()); profile.getVersion().set(launcherProfile.getId()); @@ -454,7 +457,7 @@ public void runtime(final String neoFormVersion, Directory patches, Directory re profile.data("MC_SRG", String.format("[net.minecraft:client:%s:srg]", neoFormVersion), String.format("[net.minecraft:server:%s:srg]", neoFormVersion)); profile.data("PATCHED", String.format("[%s:%s:%s:client]", "net.neoforged", "neoforge", project.getVersion()), String.format("[%s:%s:%s:server]", "net.neoforged", "neoforge", project.getVersion())); profile.data("MCP_VERSION", String.format("'%s'", neoFormVersion), String.format("'%s'", neoFormVersion)); - profile.processor(project, Constants.INSTALLERTOOLS, processor -> { + profile.processor(project, Tools::getInstallerTools, processor -> { processor.server(); processor.getArguments().addAll("--task", "EXTRACT_FILES", "--archive", "{INSTALLER}", @@ -468,35 +471,35 @@ public void runtime(final String neoFormVersion, Directory patches, Directory re "--from", "data/unix_args.txt", "--to", String.format("{ROOT}/libraries/%s/%s/%s/unix_args.txt", project.getGroup().toString().replaceAll("\\.", "/"), project.getName(), project.getVersion())); }); - profile.processor(project, Constants.INSTALLERTOOLS, processor -> { + profile.processor(project, Tools::getInstallerTools, processor -> { processor.server(); processor.getArguments().addAll("--task", "BUNDLER_EXTRACT", "--input", "{MINECRAFT_JAR}", "--output", "{ROOT}/libraries/", "--libraries"); }); - profile.processor(project, Constants.INSTALLERTOOLS, processor -> { + profile.processor(project, Tools::getInstallerTools, processor -> { processor.server(); processor.getArguments().addAll("--task", "BUNDLER_EXTRACT", "--input", "{MINECRAFT_JAR}", "--output", "{MC_UNPACKED}", "--jar-only"); }); - profile.processor(project, Constants.INSTALLERTOOLS, processor -> { + profile.processor(project, Tools::getInstallerTools, processor -> { processor.getArguments().addAll("--task", "MCP_DATA", "--input", String.format("[%s]", neoformDependency), "--output", "{MAPPINGS}", "--key", "mappings"); }); - profile.processor(project, Constants.INSTALLERTOOLS, processor -> { + profile.processor(project, Tools::getInstallerTools, processor -> { processor.getArguments().addAll("--task", "DOWNLOAD_MOJMAPS", "--version", runtimeDefinition.getSpecification().getMinecraftVersion(), "--side", "{SIDE}", "--output", "{MOJMAPS}"); }); - profile.processor(project, Constants.INSTALLERTOOLS, processor -> { + profile.processor(project, Tools::getInstallerTools, processor -> { processor.getArguments().addAll("--task", "MERGE_MAPPING", "--left", "{MAPPINGS}", "--right", "{MOJMAPS}", "--output", "{MERGED_MAPPINGS}", "--classes", "--fields", "--methods", "--reverse-right"); }); - profile.processor(project, Constants.JARSPLITTER, processor -> { + profile.processor(project, Tools::getJarSplitter, processor -> { processor.client(); processor.getArguments().addAll("--input", "{MINECRAFT_JAR}", "--slim", "{MC_SLIM}", "--extra", "{MC_EXTRA}", "--srg", "{MERGED_MAPPINGS}"); }); - profile.processor(project, Constants.JARSPLITTER, processor -> { + profile.processor(project, Tools::getJarSplitter, processor -> { processor.server(); processor.getArguments().addAll("--input", "{MC_UNPACKED}", "--slim", "{MC_SLIM}", "--extra", "{MC_EXTRA}", "--srg", "{MERGED_MAPPINGS}"); }); - profile.processor(project, Constants.FART, processor -> { + profile.processor(project, tools.getAutoRenamingTool(), processor -> { processor.getArguments().addAll("--input", "{MC_SLIM}", "--output", "{MC_SRG}", "--names", "{MERGED_MAPPINGS}", "--ann-fix", "--ids-fix", "--src-fix", "--record-fix"); }); - profile.processor(project, Constants.BINARYPATCHER, processor -> { + profile.processor(project, tools.getBinaryPatcher(), processor -> { processor.getArguments().addAll("--clean", "{MC_SRG}", "--output", "{PATCHED}", "--apply", "{BINPATCH}"); }); @@ -682,7 +685,7 @@ public void runtime(final String neoFormVersion, Directory patches, Directory re userdevProfile.getAccessTransformerDirectory().set("ats/"); userdevProfile.getBinaryPatchFile().set("joined.lzma"); userdevProfile.getBinaryPatcher().set(project.getObjects().newInstance(UserdevProfile.ToolExecution.class).configure((Action) tool -> { - tool.getTool().set(Constants.BINPATCHER); + tool.getTool().set(project.getExtensions().getByType(Subsystems.class).getTools().getBinaryPatcher()); tool.getArguments().addAll("--clean", "{clean}", "--output", "{output}", "--apply", "{patch}"); })); userdevProfile.getSourcesJarArtifactCoordinate().set(createCoordinate(project, "sources")); @@ -1011,7 +1014,7 @@ private Provider renameCompiledJar(TaskProvider>> classpathMap = new ConcurrentHashMap<>(); + + private final Map>> libraryMap = new ConcurrentHashMap<>(); + + private final Project project; + + @Inject + public LibraryManagerImpl(Project project) { + this.project = project; + } + + @Override + public Provider> getClasspathOf(Provider library) { + return library.flatMap(lib -> classpathMap.computeIfAbsent(lib, key -> { + final Provider configuration = getOrCreateConfigurationForTool(project, key); + return gatherLibraryIdsFromConfiguration(project, configuration); + })); + } + + @Override + public Provider> getLibrariesOf(Provider library) { + return library.flatMap(lib -> libraryMap.computeIfAbsent(lib, key -> { + final Provider configuration = getOrCreateConfigurationForTool(project, key); + return gatherLibrariesFromConfiguration(project, configuration); + })); + } + + @Override + public Provider> getClasspathOf(String library) { + return classpathMap.computeIfAbsent(library, key -> { + final Provider configuration = getOrCreateConfigurationForTool(project, key); + return gatherLibraryIdsFromConfiguration(project, configuration); + }); + } + + @Override + public Provider> getLibrariesOf(String library) { + return libraryMap.computeIfAbsent(library, key -> { + final Provider configuration = getOrCreateConfigurationForTool(project, key); + return gatherLibrariesFromConfiguration(project, configuration); + }); + } + + private static Provider> gatherLibrariesFromConfiguration(Project project, Provider configurationProvider) { + var repositoryUrls = project.getRepositories() + .withType(MavenArtifactRepository.class).stream().map(MavenArtifactRepository::getUrl).collect(Collectors.toList()); + var logger = project.getLogger(); + + var objectFactory = project.getObjects(); + + // We use a property because it is *not* re-evaluated when queried, while a normal provider is + var property = objectFactory.setProperty(Library.class); + property.set(configurationProvider.flatMap(config -> { + logger.info("Finding download URLs for configuration ${config.name}"); + return config.getIncoming().getArtifacts().getResolvedArtifacts().map(artifacts -> { + var libraryCollector = new LibraryCollector(objectFactory, repositoryUrls, logger); + + for (ResolvedArtifactResult resolvedArtifact : artifacts) { + libraryCollector.visit(resolvedArtifact); + } + + return libraryCollector.getLibraries(); + }); + })); + property.finalizeValueOnRead(); + property.disallowChanges(); + return property; + } + + private static Provider> gatherLibraryIdsFromConfiguration(Project project, Provider configurationProvider) { + // We use a property because it is *not* re-evaluated when queried, while a normal provider is + var property = project.getObjects().setProperty(String.class); + var logger = project.getLogger(); + property.set(configurationProvider.flatMap(config -> + config.getIncoming().getArtifacts().getResolvedArtifacts().map(artifacts -> + artifacts.stream().map(it -> { + var componentId = it.getId().getComponentIdentifier(); + if (componentId instanceof ModuleComponentIdentifier moduleComponentIdentifier) { + var group = moduleComponentIdentifier.getGroup(); + var module = moduleComponentIdentifier.getModule(); + var version = moduleComponentIdentifier.getVersion(); + var classifier = LibraryCollector.guessMavenClassifier(it.getFile(), moduleComponentIdentifier); + var extension = FilenameUtils.getExtension(it.getFile().getName()); + if (classifier != "") { + version += ":" + classifier; + } + return "%s:%s:%s@%s".formatted(group, module, version, extension); + } else { + logger.warn("Cannot handle component: {}", componentId); + return null; + } + }).filter(Objects::nonNull).collect(Collectors.toSet())))); + property.finalizeValueOnRead(); + property.disallowChanges(); + return property; + } + + private NamedDomainObjectProvider getOrCreateConfigurationForTool(Project project, String tool) { + var configName = "neoForgeInstallerTool" + ModuleDependencyUtils.toConfigurationName(tool); + try { + return project.getConfigurations().named(configName); + } catch (UnknownDomainObjectException ignored) { + return project.getConfigurations().register(configName, (Configuration spec) -> { + spec.setCanBeConsumed(false); + spec.setCanBeResolved(true); + spec.getDependencies().add(project.getDependencies().create(tool)); + }); + } + } +} diff --git a/platform/src/main/java/net/neoforged/gradle/platform/runtime/runtime/tasks/GenerateBinaryPatches.java b/platform/src/main/java/net/neoforged/gradle/platform/runtime/runtime/tasks/GenerateBinaryPatches.java index 63e3f9fe3..b2c03df28 100644 --- a/platform/src/main/java/net/neoforged/gradle/platform/runtime/runtime/tasks/GenerateBinaryPatches.java +++ b/platform/src/main/java/net/neoforged/gradle/platform/runtime/runtime/tasks/GenerateBinaryPatches.java @@ -7,6 +7,8 @@ import net.neoforged.gradle.common.runtime.tasks.DefaultExecute; import net.neoforged.gradle.common.util.ToolUtilities; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Subsystems; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Tools; import net.neoforged.gradle.dsl.common.tasks.WithOutput; import net.neoforged.gradle.dsl.common.tasks.WithWorkspace; import net.neoforged.gradle.dsl.common.util.Constants; @@ -18,7 +20,7 @@ public abstract class GenerateBinaryPatches extends DefaultExecute implements WithOutput, WithWorkspace { public GenerateBinaryPatches() { - getExecutingJar().set(ToolUtilities.resolveTool(getProject(), Constants.BINPATCHER)); + getExecutingJar().fileProvider(ToolUtilities.resolveTool(getProject(), Tools::getBinaryPatcher)); getProgramArguments().addAll("--clean", "{clean}", "--create", "{dirty}", "--output", "{output}", "--patches", "{patches}", "--srg", "{srg}"); diff --git a/vanilla/src/main/java/net/neoforged/gradle/vanilla/dependency/VanillaDependencyManager.java b/vanilla/src/main/java/net/neoforged/gradle/vanilla/dependency/VanillaDependencyManager.java index caace1c9a..167f57def 100644 --- a/vanilla/src/main/java/net/neoforged/gradle/vanilla/dependency/VanillaDependencyManager.java +++ b/vanilla/src/main/java/net/neoforged/gradle/vanilla/dependency/VanillaDependencyManager.java @@ -100,9 +100,6 @@ private static VanillaRuntimeDefinition buildVanillaRuntimeDefinition(Project pr builder.withMinecraftArtifact(StringCapitalizationUtils.deCapitalize(dependency.getName())); builder.withDistributionType(DistributionType.valueOf(dependency.getName().toUpperCase(Locale.ROOT))); builder.withMinecraftVersion(version); - builder.withFartVersion(runtimeExtension.getFartVersion()); - builder.withForgeFlowerVersion(runtimeExtension.getVineFlowerVersion()); - builder.withAccessTransformerApplierVersion(runtimeExtension.getAccessTransformerApplierVersion()); }); } diff --git a/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/extensions/VanillaRuntimeExtension.java b/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/extensions/VanillaRuntimeExtension.java index 2f643a8b9..8cc59f955 100644 --- a/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/extensions/VanillaRuntimeExtension.java +++ b/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/extensions/VanillaRuntimeExtension.java @@ -43,10 +43,6 @@ public abstract class VanillaRuntimeExtension extends CommonRuntimeExtension getVersion(); - public abstract Property getFartVersion(); - - public abstract Property getVineFlowerVersion(); - - public abstract Property getAccessTransformerApplierVersion(); - private static final class StepData { private final List steps; private final IStep rawJarStep; diff --git a/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/spec/VanillaRuntimeSpecification.java b/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/spec/VanillaRuntimeSpecification.java index 109af1637..e4d0c4435 100644 --- a/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/spec/VanillaRuntimeSpecification.java +++ b/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/spec/VanillaRuntimeSpecification.java @@ -12,6 +12,7 @@ import org.gradle.api.provider.Provider; import org.jetbrains.annotations.NotNull; +import java.util.Objects; import java.util.Optional; /** @@ -19,9 +20,6 @@ */ public final class VanillaRuntimeSpecification extends CommonRuntimeSpecification implements VanillaSpecification { private final String minecraftVersion; - private final String fartVersion; - private final String forgeFlowerVersion; - private final String accessTransformerApplierVersion; public VanillaRuntimeSpecification(Project project, String name, @@ -30,67 +28,32 @@ public VanillaRuntimeSpecification(Project project, Multimap preTaskTypeAdapters, Multimap postTypeAdapters, Multimap> taskCustomizers, - String minecraftVersion, - String fartVersion, - String forgeFlowerVersion, - String accessTransformerApplierVersion) { + String minecraftVersion) { super(project, name, version, side, preTaskTypeAdapters, postTypeAdapters, taskCustomizers, VanillaRuntimeExtension.class); this.minecraftVersion = minecraftVersion; - this.fartVersion = fartVersion; - this.forgeFlowerVersion = forgeFlowerVersion; - this.accessTransformerApplierVersion = accessTransformerApplierVersion; } public String getMinecraftVersion() { return minecraftVersion; } - @Override - public String getFartVersion() { - return fartVersion; - } - - @Override - public String getForgeFlowerVersion() { - return forgeFlowerVersion; - } - - @Override - public String getAccessTransformerApplierVersion() { - return accessTransformerApplierVersion; - } - @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof VanillaRuntimeSpecification)) return false; + if (!(o instanceof VanillaRuntimeSpecification that)) return false; if (!super.equals(o)) return false; - - VanillaRuntimeSpecification that = (VanillaRuntimeSpecification) o; - - if (!minecraftVersion.equals(that.minecraftVersion)) return false; - if (!fartVersion.equals(that.fartVersion)) return false; - if (!forgeFlowerVersion.equals(that.forgeFlowerVersion)) return false; - return accessTransformerApplierVersion.equals(that.accessTransformerApplierVersion); + return Objects.equals(getMinecraftVersion(), that.getMinecraftVersion()); } @Override public int hashCode() { - int result = super.hashCode(); - result = 31 * result + minecraftVersion.hashCode(); - result = 31 * result + fartVersion.hashCode(); - result = 31 * result + forgeFlowerVersion.hashCode(); - result = 31 * result + accessTransformerApplierVersion.hashCode(); - return result; + return Objects.hash(super.hashCode(), getMinecraftVersion()); } @Override public String toString() { - return "VanillaRuntimeSpec{" + + return "VanillaRuntimeSpecification{" + "minecraftVersion='" + minecraftVersion + '\'' + - ", fartVersion='" + fartVersion + '\'' + - ", forgeFlowerVersion='" + forgeFlowerVersion + '\'' + - ", accessTransformerApplierVersion='" + accessTransformerApplierVersion + '\'' + '}'; } @@ -100,15 +63,6 @@ public static final class Builder extends CommonRuntimeSpecification.Builder minecraftVersion; - private Provider fartVersion; - private boolean hasConfiguredFartVersion = false; - - private Provider forgeFlowerVersion; - private boolean hasConfiguredForgeFlowerVersion = false; - - private Provider accessTransformerApplierVersion; - private boolean hasConfiguredAccessTransformerApplierVersion = false; - public static Builder from(Project project) { return new Builder(project); } @@ -125,23 +79,6 @@ protected Builder getThis() { return this; } - @Override - protected void configureBuilder() { - super.configureBuilder(); - final VanillaRuntimeExtension runtimeExtension = this.getProject().getExtensions().getByType(VanillaRuntimeExtension.class); - - if (!this.hasConfiguredFartVersion) { - this.fartVersion = runtimeExtension.getFartVersion(); - } - - if (!this.hasConfiguredForgeFlowerVersion) { - this.forgeFlowerVersion = runtimeExtension.getVineFlowerVersion(); - } - - if (!this.hasConfiguredAccessTransformerApplierVersion) { - this.accessTransformerApplierVersion = runtimeExtension.getAccessTransformerApplierVersion(); - } - } public Builder withMinecraftArtifact(final Provider minecraftArtifact) { this.minecraftArtifact = minecraftArtifact; @@ -167,45 +104,6 @@ public Builder withMinecraftVersion(final String minecraftVersion) { return withMinecraftVersion(project.provider(() -> minecraftVersion)); } - public Builder withFartVersion(final Provider fartVersion) { - this.fartVersion = fartVersion; - this.hasConfiguredFartVersion = true; - return getThis(); - } - - public Builder withFartVersion(final String fartVersion) { - if (fartVersion == null) // Additional null check for convenient loading of sides from dependencies. - return getThis(); - - return withFartVersion(project.provider(() -> fartVersion)); - } - - public Builder withForgeFlowerVersion(final Provider forgeFlowerVersion) { - this.forgeFlowerVersion = forgeFlowerVersion; - this.hasConfiguredForgeFlowerVersion = true; - return getThis(); - } - - public Builder withForgeFlowerVersion(final String forgeFlowerVersion) { - if (forgeFlowerVersion == null) // Additional null check for convenient loading of sides from dependencies. - return getThis(); - - return withForgeFlowerVersion(project.provider(() -> forgeFlowerVersion)); - } - - public Builder withAccessTransformerApplierVersion(final Provider accessTransformerApplierVersion) { - this.accessTransformerApplierVersion = accessTransformerApplierVersion; - this.hasConfiguredAccessTransformerApplierVersion = true; - return getThis(); - } - - public Builder withAccessTransformerApplierVersion(final String accessTransformerApplierVersion) { - if (accessTransformerApplierVersion == null) // Additional null check for convenient loading of sides from dependencies. - return getThis(); - - return withAccessTransformerApplierVersion(project.provider(() -> accessTransformerApplierVersion)); - } - @Override public @NotNull VanillaRuntimeSpecification build() { return new VanillaRuntimeSpecification( @@ -216,10 +114,8 @@ public Builder withAccessTransformerApplierVersion(final String accessTransforme preTaskAdapters, postTaskAdapters, taskCustomizers, - minecraftVersion.get(), - fartVersion.get(), - forgeFlowerVersion.get(), - accessTransformerApplierVersion.get()); + minecraftVersion.get() + ); } } } diff --git a/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/steps/DecompileStep.java b/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/steps/DecompileStep.java index b46be1ccd..b285cb769 100644 --- a/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/steps/DecompileStep.java +++ b/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/steps/DecompileStep.java @@ -1,6 +1,7 @@ package net.neoforged.gradle.vanilla.runtime.steps; import net.neoforged.gradle.common.util.ToolUtilities; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Tools; import net.neoforged.gradle.dsl.common.util.GameArtifact; import net.neoforged.gradle.util.DecompileUtils; import net.neoforged.gradle.common.runtime.tasks.DefaultExecute; @@ -22,12 +23,8 @@ public class DecompileStep implements IStep { @Override public TaskProvider buildTask(VanillaRuntimeDefinition definition, TaskProvider inputProvidingTask, @NotNull File minecraftCache, @NotNull File workingDirectory, @NotNull Map> pipelineTasks, @NotNull Map> gameArtifactTasks, @NotNull Consumer> additionalTaskConfigurator) { - final VanillaRuntimeExtension vanillaRuntimeExtension = definition.getSpecification().getProject().getExtensions().getByType(VanillaRuntimeExtension.class); - return definition.getSpecification().getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(definition, "decompile"), DefaultExecute.class, task -> { - final String decompiler = vanillaRuntimeExtension.getVineFlowerVersion().map(version -> String.format(Constants.VINEFLOWER_ARTIFACT_INTERPOLATION, version)).get(); - - task.getExecutingJar().set(ToolUtilities.resolveTool(task.getProject(), decompiler)); + task.getExecutingJar().fileProvider(ToolUtilities.resolveTool(task.getProject(), Tools::getDecompiler)); task.getJvmArguments().addAll(DecompileUtils.DEFAULT_JVM_ARGS); task.getProgramArguments().addAll(DecompileUtils.DEFAULT_PROGRAMM_ARGS); CommonRuntimeUtils.buildArguments(task.getArguments(), definition, DecompileUtils.DEFAULT_DECOMPILE_VALUES, pipelineTasks, task, Optional.of(inputProvidingTask));