From 37aea2c413598bb5e74264ff4835270bd72db14f Mon Sep 17 00:00:00 2001 From: MiniDigger | Martin Date: Sat, 23 Sep 2023 11:08:01 +0200 Subject: [PATCH] start to fiddle with mache --- .editorconfig | 3 + .github/workflows/deploy-snapshot.yml | 4 +- .gitignore | 5 + .../src/main/kotlin/config-kotlin.gradle.kts | 30 +- gradle.properties | 2 +- gradle/libs.versions.toml | 22 +- paperweight-core/build.gradle.kts | 1 + .../paperweight/core/PaperweightCore.kt | 46 ++- .../core/extension/PaperExtension.kt | 7 +- .../extension/PaperweightCoreExtension.kt | 4 + .../core/taskcontainers/AllTasks.kt | 21 +- .../core/taskcontainers/InitialTasks.kt | 7 +- .../core/taskcontainers/SoftSpoonTasks.kt | 344 ++++++++++++++++++ .../core/taskcontainers/SpigotTasks.kt | 4 +- .../core/taskcontainers/VanillaTasks.kt | 3 +- .../io/papermc/paperweight/FunctionalTest.kt | 153 ++++++++ .../kotlin/io/papermc/paperweight/util.kt | 49 +++ .../src/test/resources/fake_mache/mache.json | 96 +++++ .../fake_mache/patches/Test.java.patch | 9 + .../bundle/META-INF/libraries.list | 1 + .../fake_mojang/bundle/META-INF/versions.list | 1 + .../resources/fake_mojang/bundle/version.json | 3 + .../test/resources/fake_mojang/mappings.txt | 19 + .../resources/fake_mojang/server/Test.java | 17 + .../resources/fake_mojang/server/test.json | 3 + .../test/resources/fake_mojang/version.json | 12 + .../fake_mojang/version_manifest.json | 14 + .../test/resources/functional_test/.gitignore | 2 + .../functional_test/build-data/expected.at | 3 + .../functional_test/build-data/fake.at | 2 + .../functional_test/build-data/paper.at | 1 + .../resources/functional_test/build.gradle | 46 +++ .../fake-patches/expected/Test.java.patch | 15 + .../fake-patches/feature/.gitkeep | 0 .../fake-patches/resources/test.json.patch | 7 + .../fake-patches/sources/Test.java.patch | 11 + .../patches/feature/0001-something.patch | 1 + .../patches/resources/version.json.patch | 11 + .../net/minecraft/CrashReport.java.patch | 15 + .../resources/functional_test/settings.gradle | 18 + .../functional_test/test-api/build.gradle | 3 + .../functional_test/test-server/build.gradle | 8 + paperweight-lib/build.gradle.kts | 10 + .../io/papermc/paperweight/DownloadService.kt | 26 +- .../paperweight/tasks/ApplyGitPatches.kt | 14 +- .../paperweight/tasks/DownloadServerJar.kt | 3 +- .../paperweight/tasks/ExtractFromBundler.kt | 13 +- .../io/papermc/paperweight/tasks/MacheTask.kt | 83 +++++ .../tasks/MergeAccessTransforms.kt | 3 +- .../paperweight/tasks/RemapAccessTransform.kt | 3 +- .../papermc/paperweight/tasks/RemapSources.kt | 3 +- .../paperweight/tasks/RemapSpigotAt.kt | 3 +- .../paperweight/tasks/download-task.kt | 53 ++- .../paperweight/tasks/mache/DecompileJar.kt | 112 ++++++ .../paperweight/tasks/mache/RemapJar.kt | 106 ++++++ .../paperweight/tasks/mache/SetupVanilla.kt | 220 +++++++++++ .../tasks/softspoon/ApplyFeaturePatches.kt | 77 ++++ .../tasks/softspoon/ApplyFilePatches.kt | 128 +++++++ .../tasks/softspoon/ApplyFilePatchesFuzzy.kt | 50 +++ .../tasks/softspoon/ApplySourceAT.kt | 135 +++++++ .../tasks/softspoon/RebuildFilePatches.kt | 204 +++++++++++ .../io/papermc/paperweight/util/McDev.kt | 23 +- .../kotlin/io/papermc/paperweight/util/at.kt | 79 ++++ .../paperweight/util/constants/constants.kt | 18 + .../data/mache/MacheAdditionalDependencies.kt | 28 ++ .../util/data/mache/MacheDependencies.kt | 31 ++ .../paperweight/util/data/mache/MacheMeta.kt | 33 ++ .../util/data/mache/MacheRepository.kt | 29 ++ .../util/data/mache/MavenArtifact.kt | 35 ++ .../io/papermc/paperweight/util/file.kt | 34 +- .../kotlin/io/papermc/paperweight/util/git.kt | 11 + .../paperweight/util/patches/JavaPatcher.kt | 107 ++++++ .../util/patches/JavaPatcherFuzzy.kt | 38 ++ .../paperweight/util/patches/NativePatcher.kt | 89 +++++ .../util/patches/NativePatcherFuzzy.kt | 40 ++ .../paperweight/util/patches/Patcher.kt | 62 ++++ .../io/papermc/paperweight/util/utils.kt | 18 +- .../src/main/resources/post-rewrite.sh | 14 + .../tasks/ApplyAccessTransformTest.kt | 83 +++++ .../io/papermc/paperweight/tasks/TaskTest.kt | 183 ++++++++++ .../tasks/softspoon/ApplyFilePatchesTest.kt | 61 ++++ .../tasks/softspoon/ApplySourceATTest.kt | 61 ++++ .../tasks/softspoon/RebuildFilePatchesTest.kt | 66 ++++ .../apply_access_transform/input/Test.java | 13 + .../apply_access_transform/input/ats.at | 3 + .../output/output.javap | 21 ++ .../apply_patches/input/base/Test.java | 13 + .../input/patches/Test.java.patch | 10 + .../apply_patches/output/source/Test.java | 13 + .../resources/apply_source_at/input/Test.java | 13 + .../apply_source_at/input/Unrelated.java | 3 + .../resources/apply_source_at/input/ats.at | 3 + .../apply_source_at/output/Test.java | 13 + .../apply_source_at/output/Unrelated.java | 3 + .../resources/rebuild_patches/input/ats.at | 1 + .../rebuild_patches/input/base/Test.java | 13 + .../rebuild_patches/input/source/Test.java | 13 + .../resources/rebuild_patches/output/ats.at | 3 + .../rebuild_patches/output/base/Test.java | 13 + .../output/patches/Test.java.patch | 10 + .../rebuild_patches/output/source/Test.java | 13 + .../paperweight/userdev/PaperweightUser.kt | 4 +- .../internal/setup/v2/SetupHandlerImplV2.kt | 4 +- 103 files changed, 3520 insertions(+), 74 deletions(-) create mode 100644 paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SoftSpoonTasks.kt create mode 100644 paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt create mode 100644 paperweight-core/src/test/kotlin/io/papermc/paperweight/util.kt create mode 100644 paperweight-core/src/test/resources/fake_mache/mache.json create mode 100644 paperweight-core/src/test/resources/fake_mache/patches/Test.java.patch create mode 100644 paperweight-core/src/test/resources/fake_mojang/bundle/META-INF/libraries.list create mode 100644 paperweight-core/src/test/resources/fake_mojang/bundle/META-INF/versions.list create mode 100644 paperweight-core/src/test/resources/fake_mojang/bundle/version.json create mode 100644 paperweight-core/src/test/resources/fake_mojang/mappings.txt create mode 100644 paperweight-core/src/test/resources/fake_mojang/server/Test.java create mode 100644 paperweight-core/src/test/resources/fake_mojang/server/test.json create mode 100644 paperweight-core/src/test/resources/fake_mojang/version.json create mode 100644 paperweight-core/src/test/resources/fake_mojang/version_manifest.json create mode 100644 paperweight-core/src/test/resources/functional_test/.gitignore create mode 100644 paperweight-core/src/test/resources/functional_test/build-data/expected.at create mode 100644 paperweight-core/src/test/resources/functional_test/build-data/fake.at create mode 100644 paperweight-core/src/test/resources/functional_test/build-data/paper.at create mode 100644 paperweight-core/src/test/resources/functional_test/build.gradle create mode 100644 paperweight-core/src/test/resources/functional_test/fake-patches/expected/Test.java.patch create mode 100644 paperweight-core/src/test/resources/functional_test/fake-patches/feature/.gitkeep create mode 100644 paperweight-core/src/test/resources/functional_test/fake-patches/resources/test.json.patch create mode 100644 paperweight-core/src/test/resources/functional_test/fake-patches/sources/Test.java.patch create mode 100644 paperweight-core/src/test/resources/functional_test/patches/feature/0001-something.patch create mode 100644 paperweight-core/src/test/resources/functional_test/patches/resources/version.json.patch create mode 100644 paperweight-core/src/test/resources/functional_test/patches/sources/net/minecraft/CrashReport.java.patch create mode 100644 paperweight-core/src/test/resources/functional_test/settings.gradle create mode 100644 paperweight-core/src/test/resources/functional_test/test-api/build.gradle create mode 100644 paperweight-core/src/test/resources/functional_test/test-server/build.gradle create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/MacheTask.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/mache/DecompileJar.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/mache/RemapJar.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/mache/SetupVanilla.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/ApplyFeaturePatches.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/ApplyFilePatches.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/ApplyFilePatchesFuzzy.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/ApplySourceAT.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/RebuildFilePatches.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/at.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheAdditionalDependencies.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheDependencies.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheMeta.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheRepository.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MavenArtifact.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/JavaPatcher.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/JavaPatcherFuzzy.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/NativePatcher.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/NativePatcherFuzzy.kt create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/Patcher.kt create mode 100644 paperweight-lib/src/main/resources/post-rewrite.sh create mode 100644 paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/ApplyAccessTransformTest.kt create mode 100644 paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/TaskTest.kt create mode 100644 paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/softspoon/ApplyFilePatchesTest.kt create mode 100644 paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/softspoon/ApplySourceATTest.kt create mode 100644 paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/softspoon/RebuildFilePatchesTest.kt create mode 100644 paperweight-lib/src/test/resources/apply_access_transform/input/Test.java create mode 100644 paperweight-lib/src/test/resources/apply_access_transform/input/ats.at create mode 100644 paperweight-lib/src/test/resources/apply_access_transform/output/output.javap create mode 100644 paperweight-lib/src/test/resources/apply_patches/input/base/Test.java create mode 100644 paperweight-lib/src/test/resources/apply_patches/input/patches/Test.java.patch create mode 100644 paperweight-lib/src/test/resources/apply_patches/output/source/Test.java create mode 100644 paperweight-lib/src/test/resources/apply_source_at/input/Test.java create mode 100644 paperweight-lib/src/test/resources/apply_source_at/input/Unrelated.java create mode 100644 paperweight-lib/src/test/resources/apply_source_at/input/ats.at create mode 100644 paperweight-lib/src/test/resources/apply_source_at/output/Test.java create mode 100644 paperweight-lib/src/test/resources/apply_source_at/output/Unrelated.java create mode 100644 paperweight-lib/src/test/resources/rebuild_patches/input/ats.at create mode 100644 paperweight-lib/src/test/resources/rebuild_patches/input/base/Test.java create mode 100644 paperweight-lib/src/test/resources/rebuild_patches/input/source/Test.java create mode 100644 paperweight-lib/src/test/resources/rebuild_patches/output/ats.at create mode 100644 paperweight-lib/src/test/resources/rebuild_patches/output/base/Test.java create mode 100644 paperweight-lib/src/test/resources/rebuild_patches/output/patches/Test.java.patch create mode 100644 paperweight-lib/src/test/resources/rebuild_patches/output/source/Test.java diff --git a/.editorconfig b/.editorconfig index 340d0e17..c2aa2e6f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -83,3 +83,6 @@ ij_kotlin_field_annotation_wrap = split_into_lines ij_kotlin_finally_on_new_line = false ij_kotlin_if_rparen_on_new_line = true ij_kotlin_import_nested_classes = false + +[*.patch] +trim_trailing_whitespace=false diff --git a/.github/workflows/deploy-snapshot.yml b/.github/workflows/deploy-snapshot.yml index 2087f8d3..a40c9c72 100644 --- a/.github/workflows/deploy-snapshot.yml +++ b/.github/workflows/deploy-snapshot.yml @@ -1,7 +1,7 @@ name: Deploy Snapshot on: push: - branches: [ 'main' ] + branches: [ 'main', 'softspoon-v2' ] paths-ignore: - 'license/*' - 'readme.md' @@ -29,7 +29,7 @@ jobs: echo version=$project_version >> $GITHUB_OUTPUT - name: Deploy snapshot version if: endsWith(steps.get_version.outputs.version, '-SNAPSHOT') - run: ./gradlew -Dorg.gradle.parallel=true publish --no-daemon --stacktrace + run: ./gradlew -Dorg.gradle.parallel=true publish --no-daemon --stacktrace -Dorg.gradle.internal.http.socketTimeout=90000 -Dorg.gradle.internal.http.connectionTimeout=90000 env: ORG_GRADLE_PROJECT_paperUsername: ${{ secrets.DEPLOY_USER }} ORG_GRADLE_PROJECT_paperPassword: ${{ secrets.DEPLOY_PASS }} diff --git a/.gitignore b/.gitignore index fd472d07..9e181772 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,8 @@ ehthumbs_vista.db # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) !gradle-wrapper.ja + +/test/ +/testpaper/ +/testfork/ +/testhistory/ diff --git a/buildSrc/src/main/kotlin/config-kotlin.gradle.kts b/buildSrc/src/main/kotlin/config-kotlin.gradle.kts index 9efe9cad..9f1706f3 100644 --- a/buildSrc/src/main/kotlin/config-kotlin.gradle.kts +++ b/buildSrc/src/main/kotlin/config-kotlin.gradle.kts @@ -16,19 +16,20 @@ tasks.withType(JavaCompile::class).configureEach { kotlin { jvmToolchain { - languageVersion = JavaLanguageVersion.of(17) + languageVersion = JavaLanguageVersion.of(11) } target { compilations.configureEach { kotlinOptions { - jvmTarget = "11" - freeCompilerArgs = listOf("-Xjvm-default=all", "-Xjdk-release=11") + jvmTarget = "17" + freeCompilerArgs = listOf("-Xjvm-default=all", "-Xjdk-release=17", "-opt-in=kotlin.io.path.ExperimentalPathApi") } } } } repositories { + mavenLocal() // TODO remove again maven("https://repo.papermc.io/repository/maven-snapshots/") { mavenContent { includeModule("org.cadixdev", "mercury") @@ -38,6 +39,28 @@ repositories { mavenContent { includeGroup("codechicken") includeGroup("net.fabricmc") + includeGroupAndSubgroups("io.papermc") + } + } + maven("https://maven.parchmentmc.org") { + name = "ParchmentMC" + mavenContent { + releasesOnly() + includeGroupAndSubgroups("org.parchmentmc") + } + } + maven("https://maven.neoforged.net/releases") { + name = "NeoForged" + mavenContent { + releasesOnly() + includeGroupAndSubgroups("net.neoforged") + } + } + maven("https://maven.fabricmc.net") { + name = "FabricMC" + mavenContent { + releasesOnly() + includeGroupAndSubgroups("net.fabricmc") } } mavenCentral() @@ -55,6 +78,7 @@ testing { useKotlinTest(embeddedKotlinVersion) dependencies { implementation("org.junit.jupiter:junit-jupiter-engine:5.10.1") + implementation("org.junit.platform:junit-platform-launcher:1.10.1") } } } diff --git a/gradle.properties b/gradle.properties index eb0d4f7e..8015950f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.papermc.paperweight -version = 1.5.13-SNAPSHOT +version = 2.0.0-SNAPSHOT org.gradle.caching = true org.gradle.parallel = true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 829b158d..b28b4726 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,9 @@ [versions] asm = "9.7" lorenz = "0.5.8" -hypo = "1.2.4" +hypo = "2.3.0" +serialize = "1.5.1" +feather = "1.1.0" [libraries] asm-core = { module = "org.ow2.asm:asm", version.ref = "asm" } @@ -9,6 +11,9 @@ asm-tree = { module = "org.ow2.asm:asm-tree", version.ref = "asm" } httpclient = "org.apache.httpcomponents:httpclient:4.5.14" kotson = "com.github.salomonbrys.kotson:kotson:2.5.0" +coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.2" +jgit = "org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r" +diffutils = "io.github.java-diff-utils:java-diff-utils:4.12" gson = "com.google.code.gson:gson:2.10.1" cadix-lorenz-core = { module = "org.cadixdev:lorenz", version.ref = "lorenz" } @@ -16,7 +21,9 @@ cadix-lorenz-asm = { module = "org.cadixdev:lorenz-asm", version.ref = "lorenz" cadix-lorenz-proguard = { module = "org.cadixdev:lorenz-io-proguard", version.ref = "lorenz" } cadix-atlas = "org.cadixdev:atlas:0.2.1" cadix-at = "org.cadixdev:at:0.1.0-rc1" +#cadix-mercury = "org.cadixdev:mercury:0.1.2-paperweight-local-SNAPSHOT" # todo local mods for patch remapping cadix-mercury = "org.cadixdev:mercury:0.1.2-paperweight-SNAPSHOT" +cadix-bombe-jar = "org.cadixdev:bombe-jar:0.4.4" hypo-model = { module = "dev.denwav.hypo:hypo-model", version.ref = "hypo" } hypo-core = { module = "dev.denwav.hypo:hypo-core", version.ref = "hypo" } @@ -29,8 +36,19 @@ slf4j-jdk14 = "org.slf4j:slf4j-jdk14:1.7.32" lorenzTiny = "net.fabricmc:lorenz-tiny:3.0.0" jbsdiff = "io.sigpipe:jbsdiff:1.0" +feather-core = { module = "org.parchmentmc:feather", version.ref = "feather" } +feather-gson = { module = "org.parchmentmc.feather:io-gson", version.ref = "feather" } + diffpatch = "codechicken:DiffPatch:1.5.0.29" +serialize-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "serialize" } +serialize-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialize" } + +restamp = "io.papermc.restamp:restamp:1.1.0" + +# test +mockk = "io.mockk:mockk:1.13.8" + # Gradle gradle-licenser = "net.kyori:indra-licenser-spotless:3.1.3" gradle-spotless = "com.diffplug.spotless:spotless-plugin-gradle:6.23.1" @@ -41,6 +59,6 @@ gradle-plugin-publish = "com.gradle.publish:plugin-publish-plugin:1.2.1" [bundles] asm = ["asm-core", "asm-tree"] -cadix = ["cadix-lorenz-core", "cadix-lorenz-asm", "cadix-lorenz-proguard", "cadix-atlas", "cadix-at", "cadix-mercury"] +cadix = ["cadix-lorenz-core", "cadix-lorenz-asm", "cadix-lorenz-proguard", "cadix-atlas", "cadix-at", "cadix-mercury", "cadix-bombe-jar"] hypo = ["hypo-model", "hypo-core", "hypo-hydrate", "hypo-asm-core", "hypo-asm-hydrate", "hypo-mappings"] kotson = ["kotson", "gson"] diff --git a/paperweight-core/build.gradle.kts b/paperweight-core/build.gradle.kts index 99133c9e..5957f60d 100644 --- a/paperweight-core/build.gradle.kts +++ b/paperweight-core/build.gradle.kts @@ -7,6 +7,7 @@ dependencies { shade(projects.paperweightLib) implementation(libs.bundles.kotson) + implementation(libs.coroutines) } gradlePlugin { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index 93fa1a79..b85e7f72 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -25,6 +25,7 @@ package io.papermc.paperweight.core import io.papermc.paperweight.DownloadService import io.papermc.paperweight.core.extension.PaperweightCoreExtension import io.papermc.paperweight.core.taskcontainers.AllTasks +import io.papermc.paperweight.core.taskcontainers.SoftSpoonTasks import io.papermc.paperweight.core.tasks.PaperweightCorePrepareForDownstream import io.papermc.paperweight.taskcontainers.BundlerJarTasks import io.papermc.paperweight.taskcontainers.DevBundleTasks @@ -48,7 +49,9 @@ class PaperweightCore : Plugin { val ext = target.extensions.create(PAPERWEIGHT_EXTENSION, PaperweightCoreExtension::class, target) - target.gradle.sharedServices.registerIfAbsent(DOWNLOAD_SERVICE_NAME, DownloadService::class) {} + target.gradle.sharedServices.registerIfAbsent(DOWNLOAD_SERVICE_NAME, DownloadService::class) { + parameters.projectPath.set(target.projectDir) + } target.tasks.register("cleanCache") { group = "paper" @@ -64,6 +67,7 @@ class PaperweightCore : Plugin { target.configurations.create(REMAPPER_CONFIG) target.configurations.create(DECOMPILER_CONFIG) target.configurations.create(PAPERCLIP_CONFIG) + target.configurations.create(MACHE_CONFIG) if (target.providers.gradleProperty("paperweight.dev").orNull == "true") { target.tasks.register("diff") { @@ -85,10 +89,12 @@ class PaperweightCore : Plugin { ext.mainClass ) + val softSpoonTasks = SoftSpoonTasks(target, tasks) + target.createPatchRemapTask(tasks) target.tasks.register(PAPERWEIGHT_PREPARE_DOWNSTREAM) { - dependsOn(tasks.applyPatches) + dependsOn(tasks.applyPatchesLegacy) vanillaJar.set(tasks.downloadServerJar.flatMap { it.outputJar }) remappedJar.set(tasks.lineMapJar.flatMap { it.outputJar }) decompiledJar.set(tasks.decompileJar.flatMap { it.outputJar }) @@ -119,21 +125,35 @@ class PaperweightCore : Plugin { } target.afterEvaluate { + println("SoftSpoon: ${ext.softSpoon.get()}") + target.repositories { - maven(ext.paramMappingsRepo) { - name = PARAM_MAPPINGS_REPO_NAME - content { onlyForConfigurations(PARAM_MAPPINGS_CONFIG) } - } - maven(ext.remapRepo) { - name = REMAPPER_REPO_NAME - content { onlyForConfigurations(REMAPPER_CONFIG) } - } - maven(ext.decompileRepo) { - name = DECOMPILER_REPO_NAME - content { onlyForConfigurations(DECOMPILER_CONFIG) } + if (!ext.softSpoon.get()) { + maven(ext.paramMappingsRepo) { + name = PARAM_MAPPINGS_REPO_NAME + content { onlyForConfigurations(PARAM_MAPPINGS_CONFIG) } + } + maven(ext.remapRepo) { + name = REMAPPER_REPO_NAME + content { onlyForConfigurations(REMAPPER_CONFIG) } + } + maven(ext.decompileRepo) { + name = DECOMPILER_REPO_NAME + content { onlyForConfigurations(DECOMPILER_CONFIG) } + } + } else { + maven(ext.macheRepo) { + name = MACHE_REPO_NAME + content { onlyForConfigurations(MACHE_CONFIG) } + } } } + if (ext.softSpoon.get()) { + softSpoonTasks.afterEvaluate() + return@afterEvaluate + } + // Setup the server jar val cache = target.layout.cache diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt index 9d5fac37..c88be671 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt @@ -38,8 +38,11 @@ open class PaperExtension(objects: ObjectFactory, layout: ProjectLayout) { val spigotServerPatchDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "patches/server") val remappedSpigotServerPatchDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "patches/server-remapped") val unmappedSpigotServerPatchDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "patches/server-unmapped") - val paperApiDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "Paper-API") - val paperServerDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "Paper-Server") + val paperApiDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "paper-api") + val paperServerDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "paper-server") + val sourcePatchDir: DirectoryProperty = objects.dirFrom(paperServerDir, "patches/sources") + val resourcePatchDir: DirectoryProperty = objects.dirFrom(paperServerDir, "patches/resources") + val featurePatchDir: DirectoryProperty = objects.dirFrom(paperServerDir, "patches/feature") @Suppress("MemberVisibilityCanBePrivate") val buildDataDir: DirectoryProperty = objects.dirWithDefault(layout, "build-data") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt index 08c2fddf..3b016c15 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt @@ -36,10 +36,13 @@ import org.gradle.kotlin.dsl.* open class PaperweightCoreExtension(project: Project, objects: ObjectFactory, layout: ProjectLayout) { + val softSpoon: Property = objects.property().convention(false) + @Suppress("MemberVisibilityCanBePrivate") val workDir: DirectoryProperty = objects.dirWithDefault(layout, "work") val minecraftVersion: Property = objects.property() + val minecraftManifestUrl: Property = objects.property().convention(MC_MANIFEST_URL) val serverProject: Property = objects.property() val mainClass: Property = objects.property().convention("org.bukkit.craftbukkit.Main") @@ -50,6 +53,7 @@ open class PaperweightCoreExtension(project: Project, objects: ObjectFactory, la val paramMappingsRepo: Property = objects.property() val decompileRepo: Property = objects.property() val remapRepo: Property = objects.property() + val macheRepo: Property = objects.property().convention("https://repo.papermc.io/repository/maven-public/") val vanillaJarIncludes: ListProperty = objects.listProperty().convention( listOf("/*.class", "/net/minecraft/**", "/com/mojang/math/**") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt index 0b7b31e8..41d8a03d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt @@ -102,6 +102,23 @@ open class AllTasks( downloader.set(downloadService) } + val downloadPaperLibrariesSources by tasks.registering { + paperDependencies.set( + project.ext.serverProject.map { p -> + val configuration = p.configurations["implementation"] + configuration.isCanBeResolved = true + configuration.resolvedConfiguration.resolvedArtifacts.map { + "${it.moduleVersion.id.group}:${it.moduleVersion.id.name}:${it.moduleVersion.id.version}" + } + } + ) + repositories.set(listOf(MAVEN_CENTRAL_URL, PAPER_MAVEN_REPO_URL)) + outputDir.set(cache.resolve(PAPER_SOURCES_JARS_PATH)) + sources.set(true) + + downloader.set(downloadService) + } + @Suppress("DuplicatedCode") val applyServerPatches by tasks.registering { group = "paper" @@ -127,7 +144,7 @@ open class AllTasks( mcDevSources.set(extension.mcDevSourceDir) } - val applyPatches by tasks.registering { + val applyPatchesLegacy by tasks.registering { group = "paper" description = "Set up the Paper development environment" dependsOn(applyApiPatches, applyServerPatches) @@ -152,7 +169,7 @@ open class AllTasks( } @Suppress("unused") - val rebuildPatches by tasks.registering { + val rebuildPatchesLegacy by tasks.registering { group = "paper" description = "Rebuilds patches to api and server" dependsOn(rebuildApiPatches, rebuildServerPatches) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/InitialTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/InitialTasks.kt index 09af05c9..ad8982f1 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/InitialTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/InitialTasks.kt @@ -45,7 +45,7 @@ open class InitialTasks( ) { val downloadMcManifest by tasks.registering { - url.set(MC_MANIFEST_URL) + url.set(project.ext.minecraftManifestUrl) outputFile.set(cache.resolve(MC_MANIFEST)) doNotTrackState("The Minecraft manifest is a changing resource") @@ -54,7 +54,7 @@ open class InitialTasks( } private val mcManifest = downloadMcManifest.flatMap { it.outputFile }.map { gson.fromJson(it) } - val downloadMcVersionManifest by tasks.registering { + val downloadMcVersionManifest by tasks.registering { url.set( mcManifest.zip(extension.minecraftVersion) { manifest, version -> manifest.versions.first { it.id == version }.url @@ -71,7 +71,7 @@ open class InitialTasks( } private val versionManifest = downloadMcVersionManifest.flatMap { it.outputFile }.map { gson.fromJson(it) } - val downloadMappings by tasks.registering { + val downloadMappings by tasks.registering { url.set(versionManifest.map { version -> version.serverMappingsDownload().url }) expectedHash.set(versionManifest.map { version -> version.serverMappingsDownload().hash() }) outputFile.set(cache.resolve(SERVER_MAPPINGS)) @@ -94,5 +94,6 @@ open class InitialTasks( serverLibrariesList.set(cache.resolve(SERVER_LIBRARIES_LIST)) serverVersionsList.set(cache.resolve(SERVER_VERSIONS_LIST)) serverLibraryJars.set(cache.resolve(MINECRAFT_JARS_PATH)) + serverJar.set(cache.resolve(SERVER_JAR)) } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SoftSpoonTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SoftSpoonTasks.kt new file mode 100644 index 00000000..0ce3f73b --- /dev/null +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SoftSpoonTasks.kt @@ -0,0 +1,344 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.core.taskcontainers + +import io.papermc.paperweight.core.ext +import io.papermc.paperweight.tasks.* +import io.papermc.paperweight.tasks.mache.* +import io.papermc.paperweight.tasks.mache.RemapJar +import io.papermc.paperweight.tasks.softspoon.ApplyFeaturePatches +import io.papermc.paperweight.tasks.softspoon.ApplyFilePatches +import io.papermc.paperweight.tasks.softspoon.ApplyFilePatchesFuzzy +import io.papermc.paperweight.tasks.softspoon.RebuildFilePatches +import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.constants.* +import io.papermc.paperweight.util.data.mache.* +import java.nio.file.Files +import java.nio.file.Path +import kotlin.io.path.* +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.TaskContainer +import org.gradle.kotlin.dsl.* + +open class SoftSpoonTasks( + val project: Project, + val allTasks: AllTasks, + tasks: TaskContainer = project.tasks +) { + + lateinit var mache: MacheMeta + + val macheCodebook by project.configurations.registering { + isTransitive = false + } + val macheRemapper by project.configurations.registering { + isTransitive = false + } + val macheDecompiler by project.configurations.registering { + isTransitive = false + } + val macheParamMappings by project.configurations.registering { + isTransitive = false + } + val macheConstants by project.configurations.registering { + isTransitive = false + } + val macheMinecraft by project.configurations.registering + val macheMinecraftExtended by project.configurations.registering + + val macheRemapJar by tasks.registering(RemapJar::class) { + group = "mache" + serverJar.set(allTasks.extractFromBundler.flatMap { it.serverJar }) + serverMappings.set(allTasks.downloadMappings.flatMap { it.outputFile }) + + remapperArgs.set(mache.remapperArgs) + codebookClasspath.from(macheCodebook) + minecraftClasspath.from(macheMinecraft) + remapperClasspath.from(macheRemapper) + paramMappings.from(macheParamMappings) + constants.from(macheConstants) + + outputJar.set(layout.cache.resolve(FINAL_REMAPPED_CODEBOOK_JAR)) + } + + val macheDecompileJar by tasks.registering(DecompileJar::class) { + group = "mache" + inputJar.set(macheRemapJar.flatMap { it.outputJar }) + decompilerArgs.set(mache.decompilerArgs) + + minecraftClasspath.from(macheMinecraft) + decompiler.from(macheDecompiler) + + outputJar.set(layout.cache.resolve(FINAL_DECOMPILE_JAR)) + } + + val collectAccessTransform by tasks.registering(CollectATsFromPatches::class) { + group = "mache" + + patchDir.set(project.ext.paper.featurePatchDir) + } + + val mergeCollectedAts by tasks.registering { + firstFile.set(project.ext.paper.additionalAts.fileExists(project)) + secondFile.set(collectAccessTransform.flatMap { it.outputFile }) + } + + val setupMacheSources by tasks.registering(SetupVanilla::class) { + group = "mache" + description = "Setup vanilla source dir (applying mache patches and paper ATs)." + + mache.from(project.configurations.named(MACHE_CONFIG)) + machePatches.set(layout.cache.resolve(PATCHES_FOLDER)) + ats.set(mergeCollectedAts.flatMap { it.outputFile }) + minecraftClasspath.from(macheMinecraft) + + libraries.from(allTasks.downloadPaperLibrariesSources.flatMap { it.outputDir }, allTasks.downloadMcLibrariesSources.flatMap { it.outputDir }) + paperPatches.from(project.ext.paper.sourcePatchDir, project.ext.paper.featurePatchDir) + devImports.set(project.ext.paper.devImports.fileExists(project)) + + inputFile.set(macheDecompileJar.flatMap { it.outputJar }) + predicate.set { Files.isRegularFile(it) && it.toString().endsWith(".java") } + outputDir.set(layout.cache.resolve(BASE_PROJECT).resolve("sources")) + } + + val setupMacheResources by tasks.registering(SetupVanilla::class) { + group = "mache" + description = "Setup vanilla resources dir" + + inputFile.set(allTasks.extractFromBundler.flatMap { it.serverJar }) + predicate.set { Files.isRegularFile(it) && !it.toString().endsWith(".class") } + outputDir.set(layout.cache.resolve(BASE_PROJECT).resolve("resources")) + } + + val applySourcePatches by tasks.registering(ApplyFilePatches::class) { + group = "softspoon" + description = "Applies patches to the vanilla sources" + + input.set(setupMacheSources.flatMap { it.outputDir }) + output.set(project.ext.serverProject.map { it.layout.projectDirectory.dir("src/vanilla/java") }) + patches.set(project.ext.paper.sourcePatchDir) + } + + val applySourcePatchesFuzzy by tasks.registering(ApplyFilePatchesFuzzy::class) { + group = "softspoon" + description = "Applies patches to the vanilla sources" + + input.set(setupMacheSources.flatMap { it.outputDir }) + output.set(project.ext.serverProject.map { it.layout.projectDirectory.dir("src/vanilla/java") }) + patches.set(project.ext.paper.sourcePatchDir) + } + + val applyResourcePatches by tasks.registering(ApplyFilePatches::class) { + group = "softspoon" + description = "Applies patches to the vanilla resources" + + input.set(setupMacheResources.flatMap { it.outputDir }) + output.set(project.ext.serverProject.map { it.layout.projectDirectory.dir("src/vanilla/resources") }) + patches.set(project.ext.paper.resourcePatchDir) + } + + val applyFilePatches by tasks.registering(Task::class) { + group = "softspoon" + description = "Applies all file patches" + dependsOn(applySourcePatches, applyResourcePatches) + } + + val applyFeaturePatches by tasks.registering(ApplyFeaturePatches::class) { + group = "softspoon" + description = "Applies all feature patches" + dependsOn(applyFilePatches) + + repo.set(project.ext.serverProject.map { it.layout.projectDirectory.dir("src/vanilla/java") }) + patches.set(project.ext.paper.featurePatchDir) + } + + val applyPatches by tasks.registering(Task::class) { + group = "softspoon" + description = "Applies all patches" + dependsOn(applyFilePatches, applyFeaturePatches) + } + + val rebuildSourcePatches by tasks.registering(RebuildFilePatches::class) { + group = "softspoon" + description = "Rebuilds patches to the vanilla sources" + + minecraftClasspath.from(macheMinecraftExtended) + atFile.set(project.ext.paper.additionalAts.fileExists(project)) + atFileOut.set(project.ext.paper.additionalAts.fileExists(project)) + + base.set(layout.cache.resolve(BASE_PROJECT).resolve("sources")) + input.set(project.ext.serverProject.map { it.layout.projectDirectory.dir("src/vanilla/java") }) + patches.set(project.ext.paper.sourcePatchDir) + } + + val rebuildResourcePatches by tasks.registering(RebuildFilePatches::class) { + group = "softspoon" + description = "Rebuilds patches to the vanilla resources" + + base.set(layout.cache.resolve(BASE_PROJECT).resolve("resources")) + input.set(project.ext.serverProject.map { it.layout.projectDirectory.dir("src/vanilla/resources") }) + patches.set(project.ext.paper.resourcePatchDir) + } + + val rebuildFilePatches by tasks.registering(Task::class) { + group = "softspoon" + description = "Rebuilds all file patches" + dependsOn(rebuildSourcePatches, rebuildResourcePatches) + } + + val rebuildFeaturePatches by tasks.registering(RebuildGitPatches::class) { + group = "softspoon" + description = "Rebuilds all feature patches" + dependsOn(rebuildFilePatches) + + inputDir.set(project.ext.serverProject.map { it.layout.projectDirectory.dir("src/vanilla/java") }) + patchDir.set(project.ext.paper.featurePatchDir) + baseRef.set("file") + } + + val rebuildPatches by tasks.registering(Task::class) { + group = "softspoon" + description = "Rebuilds all file patches" + dependsOn(rebuildFilePatches, rebuildFeaturePatches) + } + + fun afterEvaluate() { + // load mache + mache = this.project.configurations.named(MACHE_CONFIG).get().singleFile.toPath().openZip().use { zip -> + return@use gson.fromJson(zip.getPath("/mache.json").readLines().joinToString("\n")) + } + println("Loaded mache ${mache.macheVersion} for minecraft ${mache.minecraftVersion}") + + // setup repos + this.project.repositories { + println("setup repos for ${project.name}") + for (repository in mache.repositories) { + maven(repository.url) { + name = repository.name + mavenContent { + for (group in repository.groups ?: listOf()) { + includeGroupByRegex(group + ".*") + } + } + } + } + + maven(MC_LIBRARY_URL) { + name = "Minecraft" + } + mavenCentral() + } + + val libsFile = project.layout.cache.resolve(SERVER_LIBRARIES_TXT) + + // setup mc deps + macheMinecraft { + withDependencies { + project.dependencies { + val libs = libsFile.convertToPathOrNull() + if (libs != null && libs.exists()) { + libs.forEachLine { line -> + add(create(line)) + } + } + } + } + } + macheMinecraftExtended { + extendsFrom(macheMinecraft.get()) + withDependencies { + project.dependencies { + add(create(project.files(project.layout.cache.resolve(FINAL_REMAPPED_CODEBOOK_JAR)))) + } + } + } + + // setup mache deps + this.project.dependencies { + mache.dependencies.codebook.forEach { + "macheCodebook"(it.toMavenString()) + } + mache.dependencies.paramMappings.forEach { + "macheParamMappings"(it.toMavenString()) + } + mache.dependencies.constants.forEach { + "macheConstants"(it.toMavenString()) + } + mache.dependencies.remapper.forEach { + "macheRemapper"(it.toMavenString()) + } + mache.dependencies.decompiler.forEach { + "macheDecompiler"(it.toMavenString()) + } + } + + this.project.ext.serverProject.get().setupServerProject(libsFile) + } + + private fun Project.setupServerProject(libsFile: Path) { + if (!projectDir.exists()) { + return + } + + // minecraft deps + val macheMinecraft by configurations.creating { + withDependencies { + dependencies { + // setup mc deps + val libs = libsFile.convertToPathOrNull() + if (libs != null && libs.exists()) { + libs.forEachLine { line -> + add(create(line)) + } + } + } + } + } + + // impl extends minecraft + configurations.named(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME) { + extendsFrom(macheMinecraft) + } + + // repos + repositories { + mavenCentral() + maven(PAPER_MAVEN_REPO_URL) + maven(MC_LIBRARY_URL) + } + + // add vanilla source set + the().sourceSets.named(SourceSet.MAIN_SOURCE_SET_NAME) { + java { + srcDirs(projectDir.resolve("src/vanilla/java")) + } + resources { + srcDirs(projectDir.resolve("src/vanilla/resources")) + } + } + } +} diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt index 4deb154a..83b77551 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt @@ -53,7 +53,9 @@ open class SpigotTasks( val generateSpigotMappings by tasks.registering { classMappings.set(addAdditionalSpigotMappings.flatMap { it.outputClassSrg }) - sourceMappings.set(generateMappings.flatMap { it.outputMappings }) + // todo hypo update breaks generate mappings, hardcode for now + // sourceMappings.set(generateMappings.flatMap { it.outputMappings }) + sourceMappings.set(Path.of("D:\\IntellijProjects\\PaperClean\\.gradle\\caches\\paperweight\\mappings\\official-mojang+yarn.tiny")) outputMappings.set(cache.resolve(SPIGOT_MOJANG_YARN_MAPPINGS)) notchToSpigotMappings.set(cache.resolve(OBF_SPIGOT_MAPPINGS)) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/VanillaTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/VanillaTasks.kt index e73f13eb..c03e4c66 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/VanillaTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/VanillaTasks.kt @@ -49,7 +49,8 @@ open class VanillaTasks( val remapJar by tasks.registering { inputJar.set(filterVanillaJar.flatMap { it.outputJar }) - mappingsFile.set(generateMappings.flatMap { it.outputMappings }) + // mappingsFile.set(generateMappings.flatMap { it.outputMappings }) + mappingsFile.set(Path.of("D:\\IntellijProjects\\PaperClean\\.gradle\\caches\\paperweight\\mappings\\official-mojang+yarn.tiny")) fromNamespace.set(OBF_NAMESPACE) toNamespace.set(DEOBF_NAMESPACE) remapper.from(project.configurations.named(REMAPPER_CONFIG)) diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt new file mode 100644 index 00000000..3fc6a160 --- /dev/null +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt @@ -0,0 +1,153 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight + +import io.papermc.paperweight.util.* +import java.net.URL +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.* +import kotlin.test.Test +import kotlin.test.assertEquals +import org.gradle.testkit.runner.TaskOutcome +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.io.CleanupMode +import org.junit.jupiter.api.io.TempDir + +class FunctionalTest { + + val debug = false + + @Disabled + @Test + fun setupCleanTestRepo() { + val projectDir = Path.of("F:\\Projects\\paperweight\\test").ensureClean().createDirectories() + + setupMache("fake_mache", projectDir.resolve("mache.zip")) + setupMojang("fake_mojang", projectDir.resolve("fake_mojang")) + projectDir.copyProject("functional_test") + + val settings = projectDir.resolve("settings.gradle") + val text = settings.readText() + settings.writeText(text.replace("// includeBuild '..'", "includeBuild '..'").replace("functional_test", "test")) + + projectDir.resolve("patches").deleteRecursively() + } + + @Test + fun `test simple test project`(@TempDir(cleanup = CleanupMode.NEVER) tempDir: Path) { + val testResource = Paths.get("src/test/resources/functional_test") + + setupMache("fake_mache", tempDir.resolve("mache.zip")) + setupMojang("fake_mojang", tempDir.resolve("fake_mojang")) + + val gradleRunner = tempDir.copyProject("functional_test").gradleRunner() + + // appP -> works + val appP = gradleRunner + .withArguments("applyPatches", "dependencies", ":test-server:dependencies", "--stacktrace", "-Dfake=true") + .withDebug(debug) + .build() + + assertEquals(appP.task(":applyPatches")?.outcome, TaskOutcome.SUCCESS) + + // clean rebuild rebP -> changes nothing + val rebP = gradleRunner + .withArguments("rebuildPatches", "--stacktrace", "-Dfake=true") + .withDebug(debug) + .build() + + assertEquals(rebP.task(":rebuildPatches")?.outcome, TaskOutcome.SUCCESS) + assertEquals( + testResource.resolve("fake-patches/sources/Test.java.patch").readText(), + tempDir.resolve("fake-patches/sources/Test.java.patch").readText() + ) + + // add AT to source -> patch and AT file is updated + val sourceFile = tempDir.resolve("test-server/src/vanilla/java/Test.java") + val replacedContent = sourceFile.readText().replace( + "\"2\";", + "\"2\"; // Woo" + ).replace("public String getTest2() {", "private final String getTest2() {// Paper-AT: private+f getTest2()Ljava/lang/String;") + sourceFile.writeText(replacedContent) + + val rebP2 = gradleRunner + .withArguments("rebuildPatches", "--stacktrace", "-Dfake=true") + .withDebug(debug) + .build() + + assertEquals(rebP2.task(":rebuildPatches")?.outcome, TaskOutcome.SUCCESS) + assertEquals( + testResource.resolve("fake-patches/expected/Test.java.patch").readText(), + tempDir.resolve("fake-patches/sources/Test.java.patch").readText() + ) + assertEquals(testResource.resolve("build-data/expected.at").readText(), tempDir.resolve("build-data/fake.at").readText()) + } + + @Test + fun `test full vanilla project`(@TempDir(cleanup = CleanupMode.ON_SUCCESS) tempDir: Path) { + val result = tempDir.copyProject("functional_test") + .gradleRunner() + .withArguments("applyPatches", "-Dfake=false") + .withDebug(debug) + .build() + + assertEquals(result.task(":applyPatches")?.outcome, TaskOutcome.SUCCESS) + } + + fun setupMache(macheName: String, target: Path) { + val macheDir = Paths.get("src/test/resources/$macheName") + zip(macheDir, target) + } + + fun setupMojang(mojangName: String, target: Path) { + val mojangDir = Paths.get("src/test/resources/$mojangName") + mojangDir.copyRecursivelyTo(target) + + val serverFolder = target.resolve("server") + ProcessBuilder() + .directory(serverFolder) + .command("javac", serverFolder.resolve("Test.java").toString()) + .redirectErrorStream(true) + .start() + .waitFor() + + ProcessBuilder() + .directory(serverFolder) + .command("jar", "-cf", "server.jar", "Test.class", "test.json") + .redirectErrorStream(true) + .start() + .waitFor() + + val versionFolder = target.resolve("bundle/META-INF/versions/fake/") + versionFolder.createDirectories() + serverFolder.resolve("server.jar").copyTo(versionFolder.resolve("server.jar")) + + val oshiFolder = target.resolve("bundle/META-INF/libraries/com/github/oshi/oshi-core/6.4.5/") + oshiFolder.createDirectories() + oshiFolder.resolve( + "oshi-core-6.4.5.jar" + ).writeBytes(URL("https://libraries.minecraft.net/com/github/oshi/oshi-core/6.4.5/oshi-core-6.4.5.jar").readBytes()) + zip(target.resolve("bundle"), target.resolve("bundle.jar")) + } +} diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/util.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/util.kt new file mode 100644 index 00000000..102ccf98 --- /dev/null +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/util.kt @@ -0,0 +1,49 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight + +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.* +import org.gradle.testkit.runner.GradleRunner + +fun Path.copyProject(resourcesProjectName: String): ProjectFiles { + Paths.get("src/test/resources/$resourcesProjectName") + .copyToRecursively(this, followLinks = false) + return ProjectFiles(this) +} + +class ProjectFiles(val projectDir: Path) { + val gradleProperties: Path = resolve("gradle.properties") + val buildGradle: Path = resolve("build.gradle") + val buildGradleKts: Path = resolve("build.gradle.kts") + val settingsGradle: Path = resolve("settings.gradle") + val settingsGradleKts: Path = resolve("settings.gradle.kts") + + fun resolve(path: String): Path = projectDir.resolve(path) + + fun gradleRunner(): GradleRunner = GradleRunner.create() + .forwardOutput() + .withPluginClasspath() + .withProjectDir(projectDir.toFile()) +} diff --git a/paperweight-core/src/test/resources/fake_mache/mache.json b/paperweight-core/src/test/resources/fake_mache/mache.json new file mode 100644 index 00000000..17266401 --- /dev/null +++ b/paperweight-core/src/test/resources/fake_mache/mache.json @@ -0,0 +1,96 @@ +{ + "version": "fake", + "dependencies": { + "codebook": [ + { + "group": "io.papermc.codebook", + "name": "codebook", + "version": "1.0.8" + } + ], + "paramMappings": [ + { + "group": "org.parchmentmc.data", + "name": "parchment-1.20.1", + "version": "2023.09.03", + "extension": "zip" + } + ], + "constants": [ + ], + "remapper": [ + { + "group": "net.neoforged", + "name": "AutoRenamingTool", + "version": "1.0.7" + } + ], + "decompiler": [ + { + "group": "org.vineflower", + "name": "vineflower", + "version": "1.10.0-SNAPSHOT" + } + ] + }, + "repositories": [ + { + "url": "https://maven.fabricmc.net/", + "name": "FabricMC", + "groups": [ + "net.fabricmc" + ] + }, + { + "url": "https://maven.neoforged.net/releases/", + "name": "NeoForged", + "groups": [ + "net.neoforged", + "net.minecraftforge" + ] + }, + { + "url": "https://repo.papermc.io/repository/maven-public/", + "name": "PaperMC", + "groups": [ + "io.papermc" + ] + }, + { + "url": "https://maven.parchmentmc.org/", + "name": "ParchmentMC", + "groups": [ + "org.parchmentmc" + ] + }, + { + "url": "https://s01.oss.sonatype.org/content/repositories/snapshots/", + "name": "sonatype snapshots", + "groups": [ + "org.vineflower" + ] + } + ], + "decompilerArgs": [ + "-nns=true", + "-tcs=true", + "-ovr=false", + "-vvm=true", + "-iec=true", + "-jrt=current", + "-ind= ", + "-jvn=false", + "-dcc=true", + "-sef=true", + "-nls=1" + ], + "additionalCompileDependencies": { + "compileOnly": [ + { + "group": "org.jetbrains", + "name": "annotations", + "version": "24.0.1" + } + ] + } +} diff --git a/paperweight-core/src/test/resources/fake_mache/patches/Test.java.patch b/paperweight-core/src/test/resources/fake_mache/patches/Test.java.patch new file mode 100644 index 00000000..527fd0bd --- /dev/null +++ b/paperweight-core/src/test/resources/fake_mache/patches/Test.java.patch @@ -0,0 +1,9 @@ +--- a/Test.java ++++ b/Test.java +@@ -1,6 +1,6 @@ + public class Test { +- public int dum; ++ public int dum2; + private final String test; + + public Test(String var1) { diff --git a/paperweight-core/src/test/resources/fake_mojang/bundle/META-INF/libraries.list b/paperweight-core/src/test/resources/fake_mojang/bundle/META-INF/libraries.list new file mode 100644 index 00000000..5e85ec5d --- /dev/null +++ b/paperweight-core/src/test/resources/fake_mojang/bundle/META-INF/libraries.list @@ -0,0 +1 @@ +- com.github.oshi:oshi-core:6.4.5 com/github/oshi/oshi-core/6.4.5/oshi-core-6.4.5.jar diff --git a/paperweight-core/src/test/resources/fake_mojang/bundle/META-INF/versions.list b/paperweight-core/src/test/resources/fake_mojang/bundle/META-INF/versions.list new file mode 100644 index 00000000..6c9206a7 --- /dev/null +++ b/paperweight-core/src/test/resources/fake_mojang/bundle/META-INF/versions.list @@ -0,0 +1 @@ +- fake fake/server.jar diff --git a/paperweight-core/src/test/resources/fake_mojang/bundle/version.json b/paperweight-core/src/test/resources/fake_mojang/bundle/version.json new file mode 100644 index 00000000..d31cbf41 --- /dev/null +++ b/paperweight-core/src/test/resources/fake_mojang/bundle/version.json @@ -0,0 +1,3 @@ +{ + "id": "fake" +} diff --git a/paperweight-core/src/test/resources/fake_mojang/mappings.txt b/paperweight-core/src/test/resources/fake_mojang/mappings.txt new file mode 100644 index 00000000..5d04b367 --- /dev/null +++ b/paperweight-core/src/test/resources/fake_mojang/mappings.txt @@ -0,0 +1,19 @@ +com.mojang.math.Axis -> a: +# {"fileName":"Axis.java","id":"sourceFile"} + com.mojang.math.Axis XN -> a + com.mojang.math.Axis XP -> b + com.mojang.math.Axis YN -> c + com.mojang.math.Axis YP -> d + com.mojang.math.Axis ZN -> e + com.mojang.math.Axis ZP -> f + 17:17:com.mojang.math.Axis of(org.joml.Vector3f) -> of + org.joml.Quaternionf rotation(float) -> rotation + 23:23:org.joml.Quaternionf rotationDegrees(float) -> rotationDegrees + 17:17:org.joml.Quaternionf lambda$of$6(org.joml.Vector3f,float) -> a + 14:14:org.joml.Quaternionf lambda$static$5(float) -> a + 13:13:org.joml.Quaternionf lambda$static$4(float) -> b + 12:12:org.joml.Quaternionf lambda$static$3(float) -> c + 11:11:org.joml.Quaternionf lambda$static$2(float) -> d + 10:10:org.joml.Quaternionf lambda$static$1(float) -> e + 9:9:org.joml.Quaternionf lambda$static$0(float) -> f + 9:14:void () -> diff --git a/paperweight-core/src/test/resources/fake_mojang/server/Test.java b/paperweight-core/src/test/resources/fake_mojang/server/Test.java new file mode 100644 index 00000000..ab9fb25d --- /dev/null +++ b/paperweight-core/src/test/resources/fake_mojang/server/Test.java @@ -0,0 +1,17 @@ +public class Test { + + public int dum; + private final String test; + + public Test(String test) { + this.test = test; + } + + public String getTest() { + return test; + } + + public String getTest2() { + return test + "2"; + } +} diff --git a/paperweight-core/src/test/resources/fake_mojang/server/test.json b/paperweight-core/src/test/resources/fake_mojang/server/test.json new file mode 100644 index 00000000..d0ae716d --- /dev/null +++ b/paperweight-core/src/test/resources/fake_mojang/server/test.json @@ -0,0 +1,3 @@ +{ + "test": "test" +} diff --git a/paperweight-core/src/test/resources/fake_mojang/version.json b/paperweight-core/src/test/resources/fake_mojang/version.json new file mode 100644 index 00000000..b6f14b3b --- /dev/null +++ b/paperweight-core/src/test/resources/fake_mojang/version.json @@ -0,0 +1,12 @@ +{ + "downloads": { + "server": { + "sha1": "", + "url": "file://project/fake_mojang/bundle.jar" + }, + "server_mappings": { + "sha1": "", + "url": "file://project/fake_mojang/mappings.txt" + } + } +} diff --git a/paperweight-core/src/test/resources/fake_mojang/version_manifest.json b/paperweight-core/src/test/resources/fake_mojang/version_manifest.json new file mode 100644 index 00000000..7aa49bb6 --- /dev/null +++ b/paperweight-core/src/test/resources/fake_mojang/version_manifest.json @@ -0,0 +1,14 @@ +{ + "latest": { + "release": "fake", + "snapshot": "fake" + }, + "versions": [ + { + "id": "fake", + "type": "release", + "url": "file://project/fake_mojang/version.json", + "sha1": "" + } + ] +} diff --git a/paperweight-core/src/test/resources/functional_test/.gitignore b/paperweight-core/src/test/resources/functional_test/.gitignore new file mode 100644 index 00000000..c6b58a75 --- /dev/null +++ b/paperweight-core/src/test/resources/functional_test/.gitignore @@ -0,0 +1,2 @@ +.gradle +gradle diff --git a/paperweight-core/src/test/resources/functional_test/build-data/expected.at b/paperweight-core/src/test/resources/functional_test/build-data/expected.at new file mode 100644 index 00000000..c1877d75 --- /dev/null +++ b/paperweight-core/src/test/resources/functional_test/build-data/expected.at @@ -0,0 +1,3 @@ +public+f Test dum2 +public+f Test getTest()Ljava/lang/String; +private+f Test getTest2()Ljava/lang/String; diff --git a/paperweight-core/src/test/resources/functional_test/build-data/fake.at b/paperweight-core/src/test/resources/functional_test/build-data/fake.at new file mode 100644 index 00000000..135b91ba --- /dev/null +++ b/paperweight-core/src/test/resources/functional_test/build-data/fake.at @@ -0,0 +1,2 @@ +public+f Test dum2 +public+f Test getTest()Ljava/lang/String; diff --git a/paperweight-core/src/test/resources/functional_test/build-data/paper.at b/paperweight-core/src/test/resources/functional_test/build-data/paper.at new file mode 100644 index 00000000..aaf76bf2 --- /dev/null +++ b/paperweight-core/src/test/resources/functional_test/build-data/paper.at @@ -0,0 +1 @@ +private+f net.minecraft.CrashReport getTitle()Ljava/lang/String; diff --git a/paperweight-core/src/test/resources/functional_test/build.gradle b/paperweight-core/src/test/resources/functional_test/build.gradle new file mode 100644 index 00000000..e7ae99ec --- /dev/null +++ b/paperweight-core/src/test/resources/functional_test/build.gradle @@ -0,0 +1,46 @@ +plugins { + id 'java' + id 'com.github.johnrengelman.shadow' version '8.1.1' apply false + id 'io.papermc.paperweight.core' +} + +allprojects { + apply plugin: 'java' +} + +repositories { + mavenLocal() + mavenCentral() +} + +def fake = Boolean.getBoolean("fake") + +dependencies { + if (fake) { + mache files('mache.zip') // use fake mache for testing + } else { + mache 'io.papermc:mache:1.20.4+build.1' + } +} + +paperweight { + softSpoon = true + + if (fake) { + // use fake mojang data for testing + minecraftVersion = 'fake' + minecraftManifestUrl = 'file://project/fake_mojang/version_manifest.json' + + paper { + sourcePatchDir.set(file('fake-patches/sources')) + resourcePatchDir.set(file('fake-patches/resources')) + featurePatchDir.set(file('fake-patches/feature')) + + additionalAts.set(file('build-data/fake.at')) + } + } else { + minecraftVersion = '1.20.4' + } + + serverProject = project(':test-server') +} diff --git a/paperweight-core/src/test/resources/functional_test/fake-patches/expected/Test.java.patch b/paperweight-core/src/test/resources/functional_test/fake-patches/expected/Test.java.patch new file mode 100644 index 00000000..299026fd --- /dev/null +++ b/paperweight-core/src/test/resources/functional_test/fake-patches/expected/Test.java.patch @@ -0,0 +1,15 @@ +--- a/Test.java ++++ b/Test.java +@@ -7,10 +7,10 @@ + } + + public final String getTest() { +- return this.test; ++ return this.test; // WOOOO + } + + private final String getTest2() { +- return this.test + "2"; ++ return this.test + "2"; // Woo + } + } diff --git a/paperweight-core/src/test/resources/functional_test/fake-patches/feature/.gitkeep b/paperweight-core/src/test/resources/functional_test/fake-patches/feature/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/paperweight-core/src/test/resources/functional_test/fake-patches/resources/test.json.patch b/paperweight-core/src/test/resources/functional_test/fake-patches/resources/test.json.patch new file mode 100644 index 00000000..9da668bc --- /dev/null +++ b/paperweight-core/src/test/resources/functional_test/fake-patches/resources/test.json.patch @@ -0,0 +1,7 @@ +--- a/test.json ++++ b/test.json +@@ -1,3 +1,3 @@ + { +- "test": "test" ++ "test": "test2" + } diff --git a/paperweight-core/src/test/resources/functional_test/fake-patches/sources/Test.java.patch b/paperweight-core/src/test/resources/functional_test/fake-patches/sources/Test.java.patch new file mode 100644 index 00000000..1a2c3730 --- /dev/null +++ b/paperweight-core/src/test/resources/functional_test/fake-patches/sources/Test.java.patch @@ -0,0 +1,11 @@ +--- a/Test.java ++++ b/Test.java +@@ -7,7 +7,7 @@ + } + + public final String getTest() { +- return this.test; ++ return this.test; // WOOOO + } + + public String getTest2() { diff --git a/paperweight-core/src/test/resources/functional_test/patches/feature/0001-something.patch b/paperweight-core/src/test/resources/functional_test/patches/feature/0001-something.patch new file mode 100644 index 00000000..258cd572 --- /dev/null +++ b/paperweight-core/src/test/resources/functional_test/patches/feature/0001-something.patch @@ -0,0 +1 @@ +todo diff --git a/paperweight-core/src/test/resources/functional_test/patches/resources/version.json.patch b/paperweight-core/src/test/resources/functional_test/patches/resources/version.json.patch new file mode 100644 index 00000000..eadedd4b --- /dev/null +++ b/paperweight-core/src/test/resources/functional_test/patches/resources/version.json.patch @@ -0,0 +1,11 @@ +--- a/version.json ++++ b/version.json +@@ -1,6 +1,6 @@ + { +- "id": "1.20.4", +- "name": "1.20.4", ++ "id": "1.20.4-paper", ++ "name": "1.20.4 - Paper", + "world_version": 3700, + "series_id": "main", + "protocol_version": 765, diff --git a/paperweight-core/src/test/resources/functional_test/patches/sources/net/minecraft/CrashReport.java.patch b/paperweight-core/src/test/resources/functional_test/patches/sources/net/minecraft/CrashReport.java.patch new file mode 100644 index 00000000..d0d97d45 --- /dev/null +++ b/paperweight-core/src/test/resources/functional_test/patches/sources/net/minecraft/CrashReport.java.patch @@ -0,0 +1,15 @@ +--- a/net/minecraft/CrashReport.java ++++ b/net/minecraft/CrashReport.java +@@ -35,10 +35,11 @@ + public CrashReport(String title, Throwable exception) { + this.title = title; + this.exception = exception; ++ // FIXME this.systemReport.setDetail("CraftBukkit Information", new org.bukkit.craftbukkit.CraftCrashReport()); // CraftBukkit + } + + private final String getTitle() { +- return this.title; ++ return this.title; // Test + } + + public Throwable getException() { diff --git a/paperweight-core/src/test/resources/functional_test/settings.gradle b/paperweight-core/src/test/resources/functional_test/settings.gradle new file mode 100644 index 00000000..4ce338cd --- /dev/null +++ b/paperweight-core/src/test/resources/functional_test/settings.gradle @@ -0,0 +1,18 @@ +pluginManagement { + // includeBuild '..' + repositories { + mavenLocal() + gradlePluginPortal() + maven { + url "https://repo.papermc.io/repository/maven-public/" + } + maven { + url "https://maven.parchmentmc.org" + } + } +} + +rootProject.name = 'functional_test' + +include 'test-api' +include 'test-server' diff --git a/paperweight-core/src/test/resources/functional_test/test-api/build.gradle b/paperweight-core/src/test/resources/functional_test/test-api/build.gradle new file mode 100644 index 00000000..0e306a7b --- /dev/null +++ b/paperweight-core/src/test/resources/functional_test/test-api/build.gradle @@ -0,0 +1,3 @@ +plugins { + id "java-library" +} diff --git a/paperweight-core/src/test/resources/functional_test/test-server/build.gradle b/paperweight-core/src/test/resources/functional_test/test-server/build.gradle new file mode 100644 index 00000000..cd2984f1 --- /dev/null +++ b/paperweight-core/src/test/resources/functional_test/test-server/build.gradle @@ -0,0 +1,8 @@ +plugins { + id "java" + id "com.github.johnrengelman.shadow" +} + +dependencies { + implementation project(":test-api") +} diff --git a/paperweight-lib/build.gradle.kts b/paperweight-lib/build.gradle.kts index a880f52c..f6634e5a 100644 --- a/paperweight-lib/build.gradle.kts +++ b/paperweight-lib/build.gradle.kts @@ -5,6 +5,9 @@ plugins { dependencies { implementation(libs.httpclient) implementation(libs.bundles.kotson) + implementation(libs.coroutines) + implementation(libs.jgit) + implementation(libs.diffutils) // ASM for inspection implementation(libs.bundles.asm) @@ -15,9 +18,16 @@ dependencies { implementation(libs.lorenzTiny) + implementation(libs.feather.core) + implementation(libs.feather.gson) + implementation(libs.jbsdiff) + implementation(libs.restamp) + implementation(variantOf(libs.diffpatch) { classifier("all") }) { isTransitive = false } + + testImplementation(libs.mockk) } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/DownloadService.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/DownloadService.kt index 0833b603..12aae51c 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/DownloadService.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/DownloadService.kt @@ -32,6 +32,8 @@ import java.time.format.DateTimeFormatter import java.util.Locale import java.util.concurrent.TimeUnit import kotlin.io.path.* +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope import org.apache.http.HttpHost import org.apache.http.HttpStatus import org.apache.http.client.config.CookieSpecs @@ -41,12 +43,17 @@ import org.apache.http.client.methods.HttpGet import org.apache.http.client.utils.DateUtils import org.apache.http.impl.client.CloseableHttpClient import org.apache.http.impl.client.HttpClientBuilder +import org.gradle.api.file.DirectoryProperty import org.gradle.api.logging.Logger import org.gradle.api.logging.Logging import org.gradle.api.services.BuildService import org.gradle.api.services.BuildServiceParameters -abstract class DownloadService : BuildService, AutoCloseable { +abstract class DownloadService : BuildService, AutoCloseable { + + interface Params : BuildServiceParameters { + val projectPath: DirectoryProperty + } private companion object { val LOGGER: Logger = Logging.getLogger(DownloadService::class.java) @@ -64,13 +71,19 @@ abstract class DownloadService : BuildService, Auto download(url, file, hash) } + suspend fun downloadAsync(source: Any, target: Any, hash: Hash? = null) = coroutineScope { + async { + download(source.convertToUrl(), target.convertToPath(), hash, false) + } + } + private fun download(source: URL, target: Path, hash: Hash?, retry: Boolean = false) { download(source, target) if (hash == null) { return } val dlHash = target.hashFile(hash.algorithm).asHexString().lowercase(Locale.ENGLISH) - if (dlHash == hash.valueLower) { + if (hash.value == "" || dlHash == hash.valueLower) { return } LOGGER.warn( @@ -91,6 +104,15 @@ abstract class DownloadService : BuildService, Auto private fun download(source: URL, target: Path) { target.parent.createDirectories() + if (source.protocol == "file") { + var path = source.toString().replace("file://", "") + if (source.host == "project") { + path = path.replace("project", parameters.projectPath.path.absolutePathString()) + } + Path.of(path).copyTo(target, overwrite = true) + return + } + val etagDir = target.resolveSibling("etags") etagDir.createDirectories() diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ApplyGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ApplyGitPatches.kt index 9bc3ab13..03cd587b 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ApplyGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ApplyGitPatches.kt @@ -189,16 +189,16 @@ fun Git.disableAutoGpgSigningInRepo() { invoke("config", "tag.gpgSign", "false").executeSilently(silenceErr = true) } -fun checkoutRepoFromUpstream(git: Git, upstream: Path, upstreamBranch: String) { +fun checkoutRepoFromUpstream(git: Git, upstream: Path, upstreamBranch: String, upstreamName: String = "upstream", branchName: String = "master") { git("init", "--quiet").executeSilently(silenceErr = true) git.disableAutoGpgSigningInRepo() - git("remote", "remove", "upstream").runSilently(silenceErr = true) - git("remote", "add", "upstream", upstream.toUri().toString()).executeSilently(silenceErr = true) - git("fetch", "upstream", "--prune").executeSilently(silenceErr = true) - if (git("checkout", "master").runSilently(silenceErr = true) != 0) { - git("checkout", "-b", "master").runSilently(silenceErr = true) + git("remote", "remove", upstreamName).runSilently(silenceErr = true) + git("remote", "add", upstreamName, upstream.toUri().toString()).executeSilently(silenceErr = true) + git("fetch", upstreamName, "--prune").executeSilently(silenceErr = true) + if (git("checkout", branchName).runSilently(silenceErr = true) != 0) { + git("checkout", "-b", branchName).runSilently(silenceErr = true) } - git("reset", "--hard", "upstream/$upstreamBranch").executeSilently(silenceErr = true) + git("reset", "--hard", "$upstreamName/$upstreamBranch").executeSilently(silenceErr = true) git("gc").runSilently(silenceErr = true) } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/DownloadServerJar.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/DownloadServerJar.kt index 7040764d..6f13ee7d 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/DownloadServerJar.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/DownloadServerJar.kt @@ -28,7 +28,7 @@ import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.* -// Not cached since this is Mojang's server jar +@CacheableTask abstract class DownloadServerJar : BaseTask() { @get:Input @@ -41,7 +41,6 @@ abstract class DownloadServerJar : BaseTask() { abstract val downloader: Property @get:Nested - @get:Optional abstract val expectedHash: Property override fun init() { diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ExtractFromBundler.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ExtractFromBundler.kt index afd208eb..4e8ec1a7 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ExtractFromBundler.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ExtractFromBundler.kt @@ -59,11 +59,6 @@ abstract class ExtractFromBundler : BaseTask() { @get:OutputFile abstract val serverVersionsList: RegularFileProperty - override fun init() { - super.init() - serverJar.set(defaultOutput()) - } - @TaskAction fun run() { ServerBundler.extractFromBundler( @@ -99,16 +94,16 @@ object ServerBundler { } private fun extractServerJar(bundlerZip: Path, serverJar: Path, outputVersionJson: Path?) { - val serverVersionJson = bundlerZip.resolve("version.json") + val serverVersionJson = bundlerZip.resolve(FileEntry.VERSION_JSON) outputVersionJson?.let { output -> serverVersionJson.copyTo(output, overwrite = true) } val versionId = gson.fromJson(serverVersionJson)["id"].asString - val versions = bundlerZip.resolve("/META-INF/versions.list").readLines() + val versions = bundlerZip.resolve(FileEntry.VERSIONS_LIST).readLines() .map { it.split('\t') } .associate { it[1] to it[2] } - val serverJarPath = bundlerZip.resolve("/META-INF/versions/${versions[versionId]}") + val serverJarPath = bundlerZip.resolve("${FileEntry.VERSIONS_DIR}/${versions[versionId]}") serverJar.parent.createDirectories() serverJarPath.copyTo(serverJar, overwrite = true) @@ -117,7 +112,7 @@ object ServerBundler { private fun extractLibraryJars(bundlerZip: Path, serverLibraryJars: Path) { serverLibraryJars.deleteRecursive() serverLibraryJars.parent.createDirectories() - bundlerZip.resolve("/META-INF/libraries").copyRecursivelyTo(serverLibraryJars) + bundlerZip.resolve(FileEntry.LIBRARIES_DIR).copyRecursivelyTo(serverLibraryJars) } private fun writeLibrariesTxt(bundlerZip: Path, serverLibrariesTxt: Path) { diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/MacheTask.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/MacheTask.kt new file mode 100644 index 00000000..d00a9a70 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/MacheTask.kt @@ -0,0 +1,83 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.tasks + +import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.data.mache.* +import kotlin.io.path.* +import org.gradle.api.DefaultTask +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.TaskAction + +abstract class MacheTask : DefaultTask() { + + @get:Classpath + abstract val mache: ConfigurableFileCollection + + @TaskAction + fun run() { + val meta = getMacheMeta() + println("loaded meta: $meta") + } + + private fun getMacheMeta(): MacheMeta { + val metaJson = mutableListOf("") + mache.singleFile.toPath().openZip().use { zip -> + metaJson.addAll(zip.getPath("/mache.json").readLines()) + } + + return gson.fromJson(metaJson.joinToString("\n")) + } + + private fun extractVanillaJar() { + // val jar = downloadedJar.convertToPath() + // val out = serverJar.convertToPath().ensureClean() +// + // jar.useZip { root -> + // val metaInf = root.resolve("META-INF") + // val versionsList = metaInf.resolve("versions.list") + // if (versionsList.notExists()) { + // throw Exception("Could not find versions.list") + // } +// + // val lines = versionsList.readLines() + // if (lines.size != 1) { + // throw Exception("versions.list is invalid") + // } +// + // val line = lines.first() + // val parts = line.split(whitespace) + // if (parts.size != 3) { + // throw Exception("versions.list line is invalid") + // } +// + // val serverJarInJar = metaInf.resolve("versions").resolve(parts[2]) + // if (serverJarInJar.notExists()) { + // throw Exception("Could not find version jar") + // } +// + // serverJarInJar.copyTo(out) + // } + } +} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/MergeAccessTransforms.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/MergeAccessTransforms.kt index c7df0d17..ecc8d2c2 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/MergeAccessTransforms.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/MergeAccessTransforms.kt @@ -34,6 +34,7 @@ import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction +import writeLF @CacheableTask abstract class MergeAccessTransforms : BaseTask() { @@ -67,6 +68,6 @@ abstract class MergeAccessTransforms : BaseTask() { outputAt.merge(at) } - AccessTransformFormats.FML.write(outputFile.path, outputAt) + AccessTransformFormats.FML.writeLF(outputFile.path, outputAt) } } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RemapAccessTransform.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RemapAccessTransform.kt index 51359dc4..0f86bab3 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RemapAccessTransform.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RemapAccessTransform.kt @@ -32,6 +32,7 @@ import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction +import writeLF @CacheableTask abstract class RemapAccessTransform : BaseTask() { @@ -57,6 +58,6 @@ abstract class RemapAccessTransform : BaseTask() { val mappingSet = MappingFormats.TINY.read(mappings.path, SPIGOT_NAMESPACE, DEOBF_NAMESPACE) val resultAt = at.remap(mappingSet) - AccessTransformFormats.FML.write(outputFile.path, resultAt) + AccessTransformFormats.FML.writeLF(outputFile.path, resultAt) } } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RemapSources.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RemapSources.kt index 576aa55b..5afc7d79 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RemapSources.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RemapSources.kt @@ -51,6 +51,7 @@ import org.gradle.kotlin.dsl.* import org.gradle.workers.WorkAction import org.gradle.workers.WorkParameters import org.gradle.workers.WorkerExecutor +import writeLF @CacheableTask abstract class RemapSources : JavaLauncherTask() { @@ -260,7 +261,7 @@ abstract class RemapSources : JavaLauncherTask() { } if (generatedAtOutPath != null) { - AccessTransformFormats.FML.write(generatedAtOutPath, processAt) + AccessTransformFormats.FML.writeLF(generatedAtOutPath, processAt) } } } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RemapSpigotAt.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RemapSpigotAt.kt index 5f25d777..90978064 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RemapSpigotAt.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RemapSpigotAt.kt @@ -40,6 +40,7 @@ import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction +import writeLF @CacheableTask abstract class RemapSpigotAt : BaseTask() { @@ -108,7 +109,7 @@ abstract class RemapSpigotAt : BaseTask() { val mappings = MappingFormats.TINY.read(mapping.path, SPIGOT_NAMESPACE, DEOBF_NAMESPACE) val remappedAt = outputAt.remap(mappings) - AccessTransformFormats.FML.write(outputFile.path, remappedAt) + AccessTransformFormats.FML.writeLF(outputFile.path, remappedAt) } private fun parseAccess(text: String): AccessTransform { diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/download-task.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/download-task.kt index 6e4fcf25..1cb95e6f 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/download-task.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/download-task.kt @@ -71,6 +71,9 @@ abstract class DownloadTask : DefaultTask() { fun run() = downloader.get().download(url, outputFile, expectedHash.orNull) } +@CacheableTask +abstract class CacheableDownloadTask : DownloadTask() + @CacheableTask abstract class DownloadMcLibraries : BaseTask() { @@ -100,7 +103,7 @@ abstract class DownloadMcLibraries : BaseTask() { @TaskAction fun run() { - downloadMinecraftLibraries( + downloadLibraries( downloader, workerExecutor, outputDir.path, @@ -111,20 +114,62 @@ abstract class DownloadMcLibraries : BaseTask() { } } -fun downloadMinecraftLibraries( +@CacheableTask +abstract class DownloadPaperLibraries : BaseTask() { + + @get:Input + abstract val paperDependencies: ListProperty + + @get:Input + abstract val repositories: ListProperty + + @get:OutputDirectory + abstract val outputDir: DirectoryProperty + + @get:Internal + abstract val downloader: Property + + @get:Inject + abstract val workerExecutor: WorkerExecutor + + @get:Input + abstract val sources: Property + + override fun init() { + super.init() + sources.convention(false) + } + + @TaskAction + fun run() { + downloadLibraries( + downloader, + workerExecutor, + outputDir.path, + repositories.get(), + paperDependencies.get(), + sources.get() + ) + } +} + +fun downloadLibraries( download: Provider, workerExecutor: WorkerExecutor, targetDir: Path, repositories: List, - mcLibraries: List, + libraries: List, sources: Boolean ): WorkQueue { val excludes = listOf(targetDir.fileSystem.getPathMatcher("glob:*.etag")) targetDir.deleteRecursive(excludes) + if (!targetDir.exists()) { + targetDir.createDirectories() + } val queue = workerExecutor.noIsolation() - for (lib in mcLibraries) { + for (lib in libraries) { if (sources) { queue.submit(DownloadSourcesToDirAction::class) { repos.set(repositories) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/mache/DecompileJar.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/mache/DecompileJar.kt new file mode 100644 index 00000000..17015b20 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/mache/DecompileJar.kt @@ -0,0 +1,112 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.tasks.mache + +import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.constants.* +import javax.inject.Inject +import kotlin.io.path.absolutePathString +import kotlin.io.path.deleteIfExists +import kotlin.io.path.name +import kotlin.io.path.outputStream +import kotlin.io.path.writeText +import org.gradle.api.DefaultTask +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.ProjectLayout +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.CompileClasspath +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction +import org.gradle.process.ExecOperations + +@CacheableTask +abstract class DecompileJar : DefaultTask() { + + @get:PathSensitive(PathSensitivity.NONE) + @get:InputFile + abstract val inputJar: RegularFileProperty + + @get:Input + abstract val decompilerArgs: ListProperty + + @get:CompileClasspath + abstract val minecraftClasspath: ConfigurableFileCollection + + @get:Classpath + abstract val decompiler: ConfigurableFileCollection + + @get:OutputFile + abstract val outputJar: RegularFileProperty + + @get:Inject + abstract val exec: ExecOperations + + @get:Inject + abstract val layout: ProjectLayout + + @TaskAction + fun run() { + val out = outputJar.convertToPath().ensureClean() + + val cfgFile = layout.buildDirectory.file(DECOMP_CFG).convertToPath().ensureClean() + val cfgText = buildString { + for (file in minecraftClasspath.files) { + append("-e=") + append(file.toPath().absolutePathString()) + append(System.lineSeparator()) + } + } + cfgFile.writeText(cfgText) + + val logs = out.resolveSibling("${out.name}.log") + + logs.outputStream().buffered().use { log -> + exec.javaexec { + classpath(decompiler) + mainClass.set("org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler") + + maxHeapSize = "3G" + + args(decompilerArgs.get()) + args("-cfg", cfgFile.absolutePathString()) + + args(inputJar.convertToPath().absolutePathString()) + args(out.absolutePathString()) + + standardOutput = log + errorOutput = log + } + } + + out.openZip().use { root -> + root.getPath("META-INF", "MANIFEST.MF").deleteIfExists() + } + } +} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/mache/RemapJar.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/mache/RemapJar.kt new file mode 100644 index 00000000..6b8f89f5 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/mache/RemapJar.kt @@ -0,0 +1,106 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.tasks.mache + +import io.papermc.paperweight.util.* +import javax.inject.Inject +import kotlin.io.path.* +import org.gradle.api.DefaultTask +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.ProjectLayout +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.tasks.* +import org.gradle.process.ExecOperations + +@CacheableTask +abstract class RemapJar : DefaultTask() { + + @get:PathSensitive(PathSensitivity.NONE) + @get:InputFile + abstract val serverJar: RegularFileProperty + + @get:PathSensitive(PathSensitivity.NONE) + @get:InputFile + abstract val serverMappings: RegularFileProperty + + @get:Input + abstract val remapperArgs: ListProperty + + @get:Classpath + abstract val codebookClasspath: ConfigurableFileCollection + + @get:CompileClasspath + abstract val minecraftClasspath: ConfigurableFileCollection + + @get:Classpath + abstract val remapperClasspath: ConfigurableFileCollection + + @get:PathSensitive(PathSensitivity.NONE) + @get:InputFiles + abstract val paramMappings: ConfigurableFileCollection + + @get:Classpath + abstract val constants: ConfigurableFileCollection + + @get:OutputFile + abstract val outputJar: RegularFileProperty + + @get:Inject + abstract val exec: ExecOperations + + @get:Inject + abstract val layout: ProjectLayout + + @TaskAction + fun run() { + val out = outputJar.convertToPath().ensureClean() + + val logFile = out.resolveSibling("${out.name}.log") + + logFile.outputStream().buffered().use { log -> + exec.javaexec { + classpath(codebookClasspath.singleFile) + + maxHeapSize = "2G" + + remapperArgs.get().forEach { arg -> + args( + arg + .replace(Regex("\\{tempDir}")) { layout.buildDirectory.dir(".tmp_codebook").get().asFile.absolutePath } + .replace(Regex("\\{remapperFile}")) { remapperClasspath.singleFile.absolutePath } + .replace(Regex("\\{mappingsFile}")) { serverMappings.get().asFile.absolutePath } + .replace(Regex("\\{paramsFile}")) { paramMappings.singleFile.absolutePath } + .replace(Regex("\\{constantsFile}")) { constants.singleFile.absolutePath } + .replace(Regex("\\{output}")) { outputJar.get().asFile.absolutePath } + .replace(Regex("\\{input}")) { serverJar.get().asFile.absolutePath } + .replace(Regex("\\{inputClasspath}")) { minecraftClasspath.files.joinToString(":") { it.absolutePath } } + ) + } + + standardOutput = log + errorOutput = log + } + } + } +} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/mache/SetupVanilla.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/mache/SetupVanilla.kt new file mode 100644 index 00000000..e2be9d84 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/mache/SetupVanilla.kt @@ -0,0 +1,220 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.tasks.mache + +import io.papermc.paperweight.tasks.* +import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.patches.* +import io.papermc.restamp.Restamp +import io.papermc.restamp.RestampContextConfiguration +import io.papermc.restamp.RestampInput +import java.nio.file.Path +import java.util.function.Predicate +import javax.inject.Inject +import kotlin.io.path.* +import org.cadixdev.at.io.AccessTransformFormats +import org.eclipse.jgit.api.Git +import org.eclipse.jgit.api.ResetCommand +import org.eclipse.jgit.lib.PersonIdent +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* +import org.gradle.process.ExecOperations +import org.openrewrite.InMemoryExecutionContext + +abstract class SetupVanilla : BaseTask() { + + @get:PathSensitive(PathSensitivity.NONE) + @get:InputFile + abstract val inputFile: RegularFileProperty + + @get:Internal + abstract val predicate: Property> + + @get:OutputDirectory + abstract val outputDir: DirectoryProperty + + @get:Internal + abstract val machePatches: DirectoryProperty + + @get:Optional + @get:InputFile + @get:PathSensitive(PathSensitivity.NONE) + abstract val ats: RegularFileProperty + + @get:Optional + @get:InputFiles + abstract val libraries: ConfigurableFileCollection + + @get:Optional + @get:InputFiles + abstract val paperPatches: ConfigurableFileCollection + + @get:Optional + @get:InputFile + abstract val devImports: RegularFileProperty + + @get:Optional + @get:CompileClasspath + abstract val minecraftClasspath: ConfigurableFileCollection + + @get:Optional + @get:Classpath + abstract val mache: ConfigurableFileCollection + + @get:Internal + abstract val useNativeDiff: Property + + @get:Internal + abstract val patchExecutable: Property + + @get:Inject + abstract val exec: ExecOperations + + init { + run { + useNativeDiff.convention(false) + patchExecutable.convention("patch") + } + } + + @TaskAction + fun run() { + val outputPath = outputDir.convertToPath() + + val git: Git + if (outputPath.resolve(".git/HEAD").isRegularFile()) { + git = Git.open(outputPath.toFile()) + git.reset().setRef("ROOT").setMode(ResetCommand.ResetType.HARD).call() + } else { + outputPath.createDirectories() + + git = Git.init() + .setDirectory(outputPath.toFile()) + .setInitialBranch("main") + .call() + + val rootIdent = PersonIdent("ROOT", "root@automated.papermc.io") + git.commit().setMessage("ROOT").setAllowEmpty(true).setAuthor(rootIdent).setSign(false).call() + git.tagDelete().setTags("ROOT").call() + git.tag().setName("ROOT").setTagger(rootIdent).setSigned(false).call() + } + + println("Copy initial sources...") + inputFile.convertToPath().openZip().walk() + .filter(predicate.get()) + .forEach { + val target = outputPath.resolve(it.toString().substring(1)) + target.parent.createDirectories() + // make sure we have a trailing newline + var content = it.readText() + if (!content.endsWith("\n")) { + content += "\n" + } + target.writeText(content) + } + + println("Setup git repo...") + commitAndTag(git, "Vanilla") + + if (machePatches.isPresent) { + // prepare for patches for patching + val patchesFolder = machePatches.convertToPath().ensureClean() + + mache.singleFile.toPath().openZip().use { zip -> + zip.getPath("patches").copyRecursivelyTo(patchesFolder) + } + + println("Applying mache patches...") + val result = createPatcher().applyPatches(outputPath, machePatches.convertToPath(), outputPath, outputPath) + + commitAndTag(git, "Mache") + + if (result is PatchFailure) { + result.failures + .map { "Patch failed: ${it.patch.relativeTo(machePatches.get().path)}: ${it.details}" } + .forEach { logger.error(it) } + git.close() + throw Exception("Failed to apply ${result.failures.size}/${result.patches.size} patches") + } + } + + if (ats.isPresent) { + val classPath = minecraftClasspath.files.map { it.toPath() }.toMutableList() + classPath.add(outputPath) + + println("Applying access transformers...") + val configuration = RestampContextConfiguration.builder() + .accessTransformers(ats.convertToPath(), AccessTransformFormats.FML) + .sourceRoot(outputPath) + .sourceFilesFromAccessTransformers(false) + .classpath(classPath) + .executionContext(InMemoryExecutionContext { it.printStackTrace() }) + .failWithNotApplicableAccessTransformers() + .build() + + val parsedInput = RestampInput.parseFrom(configuration) + val results = Restamp.run(parsedInput).allResults + + results.forEach { result -> + if (result.after != null) { + outputPath.resolve(result.after!!.sourcePath).writeText(result.after!!.printAll()) + } + } + + commitAndTag(git, "ATs") + } + + if (!libraries.isEmpty && !paperPatches.isEmpty) { + val patches = paperPatches.files.flatMap { it.toPath().walk().filter { path -> path.toString().endsWith(".patch") }.toList() } + McDev.importMcDev(patches, null, devImports.convertToPath(), outputPath, null, libraries.files.map { it.toPath() }, true, "") + + commitAndTag(git, "Imports") + } + + git.close() + } + + private fun commitAndTag(git: Git, name: String) { + val vanillaIdent = PersonIdent(name, "${name.lowercase()}@automated.papermc.io") + + git.add().addFilepattern(".").call() + git.commit() + .setMessage(name) + .setAuthor(vanillaIdent) + .setSign(false) + .call() + git.tagDelete().setTags(name).call() + git.tag().setName(name).setTagger(vanillaIdent).setSigned(false).call() + } + + internal open fun createPatcher(): Patcher { + return if (useNativeDiff.get()) { + NativePatcher(exec, patchExecutable.get()) + } else { + JavaPatcher() + } + } +} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/ApplyFeaturePatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/ApplyFeaturePatches.kt new file mode 100644 index 00000000..5b7d120e --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/ApplyFeaturePatches.kt @@ -0,0 +1,77 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.tasks.softspoon + +import io.papermc.paperweight.tasks.* +import io.papermc.paperweight.util.* +import javax.inject.Inject +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.ProviderFactory +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.UntrackedTask + +@UntrackedTask(because = "Always apply patches") +abstract class ApplyFeaturePatches : ControllableOutputTask() { + + @get:PathSensitive(PathSensitivity.NONE) + @get:InputDirectory + abstract val repo: DirectoryProperty + + @get:PathSensitive(PathSensitivity.NONE) + @get:InputDirectory + abstract val patches: DirectoryProperty + + @get:Inject + abstract val providers: ProviderFactory + + @get:Input + abstract val verbose: Property + + override fun init() { + printOutput.convention(true).finalizeValueOnRead() + verbose.convention(true) + // verbose.convention(providers.verboseApplyPatches()) // TODO + } + + @TaskAction + fun run() { + Git.checkForGit() + + val repoPath = repo.convertToPath() + + val git = Git(repoPath) + + if (git("checkout", "main").runSilently(silenceErr = true) != 0) { + git("checkout", "-b", "main").runSilently(silenceErr = true) + } + git("reset", "--hard", "file").executeSilently(silenceErr = true) + git("gc").runSilently(silenceErr = true) + + applyGitPatches(git, "server repo", repoPath, patches.convertToPath(), printOutput.get(), verbose.get()) + } +} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/ApplyFilePatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/ApplyFilePatches.kt new file mode 100644 index 00000000..0f7031f2 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/ApplyFilePatches.kt @@ -0,0 +1,128 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.tasks.softspoon + +import io.papermc.paperweight.tasks.* +import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.patches.* +import java.nio.file.Path +import javax.inject.Inject +import kotlin.io.path.* +import org.eclipse.jgit.api.Git +import org.eclipse.jgit.lib.PersonIdent +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.UntrackedTask +import org.gradle.process.ExecOperations + +@UntrackedTask(because = "Always apply patches") +abstract class ApplyFilePatches : BaseTask() { + + @get:PathSensitive(PathSensitivity.NONE) + @get:InputDirectory + abstract val input: DirectoryProperty + + @get:OutputDirectory + abstract val output: DirectoryProperty + + @get:PathSensitive(PathSensitivity.NONE) + @get:InputDirectory + abstract val patches: DirectoryProperty + + @get:Internal + abstract val useNativeDiff: Property + + @get:Internal + abstract val patchExecutable: Property + + @get:Inject + abstract val exec: ExecOperations + + override fun init() { + useNativeDiff.convention(false) + patchExecutable.convention("patch") + } + + @TaskAction + open fun run() { + setup() + + val result = createPatcher().applyPatches(output.convertToPath(), patches.convertToPath(), output.convertToPath(), output.convertToPath()) + + commit() + + if (result is PatchFailure) { + result.failures + .map { "Patch failed: ${it.patch.relativeTo(patches.get().path)}: ${it.details}" } + .forEach { logger.error(it) } + throw Exception("Failed to apply ${result.failures.size}/${result.patches.size} patches") + } + } + + open fun setup() { + io.papermc.paperweight.util.Git.checkForGit() + + val outputPath = output.convertToPath() + recreateCloneDirectory(outputPath) + + Git(outputPath).let { git -> + checkoutRepoFromUpstream(git, input.convertToPath(), "main", "mache", "main") + } + + setupGitHook(outputPath) + } + + private fun setupGitHook(outputPath: Path) { + val hook = outputPath.resolve(".git/hooks/post-rewrite") + hook.parent.createDirectories() + hook.writeText(javaClass.getResource("/post-rewrite.sh")!!.readText()) + } + + open fun commit() { + val ident = PersonIdent("File", "filepatches@automated.papermc.io") + val git = Git.open(output.convertToPath().toFile()) + git.add().addFilepattern(".").call() + git.commit() + .setMessage("File Patches") + .setAuthor(ident) + .setSign(false) + .call() + git.tagDelete().setTags("file").call() + git.tag().setName("file").setTagger(ident).setSigned(false).call() + git.close() + } + + internal open fun createPatcher(): Patcher { + return if (useNativeDiff.get()) { + NativePatcher(exec, patchExecutable.get()) + } else { + JavaPatcher() + } + } +} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/ApplyFilePatchesFuzzy.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/ApplyFilePatchesFuzzy.kt new file mode 100644 index 00000000..316e2d65 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/ApplyFilePatchesFuzzy.kt @@ -0,0 +1,50 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.tasks.softspoon + +import io.papermc.paperweight.util.patches.* +import io.papermc.paperweight.util.patches.Patcher +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.UntrackedTask +import org.gradle.api.tasks.options.Option + +@UntrackedTask(because = "Always apply patches") +abstract class ApplyFilePatchesFuzzy : ApplyFilePatches() { + + @get:Input + @get:Option( + option = "max-fuzz", + description = "Max fuzz. Cannot be set higher than context (3). Setting this " + + "value higher increases the chances of a faulty patch.", + ) + abstract val maxFuzz: Property + + override fun createPatcher(): Patcher { + return if (useNativeDiff.get()) { + NativePatcherFuzzy(exec, patchExecutable.get(), maxFuzz.get().toInt()) + } else { + JavaPatcherFuzzy(maxFuzz.get().toInt()) + } + } +} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/ApplySourceAT.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/ApplySourceAT.kt new file mode 100644 index 00000000..2ed1a546 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/ApplySourceAT.kt @@ -0,0 +1,135 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.tasks.softspoon + +import io.papermc.paperweight.tasks.* +import io.papermc.paperweight.util.* +import io.papermc.restamp.Restamp +import io.papermc.restamp.RestampContextConfiguration +import io.papermc.restamp.RestampInput +import java.nio.file.Files +import javax.inject.Inject +import kotlin.io.path.* +import org.cadixdev.at.io.AccessTransformFormats +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.* +import org.gradle.kotlin.dsl.* +import org.gradle.workers.WorkAction +import org.gradle.workers.WorkParameters +import org.gradle.workers.WorkerExecutor +import org.openrewrite.InMemoryExecutionContext + +@CacheableTask +abstract class ApplySourceAT : BaseTask() { + + @get:Classpath + abstract val inputJar: RegularFileProperty + + @get:OutputFile + abstract val outputJar: RegularFileProperty + + @get:Optional + @get:InputFile + @get:PathSensitive(PathSensitivity.NONE) + abstract val atFile: RegularFileProperty + + @get:Optional + @get:CompileClasspath + abstract val minecraftClasspath: ConfigurableFileCollection + + @get:Inject + abstract val worker: WorkerExecutor + + override fun init() { + outputJar.convention(defaultOutput()) + } + + @TaskAction + fun run() { + val queue = worker.processIsolation { + forkOptions { + maxHeapSize = "2G" + } + } + + val classPath = minecraftClasspath.files.map { it.toPath() }.toMutableList() + classPath.add(inputJar.convertToPath()) + + queue.submit(RestampWorker::class) { + minecraftClasspath.from(minecraftClasspath) + atFile.set(atFile) + inputJar.set(inputJar) + outputJar.set(outputJar) + } + } +} + +abstract class RestampWorker : WorkAction { + interface Params : WorkParameters { + val minecraftClasspath: ConfigurableFileCollection + val atFile: RegularFileProperty + val inputJar: RegularFileProperty + val outputJar: RegularFileProperty + } + + override fun execute() { + val inputZip = parameters.inputJar.convertToPath().openZip() + + val classPath = parameters.minecraftClasspath.files.map { it.toPath() }.toMutableList() + classPath.add(parameters.inputJar.convertToPath()) + + val configuration = RestampContextConfiguration.builder() + .accessTransformers(parameters.atFile.convertToPath(), AccessTransformFormats.FML) + .sourceRoot(inputZip.getPath("/")) + .sourceFilesFromAccessTransformers() + .classpath(classPath) + .executionContext(InMemoryExecutionContext { it.printStackTrace() }) + .failWithNotApplicableAccessTransformers() + .build() + + val parsedInput = RestampInput.parseFrom(configuration) + val results = Restamp.run(parsedInput).allResults + + parameters.outputJar.convertToPath().writeZip().use { zip -> + val alreadyWritten = mutableSetOf() + results.forEach { result -> + if (result.after == null) { + println("Ignoring ${result.before?.sourcePath} because result.after is null?") + return@forEach + } + result.after?.let { after -> + zip.getPath(after.sourcePath.toString()).writeText(after.printAll()) + alreadyWritten.add("/" + after.sourcePath.toString()) + } + } + + inputZip.walk().filter { Files.isRegularFile(it) }.filter { !alreadyWritten.contains(it.toString()) }.forEach { file -> + zip.getPath(file.toString()).writeText(file.readText()) + } + + zip.close() + } + inputZip.close() + } +} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/RebuildFilePatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/RebuildFilePatches.kt new file mode 100644 index 00000000..1b4596f4 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/softspoon/RebuildFilePatches.kt @@ -0,0 +1,204 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.tasks.softspoon + +import atFromString +import com.github.difflib.DiffUtils +import com.github.difflib.UnifiedDiffUtils +import io.papermc.paperweight.tasks.* +import io.papermc.paperweight.util.* +import io.papermc.restamp.Restamp +import io.papermc.restamp.RestampContextConfiguration +import io.papermc.restamp.RestampInput +import java.nio.file.Path +import kotlin.io.path.* +import org.cadixdev.at.AccessTransformSet +import org.cadixdev.at.io.AccessTransformFormats +import org.cadixdev.bombe.type.signature.MethodSignature +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* +import org.openrewrite.InMemoryExecutionContext +import writeLF + +@UntrackedTask(because = "Always rebuild patches") +abstract class RebuildFilePatches : BaseTask() { + + @get:InputDirectory + abstract val input: DirectoryProperty + + @get:InputDirectory + abstract val base: DirectoryProperty + + @get:OutputDirectory + abstract val patches: DirectoryProperty + + @get:Optional + @get:InputFile + abstract val atFile: RegularFileProperty + + @get:Optional + @get:OutputFile + abstract val atFileOut: RegularFileProperty + + @get:Optional + @get:CompileClasspath + abstract val minecraftClasspath: ConfigurableFileCollection + + @get:Input + abstract val contextLines: Property + + override fun init() { + contextLines.convention(3) + } + + @TaskAction + fun run() { + val patchDir = patches.convertToPath().ensureClean() + patchDir.createDirectory() + val inputDir = input.convertToPath() + val baseDir = base.convertToPath() + + val oldAts = if (atFile.isPresent) AccessTransformFormats.FML.read(atFile.convertToPath()) else AccessTransformSet.create() + + val git = Git(inputDir) + git("stash", "push").executeSilently(silenceErr = true) + git("checkout", "file").executeSilently(silenceErr = true) + + val patchesCreated = baseDir.walk() + .map { it.relativeTo(baseDir).toString().replace("\\", "/") } + .filter { + !it.startsWith(".git") && !it.endsWith(".nbt") && !it.endsWith(".mcassetsroot") + } + .sumOf { + diffFile(inputDir, baseDir, it, patchDir, oldAts) + } + + git("switch", "-").executeSilently(silenceErr = true) + git("stash", "pop").runSilently(silenceErr = true) + + logger.lifecycle("Rebuilt $patchesCreated patches") + } + + private fun diffFile(sourceRoot: Path, decompRoot: Path, relativePath: String, patchDir: Path, oldAts: AccessTransformSet): Int { + val source = sourceRoot.resolve(relativePath) + val decomp = decompRoot.resolve(relativePath) + + val className = relativePath.replace(".java", "") + + var sourceLines = source.readLines(Charsets.UTF_8) + var decompLines = decomp.readLines(Charsets.UTF_8) + + val ats = AccessTransformSet.create() + + sourceLines = handleATInSource(sourceLines, ats, className, source) + decompLines = handleATInBase(decompLines, ats, decompRoot, decomp) + + if (ats.classes.isNotEmpty()) { + oldAts.merge(ats) + AccessTransformFormats.FML.writeLF(atFileOut.convertToPath(), oldAts) + } + + val patch = DiffUtils.diff(decompLines, sourceLines) + if (patch.deltas.isEmpty()) { + return 0 + } + + val unifiedPatch = UnifiedDiffUtils.generateUnifiedDiff( + "a/$relativePath", + "b/$relativePath", + decompLines, + patch, + contextLines.get(), + ) + + val patchFile = patchDir.resolve("$relativePath.patch") + patchFile.parent.createDirectories() + patchFile.writeText(unifiedPatch.joinToString("\n", postfix = "\n"), Charsets.UTF_8) + + return 1 + } + + private fun handleATInBase(decompLines: List, newAts: AccessTransformSet, decompRoot: Path, decomp: Path): List { + if (newAts.classes.isEmpty()) { + return decompLines + } + + val configuration = RestampContextConfiguration.builder() + .accessTransformSet(newAts) + .sourceRoot(decompRoot) + .sourceFiles(listOf(decomp)) + .classpath(minecraftClasspath.files.map { it.toPath() }) + .executionContext(InMemoryExecutionContext { it.printStackTrace() }) + .build() + + val parsedInput = RestampInput.parseFrom(configuration) + val results = Restamp.run(parsedInput).allResults + + if (results.size != 1) { + throw Exception("Strange resultset?! " + results) + } + + val result = results[0].after?.printAll() + if (result != null) { + decomp.writeText(result, Charsets.UTF_8) + } + return decomp.readLines(Charsets.UTF_8) + } + + private fun handleATInSource(sourceLines: List, newAts: AccessTransformSet, className: String, source: Path): ArrayList { + val fixedLines = ArrayList(sourceLines.size) + var requiresWrite = false + sourceLines.forEach { line -> + if (!line.contains("// Paper-AT: ")) { + fixedLines.add(line) + return@forEach + } + + requiresWrite = true + + val split = line.split("// Paper-AT: ") + val at = split[1] + val atClass = newAts.getOrCreateClass(className) + val parts = at.split(" ") + val accessTransform = atFromString(parts[0]) + val name = parts[1] + val index = name.indexOf('(') + if (index == -1) { + atClass.mergeField(name, accessTransform) + } else { + atClass.mergeMethod(MethodSignature.of(name.substring(0, index), name.substring(index)), accessTransform) + } + + fixedLines.add(split[0]) + } + + if (requiresWrite) { + source.writeText(fixedLines.joinToString("\n", postfix = "\n"), Charsets.UTF_8) + } + + return fixedLines + } +} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/McDev.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/McDev.kt index b7f7db83..074eff0c 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/McDev.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/McDev.kt @@ -35,16 +35,17 @@ object McDev { fun importMcDev( patches: Iterable, - decompJar: Path, + decompJar: Path?, importsFile: Path?, targetDir: Path, dataTargetDir: Path? = null, librariesDirs: List = listOf(), - printOutput: Boolean = true + printOutput: Boolean = true, + javaSourceSet: String = "/src/main/java" ) { - val (javaPatchLines, dataPatchLines) = readPatchLines(patches) + val (javaPatchLines, dataPatchLines) = readPatchLines(patches, javaSourceSet) - decompJar.openZip().use { zipFile -> + decompJar?.openZip()?.use { zipFile -> val decompFiles = mutableSetOf() zipFile.walk().use { stream -> @@ -118,7 +119,7 @@ object McDev { } // Import library classes - val imports = findLibraries(importsFile, libFiles, javaPatchLines) + val imports = findLibraries(importsFile, libFiles, javaPatchLines, javaSourceSet) logger.log(if (printOutput) LogLevel.LIFECYCLE else LogLevel.DEBUG, "Importing {} classes from library sources...", imports.size) for ((libraryFileName, importFilePath) in imports) { @@ -161,11 +162,11 @@ object McDev { } } - private fun readPatchLines(patches: Iterable): Pair, Set> { + private fun readPatchLines(patches: Iterable, javaSourceSet: String): Pair, Set> { val srcResult = hashSetOf() val dataResult = hashSetOf() - val javaPrefix = "+++ b/src/main/java/" + val javaPrefix = "+++ b$javaSourceSet/" val dataPrefix = "+++ b/src/main/resources/data/minecraft/" for (patch in patches) { @@ -207,7 +208,7 @@ object McDev { return Pair(srcResult, dataResult) } - private fun findLibraries(libraryImports: Path?, libFiles: List, patchLines: Set): Set { + private fun findLibraries(libraryImports: Path?, libFiles: List, patchLines: Set, javaSourceSet: String): Set { val result = hashSetOf() // Imports from library-imports.txt @@ -224,15 +225,15 @@ object McDev { } // Scan patches for necessary imports - result += findNeededLibraryImports(patchLines, libFiles) + result += findNeededLibraryImports(patchLines, libFiles, javaSourceSet) return result } - private fun findNeededLibraryImports(patchLines: Set, libFiles: List): Set { + private fun findNeededLibraryImports(patchLines: Set, libFiles: List, javaSourceSet: String): Set { val knownImportMap = findPossibleLibraryImports(libFiles) .associateBy { it.importFilePath } - val prefix = "+++ b/src/main/java/" + val prefix = "+++ b$javaSourceSet/" return patchLines.map { it.substringAfter(prefix) } .mapNotNull { knownImportMap[it] } .toSet() diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/at.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/at.kt new file mode 100644 index 00000000..f7e50eef --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/at.kt @@ -0,0 +1,79 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +import java.io.BufferedWriter +import java.io.StringWriter +import java.nio.file.Path +import kotlin.io.path.* +import org.cadixdev.at.AccessChange +import org.cadixdev.at.AccessTransform +import org.cadixdev.at.AccessTransformSet +import org.cadixdev.at.ModifierChange +import org.cadixdev.at.io.AccessTransformFormat +import org.cadixdev.at.io.AccessTransformFormats + +fun atFromString(input: String): AccessTransform { + var last = input.length - 1 + + val final: ModifierChange + if (input[last] == 'f') { + final = if (input[--last] == '-') ModifierChange.REMOVE else ModifierChange.ADD + } else { + final = ModifierChange.NONE + } + + val access = when (input.substring(0, last)) { + "public" -> AccessChange.PUBLIC + "protected" -> AccessChange.PROTECTED + "private" -> AccessChange.PRIVATE + else -> AccessChange.NONE + } + + println("input = $input") + println(input.substring(0, last)) + println("access = $access, final = $final") + + return AccessTransform.of(access, final) +} + +fun atToString(at: AccessTransform): String { + val access = when (at.access) { + AccessChange.PRIVATE -> "private" + AccessChange.PROTECTED -> "protected" + AccessChange.PUBLIC -> "public" + else -> "" + } + val final = when (at.final) { + ModifierChange.REMOVE -> "-f" + ModifierChange.ADD -> "+f" + else -> "" + } + return access + final +} + +fun AccessTransformFormat.writeLF(path: Path, at: AccessTransformSet) { + val stringWriter = StringWriter() + val writer = BufferedWriter(stringWriter) + AccessTransformFormats.FML.write(writer, at) + writer.close() + path.writeText(stringWriter.toString().replace("\r\n", "\n"), Charsets.UTF_8) +} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/constants/constants.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/constants/constants.kt index 3e64dca4..34803155 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/constants/constants.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/constants/constants.kt @@ -43,6 +43,7 @@ const val PARAM_MAPPINGS_CONFIG = "paramMappings" const val REMAPPER_CONFIG = "remapper" const val DECOMPILER_CONFIG = "decompiler" const val PAPERCLIP_CONFIG = "paperclip" +const val MACHE_CONFIG = "mache" const val DEV_BUNDLE_CONFIG = "paperweightDevelopmentBundle" const val MOJANG_MAPPED_SERVER_CONFIG = "mojangMappedServer" const val MOJANG_MAPPED_SERVER_RUNTIME_CONFIG = "mojangMappedServerRuntime" @@ -53,6 +54,7 @@ const val SERVER_RUNTIME_CLASSPATH = "serverRuntimeClasspath" const val PARAM_MAPPINGS_REPO_NAME = "paperweightParamMappingsRepository" const val DECOMPILER_REPO_NAME = "paperweightDecompilerRepository" const val REMAPPER_REPO_NAME = "paperweightRemapperRepository" +const val MACHE_REPO_NAME = "paperweightMacheRepository" const val CACHE_PATH = "caches" private const val PAPER_PATH = "paperweight" @@ -76,6 +78,9 @@ const val MINECRAFT_SOURCES_PATH = "$JARS_PATH/minecraft-sources" const val SPIGOT_JARS_PATH = "$JARS_PATH/spigot" const val SPIGOT_SOURCES_JARS_PATH = "$JARS_PATH/spigot-sources" +const val PAPER_JARS_PATH = "$JARS_PATH/paper" +const val PAPER_SOURCES_JARS_PATH = "$JARS_PATH/paper-sources" + private const val MAPPINGS_DIR = "$PAPER_PATH/mappings" const val SERVER_MAPPINGS = "$MAPPINGS_DIR/server_mappings.txt" const val MOJANG_YARN_MAPPINGS = "$MAPPINGS_DIR/official-mojang+yarn.tiny" @@ -88,10 +93,12 @@ const val PATCHED_SPIGOT_MOJANG_YARN_MAPPINGS = "$MAPPINGS_DIR/spigot-mojang+yar const val PATCHED_SPIGOT_MOJANG_YARN_SOURCE_MAPPINGS = "$MAPPINGS_DIR/spigot-mojang+yarn-patched-source.tiny" const val REOBF_MOJANG_SPIGOT_MAPPINGS = "$MAPPINGS_DIR/mojang+yarn-spigot-reobf.tiny" const val PATCHED_REOBF_MOJANG_SPIGOT_MAPPINGS = "$MAPPINGS_DIR/mojang+yarn-spigot-reobf-patched.tiny" +const val SPIGOT_MOJANG_PARCHMENT_MAPPINGS = "$MAPPINGS_DIR/spigot-mojang+parchment.tiny" const val OBF_NAMESPACE = "official" const val SPIGOT_NAMESPACE = "spigot" const val DEOBF_NAMESPACE = "mojang+yarn" +const val NEW_DEOBF_NAMESPACE = "mojang+parchment" private const val DATA_PATH = "$PAPER_PATH/data" const val MC_MANIFEST = "$DATA_PATH/McManifest.json" @@ -102,13 +109,17 @@ const val SERVER_VERSION_JSON = "$BUNDLER_PATH/version.json" const val SERVER_LIBRARIES_TXT = "$BUNDLER_PATH/ServerLibraries.txt" const val SERVER_LIBRARIES_LIST = "$BUNDLER_PATH/libraries.list" const val SERVER_VERSIONS_LIST = "$BUNDLER_PATH/versions.list" +const val SERVER_JAR = "$BUNDLER_PATH/server.jar" private const val SETUP_CACHE = "$PAPER_PATH/setupCache" private const val TASK_CACHE = "$PAPER_PATH/taskCache" const val FINAL_REMAPPED_JAR = "$TASK_CACHE/minecraft.jar" +const val FINAL_REMAPPED_CODEBOOK_JAR = "$TASK_CACHE/codebook-minecraft.jar" const val FINAL_FILTERED_REMAPPED_JAR = "$TASK_CACHE/filteredMinecraft.jar" const val FINAL_DECOMPILE_JAR = "$TASK_CACHE/decompileJar.jar" +const val SPIGOT_MACHE_DECOMPILE_JAR = "$TASK_CACHE/macheSpigotDecompileJar.jar" +const val DECOMP_CFG = "$TASK_CACHE/decomp_cfg.txt" const val MC_DEV_SOURCES_DIR = "$PAPER_PATH/mc-dev-sources" @@ -118,6 +129,13 @@ const val RELOCATION_EXTENSION = "relocation" const val DOWNLOAD_SERVICE_NAME = "paperweightDownloadService" +private const val MACHE_PATH = "$PAPER_PATH/mache" +const val PATCHED_JAR = "$MACHE_PATH/patched.jar" +const val FAILED_PATCH_JAR = "$MACHE_PATH/failed.jar" +const val PATCHES_FOLDER = "$MACHE_PATH/patches" +const val BASE_PROJECT = "$MACHE_PATH/base" +const val REMAPPED_CB = "$MACHE_PATH/remapped-cb" + fun paperSetupOutput(name: String, ext: String) = "$SETUP_CACHE/$name.$ext" fun Task.paperTaskOutput(ext: String) = paperTaskOutput(name, ext) fun paperTaskOutput(name: String, ext: String) = "$TASK_CACHE/$name.$ext" diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheAdditionalDependencies.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheAdditionalDependencies.kt new file mode 100644 index 00000000..417d99c1 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheAdditionalDependencies.kt @@ -0,0 +1,28 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.util.data.mache + +data class MacheAdditionalDependencies( + val compileOnly: List? = null, + val implementation: List? = null, +) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheDependencies.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheDependencies.kt new file mode 100644 index 00000000..630003ed --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheDependencies.kt @@ -0,0 +1,31 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.util.data.mache + +data class MacheDependencies( + val codebook: List, + val paramMappings: List, + val constants: List, + val remapper: List, + val decompiler: List, +) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheMeta.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheMeta.kt new file mode 100644 index 00000000..e594ed51 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheMeta.kt @@ -0,0 +1,33 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.util.data.mache + +data class MacheMeta( + val minecraftVersion: String, + val macheVersion: String, + val dependencies: MacheDependencies, + val repositories: List, + val decompilerArgs: List, + val remapperArgs: List, + val additionalCompileDependencies: MacheAdditionalDependencies? = null, +) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheRepository.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheRepository.kt new file mode 100644 index 00000000..b2f8b478 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheRepository.kt @@ -0,0 +1,29 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.util.data.mache + +data class MacheRepository( + val url: String, + val name: String, + val groups: List? = null, +) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MavenArtifact.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MavenArtifact.kt new file mode 100644 index 00000000..33c4d35c --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MavenArtifact.kt @@ -0,0 +1,35 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.util.data.mache + +data class MavenArtifact( + val group: String, + val name: String, + val version: String, + val classifier: String? = null, + val extension: String? = null, +) { + fun toMavenString(): String { + return "$group:$name:$version" + (classifier?.let { ":$it" } ?: "") + (extension?.let { "@$it" } ?: "") + } +} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/file.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/file.kt index cc252dea..094996b1 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/file.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/file.kt @@ -26,6 +26,7 @@ import io.papermc.paperweight.PaperweightException import java.io.InputStream import java.net.URI import java.nio.file.FileSystem +import java.nio.file.FileSystemNotFoundException import java.nio.file.FileSystems import java.nio.file.Files import java.nio.file.Path @@ -35,6 +36,9 @@ import java.util.Arrays import java.util.stream.Collectors import java.util.stream.Stream import java.util.stream.StreamSupport +import java.util.zip.ZipEntry +import java.util.zip.ZipInputStream +import java.util.zip.ZipOutputStream import kotlin.io.path.* import kotlin.streams.asSequence import org.gradle.api.Project @@ -142,13 +146,41 @@ private fun Path.jarUri(): URI { } fun Path.openZip(): FileSystem { - return FileSystems.newFileSystem(jarUri(), emptyMap()) + return try { + FileSystems.getFileSystem(jarUri()) + } catch (e: FileSystemNotFoundException) { + FileSystems.newFileSystem(jarUri(), emptyMap()) + } } fun Path.writeZip(): FileSystem { return FileSystems.newFileSystem(jarUri(), mapOf("create" to "true")) } +inline fun Path.writeZipStream(func: (ZipOutputStream) -> Unit) { + ZipOutputStream(this.outputStream().buffered()).use(func) +} + +inline fun Path.readZipStream(func: (ZipInputStream, ZipEntry) -> Unit) { + ZipInputStream(this.inputStream().buffered()).use { zis -> + var entry = zis.nextEntry + while (entry != null) { + func(zis, entry) + entry = zis.nextEntry + } + } +} + +fun copyEntry(input: InputStream, output: ZipOutputStream, entry: ZipEntry) { + val newEntry = ZipEntry(entry) + output.putNextEntry(newEntry) + try { + input.copyTo(output) + } finally { + output.closeEntry() + } +} + fun FileSystem.walk(): Stream { return StreamSupport.stream(rootDirectories.spliterator(), false) .flatMap { Files.walk(it) } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt index 71c9beee..4d276271 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt @@ -102,6 +102,17 @@ class Git(private val repo: Path, private val env: Map = emptyMa throw PaperweightException("You must have git installed and available on your PATH in order to use paperweight.") } + + fun checkForGitRepo(directory: Path): Boolean { + try { + val proc = ProcessBuilder("git", "status").redirectErrorStream(true).directory(directory).start() + proc.inputStream.copyTo(UselessOutputStream) + if (proc.waitFor() == 0) { + return true + } + } catch (_: Exception) {} + return false + } } } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/JavaPatcher.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/JavaPatcher.kt new file mode 100644 index 00000000..b2b42193 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/JavaPatcher.kt @@ -0,0 +1,107 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.util.patches + +import com.github.difflib.DiffUtils +import com.github.difflib.UnifiedDiffUtils +import com.github.difflib.patch.Patch +import com.github.difflib.patch.PatchFailedException +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.StandardOpenOption +import kotlin.io.path.* +import kotlin.streams.asStream + +internal open class JavaPatcher : Patcher { + + override fun applyPatches(baseDir: Path, patchDir: Path, outputDir: Path, failedDir: Path): PatchResult { + var result = baseDir.walk() + .asStream() + .map { original -> + val relPath = original.relativeTo(baseDir) + val patchPath = relPath.resolveSibling("${relPath.name}.patch").toString() + val patch = patchDir.resolve(patchPath) + val patched = outputDir.resolve(relPath.toString()) + PatchTask(patch, original, patched) + } + .filter { Files.isRegularFile(it.patch) } + .parallel() + .map { applyPatch(it) } + .toList() + .fold<_, PatchResult>(PatchSuccess) { acc, value -> + acc.fold(value) + } + + result = patchDir.walk() + .filter { it.name.endsWith(".patch") } + .filterNot { result.patches.contains(it) } + .map { patch -> + // all patches here did not have matching files + // this results in a patch failure too + val relPath = patch.relativeTo(patchDir) + PatchFailure(patch, "No matching file found for patch: " + relPath.pathString) + } + .fold(result) { acc, value -> + acc.fold(value) + } + + return result + } + + internal data class PatchTask(val patch: Path, val original: Path, val patched: Path) + + internal open fun applyPatch(task: PatchTask): PatchResult { + val (patch, original, patched) = task + + patched.parent.createDirectories() + if (patch.notExists()) { + original.copyTo(patched) + return PatchSuccess + } + + val patchLines = patch.readLines(Charsets.UTF_8) + val parsedPatch = UnifiedDiffUtils.parseUnifiedDiff(patchLines) + val javaLines = original.readLines(Charsets.UTF_8) + + return try { + val patchedLines = applyPatch(parsedPatch, javaLines) + patched.writeText( + patchedLines.joinToString("\n", postfix = "\n"), + Charsets.UTF_8, + StandardOpenOption.CREATE, + StandardOpenOption.WRITE, + StandardOpenOption.TRUNCATE_EXISTING, + ) + PatchSuccess(patch) + } catch (e: PatchFailedException) { + // patch failed, so copy the file over without the patch applied + original.copyTo(patched, overwrite = true) + PatchFailure(patch, e.message ?: "unknown") + } + } + + @Throws(PatchFailedException::class) + internal open fun applyPatch(patch: Patch, lines: List): List { + return DiffUtils.patch(lines, patch) + } +} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/JavaPatcherFuzzy.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/JavaPatcherFuzzy.kt new file mode 100644 index 00000000..d43b410b --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/JavaPatcherFuzzy.kt @@ -0,0 +1,38 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.util.patches + +import com.github.difflib.patch.Patch + +internal class JavaPatcherFuzzy(private val maxFuzz: Int) : JavaPatcher() { + + init { + if (maxFuzz < 0) { + throw IllegalArgumentException("max-fuzz argument must be a non-negative integer") + } + } + + override fun applyPatch(patch: Patch, lines: List): List { + return patch.applyFuzzy(lines, maxFuzz) + } +} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/NativePatcher.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/NativePatcher.kt new file mode 100644 index 00000000..3aba15f6 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/NativePatcher.kt @@ -0,0 +1,89 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.util.patches + +import java.io.ByteArrayOutputStream +import java.nio.charset.Charset +import java.nio.file.Path +import kotlin.io.path.absolutePathString +import kotlin.io.path.copyTo +import kotlin.io.path.createDirectories +import kotlin.io.path.exists +import kotlin.io.path.moveTo +import kotlin.io.path.nameWithoutExtension +import kotlin.io.path.relativeTo +import kotlin.io.path.walk +import org.gradle.process.ExecOperations + +internal open class NativePatcher(private val exec: ExecOperations, protected val patchExecutable: String) : Patcher { + + override fun applyPatches(baseDir: Path, patchDir: Path, outputDir: Path, failedDir: Path): PatchResult { + baseDir.walk() + .forEach { original -> + val relPath = original.relativeTo(baseDir) + val patched = outputDir.resolve(relPath.toString()) + patched.parent.createDirectories() + original.copyTo(patched) + } + + val result = patchDir.walk() + .fold<_, PatchResult>(PatchSuccess) { acc, value -> + acc.fold(patch(outputDir, failedDir, patchDir, value)) + } + + return result + } + + internal open fun commandLineArgs(patch: Path): List { + return listOf(patchExecutable, "-u", "-p1", "--merge=diff3", "-i", patch.absolutePathString()) + } + + private fun patch(out: Path, failed: Path, patchDir: Path, patch: Path): PatchResult { + val baos = ByteArrayOutputStream() + val res = exec.exec { + commandLine(commandLineArgs(patch)) + workingDir(out) + + standardOutput = baos + errorOutput = baos + + isIgnoreExitValue = true + } + + if (res.exitValue == 0) { + return PatchSuccess + } else { + val inputFile = patch.resolveSibling(patch.nameWithoutExtension) + val inputFilePath = inputFile.relativeTo(patchDir).toString() + val input = out.resolve(inputFilePath) + + if (input.exists()) { + val failedOutput = failed.resolve(inputFilePath) + failedOutput.parent.createDirectories() + input.moveTo(failedOutput) + } + } + + return PatchFailure(patch, baos.toString(Charset.defaultCharset())) + } +} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/NativePatcherFuzzy.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/NativePatcherFuzzy.kt new file mode 100644 index 00000000..53049cc8 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/NativePatcherFuzzy.kt @@ -0,0 +1,40 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.util.patches + +import java.nio.file.Path +import kotlin.io.path.absolutePathString +import org.gradle.process.ExecOperations + +internal class NativePatcherFuzzy(exec: ExecOperations, ex: String, private val maxFuzz: Int) : NativePatcher(exec, ex) { + + init { + if (maxFuzz < 0) { + throw IllegalArgumentException("max-fuzz argument must be a non-negative integer") + } + } + + override fun commandLineArgs(patch: Path): List { + return listOf(patchExecutable, "-u", "-p1", "--fuzz=$maxFuzz", "--merge=diff3", "-i", patch.absolutePathString()) + } +} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/Patcher.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/Patcher.kt new file mode 100644 index 00000000..00e5ae5c --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/patches/Patcher.kt @@ -0,0 +1,62 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.util.patches + +import java.nio.file.Path + +internal interface Patcher { + + fun applyPatches(baseDir: Path, patchDir: Path, outputDir: Path, failedDir: Path): PatchResult +} + +internal sealed interface PatchResult { + val patches: List + val failures: List + get() = emptyList() + + fun fold(next: PatchResult): PatchResult { + return when { + this is PatchSuccess && next is PatchSuccess -> PatchSuccess(this.patches + next.patches) + else -> PatchFailure(this.patches + next.patches, this.failures + next.failures) + } + } +} + +internal sealed interface PatchSuccess : PatchResult { + companion object : PatchSuccess { + + operator fun invoke(patches: List = emptyList()): PatchSuccess = PatchSuccessFile(patches) + operator fun invoke(patch: Path): PatchSuccess = PatchSuccessFile(listOf(patch)) + + override val patches: List + get() = emptyList() + } +} + +private data class PatchSuccessFile(override val patches: List) : PatchSuccess + +internal data class PatchFailure(override val patches: List, override val failures: List) : PatchResult { + constructor(patch: Path, details: String) : this(listOf(patch), listOf(PatchFailureDetails(patch, details))) +} + +internal data class PatchFailureDetails(val patch: Path, val details: String) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt index 8b131d59..67e2e199 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt @@ -70,6 +70,8 @@ import org.gradle.jvm.toolchain.JavaLauncher import org.gradle.jvm.toolchain.JavaToolchainService import org.gradle.kotlin.dsl.* +val whitespace = Regex("\\s+") + val gson: Gson = GsonBuilder().disableHtmlEscaping().setPrettyPrinting().registerTypeHierarchyAdapter(Path::class.java, PathJsonConverter()).create() class PathJsonConverter : JsonDeserializer, JsonSerializer { @@ -93,7 +95,9 @@ val ProjectLayout.cache: Path fun ProjectLayout.cacheDir(path: String) = projectDirectory.dir(".gradle/$CACHE_PATH").dir(path) fun ProjectLayout.initSubmodules() { Git.checkForGit() - Git(projectDirectory.path)("submodule", "update", "--init").executeOut() + if (Git.checkForGitRepo(projectDirectory.path)) { + Git(projectDirectory.path)("submodule", "update", "--init").executeOut() + } } fun Provider.fileExists(project: Project): Provider { @@ -185,6 +189,18 @@ fun Any.convertToPath(): Path { } } +fun Path.ensureClean(): Path { + try { + deleteRecursively() + } catch (e: Exception) { + println("Failed to delete $this: ${e.javaClass.name}: ${e.message}") + e.suppressedExceptions.forEach { println("Suppressed exception: $it") } + throw PaperweightException("Failed to delete $this", e) + } + parent.createDirectories() + return this +} + fun Any.convertToFileProvider(layout: ProjectLayout, providers: ProviderFactory): Provider { return when (this) { is Path -> layout.file(providers.provider { toFile() }) diff --git a/paperweight-lib/src/main/resources/post-rewrite.sh b/paperweight-lib/src/main/resources/post-rewrite.sh new file mode 100644 index 00000000..7da7be06 --- /dev/null +++ b/paperweight-lib/src/main/resources/post-rewrite.sh @@ -0,0 +1,14 @@ +#!/bin/sh +input=$(cat) +fileCommit=$(git rev-list -n 1 file) + +IFS=$'\n' +input=($input) +for line in $input; do + if [[ $line == $fileCommit* ]]; then + lastElement=${line##* } + git tag -d file + git tag file "$lastElement" --no-sign + echo "Updated tag file from $fileCommit to $lastElement" + fi +done diff --git a/paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/ApplyAccessTransformTest.kt b/paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/ApplyAccessTransformTest.kt new file mode 100644 index 00000000..b534103f --- /dev/null +++ b/paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/ApplyAccessTransformTest.kt @@ -0,0 +1,83 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.tasks + +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkObject +import java.nio.file.Path +import kotlin.test.BeforeTest +import kotlin.test.Test +import org.gradle.kotlin.dsl.* +import org.gradle.workers.WorkQueue +import org.gradle.workers.WorkerExecutor +import org.junit.jupiter.api.io.TempDir + +class ApplyAccessTransformTest : TaskTest() { + private lateinit var task: ApplyAccessTransform + + private val workerExecutor: WorkerExecutor = mockk() + private val workQueue: WorkQueue = mockk() + + @BeforeTest + fun setup() { + val project = setupProject() + project.apply(plugin = "java") + task = project.tasks.register("applyAccessTransform", ApplyAccessTransform::class).get() + mockkObject(task) + + every { task.workerExecutor } returns workerExecutor + every { workerExecutor.processIsolation(any()) } returns workQueue + every { workQueue.submit(ApplyAccessTransform.AtlasAction::class, any()) } answers { + val action = object : ApplyAccessTransform.AtlasAction() { + override fun getParameters(): ApplyAccessTransform.AtlasParameters { + return mockk().also { + every { it.inputJar.get() } returns task.inputJar.get() + every { it.atFile.get() } returns task.atFile.get() + every { it.outputJar.get() } returns task.outputJar.get() + } + } + } + action.execute() + } + } + + @Test + fun `should apply access transform`(@TempDir tempDir: Path) { + val testResource = Path.of("src/test/resources/apply_access_transform") + val testInput = testResource.resolve("input") + + val input = createJar(tempDir, testInput, "Test").toFile() + val output = tempDir.resolve("output.jar").toFile() + val atFile = testInput.resolve("ats.at").toFile() + + task.inputJar.set(input) + task.outputJar.set(output) + task.atFile.set(atFile) + + task.run() + + val testOutput = testResource.resolve("output") + compareJar(tempDir, testOutput, "output", "Test") + } +} diff --git a/paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/TaskTest.kt b/paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/TaskTest.kt new file mode 100644 index 00000000..1c36ba84 --- /dev/null +++ b/paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/TaskTest.kt @@ -0,0 +1,183 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.tasks + +import io.papermc.paperweight.util.* +import java.io.File +import java.nio.file.Files +import java.nio.file.Path +import kotlin.io.path.* +import org.eclipse.jgit.api.Git +import org.gradle.testfixtures.ProjectBuilder +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue + +open class TaskTest { + + fun setupProject() = ProjectBuilder.builder() + .withGradleUserHomeDir(File("build")) + .withProjectDir(File("")) + .build() + + fun setupDir(tempDir: Path, testResource: Path, name: String): Path { + val temp = tempDir.resolve(name).toFile() + temp.mkdir() + testResource.resolve(name).copyRecursivelyTo(temp.convertToPath()) + return temp.convertToPath() + } + + fun setupFile(tempDir: Path, testResource: Path, name: String): Path { + val temp = tempDir.resolve(name) + testResource.resolve(name).copyTo(temp) + return temp + } + + fun compareDir(tempDir: Path, testResource: Path, name: String) { + val actualOutput = tempDir.resolve(name) + val expectedOutput = testResource.resolve(name) + + val expectedFiles = expectedOutput.walk().filter { Files.isRegularFile(it) }.filter { !it.toString().contains(".git") }.toList() + val actualFiles = actualOutput.walk().filter { Files.isRegularFile(it) }.filter { !it.toString().contains(".git") }.toList() + + assertEquals(expectedFiles.size, actualFiles.size, "Expected $expectedFiles files, got $actualFiles") + + expectedFiles.forEach { expectedFile -> + val actualFile = actualOutput.resolve(expectedOutput.relativize(expectedFile)) + + compareFile(actualFile, expectedFile) + } + } + + fun compareZip(tempDir: Path, testResource: Path, name: String) { + val actualOutput = tempDir.resolve(name) + val expectedOutput = testResource.resolve(name) + + compareZip(actualOutput, expectedOutput) + } + + fun compareZip(actualOutput: Path, expectedOutput: Path) { + val actualZip = actualOutput.openZip() + val actualFiles = actualZip.walk().filter { Files.isRegularFile(it) }.toList() + val expectedZip = expectedOutput.openZip() + val expectedFiles = expectedZip.walk().filter { Files.isRegularFile(it) }.toList() + + assertEquals(expectedFiles.size, actualFiles.size, "Expected $expectedFiles files, got $actualFiles") + + expectedFiles.forEach { expectedFile -> + val actualFile = actualZip.getPath(expectedFile.toString()) + + compareFile(actualFile, expectedFile) + } + } + + fun compareFile(tempDir: Path, testResource: Path, name: String) { + val actualOutput = tempDir.resolve(name) + val expectedOutput = testResource.resolve(name) + + compareFile(actualOutput, expectedOutput) + } + + private fun compareFile(actual: Path, expected: Path) { + assertTrue(actual.exists(), "Expected file $actual doesn't exist") + assertEquals(expected.readText(), actual.readText(), "File $actual doesn't match expected") + } + + fun setupGitRepo(directory: File, mainBranch: String) { + var git = Git.init().setDirectory(directory).setInitialBranch(mainBranch).call() + + git.add().addFilepattern(".").call() + git.commit().setMessage("Test").call() + + git.close() + } + + fun createZip(tempDir: Path, testResource: Path, zipName: String, vararg fileNames: String,): Path { + val targetZip = tempDir.resolve(zipName) + + targetZip.writeZip().use { zip -> + fileNames.forEach { fileName -> + val sourceFile = testResource.resolve(fileName) + zip.getPath(fileName).writeText(sourceFile.readText()) + } + } + + return targetZip + } + + fun createJar(tempDir: Path, testResource: Path, name: String): Path { + val sourceFile = tempDir.resolve("$name.java") + testResource.resolve("$name.java").copyTo(sourceFile) + + // run javac on the file + ProcessBuilder() + .directory(tempDir.toFile()) + .command("javac", sourceFile.toString()) + .redirectErrorStream(true) + .start() + .waitFor() + + // create jar + ProcessBuilder() + .directory(tempDir.toFile()) + .command("jar", "-cf", "$name.jar", "$name.class") + .redirectErrorStream(true) + .start() + .waitFor() + + return tempDir.resolve("$name.jar") + } + + fun compareJar(tempDir: Path, testResource: Path, fileName: String, className: String) { + val outputJar = tempDir.resolve("$fileName.jar") + val expectedOutputFile = testResource.resolve("$fileName.javap") + + // unpack jar + ProcessBuilder() + .directory(tempDir.toFile()) + .command("jar", "-xf", outputJar.toString()) + .redirectErrorStream(true) + .start() + .waitFor() + + // disassemble class + val process = ProcessBuilder() + .directory(tempDir.toFile()) + .command("javap", "-p", "-c", "$className.class") + .redirectErrorStream(true) + .start() + + var actualOutput = process.inputStream.bufferedReader().readText() + val expectedOutput = expectedOutputFile.readText() + + // cleanup output + val lines = actualOutput.split("\n") + if (lines[0].startsWith("Picked up JAVA_TOOL_OPTIONS")) { + actualOutput = actualOutput.replace(lines[0] + "\n", "") + } + actualOutput = actualOutput.replace("\r\n", "\n") + + process.waitFor() + + assertEquals(expectedOutput, actualOutput, "Output doesn't match expected") + } +} diff --git a/paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/softspoon/ApplyFilePatchesTest.kt b/paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/softspoon/ApplyFilePatchesTest.kt new file mode 100644 index 00000000..898c26b3 --- /dev/null +++ b/paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/softspoon/ApplyFilePatchesTest.kt @@ -0,0 +1,61 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.tasks.softspoon + +import io.papermc.paperweight.tasks.* +import java.nio.file.Path +import kotlin.test.BeforeTest +import kotlin.test.Test +import org.gradle.kotlin.dsl.* +import org.junit.jupiter.api.io.TempDir + +class ApplyFilePatchesTest : TaskTest() { + private lateinit var task: ApplyFilePatches + + @BeforeTest + fun setup() { + val project = setupProject() + task = project.tasks.register("applyPatches", ApplyFilePatches::class).get() + } + + @Test + fun `should apply patches`(@TempDir tempDir: Path) { + val testResource = Path.of("src/test/resources/apply_patches") + val testInput = testResource.resolve("input") + + val input = setupDir(tempDir, testInput, "base").toFile() + val output = tempDir.resolve("source").toFile() + val patches = testInput.resolve("patches").toFile() + + setupGitRepo(input, "main") + + task.input.set(input) + task.output.set(output) + task.patches.set(patches) + + task.run() + + val testOutput = testResource.resolve("output") + compareDir(tempDir, testOutput, "source") + } +} diff --git a/paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/softspoon/ApplySourceATTest.kt b/paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/softspoon/ApplySourceATTest.kt new file mode 100644 index 00000000..e5706ea0 --- /dev/null +++ b/paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/softspoon/ApplySourceATTest.kt @@ -0,0 +1,61 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.tasks.softspoon + +import io.papermc.paperweight.tasks.* +import io.papermc.paperweight.util.* +import java.nio.file.Path +import kotlin.test.BeforeTest +import kotlin.test.Test +import org.gradle.kotlin.dsl.* +import org.junit.jupiter.api.io.TempDir + +class ApplySourceATTest : TaskTest() { + private lateinit var task: ApplySourceAT + + @BeforeTest + fun setup() { + val project = setupProject() + task = project.tasks.register("applySourceAT", ApplySourceAT::class).get() + } + + @Test + fun `should apply source access transformers`(@TempDir tempDir: Path) { + val testResource = Path.of("src/test/resources/apply_source_at") + val testInput = testResource.resolve("input") + + val inputJar = createZip(tempDir, testInput, "Test.jar", "Test.java", "Unrelated.java") + val atFile = testInput.resolve("ats.at").toFile() + val outputJar = tempDir.resolve("output.jar") + + task.inputJar.set(inputJar.toFile()) + task.atFile.set(atFile) + task.outputJar.set(outputJar.toFile()) + + task.run() + + val testOutput = testResource.resolve("output") + val expectedJar = createZip(tempDir, testOutput, "expected.jar", "Test.java", "Unrelated.java") + compareZip(outputJar, expectedJar) + } +} diff --git a/paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/softspoon/RebuildFilePatchesTest.kt b/paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/softspoon/RebuildFilePatchesTest.kt new file mode 100644 index 00000000..ab9a9978 --- /dev/null +++ b/paperweight-lib/src/test/kotlin/io/papermc/paperweight/tasks/softspoon/RebuildFilePatchesTest.kt @@ -0,0 +1,66 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.tasks.softspoon + +import io.papermc.paperweight.tasks.* +import java.nio.file.Path +import kotlin.test.BeforeTest +import kotlin.test.Test +import org.gradle.kotlin.dsl.* +import org.junit.jupiter.api.io.TempDir + +class RebuildFilePatchesTest : TaskTest() { + private lateinit var task: RebuildFilePatches + + @BeforeTest + fun setup() { + val project = setupProject() + task = project.tasks.register("rebuildPatches", RebuildFilePatches::class).get() + } + + @Test + fun `should rebuild patches`(@TempDir tempDir: Path) { + val testResource = Path.of("src/test/resources/rebuild_patches") + val testInput = testResource.resolve("input") + + val source = setupDir(tempDir, testInput, "source").toFile() + val base = setupDir(tempDir, testInput, "base").toFile() + val patches = tempDir.resolve("patches").toFile() + val atFile = testInput.resolve("ats.at").toFile() + val atFileOut = tempDir.resolve("ats.at").toFile() + + task.input.set(source) + task.base.set(base) + task.patches.set(patches) + task.atFile.set(atFile) + task.atFileOut.set(atFileOut) + + task.run() + + val testOutput = testResource.resolve("output") + compareDir(tempDir, testOutput, "base") + compareDir(tempDir, testOutput, "source") + compareDir(tempDir, testOutput, "patches") + compareFile(tempDir, testOutput, "ats.at") + } +} diff --git a/paperweight-lib/src/test/resources/apply_access_transform/input/Test.java b/paperweight-lib/src/test/resources/apply_access_transform/input/Test.java new file mode 100644 index 00000000..b8cb9b8a --- /dev/null +++ b/paperweight-lib/src/test/resources/apply_access_transform/input/Test.java @@ -0,0 +1,13 @@ +public class Test { + + public int dum; + private final String test; + + public Test(String test) { + this.test = test; + } + + public String getTest() { + return test; + } +} diff --git a/paperweight-lib/src/test/resources/apply_access_transform/input/ats.at b/paperweight-lib/src/test/resources/apply_access_transform/input/ats.at new file mode 100644 index 00000000..f8e1bfac --- /dev/null +++ b/paperweight-lib/src/test/resources/apply_access_transform/input/ats.at @@ -0,0 +1,3 @@ +public-f Test test +public+f Test dum +private+f Test getTest()Ljava/lang/String; diff --git a/paperweight-lib/src/test/resources/apply_access_transform/output/output.javap b/paperweight-lib/src/test/resources/apply_access_transform/output/output.javap new file mode 100644 index 00000000..77a6ccfa --- /dev/null +++ b/paperweight-lib/src/test/resources/apply_access_transform/output/output.javap @@ -0,0 +1,21 @@ +Compiled from "Test.java" +public class Test { + public final int dum; + + public java.lang.String test; + + public Test(java.lang.String); + Code: + 0: aload_0 + 1: invokespecial #1 // Method java/lang/Object."":()V + 4: aload_0 + 5: aload_1 + 6: putfield #7 // Field test:Ljava/lang/String; + 9: return + + private final java.lang.String getTest(); + Code: + 0: aload_0 + 1: getfield #7 // Field test:Ljava/lang/String; + 4: areturn +} diff --git a/paperweight-lib/src/test/resources/apply_patches/input/base/Test.java b/paperweight-lib/src/test/resources/apply_patches/input/base/Test.java new file mode 100644 index 00000000..b8cb9b8a --- /dev/null +++ b/paperweight-lib/src/test/resources/apply_patches/input/base/Test.java @@ -0,0 +1,13 @@ +public class Test { + + public int dum; + private final String test; + + public Test(String test) { + this.test = test; + } + + public String getTest() { + return test; + } +} diff --git a/paperweight-lib/src/test/resources/apply_patches/input/patches/Test.java.patch b/paperweight-lib/src/test/resources/apply_patches/input/patches/Test.java.patch new file mode 100644 index 00000000..fa773359 --- /dev/null +++ b/paperweight-lib/src/test/resources/apply_patches/input/patches/Test.java.patch @@ -0,0 +1,10 @@ +--- a/Test.java ++++ b/Test.java +@@ -8,6 +8,6 @@ + } + + public String getTest() { +- return test; ++ return test + "Test"; // Test + } + } diff --git a/paperweight-lib/src/test/resources/apply_patches/output/source/Test.java b/paperweight-lib/src/test/resources/apply_patches/output/source/Test.java new file mode 100644 index 00000000..bb72c175 --- /dev/null +++ b/paperweight-lib/src/test/resources/apply_patches/output/source/Test.java @@ -0,0 +1,13 @@ +public class Test { + + public int dum; + private final String test; + + public Test(String test) { + this.test = test; + } + + public String getTest() { + return test + "Test"; // Test + } +} diff --git a/paperweight-lib/src/test/resources/apply_source_at/input/Test.java b/paperweight-lib/src/test/resources/apply_source_at/input/Test.java new file mode 100644 index 00000000..b8cb9b8a --- /dev/null +++ b/paperweight-lib/src/test/resources/apply_source_at/input/Test.java @@ -0,0 +1,13 @@ +public class Test { + + public int dum; + private final String test; + + public Test(String test) { + this.test = test; + } + + public String getTest() { + return test; + } +} diff --git a/paperweight-lib/src/test/resources/apply_source_at/input/Unrelated.java b/paperweight-lib/src/test/resources/apply_source_at/input/Unrelated.java new file mode 100644 index 00000000..c821a33f --- /dev/null +++ b/paperweight-lib/src/test/resources/apply_source_at/input/Unrelated.java @@ -0,0 +1,3 @@ +class Unrealted { + +} diff --git a/paperweight-lib/src/test/resources/apply_source_at/input/ats.at b/paperweight-lib/src/test/resources/apply_source_at/input/ats.at new file mode 100644 index 00000000..f8e1bfac --- /dev/null +++ b/paperweight-lib/src/test/resources/apply_source_at/input/ats.at @@ -0,0 +1,3 @@ +public-f Test test +public+f Test dum +private+f Test getTest()Ljava/lang/String; diff --git a/paperweight-lib/src/test/resources/apply_source_at/output/Test.java b/paperweight-lib/src/test/resources/apply_source_at/output/Test.java new file mode 100644 index 00000000..869744fb --- /dev/null +++ b/paperweight-lib/src/test/resources/apply_source_at/output/Test.java @@ -0,0 +1,13 @@ +public class Test { + + public final int dum; + public String test; + + public Test(String test) { + this.test = test; + } + + private final String getTest() { + return test; + } +} diff --git a/paperweight-lib/src/test/resources/apply_source_at/output/Unrelated.java b/paperweight-lib/src/test/resources/apply_source_at/output/Unrelated.java new file mode 100644 index 00000000..c821a33f --- /dev/null +++ b/paperweight-lib/src/test/resources/apply_source_at/output/Unrelated.java @@ -0,0 +1,3 @@ +class Unrealted { + +} diff --git a/paperweight-lib/src/test/resources/rebuild_patches/input/ats.at b/paperweight-lib/src/test/resources/rebuild_patches/input/ats.at new file mode 100644 index 00000000..0341796b --- /dev/null +++ b/paperweight-lib/src/test/resources/rebuild_patches/input/ats.at @@ -0,0 +1 @@ +public Test dum diff --git a/paperweight-lib/src/test/resources/rebuild_patches/input/base/Test.java b/paperweight-lib/src/test/resources/rebuild_patches/input/base/Test.java new file mode 100644 index 00000000..b8cb9b8a --- /dev/null +++ b/paperweight-lib/src/test/resources/rebuild_patches/input/base/Test.java @@ -0,0 +1,13 @@ +public class Test { + + public int dum; + private final String test; + + public Test(String test) { + this.test = test; + } + + public String getTest() { + return test; + } +} diff --git a/paperweight-lib/src/test/resources/rebuild_patches/input/source/Test.java b/paperweight-lib/src/test/resources/rebuild_patches/input/source/Test.java new file mode 100644 index 00000000..1e61ad8f --- /dev/null +++ b/paperweight-lib/src/test/resources/rebuild_patches/input/source/Test.java @@ -0,0 +1,13 @@ +public class Test { + + public int dum; + public String test;// Paper-AT: public-f test + + public Test(String test) { + this.test = test; + } + + private final String getTest() {// Paper-AT: private+f getTest()Ljava/lang/String; + return test + "Test"; // Test + } +} diff --git a/paperweight-lib/src/test/resources/rebuild_patches/output/ats.at b/paperweight-lib/src/test/resources/rebuild_patches/output/ats.at new file mode 100644 index 00000000..f4e5285d --- /dev/null +++ b/paperweight-lib/src/test/resources/rebuild_patches/output/ats.at @@ -0,0 +1,3 @@ +public-f Test test +public Test dum +private+f Test getTest()Ljava/lang/String; diff --git a/paperweight-lib/src/test/resources/rebuild_patches/output/base/Test.java b/paperweight-lib/src/test/resources/rebuild_patches/output/base/Test.java new file mode 100644 index 00000000..fd852a16 --- /dev/null +++ b/paperweight-lib/src/test/resources/rebuild_patches/output/base/Test.java @@ -0,0 +1,13 @@ +public class Test { + + public int dum; + public String test; + + public Test(String test) { + this.test = test; + } + + private final String getTest() { + return test; + } +} diff --git a/paperweight-lib/src/test/resources/rebuild_patches/output/patches/Test.java.patch b/paperweight-lib/src/test/resources/rebuild_patches/output/patches/Test.java.patch new file mode 100644 index 00000000..044ebe3a --- /dev/null +++ b/paperweight-lib/src/test/resources/rebuild_patches/output/patches/Test.java.patch @@ -0,0 +1,10 @@ +--- a/Test.java ++++ b/Test.java +@@ -8,6 +8,6 @@ + } + + private final String getTest() { +- return test; ++ return test + "Test"; // Test + } + } diff --git a/paperweight-lib/src/test/resources/rebuild_patches/output/source/Test.java b/paperweight-lib/src/test/resources/rebuild_patches/output/source/Test.java new file mode 100644 index 00000000..a2d35e43 --- /dev/null +++ b/paperweight-lib/src/test/resources/rebuild_patches/output/source/Test.java @@ -0,0 +1,13 @@ +public class Test { + + public int dum; + public String test; + + public Test(String test) { + this.test = test; + } + + private final String getTest() { + return test + "Test"; // Test + } +} diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt index 9d08b48d..19b7e0fb 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt @@ -75,7 +75,9 @@ abstract class PaperweightUser : Plugin { null } - target.gradle.sharedServices.registerIfAbsent(DOWNLOAD_SERVICE_NAME, DownloadService::class) {} + target.gradle.sharedServices.registerIfAbsent(DOWNLOAD_SERVICE_NAME, DownloadService::class) { + parameters.projectPath.set(target.projectDir) + } val cleanAll = target.tasks.register("cleanAllPaperweightUserdevCaches") { group = "paperweight" diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v2/SetupHandlerImplV2.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v2/SetupHandlerImplV2.kt index ef7727e4..ade6bbdd 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v2/SetupHandlerImplV2.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v2/SetupHandlerImplV2.kt @@ -70,12 +70,12 @@ class SetupHandlerImplV2( override val hashFile: Path = cache.resolve(paperSetupOutput("minecraftLibraries", "hashes")) override fun run(context: SetupHandler.Context) { - downloadMinecraftLibraries( + downloadLibraries( download = parameters.downloadService, workerExecutor = context.workerExecutor, targetDir = minecraftLibraryJars, repositories = listOf(MC_LIBRARY_URL, MAVEN_CENTRAL_URL), - mcLibraries = bundle.config.buildData.vanillaServerLibraries, + libraries = bundle.config.buildData.vanillaServerLibraries, sources = false ).await() }