From 2228786e3d966a1b736b94568f4bf9e37067d77d Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Wed, 23 Apr 2025 20:35:43 +0200 Subject: [PATCH 1/3] [WIP] Lazily configure dependency configurations --- .../legacyforge/dsl/ObfuscationExtension.java | 34 +++++++++----- .../internal/LegacyForgeModDevPlugin.java | 40 ++++++++-------- .../neoforged/moddevgradle/dsl/RunModel.java | 7 +-- .../internal/DataFileCollections.java | 17 ++++--- .../internal/ModDevArtifactsWorkflow.java | 28 +++++------ .../internal/ModDevRunWorkflow.java | 30 ++++++------ .../internal/jarjar/JarJarArtifacts.java | 15 ++++-- .../neoforged/moddevgradle/tasks/JarJar.java | 46 +++++++++++-------- .../nfrtgradle/NeoFormRuntimePlugin.java | 4 +- .../nfrtgradle/NeoFormRuntimeTask.java | 18 ++++++-- 10 files changed, 143 insertions(+), 96 deletions(-) diff --git a/src/legacy/java/net/neoforged/moddevgradle/legacyforge/dsl/ObfuscationExtension.java b/src/legacy/java/net/neoforged/moddevgradle/legacyforge/dsl/ObfuscationExtension.java index a24261cb..1cf7f111 100644 --- a/src/legacy/java/net/neoforged/moddevgradle/legacyforge/dsl/ObfuscationExtension.java +++ b/src/legacy/java/net/neoforged/moddevgradle/legacyforge/dsl/ObfuscationExtension.java @@ -11,6 +11,7 @@ import org.apache.commons.lang3.StringUtils; import org.gradle.api.Action; import org.gradle.api.InvalidUserCodeException; +import org.gradle.api.NamedDomainObjectProvider; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ExternalModuleDependency; @@ -135,6 +136,7 @@ public TaskProvider reobfuscate(TaskProvider void copyAttribute(Project project, Attribute attribute, C })); } + public Configuration createRemappingConfiguration(Configuration parent) { + // TODO: what if parent comes from a different project? We should at least warn in that case... + return registerRemappingConfiguration(project.getConfigurations().named(parent.getName())).get(); + } + /** * Creates a configuration that will remap its dependencies, and adds it as a children of the provided {@code parent}. * The created configuration uses the name of its parent capitalized and prefixed by "mod". */ - public Configuration createRemappingConfiguration(Configuration parent) { - var remappingConfig = project.getConfigurations().create("mod" + StringUtils.capitalize(parent.getName()), spec -> { + public NamedDomainObjectProvider registerRemappingConfiguration(NamedDomainObjectProvider parent) { + String remappingConfigName = "mod" + StringUtils.capitalize(parent.getName()); + var remappingConfig = project.getConfigurations().register(remappingConfigName, spec -> { spec.setDescription("Configuration for dependencies of " + parent.getName() + " that needs to be remapped"); spec.setCanBeConsumed(false); spec.setCanBeResolved(true); @@ -203,17 +211,19 @@ public Configuration createRemappingConfiguration(Configuration parent) { projectDependency.setTransitive(false); } })); - }); - - var remappedDep = project.getDependencyFactory().create( - remappingConfig.getIncoming().artifactView(view -> { - view.attributes(a -> a.attribute(MinecraftMappings.ATTRIBUTE, namedMappings)); - // Forcing resolution to JAR here allows our SRG_JAR artifact transform to work - view.attributes(a -> a.attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, ArtifactTypeDefinition.JAR_TYPE)); - }).getFiles()); - remappedDep.because("Remapped mods from " + remappingConfig.getName()); - parent.getDependencies().add(remappedDep); + var remappedDep = project.getDependencyFactory().create( + spec.getIncoming().artifactView(view -> { + view.attributes(a -> a.attribute(MinecraftMappings.ATTRIBUTE, namedMappings)); + // Forcing resolution to JAR here allows our SRG_JAR artifact transform to work + view.attributes(a -> a.attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, ArtifactTypeDefinition.JAR_TYPE)); + }).getFiles()); + remappedDep.because("Remapped mods from " + remappingConfigName); + + parent.configure(p -> { + p.getDependencies().add(remappedDep); + }); + }); return remappingConfig; } diff --git a/src/legacy/java/net/neoforged/moddevgradle/legacyforge/internal/LegacyForgeModDevPlugin.java b/src/legacy/java/net/neoforged/moddevgradle/legacyforge/internal/LegacyForgeModDevPlugin.java index f72bd626..4ecbc51e 100644 --- a/src/legacy/java/net/neoforged/moddevgradle/legacyforge/internal/LegacyForgeModDevPlugin.java +++ b/src/legacy/java/net/neoforged/moddevgradle/legacyforge/internal/LegacyForgeModDevPlugin.java @@ -85,14 +85,14 @@ public void apply(Project project) { project.getDependencies().getComponents().withModule("net.neoforged:minecraft-dependencies", NonStrictDependencyTransform.class); var depFactory = project.getDependencyFactory(); - var autoRenamingToolRuntime = project.getConfigurations().create(CONFIGURATION_TOOL_ART, spec -> { + var autoRenamingToolRuntime = project.getConfigurations().register(CONFIGURATION_TOOL_ART, spec -> { spec.setDescription("The AutoRenamingTool CLI tool"); spec.setCanBeConsumed(false); spec.setCanBeResolved(true); spec.setTransitive(false); spec.getDependencies().add(depFactory.create("net.neoforged:AutoRenamingTool:2.0.4:all")); }); - var installerToolsRuntime = project.getConfigurations().create(CONFIGURATION_TOOL_INSTALLERTOOLS, spec -> { + var installerToolsRuntime = project.getConfigurations().register(CONFIGURATION_TOOL_INSTALLERTOOLS, spec -> { spec.setDescription("The InstallerTools CLI tool"); spec.setCanBeConsumed(false); spec.setCanBeResolved(true); @@ -198,15 +198,17 @@ public void enable(Project project, LegacyForgeModdingSettings settings, LegacyF project.getTasks().named("assemble", assemble -> assemble.dependsOn(reobfJar)); // Forge expects the mapping csv files on the root classpath - artifacts.runtimeDependencies() - .getDependencies().add(project.getDependencyFactory().create(project.files(mappingsCsv))); + artifacts.runtimeDependencies().configure(c -> { + c.getDependencies().add(project.getDependencyFactory().create(project.files(mappingsCsv))); + }); - var remapDeps = project.getConfigurations().create("remappingDependencies", spec -> { + var remapDeps = project.getConfigurations().register("remappingDependencies", spec -> { spec.setDescription("An internal configuration that contains the Minecraft dependencies, used for remapping mods"); spec.setCanBeConsumed(false); spec.setCanBeDeclared(false); spec.setCanBeResolved(true); - spec.extendsFrom(artifacts.runtimeDependencies()); + // TODO: is there no better way? + spec.extendsFrom(artifacts.runtimeDependencies().get()); }); project.getDependencies().registerTransform(RemappingTransform.class, params -> { @@ -244,13 +246,15 @@ private void configureDependencyRemapping(Project project, ObfuscationExtension var sourceSets = ExtensionUtils.getSourceSets(project); sourceSets.all(sourceSet -> { var configurationName = sourceSet.getTaskName(null, "jarJar"); - project.getConfigurations().getByName(configurationName).withDependencies(dependencies -> { - dependencies.forEach(dep -> { - if (dep instanceof ProjectDependency projectDependency) { - projectDependency.attributes(a -> { - a.attribute(MinecraftMappings.ATTRIBUTE, srgMappings); - }); - } + project.getConfigurations().named(configurationName, c -> { + c.withDependencies(dependencies -> { + dependencies.forEach(dep -> { + if (dep instanceof ProjectDependency projectDependency) { + projectDependency.attributes(a -> { + a.attribute(MinecraftMappings.ATTRIBUTE, srgMappings); + }); + } + }); }); }); }); @@ -266,10 +270,10 @@ private void configureDependencyRemapping(Project project, ObfuscationExtension project.getDependencies().getArtifactTypes().create(ARTIFACT_TYPE_SRG_JAR, artifactType -> { artifactType.getAttributes().attribute(MinecraftMappings.ATTRIBUTE, srgMappings); }); - obf.createRemappingConfiguration(project.getConfigurations().getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME)); - obf.createRemappingConfiguration(project.getConfigurations().getByName(JavaPlugin.RUNTIME_ONLY_CONFIGURATION_NAME)); - obf.createRemappingConfiguration(project.getConfigurations().getByName(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME)); - obf.createRemappingConfiguration(project.getConfigurations().getByName(JavaPlugin.API_CONFIGURATION_NAME)); - obf.createRemappingConfiguration(project.getConfigurations().getByName(JavaPlugin.COMPILE_ONLY_API_CONFIGURATION_NAME)); + obf.registerRemappingConfiguration(project.getConfigurations().named(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME)); + obf.registerRemappingConfiguration(project.getConfigurations().named(JavaPlugin.RUNTIME_ONLY_CONFIGURATION_NAME)); + obf.registerRemappingConfiguration(project.getConfigurations().named(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME)); + obf.registerRemappingConfiguration(project.getConfigurations().named(JavaPlugin.API_CONFIGURATION_NAME)); + obf.registerRemappingConfiguration(project.getConfigurations().named(JavaPlugin.COMPILE_ONLY_API_CONFIGURATION_NAME)); } } diff --git a/src/main/java/net/neoforged/moddevgradle/dsl/RunModel.java b/src/main/java/net/neoforged/moddevgradle/dsl/RunModel.java index eb2d9f89..3679b17e 100644 --- a/src/main/java/net/neoforged/moddevgradle/dsl/RunModel.java +++ b/src/main/java/net/neoforged/moddevgradle/dsl/RunModel.java @@ -9,6 +9,7 @@ import net.neoforged.moddevgradle.internal.utils.StringUtils; import org.gradle.api.GradleException; import org.gradle.api.Named; +import org.gradle.api.NamedDomainObjectProvider; import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration; @@ -31,7 +32,7 @@ public abstract class RunModel implements Named, Dependencies { private final String name; - private final Configuration configuration; + private final NamedDomainObjectProvider configuration; /** * The Gradle tasks that should be run before running this run. @@ -49,7 +50,7 @@ public RunModel(String name, Project project, Iterable defaultMods) { getGameDirectory().convention(project.getLayout().getProjectDirectory().dir("run")); - configuration = project.getConfigurations().create(InternalModelHelper.nameOfRun(this, "", "additionalRuntimeClasspath"), configuration -> { + configuration = project.getConfigurations().register(InternalModelHelper.nameOfRun(this, "", "additionalRuntimeClasspath"), configuration -> { configuration.setCanBeResolved(false); configuration.setCanBeConsumed(false); }); @@ -235,7 +236,7 @@ public void taskBefore(Task task) { } public Configuration getAdditionalRuntimeClasspathConfiguration() { - return configuration; + return configuration.get(); } /** diff --git a/src/main/java/net/neoforged/moddevgradle/internal/DataFileCollections.java b/src/main/java/net/neoforged/moddevgradle/internal/DataFileCollections.java index 2079587e..5268e8e2 100644 --- a/src/main/java/net/neoforged/moddevgradle/internal/DataFileCollections.java +++ b/src/main/java/net/neoforged/moddevgradle/internal/DataFileCollections.java @@ -11,6 +11,7 @@ import org.gradle.api.artifacts.Configuration; import org.gradle.api.attributes.Category; import org.gradle.api.component.AdhocComponentWithVariants; +import org.gradle.api.provider.Provider; import org.gradle.api.tasks.SourceSet; import org.jetbrains.annotations.ApiStatus; @@ -64,10 +65,10 @@ public static DataFileCollections create(Project project) { return new DataFileCollections(accessTransformers, interfaceInjectionData); } - public record CollectionWrapper(DataFileCollection extension, Configuration configuration) {} + public record CollectionWrapper(DataFileCollection extension, Provider configuration) {} private static CollectionWrapper createCollection(Project project, String name, String description, String category) { - var configuration = project.getConfigurations().create(name, spec -> { + var configuration = project.getConfigurations().register(name, spec -> { spec.setDescription(description); spec.setCanBeConsumed(false); spec.setCanBeResolved(true); @@ -76,7 +77,7 @@ private static CollectionWrapper createCollection(Project project, String name, }); }); - var elementsConfiguration = project.getConfigurations().create(name + "Elements", spec -> { + var elementsConfiguration = project.getConfigurations().register(name + "Elements", spec -> { spec.setDescription("Published data files for " + name); spec.setCanBeConsumed(true); spec.setCanBeResolved(false); @@ -87,7 +88,8 @@ private static CollectionWrapper createCollection(Project project, String name, // Set up the variant publishing conditionally var java = (AdhocComponentWithVariants) project.getComponents().getByName("java"); - java.addVariantsFromConfiguration(elementsConfiguration, variant -> { + // TODO: no better way? + java.addVariantsFromConfiguration(elementsConfiguration.get(), variant -> { // This should be invoked lazily, so checking if the artifacts are empty is fine: // "The details object used to determine what to do with a configuration variant **when publishing**." if (variant.getConfigurationVariant().getArtifacts().isEmpty()) { @@ -110,7 +112,8 @@ public void accept(Object artifactNotation) { var artifactFile = dummyArtifact.getFile(); var artifactDependencies = dummyArtifact.getBuildDependencies(); - elementsConfiguration.getArtifacts().remove(dummyArtifact); + // TODO: no better way? + elementsConfiguration.get().getArtifacts().remove(dummyArtifact); var copyOutput = project.getLayout().getBuildDirectory().file(copyTaskName + "/" + artifactCount + "-" + artifactFile.getName()); copyTask.configure(t -> { @@ -136,7 +139,9 @@ public void accept(Object artifactNotation) { }; var extension = project.getObjects().newInstance(DataFileCollection.class, publishCallback); - configuration.getDependencies().add(depFactory.create(extension.getFiles())); + configuration.configure(c -> { + c.getDependencies().add(depFactory.create(extension.getFiles())); + }); return new CollectionWrapper(extension, configuration); } diff --git a/src/main/java/net/neoforged/moddevgradle/internal/ModDevArtifactsWorkflow.java b/src/main/java/net/neoforged/moddevgradle/internal/ModDevArtifactsWorkflow.java index 4dbf034e..e46850ab 100644 --- a/src/main/java/net/neoforged/moddevgradle/internal/ModDevArtifactsWorkflow.java +++ b/src/main/java/net/neoforged/moddevgradle/internal/ModDevArtifactsWorkflow.java @@ -14,6 +14,7 @@ import org.gradle.api.GradleException; import org.gradle.api.InvalidUserCodeException; import org.gradle.api.Named; +import org.gradle.api.NamedDomainObjectProvider; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.Dependency; @@ -45,8 +46,8 @@ public record ModDevArtifactsWorkflow( TaskProvider createArtifacts, Provider minecraftClassesDependency, TaskProvider downloadAssets, - Configuration runtimeDependencies, - Configuration compileDependencies, + NamedDomainObjectProvider runtimeDependencies, + NamedDomainObjectProvider compileDependencies, Provider modDevBuildDir, Provider artifactsBuildDir) { @@ -102,7 +103,7 @@ public static ModDevArtifactsWorkflow create(Project project, } var parchment = extension.getParchment(); - var parchmentData = configurations.create("parchmentData", spec -> { + var parchmentData = configurations.register("parchmentData", spec -> { spec.setDescription("Data used to add parameter names and javadoc to Minecraft sources"); spec.setCanBeResolved(true); spec.setCanBeConsumed(false); @@ -168,7 +169,7 @@ public static ModDevArtifactsWorkflow create(Project project, // Name of the configuration in which we place the required dependencies to develop mods for use in the runtime-classpath. // We cannot use "runtimeOnly", since the contents of that are published. - var runtimeDependencies = configurations.create("modDevRuntimeDependencies", config -> { + var runtimeDependencies = configurations.register("modDevRuntimeDependencies", config -> { config.setDescription("The runtime dependencies to develop a mod for, including Minecraft classes and modding platform classes."); config.setCanBeResolved(false); config.setCanBeConsumed(false); @@ -182,7 +183,7 @@ public static ModDevArtifactsWorkflow create(Project project, // Configuration in which we place the required dependencies to develop mods for use in the compile-classpath. // While compile only is not published, we also use a configuration here to be consistent. - var compileDependencies = configurations.create("modDevCompileDependencies", config -> { + var compileDependencies = configurations.register("modDevCompileDependencies", config -> { config.setDescription("The compile-time dependencies to develop a mod, including Minecraft and modding platform classes."); config.setCanBeResolved(false); config.setCanBeConsumed(false); @@ -222,7 +223,7 @@ public static ModDevArtifactsWorkflow create(Project project, /** * Collects all dependencies needed by the NeoFormRuntime */ - private static List configureArtifactManifestConfigurations( + private static List> configureArtifactManifestConfigurations( Project project, @Nullable ModuleDependency moddingPlatformDependency, @Nullable ModuleDependency recompilableMinecraftWorkflowDependency) { @@ -230,11 +231,11 @@ private static List configureArtifactManifestConfigurations( var configurationPrefix = "neoFormRuntimeDependencies"; - var result = new ArrayList(); + var result = new ArrayList>(); // Gradle prevents us from having dependencies with "incompatible attributes" in the same configuration. // What constitutes incompatible cannot be overridden on a per-configuration basis. - var neoForgeClassesAndData = configurations.create(configurationPrefix + "NeoForgeClasses", spec -> { + var neoForgeClassesAndData = configurations.register(configurationPrefix + "NeoForgeClasses", spec -> { spec.setDescription("Dependencies needed for running NeoFormRuntime for the selected NeoForge/NeoForm version (NeoForge classes)"); spec.setCanBeConsumed(false); spec.setCanBeResolved(true); @@ -252,7 +253,7 @@ private static List configureArtifactManifestConfigurations( result.add(neoForgeClassesAndData); if (moddingPlatformDependency != null) { - var neoForgeSources = configurations.create(configurationPrefix + "NeoForgeSources", spec -> { + var neoForgeSources = configurations.register(configurationPrefix + "NeoForgeSources", spec -> { spec.setDescription("Dependencies needed for running NeoFormRuntime for the selected NeoForge/NeoForm version (NeoForge sources)"); spec.setCanBeConsumed(false); spec.setCanBeResolved(true); @@ -267,7 +268,7 @@ private static List configureArtifactManifestConfigurations( // Compile-time dependencies used by NeoForm, NeoForge and Minecraft. // Also includes any classes referenced by compiled Minecraft code (used by decompilers, renamers, etc.) - var compileClasspath = configurations.create(configurationPrefix + "CompileClasspath", spec -> { + var compileClasspath = configurations.register(configurationPrefix + "CompileClasspath", spec -> { spec.setDescription("Dependencies needed for running NeoFormRuntime for the selected NeoForge/NeoForm version (Classpath)"); spec.setCanBeConsumed(false); spec.setCanBeResolved(true); @@ -288,7 +289,7 @@ private static List configureArtifactManifestConfigurations( result.add(compileClasspath); // Runtime-time dependencies used by NeoForm, NeoForge and Minecraft. - var runtimeClasspath = configurations.create(configurationPrefix + "RuntimeClasspath", spec -> { + var runtimeClasspath = configurations.register(configurationPrefix + "RuntimeClasspath", spec -> { spec.setDescription("Dependencies needed for running NeoFormRuntime for the selected NeoForge/NeoForm version (Classpath)"); spec.setCanBeConsumed(false); spec.setCanBeResolved(true); @@ -322,8 +323,9 @@ public void addToSourceSet(SourceSet sourceSet) { throw new GradleException("Cannot add to the source set in another project: " + sourceSet); } - configurations.getByName(sourceSet.getRuntimeClasspathConfigurationName()).extendsFrom(runtimeDependencies); - configurations.getByName(sourceSet.getCompileClasspathConfigurationName()).extendsFrom(compileDependencies); + // TODO: no better way? + configurations.named(sourceSet.getRuntimeClasspathConfigurationName(), c -> c.extendsFrom(runtimeDependencies.get())); + configurations.named(sourceSet.getCompileClasspathConfigurationName(), c -> c.extendsFrom(compileDependencies.get())); } public Provider requestAdditionalMinecraftArtifact(String id, String filename) { diff --git a/src/main/java/net/neoforged/moddevgradle/internal/ModDevRunWorkflow.java b/src/main/java/net/neoforged/moddevgradle/internal/ModDevRunWorkflow.java index 246f6212..0b44a75a 100644 --- a/src/main/java/net/neoforged/moddevgradle/internal/ModDevRunWorkflow.java +++ b/src/main/java/net/neoforged/moddevgradle/internal/ModDevRunWorkflow.java @@ -18,6 +18,7 @@ import org.gradle.api.DomainObjectCollection; import org.gradle.api.InvalidUserCodeException; import org.gradle.api.Named; +import org.gradle.api.NamedDomainObjectProvider; import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration; @@ -59,8 +60,8 @@ public class ModDevRunWorkflow { @Nullable private final ModuleDependency testFixturesDependency; private final ModuleDependency gameLibrariesDependency; - private final Configuration additionalClasspath; - private final Configuration userDevConfigOnly; + private final NamedDomainObjectProvider additionalClasspath; + private final NamedDomainObjectProvider userDevConfigOnly; /** * @param gameLibrariesDependency A module dependency that represents the library dependencies of the game. @@ -87,7 +88,7 @@ private ModDevRunWorkflow(Project project, // Let's try to get the userdev JSON out of the universal jar // I don't like having to use a configuration for this... - userDevConfigOnly = configurations.create("neoForgeConfigOnly", spec -> { + userDevConfigOnly = configurations.register("neoForgeConfigOnly", spec -> { spec.setDescription("Resolves exclusively the NeoForge userdev JSON for configuring runs"); spec.setCanBeResolved(true); spec.setCanBeConsumed(false); @@ -97,7 +98,7 @@ private ModDevRunWorkflow(Project project, } }); - additionalClasspath = configurations.create("additionalRuntimeClasspath", spec -> { + additionalClasspath = configurations.register("additionalRuntimeClasspath", spec -> { spec.setDescription("Contains dependencies of every run, that should not be considered boot classpath modules."); spec.setCanBeResolved(true); spec.setCanBeConsumed(false); @@ -122,7 +123,7 @@ private ModDevRunWorkflow(Project project, modulePath.getDependencies().add(modulePathDependency); } }, - legacyClassPath -> legacyClassPath.extendsFrom(additionalClasspath), + legacyClassPath -> legacyClassPath.extendsFrom(additionalClasspath.get()), artifactsWorkflow.downloadAssets().flatMap(DownloadAssets::getAssetPropertiesFile), versionCapabilities); } @@ -224,7 +225,7 @@ public static void setupRuns( // Create a configuration to resolve DevLaunch and DevLogin without leaking them to consumers var supplyDevLogin = project.provider(() -> runs.stream().anyMatch(model -> model.getDevLogin().get())); - var devLaunchConfig = project.getConfigurations().create("devLaunchConfig", spec -> { + var devLaunchConfig = project.getConfigurations().register("devLaunchConfig", spec -> { spec.setDescription("This configuration is used to inject DevLaunch and optionally DevLogin into the runtime classpaths of runs."); spec.getDependencies().add(dependencyFactory.create(RunUtils.DEV_LAUNCH_GAV)); spec.getDependencies().addAllLater(supplyDevLogin.map( @@ -276,7 +277,7 @@ private static TaskProvider setupRunInGradle( Consumer configureModulePath, Consumer configureLegacyClasspath, // TODO: can be removed in favor of directly passing a configuration for the moddev libraries Provider assetPropertiesFile, - Configuration devLaunchConfig, + Provider devLaunchConfig, VersionCapabilitiesInternal versionCapabilities, TaskProvider createLaunchScriptsTask) { var ideIntegration = IdeIntegration.of(project, branding); @@ -289,12 +290,13 @@ private static TaskProvider setupRunInGradle( // Sucks, but what can you do... Only at the end do we actually know which source set this run will use project.afterEvaluate(ignored -> { - runtimeClasspathConfig.get().extendsFrom(devLaunchConfig); + // TODO: this .get() forces a resolution + runtimeClasspathConfig.get().extendsFrom(devLaunchConfig.get()); }); var type = RunUtils.getRequiredType(project, run); - var modulePathConfiguration = project.getConfigurations().create(InternalModelHelper.nameOfRun(run, "", "modulesOnly"), spec -> { + var modulePathConfiguration = configurations.register(InternalModelHelper.nameOfRun(run, "", "modulesOnly"), spec -> { spec.setDescription("Libraries that should be placed on the JVMs boot module path for run " + run.getName() + "."); spec.setCanBeResolved(true); spec.setCanBeConsumed(false); @@ -302,7 +304,7 @@ private static TaskProvider setupRunInGradle( configureModulePath.accept(spec); }); - var legacyClasspathConfiguration = configurations.create(InternalModelHelper.nameOfRun(run, "", "legacyClasspath"), spec -> { + var legacyClasspathConfiguration = configurations.register(InternalModelHelper.nameOfRun(run, "", "legacyClasspath"), spec -> { spec.setDescription("Contains all dependencies of the " + run.getName() + " run that should not be considered boot classpath modules."); spec.setCanBeResolved(true); spec.setCanBeConsumed(false); @@ -414,7 +416,7 @@ static void setupTestTask(Project project, var testRuntimeClasspath = configurations.getByName(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME); - var neoForgeModDevModules = project.getConfigurations().create("neoForgeTestModules", spec -> { + var neoForgeModDevModules = configurations.register("neoForgeTestModules", spec -> { spec.setDescription("Libraries that should be placed on the JVMs boot module path for unit tests."); spec.setCanBeResolved(true); spec.setCanBeConsumed(false); @@ -422,7 +424,7 @@ static void setupTestTask(Project project, configureModulePath.accept(spec); }); - var legacyClasspathConfiguration = configurations.create("neoForgeTestLibraries", spec -> { + var legacyClasspathConfiguration = configurations.register("neoForgeTestLibraries", spec -> { spec.setDescription("Contains the legacy classpath of unit tests."); spec.setCanBeResolved(true); spec.setCanBeConsumed(false); @@ -484,10 +486,6 @@ static void setupTestTask(Project project, ideIntegration.configureTesting(loadedMods, testedMod, runArgsDir, gameDirectory, programArgsFile, vmArgsFile); } - public Configuration getAdditionalClasspath() { - return additionalClasspath; - } - private static void setNamedAttribute(Project project, AttributeContainer attributes, Attribute attribute, String value) { attributes.attribute(attribute, project.getObjects().named(attribute.getType(), value)); } diff --git a/src/main/java/net/neoforged/moddevgradle/internal/jarjar/JarJarArtifacts.java b/src/main/java/net/neoforged/moddevgradle/internal/jarjar/JarJarArtifacts.java index 9b6c0744..eb476d4e 100644 --- a/src/main/java/net/neoforged/moddevgradle/internal/jarjar/JarJarArtifacts.java +++ b/src/main/java/net/neoforged/moddevgradle/internal/jarjar/JarJarArtifacts.java @@ -28,6 +28,8 @@ import org.gradle.api.artifacts.type.ArtifactTypeDefinition; import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Provider; +import org.gradle.api.provider.ProviderFactory; import org.gradle.api.provider.SetProperty; import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.Nested; @@ -53,6 +55,9 @@ protected SetProperty getIncludedArtifacts() { @Inject protected abstract ObjectFactory getObjectFactory(); + @Inject + protected abstract ProviderFactory getProviderFactory(); + @Nested public abstract ListProperty getResolvedArtifacts(); @@ -67,11 +72,15 @@ public JarJarArtifacts() { } public void configuration(Configuration jarJarConfiguration) { - getIncludedArtifacts().addAll(jarJarConfiguration.getIncoming().artifactView(config -> { + configuration(getProviderFactory().provider(() -> jarJarConfiguration)); + } + + public void configuration(Provider jarJarConfiguration) { + getIncludedArtifacts().addAll(jarJarConfiguration.flatMap(c -> c.getIncoming().artifactView(config -> { config.attributes( attr -> attr.attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, ArtifactTypeDefinition.JAR_TYPE)); - }).getArtifacts().getResolvedArtifacts()); - getIncludedRootComponents().add(jarJarConfiguration.getIncoming().getResolutionResult().getRootComponent()); + }).getArtifacts().getResolvedArtifacts())); + getIncludedRootComponents().add(jarJarConfiguration.flatMap(c -> c.getIncoming().getResolutionResult().getRootComponent())); } public void setConfigurations(Collection configurations) { diff --git a/src/main/java/net/neoforged/moddevgradle/tasks/JarJar.java b/src/main/java/net/neoforged/moddevgradle/tasks/JarJar.java index 50e9cc01..d0bafc17 100644 --- a/src/main/java/net/neoforged/moddevgradle/tasks/JarJar.java +++ b/src/main/java/net/neoforged/moddevgradle/tasks/JarJar.java @@ -30,6 +30,7 @@ import org.gradle.api.file.FileSystemOperations; import org.gradle.api.model.ObjectFactory; import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.provider.Provider; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.Nested; @@ -78,26 +79,27 @@ public JarJar(FileSystemOperations fileSystemOperations) { * the dependencies to embed are sourced. */ public static TaskProvider registerWithConfiguration(Project project, String name) { - var configuration = project.getConfigurations().create(name); - configuration.setTransitive(false); - // jarJar configurations should be resolvable, but ought not to be exposed to consumers; - // as it has attributes, it could conflict with normal exposed configurations - configuration.setCanBeResolved(true); - configuration.setCanBeConsumed(false); - - var javaPlugin = project.getExtensions().getByType(JavaPluginExtension.class); - - configuration.attributes(attributes -> { - // Unfortunately, while we can hopefully rely on disambiguation rules to get us some of these, others run - // into issues. The target JVM version is the most worrying - we don't want to pull in a variant for a newer - // jvm version. We could copy DefaultJvmFeature, and search for the target version of the compile task, - // but this is difficult - we only have a feature name, not the linked source set. For this reason, we use - // the toolchain version, which is the most likely to be correct. - attributes.attributeProvider(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, javaPlugin.getToolchain().getLanguageVersion().orElse(JavaLanguageVersion.current()).map(JavaLanguageVersion::asInt)); - attributes.attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME)); - attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.getObjects().named(LibraryElements.class, LibraryElements.JAR)); - attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, Category.LIBRARY)); - attributes.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.EXTERNAL)); + var configuration = project.getConfigurations().register(name, c -> { + c.setTransitive(false); + // jarJar configurations should be resolvable, but ought not to be exposed to consumers; + // as it has attributes, it could conflict with normal exposed configurations + c.setCanBeResolved(true); + c.setCanBeConsumed(false); + + var javaPlugin = project.getExtensions().getByType(JavaPluginExtension.class); + + c.attributes(attributes -> { + // Unfortunately, while we can hopefully rely on disambiguation rules to get us some of these, others run + // into issues. The target JVM version is the most worrying - we don't want to pull in a variant for a newer + // jvm version. We could copy DefaultJvmFeature, and search for the target version of the compile task, + // but this is difficult - we only have a feature name, not the linked source set. For this reason, we use + // the toolchain version, which is the most likely to be correct. + attributes.attributeProvider(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, javaPlugin.getToolchain().getLanguageVersion().orElse(JavaLanguageVersion.current()).map(JavaLanguageVersion::asInt)); + attributes.attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME)); + attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.getObjects().named(LibraryElements.class, LibraryElements.JAR)); + attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, Category.LIBRARY)); + attributes.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.EXTERNAL)); + }); }); return project.getTasks().register(name, JarJar.class, jarJar -> { @@ -187,6 +189,10 @@ private Path writeMetadata(List includedJars) { } public void configuration(Configuration jarJarConfiguration) { + configuration(getProject().provider(() -> jarJarConfiguration)); + } + + public void configuration(Provider jarJarConfiguration) { getInputFiles().from(jarJarConfiguration); getJarJarArtifacts().configuration(jarJarConfiguration); dependsOn(jarJarConfiguration); diff --git a/src/main/java/net/neoforged/nfrtgradle/NeoFormRuntimePlugin.java b/src/main/java/net/neoforged/nfrtgradle/NeoFormRuntimePlugin.java index c84d1bbb..8cf558a9 100644 --- a/src/main/java/net/neoforged/nfrtgradle/NeoFormRuntimePlugin.java +++ b/src/main/java/net/neoforged/nfrtgradle/NeoFormRuntimePlugin.java @@ -16,7 +16,7 @@ public void apply(Project project) { var nfrtDependency = extension.getVersion().map(version -> dependencyFactory.create("net.neoforged:neoform-runtime:" + version)); - var toolConfiguration = configurations.create("neoFormRuntimeTool", spec -> { + var toolConfiguration = configurations.register("neoFormRuntimeTool", spec -> { spec.setDescription("The NeoFormRuntime CLI tool"); spec.setCanBeConsumed(false); spec.setCanBeResolved(true); @@ -27,7 +27,7 @@ public void apply(Project project) { }); }); - var externalToolsConfiguration = configurations.create("neoFormRuntimeExternalTools", spec -> { + var externalToolsConfiguration = configurations.register("neoFormRuntimeExternalTools", spec -> { spec.setDescription("The external tools used by NeoFormRuntime"); spec.setCanBeConsumed(false); spec.setCanBeResolved(true); diff --git a/src/main/java/net/neoforged/nfrtgradle/NeoFormRuntimeTask.java b/src/main/java/net/neoforged/nfrtgradle/NeoFormRuntimeTask.java index 15c83004..49cd2c71 100644 --- a/src/main/java/net/neoforged/nfrtgradle/NeoFormRuntimeTask.java +++ b/src/main/java/net/neoforged/nfrtgradle/NeoFormRuntimeTask.java @@ -17,6 +17,7 @@ import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; import org.gradle.api.provider.SetProperty; import org.gradle.api.tasks.Classpath; import org.gradle.api.tasks.Input; @@ -161,9 +162,20 @@ public final void run(List args) { * dependency coordinate, instead of downloading them. */ public final void addArtifactsToManifest(Configuration configuration) { - artifactManifestEntries.addAll(configuration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { - return results.stream().map(ArtifactManifestEntry::new).collect(Collectors.toSet()); - })); + addArtifactsToManifest(getProject().provider(() -> configuration)); + } + + /** + * Add all incoming dependencies in the given configuration to the artifact manifest passed to NFRT. + * This causes NFRT to use files from the configuration when trying to resolve the same + * dependency coordinate, instead of downloading them. + */ + public final void addArtifactsToManifest(Provider configuration) { + artifactManifestEntries.addAll(configuration + .flatMap(c -> c.getIncoming().getArtifacts().getResolvedArtifacts()) + .map(results -> { + return results.stream().map(ArtifactManifestEntry::new).collect(Collectors.toSet()); + })); artifacts.from(configuration); } From 6ffc8b5576ed0742404f4bce1709432efd992d29 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Sun, 27 Apr 2025 12:56:23 +0200 Subject: [PATCH 2/3] Fun --- .../neoforged/moddevgradle/internal/ModDevRunWorkflow.java | 5 ++--- src/main/java/net/neoforged/moddevgradle/tasks/JarJar.java | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/neoforged/moddevgradle/internal/ModDevRunWorkflow.java b/src/main/java/net/neoforged/moddevgradle/internal/ModDevRunWorkflow.java index 0b44a75a..7bbdb199 100644 --- a/src/main/java/net/neoforged/moddevgradle/internal/ModDevRunWorkflow.java +++ b/src/main/java/net/neoforged/moddevgradle/internal/ModDevRunWorkflow.java @@ -286,12 +286,11 @@ private static TaskProvider setupRunInGradle( var tasks = project.getTasks(); var runtimeClasspathConfig = run.getSourceSet().map(SourceSet::getRuntimeClasspathConfigurationName) - .map(configurations::getByName); + .map(configurations::named); // Sucks, but what can you do... Only at the end do we actually know which source set this run will use project.afterEvaluate(ignored -> { - // TODO: this .get() forces a resolution - runtimeClasspathConfig.get().extendsFrom(devLaunchConfig.get()); + runtimeClasspathConfig.get().configure(c -> c.extendsFrom(devLaunchConfig.get())); }); var type = RunUtils.getRequiredType(project, run); diff --git a/src/main/java/net/neoforged/moddevgradle/tasks/JarJar.java b/src/main/java/net/neoforged/moddevgradle/tasks/JarJar.java index d0bafc17..88d0fabf 100644 --- a/src/main/java/net/neoforged/moddevgradle/tasks/JarJar.java +++ b/src/main/java/net/neoforged/moddevgradle/tasks/JarJar.java @@ -198,6 +198,7 @@ public void configuration(Provider jarJarConfiguration) { dependsOn(jarJarConfiguration); } + // TODO: do something about this? public void setConfigurations(Collection configurations) { var newConfig = getObjects().fileCollection(); newConfig.from(configurations.toArray()); From b0ba8327434dd7527583a9cd3f3e1eeda63e2a8a Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Tue, 13 May 2025 00:32:47 +0200 Subject: [PATCH 3/3] Tried to make progress but Gradle makes most configs non-lazy --- legacytest/gradle/wrapper/gradle-wrapper.properties | 2 +- .../legacyforge/dsl/ObfuscationExtension.java | 9 +++++---- .../legacyforge/internal/LegacyForgeModDevPlugin.java | 7 +++++++ .../moddevgradle/internal/ModDevRunWorkflow.java | 5 +++-- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/legacytest/gradle/wrapper/gradle-wrapper.properties b/legacytest/gradle/wrapper/gradle-wrapper.properties index c1d5e018..6514f919 100644 --- a/legacytest/gradle/wrapper/gradle-wrapper.properties +++ b/legacytest/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/legacy/java/net/neoforged/moddevgradle/legacyforge/dsl/ObfuscationExtension.java b/src/legacy/java/net/neoforged/moddevgradle/legacyforge/dsl/ObfuscationExtension.java index 49858dd9..0b6d237a 100644 --- a/src/legacy/java/net/neoforged/moddevgradle/legacyforge/dsl/ObfuscationExtension.java +++ b/src/legacy/java/net/neoforged/moddevgradle/legacyforge/dsl/ObfuscationExtension.java @@ -29,16 +29,17 @@ public abstract class ObfuscationExtension { private final Project project; - private final Configuration autoRenamingToolRuntime; - private final Configuration installerToolsRuntime; + private final Provider autoRenamingToolRuntime; + private final Provider installerToolsRuntime; private final FileCollection extraMixinMappings; private final MinecraftMappings namedMappings; + @ApiStatus.Internal @Inject public ObfuscationExtension(Project project, - Configuration autoRenamingToolRuntime, - Configuration installerToolsRuntime, + Provider autoRenamingToolRuntime, + Provider installerToolsRuntime, FileCollection extraMixinMappings) { this.project = project; this.autoRenamingToolRuntime = autoRenamingToolRuntime; diff --git a/src/legacy/java/net/neoforged/moddevgradle/legacyforge/internal/LegacyForgeModDevPlugin.java b/src/legacy/java/net/neoforged/moddevgradle/legacyforge/internal/LegacyForgeModDevPlugin.java index c8a9a33d..7b9c2738 100644 --- a/src/legacy/java/net/neoforged/moddevgradle/legacyforge/internal/LegacyForgeModDevPlugin.java +++ b/src/legacy/java/net/neoforged/moddevgradle/legacyforge/internal/LegacyForgeModDevPlugin.java @@ -1,6 +1,7 @@ package net.neoforged.moddevgradle.legacyforge.internal; import java.net.URI; +import java.util.List; import java.util.stream.Stream; import javax.inject.Inject; import net.neoforged.minecraftdependencies.MinecraftDependenciesPlugin; @@ -19,6 +20,7 @@ import net.neoforged.moddevgradle.legacyforge.dsl.MixinExtension; import net.neoforged.moddevgradle.legacyforge.dsl.ObfuscationExtension; import net.neoforged.nfrtgradle.NeoFormRuntimePlugin; +import org.gradle.api.GradleException; import org.gradle.api.InvalidUserCodeException; import org.gradle.api.Plugin; import org.gradle.api.Project; @@ -56,6 +58,11 @@ public LegacyForgeModDevPlugin(ObjectFactory objectFactory) { @Override public void apply(Project project) { + project.getConfigurations().configureEach(c -> { + LOG.warn("Configuring configuration " + c.getName()); + throw new GradleException("Configuring configuration " + c.getName()); + }); + project.getPlugins().apply(JavaLibraryPlugin.class); project.getPlugins().apply(NeoFormRuntimePlugin.class); project.getPlugins().apply(MinecraftDependenciesPlugin.class); diff --git a/src/main/java/net/neoforged/moddevgradle/internal/ModDevRunWorkflow.java b/src/main/java/net/neoforged/moddevgradle/internal/ModDevRunWorkflow.java index e68c8b78..bcb8a5a4 100644 --- a/src/main/java/net/neoforged/moddevgradle/internal/ModDevRunWorkflow.java +++ b/src/main/java/net/neoforged/moddevgradle/internal/ModDevRunWorkflow.java @@ -295,11 +295,12 @@ private static TaskProvider setupRunInGradle( var type = RunUtils.getRequiredType(project, run); + // TODO: should potentially move the .get().get() to afterEvaluate to give the modder a chance to change the source set? var modulePathConfiguration = configurations.register(InternalModelHelper.nameOfRun(run, "", "modulesOnly"), spec -> { spec.setDescription("Libraries that should be placed on the JVMs boot module path for run " + run.getName() + "."); spec.setCanBeResolved(true); spec.setCanBeConsumed(false); - spec.shouldResolveConsistentlyWith(runtimeClasspathConfig.get()); + spec.shouldResolveConsistentlyWith(runtimeClasspathConfig.get().get()); configureModulePath.accept(spec); }); @@ -307,7 +308,7 @@ private static TaskProvider setupRunInGradle( spec.setDescription("Contains all dependencies of the " + run.getName() + " run that should not be considered boot classpath modules."); spec.setCanBeResolved(true); spec.setCanBeConsumed(false); - spec.shouldResolveConsistentlyWith(runtimeClasspathConfig.get()); + spec.shouldResolveConsistentlyWith(runtimeClasspathConfig.get().get()); spec.attributes(attributes -> { attributes.attributeProvider(MinecraftDistribution.ATTRIBUTE, type.map(t -> { var name = t.equals("client") || t.equals("data") || t.equals("clientData") ? MinecraftDistribution.CLIENT : MinecraftDistribution.SERVER;