From 2a8a8339b52814fdb049c6a88c0c9123a54a384a Mon Sep 17 00:00:00 2001 From: Marc Hermans Date: Wed, 26 Jun 2024 11:39:04 +0200 Subject: [PATCH] [Fix]: Update DiffPatch and introduce testing (#218) --- build.gradle | 4 + .../extensions/IdeManagementExtension.java | 2 +- .../subsystems/SubsystemsExtension.java | 4 +- .../extensions/subsystems/Parchment.groovy | 7 + .../gradle/dsl/common/util/Constants.groovy | 2 +- gradle.properties | 2 +- .../extensions/NeoFormRuntimeExtension.java | 21 +- platform/build.gradle | 1 + .../neoforged/gradle/platform/E2ETests.groovy | 567 ++++++++++++++++++ .../src/functionalTest/resources/icon.ico | Bin 0 -> 4030 bytes .../src/functionalTest/resources/icon.png | Bin 0 -> 779 bytes .../extensions/DynamicProjectExtension.java | 9 +- .../RuntimeDevRuntimeSpecification.java | 9 + .../runtime/tasks/GenerateSourcePatches.java | 4 +- .../platform/tasks/CreateClasspathFiles.java | 6 +- .../platform/tasks/CreateLegacyInstaller.java | 3 +- .../test/extensions/FileExtensions.groovy | 23 + .../vanilla/runtime/steps/ParchmentStep.java | 24 +- 18 files changed, 659 insertions(+), 29 deletions(-) create mode 100644 platform/src/functionalTest/groovy/net/neoforged/gradle/platform/E2ETests.groovy create mode 100644 platform/src/functionalTest/resources/icon.ico create mode 100644 platform/src/functionalTest/resources/icon.png diff --git a/build.gradle b/build.gradle index e3555bc2b..e2a591f88 100644 --- a/build.gradle +++ b/build.gradle @@ -78,6 +78,10 @@ subprojects.forEach { Project subProject -> } } + subProject.tasks.withType(ProcessResources).configureEach { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + //Wire up our custom repositories. subProject.repositories.mavenLocal() subProject.repositories.maven { MavenArtifactRepository repository -> diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/IdeManagementExtension.java b/common/src/main/java/net/neoforged/gradle/common/extensions/IdeManagementExtension.java index 5a059be83..48de350fe 100644 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/IdeManagementExtension.java +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/IdeManagementExtension.java @@ -63,7 +63,7 @@ public IdeManagementExtension(Project project) { * @return whether this is an IntelliJ-based invocation */ public boolean isIdeaImport() { - return Boolean.getBoolean("idea.active"); + return Boolean.getBoolean("idea.sync.active"); } /** 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 c67e0babe..28f8745dd 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 @@ -89,13 +89,11 @@ private void configureParchmentDefaults() { return DEFAULT_PARCHMENT_GROUP + ":" + DEFAULT_PARCHMENT_ARTIFACT_PREFIX + minecraftVersion + ":" + mappingVersion - // We need the checked variant for now since it resolves - // parameters conflicting with local variables by prefixing everything with "p" - + ":checked" + "@zip"; }) ) ); + parchment.getConflictPrefix().convention("p_"); parchment.getMinecraftVersion().convention( getStringProperty("parchment.minecraftVersion") ); diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Parchment.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Parchment.groovy index f8ff8a227..b09325a25 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Parchment.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Parchment.groovy @@ -22,6 +22,13 @@ interface Parchment extends ConfigurableDSLElement { @DSLProperty Property getParchmentArtifact(); + /** + * The prefix added to parameters in parchment when a conflict arises. + */ + @Input + @DSLProperty + Property getConflictPrefix(); + /** * Minecraft version of parchment to use. This property is * ignored if {@link #getParchmentArtifact()} is set explicitly. 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 3ae94de18..f4eb9322d 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 @@ -27,7 +27,7 @@ class Constants { 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/" - public static final String JST_TOOL_ARTIFACT = "net.neoforged.jst:jst-cli-bundle:1.0.39" + public static final String JST_TOOL_ARTIFACT = "net.neoforged.jst:jst-cli-bundle:1.0.43" public static final String DEVLOGIN_TOOL_ARTIFACT = "net.covers1624:DevLogin:0.1.0.4" public static final String DEVLOGIN_MAIN_CLASS = "net.covers1624.devlogin.DevLogin" diff --git a/gradle.properties b/gradle.properties index 4c8860a51..f57a53cb0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -37,4 +37,4 @@ spock_version=2.1 spock_groovy_version=3.0 mockito_version=4.11.0 jimfs_version=1.2 -trainingwheels_version=1.0.48 \ No newline at end of file +trainingwheels_version=1.0.49 \ No newline at end of file diff --git a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/extensions/NeoFormRuntimeExtension.java b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/extensions/NeoFormRuntimeExtension.java index af8355511..ff66f36a3 100644 --- a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/extensions/NeoFormRuntimeExtension.java +++ b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/extensions/NeoFormRuntimeExtension.java @@ -436,7 +436,7 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) { remapTask.configure(task -> configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, task)); TaskProvider recompileInput = maybeApplyParchment( - spec, + definition, remapTask, symbolicDataSources, neoFormDirectory, @@ -492,21 +492,22 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) { }); } - private static TaskProvider maybeApplyParchment(NeoFormRuntimeSpecification spec, + private static TaskProvider maybeApplyParchment(NeoFormRuntimeDefinition runtimeDefinition, TaskProvider recompileInput, Map symbolicDataSources, File neoFormDirectory, Provider listLibrariesOutput) { - Project project = spec.getProject(); + Project project = runtimeDefinition.getSpecification().getProject(); Parchment parchment = project.getExtensions().getByType(Subsystems.class).getParchment(); Tools tools = project.getExtensions().getByType(Subsystems.class).getTools(); if (!parchment.getEnabled().get()) { return recompileInput; } - TaskProvider applyParchmentTask = project.getTasks().register(CommonRuntimeUtils.buildTaskName(spec, "applyParchment"), DefaultExecute.class, task -> { + TaskProvider applyParchmentTask = project.getTasks().register(CommonRuntimeUtils.buildTaskName(runtimeDefinition, "applyParchment"), DefaultExecute.class, task -> { // Provide the mappings via artifact File mappingFile = ToolUtilities.resolveTool(project, parchment.getParchmentArtifact().get()); + String conflictPrefix = parchment.getConflictPrefix().get(); File toolExecutable = ToolUtilities.resolveTool(project, tools.getJST().get()); task.getArguments().putFile("mappings", project.provider(() -> mappingFile)); @@ -521,13 +522,23 @@ private static TaskProvider maybeApplyParchment(NeoFormRun task.getProgramArguments().add("{mappings}"); task.getProgramArguments().add("--in-format=archive"); task.getProgramArguments().add("--out-format=archive"); + task.getProgramArguments().add("--parchment-conflict-prefix=%s".formatted(conflictPrefix)); task.getProgramArguments().add("{input}"); task.getProgramArguments().add("{output}"); + final StringBuilder builder = new StringBuilder(); + runtimeDefinition.getAllDependencies().forEach(f -> { + if (!builder.isEmpty()) { + builder.append(File.pathSeparator); + } + builder.append(f.getAbsolutePath()); + }); + task.getProgramArguments().add("--classpath=" + builder); + task.dependsOn(listLibrariesOutput); task.dependsOn(recompileInput); - configureCommonRuntimeTaskParameters(task, symbolicDataSources, "applyParchment", spec, neoFormDirectory); + configureCommonRuntimeTaskParameters(task, symbolicDataSources, "applyParchment", runtimeDefinition.getSpecification(), neoFormDirectory); }); return applyParchmentTask; diff --git a/platform/build.gradle b/platform/build.gradle index bd40b7ded..8ebc89360 100644 --- a/platform/build.gradle +++ b/platform/build.gradle @@ -1,4 +1,5 @@ plugins { + id 'groovy' id 'java-gradle-plugin' } diff --git a/platform/src/functionalTest/groovy/net/neoforged/gradle/platform/E2ETests.groovy b/platform/src/functionalTest/groovy/net/neoforged/gradle/platform/E2ETests.groovy new file mode 100644 index 000000000..fb7d651ff --- /dev/null +++ b/platform/src/functionalTest/groovy/net/neoforged/gradle/platform/E2ETests.groovy @@ -0,0 +1,567 @@ +package net.neoforged.gradle.platform + +import net.neoforged.gradle.common.caching.CentralCacheService +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 E2ETests 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) { + def rootProject = create(projectId, { + 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' + } + + 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 "default setup initializes"() { + given: + def project = createPublishingProject("default-setup") + + when: + def rootRun = project.rootProject.run { + it.tasks ':tasks' + it.stacktrace() + } + + then: + rootRun.task(":tasks").outcome == TaskOutcome.SUCCESS + + when: + def baseRun = project.rootProject.run { + it.tasks ':base:tasks' + it.stacktrace() + } + + then: + baseRun.task(":base:tasks").outcome == TaskOutcome.SUCCESS + + when: + def patchedRun = project.rootProject.run { + it.tasks ':neoforge:tasks' + it.stacktrace() + } + + then: + patchedRun.task(":neoforge:tasks").outcome == TaskOutcome.SUCCESS + } + + def "default setup can run setup on base project"() { + given: + def project = createPublishingProject("setup-base") + + when: + def rootRun = project.rootProject.run { + it.tasks ':base:setup' + it.stacktrace() + } + + then: + rootRun.task(":base:setup").outcome == TaskOutcome.SUCCESS + } + + def "default setup can run setup on patched project"() { + given: + def project = createPublishingProject("setup-patched") + + when: + def rootRun = project.rootProject.run { + it.tasks ':neoforge:setup' + it.stacktrace() + } + + then: + rootRun.task(":neoforge:setup").outcome == TaskOutcome.SUCCESS + } + + def "default setup can run setup globally"() { + given: + def project = createPublishingProject("setup-globally") + + when: + def rootRun = project.rootProject.run { + it.tasks 'setup' + it.stacktrace() + } + + then: + rootRun.task(":base:setup").outcome == TaskOutcome.SUCCESS + rootRun.task(":neoforge:setup").outcome == TaskOutcome.SUCCESS + } + + def "default setup can run setup globally, but does not requires double decompile"() { + given: + def project = createPublishingProject("setup-caching") + + when: + def rootRun = project.rootProject.run { + it.tasks 'setup' + it.stacktrace() + } + + then: + rootRun.task(":base:neoFormDecompile").outcome == TaskOutcome.SUCCESS || rootRun.task(":base:neoFormDecompile").outcome == TaskOutcome.UP_TO_DATE + rootRun.task(":neoforge:neoFormDecompile").outcome == TaskOutcome.SUCCESS || rootRun.task(":neoforge:neoFormDecompile").outcome == TaskOutcome.UP_TO_DATE + + (rootRun.task(":base:neoFormDecompile").outcome == TaskOutcome.SUCCESS && rootRun.task(":neoforge:neoFormDecompile").outcome == TaskOutcome.UP_TO_DATE) || + (rootRun.task(":base:neoFormDecompile").outcome == TaskOutcome.UP_TO_DATE && rootRun.task(":neoforge:neoFormDecompile").outcome == TaskOutcome.SUCCESS) + } + + def "patching and building is possible"() { + given: + def project = createPublishingProject("patching-assemble") + + when: + def rootRun = project.rootProject.run { + it.tasks ':neoforge:setup' + it.stacktrace() + } + + then: + rootRun.task(":neoforge:setup").outcome == TaskOutcome.SUCCESS + + when: + patch(project) + + then: + project.patchedProject.file(PATCH_TARGET_PATH).text.contains("This is a comment inserted by the test") + + when: + def createdPatchesRun = project.rootProject.run { + it.tasks ':neoforge:unpackSourcePatches' + it.stacktrace() + } + + then: + createdPatchesRun.task(":neoforge:unpackSourcePatches").outcome == TaskOutcome.SUCCESS + + when: + def patchedRun = project.rootProject.run { + it.tasks ':neoforge:assemble' + it.stacktrace() + } + + then: + patchedRun.task(":neoforge:assemble").outcome == TaskOutcome.SUCCESS + } + + def "userdev contains patch"() { + given: + def project = createPublishingProject("userdev-with-patch") + + when: + def rootRun = project.rootProject.run { + it.tasks ':neoforge:setup' + it.stacktrace() + } + + then: + rootRun.task(":neoforge:setup").outcome == TaskOutcome.SUCCESS + + when: + patch(project) + + then: + project.patchedProject.file(PATCH_TARGET_PATH).text.contains("This is a comment inserted by the test") + + when: + def createdPatchesRun = project.rootProject.run { + it.tasks ':neoforge:unpackSourcePatches' + it.stacktrace() + } + + then: + createdPatchesRun.task(":neoforge:unpackSourcePatches").outcome == TaskOutcome.SUCCESS + + when: + def patchedRun = project.rootProject.run { + it.tasks ':neoforge:assemble' + it.stacktrace() + } + + then: + patchedRun.task(":neoforge:assemble").outcome == TaskOutcome.SUCCESS + patchedRun.task(":neoforge:userdevJar").outcome == TaskOutcome.SUCCESS + + def userdevJar = project.patchedProject.file("build/libs/neoforge-1.0.0-userdev-with-patch-userdev.jar") + userdevJar.exists() + + def patch = userdevJar.getZipEntry(PATCH_RESULT_PATH) + patch.exists() + + def patchContent = patch.text + patchContent.contains("This is a comment inserted by the test") + } + + def "a published userdev artifact can be loaded into userdev"() { + given: + def project = createPublishingProject("published-userdev") + 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 userdevProject = create("published-userdev-cons", { + it.plugin("net.neoforged.gradle.userdev") + it.build(""" + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + repositories { + maven { + name = 'test' + url = "file://${getTestTempDirectory().absolutePath.replace(File.separator, "/")}/maven" + } + } + + dependencies { + implementation 'net.neoforged:neoforge:1.0.0-published-userdev' + } + """) + it.withToolchains() + it.withGlobalCacheDirectory(tempDir) + }) + + when: + def run = userdevProject.run { + it.tasks(':neoFormRecompile') + } + + then: + run.task(':neoFormRecompile').outcome == TaskOutcome.SUCCESS + } +} diff --git a/platform/src/functionalTest/resources/icon.ico b/platform/src/functionalTest/resources/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..54a5670613377d393e82c048e824ea87fd4d6c89 GIT binary patch literal 4030 zcmc&%`CF4$7H0l{dD_|9I%Bajt#+PPAd-Y6FgQA+sEAcr6$E4pC<1|?QWpdTko!w(GT_Xh@N-ZC(F$H2hguLMKF0s{A*J!JdPSPSh70|UIl^Pe9^;UO=Cz$O!- z&$3<#A=w-^D)8v>lQ&@e=_${j13G+9x}Kqh5U$9Ac;%~MMm8sZt**|G8{hPK_Ut*b zzU)AFq7o7Rs>H7Sm54f6jl7F$Tr9bTyh|#i%?spVqzGH;k(Kd#I@EgU8mZFtf{t*dZSx`+O{RSD?9l08gKcp{1=8 zCo|3x^0i1mi@N$YJRzI<)+1PZUt&I-FUPS)R)n(ZUdDav?&B$OA3oIx3FXexE(a2~ zD-e2IV-T#rhJW~1ph-PQ@jgfD(Nq}Dco(z&_#VT|cmII+*m#O}9O~{NEDfpz=cDr% zyBEXK{tT;&y~#CGbPuLAuyqx$%!bJI3IwFfflnL@Nw=kI5aVM&YRVD3_uIEH<9BZ} zyz}<25f>AKXJgNyxjTdyjiO16FZeV8wAWImiP$6+R|m{#}DNiN=WwjQP% zYGD+rhB3ipb0>q5Tm_>LmG<41E||OoZXRdj=IG|LcXBmMNn5lkhvH2~)JyT+=^X-x zJ%=ea+p`ckoWtU+4Y1y;!Gdj#nCo)|bEQQv32uf-s0uuJ2YA74lObxU zS#vP!BV!nQD=3yO2!om+@S;4tjKBu%n)rT=S+fQB)6Bmha^Dfu5=Zvc9*8&JgfVej z_$&2#T%X|}^e&_CtcGB#8abuC=(;n2BS+K0)$98+4Pos0CDSzvYJ}NDt?O!57*Ma( zrFD7Va5iTA{x^t=J4kzc00otO5Pz=CnZQ@cv^c%XrQHm&q*j>v)<6<)1BKNOFgWxC zdAa9c{PBk{{$wspr3H-JRDKiuz@|ws4QXRGo?i)!jZE?BN3(Du^&|$VSC=b6#WCkXzA!dZA%aAFD&_g}72lv0xa@2Kx2lMT;7ddXK4V|8)UpY94Weu1Y+5xkzDik$7 z#_;e6dcXM&jj9KzZoN;s8ojQVbS0!K+@WDH^ET7|Wx8D`YrK!fjt97VZx}L!8c+ zVBuB>iGMZaoA&7hM6xEBhH87#bh<20G*{=xjgn2Zuy8Mg8F5Faw(8{cBX=GN>CBMm z&!aN!dBpVyt~YUe^fmF$3%Lmaohf|s(VjJ`B)Fc0s9d0Y?W^##m#bupWW>c%Rdoca9Srahqc{i(oHAbtXJx$rk7yR>If`#*bNu2NVrLNA}S^g zHRYYCDD8kWFa=h&5wLPjfw{8+lC=dBP+*aBDy-}x;3_?U(&AR!P-+mDkjXgKc}LRv z?PlDGhZXvI(tSvy`bv{7j%^n1V?7@oFo>=A(2{Ctf zr}(tUf|Y$VY;3*YyEO$_lMbKM$?3<8>j(d}Z;7JD7`xHtLhazkqJLe$y21X$=HUkm+PQk@v zGks?-M8~J0MfH&S_5|qj{^T5Hb99vOy?8n@I>qP5 zFF4)HSIUrbEE_Qisc`q*#p=V3IJtOJQm5)>hU^6k~Lh zF2FzIsjP0Gn()I4J1N6-y31F4>*v|n`k<(!;%6vLo~o>>|HZmjipwXH{pc^<|H1#S F@qfh3wW|OC literal 0 HcmV?d00001 diff --git a/platform/src/functionalTest/resources/icon.png b/platform/src/functionalTest/resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6a9c5b2b1b43d34d100b6c87e080fb308f4fce30 GIT binary patch literal 779 zcmeAS@N?(olHy`uVBq!ia0vp^VL%+q!3HGlCr&g0Qk(@Ik;M!Qe1}1p@p%4<6riAF ziEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$QVa}CGdx`!Ln`LHonx3C;wa)) zZpvtVbODQaOT?O~p&>4g!O=60dF6_@IR0VI?D75Az|obkG|X(Hz?vRMSEaC9GY)wq zBnO0T5ni-r|ATu6D=j-4i*NOp-`{gsWV-d?Y<{+8v4c}Lr?29=U;AgtUeSBuNz#EK zBHarkIF34Lh&gQ(aQ2lu7d5B(xAntf`RUcZalg-}WS`u(&{O+aUu%F)OOm1pH(2qO z%`d|;lcsDne7-J@d$V+KaAH}}+w5aWD z?VZxS@IccLOVZdq-}K(u<@7_W9a|@ literal 0 HcmV?d00001 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 778e2e059..5ca329d83 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 @@ -142,7 +142,7 @@ public void neoform(final String neoFormVersion) { final NeoFormRuntimeExtension neoFormRuntimeExtension = project.getExtensions().getByType(NeoFormRuntimeExtension.class); final NeoFormRuntimeDefinition runtimeDefinition = neoFormRuntimeExtension.create(builder -> { - builder.withNeoFormVersion(neoFormVersion).withDistributionType(DistributionType.CLIENT); + builder.withNeoFormVersion(neoFormVersion).withDistributionType(DistributionType.JOINED); NeoFormRuntimeUtils.configureDefaultRuntimeSpecBuilder(project, builder); }); @@ -439,7 +439,8 @@ public void runtime(final String neoFormVersion, Directory patches, Directory re }); profile.getLibraries().add(Library.fromOutput(signUniversalJar, project, "net.neoforged", "neoforge", project.getVersion().toString(), "universal")); - + + //TODO: Abstract this away to some kind of DSL property profile.getIcon().set(project.provider(() -> { final File icon = new File(project.getRootProject().getProjectDir(), "docs/assets/neoforged.ico"); if (!icon.exists()) { @@ -818,6 +819,8 @@ private void configureRunType(final Project project, final RunType runType, fina } private static void configureInstallerTokens(final TokenizedTask tokenizedTask, final RuntimeDevRuntimeDefinition runtimeDefinition, final Collection ignoreConfigurations, final Configuration pluginLayerLibraries, final Configuration gameLayerLibraries) { + //TODO: This should be moved to a DSL configurable model. + tokenizedTask.token("TASK", "forgeserver"); tokenizedTask.token("MAVEN_PATH", String.format("%s/%s/%s", tokenizedTask.getProject().getGroup().toString().replace('.', '/'), tokenizedTask.getProject().getName(), tokenizedTask.getProject().getVersion())); tokenizedTask.token("FORGE_VERSION", tokenizedTask.getProject().getVersion()); @@ -928,7 +931,7 @@ private void configureUserdevRunType(final RunType runType, Configuration module } private static String createCoordinate(final Project project, final String classifier) { - return Artifact.from(project, classifier, "jar").toString(); + return Objects.toString(Artifact.from(project, classifier, "jar")); } @NotNull diff --git a/platform/src/main/java/net/neoforged/gradle/platform/runtime/runtime/specification/RuntimeDevRuntimeSpecification.java b/platform/src/main/java/net/neoforged/gradle/platform/runtime/runtime/specification/RuntimeDevRuntimeSpecification.java index 6b42125a1..9ef09b03a 100644 --- a/platform/src/main/java/net/neoforged/gradle/platform/runtime/runtime/specification/RuntimeDevRuntimeSpecification.java +++ b/platform/src/main/java/net/neoforged/gradle/platform/runtime/runtime/specification/RuntimeDevRuntimeSpecification.java @@ -57,10 +57,19 @@ public String getMinecraftVersion() { } public Directory getPatchesDirectory() { + if (!patchesDirectory.getAsFile().exists()) { + //Ensure the directory exists + patchesDirectory.getAsFile().mkdirs(); + } + return patchesDirectory; } public Directory getRejectsDirectory() { + if (!rejectsDirectory.getAsFile().exists()) { + //Ensure the directory exists + rejectsDirectory.getAsFile().mkdirs(); + } return rejectsDirectory; } diff --git a/platform/src/main/java/net/neoforged/gradle/platform/runtime/runtime/tasks/GenerateSourcePatches.java b/platform/src/main/java/net/neoforged/gradle/platform/runtime/runtime/tasks/GenerateSourcePatches.java index 419f3134d..bf0750d50 100644 --- a/platform/src/main/java/net/neoforged/gradle/platform/runtime/runtime/tasks/GenerateSourcePatches.java +++ b/platform/src/main/java/net/neoforged/gradle/platform/runtime/runtime/tasks/GenerateSourcePatches.java @@ -36,8 +36,8 @@ public void doTask() throws Exception { Path base = getBase().get().getAsFile().toPath(); Path modified = getModified().get().getAsFile().toPath(); Path output = getOutput().get().getAsFile().toPath(); - getProject().getLogger().info("Base: {}", base); - getProject().getLogger().info("Modified: {}", modified); + getLogger().info("Base: {}", base); + getLogger().info("Modified: {}", modified); DiffOperation.Builder builder = DiffOperation.builder() .logTo(getLogger()::lifecycle) diff --git a/platform/src/main/java/net/neoforged/gradle/platform/tasks/CreateClasspathFiles.java b/platform/src/main/java/net/neoforged/gradle/platform/tasks/CreateClasspathFiles.java index 915f2db35..a280ff424 100644 --- a/platform/src/main/java/net/neoforged/gradle/platform/tasks/CreateClasspathFiles.java +++ b/platform/src/main/java/net/neoforged/gradle/platform/tasks/CreateClasspathFiles.java @@ -22,10 +22,10 @@ import java.util.Set; import java.util.stream.Collectors; +//TODO: This task crashes a run when the input template file is not present. @CacheableTask public abstract class CreateClasspathFiles extends DefaultRuntime implements TokenizedTask { - - + public CreateClasspathFiles() { getTemplate().set(getProject().getRootProject().file("server_files/args.txt")); getOutputFileName().convention("args.txt"); @@ -71,7 +71,7 @@ public void doTask() throws Exception { copySpec.into(getOutputDirectory().get().getAsFile()); }); } - + @InputFile @PathSensitive(PathSensitivity.NONE) public abstract RegularFileProperty getTemplate(); diff --git a/platform/src/main/java/net/neoforged/gradle/platform/tasks/CreateLegacyInstaller.java b/platform/src/main/java/net/neoforged/gradle/platform/tasks/CreateLegacyInstaller.java index 33ed239c7..a2f9e2a4e 100644 --- a/platform/src/main/java/net/neoforged/gradle/platform/tasks/CreateLegacyInstaller.java +++ b/platform/src/main/java/net/neoforged/gradle/platform/tasks/CreateLegacyInstaller.java @@ -57,7 +57,8 @@ public CreateLegacyInstaller() { spec.into("data"); spec.rename(name -> "server.lzma"); }); - + + //TODO: Handle this better. They are hardcoded and if they don't exists it will fail. getUrlIcon().fileValue(getProject().getRootProject().file("src/main/resources/url.png")); getInstallerLogo().fileValue(getProject().getRootProject().file("src/main/resources/neoforged_logo.png")); from(getData(), spec -> { diff --git a/test-utils/src/main/groovy/net/neoforged/gradle/utils/test/extensions/FileExtensions.groovy b/test-utils/src/main/groovy/net/neoforged/gradle/utils/test/extensions/FileExtensions.groovy index 07f9d0038..c3c304819 100644 --- a/test-utils/src/main/groovy/net/neoforged/gradle/utils/test/extensions/FileExtensions.groovy +++ b/test-utils/src/main/groovy/net/neoforged/gradle/utils/test/extensions/FileExtensions.groovy @@ -2,6 +2,11 @@ package net.neoforged.gradle.utils.test.extensions import groovy.transform.CompileStatic +import java.util.zip.ZipEntry +import java.util.zip.ZipFile +import java.util.zip.ZipInputStream +import java.util.zip.ZipOutputStream + /** * Extensions for the [File] class. */ @@ -13,4 +18,22 @@ class FileExtensions { static String getPropertiesPath(final File self) { return self.absolutePath.replace("\\", "\\\\") } + + static File getZipEntry(final File self, final String path) { + final ZipFile zipFile = new ZipFile(self) + final ZipEntry entry = zipFile.getEntry(path) + if (entry == null) { + throw new IllegalArgumentException("Entry not found: $path") + } + + final File tempFile = File.createTempFile("zipEntry", ".tmp") + final InputStream zipInputStream = zipFile.getInputStream(entry) + final OutputStream tempOutputStream = new FileOutputStream(tempFile) + tempOutputStream << zipInputStream + tempOutputStream.close() + zipInputStream.close() + zipFile.close() + + return tempFile; + } } diff --git a/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/steps/ParchmentStep.java b/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/steps/ParchmentStep.java index 2fff73a37..041f49b74 100644 --- a/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/steps/ParchmentStep.java +++ b/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/steps/ParchmentStep.java @@ -12,10 +12,7 @@ import net.neoforged.gradle.dsl.common.util.CommonRuntimeUtils; import net.neoforged.gradle.dsl.common.util.GameArtifact; import net.neoforged.gradle.vanilla.runtime.VanillaRuntimeDefinition; -import net.neoforged.gradle.vanilla.runtime.spec.VanillaRuntimeSpecification; import org.gradle.api.Project; -import org.gradle.api.Transformer; -import org.gradle.api.artifacts.ResolvedArtifact; import org.gradle.api.file.RegularFile; import org.gradle.api.provider.Provider; import org.gradle.api.tasks.TaskProvider; @@ -34,28 +31,28 @@ public TaskProvider buildTask(VanillaRuntimeDefinition defini final TaskProvider collectLibraryInformationTask = pipelineTasks.get(CommonRuntimeUtils.buildTaskName(definition, "libraries")); return maybeApplyParchment( - definition.getSpecification(), + definition, inputProvidingTask, workingDirectory, collectLibraryInformationTask.flatMap(WithOutput::getOutput) ); } - private static TaskProvider maybeApplyParchment(VanillaRuntimeSpecification spec, + private static TaskProvider maybeApplyParchment(VanillaRuntimeDefinition definition, TaskProvider inputProvidingTask, File vanillaDirectory, Provider listLibrariesOutput) { - Project project = spec.getProject(); + Project project = definition.getSpecification().getProject(); Parchment parchment = project.getExtensions().getByType(Subsystems.class).getParchment(); Tools tools = project.getExtensions().getByType(Subsystems.class).getTools(); if (!parchment.getEnabled().get()) { - return project.getTasks().register(CommonRuntimeUtils.buildTaskName(spec, "applyParchmentNoop"), NoopRuntime.class, task -> { + return project.getTasks().register(CommonRuntimeUtils.buildTaskName(definition, "applyParchmentNoop"), NoopRuntime.class, task -> { task.getInput().set(inputProvidingTask.flatMap(WithOutput::getOutput)); }); } - return project.getTasks().register(CommonRuntimeUtils.buildTaskName(spec, "applyParchment"), DefaultExecute.class, task -> { + return project.getTasks().register(CommonRuntimeUtils.buildTaskName(definition, "applyParchment"), DefaultExecute.class, task -> { // Provide the mappings via artifact File mappingFile = ToolUtilities.resolveTool(project, parchment.getParchmentArtifact().get()); File toolExecutable = ToolUtilities.resolveTool(project, tools.getJST().get()); @@ -75,10 +72,19 @@ private static TaskProvider maybeApplyParchment(VanillaRuntim task.getProgramArguments().add("{input}"); task.getProgramArguments().add("{output}"); + final StringBuilder builder = new StringBuilder(); + definition.getAllDependencies().forEach(f -> { + if (!builder.isEmpty()) { + builder.append(File.pathSeparator); + } + builder.append(f.getAbsolutePath()); + }); + task.getProgramArguments().add("--classpath=" + builder); + task.dependsOn(inputProvidingTask); task.dependsOn(listLibrariesOutput); - configureCommonRuntimeTaskParameters(task, Maps.newHashMap(), "applyParchment", spec, vanillaDirectory); + configureCommonRuntimeTaskParameters(task, Maps.newHashMap(), "applyParchment", definition.getSpecification(), vanillaDirectory); }); }