diff --git a/.gitattributes b/.gitattributes index 1978225..2fb638f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,4 +5,3 @@ gradlew text eol=lf *.bat text eol=crlf *.jar binary - diff --git a/.gitignore b/.gitignore index 527d4fc..7e80851 100644 --- a/.gitignore +++ b/.gitignore @@ -1,25 +1,14 @@ -### Intellij ### +# IntelliJ .idea/ +out/ -# File-based project format +*.ipr *.iws *.iml -# IntelliJ -out/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -### Eclipse ### - +# Eclipse .metadata +.project bin/ tmp/ *.tmp @@ -30,143 +19,27 @@ local.properties .settings/ .loadpath .recommenders - -# External tool builders .externalToolBuilders/ - -# Locally stored "Eclipse launch configurations" *.launch - -# PyDev specific (Python IDE for Eclipse) -*.pydevproject - -# CDT-specific (C/C++ Development Tooling) -.cproject - -# CDT- autotools -.autotools - -# Java annotation processor (APT) .factorypath - -# PDT-specific (PHP Development Tools) -.buildpath - -# sbteclipse plugin -.target - -# Tern plugin -.tern-project - -# TeXlipse plugin -.texlipse - -# STS (Spring Tool Suite) -.springBeans - -# Code Recommenders -.recommenders/ - -# Annotation Processing .apt_generated/ -# Scala IDE specific (Scala & Java development for Eclipse) -.cache-main -.scala_dependencies -.worksheet - -### Eclipse Patch ### -# Eclipse Core -.project - -# JDT-specific (Eclipse Java Development Tools) -.classpath - -# Annotation Processing -.apt_generated - -.sts4-cache/ - -### Linux ### -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -### macOS ### -# General +# macOS .DS_Store -.AppleDouble -.LSOverride -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### Windows ### +# Windows # Windows thumbnail cache files Thumbs.db ehthumbs.db ehthumbs_vista.db - -# Dump file -*.stackdump - # Folder config file [Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts *.lnk -### Gradle ### +# Gradle .gradle /build/ - -# Ignore Gradle GUI config -gradle-app.setting - -# Cache of project .gradletasknamecache -# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 -# gradle/wrapper/gradle-wrapper.properties - # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) !gradle-wrapper.ja diff --git a/build.gradle.kts b/build.gradle.kts index 5b61afd..0382fe4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,6 @@ plugins { idea eclipse maven - kotlin("jvm") version "1.3.70" `kotlin-dsl` id("net.minecrell.licenser") version "0.4.1" id("com.github.johnrengelman.shadow") version "6.0.0" @@ -16,43 +15,36 @@ version = "1.0.0-SNAPSHOT" repositories { mavenLocal() mavenCentral() + jcenter() maven("https://oss.sonatype.org/content/repositories/snapshots/") maven("https://files.minecraftforge.net/maven/") } -val mcInjector: Configuration by configurations.creating - dependencies { - implementation(kotlin("stdlib-jdk8")) - compileOnly(gradleApi()) - compileOnly(gradleKotlinDsl()) + implementation("org.apache.httpcomponents:httpclient:4.5.12") + // Utils implementation("net.sf.opencsv:opencsv:2.3") implementation("com.github.salomonbrys.kotson:kotson:2.5.0") + // ASM for inspection + implementation("org.ow2.asm:asm:8.0.1") + // Cadix implementation("org.cadixdev:lorenz:0.5.4-SNAPSHOT") implementation("org.cadixdev:lorenz-asm:0.5.3") - implementation("org.cadixdev:mercury:0.1.0-SNAPSHOT") implementation("org.cadixdev:atlas:0.2.0") + implementation("org.cadixdev:at:0.1.0-SNAPSHOT") + + implementation("org.cadixdev:mercury:0.1.0-SNAPSHOT") } tasks.withType { kotlinOptions.jvmTarget = "1.8" -} - -val mcinjectorJar by tasks.registering(com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar::class) { - configurations = listOf(mcInjector) - archiveBaseName.set("mcinjector-shadowed") - archiveVersion.set("") - archiveClassifier.set("") - manifest { - attributes(mapOf("Main-Class" to "de.oceanlabs.mcp.mcinjector.MCInjector")) - } + kotlinOptions.freeCompilerArgs = listOf("-Xjvm-default=enable") } tasks.jar { - from(mcinjectorJar) archiveBaseName.set("io.papermc.paperweight.gradle.plugin") } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 28861d2..e708b1c 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 186b715..be52383 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cccdd3d..4f906e0 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -66,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -109,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -138,19 +156,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -159,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index f955316..107acd3 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/license/copyright.txt b/license/copyright.txt index db3a615..ccc3fbe 100644 --- a/license/copyright.txt +++ b/license/copyright.txt @@ -2,7 +2,6 @@ paperweight is a Gradle plugin for the PaperMC project. It uses some code and systems originally from ForgeGradle. Copyright (C) 2020 Kyle Wood -Copyright (C) 2018 Forge Development LLC This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/main/kotlin/Paperweight.kt b/src/main/kotlin/Paperweight.kt index 55dbb72..dc32563 100644 --- a/src/main/kotlin/Paperweight.kt +++ b/src/main/kotlin/Paperweight.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,149 +24,264 @@ package io.papermc.paperweight +import com.github.salomonbrys.kotson.array +import com.github.salomonbrys.kotson.fromJson +import com.github.salomonbrys.kotson.get +import com.github.salomonbrys.kotson.string +import com.google.gson.JsonObject import io.papermc.paperweight.ext.PaperweightExtension import io.papermc.paperweight.tasks.AddMissingSpigotClassMappings -import io.papermc.paperweight.tasks.ApplyAccessTransform import io.papermc.paperweight.tasks.ApplyDiffPatches import io.papermc.paperweight.tasks.ApplyGitPatches +import io.papermc.paperweight.tasks.ApplyMcpPatches +import io.papermc.paperweight.tasks.ApplyPaperPatches +import io.papermc.paperweight.tasks.ApplySourceAt import io.papermc.paperweight.tasks.DecompileVanillaJar +import io.papermc.paperweight.tasks.DownloadMcLibraries +import io.papermc.paperweight.tasks.DownloadMcpFiles +import io.papermc.paperweight.tasks.DownloadMcpTools import io.papermc.paperweight.tasks.DownloadServerJar -import io.papermc.paperweight.tasks.ExtractMcpData -import io.papermc.paperweight.tasks.ExtractMcpMappings +import io.papermc.paperweight.tasks.DownloadSpigotDependencies +import io.papermc.paperweight.tasks.DownloadTask +import io.papermc.paperweight.tasks.ExtractMappings +import io.papermc.paperweight.tasks.ExtractMcp +import io.papermc.paperweight.tasks.Filter import io.papermc.paperweight.tasks.FilterExcludes -import io.papermc.paperweight.tasks.GatherBuildData import io.papermc.paperweight.tasks.GenerateSpigotSrgs import io.papermc.paperweight.tasks.GenerateSrgs -import io.papermc.paperweight.tasks.GetRemoteJsons +import io.papermc.paperweight.tasks.InspectVanillaJar +import io.papermc.paperweight.tasks.Merge +import io.papermc.paperweight.tasks.MergeAccessTransforms import io.papermc.paperweight.tasks.PatchMcpCsv -import io.papermc.paperweight.tasks.RemapSources -import io.papermc.paperweight.tasks.RemapVanillaJarSrg +import io.papermc.paperweight.tasks.RemapAccessTransform +import io.papermc.paperweight.tasks.RemapSpigotAt +import io.papermc.paperweight.tasks.RemapVanillaJarSpigot import io.papermc.paperweight.tasks.RunForgeFlower import io.papermc.paperweight.tasks.RunMcInjector -import io.papermc.paperweight.tasks.SetupMcpDependencies -import io.papermc.paperweight.tasks.SetupSpigotDependencies +import io.papermc.paperweight.tasks.RunSpecialSource +import io.papermc.paperweight.tasks.SetupMcLibraries import io.papermc.paperweight.tasks.WriteLibrariesFile +import io.papermc.paperweight.tasks.patchremap.ApplyAccessTransform +import io.papermc.paperweight.tasks.patchremap.RemapPatches +import io.papermc.paperweight.tasks.sourceremap.RemapSources +import io.papermc.paperweight.util.BuildDataInfo import io.papermc.paperweight.util.Constants import io.papermc.paperweight.util.Git -import io.papermc.paperweight.tasks.RemapSrgSources -import io.papermc.paperweight.tasks.RemapVanillaJarSpigot +import io.papermc.paperweight.util.MinecraftManifest import io.papermc.paperweight.util.cache +import io.papermc.paperweight.util.contents import io.papermc.paperweight.util.ext +import io.papermc.paperweight.util.fromJson +import io.papermc.paperweight.util.gson +import io.papermc.paperweight.util.registering import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Delete import org.gradle.api.tasks.TaskProvider -import org.gradle.api.tasks.bundling.Zip import org.gradle.kotlin.dsl.maven import org.gradle.kotlin.dsl.register -import util.BuildDataInfo import java.io.File class Paperweight : Plugin { override fun apply(target: Project) { - target.extensions.create(Constants.EXTENSION, PaperweightExtension::class.java, target) + target.extensions.create(Constants.EXTENSION, PaperweightExtension::class.java, target.objects, target.layout) - createConfigurations(target) - setupMcpDeps(target) - - createTasks(target) - - target.tasks.register("cleanCache").configure { - destroyables.register(target.cache) - doLast { - target.delete(target.cache) - } + target.tasks.register("cleanCache") { + delete(target.layout.cache) } - } - private fun createConfigurations(project: Project) { - project.repositories.apply { + // Make sure the submodules are initialized + Git(target.projectDir)("submodule", "update", "--init").execute() + + target.repositories.apply { + mavenCentral() + // Both of these are needed for Spigot maven("https://oss.sonatype.org/content/repositories/snapshots/") maven("https://hub.spigotmc.org/nexus/content/groups/public/") - maven { - name = "forge" - url = project.uri(Constants.FORGE_MAVEN_URL) - metadataSources { - artifact() - } - } - mavenCentral() - maven { - name = "minecraft" - url = project.uri(Constants.MC_LIBRARY_URL) - } } - project.configurations.register(Constants.MCP_MAPPINGS_CONFIG) - project.configurations.register(Constants.MCP_DATA_CONFIG) - project.configurations.register(Constants.SPIGOT_DEP_CONFIG) - project.configurations.create(Constants.MINECRAFT_DEP_CONFIG) - project.configurations.register(Constants.FORGE_FLOWER_CONFIG) - project.configurations.create(Constants.MCINJECT_CONFIG) + target.createTasks() } - private fun setupMcpDeps(project: Project) { - project.dependencies.add(Constants.MCP_DATA_CONFIG, project.provider { - mapOf( - "group" to "de.oceanlabs.mcp", - "name" to "mcp_config", - "version" to "${project.ext.mcpMinecraftVersion.get()}-${project.ext.mcpVersion.get()}", - "ext" to "zip" - ) - }) + private fun Project.createTasks() { + val extension = ext - project.dependencies.add(Constants.MCP_MAPPINGS_CONFIG, project.provider { - mapOf( - "group" to "de.oceanlabs.mcp", - "name" to "mcp_${project.ext.mcpMappingsChannel.get()}", - "version" to project.ext.mcpMappingsVersion.get(), - "ext" to "zip" - ) - }) + val initialTasks = createInitialTasks() + val generalTasks = createGeneralTasks() + val mcpTasks = createMcpTasks(initialTasks, generalTasks) + val spigotTasks = createSpigotTasks(initialTasks, generalTasks, mcpTasks) + + createPatchRemapTasks(initialTasks, generalTasks, mcpTasks, spigotTasks) + + val applySourceAt by tasks.registering { + inputZip.set(mcpTasks.applyMcpPatches.flatMap { it.outputZip }) + vanillaJar.set(generalTasks.downloadServerJar.flatMap { it.outputJar }) + vanillaRemappedSrgJar.set(mcpTasks.remapVanillaJarSrg.flatMap { it.outputJar }) + atFile.set(spigotTasks.mergeGeneratedAts.flatMap { it.outputFile }) + } + + val mergeRemappedSources by tasks.registering { + inputJars.add(spigotTasks.remapSpigotSources.flatMap { it.outputZip }) + inputJars.add(applySourceAt.flatMap { it.outputZip }) + } + + val patchPaperApi by tasks.registering { + branch.set("HEAD") + upstreamBranch.set("upstream") + upstream.set(spigotTasks.patchSpigotApi.flatMap { it.outputDir }) + patchDir.set(extension.paper.spigotApiPatchDir) + printOutput.set(true) + + outputDir.set(extension.paper.paperApiDir) + } + + val patchPaperServer by tasks.registering { + patchDir.set(extension.paper.spigotServerPatchDir) + remappedSource.set(mergeRemappedSources.flatMap { it.outputJar }) + templateGitIgnore.set(layout.projectDirectory.file(".gitignore")) + + outputDir.set(extension.paper.paperServerDir) + } + + val patchPaper by tasks.registering { + dependsOn(patchPaperApi, patchPaperServer) + } + + /* + * Not bothering mapping away from SRG until everything is stable under SRG + * Moving off of SRG will make things a lot more fragile + val remapSrgSourcesSpigotVanilla by tasks.registering { + inputZips.add(ZipTarget.base(applyMcpPatches.flatMap { outputZip })) + methodsCsv.set(mcpRewrites.flatMap { methodsCsv }) + fieldsCsv.set(mcpRewrites.flatMap { fieldsCsv }) + paramsCsv.set(mcpRewrites.flatMap { paramsCsv }) + } + */ } - // Types are specified in this method to hopefully improve editor performance a little, though I don't think it helps - private fun createTasks(project: Project) { - val cache: File = project.cache - val extension: PaperweightExtension = project.ext + // Shared task containers + data class InitialTasks( + val setupMcLibraries: TaskProvider, + val extractMcp: Provider, + val mcpMappings: Provider, + val downloadMcpTools: TaskProvider + ) - val initGitSubmodules: TaskProvider = project.tasks.register("initGitSubmodules") { - outputs.upToDateWhen { false } - doLast { - Git(project.projectDir)("submodule", "update", "--init").execute() - } - } - val gatherBuildData: TaskProvider = project.tasks.register("gatherBuildData") { - dependsOn(initGitSubmodules) - buildDataInfoFile.set(extension.craftBukkit.buildDataInfo) - } - val buildDataInfo: Provider = gatherBuildData.flatMap { it.buildDataInfo } + data class GeneralTasks( + val buildDataInfo: Provider, + val downloadServerJar: TaskProvider, + val filterVanillaJar: TaskProvider + ) - val extractMcpData: TaskProvider = project.tasks.register("extractMcpData") { - config.set(Constants.MCP_DATA_CONFIG) + data class McpTasks( + val generateSrgs: TaskProvider, + val remapVanillaJarSrg: TaskProvider, + val applyMcpPatches: TaskProvider + ) + + data class SpigotTasks( + val generateSpigotSrgs: TaskProvider, + val decompileVanillaJarSpigot: TaskProvider, + val patchSpigotApi: TaskProvider, + val patchSpigotServer: TaskProvider, + val remapSpigotSources: TaskProvider, + val mergeGeneratedAts: TaskProvider + ) + + private fun Project.createInitialTasks(): InitialTasks { + val cache: File = layout.cache + val extension: PaperweightExtension = ext + + val downloadMcManifest by tasks.registering { + url.set(Constants.MC_MANIFEST_URL) + outputFile.set(cache.resolve(Constants.MC_MANIFEST)) + } + + val mcManifest = downloadMcManifest.flatMap { it.outputFile }.map { gson.fromJson(it) } + + val downloadMcVersionManifest by tasks.registering { + url.set(mcManifest.zip(extension.minecraftVersion) { manifest, version -> + manifest.versions.first { it.id == version }.url + }) + outputFile.set(cache.resolve(Constants.VERSION_JSON)) + } + + val versionManifest = downloadMcVersionManifest.flatMap { it.outputFile }.map { gson.fromJson(it) } + + val setupMcLibraries by tasks.registering { + dependencies.set(versionManifest.map { version -> + version["libraries"].array.map { library -> + library["name"].string + }.filter { !it.contains("lwjgl") } // we don't need these on the server + }) + outputFile.set(cache.resolve(Constants.MC_LIBRARIES)) + } + + val downloadMcpFiles by tasks.registering { + mcpMinecraftVersion.set(extension.mcpMinecraftVersion) + mcpConfigVersion.set(extension.mcpConfigVersion) + mcpMappingsChannel.set(extension.mcpMappingsChannel) + mcpMappingsVersion.set(extension.mcpMappingsVersion) + + configZip.set(cache.resolve(Constants.MCP_ZIPS_PATH).resolve("McpConfig.zip")) + mappingsZip.set(cache.resolve(Constants.MCP_ZIPS_PATH).resolve("McpMappings.zip")) + } + + val extractMcpConfig by tasks.registering { + inputFile.set(downloadMcpFiles.flatMap { it.configZip }) outputDir.set(cache.resolve(Constants.MCP_DATA_DIR)) } - - val setupMcpDependencies: TaskProvider = project.tasks.register("setupMcpDependencies") { - configFile.set(extractMcpData.flatMap { it.configJson }) - forgeFlowerConfig.set(Constants.FORGE_FLOWER_CONFIG) - mcInjectorConfig.set(Constants.MCINJECT_CONFIG) - } - - val extractMcpMappings: TaskProvider = project.tasks.register("extractMcpMappings") { - config.set(Constants.MCP_MAPPINGS_CONFIG) + val extractMcpMappings by tasks.registering { + inputFile.set(downloadMcpFiles.flatMap { it.mappingsZip }) outputDir.set(cache.resolve(Constants.MCP_MAPPINGS_DIR)) } - val getRemoteJsons: TaskProvider = project.tasks.register("getRemoteJsons") { - config.set(Constants.MINECRAFT_DEP_CONFIG) + val downloadMcpTools by tasks.registering { + configFile.set(extractMcpConfig.flatMap { it.configFile }) + + val toolsPath = cache.resolve(Constants.MCP_TOOLS_PATH) + forgeFlowerFile.set(toolsPath.resolve("ForgeFlower.jar")) + mcInjectorFile.set(toolsPath.resolve("McInjector.jar")) + specialSourceFile.set(toolsPath.resolve("SpecialSource.jar")) } - val mcpRewrites: TaskProvider = project.tasks.register("mcpRewrites") { - fieldsCsv.set(extractMcpMappings.flatMap { it.fieldsCsv }) - methodsCsv.set(extractMcpMappings.flatMap { it.methodsCsv }) - paramsCsv.set(extractMcpMappings.flatMap { it.paramsCsv }) + return InitialTasks( + setupMcLibraries, + extractMcpConfig, + extractMcpMappings, + downloadMcpTools + ) + } + + private fun Project.createGeneralTasks(): GeneralTasks { + val buildDataInfo: Provider = contents(ext.craftBukkit.buildDataInfo) { + gson.fromJson(it) + } + + val downloadServerJar by tasks.registering { + downloadUrl.set(buildDataInfo.map { it.serverUrl }) + hash.set(buildDataInfo.map { it.minecraftHash }) + } + + val filterVanillaJar by tasks.registering { + inputJar.set(downloadServerJar.flatMap { it.outputJar }) + includes.set(listOf("/*.class", "/net/minecraft/**")) + } + + return GeneralTasks(buildDataInfo, downloadServerJar, filterVanillaJar) + } + + private fun Project.createMcpTasks(initialTasks: InitialTasks, generalTasks: GeneralTasks): McpTasks { + val filterVanillaJar: TaskProvider = generalTasks.filterVanillaJar + val cache: File = layout.cache + val extension: PaperweightExtension = ext + + val mcpRewrites by tasks.registering { + fieldsCsv.set(initialTasks.mcpMappings.flatMap { it.fieldsCsv }) + methodsCsv.set(initialTasks.mcpMappings.flatMap { it.methodsCsv }) + paramsCsv.set(initialTasks.mcpMappings.flatMap { it.paramsCsv }) changesFile.set(extension.paper.mcpRewritesFile) paperFieldCsv.set(cache.resolve(Constants.PAPER_FIELDS_CSV)) @@ -175,8 +289,9 @@ class Paperweight : Plugin { paperParamCsv.set(cache.resolve(Constants.PAPER_PARAMS_CSV)) } - val generateSrgs: TaskProvider = project.tasks.register("generateSrgs") { - configFile.set(extractMcpData.flatMap { it.configJson }) + val generateSrgs by tasks.registering { + inSrg.set(initialTasks.extractMcp.flatMap { it.mappings }) + methodsCsv.set(mcpRewrites.flatMap { it.paperMethodCsv }) fieldsCsv.set(mcpRewrites.flatMap { it.paperFieldCsv }) extraNotchSrgMappings.set(extension.paper.extraNotchSrgMappings) @@ -189,20 +304,78 @@ class Paperweight : Plugin { mcpToSrg.set(cache.resolve(Constants.MCP_TO_SRG)) } - val addMissingSpigotClassMappings: TaskProvider = project.tasks.register("addMissingSpigotClassMappings") { + val remapVanillaJarSrg by tasks.registering { + inputJar.set(filterVanillaJar.flatMap { it.outputJar }) + mappings.set(generateSrgs.flatMap { it.notchToSrg }) + + executable.set(initialTasks.downloadMcpTools.flatMap { it.specialSourceFile }) + configFile.set(initialTasks.extractMcp.flatMap { it.configFile }) + } + + val injectVanillaJarSrg by tasks.registering { + executable.set(initialTasks.downloadMcpTools.flatMap { it.mcInjectorFile }) + configFile.set(initialTasks.extractMcp.flatMap { it.configFile }) + + exceptions.set(initialTasks.extractMcp.flatMap { it.exceptions }) + access.set(initialTasks.extractMcp.flatMap { it.access }) + constructors.set(initialTasks.extractMcp.flatMap { it.constructors }) + + inputJar.set(remapVanillaJarSrg.flatMap { it.outputJar }) + } + + val downloadMcLibraries by tasks.registering { + mcLibrariesFile.set(initialTasks.setupMcLibraries.flatMap { it.outputFile }) + mcRepo.set(Constants.MC_LIBRARY_URL) + outputDir.set(cache.resolve(Constants.MINECRAFT_JARS_PATH)) + } + + val writeLibrariesFile by tasks.registering { + libraries.set(downloadMcLibraries.flatMap { it.outputDir }) + } + + val decompileVanillaJarSrg by tasks.registering { + executable.set(initialTasks.downloadMcpTools.flatMap { it.forgeFlowerFile }) + configFile.set(initialTasks.extractMcp.flatMap { it.configFile }) + + inputJar.set(injectVanillaJarSrg.flatMap { it.outputJar }) + libraries.set(writeLibrariesFile.flatMap { it.outputFile }) + } + + val applyMcpPatches by tasks.registering { + inputZip.set(decompileVanillaJarSrg.flatMap { it.outputJar }) + serverPatchDir.set(initialTasks.extractMcp.flatMap { it.patchDir }) + configFile.set(cache.resolve(Constants.MCP_CONFIG_JSON)) + } + + return McpTasks(generateSrgs, remapVanillaJarSrg, applyMcpPatches) + } + + private fun Project.createSpigotTasks(initialTasks: InitialTasks, generalTasks: GeneralTasks, mcpTasks: McpTasks): SpigotTasks { + val cache: File = layout.cache + val extension: PaperweightExtension = ext + + val (buildDataInfo, downloadServerJar, filterVanillaJar) = generalTasks + val (generateSrgs, _, _) = mcpTasks + + val addMissingSpigotClassMappings by tasks.registering { classSrg.set(extension.craftBukkit.mappingsDir.file(buildDataInfo.map { it.classMappings })) memberSrg.set(extension.craftBukkit.mappingsDir.file(buildDataInfo.map { it.memberMappings })) missingClassEntriesSrg.set(extension.paper.missingClassEntriesSrgFile) missingMemberEntriesSrg.set(extension.paper.missingMemberEntriesSrgFile) } - val generateSpigotSrgs: TaskProvider = project.tasks.register("generateSpigotSrgs") { + val inspectVanillaJar by tasks.registering { + inputJar.set(downloadServerJar.flatMap { it.outputJar }) + } + + val generateSpigotSrgs by tasks.registering { notchToSrg.set(generateSrgs.flatMap { it.notchToSrg }) srgToMcp.set(generateSrgs.flatMap { it.srgToMcp }) classMappings.set(addMissingSpigotClassMappings.flatMap { it.outputClassSrg }) memberMappings.set(addMissingSpigotClassMappings.flatMap { it.outputMemberSrg }) packageMappings.set(extension.craftBukkit.mappingsDir.file(buildDataInfo.map { it.packageMappings })) extraSpigotSrgMappings.set(extension.paper.extraSpigotSrgMappings) + loggerFields.set(inspectVanillaJar.flatMap { it.outputFile }) spigotToSrg.set(cache.resolve(Constants.SPIGOT_TO_SRG)) spigotToMcp.set(cache.resolve(Constants.SPIGOT_TO_MCP)) @@ -212,25 +385,8 @@ class Paperweight : Plugin { notchToSpigot.set(cache.resolve(Constants.NOTCH_TO_SPIGOT)) } - val downloadServerJar: TaskProvider = project.tasks.register("downloadServerJar") { - dependsOn(gatherBuildData) - downloadUrl.set(buildDataInfo.map { it.serverUrl }) - hash.set(buildDataInfo.map { it.minecraftHash }) - } - - val filterVanillaJar: TaskProvider = project.tasks.register("filterVanillaJar") { - dependsOn(downloadServerJar) // the from() block below doesn't set up this dependency - archiveFileName.set("filterVanillaJar.jar") - destinationDirectory.set(cache.resolve(Constants.TASK_CACHE)) - - from(project.zipTree(downloadServerJar.flatMap { it.outputJar })) { - include("/*.class") - include("/net/minecraft/**") - } - } - - val remapVanillaJar: TaskProvider = project.tasks.register("remapVanillaJar") { - inputJar.set(project.layout.file(filterVanillaJar.map { it.outputs.files.singleFile })) + val remapVanillaJarSpigot by tasks.registering { + inputJar.set(filterVanillaJar.flatMap { it.outputJar }) classMappings.set(extension.craftBukkit.mappingsDir.file(buildDataInfo.map { it.classMappings })) memberMappings.set(extension.craftBukkit.mappingsDir.file(buildDataInfo.map { it.memberMappings })) packageMappings.set(extension.craftBukkit.mappingsDir.file(buildDataInfo.map { it.packageMappings })) @@ -246,18 +402,18 @@ class Paperweight : Plugin { finalMapCommand.set(buildDataInfo.map { it.finalMapCommand }) } - val removeSpigotExcludes: TaskProvider = project.tasks.register("removeSpigotExcludes") { - inputZip.set(remapVanillaJar.flatMap { it.outputJar }) + val removeSpigotExcludes by tasks.registering { + inputZip.set(remapVanillaJarSpigot.flatMap { it.outputJar }) excludesFile.set(extension.craftBukkit.excludesFile) } - val decompileVanillaJarSpigot: TaskProvider = project.tasks.register("decompileVanillaJarSpigot") { + val decompileVanillaJarSpigot by tasks.registering { inputJar.set(removeSpigotExcludes.flatMap { it.outputZip }) fernFlowerJar.set(extension.craftBukkit.fernFlowerJar) decompileCommand.set(buildDataInfo.map { it.decompileCommand }) } - val patchCraftBukkit: TaskProvider = project.tasks.register("patchCraftBukkit") { + val patchCraftBukkit by tasks.registering { sourceJar.set(decompileVanillaJarSpigot.flatMap { it.outputJar }) sourceBasePath.set("net/minecraft/server") branch.set("patched") @@ -266,7 +422,7 @@ class Paperweight : Plugin { outputDir.set(extension.craftBukkit.craftBukkitDir) } - val patchSpigotApi: TaskProvider = project.tasks.register("patchSpigotApi") { + val patchSpigotApi by tasks.registering { branch.set("HEAD") upstreamBranch.set("upstream") upstream.set(extension.craftBukkit.bukkitDir) @@ -275,7 +431,7 @@ class Paperweight : Plugin { outputDir.set(extension.spigot.spigotApiDir) } - val patchSpigotServer: TaskProvider = project.tasks.register("patchSpigotServer") { + val patchSpigotServer by tasks.registering { branch.set(patchCraftBukkit.flatMap { it.branch }) upstreamBranch.set("upstream") upstream.set(patchCraftBukkit.flatMap { it.outputDir }) @@ -284,78 +440,98 @@ class Paperweight : Plugin { outputDir.set(extension.spigot.spigotServerDir) } - val patchSpigot: TaskProvider = project.tasks.register("patchSpigot") { + val patchSpigot by tasks.registering { dependsOn(patchSpigotApi, patchSpigotServer) } - val setupSpigotDependencies: TaskProvider = project.tasks.register("setupSpigotDependencies") { + val downloadSpigotDependencies by tasks.registering { dependsOn(patchSpigot) - spigotApi.set(patchSpigotApi.flatMap { it.outputDir }) - spigotServer.set(patchSpigotServer.flatMap { it.outputDir }) - configurationName.set(Constants.SPIGOT_DEP_CONFIG) + apiPom.set(patchSpigotApi.flatMap { it.outputDir.file("pom.xml") }) + serverPom.set(patchSpigotServer.flatMap { it.outputDir.file("pom.xml") }) + apiOutputDir.set(cache.resolve(Constants.SPIGOT_API_JARS_PATH)) + serverOutputDir.set(cache.resolve(Constants.SPIGOT_SERVER_JARS_PATH)) } - val remapSpigotJarSrg: TaskProvider = project.tasks.register("remapSpigotJarSrg") { - // Basing off of the Spigot jar lets us inherit the AT modifications Spigot does before decompile - inputJar.set(remapVanillaJar.flatMap { it.outputJar }) - mappings.set(generateSpigotSrgs.flatMap { it.spigotToSrg }) + val remapSpigotAt by tasks.registering { + inputJar.set(remapVanillaJarSpigot.flatMap { it.outputJar }) + mapping.set(generateSpigotSrgs.flatMap { it.spigotToSrg }) + spigotAt.set(extension.craftBukkit.atFile) } - val remapSpigotSources: TaskProvider = project.tasks.register("remapSpigotSources") { - dependsOn(setupSpigotDependencies) + val remapSpigotSources by tasks.registering { spigotServerDir.set(patchSpigotServer.flatMap { it.outputDir }) spigotApiDir.set(patchSpigotApi.flatMap { it.outputDir }) mappings.set(generateSpigotSrgs.flatMap { it.spigotToSrg }) vanillaJar.set(downloadServerJar.flatMap { it.outputJar }) vanillaRemappedSpigotJar.set(removeSpigotExcludes.flatMap { it.outputZip }) - vanillaRemappedSrgJar.set(remapSpigotJarSrg.flatMap { it.outputJar }) - configuration.set(setupSpigotDependencies.flatMap { it.configurationName }) - configFile.set(extractMcpData.flatMap { it.configJson }) + spigotApiDeps.set(downloadSpigotDependencies.flatMap { it.apiOutputDir }) + spigotServerDeps.set(downloadSpigotDependencies.flatMap { it.serverOutputDir }) + constructors.set(initialTasks.extractMcp.flatMap { it.constructors }) } - val fixVanillaJarAccess: TaskProvider = project.tasks.register("fixVanillaJarAccess") { - inputJar.set(remapSpigotJarSrg.flatMap { it.outputJar }) - atFile.set(remapSpigotSources.flatMap { it.generatedAt }) - mapping.set(generateSpigotSrgs.flatMap { it.spigotToSrg }) + val remapGeneratedAt by tasks.registering { + inputFile.set(remapSpigotSources.flatMap { it.generatedAt }) + mappings.set(generateSpigotSrgs.flatMap { it.spigotToSrg }) } - val remapSrgSourcesSpigot: TaskProvider = project.tasks.register("remapSrgSourcesSpigot") { - inputZip.set(remapSpigotSources.flatMap { it.outputZip }) - methodsCsv.set(mcpRewrites.flatMap { it.methodsCsv }) - fieldsCsv.set(mcpRewrites.flatMap { it.fieldsCsv }) - paramsCsv.set(mcpRewrites.flatMap { it.paramsCsv }) + val mergeGeneratedAts by tasks.registering { + inputFiles.add(remapGeneratedAt.flatMap { it.outputFile }) + inputFiles.add(remapSpigotAt.flatMap { it.outputFile }) } - val injectVanillaJarForge: TaskProvider = project.tasks.register("injectVanillaJarForge") { - dependsOn(setupMcpDependencies) - configuration.set(setupMcpDependencies.flatMap { it.mcInjectorConfig }) - inputJar.set(remapSpigotJarSrg.flatMap { it.outputJar }) - configFile.set(extractMcpData.flatMap { it.configJson }) + return SpigotTasks( + generateSpigotSrgs, + decompileVanillaJarSpigot, + patchSpigotApi, + patchSpigotServer, + remapSpigotSources, + mergeGeneratedAts + ) + } + + private fun Project.createPatchRemapTasks( + initialTasks: InitialTasks, + generalTasks: GeneralTasks, + mcpTasks: McpTasks, + spigotTasks: SpigotTasks + ) { + val extension: PaperweightExtension = ext + + /* + * I don't remember what this is supposed to be for tbh + val patchPaperServerForPatchRemap by tasks.registering { + patchDir.set(extension.paper.spigotServerPatchDir) + remappedSource.set(spigotTasks.remapSpigotSources.flatMap { it.outputZip }) + + outputDir.set(cache.resolve("patch-paper-server-for-remap")) + } + */ + + val applyVanillaSrgAt by tasks.registering { + inputJar.set(mcpTasks.remapVanillaJarSrg.flatMap { it.outputJar }) + atFile.set(spigotTasks.mergeGeneratedAts.flatMap { it.outputFile }) } - val writeLibrariesFile: TaskProvider = project.tasks.register("writeLibrariesFile") { - dependsOn(getRemoteJsons) - config.set(getRemoteJsons.flatMap { it.config }) - } + val remapPatches by tasks.registering { + inputPatchDir.set(extension.paper.unmappedSpigotServerPatchDir) + sourceJar.set(spigotTasks.remapSpigotSources.flatMap { it.outputZip }) + apiPatchDir.set(extension.paper.spigotApiPatchDir) - val decompileVanillaJarForge: TaskProvider = project.tasks.register("decompileVanillaJarForge") { - dependsOn(setupMcpDependencies) - configuration.set(setupMcpDependencies.flatMap { it.forgeFlowerConfig }) - inputJar.set(injectVanillaJarForge.flatMap { it.outputJar }) - libraries.set(writeLibrariesFile.flatMap { it.outputFile }) - configFile.set(extractMcpData.flatMap { it.configJson }) - } + mappingsFile.set(spigotTasks.generateSpigotSrgs.flatMap { it.spigotToSrg }) -// val applyMcpPatches: TaskProvider = project.tasks.register("applyMcpPatches") { -// inputZips.add(ZipTarget.base(decompileVanillaJarForge.flatMap { it.outputJar })) -// serverPatchDir.set(extractMcpData.flatMap { it.patches }) -// } -// -// val remapSrgSourcesSpigotVanilla: TaskProvider = project.tasks.register("remapSrgSourcesSpigotVanilla") { -// inputZips.add(ZipTarget.base(applyMcpPatches.flatMap { outputZip })) -// methodsCsv.set(mcpRewrites.flatMap { methodsCsv }) -// fieldsCsv.set(mcpRewrites.flatMap { fieldsCsv }) -// paramsCsv.set(mcpRewrites.flatMap { paramsCsv }) -// } + // Pull in as many jars as possible to reduce the possibility of type bindings not resolving + classpathJars.add(generalTasks.downloadServerJar.flatMap { it.outputJar }) + classpathJars.add(spigotTasks.remapSpigotSources.flatMap { it.vanillaRemappedSpigotJar }) + classpathJars.add(applyVanillaSrgAt.flatMap { it.outputJar }) + + spigotApiDir.set(spigotTasks.patchSpigotApi.flatMap { it.outputDir }) + spigotServerDir.set(spigotTasks.patchSpigotServer.flatMap { it.outputDir }) + spigotDecompJar.set(spigotTasks.decompileVanillaJarSpigot.flatMap { it.outputJar }) + constructors.set(initialTasks.extractMcp.flatMap { it.constructors }) + + parameterNames.set(spigotTasks.remapSpigotSources.flatMap { it.parameterNames }) + + outputPatchDir.set(extension.paper.remappedSpigotServerPatchDir) + } } } diff --git a/src/main/kotlin/PaperweightException.kt b/src/main/kotlin/PaperweightException.kt index d70d79e..59f2660 100644 --- a/src/main/kotlin/PaperweightException.kt +++ b/src/main/kotlin/PaperweightException.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/main/kotlin/ext/CraftBukkitExtension.kt b/src/main/kotlin/ext/CraftBukkitExtension.kt index 885facc..c2b43b8 100644 --- a/src/main/kotlin/ext/CraftBukkitExtension.kt +++ b/src/main/kotlin/ext/CraftBukkitExtension.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,18 +22,35 @@ package io.papermc.paperweight.ext -import org.gradle.api.Project import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory -open class CraftBukkitExtension(project: Project) { - val bukkitDir: DirectoryProperty = project.dirWithDefault("work/Bukkit") - var craftBukkitDir: DirectoryProperty = project.dirWithDefault("work/CraftBukkit") - var patchDir: DirectoryProperty = project.dirWithDefault("work/CraftBukkit/nms-patches") - var mappingsDir: DirectoryProperty = project.dirWithDefault("work/BuildData/mappings") - val excludesFile: RegularFileProperty = project.fileWithDefault("work/BuildData/mappings/bukkit-1.16.1.exclude") - var buildDataInfo: RegularFileProperty = project.fileWithDefault("work/BuildData/info.json") - var fernFlowerJar: RegularFileProperty = project.fileWithDefault("work/BuildData/bin/fernflower.jar") - var specialSourceJar: RegularFileProperty = project.fileWithDefault("work/BuildData/bin/SpecialSource.jar") - var specialSource2Jar: RegularFileProperty = project.fileWithDefault("work/BuildData/bin/SpecialSource-2.jar") +open class CraftBukkitExtension(objects: ObjectFactory, workDir: DirectoryProperty) { + + val bukkitDir: DirectoryProperty = objects.dirFrom(workDir, "Bukkit") + val craftBukkitDir: DirectoryProperty = objects.dirFrom(workDir, "CraftBukkit") + val patchDir: DirectoryProperty = objects.dirFrom(craftBukkitDir, "nms-patches") + @Suppress("MemberVisibilityCanBePrivate") + val buildDataDir: DirectoryProperty = objects.dirFrom(workDir, "BuildData") + val mappingsDir: DirectoryProperty = objects.dirFrom(buildDataDir, "mappings") + val excludesFile: RegularFileProperty = objects.bukkitFileFrom(mappingsDir, "exclude") + val atFile: RegularFileProperty = objects.bukkitFileFrom(mappingsDir, "at") + val buildDataInfo: RegularFileProperty = objects.fileFrom(buildDataDir, "info.json") + @Suppress("MemberVisibilityCanBePrivate") + val buildDataBinDir: DirectoryProperty = objects.dirFrom(buildDataDir, "bin") + val fernFlowerJar: RegularFileProperty = objects.fileFrom(buildDataBinDir, "fernflower.jar") + val specialSourceJar: RegularFileProperty = objects.fileFrom(buildDataBinDir, "SpecialSource.jar") + val specialSource2Jar: RegularFileProperty = objects.fileFrom(buildDataBinDir, "SpecialSource-2.jar") + + private fun ObjectFactory.bukkitFileFrom(base: DirectoryProperty, extension: String): RegularFileProperty = + fileProperty().convention(base.flatMap { dir -> + val file = dir.asFile.listFiles()?.firstOrNull { it.name.endsWith(extension) } + if (file != null) { + mappingsDir.file(file.name) + } else { + // empty + fileProperty() + } + }) } diff --git a/src/main/kotlin/ext/PaperExtension.kt b/src/main/kotlin/ext/PaperExtension.kt index 35a2fde..2d39f69 100644 --- a/src/main/kotlin/ext/PaperExtension.kt +++ b/src/main/kotlin/ext/PaperExtension.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,25 +22,28 @@ package io.papermc.paperweight.ext -import org.gradle.api.Project +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.ProjectLayout import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.kotlin.dsl.property +import org.gradle.api.model.ObjectFactory -open class PaperExtension(project: Project) { - val spigotApiPatchDir: Property = project.objects.property().convention("Spigot-API-Patches") - val spigotServerPatchDir: Property = project.objects.property().convention("Spigot-Server-Patches") - val paperApiDir: Property = project.objects.property().convention("Paper-API") - val paperServerDir: Property = project.objects.property().convention("Paper-Server") +open class PaperExtension(objects: ObjectFactory, layout: ProjectLayout) { + @Suppress("MemberVisibilityCanBePrivate") + val baseTargetDir: DirectoryProperty = objects.dirWithDefault(layout, ".") + val spigotApiPatchDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "Spigot-API-Patches") + val spigotServerPatchDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "Spigot-Server-Patches") + val remappedSpigotServerPatchDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "Spigot-Server-Patches-Remapped") + val unmappedSpigotServerPatchDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "Spigot-Server-Patches-Unmapped") + val paperApiDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "Paper-API") + val paperServerDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "Paper-Server") - val mcpRewritesFile: RegularFileProperty = project.fileWithDefault("mcp/mcp-rewrites.txt") - val missingClassEntriesSrgFile: RegularFileProperty = project.fileWithDefault("mcp/missing-spigot-class-mappings.csrg") - val missingMemberEntriesSrgFile: RegularFileProperty = project.fileWithDefault("mcp/missing-spigot-member-mappings.csrg") - val extraNotchSrgMappings: RegularFileProperty = project.fileWithDefault("mcp/extra-notch-srg.tsrg") - val extraSpigotSrgMappings: RegularFileProperty = project.fileWithDefault("mcp/extra-spigot-srg.tsrg") - val preMapSrgFile: RegularFileProperty = project.fileWithDefault("mcp/paper.srg") - val removeListFile: RegularFileProperty = project.fileWithDefault("mcp/remove-list.txt") - val memberMoveListFile: RegularFileProperty = project.fileWithDefault("mcp/member-moves.txt") + @Suppress("MemberVisibilityCanBePrivate") + val mcpDir: DirectoryProperty = objects.dirWithDefault(layout, "mcp") + val mcpRewritesFile: RegularFileProperty = objects.fileFrom(mcpDir, "mcp-rewrites.txt") + val missingClassEntriesSrgFile: RegularFileProperty = objects.fileFrom(mcpDir, "missing-spigot-class-mappings.csrg") + val missingMemberEntriesSrgFile: RegularFileProperty = objects.fileFrom(mcpDir, "missing-spigot-member-mappings.csrg") + val extraNotchSrgMappings: RegularFileProperty = objects.fileFrom(mcpDir, "extra-notch-srg.tsrg") + val extraSpigotSrgMappings: RegularFileProperty = objects.fileFrom(mcpDir, "extra-spigot-srg.tsrg") init { spigotApiPatchDir.disallowUnsafeRead() @@ -50,8 +52,5 @@ open class PaperExtension(project: Project) { paperServerDir.disallowUnsafeRead() mcpRewritesFile.disallowUnsafeRead() - preMapSrgFile.disallowUnsafeRead() - removeListFile.disallowUnsafeRead() - memberMoveListFile.disallowUnsafeRead() } } diff --git a/src/main/kotlin/ext/PaperweightExtension.kt b/src/main/kotlin/ext/PaperweightExtension.kt index 34a7840..b70658c 100644 --- a/src/main/kotlin/ext/PaperweightExtension.kt +++ b/src/main/kotlin/ext/PaperweightExtension.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,26 +23,33 @@ package io.papermc.paperweight.ext import org.gradle.api.Action -import org.gradle.api.Project +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.ProjectLayout +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property import org.gradle.kotlin.dsl.property -open class PaperweightExtension(project: Project) { +open class PaperweightExtension(objects: ObjectFactory, layout: ProjectLayout) { - val minecraftVersion: Property = project.objects.property() - val mcpMinecraftVersion: Property = project.objects.property().convention(minecraftVersion) - val mcpVersion: Property = project.objects.property() - val mcpMappingsChannel: Property = project.objects.property() - val mcpMappingsVersion: Property = project.objects.property() + @Suppress("MemberVisibilityCanBePrivate") + val workDir: DirectoryProperty = objects.dirWithDefault(layout, "work") - val craftBukkit = CraftBukkitExtension(project) - val spigot = SpigotExtension(project) - val paper = PaperExtension(project) + val minecraftVersion: Property = objects.property() + val mcpMinecraftVersion: Property = objects.property().convention(minecraftVersion) + val mcpConfigVersion: Property = objects.property() + val mcpMappingsChannel: Property = objects.property() + val mcpMappingsVersion: Property = objects.property() + + val mcpConfigFile: RegularFileProperty = objects.fileProperty().convention(null) + + val craftBukkit = CraftBukkitExtension(objects, workDir) + val spigot = SpigotExtension(objects, workDir) + val paper = PaperExtension(objects, layout) init { minecraftVersion.disallowUnsafeRead() mcpMinecraftVersion.disallowUnsafeRead() - mcpVersion.disallowUnsafeRead() mcpMappingsChannel.disallowUnsafeRead() mcpMappingsVersion.disallowUnsafeRead() } diff --git a/src/main/kotlin/ext/SpigotExtension.kt b/src/main/kotlin/ext/SpigotExtension.kt index a1b6e1a..797241f 100644 --- a/src/main/kotlin/ext/SpigotExtension.kt +++ b/src/main/kotlin/ext/SpigotExtension.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,15 +22,15 @@ package io.papermc.paperweight.ext -import org.gradle.api.Project import org.gradle.api.file.DirectoryProperty +import org.gradle.api.model.ObjectFactory -open class SpigotExtension(project: Project) { - var spigotDir: DirectoryProperty = project.dirWithDefault("work/Spigot") - var spigotApiDir: DirectoryProperty = project.dirWithDefault("work/Spigot/Spigot-API") - var spigotServerDir: DirectoryProperty = project.dirWithDefault("work/Spigot/Spigot-Server") - var bukkitPatchDir: DirectoryProperty = project.dirWithDefault("work/Spigot/Bukkit-Patches") - var craftBukkitPatchDir: DirectoryProperty = project.dirWithDefault("work/Spigot/CraftBukkit-Patches") +open class SpigotExtension(objects: ObjectFactory, workDir: DirectoryProperty) { + var spigotDir: DirectoryProperty = objects.dirFrom(workDir, "Spigot") + var spigotApiDir: DirectoryProperty = objects.dirFrom(spigotDir, "Spigot-API") + var spigotServerDir: DirectoryProperty = objects.dirFrom(spigotDir, "Spigot-Server") + var bukkitPatchDir: DirectoryProperty = objects.dirFrom(spigotDir, "Bukkit-Patches") + var craftBukkitPatchDir: DirectoryProperty = objects.dirFrom(spigotDir, "CraftBukkit-Patches") init { spigotDir.disallowUnsafeRead() diff --git a/src/main/kotlin/ext/extensions.kt b/src/main/kotlin/ext/extensions.kt index fa14b2f..6ab0b5c 100644 --- a/src/main/kotlin/ext/extensions.kt +++ b/src/main/kotlin/ext/extensions.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,12 +22,16 @@ package io.papermc.paperweight.ext -import org.gradle.api.Project import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.ProjectLayout import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory -fun Project.dirWithDefault(path: String): DirectoryProperty = - project.objects.directoryProperty().convention(layout.dir(provider { file(path) })) +fun ObjectFactory.dirWithDefault(layout: ProjectLayout, path: String): DirectoryProperty = + directoryProperty().convention(layout.projectDirectory.dir(path)) -fun Project.fileWithDefault(path: String): RegularFileProperty = - project.objects.fileProperty().convention(layout.file(provider { file(path) })) +fun ObjectFactory.dirFrom(base: DirectoryProperty, name: String): DirectoryProperty = + directoryProperty().convention(base.dir(name)) + +fun ObjectFactory.fileFrom(base: DirectoryProperty, name: String): RegularFileProperty = + fileProperty().convention(base.file(name)) diff --git a/src/main/kotlin/tasks/AddMissingSpigotClassMappings.kt b/src/main/kotlin/tasks/AddMissingSpigotClassMappings.kt index a25dd89..d5fa038 100644 --- a/src/main/kotlin/tasks/AddMissingSpigotClassMappings.kt +++ b/src/main/kotlin/tasks/AddMissingSpigotClassMappings.kt @@ -1,48 +1,73 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.defaultOutput import io.papermc.paperweight.util.file -import org.gradle.api.DefaultTask +import io.papermc.paperweight.util.fileOrNull import org.gradle.api.file.RegularFileProperty import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction import java.io.File -open class AddMissingSpigotClassMappings : DefaultTask() { +abstract class AddMissingSpigotClassMappings : BaseTask() { - @InputFile - val classSrg: RegularFileProperty = project.objects.fileProperty() - @InputFile - val memberSrg: RegularFileProperty = project.objects.fileProperty() + @get:InputFile + abstract val classSrg: RegularFileProperty + @get:InputFile + abstract val memberSrg: RegularFileProperty + @get:Optional + @get:InputFile + abstract val missingClassEntriesSrg: RegularFileProperty + @get:Optional + @get:InputFile + abstract val missingMemberEntriesSrg: RegularFileProperty - @InputFile - val missingClassEntriesSrg: RegularFileProperty = project.objects.fileProperty() - @InputFile - val missingMemberEntriesSrg: RegularFileProperty = project.objects.fileProperty() + @get:OutputFile + abstract val outputClassSrg: RegularFileProperty + @get:OutputFile + abstract val outputMemberSrg: RegularFileProperty - @OutputFile - val outputClassSrg: RegularFileProperty = defaultOutput("class.csrg") - @OutputFile - val outputMemberSrg: RegularFileProperty = defaultOutput("member.csrg") + override fun init() { + outputClassSrg.convention(defaultOutput("class.csrg")) + outputMemberSrg.convention(defaultOutput("member.csrg")) + } @TaskAction fun run() { - addLines(classSrg.file, missingClassEntriesSrg.file, outputClassSrg.file) - addLines(memberSrg.file, missingMemberEntriesSrg.file, outputMemberSrg.file) + addLines(classSrg.file, missingClassEntriesSrg.fileOrNull, outputClassSrg.file) + addLines(memberSrg.file, missingMemberEntriesSrg.fileOrNull, outputMemberSrg.file) } - private fun addLines(inFile: File, appendFile: File, outputFile: File) { + private fun addLines(inFile: File, appendFile: File?, outputFile: File) { val lines = mutableListOf() - inFile.bufferedReader().use { reader -> - lines.addAll(reader.readLines()) - } - appendFile.bufferedReader().use { reader -> - lines.addAll(reader.readLines()) - } + inFile.useLines { seq -> seq.forEach { lines.add(it) } } + appendFile?.useLines { seq -> seq.forEach { lines.add(it) } } lines.sort() outputFile.bufferedWriter().use { writer -> - lines.forEach(writer::appendln) + lines.forEach { writer.appendln(it) } } } } diff --git a/src/main/kotlin/tasks/ApplyDiffPatches.kt b/src/main/kotlin/tasks/ApplyDiffPatches.kt index 8c8f5ab..3ea1ffc 100644 --- a/src/main/kotlin/tasks/ApplyDiffPatches.kt +++ b/src/main/kotlin/tasks/ApplyDiffPatches.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,10 +23,11 @@ package io.papermc.paperweight.tasks import io.papermc.paperweight.PaperweightException +import io.papermc.paperweight.util.Command import io.papermc.paperweight.util.Git +import io.papermc.paperweight.util.UselessOutputStream import io.papermc.paperweight.util.ensureParentExists import io.papermc.paperweight.util.file -import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property @@ -36,33 +36,34 @@ import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.property import java.net.URI import java.nio.file.FileSystems import java.nio.file.Files import java.util.Date -open class ApplyDiffPatches : DefaultTask() { +abstract class ApplyDiffPatches : ControllableOutputTask() { - @InputFile - val sourceJar: RegularFileProperty = project.objects.fileProperty() - @Input - val sourceBasePath: Property = project.objects.property() - @InputDirectory - val patchDir: DirectoryProperty = project.objects.directoryProperty() - @Input - val branch: Property = project.objects.property() + @get:InputFile + abstract val sourceJar: RegularFileProperty + @get:Input + abstract val sourceBasePath: Property + @get:InputDirectory + abstract val patchDir: DirectoryProperty + @get:Input + abstract val branch: Property - @OutputDirectory - val outputDir: DirectoryProperty = project.objects.directoryProperty() + @get:OutputDirectory + abstract val outputDir: DirectoryProperty + + override fun init() { + printOutput.convention(false) + } @TaskAction fun run() { val git = Git(outputDir.file) git("checkout", "-B", branch.get(), "HEAD").executeSilently(silenceErr = true) - val uri = URI.create("jar:${sourceJar.file.toURI()}") - val basePatchDirFile = outputDir.file.resolve("src/main/java") val outputDirFile = basePatchDirFile.resolve(sourceBasePath.get()) outputDirFile.deleteRecursively() @@ -73,6 +74,7 @@ open class ApplyDiffPatches : DefaultTask() { } // Copy in patch targets + val uri = URI.create("jar:" + sourceJar.file.toURI()) FileSystems.newFileSystem(uri, mapOf()).use { fs -> for (file in patchList) { val javaName = file.name.replaceAfterLast('.', "java") @@ -88,19 +90,29 @@ open class ApplyDiffPatches : DefaultTask() { } } - git("add", "src").executeOut() - git("commit", "-m", "Vanilla $ ${Date()}", "--author=Vanilla ").executeOut() + git("add", "src").setupOut().execute() + git("commit", "-m", "Vanilla $ ${Date()}", "--author=Vanilla ").setupOut().execute() // Apply patches for (file in patchList) { val javaName = file.name.replaceAfterLast('.', "java") - println("Patching ${javaName.removeSuffix(".java")}") - git("apply", "--directory=${basePatchDirFile.relativeTo(outputDir.file).path}", file.absolutePath).executeOut() + if (printOutput.get()) { + println("Patching ${javaName.removeSuffix(".java")}") + } + git("apply", "--directory=${basePatchDirFile.relativeTo(outputDir.file).path}", file.absolutePath).setupOut().execute() } - git("add", "src").executeOut() - git("commit", "-m", "CraftBukkit $ ${Date()}", "--author=CraftBukkit ").executeOut() - git("checkout", "-f", "HEAD~2").executeSilently() + git("add", "src").setupOut().execute() + git("commit", "-m", "CraftBukkit $ ${Date()}", "--author=CraftBukkit ").setupOut().execute() + git("checkout", "-f", "HEAD~2").setupOut().execute() + } + + private fun Command.setupOut() = apply { + if (printOutput.get()) { + setup(System.out, System.err) + } else { + setup(UselessOutputStream, UselessOutputStream) + } } } diff --git a/src/main/kotlin/tasks/ApplyGitPatches.kt b/src/main/kotlin/tasks/ApplyGitPatches.kt index 80084e4..68db309 100644 --- a/src/main/kotlin/tasks/ApplyGitPatches.kt +++ b/src/main/kotlin/tasks/ApplyGitPatches.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -26,7 +25,6 @@ package io.papermc.paperweight.tasks import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.util.Git import io.papermc.paperweight.util.file -import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input @@ -34,80 +32,95 @@ import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction import org.gradle.internal.os.OperatingSystem -import org.gradle.kotlin.dsl.property +import java.io.File -open class ApplyGitPatches : DefaultTask() { +abstract class ApplyGitPatches : ControllableOutputTask() { - @Input - val branch: Property = project.objects.property() - @Input - val upstreamBranch: Property = project.objects.property() - @InputDirectory - val upstream: DirectoryProperty = project.objects.directoryProperty() - @InputDirectory - val patchDir: DirectoryProperty = project.objects.directoryProperty() + @get:Input + abstract val branch: Property + @get:Input + abstract val upstreamBranch: Property + @get:InputDirectory + abstract val upstream: DirectoryProperty + @get:InputDirectory + abstract val patchDir: DirectoryProperty - @OutputDirectory - val outputDir: DirectoryProperty = project.objects.directoryProperty() + @get:OutputDirectory + abstract val outputDir: DirectoryProperty + + override fun init() { + printOutput.convention(false) + } @TaskAction fun run() { Git(upstream.file).let { git -> - git("fetch").run() + git("fetch").setupOut().run() git("branch", "-f", upstreamBranch.get(), branch.get()).runSilently() } if (!outputDir.file.exists() || !outputDir.file.resolve(".git").exists()) { outputDir.file.deleteRecursively() - Git(outputDir.file.parentFile)("clone", upstream.file.absolutePath, outputDir.file.name).runOut() + Git(outputDir.file.parentFile)("clone", upstream.file.absolutePath, outputDir.file.name).setupOut().run() } val target = outputDir.file.name - println(" Resetting $target to ${upstream.file.name}...") + if (printOutput.get()) { + println(" Resetting $target to ${upstream.file.name}...") + } Git(outputDir.file).let { git -> git("remote", "rm", "upstream").runSilently(silenceErr = true) git("remote", "add", "upstream", upstream.file.absolutePath).runSilently(silenceErr = true) - if (git("checkout", "master").runSilently(silenceOut = false, silenceErr = true) != 0) { - git("checkout", "-b", "master").runOut() + if (git("checkout", "master").setupOut(showError = false).run() != 0) { + git("checkout", "-b", "master").setupOut().run() } git("fetch", "upstream").runSilently(silenceErr = true) - git("reset", "--hard", "upstream/${upstreamBranch.get()}").runOut() + git("reset", "--hard", "upstream/${upstreamBranch.get()}").setupOut().run() - println(" Applying patches to $target...") - - val statusFile = outputDir.file.resolve(".git/patch-apply-failed") - if (statusFile.exists()) { - statusFile.delete() - } - git("am", "--abort").runSilently(silenceErr = true) - - val patches = patchDir.file.listFiles { _, name -> name.endsWith(".patch") } ?: run { - println("No patches found") - return - } - - patches.sort() - - if (git("am", "--3way", "--ignore-whitespace", *patches.map { it.absolutePath }.toTypedArray()).runOut() != 0) { - Thread.sleep(100) // Wait for git - statusFile.writeText("1") - logger.error("*** Something did not apply cleanly to $target.") - logger.error("*** Please review above details and finish the apply then") - logger.error("*** save the changes with ./gradlew `rebuildPatches`") - - if (OperatingSystem.current().isWindows) { - logger.error("") - logger.error("*** Because you're on Windows you'll need to finish the AM,") - logger.error("*** rebuild all patches, and then re-run the patch apply again.") - logger.error("*** Consider using the scripts with Windows Subsystem for Linux.") - } - - throw PaperweightException("Failed to apply patches") - } else { - statusFile.delete() - println(" Patches applied cleanly to $target") - } + applyGitPatches(git, target, outputDir.file, patchDir.file, printOutput.get()) } } } + +fun ControllableOutputTask.applyGitPatches(git: Git, target: String, outputDir: File, patchDir: File, printOutput: Boolean) { + if (printOutput) { + println(" Applying patches to $target...") + } + + val statusFile = outputDir.resolve(".git/patch-apply-failed") + if (statusFile.exists()) { + statusFile.delete() + } + git("am", "--abort").runSilently(silenceErr = true) + + val patches = patchDir.listFiles { _, name -> name.endsWith(".patch") } ?: run { + if (printOutput) { + println("No patches found") + } + return + } + + patches.sort() + + if (git("am", "--3way", "--ignore-whitespace", *patches.map { it.absolutePath }.toTypedArray()).showErrors().run() != 0) { + statusFile.writeText("1") + logger.error("*** Please review above details and finish the apply then") + logger.error("*** save the changes with ./gradlew `rebuildPatches`") + + if (OperatingSystem.current().isWindows) { + logger.error("") + logger.error("*** Because you're on Windows you'll need to finish the AM,") + logger.error("*** rebuild all patches, and then re-run the patch apply again.") + logger.error("*** Consider using the scripts with Windows Subsystem for Linux.") + } + + throw PaperweightException("Failed to apply patches") + } else { + statusFile.delete() + if (printOutput) { + println(" Patches applied cleanly to $target") + } + } +} + diff --git a/src/main/kotlin/tasks/ApplyMcpPatches.kt b/src/main/kotlin/tasks/ApplyMcpPatches.kt index 34aee45..7576da5 100644 --- a/src/main/kotlin/tasks/ApplyMcpPatches.kt +++ b/src/main/kotlin/tasks/ApplyMcpPatches.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,39 +23,27 @@ package io.papermc.paperweight.tasks import io.papermc.paperweight.util.Git -import io.papermc.paperweight.util.mcpConfig -import io.papermc.paperweight.util.mcpFile +import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.InputFile import java.io.File -open class ApplyMcpPatches : ZippedTask() { +abstract class ApplyMcpPatches : ZippedTask() { - @InputFile - val configFile: RegularFileProperty = project.objects.fileProperty() - - init { - inputs.dir(configFile.map { mcpConfig(configFile) }.map { mcpFile(configFile, it.data.patches.server) }) - } + @get:InputDirectory + abstract val serverPatchDir: DirectoryProperty + @get:InputFile + abstract val configFile: RegularFileProperty override fun run(rootDir: File) { - val config = mcpConfig(configFile) - val serverPatchDir = mcpFile(configFile, config.data.patches.server) - val git = Git(rootDir) val extension = ".java.patch" - project.fileTree(serverPatchDir).matching { - include("*$extension") + objects.fileTree().from(serverPatchDir).matching { + include("**/*$extension") }.forEach { patch -> - val patchName = patch.name - print("Patching ${patchName.substring(0, extension.length)}") - val exit = git("apply", "--ignore-whitespace", patch.absolutePath).setup(System.out, null).run() - if (exit != 0) { - println("...Failed") - } else { - println() - } + git("apply", "--ignore-whitespace", patch.absolutePath).executeSilently() } } } diff --git a/src/main/kotlin/tasks/ApplyPaperPatches.kt b/src/main/kotlin/tasks/ApplyPaperPatches.kt new file mode 100644 index 0000000..523cecd --- /dev/null +++ b/src/main/kotlin/tasks/ApplyPaperPatches.kt @@ -0,0 +1,89 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.Git +import io.papermc.paperweight.util.file +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction + +abstract class ApplyPaperPatches : ControllableOutputTask() { + + @get:InputDirectory + abstract val patchDir: DirectoryProperty + @get:InputFile + abstract val remappedSource: RegularFileProperty + @get:InputFile + abstract val templateGitIgnore: RegularFileProperty + + @get:OutputDirectory + abstract val outputDir: DirectoryProperty + @get:OutputDirectory + abstract val remapTargetDir: DirectoryProperty + + override fun init() { + printOutput.convention(true) + remapTargetDir.convention(outputDir.dir("src/main/java")) + } + + @TaskAction + fun run() { + val outputFile = outputDir.file + if (outputFile.exists()) { + outputFile.deleteRecursively() + } + outputFile.mkdirs() + + val target = outputFile.name + + if (printOutput.get()) { + println(" Creating $target from remapped source...") + } + + Git(outputFile).let { git -> + git("init").runSilently(silenceErr = true) + + val sourceDir = remapTargetDir.file + if (sourceDir.exists()) { + sourceDir.deleteRecursively() + } + sourceDir.mkdirs() + + fs.copy { + from(archives.zipTree(remappedSource.file)) + into(sourceDir) + } + + templateGitIgnore.file.copyTo(outputFile.resolve(".gitignore")) + + git("add", ".gitignore", ".").executeSilently() + git("commit", "-m", "Initial", "--author=Initial ").executeSilently() + + applyGitPatches(git, target, outputFile, patchDir.file, printOutput.get()) + } + } +} diff --git a/src/main/kotlin/tasks/ApplySourceAt.kt b/src/main/kotlin/tasks/ApplySourceAt.kt new file mode 100644 index 0000000..46dd825 --- /dev/null +++ b/src/main/kotlin/tasks/ApplySourceAt.kt @@ -0,0 +1,81 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.path +import org.cadixdev.at.io.AccessTransformFormats +import org.cadixdev.mercury.Mercury +import org.cadixdev.mercury.at.AccessTransformerRewriter +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputFile +import java.io.File + +abstract class ApplySourceAt : ZippedTask() { + + @get:InputFile + abstract val vanillaJar: RegularFileProperty + @get:InputFile + abstract val vanillaRemappedSrgJar: RegularFileProperty + @get:InputFile + abstract val atFile: RegularFileProperty + + override fun run(rootDir: File) { + val inputDir = rootDir.resolve("input") + val outputDir = rootDir.resolve("output") + + // Move everything into `input/` so we can put output into `output/` + inputDir.mkdirs() + rootDir.listFiles()?.forEach { file -> + if (file != inputDir) { + file.renameTo(inputDir.resolve(file.name)) + } + } + outputDir.mkdirs() + + val at = AccessTransformFormats.FML.read(atFile.path) + + Mercury().apply { + classPath.addAll(listOf( + vanillaJar.path, + vanillaRemappedSrgJar.path + )) + + processors.add(AccessTransformerRewriter.create(at)) + + rewrite(inputDir.toPath(), outputDir.toPath()) + } + + // Remove input files + rootDir.listFiles()?.forEach { file -> + if (file != outputDir) { + file.deleteRecursively() + } + } + + // Move output into root + outputDir.listFiles()?.forEach { file -> + file.renameTo(rootDir.resolve(file.name)) + } + outputDir.delete() + } +} diff --git a/src/main/kotlin/tasks/BaseTask.kt b/src/main/kotlin/tasks/BaseTask.kt new file mode 100644 index 0000000..6799146 --- /dev/null +++ b/src/main/kotlin/tasks/BaseTask.kt @@ -0,0 +1,48 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 org.gradle.api.DefaultTask +import org.gradle.api.file.ArchiveOperations +import org.gradle.api.file.FileSystemOperations +import org.gradle.api.file.ProjectLayout +import org.gradle.api.model.ObjectFactory +import javax.inject.Inject + +abstract class BaseTask : DefaultTask() { + + @get:Inject + abstract val objects: ObjectFactory + @get:Inject + abstract val layout: ProjectLayout + @get:Inject + abstract val fs: FileSystemOperations + @get:Inject + abstract val archives: ArchiveOperations + + open fun init() {} + + init { + this.init() + } +} diff --git a/src/main/kotlin/tasks/ControllableOutputTask.kt b/src/main/kotlin/tasks/ControllableOutputTask.kt new file mode 100644 index 0000000..d6bc64f --- /dev/null +++ b/src/main/kotlin/tasks/ControllableOutputTask.kt @@ -0,0 +1,51 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.Command +import io.papermc.paperweight.util.UselessOutputStream +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Console + +abstract class ControllableOutputTask : BaseTask() { + + @get:Console + abstract val printOutput: Property + + fun Command.setupOut(showError: Boolean = true) = apply { + if (printOutput.get()) { + val err = if (showError) System.out else UselessOutputStream + setup(System.out, err) + } else { + setup(UselessOutputStream, UselessOutputStream) + } + } + + fun Command.showErrors() = apply { + if (printOutput.get()) { + setup(System.out, System.out) + } else { + setup(UselessOutputStream, System.out) + } + } +} diff --git a/src/main/kotlin/tasks/DecompileVanillaJar.kt b/src/main/kotlin/tasks/DecompileVanillaJar.kt index 9b5bd6a..aca67fd 100644 --- a/src/main/kotlin/tasks/DecompileVanillaJar.kt +++ b/src/main/kotlin/tasks/DecompileVanillaJar.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -29,27 +28,29 @@ import io.papermc.paperweight.util.cache import io.papermc.paperweight.util.defaultOutput import io.papermc.paperweight.util.ensureDeleted import io.papermc.paperweight.util.runJar -import org.gradle.api.DefaultTask import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.property import java.util.concurrent.ThreadLocalRandom -open class DecompileVanillaJar : DefaultTask() { +abstract class DecompileVanillaJar : BaseTask() { - @InputFile - val inputJar: RegularFileProperty = project.objects.fileProperty() - @InputFile - val fernFlowerJar: RegularFileProperty = project.objects.fileProperty() - @Input - val decompileCommand: Property = project.objects.property() + @get:InputFile + abstract val inputJar: RegularFileProperty + @get:InputFile + abstract val fernFlowerJar: RegularFileProperty + @get:Input + abstract val decompileCommand: Property - @OutputFile - val outputJar: RegularFileProperty = defaultOutput() + @get:OutputFile + abstract val outputJar: RegularFileProperty + + override fun init() { + outputJar.convention(defaultOutput()) + } @TaskAction fun run() { @@ -68,10 +69,10 @@ open class DecompileVanillaJar : DefaultTask() { cmd += inputJarPath cmd += decomp.canonicalPath - val logFile = project.cache.resolve(paperTaskOutput("log")) + val logFile = layout.cache.resolve(paperTaskOutput("log")) logFile.delete() - runJar(fernFlowerJar, workingDir = project.cache, logFile = logFile, args = *cmd.toTypedArray()) + runJar(fernFlowerJar, workingDir = layout.cache, logFile = logFile, args = *cmd.toTypedArray()) ensureDeleted(outputJarFile) decomp.resolve(inputJarFile.name).renameTo(outputJarFile) diff --git a/src/main/kotlin/tasks/DownloadServerJar.kt b/src/main/kotlin/tasks/DownloadServerJar.kt index 4fb8de7..a4d31ee 100644 --- a/src/main/kotlin/tasks/DownloadServerJar.kt +++ b/src/main/kotlin/tasks/DownloadServerJar.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,38 +24,34 @@ package io.papermc.paperweight.tasks import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.util.defaultOutput -import io.papermc.paperweight.util.ensureDeleted -import io.papermc.paperweight.util.ensureParentExists -import org.gradle.api.DefaultTask +import io.papermc.paperweight.util.download import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.property import java.math.BigInteger -import java.net.URL import java.security.MessageDigest -open class DownloadServerJar : DefaultTask() { +abstract class DownloadServerJar : BaseTask() { - @Input - val downloadUrl: Property = project.objects.property() - @Input - val hash: Property = project.objects.property() + @get:Input + abstract val downloadUrl: Property + @get:Input + abstract val hash: Property - @OutputFile - val outputJar: RegularFileProperty = defaultOutput() + @get:OutputFile + abstract val outputJar: RegularFileProperty + + override fun init() { + outputJar.convention(defaultOutput()) + } @TaskAction fun run() { val file = outputJar.asFile.get() - ensureParentExists(file) - ensureDeleted(file) - file.outputStream().buffered().use { out -> - URL(downloadUrl.get()).openStream().copyTo(out) - } + download(downloadUrl, outputJar) val digest = MessageDigest.getInstance("MD5") diff --git a/src/main/kotlin/tasks/Extract.kt b/src/main/kotlin/tasks/Extract.kt deleted file mode 100644 index 7ba6752..0000000 --- a/src/main/kotlin/tasks/Extract.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. It uses - * some code and systems originally from ForgeGradle. - * - * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC - * - * 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; either - * version 2.1 of the License, or (at your option) any later version. - * - * 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 org.gradle.api.DefaultTask -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.get -import org.gradle.kotlin.dsl.property - -open class Extract : DefaultTask() { - - @Input - val config: Property = project.objects.property() - - @OutputDirectory - val outputDir: DirectoryProperty = project.objects.directoryProperty() - - @TaskAction - fun run() { - project.copy { - from(project.zipTree(project.configurations[config.get()].resolve().single())) - into(outputDir) - } - } -} - -open class ExtractMcpMappings : Extract() { - - @OutputFile - val fieldsCsv: RegularFileProperty = project.objects.fileProperty() - .convention(outputDir.map { it.file("fields.csv") }) - @OutputFile - val methodsCsv: RegularFileProperty = project.objects.fileProperty() - .convention(outputDir.map { it.file("methods.csv") }) - @OutputFile - val paramsCsv: RegularFileProperty = project.objects.fileProperty() - .convention(outputDir.map { it.file("params.csv") }) -} - -open class ExtractMcpData : Extract() { - - @OutputFile - val configJson: RegularFileProperty = project.objects.fileProperty() - .convention(outputDir.map { it.file("config.json") }) -} diff --git a/src/main/kotlin/tasks/FilterExcludes.kt b/src/main/kotlin/tasks/FilterExcludes.kt index 41d1066..be16a83 100644 --- a/src/main/kotlin/tasks/FilterExcludes.kt +++ b/src/main/kotlin/tasks/FilterExcludes.kt @@ -1,3 +1,25 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.file @@ -9,10 +31,10 @@ import java.io.File * Because Spigot doesn't remap all classes, there are class and package name clashes if we don't do this in the source * remap step. Other than that, we don't need this jar */ -open class FilterExcludes : ZippedTask() { +abstract class FilterExcludes : ZippedTask() { - @InputFile - val excludesFile: RegularFileProperty = project.objects.fileProperty() + @get:InputFile + abstract val excludesFile: RegularFileProperty override fun run(rootDir: File) { excludesFile.file.useLines { lines -> diff --git a/src/main/kotlin/tasks/GenerateSpigotSrgs.kt b/src/main/kotlin/tasks/GenerateSpigotSrgs.kt index 4c67494..cf9a571 100644 --- a/src/main/kotlin/tasks/GenerateSpigotSrgs.kt +++ b/src/main/kotlin/tasks/GenerateSpigotSrgs.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,6 +23,7 @@ package io.papermc.paperweight.tasks import io.papermc.paperweight.util.file +import io.papermc.paperweight.util.fileOrNull import io.papermc.paperweight.util.writeMappings import org.cadixdev.lorenz.MappingSet import org.cadixdev.lorenz.io.MappingFormats @@ -40,37 +40,40 @@ import org.cadixdev.lorenz.model.TopLevelClassMapping import org.gradle.api.DefaultTask import org.gradle.api.file.RegularFileProperty import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction -import java.lang.IllegalStateException -open class GenerateSpigotSrgs : DefaultTask() { +abstract class GenerateSpigotSrgs : DefaultTask() { - @InputFile - val notchToSrg: RegularFileProperty = project.objects.fileProperty() - @InputFile - val srgToMcp: RegularFileProperty = project.objects.fileProperty() - @InputFile - val classMappings: RegularFileProperty = project.objects.fileProperty() - @InputFile - val memberMappings: RegularFileProperty = project.objects.fileProperty() - @InputFile - val packageMappings: RegularFileProperty = project.objects.fileProperty() - @InputFile - val extraSpigotSrgMappings: RegularFileProperty = project.objects.fileProperty() + @get:InputFile + abstract val notchToSrg: RegularFileProperty + @get:InputFile + abstract val srgToMcp: RegularFileProperty + @get:InputFile + abstract val classMappings: RegularFileProperty + @get:InputFile + abstract val memberMappings: RegularFileProperty + @get:InputFile + abstract val packageMappings: RegularFileProperty + @get:Optional + @get:InputFile + abstract val extraSpigotSrgMappings: RegularFileProperty + @get:InputFile + abstract val loggerFields: RegularFileProperty - @OutputFile - val spigotToSrg: RegularFileProperty = project.objects.fileProperty() - @OutputFile - val spigotToMcp: RegularFileProperty = project.objects.fileProperty() - @OutputFile - val spigotToNotch: RegularFileProperty = project.objects.fileProperty() - @OutputFile - val srgToSpigot: RegularFileProperty = project.objects.fileProperty() - @OutputFile - val mcpToSpigot: RegularFileProperty = project.objects.fileProperty() - @OutputFile - val notchToSpigot: RegularFileProperty = project.objects.fileProperty() + @get:OutputFile + abstract val spigotToSrg: RegularFileProperty + @get:OutputFile + abstract val spigotToMcp: RegularFileProperty + @get:OutputFile + abstract val spigotToNotch: RegularFileProperty + @get:OutputFile + abstract val srgToSpigot: RegularFileProperty + @get:OutputFile + abstract val mcpToSpigot: RegularFileProperty + @get:OutputFile + abstract val notchToSpigot: RegularFileProperty @TaskAction fun run() { @@ -78,6 +81,12 @@ open class GenerateSpigotSrgs : DefaultTask() { val memberMappingSet = MappingFormats.CSRG.createReader(memberMappings.file.toPath()).use { it.read() } val mergedMappingSet = MappingSetMerger.create(classMappingSet, memberMappingSet).merge() + for (line in loggerFields.file.readLines(Charsets.UTF_8)) { + val (className, fieldName) = line.split(' ') + val classMapping = mergedMappingSet.getClassMapping(className).orElse(null) ?: continue + classMapping.getOrCreateFieldMapping(fieldName).deobfuscatedName = "LOGGER" + } + // Get the new package name val newPackage = packageMappings.asFile.get().readLines()[0].split(Regex("\\s+"))[1] @@ -88,14 +97,16 @@ open class GenerateSpigotSrgs : DefaultTask() { mergedMappingSet, notchToSrgSet, MergeConfig.builder() - .withHandler(SpigotPackageMergerHandler(newPackage)) + .withMergeHandler(SpigotPackageMergerHandler(newPackage)) .build() ).merge() val srgToMcpSet = MappingFormats.TSRG.createReader(srgToMcp.file.toPath()).use { it.read() } val spigotToSrgSet = MappingSetMerger.create(notchToSpigotSet.reverse(), notchToSrgSet).merge() - MappingFormats.TSRG.createReader(extraSpigotSrgMappings.file.toPath()).use { it.read(spigotToSrgSet) } + extraSpigotSrgMappings.fileOrNull?.toPath()?.let { path -> + MappingFormats.TSRG.createReader(path).use { it.read(spigotToSrgSet) } + } val mcpToNotchSet = MappingSetMerger.create(notchToSrgSet, srgToMcpSet).merge().reverse() val mcpToSpigotSet = MappingSetMerger.create(mcpToNotchSet, notchToSpigotSet).merge() @@ -135,7 +146,7 @@ class SpigotPackageMergerHandler(private val newPackage: String) : MappingSetMer ): MergeResult { // If both are provided, keep spigot return MergeResult( - target.createTopLevelClassMapping(left.obfuscatedName, prependPackage(left.deobfuscatedName)), + target.createTopLevelClassMapping(prependPackage(left.obfuscatedName), prependPackage(left.deobfuscatedName)), right ) } @@ -145,7 +156,7 @@ class SpigotPackageMergerHandler(private val newPackage: String) : MappingSetMer context: MergeContext ): MergeResult { return MergeResult( - target.createTopLevelClassMapping(left.obfuscatedName, prependPackage(left.deobfuscatedName)) + target.createTopLevelClassMapping(prependPackage(left.obfuscatedName), prependPackage(left.deobfuscatedName)) ) } override fun addRightTopLevelClassMapping( @@ -157,8 +168,10 @@ class SpigotPackageMergerHandler(private val newPackage: String) : MappingSetMer return if (right.deobfuscatedName.contains("/client/")) { MergeResult(null) } else { + // This is a mapping Spigot is totally missing, but Spigot maps all classes without a package to + // /net/minecraft regardless if there are mappings for the classes or not MergeResult( - target.createTopLevelClassMapping(right.obfuscatedName, prependPackage(right.obfuscatedName)), + target.createTopLevelClassMapping(prependPackage(right.obfuscatedName), right.obfuscatedName), right ) } @@ -195,19 +208,23 @@ class SpigotPackageMergerHandler(private val newPackage: String) : MappingSetMer override fun mergeFieldMappings( left: FieldMapping, - right: FieldMapping, + strictRight: FieldMapping?, + looseRight: FieldMapping?, target: ClassMapping<*, *>, context: MergeContext - ): FieldMapping? { + ): FieldMapping { throw IllegalStateException("Unexpectedly merged field: $left") } + override fun mergeDuplicateFieldMappings( left: FieldMapping, - right: FieldMapping, - rightContinuation: FieldMapping?, + strictRightDuplicate: FieldMapping?, + looseRightDuplicate: FieldMapping?, + strictRightContinuation: FieldMapping?, + looseRightContinuation: FieldMapping?, target: ClassMapping<*, *>, context: MergeContext - ): FieldMapping? { + ): FieldMapping { return target.createFieldMapping(left.obfuscatedName, left.deobfuscatedName) } diff --git a/src/main/kotlin/tasks/GenerateSrgs.kt b/src/main/kotlin/tasks/GenerateSrgs.kt index 5080bd4..b37c686 100644 --- a/src/main/kotlin/tasks/GenerateSrgs.kt +++ b/src/main/kotlin/tasks/GenerateSrgs.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,10 +24,10 @@ package io.papermc.paperweight.tasks import io.papermc.paperweight.util.ensureParentExists import io.papermc.paperweight.util.file +import io.papermc.paperweight.util.fileOrNull import io.papermc.paperweight.util.getCsvReader -import io.papermc.paperweight.util.mcpConfig -import io.papermc.paperweight.util.mcpFile import io.papermc.paperweight.util.writeMappings +import org.cadixdev.bombe.type.signature.MethodSignature import org.cadixdev.lorenz.MappingSet import org.cadixdev.lorenz.io.MappingFormats import org.cadixdev.lorenz.merge.MappingSetMerger @@ -37,44 +36,46 @@ import org.cadixdev.lorenz.model.InnerClassMapping import org.gradle.api.DefaultTask import org.gradle.api.file.RegularFileProperty import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction -open class GenerateSrgs : DefaultTask() { +abstract class GenerateSrgs : DefaultTask() { - @InputFile - val configFile: RegularFileProperty = project.objects.fileProperty() - @InputFile - val methodsCsv: RegularFileProperty = project.objects.fileProperty() - @InputFile - val fieldsCsv: RegularFileProperty = project.objects.fileProperty() - @InputFile - val extraNotchSrgMappings: RegularFileProperty = project.objects.fileProperty() + @get:InputFile + abstract val methodsCsv: RegularFileProperty + @get:InputFile + abstract val fieldsCsv: RegularFileProperty + @get:Optional + @get:InputFile + abstract val extraNotchSrgMappings: RegularFileProperty - @OutputFile - val notchToSrg: RegularFileProperty = project.objects.fileProperty() - @OutputFile - val notchToMcp: RegularFileProperty = project.objects.fileProperty() - @OutputFile - val mcpToNotch: RegularFileProperty = project.objects.fileProperty() - @OutputFile - val mcpToSrg: RegularFileProperty = project.objects.fileProperty() - @OutputFile - val srgToNotch: RegularFileProperty = project.objects.fileProperty() - @OutputFile - val srgToMcp: RegularFileProperty = project.objects.fileProperty() + @get:InputFile + abstract val inSrg: RegularFileProperty + + @get:OutputFile + abstract val notchToSrg: RegularFileProperty + @get:OutputFile + abstract val notchToMcp: RegularFileProperty + @get:OutputFile + abstract val mcpToNotch: RegularFileProperty + @get:OutputFile + abstract val mcpToSrg: RegularFileProperty + @get:OutputFile + abstract val srgToNotch: RegularFileProperty + @get:OutputFile + abstract val srgToMcp: RegularFileProperty @TaskAction fun run() { - val config = mcpConfig(configFile) - val inSrg = mcpFile(configFile, config.data.mappings) - val methods = HashMap() val fields = HashMap() readCsvs(methods, fields) - val inSet = MappingFormats.TSRG.createReader(inSrg.toPath()).use { it.read() } - MappingFormats.TSRG.createReader(extraNotchSrgMappings.file.toPath()).use { it.read(inSet) } + val inSet = MappingFormats.TSRG.createReader(inSrg.file.toPath()).use { it.read() } + extraNotchSrgMappings.fileOrNull?.toPath()?.let { path -> + MappingFormats.TSRG.createReader(path).use { it.read(inSet) } + } ensureParentExists(notchToSrg, notchToMcp, mcpToNotch, mcpToSrg, srgToNotch, srgToMcp) createMappings(inSet, methods, fields) @@ -96,10 +97,12 @@ open class GenerateSrgs : DefaultTask() { private fun createMappings(inSet: MappingSet, methods: Map, fields: Map) { val notchToSrgSet = MappingSet.create() + for (mapping in inSet.topLevelClassMappings) { val newMapping = notchToSrgSet.createTopLevelClassMapping(mapping.obfuscatedName, mapping.deobfuscatedName) remapMembers(mapping, newMapping) } + handleKeywordMappings(notchToSrgSet) // We have Notch -> SRG val srgToNotchSet = notchToSrgSet.reverse() @@ -138,6 +141,39 @@ open class GenerateSrgs : DefaultTask() { ) } + private fun handleKeywordMappings(mappings: MappingSet) { + for (classMapping in mappings.topLevelClassMappings) { + handleKeywordMappingClass(classMapping) + } + } + + private fun handleKeywordMappingClass(classMapping: ClassMapping<*, *>) { + for (innerClassMapping in classMapping.innerClassMappings) { + handleKeywordMappingClass(innerClassMapping) + } + for (fieldMapping in classMapping.fieldMappings) { + if (fieldMapping.obfuscatedName in javaKeywords) { + val sourceName = fieldMapping.obfuscatedName + '_' + if (classMapping.hasFieldMapping(sourceName)) { + // If the "source name" of the mapping already exists, just skip it. I don't even know what would + // happen in this case at decompile time to be honest + continue + } + classMapping.createFieldMapping(sourceName, fieldMapping.deobfuscatedName) + } + } + for (methodMapping in classMapping.methodMappings) { + if (methodMapping.obfuscatedName in javaKeywords) { + val sourceName = methodMapping.obfuscatedName + "_" + val sourceSignature = MethodSignature(sourceName, methodMapping.signature.descriptor) + if (classMapping.hasMethodMapping(sourceSignature)) { + continue + } + classMapping.createMethodMapping(sourceSignature, methodMapping.deobfuscatedName) + } + } + } + private fun mapClass(inClass: ClassMapping<*, *>, outClass: ClassMapping<*, *>, methods: Map, fields: Map) { for (fieldMapping in inClass.fieldMappings) { val mcpName = fields[fieldMapping.deobfuscatedName] ?: fieldMapping.deobfuscatedName @@ -155,13 +191,7 @@ open class GenerateSrgs : DefaultTask() { } private fun remapAnonymousClasses(mapping: InnerClassMapping, target: ClassMapping<*, *>) { - val newMapping = if (mapping.obfuscatedName.toIntOrNull() != null) { - // This is an anonymous class, keep the index - target.createInnerClassMapping(mapping.deobfuscatedName, mapping.deobfuscatedName) - } else { - target.createInnerClassMapping(mapping.obfuscatedName, mapping.deobfuscatedName) - } - + val newMapping = target.createInnerClassMapping(mapping.obfuscatedName, mapping.deobfuscatedName) remapMembers(mapping, newMapping) } @@ -177,3 +207,56 @@ open class GenerateSrgs : DefaultTask() { } } } + +private val javaKeywords: HashSet = hashSetOf( + "abstract", + "continue", + "for", + "new", + "switch", + "assert", + "default", + "goto", + "package", + "synchronized", + "boolean", + "do", + "if", + "private", + "this", + "break", + "double", + "implements", + "protected", + "throw", + "byte", + "else", + "import", + "public", + "throws", + "case", + "enum", + "instanceof", + "return", + "transient", + "catch", + "extends", + "int", + "short", + "try", + "char", + "final", + "interface", + "static", + "void", + "class", + "finally", + "long", + "strictfp", + "volatile", + "const", + "float", + "native", + "super", + "while" +) diff --git a/src/main/kotlin/tasks/GetRemoteJsons.kt b/src/main/kotlin/tasks/GetRemoteJsons.kt deleted file mode 100644 index bfe49b9..0000000 --- a/src/main/kotlin/tasks/GetRemoteJsons.kt +++ /dev/null @@ -1,96 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. It uses - * some code and systems originally from ForgeGradle. - * - * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC - * - * 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; either - * version 2.1 of the License, or (at your option) any later version. - * - * 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 com.github.salomonbrys.kotson.array -import com.github.salomonbrys.kotson.fromJson -import com.github.salomonbrys.kotson.get -import com.github.salomonbrys.kotson.string -import com.google.gson.JsonObject -import io.papermc.paperweight.util.Constants -import io.papermc.paperweight.util.Constants.paperTaskOutput -import io.papermc.paperweight.util.cache -import io.papermc.paperweight.util.ensureParentExists -import io.papermc.paperweight.util.ext -import io.papermc.paperweight.util.file -import io.papermc.paperweight.util.getWithEtag -import io.papermc.paperweight.util.gson -import io.papermc.paperweight.util.toProvider -import org.gradle.api.DefaultTask -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.property -import util.MinecraftManifest - -open class GetRemoteJsons : DefaultTask() { - - @Input - val config: Property = project.objects.property() - - @OutputFile - val artifactOutputFile: RegularFileProperty = project.objects.run { - fileProperty().convention(project.toProvider { - project.cache.resolve(paperTaskOutput("mc-libraries", "txt")) - }) - } - - init { - outputs.upToDateWhen { false } - } - - @TaskAction - fun run() { - val cache = project.cache - val extension = project.ext - - val mcManifestJson = cache.resolve(Constants.MC_MANIFEST) - val mcVersionJson = cache.resolve(Constants.VERSION_JSON) - ensureParentExists(mcManifestJson, mcVersionJson) - - // McManifest.json - val mcManifestEtag = cache.resolve("${Constants.MC_MANIFEST}.etag") - getWithEtag(Constants.MC_MANIFEST_URL, mcManifestJson, mcManifestEtag) - val mcManifestText = gson.fromJson(mcManifestJson.readText()) - - val mcVersionEtag = mcVersionJson.resolveSibling("${mcVersionJson.name}.etag") - getWithEtag(mcManifestText.versions.first { it.id == extension.minecraftVersion.get() }.url, mcVersionJson, mcVersionEtag) - - val jsonObject = mcVersionJson.bufferedReader().use { reader -> - gson.fromJson(reader) - } - - val conf = config.get() - artifactOutputFile.file.delete() - artifactOutputFile.file.bufferedWriter().use { writer -> - for (library in jsonObject["libraries"].array) { - val artifact = library["name"].string - project.dependencies.add(conf, artifact) - writer.appendln(artifact) - } - } - } -} diff --git a/src/main/kotlin/tasks/InspectVanillaJar.kt b/src/main/kotlin/tasks/InspectVanillaJar.kt new file mode 100644 index 0000000..924c9cc --- /dev/null +++ b/src/main/kotlin/tasks/InspectVanillaJar.kt @@ -0,0 +1,109 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.defaultOutput +import io.papermc.paperweight.util.file +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.FieldVisitor +import org.objectweb.asm.Opcodes + +abstract class InspectVanillaJar : BaseTask() { + + @get:InputFile + abstract val inputJar: RegularFileProperty + + @get:OutputFile + abstract val outputFile: RegularFileProperty + + override fun init() { + outputFile.convention(defaultOutput("txt")) + } + + @TaskAction + fun run() { + val outputList = mutableListOf() + + val jarFile = inputJar.file + archives.zipTree(jarFile).matching { + include("/*.class") + include("/net/minecraft/**/*.class") + }.forEach { file -> + if (file.isDirectory) { + return@forEach + } + val classData = file.readBytes() + + val reader = ClassReader(classData) + reader.accept(LoggerFinder(outputList), 0) + + } + + outputFile.file.bufferedWriter(Charsets.UTF_8).use { writer -> + for (loggerField in outputList) { + writer.append(loggerField.className) + writer.append(' ') + writer.append(loggerField.fieldName) + writer.newLine() + } + } + } +} + +class LoggerFinder(private val fields: MutableList) : ClassVisitor(Opcodes.ASM8) { + + private var currentClass: String? = null + + override fun visit( + version: Int, + access: Int, + name: String, + signature: String?, + superName: String?, + interfaces: Array? + ) { + this.currentClass = name + } + + override fun visitField( + access: Int, + name: String, + descriptor: String, + signature: String?, + value: Any? + ): FieldVisitor? { + val className = currentClass ?: return null + if (descriptor != "Lorg/apache/logging/log4j/Logger;") { + return null + } + fields += LoggerField(className, name) + return null + } +} + +data class LoggerField(val className: String, val fieldName: String) diff --git a/src/main/kotlin/tasks/RemapVanillaJarSrg.kt b/src/main/kotlin/tasks/MergeAccessTransforms.kt similarity index 52% rename from src/main/kotlin/tasks/RemapVanillaJarSrg.kt rename to src/main/kotlin/tasks/MergeAccessTransforms.kt index 081eaca..fad7690 100644 --- a/src/main/kotlin/tasks/RemapVanillaJarSrg.kt +++ b/src/main/kotlin/tasks/MergeAccessTransforms.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,42 +23,38 @@ package io.papermc.paperweight.tasks import io.papermc.paperweight.util.defaultOutput -import io.papermc.paperweight.util.ensureDeleted -import io.papermc.paperweight.util.ensureParentExists import io.papermc.paperweight.util.file -import org.cadixdev.atlas.Atlas -import org.cadixdev.bombe.asm.jar.JarEntryRemappingTransformer -import org.cadixdev.lorenz.asm.LorenzRemapper -import org.cadixdev.lorenz.io.MappingFormats -import org.gradle.api.DefaultTask +import org.cadixdev.at.AccessTransformSet +import org.cadixdev.at.io.AccessTransformFormats +import org.gradle.api.file.RegularFile import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.InputFile +import org.gradle.api.provider.ListProperty +import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction -open class RemapVanillaJarSrg : DefaultTask() { +abstract class MergeAccessTransforms : BaseTask() { - @InputFile - val inputJar: RegularFileProperty = project.objects.fileProperty() + @get:InputFiles + abstract val inputFiles: ListProperty - @InputFile - val mappings: RegularFileProperty = project.objects.fileProperty() + @get:OutputFile + abstract val outputFile: RegularFileProperty - @OutputFile - val outputJar: RegularFileProperty = defaultOutput() + override fun init() { + outputFile.convention(defaultOutput("at")) + } @TaskAction fun run() { - ensureParentExists(outputJar.file) - ensureDeleted(outputJar.file) + val ats = inputFiles.get() + .map { AccessTransformFormats.FML.read(it.asFile.toPath()) } - val mappings = MappingFormats.TSRG.createReader(mappings.file.toPath()).use { it.read() } - Atlas().apply { - install { ctx -> - JarEntryRemappingTransformer(LorenzRemapper(mappings, ctx.inheritanceProvider())) - } - run(inputJar.file.toPath(), outputJar.file.toPath()) + val outputAt = AccessTransformSet.create() + for (at in ats) { + outputAt.merge(at) } + AccessTransformFormats.FML.write(outputFile.file.toPath(), outputAt) } } diff --git a/src/main/kotlin/tasks/PatchMcpCsv.kt b/src/main/kotlin/tasks/PatchMcpCsv.kt index 8cd7b49..fd7f2e6 100644 --- a/src/main/kotlin/tasks/PatchMcpCsv.kt +++ b/src/main/kotlin/tasks/PatchMcpCsv.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -31,23 +30,23 @@ import org.gradle.api.tasks.TaskAction import java.io.PrintWriter import java.util.regex.Pattern -open class PatchMcpCsv : DefaultTask() { +abstract class PatchMcpCsv : DefaultTask() { - @InputFile - val fieldsCsv: RegularFileProperty = project.objects.fileProperty() - @InputFile - val methodsCsv: RegularFileProperty = project.objects.fileProperty() - @InputFile - val paramsCsv: RegularFileProperty = project.objects.fileProperty() - @InputFile - val changesFile: RegularFileProperty = project.objects.fileProperty() + @get:InputFile + abstract val fieldsCsv: RegularFileProperty + @get:InputFile + abstract val methodsCsv: RegularFileProperty + @get:InputFile + abstract val paramsCsv: RegularFileProperty + @get:InputFile + abstract val changesFile: RegularFileProperty - @OutputFile - val paperFieldCsv: RegularFileProperty = project.objects.fileProperty() - @OutputFile - val paperMethodCsv: RegularFileProperty = project.objects.fileProperty() - @OutputFile - val paperParamCsv: RegularFileProperty = project.objects.fileProperty() + @get:OutputFile + abstract val paperFieldCsv: RegularFileProperty + @get:OutputFile + abstract val paperMethodCsv: RegularFileProperty + @get:OutputFile + abstract val paperParamCsv: RegularFileProperty @TaskAction fun run() { diff --git a/src/main/kotlin/tasks/SetupMcpDependencies.kt b/src/main/kotlin/tasks/RemapAccessTransform.kt similarity index 56% rename from src/main/kotlin/tasks/SetupMcpDependencies.kt rename to src/main/kotlin/tasks/RemapAccessTransform.kt index 4627601..ac480fb 100644 --- a/src/main/kotlin/tasks/SetupMcpDependencies.kt +++ b/src/main/kotlin/tasks/RemapAccessTransform.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,33 +22,35 @@ package io.papermc.paperweight.tasks -import io.papermc.paperweight.util.mcpConfig -import org.gradle.api.DefaultTask +import io.papermc.paperweight.util.defaultOutput +import io.papermc.paperweight.util.file +import org.cadixdev.at.io.AccessTransformFormats +import org.cadixdev.lorenz.io.MappingFormats import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.property -open class SetupMcpDependencies : DefaultTask() { +abstract class RemapAccessTransform : BaseTask() { - @InputFile - val configFile: RegularFileProperty = project.objects.fileProperty() - @Input - val forgeFlowerConfig: Property = project.objects.property() - @Input - val mcInjectorConfig: Property = project.objects.property() + @get:InputFile + abstract val inputFile: RegularFileProperty + @get:InputFile + abstract val mappings: RegularFileProperty - init { - outputs.upToDateWhen { false } + @get:OutputFile + abstract val outputFile: RegularFileProperty + + override fun init() { + outputFile.convention(defaultOutput("at")) } @TaskAction fun run() { - val config = mcpConfig(configFile) + val at = AccessTransformFormats.FML.read(inputFile.file.toPath()) + val mappingSet = MappingFormats.TSRG.createReader(mappings.file.toPath()).use { it.read() } - project.dependencies.add(forgeFlowerConfig.get(), config.functions.getValue("decompile").version) - project.dependencies.add(mcInjectorConfig.get(), config.functions.getValue("mcinject").version) + val resultAt = at.remap(mappingSet) + AccessTransformFormats.FML.write(outputFile.file.toPath(), resultAt) } } diff --git a/src/main/kotlin/tasks/RemapSources.kt b/src/main/kotlin/tasks/RemapSources.kt deleted file mode 100644 index 2abf631..0000000 --- a/src/main/kotlin/tasks/RemapSources.kt +++ /dev/null @@ -1,267 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. It uses - * some code and systems originally from ForgeGradle. - * - * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC - * - * 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; either - * version 2.1 of the License, or (at your option) any later version. - * - * 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.PaperweightException -import io.papermc.paperweight.util.defaultOutput -import io.papermc.paperweight.util.file -import io.papermc.paperweight.util.mcpConfig -import io.papermc.paperweight.util.mcpFile -import org.cadixdev.at.AccessTransformSet -import org.cadixdev.at.io.AccessTransformFormats -import org.cadixdev.lorenz.io.MappingFormats -import org.cadixdev.mercury.Mercury -import org.cadixdev.mercury.RewriteContext -import org.cadixdev.mercury.SourceProcessor -import org.cadixdev.mercury.SourceRewriter -import org.cadixdev.mercury.at.AccessTransformerRewriter -import org.cadixdev.mercury.extra.AccessAnalyzerProcessor -import org.cadixdev.mercury.extra.BridgeMethodRewriter -import org.cadixdev.mercury.remapper.MercuryRemapper -import org.cadixdev.mercury.util.BombeBindings -import org.eclipse.jdt.core.dom.ASTVisitor -import org.eclipse.jdt.core.dom.IMethodBinding -import org.eclipse.jdt.core.dom.IVariableBinding -import org.eclipse.jdt.core.dom.MethodDeclaration -import org.eclipse.jdt.core.dom.Modifier -import org.eclipse.jdt.core.dom.SimpleName -import org.eclipse.jdt.core.dom.VariableDeclaration -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.OutputFile -import org.gradle.kotlin.dsl.get -import org.gradle.kotlin.dsl.property -import java.io.File - -open class RemapSources : ZippedTask() { - - @InputFile - val vanillaJar: RegularFileProperty = project.objects.fileProperty() - @InputFile - val vanillaRemappedSpigotJar: RegularFileProperty = project.objects.fileProperty() // Required for pre-remap pass - @InputFile - val vanillaRemappedSrgJar: RegularFileProperty = project.objects.fileProperty() // Required for post-remap pass - @InputFile - val mappings: RegularFileProperty = project.objects.fileProperty() - @Input - val configuration: Property = project.objects.property() - @InputFile - val configFile: RegularFileProperty = project.objects.fileProperty() - @InputDirectory - val spigotServerDir: DirectoryProperty = project.objects.directoryProperty() - @InputDirectory - val spigotApiDir: DirectoryProperty = project.objects.directoryProperty() - - @OutputFile - val generatedAt: RegularFileProperty = defaultOutput("at") - - override fun run(rootDir: File) { - val config = mcpConfig(configFile) - val constructors = mcpFile(configFile, config.data.constructors) - - val mappings = MappingFormats.TSRG.createReader(mappings.file.toPath()).use { it.read() } - - val srcDir = spigotServerDir.file.resolve("src/main/java") - val pass1Dir = rootDir.resolve("pass1") - val outDir = rootDir.resolve("out") - - val configuration = project.configurations[configuration.get()] - - val ats = AccessTransformSet.create() - - // Remap any references Spigot maps to SRG - Mercury().apply { - classPath.addAll(listOf( - vanillaJar.asFile.get().toPath(), - vanillaRemappedSpigotJar.asFile.get().toPath(), - spigotApiDir.file.resolve("src/main/java").toPath() - )) - - for (file in configuration.resolvedConfiguration.files) { - classPath.add(file.toPath()) - } - - // Generate AT - processors.add(AccessAnalyzerProcessor.create(ats, mappings)) - process(srcDir.toPath()) - - // Remap - processors.clear() - processors.addAll(listOf( - MercuryRemapper.create(mappings), - BridgeMethodRewriter.create(), - AccessTransformerRewriter.create(ats) - )) - - rewrite(srcDir.toPath(), pass1Dir.toPath()) - } - - Mercury().apply { - // Remap SRG parameters - classPath.addAll(listOf( - vanillaJar.asFile.get().toPath(), - vanillaRemappedSrgJar.asFile.get().toPath(), - spigotApiDir.file.resolve("src/main/java").toPath() - )) - - processors.add(SrgParameterRemapper(constructors)) - rewrite(pass1Dir.toPath(), outDir.toPath()) - } - - // Only leave remapped source - val fileList = rootDir.listFiles() ?: throw PaperweightException("Could not list files in $rootDir") - for (file in fileList) { - if (file != outDir) { - file.deleteRecursively() - } - } - - // Move out/* to base dir - val files = outDir.listFiles() ?: throw PaperweightException("No files found in $outDir") - for (file in files) { - file.renameTo(rootDir.resolve(file.name)) - } - outDir.delete() - - // And write generated AT file - AccessTransformFormats.FML.write(generatedAt.asFile.get().toPath(), ats) - } -} - -class SrgParameterRemapper(constructors: File) : SourceRewriter { - - private val constructorMap = hashMapOf>() - - init { - constructors.useLines { lines -> - lines.forEach { line -> - val parts = line.split(' ') - constructorMap.compute(parts[1].replace('/', '.')) { _, v -> - val node = ConstructorNode(parts[0].toInt(), parts[2]) - if (v == null) { - return@compute mutableListOf(node) - } else { - v += node - return@compute v - } - } - } - } - - for (list in constructorMap.values) { - // Put bigger numbers first - // Old constructor entries are still present, just with smaller numbers. So we don't want to grab an older - // entry - list.reverse() - } - } - - override fun getFlags(): Int = SourceProcessor.FLAG_RESOLVE_BINDINGS - - override fun rewrite(context: RewriteContext) { - context.compilationUnit.accept(SrgParameterVisitor(context, constructorMap)) - } -} - -data class ConstructorNode( - val id: Int, - val signature: String -) - -class SrgParameterVisitor( - private val context: RewriteContext, - private val constructors: Map> -) : ASTVisitor() { - - companion object { - private val MATCHER = Regex("func_(\\d+)_.*") - } - - override fun visit(node: SimpleName): Boolean { - val binding = node.resolveBinding() as? IVariableBinding ?: return false - if (!binding.isParameter) { - return false - } - - val variableDecl = context.compilationUnit.findDeclaringNode(binding.variableDeclaration) as VariableDeclaration - - val method = binding.declaringMethod - val methodDecl = context.compilationUnit.findDeclaringNode(method) as? MethodDeclaration ?: return false - - handleMethod(node, methodDecl, method, variableDecl) - - return false - } - - private fun handleMethod(node: SimpleName, methodDecl: MethodDeclaration, method: IMethodBinding, variableDecl: VariableDeclaration) { - if (method.isConstructor) { - handleConstructor(node, methodDecl, method, variableDecl) - return - } - - val match = MATCHER.matchEntire(method.name) ?: return - val isStatic = method.modifiers and Modifier.STATIC != 0 - - var index = getParameterIndex(methodDecl, variableDecl) - if (index == -1) { - return - } - if (!isStatic) { - index++ - } - - val paramName = "p_${match.groupValues[1]}_${index}_" - context.createASTRewrite().set(node, SimpleName.IDENTIFIER_PROPERTY, paramName, null) - } - - private fun handleConstructor(node: SimpleName, methodDecl: MethodDeclaration, method: IMethodBinding, variableDecl: VariableDeclaration) { - val constructorNodes = constructors[method.declaringClass.binaryName] ?: return - - val constructorNode = constructorNodes.firstOrNull { constructorNode -> - constructorNode.signature == BombeBindings.convertSignature(method).descriptor.toString() - } ?: return - - val id = constructorNode.id - - var index = getParameterIndex(methodDecl, variableDecl) - if (index == -1) { - return - } - // Constructors are never static - index++ - - val paramName = "p_i${id}_${index}_" - context.createASTRewrite().set(node, SimpleName.IDENTIFIER_PROPERTY, paramName, null) - } - - private fun getParameterIndex(methodDecl: MethodDeclaration, decl: VariableDeclaration): Int { - @Suppress("UNCHECKED_CAST") - val params = methodDecl.parameters() as List - return params.indexOfFirst { it === decl } - } -} diff --git a/src/main/kotlin/tasks/RemapSpigotAt.kt b/src/main/kotlin/tasks/RemapSpigotAt.kt new file mode 100644 index 0000000..84b21ba --- /dev/null +++ b/src/main/kotlin/tasks/RemapSpigotAt.kt @@ -0,0 +1,135 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.defaultOutput +import io.papermc.paperweight.util.file +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.AccessTransformFormats +import org.cadixdev.bombe.type.MethodDescriptor +import org.cadixdev.bombe.type.signature.MethodSignature +import org.cadixdev.lorenz.io.MappingFormats +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import java.util.jar.JarFile + +abstract class RemapSpigotAt : BaseTask() { + + @get:InputFile + abstract val inputJar: RegularFileProperty + @get:InputFile + abstract val spigotAt: RegularFileProperty + @get:InputFile + abstract val mapping: RegularFileProperty + + @get:OutputFile + abstract val outputFile: RegularFileProperty + + override fun init() { + outputFile.convention(defaultOutput("at")) + } + + @TaskAction + fun run() { + val outputAt = AccessTransformSet.create() + + spigotAt.file.useLines { lines -> + JarFile(inputJar.file).use { jarFile -> + for (line in lines) { + if (line.isBlank() || line.startsWith('#')) { + continue + } + + val (access, desc) = line.split(' ') + + if (desc.contains('(')) { + // method + val index = desc.indexOf('(') + val methodDesc = desc.substring(index) + val classAndMethodName = desc.substring(0, index) + val slashIndex = classAndMethodName.lastIndexOf('/') + val className = classAndMethodName.substring(0, slashIndex) + val methodName = classAndMethodName.substring(slashIndex + 1) + + outputAt.getOrCreateClass(className).replaceMethod( + MethodSignature(methodName, MethodDescriptor.of(methodDesc)), + parseAccess(access) + ) + } else { + // either field or class + if (jarFile.getJarEntry("$desc.class") == null) { + // field + val index = desc.lastIndexOf('/') + val className = desc.substring(0, index) + val fieldName = desc.substring(index + 1) + outputAt.getOrCreateClass(className).replaceField(fieldName, parseAccess(access)) + } else { + // class + outputAt.getOrCreateClass(desc).replace(parseAccess(access)) + } + } + } + } + } + + val mappings = MappingFormats.TSRG.createReader(mapping.file.toPath()).use { it.read() } + val remappedAt = outputAt.remap(mappings) + + AccessTransformFormats.FML.write(outputFile.file.toPath(), remappedAt) + } + + private fun parseAccess(text: String): AccessTransform { + val index = text.indexOfAny(charArrayOf('+', '-')) + return if (index == -1) { + // only access + AccessTransform.of(parseAccessChange(text)) + } else { + val accessChange = parseAccessChange(text.substring(0, index)) + val modifierChange = parseModifierChange(text[index]) + AccessTransform.of(accessChange, modifierChange) + } + } + + private fun parseAccessChange(text: String): AccessChange { + return when (text) { + "public" -> AccessChange.PUBLIC + "private" -> AccessChange.PRIVATE + "protected" -> AccessChange.PROTECTED + "default" -> AccessChange.PACKAGE_PRIVATE + else -> AccessChange.NONE + } + } + + private fun parseModifierChange(c: Char): ModifierChange { + return when (c) { + '+' -> ModifierChange.ADD + '-' -> ModifierChange.REMOVE + else -> ModifierChange.NONE + } + } +} diff --git a/src/main/kotlin/tasks/RemapSrgSources.kt b/src/main/kotlin/tasks/RemapSrgSources.kt index e5505d7..cd24522 100644 --- a/src/main/kotlin/tasks/RemapSrgSources.kt +++ b/src/main/kotlin/tasks/RemapSrgSources.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -32,14 +31,14 @@ import java.io.BufferedWriter import java.io.File import java.util.regex.Pattern -open class RemapSrgSources : ZippedTask() { +abstract class RemapSrgSources : ZippedTask() { - @InputFile - val methodsCsv: RegularFileProperty = project.objects.fileProperty() - @InputFile - val fieldsCsv: RegularFileProperty = project.objects.fileProperty() - @InputFile - val paramsCsv: RegularFileProperty = project.objects.fileProperty() + @get:InputFile + abstract val methodsCsv: RegularFileProperty + @get:InputFile + abstract val fieldsCsv: RegularFileProperty + @get:InputFile + abstract val paramsCsv: RegularFileProperty private val methods = hashMapOf() private val methodDocs = hashMapOf() diff --git a/src/main/kotlin/tasks/RemapVanillaJarSpigot.kt b/src/main/kotlin/tasks/RemapVanillaJarSpigot.kt index c232c63..984f641 100644 --- a/src/main/kotlin/tasks/RemapVanillaJarSpigot.kt +++ b/src/main/kotlin/tasks/RemapVanillaJarSpigot.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -28,52 +27,44 @@ import io.papermc.paperweight.util.cache import io.papermc.paperweight.util.defaultOutput import io.papermc.paperweight.util.runJar import io.papermc.paperweight.util.wrapException -import org.gradle.api.DefaultTask import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.property -open class RemapVanillaJarSpigot : DefaultTask() { +abstract class RemapVanillaJarSpigot : BaseTask() { - @InputFile - val inputJar: RegularFileProperty = project.objects.fileProperty() + @get:InputFile + abstract val inputJar: RegularFileProperty + @get:InputFile + abstract val classMappings: RegularFileProperty + @get:InputFile + abstract val memberMappings: RegularFileProperty + @get:InputFile + abstract val packageMappings: RegularFileProperty + @get:InputFile + abstract val accessTransformers: RegularFileProperty + @get:Input + abstract val workDirName: Property + @get:InputFile + abstract val specialSourceJar: RegularFileProperty + @get:InputFile + abstract val specialSource2Jar: RegularFileProperty + @get:Input + abstract val classMapCommand: Property + @get:Input + abstract val memberMapCommand: Property + @get:Input + abstract val finalMapCommand: Property - @InputFile - val classMappings: RegularFileProperty = project.objects.fileProperty() + @get:OutputFile + abstract val outputJar: RegularFileProperty - @InputFile - val memberMappings: RegularFileProperty = project.objects.fileProperty() - - @InputFile - val packageMappings: RegularFileProperty = project.objects.fileProperty() - - @InputFile - val accessTransformers: RegularFileProperty = project.objects.fileProperty() - - @Input - val workDirName: Property = project.objects.property() - - @InputFile - val specialSourceJar: RegularFileProperty = project.objects.fileProperty() - - @InputFile - val specialSource2Jar: RegularFileProperty = project.objects.fileProperty() - - @Input - val classMapCommand: Property = project.objects.property() - - @Input - val memberMapCommand: Property = project.objects.property() - - @Input - val finalMapCommand: Property = project.objects.property() - - @OutputFile - val outputJar: RegularFileProperty = defaultOutput() + override fun init() { + outputJar.convention(defaultOutput()) + } @TaskAction fun run() { @@ -92,14 +83,15 @@ open class RemapVanillaJarSpigot : DefaultTask() { val packageMappingsPath = packageMappings.asFile.get().canonicalPath val accessTransformersPath = accessTransformers.asFile.get().canonicalPath + val work = layout.projectDirectory.file(workDirName.get()) + try { - println("Applying class mappings...") wrapException("Failed to apply class mappings") { - val logFile = project.cache.resolve(paperTaskOutput("class.log")) + val logFile = layout.cache.resolve(paperTaskOutput("class.log")) logFile.delete() runJar( specialSource2Jar, - workingDir = workDirName.get(), + workingDir = work, logFile = logFile, args = *doReplacements(classMapCommand.get(), inputJarPath, classMappingPath, classJarPath) { // ignore excludes, we actually want to map every class @@ -107,24 +99,22 @@ open class RemapVanillaJarSpigot : DefaultTask() { } ) } - println("Applying member mappings...") wrapException("Failed to apply member mappings") { - val logFile = project.cache.resolve(paperTaskOutput("member.log")) + val logFile = layout.cache.resolve(paperTaskOutput("member.log")) logFile.delete() runJar( specialSource2Jar, - workingDir = workDirName.get(), + workingDir = work, logFile = logFile, args = *doReplacements(memberMapCommand.get(), classJarPath, memberMappingsPath, membersJarPath) ) } - println("Creating remapped jar...") wrapException("Failed to create remapped jar") { - val logFile = project.cache.resolve(paperTaskOutput("final.log")) + val logFile = layout.cache.resolve(paperTaskOutput("final.log")) logFile.delete() runJar( specialSourceJar, - workingDir = workDirName.get(), + workingDir = work, logFile = logFile, args = *doReplacements(finalMapCommand.get(), membersJarPath, accessTransformersPath, packageMappingsPath, outputJarPath) ) diff --git a/src/main/kotlin/tasks/RunForgeFlower.kt b/src/main/kotlin/tasks/RunForgeFlower.kt index 0ac48b2..84b2282 100644 --- a/src/main/kotlin/tasks/RunForgeFlower.kt +++ b/src/main/kotlin/tasks/RunForgeFlower.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,57 +23,67 @@ package io.papermc.paperweight.tasks import io.papermc.paperweight.util.Constants.paperTaskOutput +import io.papermc.paperweight.util.McpConfig import io.papermc.paperweight.util.cache +import io.papermc.paperweight.util.decompile import io.papermc.paperweight.util.defaultOutput import io.papermc.paperweight.util.file -import io.papermc.paperweight.util.mcpConfig +import io.papermc.paperweight.util.fromJson +import io.papermc.paperweight.util.gson import io.papermc.paperweight.util.runJar -import org.gradle.api.DefaultTask import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.get -import org.gradle.kotlin.dsl.property -open class RunForgeFlower : DefaultTask() { +abstract class RunForgeFlower : BaseTask() { - @Input - val configuration: Property = project.objects.property() + @get:InputFile + abstract val configFile: RegularFileProperty + @get:InputFile + abstract val executable: RegularFileProperty - @InputFile - val inputJar: RegularFileProperty = project.objects.fileProperty() - @InputFile - val libraries: RegularFileProperty = project.objects.fileProperty() + @get:InputFile + abstract val inputJar: RegularFileProperty + @get:InputFile + abstract val libraries: RegularFileProperty - @InputFile - val configFile: RegularFileProperty = project.objects.fileProperty() + @get:OutputFile + abstract val outputJar: RegularFileProperty - @OutputFile - val outputJar: RegularFileProperty = defaultOutput() + override fun init() { + outputJar.convention(defaultOutput()) + } @TaskAction fun run() { - val config = mcpConfig(configFile) + val out = outputJar.file + val target = out.resolveSibling("${out.name}.dir") + if (target.exists()) { + target.deleteRecursively() + } + target.mkdirs() - val forgeFlowerArgs = config.functions.getValue("decompile").args - val jvmArgs = config.functions.getValue("decompile").jvmargs ?: listOf() + val config = gson.fromJson(configFile) - val argList = forgeFlowerArgs.map { + val argList = config.functions.decompile.args.map { when (it) { "{libraries}" -> libraries.file.absolutePath "{input}" -> inputJar.file.absolutePath - "{output}" -> outputJar.file.absolutePath + "{output}" -> target.absolutePath else -> it } } - val logFile = project.cache.resolve(paperTaskOutput("log")) + val logFile = layout.cache.resolve(paperTaskOutput("log")) logFile.delete() - val forgeFlowerJar = project.configurations[configuration.get()].resolve().single() - runJar(forgeFlowerJar, project.cache, logFile, jvmArgs = jvmArgs, args = *argList.toTypedArray()) + val jvmArgs = config.functions.decompile.jvmargs ?: listOf() + + runJar(executable.file, layout.cache, logFile, jvmArgs = jvmArgs, args = *argList.toTypedArray()) + + // FernFlower is weird with how it does directory output + target.resolve(inputJar.file.name).renameTo(out) + target.deleteRecursively() } } diff --git a/src/main/kotlin/tasks/RunMcInjector.kt b/src/main/kotlin/tasks/RunMcInjector.kt index bb04d9e..f3484c4 100644 --- a/src/main/kotlin/tasks/RunMcInjector.kt +++ b/src/main/kotlin/tasks/RunMcInjector.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,60 +22,65 @@ package io.papermc.paperweight.tasks +import io.papermc.paperweight.util.McpConfig import io.papermc.paperweight.util.cache import io.papermc.paperweight.util.defaultOutput import io.papermc.paperweight.util.file -import io.papermc.paperweight.util.mcpConfig -import io.papermc.paperweight.util.mcpFile +import io.papermc.paperweight.util.fromJson +import io.papermc.paperweight.util.gson +import io.papermc.paperweight.util.mcinject import io.papermc.paperweight.util.runJar -import org.gradle.api.DefaultTask import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.get -import org.gradle.kotlin.dsl.property -open class RunMcInjector : DefaultTask() { +abstract class RunMcInjector : BaseTask() { - @Input - val configuration: Property = project.objects.property() - @InputFile - val inputJar: RegularFileProperty = project.objects.fileProperty() - @InputFile - val configFile: RegularFileProperty = project.objects.fileProperty() + @get:InputFile + abstract val configFile: RegularFileProperty + @get:InputFile + abstract val executable: RegularFileProperty - @OutputFile - val outputJar: RegularFileProperty = defaultOutput() - @OutputFile - val logFile: RegularFileProperty = defaultOutput("log") + @get:InputFile + abstract val exceptions: RegularFileProperty + @get:InputFile + abstract val access: RegularFileProperty + @get:InputFile + abstract val constructors: RegularFileProperty + + @get:InputFile + abstract val inputJar: RegularFileProperty + + @get:OutputFile + abstract val outputJar: RegularFileProperty + @get:Internal + abstract val logFile: RegularFileProperty + + override fun init() { + outputJar.convention(defaultOutput()) + logFile.convention(defaultOutput("log")) + } @TaskAction fun run() { - val config = mcpConfig(configFile) - val exceptions = mcpFile(configFile, config.data.exceptions) - val access = mcpFile(configFile, config.data.access) - val constructors = mcpFile(configFile, config.data.constructors) + val config = gson.fromJson(configFile) - val mcInjectorArgs = config.functions.getValue("mcinject").args - val jvmArgs = config.functions.getValue("mcinject").jvmargs ?: listOf() - - val argList = mcInjectorArgs.map { + val argList = config.functions.mcinject.args.map { when (it) { "{input}" -> inputJar.file.absolutePath "{output}" -> outputJar.file.absolutePath "{log}" -> logFile.file.absolutePath - "{exceptions}" -> exceptions.absolutePath - "{access}" -> access.absolutePath - "{constructors}" -> constructors.absolutePath + "{exceptions}" -> exceptions.file.absolutePath + "{access}" -> access.file.absolutePath + "{constructors}" -> constructors.file.absolutePath else -> it } } - val mcInjectorJar = project.configurations[configuration.get()].resolve().single() + val jvmArgs = config.functions.mcinject.jvmargs ?: listOf() - runJar(mcInjectorJar, project.cache, logFile = null, jvmArgs = jvmArgs, args = *argList.toTypedArray()) + runJar(executable, layout.cache, logFile = null, jvmArgs = jvmArgs, args = *argList.toTypedArray()) } } diff --git a/src/main/kotlin/tasks/RunSpecialSource.kt b/src/main/kotlin/tasks/RunSpecialSource.kt new file mode 100644 index 0000000..1726b82 --- /dev/null +++ b/src/main/kotlin/tasks/RunSpecialSource.kt @@ -0,0 +1,84 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.Constants.paperTaskOutput +import io.papermc.paperweight.util.McpConfig +import io.papermc.paperweight.util.cache +import io.papermc.paperweight.util.defaultOutput +import io.papermc.paperweight.util.ensureDeleted +import io.papermc.paperweight.util.ensureParentExists +import io.papermc.paperweight.util.file +import io.papermc.paperweight.util.fromJson +import io.papermc.paperweight.util.gson +import io.papermc.paperweight.util.rename +import io.papermc.paperweight.util.runJar +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction + +abstract class RunSpecialSource : BaseTask() { + + @get:InputFile + abstract val inputJar: RegularFileProperty + @get:InputFile + abstract val mappings: RegularFileProperty + + @get:InputFile + abstract val configFile: RegularFileProperty + @get:InputFile + abstract val executable: RegularFileProperty + + @get:OutputFile + abstract val outputJar: RegularFileProperty + + override fun init() { + outputJar.convention(defaultOutput()) + } + + @TaskAction + fun run() { + val out = outputJar.file + ensureParentExists(out) + ensureDeleted(out) + + val config = gson.fromJson(configFile) + + val argList = config.functions.rename.args.map { + when (it) { + "{input}" -> inputJar.file.absolutePath + "{output}" -> outputJar.file.absolutePath + "{mappings}" -> mappings.file.absolutePath + else -> it + } + } + + val jvmArgs = config.functions.rename.jvmargs ?: listOf() + + val logFile = layout.cache.resolve(paperTaskOutput("log")) + logFile.delete() + + runJar(executable.file, layout.cache, logFile, jvmArgs = jvmArgs, args = *argList.toTypedArray()) + } +} diff --git a/src/main/kotlin/tasks/GatherBuildData.kt b/src/main/kotlin/tasks/SetupMcLibraries.kt similarity index 65% rename from src/main/kotlin/tasks/GatherBuildData.kt rename to src/main/kotlin/tasks/SetupMcLibraries.kt index 553edd2..ecb9b0d 100644 --- a/src/main/kotlin/tasks/GatherBuildData.kt +++ b/src/main/kotlin/tasks/SetupMcLibraries.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,22 +22,30 @@ package io.papermc.paperweight.tasks -import com.github.salomonbrys.kotson.fromJson -import io.papermc.paperweight.util.gson +import io.papermc.paperweight.util.file import org.gradle.api.DefaultTask import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Provider +import org.gradle.api.provider.ListProperty +import org.gradle.api.tasks.Input import org.gradle.api.tasks.OutputFile -import util.BuildDataInfo +import org.gradle.api.tasks.TaskAction -open class GatherBuildData : DefaultTask() { +abstract class SetupMcLibraries : DefaultTask() { - @OutputFile - val buildDataInfoFile: RegularFileProperty = project.objects.fileProperty() + @get:Input + abstract val dependencies: ListProperty - val buildDataInfo: Provider = buildDataInfoFile.map { - it.asFile.bufferedReader().use { reader -> - gson.fromJson(reader) + @get:OutputFile + abstract val outputFile: RegularFileProperty + + @TaskAction + fun run() { + val list = dependencies.get().sorted() + + outputFile.file.bufferedWriter().use { writer -> + for (line in list) { + writer.appendln(line) + } } } } diff --git a/src/main/kotlin/tasks/SetupSpigotDependencies.kt b/src/main/kotlin/tasks/SetupSpigotDependencies.kt deleted file mode 100644 index 60ce187..0000000 --- a/src/main/kotlin/tasks/SetupSpigotDependencies.kt +++ /dev/null @@ -1,117 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. It uses - * some code and systems originally from ForgeGradle. - * - * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC - * - * 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; either - * version 2.1 of the License, or (at your option) any later version. - * - * 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.file -import org.gradle.api.DefaultTask -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.property -import org.w3c.dom.Element -import java.io.File -import javax.xml.parsers.DocumentBuilderFactory - -open class SetupSpigotDependencies : DefaultTask() { - - @InputDirectory - val spigotApi: DirectoryProperty = project.objects.directoryProperty() - @InputDirectory - val spigotServer: DirectoryProperty = project.objects.directoryProperty() - - @Input - val configurationName: Property = project.objects.property() - - init { - // we set dependencies here, can't cache this - outputs.upToDateWhen { false } - } - - @TaskAction - fun run() { - val apiDeps = parsePom(spigotApi.file.resolve("pom.xml")) - val serverDeps = parsePom(spigotServer.file.resolve("pom.xml")) - - for (dep in apiDeps) { - project.dependencies.add(configurationName.get(), dep) - } - for (dep in serverDeps) { - project.dependencies.add(configurationName.get(), dep) - } - } - - private fun parsePom(pomFile: File): List { - val depList = arrayListOf() - - val builder = DocumentBuilderFactory.newInstance().newDocumentBuilder() - val doc = pomFile.inputStream().buffered().use { stream -> - stream.buffered().use { buffered -> - builder.parse(buffered) - } - } - - doc.documentElement.normalize() - - val list = doc.getElementsByTagName("dependencies") - for (i in 0 until list.length) { - val node = list.item(i) as? Element ?: continue - - val depNode = node.getElementsByTagName("dependency") - for (j in 0 until depNode.length) { - val dependency = depNode.item(j) as? Element ?: continue - val text = getDependency(dependency) ?: continue - depList += text - } - } - - return depList - } - - private fun getDependency(node: Element): String? { - val scopeNode = node.getElementsByTagName("scope") - val scope = if (scopeNode.length == 0) { - "compile" - } else { - scopeNode.item(0).textContent - } - - if (scope != "compile") { - return null - } - - val group = node.getElementsByTagName("groupId").item(0).textContent - val artifact = node.getElementsByTagName("artifactId").item(0).textContent - val version = node.getElementsByTagName("version").item(0).textContent - - if (version.contains("\${")) { - // Don't handle complicated things - // We don't need to (for now anyways) - return null - } - - return "$group:$artifact:$version" - } -} diff --git a/src/main/kotlin/tasks/WriteLibrariesFile.kt b/src/main/kotlin/tasks/WriteLibrariesFile.kt index ec4a058..ad541c7 100644 --- a/src/main/kotlin/tasks/WriteLibrariesFile.kt +++ b/src/main/kotlin/tasks/WriteLibrariesFile.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,32 +22,31 @@ package io.papermc.paperweight.tasks +import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.util.defaultOutput import io.papermc.paperweight.util.file -import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Classpath import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.get -import org.gradle.kotlin.dsl.property -open class WriteLibrariesFile : DefaultTask() { +abstract class WriteLibrariesFile : BaseTask() { - @Input - val config: Property = project.objects.property() + @get:Classpath + abstract val libraries: DirectoryProperty - @OutputFile - val outputFile: RegularFileProperty = defaultOutput("txt") + @get:OutputFile + abstract val outputFile: RegularFileProperty - init { - outputs.upToDateWhen { false } + override fun init() { + outputFile.convention(defaultOutput("txt")) } @TaskAction fun run() { - val files = project.configurations[config.get()].resolve().sorted() + val files = libraries.file.listFiles()?.sorted() + ?: throw PaperweightException("Libraries directory does not exist") outputFile.file.delete() outputFile.file.bufferedWriter().use { writer -> diff --git a/src/main/kotlin/tasks/ZippedTask.kt b/src/main/kotlin/tasks/ZippedTask.kt index 416c9e2..6de5b55 100644 --- a/src/main/kotlin/tasks/ZippedTask.kt +++ b/src/main/kotlin/tasks/ZippedTask.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -29,7 +28,6 @@ import io.papermc.paperweight.util.file import io.papermc.paperweight.util.fileOrNull import io.papermc.paperweight.util.unzip import io.papermc.paperweight.util.zip -import org.gradle.api.DefaultTask import org.gradle.api.file.RegularFileProperty import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.Optional @@ -38,17 +36,21 @@ import org.gradle.api.tasks.TaskAction import java.io.File import java.util.concurrent.ThreadLocalRandom -abstract class ZippedTask : DefaultTask() { +abstract class ZippedTask : BaseTask() { - @InputFile - @Optional - val inputZip: RegularFileProperty = project.objects.fileProperty() + @get:InputFile + @get:Optional + abstract val inputZip: RegularFileProperty - @OutputFile - val outputZip: RegularFileProperty = defaultOutput("zip") + @get:OutputFile + abstract val outputZip: RegularFileProperty abstract fun run(rootDir: File) + override fun init() { + outputZip.convention(defaultOutput("zip")) + } + @TaskAction fun exec() { val outputZipFile = outputZip.file diff --git a/src/main/kotlin/tasks/download-task.kt b/src/main/kotlin/tasks/download-task.kt new file mode 100644 index 0000000..5cbc522 --- /dev/null +++ b/src/main/kotlin/tasks/download-task.kt @@ -0,0 +1,317 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.Constants +import io.papermc.paperweight.util.MavenArtifact +import io.papermc.paperweight.util.McpConfig +import io.papermc.paperweight.util.decompile +import io.papermc.paperweight.util.download +import io.papermc.paperweight.util.file +import io.papermc.paperweight.util.fromJson +import io.papermc.paperweight.util.gson +import io.papermc.paperweight.util.mcinject +import io.papermc.paperweight.util.rename +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.ProjectLayout +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import org.gradle.workers.WorkAction +import org.gradle.workers.WorkParameters +import org.gradle.workers.WorkerExecutor +import org.w3c.dom.Element +import java.io.File +import javax.inject.Inject +import javax.xml.parsers.DocumentBuilderFactory + +abstract class DownloadTask : DefaultTask() { + + @get:Input + abstract val url: Property + + @get:OutputFile + abstract val outputFile: RegularFileProperty + + @TaskAction + fun run() = download(url, outputFile) +} + +abstract class DownloadMcpFiles : DefaultTask() { + + @get:Input + abstract val mcpMinecraftVersion: Property + @get:Input + abstract val mcpConfigVersion: Property + @get:Input + abstract val mcpMappingsChannel: Property + @get:Input + abstract val mcpMappingsVersion: Property + + @get:OutputFile + abstract val configZip: RegularFileProperty + @get:OutputFile + abstract val mappingsZip: RegularFileProperty + + @TaskAction + fun run() { + val repo = listOf(Constants.FORGE_MAVEN_URL) + + MavenArtifact( + group = "de.oceanlabs.mcp", + artifact = "mcp_config", + version = mcpMinecraftVersion.get() + "-" + mcpConfigVersion.get(), + extension = "zip" + ).downloadToFile(configZip.file, repo) + + MavenArtifact( + group = "de.oceanlabs.mcp", + artifact = "mcp_${mcpMappingsChannel.get()}", + version = mcpMappingsVersion.get(), + extension = "zip" + ).downloadToFile(mappingsZip.file, repo) + } +} + +abstract class DownloadMcpTools : DefaultTask() { + + @get:InputFile + abstract val configFile: RegularFileProperty + + @get:OutputFile + abstract val forgeFlowerFile: RegularFileProperty + @get:OutputFile + abstract val mcInjectorFile: RegularFileProperty + @get:OutputFile + abstract val specialSourceFile: RegularFileProperty + + @get:Inject + abstract val workerExecutor: WorkerExecutor + + @TaskAction + fun run() { + val config = gson.fromJson(configFile) + + val queue = workerExecutor.noIsolation() + queue.submit(DownloadWorker::class.java) { + repos.add(config.functions.decompile.repo) + artifact.set(config.functions.decompile.version) + target.set(forgeFlowerFile.file) + downloadToDir.set(false) + } + queue.submit(DownloadWorker::class.java) { + repos.add(config.functions.mcinject.repo) + artifact.set(config.functions.mcinject.version) + target.set(mcInjectorFile.file) + downloadToDir.set(false) + } + queue.submit(DownloadWorker::class.java) { + repos.add(config.functions.rename.repo) + artifact.set(config.functions.rename.version) + target.set(specialSourceFile.file) + downloadToDir.set(false) + } + } +} + +abstract class DownloadMcLibraries : DefaultTask() { + + @get:InputFile + abstract val mcLibrariesFile: RegularFileProperty + @get:Input + abstract val mcRepo: Property + + @get:OutputDirectory + abstract val outputDir: DirectoryProperty + + @get:Inject + abstract val workerExecutor: WorkerExecutor + + @TaskAction + fun run() { + val out = outputDir.file + out.deleteRecursively() + + val mcRepos = listOf(mcRepo.get()) + + val queue = workerExecutor.noIsolation() + mcLibrariesFile.file.useLines { lines -> + lines.forEach { line -> + queue.submit(DownloadWorker::class.java) { + repos.set(mcRepos) + artifact.set(line) + target.set(out) + downloadToDir.set(true) + } + } + } + } +} + +abstract class DownloadSpigotDependencies : BaseTask() { + + @get:InputFile + abstract val apiPom: RegularFileProperty + @get:InputFile + abstract val serverPom: RegularFileProperty + + @get:OutputDirectory + abstract val apiOutputDir: DirectoryProperty + @get:OutputDirectory + abstract val serverOutputDir: DirectoryProperty + + @get:Inject + abstract val workerExecutor: WorkerExecutor + + @TaskAction + fun run() { + val apiSetup = parsePom(apiPom.file) + val serverSetup = parsePom(serverPom.file) + + val apiOut = apiOutputDir.file + apiOut.deleteRecursively() + + val serverOut = serverOutputDir.file + serverOut.deleteRecursively() + + val queue = workerExecutor.noIsolation() + for (art in apiSetup.artifacts) { + queue.submit(DownloadWorker::class.java) { + repos.set(apiSetup.repos) + artifact.set(art.toString()) + target.set(apiOut) + downloadToDir.set(true) + } + } + for (art in serverSetup.artifacts) { + queue.submit(DownloadWorker::class.java) { + repos.set(serverSetup.repos) + artifact.set(art.toString()) + target.set(serverOut) + downloadToDir.set(true) + } + } + } + + private fun parsePom(pomFile: File): MavenSetup { + val depList = arrayListOf() + val repoList = arrayListOf() + // Maven Central is implicit + repoList += "https://repo.maven.apache.org/maven2/" + + val builder = DocumentBuilderFactory.newInstance().newDocumentBuilder() + val doc = pomFile.inputStream().buffered().use { stream -> + stream.buffered().use { buffered -> + builder.parse(buffered) + } + } + + doc.documentElement.normalize() + + val list = doc.getElementsByTagName("dependencies") + for (i in 0 until list.length) { + val node = list.item(i) as? Element ?: continue + + val depNode = node.getElementsByTagName("dependency") + for (j in 0 until depNode.length) { + val dependency = depNode.item(j) as? Element ?: continue + val artifact = getDependency(dependency) ?: continue + depList += artifact + } + } + + val repos = doc.getElementsByTagName("repositories") + for (i in 0 until repos.length) { + val node = repos.item(i) as? Element ?: continue + val depNode = node.getElementsByTagName("repository") + for (j in 0 until depNode.length) { + val repo = depNode.item(j) as? Element ?: continue + val repoUrl = repo.getElementsByTagName("url").item(0).textContent + repoList += repoUrl + } + } + + return MavenSetup(repos = repoList, artifacts = depList) + } + + private fun getDependency(node: Element): MavenArtifact? { + val scopeNode = node.getElementsByTagName("scope") + val scope = if (scopeNode.length == 0) { + "compile" + } else { + scopeNode.item(0).textContent + } + + if (scope != "compile") { + return null + } + + val group = node.getElementsByTagName("groupId").item(0).textContent + val artifact = node.getElementsByTagName("artifactId").item(0).textContent + val version = node.getElementsByTagName("version").item(0).textContent + + if (version.contains("\${")) { + // Don't handle complicated things + // We don't need to (for now anyways) + return null + } + + return MavenArtifact( + group = group, + artifact = artifact, + version = version + ) + } +} + +data class MavenSetup( + val repos: List, + val artifacts: List +) + +interface DownloadParams : WorkParameters { + val repos: ListProperty + val artifact: Property + val target: RegularFileProperty + val downloadToDir: Property +} +abstract class DownloadWorker : WorkAction { + @get:Inject + abstract val layout: ProjectLayout + + override fun execute() { + val artifact = MavenArtifact.parse(parameters.artifact.get()) + if (parameters.downloadToDir.get()) { + artifact.downloadToDir(parameters.target.file, parameters.repos.get()) + } else { + artifact.downloadToFile(parameters.target.file, parameters.repos.get()) + } + } +} + diff --git a/src/main/kotlin/tasks/extract-tasks.kt b/src/main/kotlin/tasks/extract-tasks.kt new file mode 100644 index 0000000..b45d8d9 --- /dev/null +++ b/src/main/kotlin/tasks/extract-tasks.kt @@ -0,0 +1,127 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.McpConfig +import io.papermc.paperweight.util.file +import io.papermc.paperweight.util.fromJson +import io.papermc.paperweight.util.gson +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import java.io.File + +abstract class Extract : BaseTask() { + + @get:InputFile + abstract val inputFile: RegularFileProperty + + @get:OutputDirectory + abstract val outputDir: DirectoryProperty + + @TaskAction + open fun run() { + val input = inputFile.file + val output = outputDir.file + if (output.exists()) { + output.deleteRecursively() + } + output.mkdirs() + fs.copy { + from(archives.zipTree(input)) + into(output) + } + } +} + +abstract class ExtractMcp : Extract() { + + @get:OutputFile + abstract val configFile: RegularFileProperty + + @get:OutputFile + abstract val access: RegularFileProperty + @get:OutputFile + abstract val constructors: RegularFileProperty + @get:OutputFile + abstract val exceptions: RegularFileProperty + @get:OutputFile + abstract val mappings: RegularFileProperty + @get:OutputDirectory + abstract val patchDir: DirectoryProperty + + override fun init() { + configFile.convention(outputDir.file("config.json")) + access.convention(outputDir.file("config/access.txt")) + constructors.convention(outputDir.file("config/constructors.txt")) + exceptions.convention(outputDir.file("config/exceptions.txt")) + mappings.convention(outputDir.file("config/joined.tsrg")) + patchDir.convention(outputDir.dir("config/patches/server")) + } + + @TaskAction + override fun run() { + super.run() + + val output = outputDir.file + val config = gson.fromJson(output.resolve("config.json")) + + // We have to know what our output file paths are at configuration time, but these could change based on the + // config.json file. + // So as a workaround we just rename the files the config.json file points to to our expected paths. Likely + // is a no-op. + + output.resolve(config.data.access).renameTo(access.file.createParent()) + output.resolve(config.data.constructors).renameTo(constructors.file.createParent()) + output.resolve(config.data.exceptions).renameTo(exceptions.file.createParent()) + output.resolve(config.data.mappings).renameTo(mappings.file.createParent()) + output.resolve(config.data.patches.server).renameTo(patchDir.file.createParent()) + } + + private fun File.createParent(): File { + val par = this.parentFile + if (!par.exists()) { + par.mkdirs() + } + return this + } +} + +abstract class ExtractMappings : Extract() { + + @get:OutputFile + abstract val fieldsCsv: RegularFileProperty + @get:OutputFile + abstract val methodsCsv: RegularFileProperty + @get:OutputFile + abstract val paramsCsv: RegularFileProperty + + override fun init() { + fieldsCsv.convention(outputDir.file("fields.csv")) + methodsCsv.convention(outputDir.file("methods.csv")) + paramsCsv.convention(outputDir.file("params.csv")) + } +} diff --git a/src/main/kotlin/tasks/jar-tasks.kt b/src/main/kotlin/tasks/jar-tasks.kt new file mode 100644 index 0000000..c90eca4 --- /dev/null +++ b/src/main/kotlin/tasks/jar-tasks.kt @@ -0,0 +1,100 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.defaultOutput +import io.papermc.paperweight.util.file +import io.papermc.paperweight.util.zip +import org.gradle.api.file.DuplicatesStrategy +import org.gradle.api.file.RegularFile +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction + +abstract class Filter : BaseTask() { + + @get:InputFile + abstract val inputJar: RegularFileProperty + @get:Input + abstract val includes: ListProperty + + @get:OutputFile + abstract val outputJar: RegularFileProperty + + override fun init() { + outputJar.convention(defaultOutput()) + } + + @TaskAction + fun run() { + val out = outputJar.file + val target = out.resolveSibling("${out.name}.dir") + target.mkdirs() + + fs.copy { + from(archives.zipTree(inputJar)) { + for (inc in this@Filter.includes.get()) { + include(inc) + } + } + into(target) + } + + zip(target, outputJar) + target.deleteRecursively() + } +} + +abstract class Merge : BaseTask() { + @get:InputFiles + abstract val inputJars: ListProperty + + @get:OutputFile + abstract val outputJar: RegularFileProperty + + override fun init() { + outputJar.convention(defaultOutput()) + } + + @TaskAction + fun run() { + val out = outputJar.file + val target = out.resolveSibling("${out.name}.dir") + target.mkdirs() + + fs.copy { + for (file in inputJars.get()) { + from(archives.zipTree(file)) + } + into(target) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } + + zip(target, outputJar) + target.deleteRecursively() + } +} diff --git a/src/main/kotlin/tasks/ApplyAccessTransform.kt b/src/main/kotlin/tasks/patchremap/ApplyAccessTransform.kt similarity index 73% rename from src/main/kotlin/tasks/ApplyAccessTransform.kt rename to src/main/kotlin/tasks/patchremap/ApplyAccessTransform.kt index 624881f..789f2d9 100644 --- a/src/main/kotlin/tasks/ApplyAccessTransform.kt +++ b/src/main/kotlin/tasks/patchremap/ApplyAccessTransform.kt @@ -1,5 +1,28 @@ -package io.papermc.paperweight.tasks +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.patchremap + +import io.papermc.paperweight.tasks.BaseTask import io.papermc.paperweight.util.defaultOutput import io.papermc.paperweight.util.ensureDeleted import io.papermc.paperweight.util.ensureParentExists @@ -14,11 +37,8 @@ import org.cadixdev.atlas.Atlas import org.cadixdev.bombe.jar.JarClassEntry import org.cadixdev.bombe.jar.JarEntryTransformer import org.cadixdev.bombe.type.signature.MethodSignature -import org.cadixdev.lorenz.io.MappingFormats -import org.gradle.api.DefaultTask import org.gradle.api.file.RegularFileProperty import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction import org.objectweb.asm.ClassReader @@ -28,19 +48,20 @@ import org.objectweb.asm.FieldVisitor import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes -open class ApplyAccessTransform : DefaultTask() { +abstract class ApplyAccessTransform : BaseTask() { - @InputFile - val inputJar: RegularFileProperty = project.objects.fileProperty() + @get:InputFile + abstract val inputJar: RegularFileProperty - @InputFile - val atFile: RegularFileProperty = project.objects.fileProperty() + @get:InputFile + abstract val atFile: RegularFileProperty - @InputFile - val mapping: RegularFileProperty = project.objects.fileProperty() + @get:OutputFile + abstract val outputJar: RegularFileProperty - @OutputFile - val outputJar: RegularFileProperty = defaultOutput() + override fun init() { + outputJar.convention(defaultOutput()) + } @TaskAction fun run() { @@ -48,12 +69,10 @@ open class ApplyAccessTransform : DefaultTask() { ensureDeleted(outputJar.file) val at = AccessTransformFormats.FML.read(atFile.file.toPath()) - val mappings = MappingFormats.TSRG.createReader(mapping.file.toPath()).use { it.read() } - val remappedAt = at.remap(mappings) Atlas().apply { install { - AtJarEntryTransformer(remappedAt) + AtJarEntryTransformer(at) } run(inputJar.file.toPath(), outputJar.file.toPath()) } diff --git a/src/main/kotlin/tasks/patchremap/PatchApplier.kt b/src/main/kotlin/tasks/patchremap/PatchApplier.kt new file mode 100644 index 0000000..5ad6477 --- /dev/null +++ b/src/main/kotlin/tasks/patchremap/PatchApplier.kt @@ -0,0 +1,91 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.patchremap + +import io.papermc.paperweight.PaperweightException +import io.papermc.paperweight.util.Git +import java.io.File + +class PatchApplier( + private val remappedBranch: String, + private val unmappedBranch: String, + targetDir: File +) { + + private val git = Git(targetDir) + + private var commitMessage: String? = null + private var commitAuthor: String? = null + private var commitTime: String? = null + + fun initRepo() { + println("Initializing patch remap repo") + git("init").executeSilently() + git("commit", "-m", "Initial", "--author=Initial ", "--allow-empty").executeSilently() + git("branch", remappedBranch).executeSilently() + git("branch", unmappedBranch).executeSilently() + git("checkout", unmappedBranch).executeSilently() + } + + fun checkoutRemapped() { + println("Switching to $remappedBranch without losing changes") + git("symbolic-ref", "HEAD", "refs/heads/$remappedBranch").executeSilently() + } + + fun checkoutOld() { + println("Resetting back to $unmappedBranch branch") + git("checkout", unmappedBranch).executeSilently() + } + + fun commitInitialSource() { + git("add", ".").executeSilently() + git("commit", "-m", "Initial Source", "--author=Initial ").executeSilently() + } + + fun recordCommit() { + commitMessage = git("git", "log", "--format=%B", "-n", "1", "HEAD").getText() + commitAuthor = git("git", "log", "--format=%an <%ae>", "-n", "1", "HEAD").getText() + commitTime = git("git", "log", "--format=%aD", "-n", "1", "HEAD").getText() + } + + fun commitChanges() { + println("Committing remapped changes to $remappedBranch") + val message = commitMessage ?: throw PaperweightException("commitMessage not set") + val author = commitAuthor ?: throw PaperweightException("commitAuthor not set") + val time = commitTime ?: throw PaperweightException("commitTime not set") + commitMessage = null + commitAuthor = null + commitTime = null + + git("add", ".").executeSilently() + git("commit", "-m", message, "--author=$author", "--date=$time").execute() + } + + fun applyPatch(patch: File) { + println("Applying patch ${patch.name}") + val result = git("am", "--3way", "--ignore-whitespace", patch.absolutePath).runOut() + if (result != 0) { + System.err.println("Patch failed to apply: $patch") + } + } +} diff --git a/src/main/kotlin/tasks/patchremap/PatchSourceRemapWorker.kt b/src/main/kotlin/tasks/patchremap/PatchSourceRemapWorker.kt new file mode 100644 index 0000000..42dbdfd --- /dev/null +++ b/src/main/kotlin/tasks/patchremap/PatchSourceRemapWorker.kt @@ -0,0 +1,96 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.patchremap + +import io.papermc.paperweight.tasks.sourceremap.ConstructorsData +import io.papermc.paperweight.tasks.sourceremap.ParamNames +import io.papermc.paperweight.tasks.sourceremap.PatchParameterRemapper +import io.papermc.paperweight.tasks.sourceremap.SrgParameterRemapper +import org.cadixdev.lorenz.MappingSet +import org.cadixdev.mercury.Mercury +import org.cadixdev.mercury.remapper.MercuryRemapper +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.StandardCopyOption.REPLACE_EXISTING + +class PatchSourceRemapWorker( + private val mappings: MappingSet, + private val classpath: Collection, + private val paramNames: ParamNames, + private val constructorsData: ConstructorsData, + private val inputDir: Path, + private val outputDir: Path +) { + + private val reverseMappings: MappingSet = mappings.reverse() + + fun remap() { + setup() + + Mercury().apply { + classPath.addAll(classpath) + + processors.addAll(listOf( + MercuryRemapper.create(reverseMappings), + PatchParameterRemapper(paramNames, constructorsData) + )) + + rewrite(inputDir, outputDir) + } + + cleanup() + } + + fun remapBack() { + setup() + + Mercury().apply { + classPath.addAll(classpath) + + processors.addAll(listOf( + MercuryRemapper.create(mappings), + SrgParameterRemapper(mappings, constructorsData, paramNames) + )) + + rewrite(inputDir, outputDir) + } + + cleanup() + } + + private fun setup() { + Files.walk(outputDir).use { it.forEach(Files::delete) } + Files.createDirectories(outputDir) + } + + private fun cleanup() { + Files.walk(inputDir).use { it.forEach(Files::delete) } + Files.walk(outputDir).use { + it.forEach { src -> + val dest = inputDir.resolve(outputDir.relativize(src)) + Files.createDirectories(dest.parent) + Files.copy(src, dest, REPLACE_EXISTING) + } + } + } +} diff --git a/src/main/kotlin/tasks/patchremap/RemapPatches.kt b/src/main/kotlin/tasks/patchremap/RemapPatches.kt new file mode 100644 index 0000000..9d5c524 --- /dev/null +++ b/src/main/kotlin/tasks/patchremap/RemapPatches.kt @@ -0,0 +1,187 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.patchremap + +import io.papermc.paperweight.tasks.BaseTask +import io.papermc.paperweight.tasks.sourceremap.parseConstructors +import io.papermc.paperweight.tasks.sourceremap.parseParamNames +import io.papermc.paperweight.util.cache +import io.papermc.paperweight.util.file +import org.cadixdev.lorenz.io.MappingFormats +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFile +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import org.gradle.kotlin.dsl.get +import java.io.File +import java.util.zip.ZipFile + +abstract class RemapPatches : BaseTask() { + + @get:InputDirectory + abstract val inputPatchDir: DirectoryProperty + @get:InputFile + abstract val sourceJar: RegularFileProperty + @get:InputDirectory + abstract val apiPatchDir: DirectoryProperty + + @get:InputFile + abstract val mappingsFile: RegularFileProperty + + @get:Classpath + abstract val classpathJars: ListProperty + + @get:InputDirectory + abstract val spigotApiDir: DirectoryProperty + @get:InputDirectory + abstract val spigotServerDir: DirectoryProperty + @get:InputFile + abstract val spigotDecompJar: RegularFileProperty + + // For parameter name remapping + @get:InputFile + abstract val parameterNames: RegularFileProperty + @get:InputFile + abstract val constructors: RegularFileProperty + + @get:OutputDirectory + abstract val outputPatchDir: DirectoryProperty + + @TaskAction + fun run() { + // Check patches + val patches = inputPatchDir.file.listFiles() ?: return run { + println("No input patches found") + } + + patches.sort() + + // Setup param remapping + val constructorsData = parseConstructors(constructors.file) + val paramMap = parseParamNames(parameterNames.file) + + val mappings = MappingFormats.TSRG.createReader(mappingsFile.file.toPath()).use { it.read() } + + // This should pull in any libraries needed for type bindings + val configFiles = project.project(":Paper-Server").configurations["runtimeClasspath"].resolve() + val classpathFiles = classpathJars.get().map { it.asFile } + configFiles + + // Remap output directory, after each output this directory will be re-named to the input directory below for + // the next remap operation + val tempApiDir = createWorkDir("patch-remap-api", source = spigotApiDir.file) + val tempInputDir = createWorkDir("patch-remap-input", source = spigotServerDir.file) + val tempOutputDir = createWorkDir("patch-remap-output") + + val sourceInputDir = tempInputDir.resolve("src/main/java") + sourceInputDir.deleteRecursively() + sourceInputDir.mkdirs() + + project.copy { + from(project.zipTree(sourceJar.file)) + into(sourceInputDir) + } + + tempInputDir.resolve(".git").deleteRecursively() + + PatchSourceRemapWorker( + mappings, + listOf(*classpathFiles.toTypedArray(), tempApiDir.resolve("src/main/java")).map { it.toPath() }, + paramMap, + constructorsData, + sourceInputDir.toPath(), + tempOutputDir.toPath() + ).let { remapper -> + val patchApplier = PatchApplier("remapped", "old", tempInputDir) + // Setup patch remapping repo + patchApplier.initRepo() // Create empty initial commit + remapper.remap() // Remap to Spigot mappings + + // We need to include any missing classes for the patches later on + importMcDev(patches, tempInputDir.resolve("src/main/java")) + patchApplier.commitInitialSource() // Initial commit of Spigot sources + patchApplier.checkoutRemapped() // Switch to remapped branch without checking out files + + remapper.remapBack() // Remap to new mappings + patchApplier.commitInitialSource() // Initial commit of Spigot sources mapped to new mappings + patchApplier.checkoutOld() // Normal checkout back to Spigot mappings branch + + // Repo setup is done, we can begin the patch "loop" now + // - not a loop yet cause it doesn't even work for the first patch + remapper.remap() // Remap to to Spigot mappings TODO: verify this step produces correct results + patchApplier.applyPatch(patches.first()) // Apply patch on Spigot mappings + patchApplier.recordCommit() // Keep track of commit author, message, and time + patchApplier.checkoutRemapped() // Switch to remapped branch without checkout out files + remapper.remapBack() // Remap to new mappings + patchApplier.commitChanges() // Commit the changes + patchApplier.checkoutOld() // Normal checkout back to Spigot mappings branch + } + } + + private fun importMcDev(patches: Array, inputDir: File) { + val importMcDev = readMcDevNames(patches).asSequence() + .map { inputDir.resolve("net/minecraft/server/$it.java") } + .filter { !it.exists() } + .toSet() + ZipFile(spigotDecompJar.file).use { zipFile -> + for (file in importMcDev) { + val zipEntry = zipFile.getEntry(file.relativeTo(inputDir).path) ?: continue + zipFile.getInputStream(zipEntry).use { input -> + file.outputStream().buffered().use { output -> + input.copyTo(output) + } + } + } + } + } + + private fun readMcDevNames(patches: Array): Set { + val result = hashSetOf() + + val prefix = "+++ b/src/main/java/net/minecraft/server/" + val suffix = ".java" + + for (patch in patches) { + patch.useLines { lines -> + lines + .filter { it.startsWith(prefix) } + .map { it.substring(prefix.length, it.length - suffix.length) } + .forEach { result.add(it) } + } + } + + return result + } + + private fun createWorkDir(name: String, source: File? = null): File { + return layout.cache.resolve("paperweight").resolve(name).apply { + deleteRecursively() + mkdirs() + source?.copyRecursively(this) + } + } +} diff --git a/src/main/kotlin/tasks/sourceremap/AbstractParameterVisitor.kt b/src/main/kotlin/tasks/sourceremap/AbstractParameterVisitor.kt new file mode 100644 index 0000000..280044f --- /dev/null +++ b/src/main/kotlin/tasks/sourceremap/AbstractParameterVisitor.kt @@ -0,0 +1,74 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.sourceremap + +import org.cadixdev.mercury.RewriteContext +import org.eclipse.jdt.core.dom.ASTVisitor +import org.eclipse.jdt.core.dom.IMethodBinding +import org.eclipse.jdt.core.dom.IVariableBinding +import org.eclipse.jdt.core.dom.MethodDeclaration +import org.eclipse.jdt.core.dom.SimpleName +import org.eclipse.jdt.core.dom.VariableDeclaration + +abstract class AbstractParameterVisitor(protected val context: RewriteContext) : ASTVisitor() { + + override fun visit(node: SimpleName): Boolean { + val binding = node.resolveBinding() as? IVariableBinding ?: return false + if (!binding.isParameter) { + return false + } + + val variableDecl = context.compilationUnit.findDeclaringNode(binding.variableDeclaration) as VariableDeclaration + + val method = binding.declaringMethod + val methodDecl = context.compilationUnit.findDeclaringNode(method) as? MethodDeclaration ?: return false + + if (method.isConstructor) { + handleConstructor(node, methodDecl, method, variableDecl) + } else { + handleMethod(node, methodDecl, method, variableDecl) + } + + return false + } + + abstract fun handleMethod( + node: SimpleName, + methodDecl: MethodDeclaration, + method: IMethodBinding, + variableDecl: VariableDeclaration + ) + + abstract fun handleConstructor( + node: SimpleName, + methodDecl: MethodDeclaration, + method: IMethodBinding, + variableDecl: VariableDeclaration + ) + + fun getParameterIndex(methodDecl: MethodDeclaration, decl: VariableDeclaration): Int { + @Suppress("UNCHECKED_CAST") + val params = methodDecl.parameters() as List + return params.indexOfFirst { it === decl } + } +} diff --git a/src/main/kotlin/tasks/sourceremap/PatchParameterRemapper.kt b/src/main/kotlin/tasks/sourceremap/PatchParameterRemapper.kt new file mode 100644 index 0000000..121e538 --- /dev/null +++ b/src/main/kotlin/tasks/sourceremap/PatchParameterRemapper.kt @@ -0,0 +1,95 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.sourceremap + +import io.papermc.paperweight.PaperweightException +import org.cadixdev.mercury.RewriteContext +import org.cadixdev.mercury.SourceProcessor +import org.cadixdev.mercury.SourceRewriter +import org.cadixdev.mercury.util.BombeBindings +import org.eclipse.jdt.core.dom.IMethodBinding +import org.eclipse.jdt.core.dom.MethodDeclaration +import org.eclipse.jdt.core.dom.SimpleName +import org.eclipse.jdt.core.dom.VariableDeclaration + +class PatchParameterRemapper( + private val paramNames: ParamNames, + private val constructorsData: ConstructorsData +) : SourceRewriter { + override fun getFlags(): Int = SourceProcessor.FLAG_RESOLVE_BINDINGS + + override fun rewrite(context: RewriteContext) { + context.compilationUnit.accept(PatchParameterVisitor(context, paramNames, constructorsData)) + } +} + +class PatchParameterVisitor( + context: RewriteContext, + private val paramNames: ParamNames, + private val constructorsData: ConstructorsData +) : AbstractParameterVisitor(context) { + + override fun handleMethod( + node: SimpleName, + methodDecl: MethodDeclaration, + method: IMethodBinding, + variableDecl: VariableDeclaration + ) { + val paramNames = paramNames[methodDecl.name.identifier] ?: return + val params = methodDecl.parameters() + + if (paramNames.size != params.size) { + throw PaperweightException("Invalid parameter length; expected ${paramNames.size}, actual ${params.size} " + + "for method ${methodDecl.name.identifier}") + } + + val index = getParameterIndex(methodDecl, variableDecl) + val newName = paramNames[index] ?: return + + context.createASTRewrite().set(node, SimpleName.IDENTIFIER_PROPERTY, newName, null) + } + + override fun handleConstructor( + node: SimpleName, + methodDecl: MethodDeclaration, + method: IMethodBinding, + variableDecl: VariableDeclaration + ) { + val className = method.declaringClass.binaryName.replace('.', '/') + val descriptor = BombeBindings.convertSignature(method).descriptor + + val constructorNode = constructorsData.findConstructorNode(className, descriptor) ?: return + val paramNames = paramNames["const_${constructorNode.id}"] ?: return + val params = methodDecl.parameters() + + if (paramNames.size != params.size) { + throw PaperweightException("Invalid parameter length; expected ${paramNames.size}, actual ${params.size} " + + "for constructor $className $descriptor") + } + + val index = getParameterIndex(methodDecl, variableDecl) + val newName = paramNames[index] + + context.createASTRewrite().set(node, SimpleName.IDENTIFIER_PROPERTY, newName, null) + } +} diff --git a/src/main/kotlin/tasks/sourceremap/RemapSources.kt b/src/main/kotlin/tasks/sourceremap/RemapSources.kt new file mode 100644 index 0000000..91a469a --- /dev/null +++ b/src/main/kotlin/tasks/sourceremap/RemapSources.kt @@ -0,0 +1,114 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.sourceremap + +import io.papermc.paperweight.tasks.ZippedTask +import io.papermc.paperweight.util.defaultOutput +import io.papermc.paperweight.util.file +import io.papermc.paperweight.util.path +import org.cadixdev.at.AccessTransformSet +import org.cadixdev.at.io.AccessTransformFormats +import org.cadixdev.lorenz.io.MappingFormats +import org.cadixdev.mercury.Mercury +import org.cadixdev.mercury.at.AccessTransformerRewriter +import org.cadixdev.mercury.extra.AccessAnalyzerProcessor +import org.cadixdev.mercury.extra.BridgeMethodRewriter +import org.cadixdev.mercury.remapper.MercuryRemapper +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputFile +import java.io.File + +abstract class RemapSources : ZippedTask() { + + @get:InputFile + abstract val vanillaJar: RegularFileProperty + @get:InputFile + abstract val vanillaRemappedSpigotJar: RegularFileProperty // Required for pre-remap pass + @get:InputFile + abstract val mappings: RegularFileProperty + + @get:InputDirectory + abstract val spigotApiDeps: DirectoryProperty + @get:InputDirectory + abstract val spigotServerDeps: DirectoryProperty + + @get:InputFile + abstract val constructors: RegularFileProperty + @get:InputDirectory + abstract val spigotServerDir: DirectoryProperty + @get:InputDirectory + abstract val spigotApiDir: DirectoryProperty + + @get:OutputFile + abstract val generatedAt: RegularFileProperty + @get:OutputFile + abstract val parameterNames: RegularFileProperty + + override fun init() { + super.init() + generatedAt.convention(defaultOutput("at")) + parameterNames.convention(defaultOutput("params")) + } + + override fun run(rootDir: File) { + val constructorsData = parseConstructors(constructors.file) + + val paramNames: ParamNames = newParamNames() + + val srcDir = spigotServerDir.file.resolve("src/main/java") + + val mappingSet = MappingFormats.TSRG.read(mappings.path) + val processAt = AccessTransformSet.create() + + // Remap any references Spigot maps to SRG + Mercury().apply { + classPath.addAll(listOf( + vanillaJar.path, + vanillaRemappedSpigotJar.path, + spigotApiDir.path.resolve("src/main/java"), + *spigotApiDeps.get().asFileTree.files.map { it.toPath() }.toTypedArray(), + *spigotServerDeps.get().asFileTree.files.map { it.toPath() }.toTypedArray() + )) + + processors += AccessAnalyzerProcessor.create(processAt, mappingSet) + + process(srcDir.toPath()) + + processors.clear() + processors.addAll(listOf( + MercuryRemapper.create(mappingSet), + BridgeMethodRewriter.create(), + AccessTransformerRewriter.create(processAt), + SrgParameterRemapper(mappingSet, constructorsData, paramNames) + )) + + rewrite(srcDir.toPath(), rootDir.toPath()) + } + + AccessTransformFormats.FML.write(generatedAt.path, processAt) + writeParamNames(paramNames, parameterNames.file) + } +} diff --git a/src/main/kotlin/tasks/sourceremap/SrgParameterRemapper.kt b/src/main/kotlin/tasks/sourceremap/SrgParameterRemapper.kt new file mode 100644 index 0000000..a44068b --- /dev/null +++ b/src/main/kotlin/tasks/sourceremap/SrgParameterRemapper.kt @@ -0,0 +1,142 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.sourceremap + +import org.cadixdev.bombe.type.MethodDescriptor +import org.cadixdev.lorenz.MappingSet +import org.cadixdev.mercury.RewriteContext +import org.cadixdev.mercury.SourceProcessor +import org.cadixdev.mercury.SourceRewriter +import org.cadixdev.mercury.util.BombeBindings +import org.eclipse.jdt.core.dom.IMethodBinding +import org.eclipse.jdt.core.dom.MethodDeclaration +import org.eclipse.jdt.core.dom.Modifier +import org.eclipse.jdt.core.dom.SimpleName +import org.eclipse.jdt.core.dom.VariableDeclaration + +class SrgParameterRemapper( + private val mappings: MappingSet, + private val constructorsData: ConstructorsData, + private val parameterNames: ParamNames? = null +) : SourceRewriter { + + override fun getFlags(): Int = SourceProcessor.FLAG_RESOLVE_BINDINGS + + override fun rewrite(context: RewriteContext) { + context.compilationUnit.accept(SrgParameterVisitor(context, mappings, constructorsData, parameterNames)) + } +} + +class SrgParameterVisitor( + context: RewriteContext, + private val mappings: MappingSet, + private val constructorsData: ConstructorsData, + private val paramNames: ParamNames? +) : AbstractParameterVisitor(context) { + + companion object { + private val MATCHER = Regex("func_(\\d+)_.*") + } + + override fun handleMethod( + node: SimpleName, + methodDecl: MethodDeclaration, + method: IMethodBinding, + variableDecl: VariableDeclaration + ) { + val methodName = mappings.getClassMapping(method.declaringClass.binaryName) + .flatMap { it.getMethodMapping(BombeBindings.convertSignature(method)) } + .map { it.deobfuscatedName } + .orElse(null) ?: return + + val match = MATCHER.matchEntire(methodName) ?: return + val isStatic = method.modifiers and Modifier.STATIC != 0 + + var index = getParameterIndex(methodDecl, variableDecl) + if (index == -1) { + return + } + + recordName(methodName, method, node, index) + + if (!isStatic) { + index++ + } + + val paramName = "p_${match.groupValues[1]}_${index}_" + context.createASTRewrite().set(node, SimpleName.IDENTIFIER_PROPERTY, paramName, null) + } + + override fun handleConstructor( + node: SimpleName, + methodDecl: MethodDeclaration, + method: IMethodBinding, + variableDecl: VariableDeclaration + ) { + val binaryName = method.declaringClass.binaryName + val classMapping = mappings.getClassMapping(binaryName) + val className = classMapping + .map { it.fullDeobfuscatedName } + .orElse(binaryName) + + val descriptor = BombeBindings.convertSignature(method).descriptor + val constructorNode = constructorsData.findConstructorNode(className, descriptor) ?: return + + val id = constructorNode.id + + var index = getParameterIndex(methodDecl, variableDecl) + if (index == -1) { + return + } + + recordName("const_$id", method, node, index) + + // Constructors are never static + index++ + + val paramName = "p_i${id}_${index}_" + context.createASTRewrite().set(node, SimpleName.IDENTIFIER_PROPERTY, paramName, null) + } + + private fun recordName( + methodName: String, + method: IMethodBinding, + node: SimpleName, + index: Int + ) { + paramNames?.let { map -> + val paramCount = method.parameterTypes.size + map.computeIfAbsent(methodName) { arrayOfNulls(paramCount) }[index] = node.identifier + } + } +} + +fun ConstructorsData.findConstructorNode(className: String, desc: MethodDescriptor): ConstructorNode? { + val constructorNodes = constructors[className] ?: return null + + val descriptorText = desc.toString() + return constructorNodes.firstOrNull { constructorNode -> + constructorNode.descriptor == descriptorText + } +} + diff --git a/src/main/kotlin/tasks/sourceremap/remap.kt b/src/main/kotlin/tasks/sourceremap/remap.kt new file mode 100644 index 0000000..0a901d3 --- /dev/null +++ b/src/main/kotlin/tasks/sourceremap/remap.kt @@ -0,0 +1,90 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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.sourceremap + +import java.io.File + +data class ConstructorsData(val constructors: Map>) + +data class ConstructorNode( + val id: Int, + val descriptor: String +) + +fun parseConstructors(constructors: File): ConstructorsData { + val constructorMap = hashMapOf>() + + constructors.useLines { lines -> + lines.forEach { line -> + val parts = line.split(' ') + constructorMap.compute(parts[1]) { _, v -> + val node = ConstructorNode(parts[0].toInt(), parts[2]) + if (v == null) { + return@compute mutableListOf(node) + } else { + v += node + return@compute v + } + } + } + } + + for (list in constructorMap.values) { + // Put bigger numbers first + // Old constructor entries are still present, just with smaller numbers. So we don't want to grab an older + // entry + list.reverse() + } + + return ConstructorsData(constructorMap) +} + +typealias ParamNames = MutableMap> +fun newParamNames(): ParamNames = mutableMapOf() + +fun writeParamNames(names: ParamNames, file: File) { + file.bufferedWriter().use { writer -> + for ((desc, params) in names.entries) { + writer.append(desc).append(' ') + for (i in params.indices) { + writer.append(i.toString()).append(' ').append(params[i]) + if (i != params.lastIndex) { + writer.append(' ') + } + } + writer.newLine() + } + } +} + +fun parseParamNames(file: File): ParamNames { + val paramNames: MutableMap> = mutableMapOf() + file.useLines { lines -> + for (line in lines) { + val parts = line.split(' ') + val params = parts.asSequence().drop(1).chunked(2).associate { it[0].toInt() to it[1] } + paramNames[parts.first()] = Array(params.size) { params.getValue(it) } + } + } + return paramNames +} diff --git a/src/main/kotlin/util/BuildDataInfo.kt b/src/main/kotlin/util/BuildDataInfo.kt index 6a21f44..9d4756c 100644 --- a/src/main/kotlin/util/BuildDataInfo.kt +++ b/src/main/kotlin/util/BuildDataInfo.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,7 +20,7 @@ * USA */ -package util +package io.papermc.paperweight.util data class BuildDataInfo( val minecraftVersion: String, diff --git a/src/main/kotlin/util/Constants.kt b/src/main/kotlin/util/Constants.kt index be7e32e..12dcdd0 100644 --- a/src/main/kotlin/util/Constants.kt +++ b/src/main/kotlin/util/Constants.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -28,14 +27,6 @@ import org.gradle.api.Task object Constants { const val EXTENSION = "paperweight" - const val MCP_MAPPINGS_CONFIG = "mcpConfig" - - const val MCP_DATA_CONFIG = "mcpData" - const val SPIGOT_DEP_CONFIG = "spigotDeps" - const val MINECRAFT_DEP_CONFIG = "minecraft" - const val FORGE_FLOWER_CONFIG = "forgeFlower" - const val MCINJECT_CONFIG = "mcinject" - const val FORGE_MAVEN_URL = "https://files.minecraftforge.net/maven" const val MC_LIBRARY_URL = "https://libraries.minecraft.net/" const val MC_MANIFEST_URL = "https://launchermeta.mojang.com/mc/game/version_manifest.json" @@ -43,9 +34,19 @@ object Constants { const val CACHE_PATH = "caches" private const val PAPER_PATH = "paperweight" + private const val JARS_PATH = "$PAPER_PATH/jars" + const val MINECRAFT_JARS_PATH = "$JARS_PATH/minecraft" + const val MCP_TOOLS_PATH = "$JARS_PATH/tools" + const val MCP_ZIPS_PATH = "$JARS_PATH/mcp" + private const val SPIGOT_JARS_PATH = "$JARS_PATH/spigot" + const val SPIGOT_API_JARS_PATH = "$SPIGOT_JARS_PATH/api" + const val SPIGOT_SERVER_JARS_PATH = "$SPIGOT_JARS_PATH/server" + const val MCP_DATA_DIR = "mcp/data" const val MCP_MAPPINGS_DIR = "mcp/mappings" - const val SRG_DIR = "$MCP_MAPPINGS_DIR/srgs" + private const val SRG_DIR = "$MCP_MAPPINGS_DIR/srgs" + + const val MCP_CONFIG_JSON = "$MCP_DATA_DIR/config.json" const val PAPER_FIELDS_CSV = "$MCP_MAPPINGS_DIR/paper_fields.csv" const val PAPER_METHODS_CSV = "$MCP_MAPPINGS_DIR/paper_methods.csv" @@ -69,10 +70,10 @@ object Constants { const val MC_MANIFEST = "jsons/McManifest.json" const val VERSION_JSON = "jsons/McVersion.json" + const val MC_LIBRARIES = "jsons/McLibraries.txt" - const val TASK_CACHE = "$PAPER_PATH/taskCache" + private const val TASK_CACHE = "$PAPER_PATH/taskCache" - fun Task.paperTaskOutput() = paperTaskOutput("jar") fun Task.paperTaskOutput(ext: String) = paperTaskOutput(name, ext) - fun Task.paperTaskOutput(name: String, ext: String) = "$TASK_CACHE/$name.$ext" + fun paperTaskOutput(name: String, ext: String) = "$TASK_CACHE/$name.$ext" } diff --git a/src/main/kotlin/util/ArtifactDescriptor.kt b/src/main/kotlin/util/MavenArtifact.kt similarity index 58% rename from src/main/kotlin/util/ArtifactDescriptor.kt rename to src/main/kotlin/util/MavenArtifact.kt index cf5777a..3be8353 100644 --- a/src/main/kotlin/util/ArtifactDescriptor.kt +++ b/src/main/kotlin/util/MavenArtifact.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,23 +23,66 @@ package io.papermc.paperweight.util import io.papermc.paperweight.PaperweightException +import java.io.File -data class ArtifactDescriptor( - val group: String, - val artifact: String, - val version: String, - val classifier: String?, - val extension: String +class MavenArtifact( + private val group: String, + private val artifact: String, + private val version: String, + private val classifier: String? = null, + private val extension: String? = null ) { + + private val classifierText: String + get() = if (classifier != null) "-$classifier" else "" + + private val ext: String + get() = extension ?: "jar" + + private val path: String + get() = "${group.replace('.', '/')}/$artifact/$version/$file" + val file: String + get() = "$artifact-$version$classifierText.$ext" + + fun downloadToFile(targetFile: File, repos: List) { + targetFile.parentFile.mkdirs() + + var thrown: Exception? = null + for (repo in repos) { + try { + download(addSlash(repo) + path, targetFile) + return + } catch (e: Exception) { + if (thrown != null) { + thrown.addSuppressed(e) + } else { + thrown = e + } + } + } + thrown?.let { throw PaperweightException("Failed to download artifact: $this. Checked repos: $repos", it) } + } + + fun downloadToDir(targetDir: File, repos: List): File { + val out = targetDir.resolve(file) + downloadToFile(targetDir.resolve(file), repos) + return out + } + override fun toString(): String { - val path = group.replace('.', '/') - val classifierText = classifier?.let { "-$it" } ?: "" - val file = "$artifact-$version$classifierText.$extension" - return "$path/$artifact/$version/$file" + return if (classifier == null) { + "$group:$artifact:$version" + } else { + "$group:$artifact:$version:$classifier" + } + } + + private fun addSlash(url: String): String { + return if (url.endsWith('/')) url else "$url/" } companion object { - fun parse(text: String): ArtifactDescriptor { + fun parse(text: String): MavenArtifact { val (group, groupIndex) = text.nextSubstring(0, charArrayOf(':')) val (artifact, artifactIndex) = text.nextSubstring(groupIndex, charArrayOf(':')) val (version, versionIndex) = text.nextSubstring(artifactIndex, charArrayOf(':', '@'), goToEnd = true) @@ -51,7 +93,7 @@ data class ArtifactDescriptor( artifact ?: throw PaperweightException("Invalid Maven artifact descriptor (no artifactId found): $text") version ?: throw PaperweightException("Invalid Maven artifact descriptor (no version found): $text") - return ArtifactDescriptor(group, artifact, version, classifier, extension ?: "jar") + return MavenArtifact(group, artifact, version, classifier, extension) } private fun String.nextSubstring(startIndex: Int, stops: CharArray, goToEnd: Boolean = false): Pair { diff --git a/src/main/kotlin/util/McpConfig.kt b/src/main/kotlin/util/McpConfig.kt index 52e4914..2a99b58 100644 --- a/src/main/kotlin/util/McpConfig.kt +++ b/src/main/kotlin/util/McpConfig.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,6 +22,14 @@ package io.papermc.paperweight.util +typealias FunctionMap = Map +val FunctionMap.decompile: McpJvmCommand + get() = getValue("decompile") +val FunctionMap.mcinject: McpJvmCommand + get() = getValue("mcinject") +val FunctionMap.rename: McpJvmCommand + get() = getValue("rename") + data class McpConfig( val spec: Int, val version: String, diff --git a/src/main/kotlin/util/MinecraftManifest.kt b/src/main/kotlin/util/MinecraftManifest.kt index e573689..33ab579 100644 --- a/src/main/kotlin/util/MinecraftManifest.kt +++ b/src/main/kotlin/util/MinecraftManifest.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,7 +20,7 @@ * USA */ -package util +package io.papermc.paperweight.util data class MinecraftManifest( internal val latest: Map, diff --git a/src/main/kotlin/util/download.kt b/src/main/kotlin/util/download.kt new file mode 100644 index 0000000..9b355ca --- /dev/null +++ b/src/main/kotlin/util/download.kt @@ -0,0 +1,140 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. It uses + * some code and systems originally from ForgeGradle. + * + * Copyright (C) 2020 Kyle Wood + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 + +import io.papermc.paperweight.PaperweightException +import org.apache.http.HttpHost +import org.apache.http.HttpStatus +import org.apache.http.client.config.CookieSpecs +import org.apache.http.client.config.RequestConfig +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.methods.HttpGet +import org.apache.http.client.utils.DateUtils +import org.apache.http.impl.client.HttpClientBuilder +import org.gradle.api.provider.Provider +import java.io.File +import java.net.URI +import java.net.URL +import java.util.Date +import java.util.concurrent.TimeUnit + +fun download(source: Any, target: Any) { + val url = source.convertToUrl() + val file = target.convertToFile() + download(url, file) +} + +private fun download(source: URL, target: File) { + target.parentFile.mkdirs() + + val etagFile = target.resolveSibling(target.name + ".etag") + val etag = if (etagFile.exists()) etagFile.readText() else null + + val host = HttpHost(source.host, source.port, source.protocol) + val httpClient = HttpClientBuilder.create().run { + setRetryHandler { _, count, _ -> count < 3 } + useSystemProperties() + build() + } + + val time = if (target.exists()) target.lastModified() else 0 + + httpClient.use { client -> + val httpGet = HttpGet(source.file) + // Super high timeout, reduce chances of weird things going wrong + val timeouts = TimeUnit.MINUTES.toMillis(5).toInt() + + httpGet.config = RequestConfig.custom() + .setConnectTimeout(timeouts) + .setConnectionRequestTimeout(timeouts) + .setSocketTimeout(timeouts) + .setCookieSpec(CookieSpecs.STANDARD) + .build() + + if (time > 0) { + httpGet.setHeader("If-Modified-Since", DateUtils.formatDate(Date(time))) + } + if (etag != null) { + httpGet.setHeader("If-None-Match", etag) + } + + client.execute(host, httpGet).use { response -> + val code = response.statusLine.statusCode + if ((code < 200 || code > 299) && code != HttpStatus.SC_NOT_MODIFIED) { + val reason = response.statusLine.reasonPhrase + throw PaperweightException("Download failed, HTTP code: $code; URL: $source; Reason: $reason") + } + + val lastModified = handleResponse(response, time, target) + saveEtag(response, lastModified, target, etagFile) + } + } +} + +private fun handleResponse(response: CloseableHttpResponse, time: Long, target: File): Long { + val lastModified = with(response.getLastHeader("Last-Modified")) { + if (this == null) { + return@with 0 + } + if (value.isNullOrBlank()) { + return@with 0 + } + val date = DateUtils.parseDate(value) ?: return@with 0 + return@with date.time + } + if (response.statusLine.statusCode == HttpStatus.SC_NOT_MODIFIED) { + if (lastModified != 0L && time >= lastModified) { + return lastModified + } + } + + val entity = response.entity ?: return lastModified + entity.content.use { input -> + target.outputStream().buffered().use { output -> + input.copyTo(output) + } + } + + return lastModified +} + +private fun saveEtag(response: CloseableHttpResponse, lastModified: Long, target: File, etagFile: File) { + if (lastModified > 0) { + target.setLastModified(lastModified) + } + + val header = response.getFirstHeader("ETag") ?: return + val etag = header.value + + etagFile.writeText(etag) +} + +private fun Any.convertToUrl(): URL { + return when (this) { + is URL -> this + is URI -> this.toURL() + is String -> URI.create(this).toURL() + is Provider<*> -> this.get().convertToUrl() + else -> throw PaperweightException("Unknown URL type: ${this.javaClass.name}") + } +} diff --git a/src/main/kotlin/util/etag-util.kt b/src/main/kotlin/util/etag-util.kt deleted file mode 100644 index d5af5e6..0000000 --- a/src/main/kotlin/util/etag-util.kt +++ /dev/null @@ -1,106 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. It uses - * some code and systems originally from ForgeGradle. - * - * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC - * - * 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; either - * version 2.1 of the License, or (at your option) any later version. - * - * 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 - -import io.papermc.paperweight.PaperweightException -import java.io.File -import java.net.HttpURLConnection -import java.net.URL -import java.util.concurrent.TimeUnit - -private const val USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11" - -fun getWithEtag(urlText: String, cache: File, etagFile: File) { - if (cache.exists() && cache.lastModified() + TimeUnit.MINUTES.toMillis(1) >= System.currentTimeMillis()) { - return - } - - val etag = if (etagFile.exists()) { - etagFile.readText() - } else { - etagFile.parentFile.mkdirs() - "" - } - - var thrown: Throwable? = null - - try { - val url = URL(urlText) - - val con = url.openConnection() as HttpURLConnection - con.instanceFollowRedirects = true - con.setRequestProperty("User-Agent", USER_AGENT) - con.ifModifiedSince = cache.lastModified() - - if (etag.isNotEmpty()) { - con.setRequestProperty("If-None-Match", etag) - } - - try { - con.connect() - - when (con.responseCode) { - 304 -> { - cache.setLastModified(System.currentTimeMillis()) - return - } - 200 -> { - val data = con.inputStream.use { stream -> - stream.readBytes() - } - - cache.writeBytes(data) - - val newEtag = con.getHeaderField("ETag") - if (newEtag.isNullOrEmpty()) { - if (!etagFile.createNewFile()) { - etagFile.setLastModified(System.currentTimeMillis()) - } - } else { - etagFile.writeText(newEtag) - } - - return - } - else -> throw RuntimeException("Etag download for $urlText failed with code ${con.responseCode}") - } - } finally { - con.disconnect() - } - } catch (e: Exception) { - if (thrown == null) { - thrown = e - } else { - thrown.addSuppressed(e) - } - } - - val errorString = "Unable to download from $urlText with etag" - val ex = if (thrown != null) { - PaperweightException(errorString, thrown) - } else { - PaperweightException(errorString) - } - throw ex -} diff --git a/src/main/kotlin/util/git.kt b/src/main/kotlin/util/git.kt index 3bd03a9..8387b3f 100644 --- a/src/main/kotlin/util/git.kt +++ b/src/main/kotlin/util/git.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -37,12 +36,6 @@ class Git(private var repo: File) { } } - val status - get() = this("status", "-z").getText() - - val ref - get() = this("rev-parse", "HEAD").getText().replace('\n', ' ').replace(Regex("\\s+"), "") - operator fun invoke(vararg args: String, disableGpg: Boolean = true): Command { val cmd = if (disableGpg) { arrayOf("git", "-c", "commit.gpgsign=false", *args) @@ -57,17 +50,37 @@ class Git(private var repo: File) { } } -class Command(internal val process: Process, private val command: String) { +class Command(private val process: Process, private val command: String) { - var outStream: OutputStream? = null + private var outStream: OutputStream = UselessOutputStream + private var errStream: OutputStream = UselessOutputStream - fun run(): Int = try { - outStream?.let { out -> - process.inputStream.copyTo(out) + fun run(): Int { + try { + val input = process.inputStream + val error = process.errorStream + val buffer = ByteArray(1000) + + while (process.isAlive) { + // Read both stdout and stderr on the same thread + // This is important for how Gradle outputs the logs + if (input.available() > 0) { + val count = input.read(buffer) + outStream.write(buffer, 0, count) + } + if (error.available() > 0) { + val count = error.read(buffer) + errStream.write(buffer, 0, count) + } + Thread.sleep(1) + } + // Catch any other output we may have missed + outStream.write(input.readBytes()) + errStream.write(error.readBytes()) + return process.waitFor() + } catch (e: Exception) { + throw PaperweightException("Failed to call git command: $command", e) } - process.waitFor() - } catch (e: Exception) { - throw PaperweightException("Failed to call git command: $command", e) } fun runSilently(silenceOut: Boolean = true, silenceErr: Boolean = false): Int { @@ -76,7 +89,7 @@ class Command(internal val process: Process, private val command: String) { } fun runOut(): Int { - setup(System.out, System.out) + setup(System.out, System.err) return run() } @@ -93,8 +106,8 @@ class Command(internal val process: Process, private val command: String) { } private fun silence(silenceOut: Boolean, silenceErr: Boolean) { - val out = if (silenceOut) UselessOutputStream else System.out - val err = if (silenceErr) UselessOutputStream else System.err + val out = if (silenceOut) null else System.out + val err = if (silenceErr) null else System.err setup(out, err) } @@ -104,16 +117,15 @@ class Command(internal val process: Process, private val command: String) { } fun setup(out: OutputStream? = null, err: OutputStream? = null): Command { - outStream = out - if (err != null) { - redirect(process.errorStream, err) - } + outStream = out ?: UselessOutputStream + errStream = err ?: UselessOutputStream return this } fun getText(): String { val out = ByteArrayOutputStream() setup(out, System.err) + execute() return String(out.toByteArray(), Charsets.UTF_8) } diff --git a/src/main/kotlin/util/jvm.kt b/src/main/kotlin/util/jvm.kt index b529b49..d1a1848 100644 --- a/src/main/kotlin/util/jvm.kt +++ b/src/main/kotlin/util/jvm.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,19 +23,12 @@ package io.papermc.paperweight.util import io.papermc.paperweight.PaperweightException -import org.gradle.api.Task import org.gradle.internal.jvm.Jvm import java.io.OutputStream -fun Task.runJar( - jar: Any, - workingDir: Any, - logFile: Any?, - jvmArgs: List = listOf(), - vararg args: String -) { - val jarFile = project.file(jar) - val dir = project.file(workingDir) +fun runJar(jar: Any, workingDir: Any, logFile: Any?, jvmArgs: List = listOf(), vararg args: String) { + val jarFile = jar.convertToFile() + val dir = workingDir.convertToFile() val process = ProcessBuilder( Jvm.current().javaExecutable.canonicalPath, *jvmArgs.toTypedArray(), @@ -47,20 +39,15 @@ fun Task.runJar( val output = when { logFile is OutputStream -> logFile logFile != null -> { - val log = project.file(logFile) + val log = logFile.convertToFile() log.outputStream().buffered() } - else -> null + else -> UselessOutputStream } output.use { - output?.let { - redirect(process.inputStream, it) - redirect(process.errorStream, it) - } ?: run { - redirect(process.inputStream, UselessOutputStream) - redirect(process.errorStream, UselessOutputStream) - } + redirect(process.inputStream, it) + redirect(process.errorStream, it) val e = process.waitFor() if (e != 0) { diff --git a/src/main/kotlin/util/utils.kt b/src/main/kotlin/util/utils.kt index 69fceb9..44933dd 100644 --- a/src/main/kotlin/util/utils.kt +++ b/src/main/kotlin/util/utils.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -29,28 +28,36 @@ import com.github.salomonbrys.kotson.fromJson import com.google.gson.Gson import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.ext.PaperweightExtension +import io.papermc.paperweight.tasks.BaseTask import io.papermc.paperweight.util.Constants.paperTaskOutput import org.cadixdev.lorenz.MappingSet import org.cadixdev.lorenz.io.TextMappingFormat -import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.Project import org.gradle.api.Task -import org.gradle.api.artifacts.Configuration import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.ProjectLayout import org.gradle.api.file.RegularFile import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Provider +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider import java.io.File import java.io.InputStream import java.io.OutputStream +import java.nio.file.Path import java.util.Optional +import kotlin.reflect.KClass +import kotlin.reflect.KProperty val gson: Gson = Gson() -inline val Project.ext: PaperweightExtension +inline fun Gson.fromJson(file: Any): T = + file.convertToFile().bufferedReader().use { fromJson(it) } + +val Project.ext: PaperweightExtension get() = extensions.getByName(Constants.EXTENSION) as PaperweightExtension -inline val Project.cache: File - get() = file(".gradle").resolve(Constants.CACHE_PATH) +val ProjectLayout.cache: File + get() = projectDirectory.file(".gradle/${Constants.CACHE_PATH}").asFile fun writeMappings(format: TextMappingFormat, vararg mappings: Pair) { for ((set, file) in mappings) { @@ -58,8 +65,8 @@ fun writeMappings(format: TextMappingFormat, vararg mappings: Pair this + is Path -> this.toFile() + is RegularFile -> this.asFile + is Provider<*> -> this.get().convertToFile() + else -> throw PaperweightException("Unknown type representing a file: ${this.javaClass.name}") + } +} + +fun ensureParentExists(vararg files: Any) { for (file in files) { - val parent = project.file(file).parentFile + val parent = file.convertToFile().parentFile if (!parent.exists() && !parent.mkdirs()) { throw PaperweightException("Failed to create directory $parent") } } } -fun Task.ensureDeleted(vararg files: Any) { +fun ensureDeleted(vararg files: Any) { for (file in files) { - val f = project.file(file) + val f = file.convertToFile() if (f.exists() && !f.deleteRecursively()) { throw PaperweightException("Failed to delete file $f") } } } -inline fun Project.toProvider(crossinline func: () -> File): Provider { - return layout.file(provider { func() }) +fun BaseTask.defaultOutput(name: String, ext: String): RegularFileProperty { + return objects.fileProperty().convention { + layout.cache.resolve(paperTaskOutput(name, ext)) + } } - -fun Task.defaultOutput(name: String, ext: String): RegularFileProperty { - return project.objects.fileProperty().convention(project.toProvider { - project.cache.resolve(paperTaskOutput(name, ext)) - }) -} -fun Task.defaultOutput(ext: String): RegularFileProperty { +fun BaseTask.defaultOutput(ext: String): RegularFileProperty { return defaultOutput(name, ext) } -fun Task.defaultOutput(): RegularFileProperty { +fun BaseTask.defaultOutput(): RegularFileProperty { return defaultOutput("jar") } val Optional.orNull: T? get() = orElse(null) -val RegularFileProperty.file +val RegularFileProperty.file: File get() = get().asFile -val RegularFileProperty.fileOrNull +val RegularFileProperty.fileOrNull: File? get() = orNull?.asFile -val DirectoryProperty.file +val RegularFileProperty.path: Path + get() = file.toPath() +val DirectoryProperty.file: File get() = get().asFile -val DirectoryProperty.fileOrNull - get() = orNull?.asFile +val DirectoryProperty.path: Path + get() = file.toPath() +inline fun Project.contents(contentFile: Any, crossinline convert: (String) -> T): Provider { + return providers.fileContents(layout.projectDirectory.file(contentFile.convertToFile().absolutePath)) + .asText + .forUseAtConfigurationTime() + .map { convert(it) } +} -private var parsedConfig: McpConfig? = null -fun mcpConfig(file: Provider): McpConfig { - if (parsedConfig != null) { - return parsedConfig as McpConfig - } - parsedConfig = file.get().asFile.bufferedReader().use { reader -> - gson.fromJson(reader) - } - return parsedConfig as McpConfig +// We have to create our own delegate because the ones Gradle provides don't work in plugin dev environments +inline fun TaskContainer.registering(noinline configure: T.() -> Unit): TaskDelegateProvider { + return TaskDelegateProvider(this, T::class, configure) } -fun mcpFile(configFile: RegularFileProperty, path: String): File { - return configFile.file.resolveSibling(path) +class TaskDelegateProvider( + private val container: TaskContainer, + private val type: KClass, + private val configure: T.() -> Unit +) { + operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): TaskDelegate { + val provider = container.register(property.name, type.java, configure) + return TaskDelegate(provider) + } +} +class TaskDelegate(private val provider: TaskProvider) { + operator fun getValue(thisRef: Any?, property: KProperty<*>): TaskProvider = provider } diff --git a/src/main/kotlin/util/zip.kt b/src/main/kotlin/util/zip.kt index 3709b64..deab8a5 100644 --- a/src/main/kotlin/util/zip.kt +++ b/src/main/kotlin/util/zip.kt @@ -3,7 +3,6 @@ * some code and systems originally from ForgeGradle. * * Copyright (C) 2020 Kyle Wood - * Copyright (C) 2018 Forge Development LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,7 +23,7 @@ package io.papermc.paperweight.util import io.papermc.paperweight.PaperweightException -import org.gradle.api.Task +import io.papermc.paperweight.tasks.BaseTask import java.io.File import java.net.URI import java.nio.file.FileSystems @@ -35,26 +34,26 @@ import java.nio.file.SimpleFileVisitor import java.nio.file.attribute.BasicFileAttributes import java.util.concurrent.ThreadLocalRandom -fun Task.unzip(zip: Any, target: Any? = null): File { - val input = project.file(zip) - val outputDir = target?.let { project.file(it) } +fun BaseTask.unzip(zip: Any, target: Any? = null): File { + val input = zip.convertToFile() + val outputDir = target?.convertToFile() ?: input.resolveSibling("${input.name}-" + ThreadLocalRandom.current().nextInt()) - project.copy { - from(project.zipTree(zip)) + fs.copy { + from(archives.zipTree(zip)) into(outputDir) } return outputDir } -fun Task.zip(inputDir: Any, zip: Any) { - val outputZipFile = project.file(zip) +fun zip(inputDir: Any, zip: Any) { + val outputZipFile = zip.convertToFile() if (outputZipFile.exists() && !outputZipFile.delete()) { throw PaperweightException("Could not delete $outputZipFile") } - val dirPath = project.file(inputDir).toPath() + val dirPath = inputDir.convertToFile().toPath() val outUri = URI.create("jar:${outputZipFile.toURI()}") FileSystems.newFileSystem(outUri, mapOf("create" to "true")).use { fs ->