diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e7646dea..a4413138 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a42..b740cf13 100644 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. diff --git a/src/main/java/net/neoforged/moddevgradle/dsl/NeoFormRuntime.java b/src/main/java/net/neoforged/moddevgradle/dsl/NeoFormRuntime.java index aeb23cfc..9b45e621 100644 --- a/src/main/java/net/neoforged/moddevgradle/dsl/NeoFormRuntime.java +++ b/src/main/java/net/neoforged/moddevgradle/dsl/NeoFormRuntime.java @@ -7,7 +7,7 @@ import javax.inject.Inject; public abstract class NeoFormRuntime { - private static final String DEFAULT_NFRT_VERSION = "0.1.36"; + private static final String DEFAULT_NFRT_VERSION = "0.1.38"; @Inject public NeoFormRuntime(Project project) { diff --git a/src/main/java/net/neoforged/moddevgradle/internal/CreateMinecraftArtifactsTask.java b/src/main/java/net/neoforged/moddevgradle/internal/CreateMinecraftArtifactsTask.java index 49b42af1..94e68ccf 100644 --- a/src/main/java/net/neoforged/moddevgradle/internal/CreateMinecraftArtifactsTask.java +++ b/src/main/java/net/neoforged/moddevgradle/internal/CreateMinecraftArtifactsTask.java @@ -4,7 +4,6 @@ import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.Property; -import org.gradle.api.tasks.Classpath; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.InputFiles; @@ -12,15 +11,19 @@ import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.OutputFile; import org.gradle.api.tasks.TaskAction; +import org.gradle.work.DisableCachingByDefault; import javax.inject.Inject; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +@DisableCachingByDefault(because = "Implements its own caching") abstract class CreateMinecraftArtifactsTask extends NeoFormRuntimeTask { @Inject public CreateMinecraftArtifactsTask() { + // When cache is disabled, the task is NEVER up-to-date to aid with debugging problems + getOutputs().upToDateWhen(task -> ((CreateMinecraftArtifactsTask) task).getEnableCache().get()); } @InputFile @@ -36,10 +39,6 @@ public CreateMinecraftArtifactsTask() { @Input abstract Property getNeoForgeArtifact(); - @Classpath - @InputFiles - abstract ConfigurableFileCollection getCompileClasspath(); - @InputFiles abstract ConfigurableFileCollection getAccessTransformers(); diff --git a/src/main/java/net/neoforged/moddevgradle/internal/ModDevPlugin.java b/src/main/java/net/neoforged/moddevgradle/internal/ModDevPlugin.java index 12da38ed..89c656f9 100644 --- a/src/main/java/net/neoforged/moddevgradle/internal/ModDevPlugin.java +++ b/src/main/java/net/neoforged/moddevgradle/internal/ModDevPlugin.java @@ -11,6 +11,7 @@ import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ExternalModuleDependency; import org.gradle.api.artifacts.ModuleDependency; import org.gradle.api.artifacts.dsl.RepositoryHandler; import org.gradle.api.artifacts.repositories.MavenArtifactRepository; @@ -103,47 +104,11 @@ public void apply(Project project) { })); addTemporaryRepositories(repositories); - // Configuration for all artifact that should be passed to NFRT for preventing repeated downloads - var neoFormRuntimeArtifactManifestNeoForgeClasses = configurations.create("neoFormRuntimeArtifactManifestNeoForgeClasses", spec -> { - spec.setCanBeConsumed(false); - spec.setCanBeResolved(true); - spec.withDependencies(dependencies -> { - // Add the dep on NeoForge itself - dependencies.addLater(extension.getVersion().map(version -> { - return dependencyFactory.create("net.neoforged:neoforge:" + version) - .capabilities(caps -> { - caps.requireCapability("net.neoforged:neoforge-moddev-bundle"); - }); - })); - }); - }); - // Configuration for all artifact that should be passed to NFRT for preventing repeated downloads - var neoFormRuntimeArtifactManifestNeoForgeSources = configurations.create("neoFormRuntimeArtifactManifestNeoForgeSources", spec -> { - spec.setCanBeConsumed(false); - spec.setCanBeResolved(true); - spec.withDependencies(dependencies -> { - // Add the dep on NeoForge itself - dependencies.addLater(extension.getVersion().map(version -> { - return dependencyFactory.create("net.neoforged:neoforge:" + version) - .attributes(attributes -> { - attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, Category.DOCUMENTATION)); - attributes.attribute(DocsType.DOCS_TYPE_ATTRIBUTE, project.getObjects().named(DocsType.class, DocsType.SOURCES)); - }); - })); - }); - }); - var neoFormRuntimeArtifactManifestNeoForm = configurations.create("neoFormRuntimeArtifactManifestNeoForm", spec -> { - spec.setCanBeConsumed(false); - spec.setCanBeResolved(true); - spec.withDependencies(dependencies -> { - dependencies.addLater(extension.getVersion().map(version -> { - return dependencyFactory.create("net.neoforged:neoforge:" + version) - .capabilities(caps -> { - caps.requireCapability("net.neoforged:neoforge-dependencies"); - }); - })); - }); + var createManifest = tasks.register("createArtifactManifest", CreateArtifactManifestTask.class, task -> { + configureArtifactManifestTask(task, extension); + task.getManifestFile().set(modDevBuildDir.map(dir -> dir.file("nfrt_artifact_manifest.properties"))); }); + var neoFormRuntimeConfig = configurations.create("neoFormRuntime", files -> { files.setCanBeConsumed(false); files.setCanBeResolved(true); @@ -167,50 +132,6 @@ public void apply(Project project) { }); }); - // This configuration will include the classpath needed to decompile and recompile Minecraft, - // and has to include the libraries added by NeoForm and NeoForge. - var minecraftCompileClasspath = configurations.create("minecraftCompileClasspath", spec -> { - spec.setCanBeResolved(true); - spec.setCanBeConsumed(false); - spec.setVisible(false); - spec.withDependencies(set -> set.addLater(neoForgeModDevLibrariesDependency)); - spec.attributes(attributes -> { - attributes.attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_API)); - attributes.attribute(ATTRIBUTE_DISTRIBUTION, "client"); - }); - }); - - var createManifest = tasks.register("createArtifactManifest", CreateArtifactManifestTask.class, task -> { - task.getNeoForgeModDevArtifacts().addAll(neoFormRuntimeArtifactManifestNeoForgeClasses.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { - return results.stream().map(result -> { - var gav = guessMavenGav(result); - return new ArtifactManifestEntry( - gav, - result.getFile() - ); - }).collect(Collectors.toSet()); - })); - task.getNeoForgeModDevArtifacts().addAll(neoFormRuntimeArtifactManifestNeoForgeSources.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { - return results.stream().map(result -> { - var gav = guessMavenGav(result); - return new ArtifactManifestEntry( - gav, - result.getFile() - ); - }).collect(Collectors.toSet()); - })); - task.getNeoForgeModDevArtifacts().addAll(neoFormRuntimeArtifactManifestNeoForm.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { - return results.stream().map(result -> { - var gav = guessMavenGav(result); - return new ArtifactManifestEntry( - gav, - result.getFile() - ); - }).collect(Collectors.toSet()); - })); - task.getManifestFile().set(modDevBuildDir.map(dir -> dir.file("nfrt_artifact_manifest.properties"))); - }); - // Add a filtered parchment repository automatically if enabled var parchment = extension.getParchment(); configureParchmentRepository(project, parchment); @@ -235,7 +156,6 @@ public void apply(Project project) { task.getAccessTransformers().from(accessTransformers); task.getParchmentData().from(parchmentData); task.getNeoFormRuntime().from(neoFormRuntimeConfig); - task.getCompileClasspath().from(minecraftCompileClasspath); var minecraftArtifactsDir = modDevBuildDir.map(dir -> dir.dir("artifacts")); task.getCompiledArtifact().set(minecraftArtifactsDir.map(dir -> dir.file("neoforge-minecraft-joined-local.jar"))); @@ -405,6 +325,87 @@ public void apply(Project project) { configureEclipseModel(project, ideSyncTask, createArtifacts, extension, prepareRunTasks); } + /** + * Collects all dependencies needed by the NeoFormRuntime and adds them to the task for creating + * an artifact manifest for NFRT. + */ + private void configureArtifactManifestTask(CreateArtifactManifestTask task, NeoForgeExtension extension) { + var project = task.getProject(); + var configurations = project.getConfigurations(); + var dependencyFactory = project.getDependencyFactory(); + + var configurationPrefix = "neoFormRuntimeDependencies"; + + Provider neoForgeDependency = extension.getVersion().map(version -> dependencyFactory.create("net.neoforged:neoforge:" + version)); + + // 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 neoForgeClasses = configurations.create(configurationPrefix + "NeoForgeClasses", spec -> { + spec.setDescription("Dependencies needed for running NeoFormRuntime for the selected NeoForge/NeoForm version (NeoForge classes)"); + spec.setCanBeConsumed(false); + spec.setCanBeResolved(true); + spec.withDependencies(depSpec -> depSpec.addLater(neoForgeDependency.map(dependency -> dependency.copy() + .capabilities(caps -> { + caps.requireCapability("net.neoforged:neoforge-moddev-bundle"); + })))); + }); + + var neoForgeSources = configurations.create(configurationPrefix + "NeoForgeSources", spec -> { + spec.setDescription("Dependencies needed for running NeoFormRuntime for the selected NeoForge/NeoForm version (NeoForge sources)"); + spec.setCanBeConsumed(false); + spec.setCanBeResolved(true); + spec.withDependencies(depSpec -> depSpec.addLater(neoForgeDependency)); + spec.attributes(attributes -> { + attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, Category.DOCUMENTATION)); + attributes.attribute(DocsType.DOCS_TYPE_ATTRIBUTE, project.getObjects().named(DocsType.class, DocsType.SOURCES)); + }); + }); + + // 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 -> { + spec.setDescription("Dependencies needed for running NeoFormRuntime for the selected NeoForge/NeoForm version (Classpath)"); + spec.setCanBeConsumed(false); + spec.setCanBeResolved(true); + spec.withDependencies(depSpec -> depSpec.addLater(neoForgeDependency.map(dependency -> dependency.copy() + .capabilities(caps -> { + caps.requireCapability("net.neoforged:neoforge-dependencies"); + })))); + spec.attributes(attributes -> { + attributes.attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_API)); + attributes.attribute(ATTRIBUTE_DISTRIBUTION, "client"); + }); + }); + + // Runtime-time dependencies used by NeoForm, NeoForge and Minecraft. + var runtimeClasspath = configurations.create(configurationPrefix + "RuntimeClasspath", spec -> { + spec.setDescription("Dependencies needed for running NeoFormRuntime for the selected NeoForge/NeoForm version (Classpath)"); + spec.setCanBeConsumed(false); + spec.setCanBeResolved(true); + spec.withDependencies(depSpec -> depSpec.addLater(neoForgeDependency.map(dependency -> dependency.copy() + .capabilities(caps -> { + caps.requireCapability("net.neoforged:neoforge-dependencies"); + })))); + spec.attributes(attributes -> { + attributes.attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME)); + attributes.attribute(ATTRIBUTE_DISTRIBUTION, "client"); + }); + }); + + for (var configuration : List.of(neoForgeClasses, neoForgeSources, compileClasspath, runtimeClasspath)) { + // Convert to a serializable representation for the task. + task.getNeoForgeModDevArtifacts().addAll(configuration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { + return results.stream().map(result -> { + var gav = guessMavenGav(result); + return new ArtifactManifestEntry( + gav, + result.getFile() + ); + }).collect(Collectors.toSet()); + })); + } + } + private static void configureParchmentRepository(Project project, Parchment parchment) { project.afterEvaluate(p -> { if (!parchment.getEnabled().get() || !parchment.getAddRepository().get()) { @@ -757,16 +758,16 @@ private static void configureEclipseModel(Project project, // TODO: This should be moved into its own task being triggered via eclipseModel.synchronizationTask System.out.println(System.getProperties()); if (System.getProperty("eclipse.application") != null) { - project.afterEvaluate(ignored -> { - for (var run : extension.getRuns()) { - var prepareTask = prepareRunTasks.get(run).get(); - if (!prepareTask.getEnabled()) { - project.getLogger().lifecycle("Not creating Eclipse run {} since its prepare task {} is disabled", run, prepareTask); - continue; + project.afterEvaluate(ignored -> { + for (var run : extension.getRuns()) { + var prepareTask = prepareRunTasks.get(run).get(); + if (!prepareTask.getEnabled()) { + project.getLogger().lifecycle("Not creating Eclipse run {} since its prepare task {} is disabled", run, prepareTask); + continue; + } + addEclipseLaunchConfiguration(project, null, run, prepareTask); } - addEclipseLaunchConfiguration(project, null, run, prepareTask); - } - }); + }); } } diff --git a/src/main/java/net/neoforged/moddevgradle/internal/NeoFormRuntimeTask.java b/src/main/java/net/neoforged/moddevgradle/internal/NeoFormRuntimeTask.java index b2989d88..a8c5600a 100644 --- a/src/main/java/net/neoforged/moddevgradle/internal/NeoFormRuntimeTask.java +++ b/src/main/java/net/neoforged/moddevgradle/internal/NeoFormRuntimeTask.java @@ -3,10 +3,12 @@ import org.gradle.api.DefaultTask; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.provider.Property; import org.gradle.api.tasks.Classpath; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.Internal; import org.gradle.jvm.toolchain.JavaLanguageVersion; +import org.gradle.jvm.toolchain.JavaLauncher; import org.gradle.jvm.toolchain.JavaToolchainService; import org.gradle.process.ExecOperations; @@ -21,6 +23,12 @@ abstract public class NeoFormRuntimeTask extends DefaultTask { @InputFiles abstract ConfigurableFileCollection getNeoFormRuntime(); + /** + * Launcher for the java version used by NFRT itself. + */ + @Internal + abstract Property getNeoFormRuntimeLauncher(); + @Inject protected abstract JavaToolchainService getJavaToolchainService(); @@ -50,11 +58,12 @@ public NeoFormRuntimeTask() { // Store temporary working directories in this projects build directory such that gradle clean removes them getWorkDirectory().set(project.getLayout().getBuildDirectory().dir("tmp/neoformruntime")); + + // Default to J21 for NFRT + getNeoFormRuntimeLauncher().convention(getJavaToolchainService().launcherFor(spec -> spec.getLanguageVersion().set(JavaLanguageVersion.of(21)))); } protected final void run(List args) { - var launcher = getJavaToolchainService().launcherFor(spec -> spec.getLanguageVersion().set(JavaLanguageVersion.of(21))); - // Use Gradle-specific directories when running NFRT var realArgs = new ArrayList<>(args); realArgs.add(0, "--home-dir"); @@ -65,7 +74,7 @@ protected final void run(List args) { getExecOperations().javaexec(execSpec -> { // See https://github.com/gradle/gradle/issues/28959 execSpec.jvmArgs("-Dstdout.encoding=UTF-8", "-Dstderr.encoding=UTF-8"); - execSpec.executable(launcher.get().getExecutablePath().getAsFile()); + execSpec.executable(getNeoFormRuntimeLauncher().get().getExecutablePath().getAsFile()); execSpec.classpath(getNeoFormRuntime()); execSpec.args(realArgs); }); diff --git a/testproject/build.gradle b/testproject/build.gradle index e0b364cc..7dba33ed 100644 --- a/testproject/build.gradle +++ b/testproject/build.gradle @@ -8,7 +8,7 @@ repositories { } dependencies { - testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1' + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testImplementation "net.neoforged:testframework:${project.neoforge_version}" } @@ -43,6 +43,7 @@ neoForge { neoFormRuntime { useEclipseCompiler = true + // enableCache = false } parchment { diff --git a/testproject/gradle.properties b/testproject/gradle.properties index 4b68e471..b515153a 100644 --- a/testproject/gradle.properties +++ b/testproject/gradle.properties @@ -2,5 +2,5 @@ org.gradle.configuration-cache=true org.gradle.warning.mode=fail # Dependency versions -neoforge_version=20.6.91-beta-pr-959-features-gradle-metadata +neoforge_version=20.6.104-beta-pr-959-features-gradle-metadata diff --git a/testproject/gradle/wrapper/gradle-wrapper.properties b/testproject/gradle/wrapper/gradle-wrapper.properties index b82aa23a..6f7a6eb3 100644 --- a/testproject/gradle/wrapper/gradle-wrapper.properties +++ b/testproject/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME