diff --git a/.gitversion.toml b/.gitversion.toml new file mode 100644 index 0000000..44aff5d --- /dev/null +++ b/.gitversion.toml @@ -0,0 +1,3 @@ +[gradlePlugin] +path = "renamer-gradle" +tag = "gradle" diff --git a/build.gradle b/build.gradle index ade38de..20cd631 100644 --- a/build.gradle +++ b/build.gradle @@ -5,19 +5,21 @@ plugins { id 'java-library' id 'eclipse' id 'maven-publish' - id 'com.github.johnrengelman.shadow' version '8.1.1' - id 'net.minecraftforge.licenser' version '1.0.1' - id 'net.minecraftforge.gradleutils' version '[2.3,2.4)' + id 'net.minecraftforge.gradleutils' + alias libs.plugins.gitversion + alias libs.plugins.changelog + alias libs.plugins.licenser + alias libs.plugins.shadow } +gradleutils.displayName = 'Forge Auto Renaming Tool' +description = 'A command line tool to rename java class files' group = 'net.minecraftforge' -version = gradleutils.getTagOffsetBranchVersion(null, 'master', 'HEAD') +version = gitversion.tagOffset println "Version: $version" java { - toolchain { - languageVersion = JavaLanguageVersion.of(8) - } + toolchain.languageVersion = JavaLanguageVersion.of(8) withSourcesJar() } @@ -38,52 +40,31 @@ dependencies { compileOnly(libs.nulls) } -tasks.named('test', Test).configure { +tasks.named('test', Test) { useJUnitPlatform() testLogging { events 'passed', 'skipped', 'failed' } } -tasks.named('jar', Jar).configure { +tasks.named('jar') { manifest { attributes('Main-Class': 'net.minecraftforge.fart.Main') attributes([ - 'Specification-Title': 'ForgeAutoRenamingTool', - 'Specification-Vendor': 'Forge Development LLC', - 'Specification-Version': gradleutils.gitInfo.tag, - 'Implementation-Title': 'ForgeAutoRenamingTool', - 'Implementation-Version': project.version, - 'Implementation-Vendor': 'Forge Development LLC' + 'Specification-Title': 'ForgeAutoRenamingTool', + 'Specification-Vendor': 'Forge Development LLC', + 'Specification-Version': gitversion.info.tag, + 'Implementation-Title': 'ForgeAutoRenamingTool', + 'Implementation-Version': project.version, + 'Implementation-Vendor': 'Forge Development LLC' ], 'net/minecraftforge/fart/') } } -tasks.named('shadowJar', ShadowJar).configure { - manifest { - attributes('Main-Class': 'net.minecraftforge.fart.Main') - attributes([ - 'Specification-Title': 'ForgeAutoRenamingTool', - 'Specification-Vendor': 'Forge Development LLC', - 'Specification-Version': gradleutils.gitInfo.tag, - 'Implementation-Title': 'ForgeAutoRenamingTool', - 'Implementation-Version': project.version, - 'Implementation-Vendor': 'Forge Development LLC' - ], 'net/minecraftforge/fart/') - } - +tasks.named('shadowJar') { minimize() - - final List relocations = [ - 'org.objectweb.asm', - 'net.minecraftforge.srgutils', - 'joptsimple' - ] - - relocations.each { - relocate it, "net.minecraftforge.fart.relocated.$it" - } - + enableAutoRelocation = true + relocationPrefix = 'net.minecraftforge.fart.relocated' // Rewrite JOpt's message files, so that help text is displayed nicely. transform(PropertiesFileTransformer) { paths = [ 'Messages.properties$' ] @@ -91,11 +72,11 @@ tasks.named('shadowJar', ShadowJar).configure { } } -tasks.named('assemble').configure { +tasks.named('assemble') { dependsOn 'shadowJar' } -tasks.named('compileJava', JavaCompile).configure { +tasks.named('compileJava', JavaCompile) { options.encoding = 'UTF-8' } @@ -118,12 +99,12 @@ publishing { description = 'A tool that renames java bytecode elements.' url = 'https://github.com/MinecraftForge/ForgeAutoRenamingTool' - gradleutils.pom.setGitHubDetails(pom, 'ForgeAutoRenamingTool') + gradleutils.pom.addRemoteDetails(pom) - license gradleutils.pom.Licenses.LGPLv2_1 + license gradleutils.pom.licenses.LGPLv2_1 developers { - developer gradleutils.pom.Developers.LexManos + developer gradleutils.pom.developers.LexManos } } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f93135..8bdaf60 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 3fa8f86..2e11132 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a4..ef07e01 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -203,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. @@ -211,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 93e3f59..db3a6ac 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,22 +59,22 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/renamer-gradle-demo/build.gradle b/renamer-gradle-demo/build.gradle new file mode 100644 index 0000000..5a33e62 --- /dev/null +++ b/renamer-gradle-demo/build.gradle @@ -0,0 +1,33 @@ +plugins { + id 'java' + id 'maven-publish' + id 'net.minecraftforge.renamer' +} + +group = 'net.minecraftforge' + +java.toolchain.languageVersion = JavaLanguageVersion.of(17) + +// I dont know why this needs to be here and not in the settings.gradle +repositories { + mavenCentral() + maven { url = 'https://maven.minecraftforge.net' } + flatDir dirs: './libs' +} + +dependencies { + compileOnly libs.log4j.api +} + +publishing { + publications.register('mavenJava', MavenPublication) { + from components.java + } +} + +renamer.rename(jar) { + map = files('libs/mappings_test-1.0.tsrg.gz') +} + +renamer.mappings('test', '1.0') +renamer.rename(jar, 'jarRenameRemote') \ No newline at end of file diff --git a/renamer-gradle-demo/gradlew b/renamer-gradle-demo/gradlew new file mode 100644 index 0000000..ac3f7cc --- /dev/null +++ b/renamer-gradle-demo/gradlew @@ -0,0 +1,2 @@ +#!/bin/sh +../gradlew "$@" \ No newline at end of file diff --git a/renamer-gradle-demo/gradlew.bat b/renamer-gradle-demo/gradlew.bat new file mode 100644 index 0000000..d20a80c --- /dev/null +++ b/renamer-gradle-demo/gradlew.bat @@ -0,0 +1 @@ +../gradlew.bat %* diff --git a/renamer-gradle-demo/libs/mappings_test-1.0.tsrg.gz b/renamer-gradle-demo/libs/mappings_test-1.0.tsrg.gz new file mode 100644 index 0000000..95a67d3 Binary files /dev/null and b/renamer-gradle-demo/libs/mappings_test-1.0.tsrg.gz differ diff --git a/renamer-gradle-demo/settings.gradle b/renamer-gradle-demo/settings.gradle new file mode 100644 index 0000000..f264e49 --- /dev/null +++ b/renamer-gradle-demo/settings.gradle @@ -0,0 +1,29 @@ +pluginManagement { + includeBuild '../renamer-gradle' + + repositories { + mavenCentral() + gradlePluginPortal() + maven { url = 'https://maven.minecraftforge.net' } + //mavenLocal() + } +} + +plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' + id 'net.minecraftforge.gradleutils' version '3.3.13' +} + +rootProject.name = 'renamer-gradle-demo' + +dependencyResolutionManagement { + repositories { + mavenCentral() + maven gradleutils.forgeMaven + } + + versionCatalogs.register('libs') { + version 'log4j', '2.19.0' + library 'log4j-api', 'org.apache.logging.log4j', 'log4j-api' versionRef 'log4j' + } +} diff --git a/renamer-gradle-demo/src/main/java/net/minecraftforge/renamer/gradle/demo/TestClass.java b/renamer-gradle-demo/src/main/java/net/minecraftforge/renamer/gradle/demo/TestClass.java new file mode 100644 index 0000000..f59a7c0 --- /dev/null +++ b/renamer-gradle-demo/src/main/java/net/minecraftforge/renamer/gradle/demo/TestClass.java @@ -0,0 +1,7 @@ +package net.minecraftforge.renamer.gradle.demo; + +public class TestClass { + public static void main(String[] args) { + System.out.println(TestClass.class.getName()); + } +} diff --git a/renamer-gradle/build.gradle b/renamer-gradle/build.gradle new file mode 100644 index 0000000..1fdf15a --- /dev/null +++ b/renamer-gradle/build.gradle @@ -0,0 +1,112 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin +import org.gradle.api.attributes.plugin.GradlePluginApiVersion + +plugins { + id 'java-gradle-plugin' + id 'idea' + id 'eclipse' + id 'maven-publish' + id 'net.minecraftforge.gradleutils' + alias libs.plugins.gitversion + alias libs.plugins.changelog + alias libs.plugins.licenser + alias libs.plugins.plugin.publish + alias libs.plugins.shadow +} + +gradleutils.displayName = 'Renamer Gradle Plugin' +description = 'Utilities for renaming java related files.' +base.archivesName = 'renamer-gradle' +group = 'net.minecraftforge' +version = gitversion.tagOffset + +java { + toolchain.languageVersion = JavaLanguageVersion.of(17) + withSourcesJar() + //withJavadocJar() +} + +gradleutils.pluginDevDefaults(configurations, libs.versions.gradle) + +dependencies { + // Static Analysis + compileOnly libs.nulls + compileOnly libs.jetbrains + + // Gradle API + compileOnly libs.gradle + + // GradleUtils Shared Base + implementation libs.gradleutils.shared +} + +license { + header = rootProject.file('../LICENSE-header.txt') + newLine = false + exclude '**/*.properties' +} + +tasks.named('jar', Jar) { + archiveClassifier = 'thin' +} + +tasks.named('shadowJar', ShadowJar) { + enableAutoRelocation = true + archiveClassifier = null + relocationPrefix = 'net.minecraftforge.renamer.gradle.shadow' +} + +/* +tasks.withType(Javadoc).configureEach { + javadocTool = javaToolchains.javadocToolFor { languageVersion = JavaLanguageVersion.of(24) } +} +tasks.named('javadocJar') { + // Remove 4MB from javadocs jar + exclude 'resource-files/fonts/' +} +*/ + +changelog { + fromBase() +} + +gradlePlugin { + website = gitversion.url + vcsUrl = gitversion.url + '.git' + + plugins.register('renamer') { + id = 'net.minecraftforge.renamer' + implementationClass = 'net.minecraftforge.renamer.gradle.RenamerPlugin' + displayName = gradleutils.displayName + description = project.description + tags = ['minecraftforge'] + } +} + +publishing { + repositories { + maven gradleutils.getPublishingForgeMaven(rootProject.file('../repo')) + } + + publications.register('pluginMaven', MavenPublication) { + changelog.publish(it) + gradleutils.promote(it) + + pom { pom -> + name = gradleutils.displayName + description = project.description + + gradleutils.pom.addRemoteDetails(pom) + + licenses { + license gradleutils.pom.licenses.LGPLv2_1 + } + + developers { + developer gradleutils.pom.developers.Jonathing + developer gradleutils.pom.developers.LexManos + } + } + } +} diff --git a/renamer-gradle/settings.gradle b/renamer-gradle/settings.gradle new file mode 100644 index 0000000..64daf6b --- /dev/null +++ b/renamer-gradle/settings.gradle @@ -0,0 +1,38 @@ +plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' + id 'net.minecraftforge.gradleutils' version '3.3.39' // https://plugins.gradle.org/plugin/net.minecraftforge.gradleutils +} + +rootProject.name = 'renamer-gradle' + +dependencyResolutionManagement { + repositories { + mavenCentral() + maven gradleutils.forgeMaven + maven { url = 'https://maven.moddinglegacy.com/maven' } // Gradle API + //mavenLocal() + } + + //@formatter:off + versionCatalogs.register('libs') { + plugin 'licenser', 'net.minecraftforge.licenser' version '1.2.0' // https://plugins.gradle.org/plugin/net.minecraftforge.licenser + plugin 'gitversion', 'net.minecraftforge.gitversion' version '3.1.0' // https://plugins.gradle.org/plugin/net.minecraftforge.changelog + plugin 'changelog', 'net.minecraftforge.changelog' version '3.1.1' // https://plugins.gradle.org/plugin/net.minecraftforge.changelog + plugin 'plugin-publish', 'com.gradle.plugin-publish' version '1.3.1' // https://plugins.gradle.org/plugin/com.gradle.plugin-publish + plugin 'shadow', 'com.gradleup.shadow' version '9.3.0' // https://plugins.gradle.org/plugin/com.gradleup.shadow + + // Static Analysis + library 'nulls', 'org.jspecify', 'jspecify' version '1.0.0' + library 'jetbrains', 'org.jetbrains', 'annotations' version '26.0.2' + + // Gradle API + // Original: https://github.com/remal-gradle-api/packages/packages/760197?version=9.0.0 + // Mirror: https://repos.moddinglegacy.com/#/modding-legacy/name/remal/gradle-api/gradle-api/9.0.0 + version 'gradle', '9.0.0' + library 'gradle', 'name.remal.gradle-api', 'gradle-api' versionRef 'gradle' + + // GradleUtils Shared Base + library 'gradleutils-shared', 'net.minecraftforge', 'gradleutils-shared' version '3.3.39' + } + //@formatter:on +} diff --git a/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/Constants.java b/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/Constants.java new file mode 100644 index 0000000..9e62c5f --- /dev/null +++ b/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/Constants.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.renamer.gradle; + +import java.util.List; + +final class Constants { + private Constants() { } + + static final String ARTIFACT = "net.minecraftforge:ForgeAutoRenamingTool:1.1.2:all"; + static final int JAVA_VERSION = 8; + + static final List DEFAULT_ARGS = List.of( + "--input", "{input}", + "--output", "{output}", + "--map", "{map}", + "--lib={library}" + ); +} diff --git a/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerExtension.java b/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerExtension.java new file mode 100644 index 0000000..6d520c0 --- /dev/null +++ b/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerExtension.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.renamer.gradle; + +import org.gradle.api.Action; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.tasks.bundling.AbstractArchiveTask; +import org.jspecify.annotations.Nullable; + +/// The extension interface for the Renamer Gradle plugin. +public sealed interface RenamerExtension permits RenamerExtensionInternal { + /// The name for this extension when added to [projects][org.gradle.api.Project]. + String NAME = "renamer"; + + default void mappings(String channel, String version) { + mappings("net.minecraft:mappings_" + channel + ':' + version + "@tsrg.gz"); + } + + void mappings(String artifact); + void mappings(Dependency dependency); + + default void rename(AbstractArchiveTask task) { + rename(task, (Action)null); + } + + default void rename(AbstractArchiveTask task, @Nullable Action config) { + rename(task, task.getName() + "Rename", config); + } + + default void rename(AbstractArchiveTask task, String name) { + rename(task, name, null); + } + + void rename(AbstractArchiveTask task, String name, @Nullable Action config); + + void setTool(String coords); + + String getTool(); +} diff --git a/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerExtensionImpl.java b/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerExtensionImpl.java new file mode 100644 index 0000000..6c33c8f --- /dev/null +++ b/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerExtensionImpl.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.renamer.gradle; + +import org.gradle.api.Action; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.provider.ProviderFactory; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.bundling.AbstractArchiveTask; +import org.gradle.jvm.toolchain.JavaToolchainService; +import org.jspecify.annotations.Nullable; + +import java.io.File; + +import javax.inject.Inject; + +abstract class RenamerExtensionImpl implements RenamerExtensionInternal { + @SuppressWarnings("unused") + private static final Logger LOGGER = Logging.getLogger(RenamerExtension.class); + private final Project project; + private final RenamerProblems problems = this.getObjects().newInstance(RenamerProblems.class); + + private Configuration toolConfig; + private String tool; + private Configuration mapConfig; + + protected abstract @Inject ObjectFactory getObjects(); + protected abstract @Inject ProviderFactory getProviders(); + protected abstract @Inject JavaToolchainService getJavaToolchains(); + + @Inject + public RenamerExtensionImpl(RenamerPlugin plugin, Project project) { + this.project = project; + this.setTool(Constants.ARTIFACT); + } + + @Override + public void setTool(String artifact) { + this.tool = artifact; + var dep = this.project.getDependencies().create(artifact); + this.toolConfig = this.project.getConfigurations().detachedConfiguration(dep); + this.toolConfig.setTransitive(false); + } + + @Override + public String getTool() { + return this.tool; + } + + @Override + public void mappings(String artifact) { + mappings(this.project.getDependencies().create(artifact)); + } + + @Override + public void mappings(Dependency dependency) { + this.mapConfig = this.project.getConfigurations().detachedConfiguration(dependency); + this.mapConfig.setTransitive(false); + } + + @Override + public void rename(AbstractArchiveTask source, String name, @Nullable Action config) { + var existing = project.getTasks().findByName(name); + if (existing != null) { + problems.reportIllegalTaskName(existing, name); + return; + } + + project.getTasks().register(name, RenamerTask.class).configure(task -> { + // We need the compile class path so we can calculate inheritance properly. + var javaExtension = project.getExtensions().getByType(JavaPluginExtension.class); + SourceSet mainSourceSet = javaExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME); + task.getClasspath().setFrom(mainSourceSet.getCompileClasspath()); + + // Set the default output file to the same as the input task, but with an extra "-renamed" classifier + task.getOutput().convention(this.project.getLayout().file(this.getProviders().provider(() -> { + var sourceFile = source.getArchiveFile().get().getAsFile(); + var sourceName = sourceFile.getName(); + var ext = source.getArchiveExtension().getOrNull(); + String fileName; + if (ext == null || ext.isEmpty()) + fileName = sourceName + "-renamed"; + else + fileName = sourceName.substring(0, sourceName.length() - 1 - ext.length()) + "-renamed." + ext; + + //LOGGER.lifecycle("Output Provider: " + new File(sourceFile.getParentFile(), fileName)); + return new File(sourceFile.getParentFile(), fileName); + }))); + + task.getInput().convention(source.getArchiveFile()); + if (this.mapConfig != null) // Explicitly allow null, in case people want to configure the map in the closure + task.getMap().setFrom(this.mapConfig); + task.getToolClasspath().setFrom(this.toolConfig); + task.getLogFile().convention(task.getDefaultLogFile()); + task.getWorkingDir().convention(this.project.getLayout().getBuildDirectory()); + task.getArgs().convention(Constants.DEFAULT_ARGS); + task.getJavaLauncher().convention(getJavaToolchains().launcherFor(javaExtension.getToolchain())); + + if (config != null) + config.execute(task); + }); + } +} diff --git a/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerExtensionInternal.java b/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerExtensionInternal.java new file mode 100644 index 0000000..718f42c --- /dev/null +++ b/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerExtensionInternal.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.renamer.gradle; + +import org.gradle.api.reflect.HasPublicType; +import org.gradle.api.reflect.TypeOf; + +non-sealed interface RenamerExtensionInternal extends RenamerExtension, HasPublicType { + @Override + default TypeOf getPublicType() { + return TypeOf.typeOf(RenamerExtension.class); + } +} diff --git a/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerPlugin.java b/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerPlugin.java new file mode 100644 index 0000000..54b476c --- /dev/null +++ b/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerPlugin.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.renamer.gradle; + +import net.minecraftforge.gradleutils.shared.EnhancedPlugin; +import org.gradle.api.Project; + +import javax.inject.Inject; + +abstract class RenamerPlugin extends EnhancedPlugin { + static final String DISPLAY_NAME = "Renamer Gradle"; + + @Inject + public RenamerPlugin() { + super(RenamerExtension.NAME, DISPLAY_NAME); + } + + @Override + public void setup(Project project) { + project.getExtensions().create(RenamerExtension.NAME, RenamerExtensionImpl.class, this, project); + } +} diff --git a/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerProblems.java b/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerProblems.java new file mode 100644 index 0000000..abb5683 --- /dev/null +++ b/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerProblems.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.renamer.gradle; + +import net.minecraftforge.gradleutils.shared.EnhancedProblems; +import javax.inject.Inject; + +import org.gradle.api.Task; +import org.gradle.api.problems.Severity; + +import java.io.Serial; + +abstract class RenamerProblems extends EnhancedProblems { + private static final @Serial long serialVersionUID = -5334414678185075096L; + + @Inject + public RenamerProblems() { + super(RenamerExtension.NAME, RenamerPlugin.DISPLAY_NAME); + } + + void reportIllegalTaskName(Task existing, String name) { + this.getLogger().error("ERROR: Cannot register renamer task {}, name already exists", name); + this.report("rename-duplicate-task-name", "Cannot register renamer task", spec -> spec + .details(""" + Cannot register renamer task, as a task with that name already exists. + Name: %s""" + .formatted(name)) + .severity(Severity.ERROR) + .stackLocation() + .solution("Use the `renamer.rename` methods that take in a explicit task name.") + .solution(HELP_MESSAGE)); + } + + void reportNoMainClass(RenamerTask task) { + this.getLogger().error("ERROR: Failed to find Main-Class for Renamer Tool"); + this.report("rename-no-main-class", "Renamer tool not executable jar", spec -> spec + .details(""" + When using a custom renamer tool, it must be a executable jar. With no transitive depdencies. + If this is not the case, then you must specify the main class using %s.mainClass = 'some.class' + """.formatted(task.getName())) + .severity(Severity.ERROR) + .stackLocation() + .solution("Specify main class for " + task.getName()) + .solution(HELP_MESSAGE)); + } + + void reportMultipleMapFiles(RenamerTask task) { + this.getLogger().error("ERROR: Failed to find Mapping File"); + this.report("rename-multiple-map-files", "Renamer Map File returned to many files", spec -> spec + .details(""" + Only expected one file for the renaming map task. If using a configuration to resolve the file + be sure to disable transtive dependencies. + """.formatted(task.getName())) + .severity(Severity.ERROR) + .stackLocation() + .solution("Specify one map file for " + task.getName()) + .solution(HELP_MESSAGE)); + } +} diff --git a/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerTask.java b/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerTask.java new file mode 100644 index 0000000..31ccb85 --- /dev/null +++ b/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/RenamerTask.java @@ -0,0 +1,219 @@ +package net.minecraftforge.renamer.gradle; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.jar.JarFile; + +import javax.inject.Inject; + +import org.gradle.api.DefaultTask; +import org.gradle.api.Project; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.logging.LogLevel; +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.ProviderFactory; +import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.Nested; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; +import org.gradle.jvm.toolchain.JavaLauncher; +import org.gradle.jvm.toolchain.JavaToolchainService; +import org.gradle.process.ExecOperations; +import org.gradle.process.ExecResult; + +import net.minecraftforge.gradleutils.shared.EnhancedPlugin; +import net.minecraftforge.gradleutils.shared.EnhancedTask; +import net.minecraftforge.gradleutils.shared.SharedUtil; + +public abstract class RenamerTask extends DefaultTask implements EnhancedTask { + @SuppressWarnings("unused") + private static final Logger LOGGER = Logging.getLogger(RenamerExtension.class); + + private final RenamerProblems problems = this.getObjects().newInstance(this.problemsType()); + + public @InputFile abstract RegularFileProperty getInput(); + public @InputFiles @Classpath abstract ConfigurableFileCollection getMap(); + public @InputFiles @Classpath abstract ConfigurableFileCollection getToolClasspath(); + public @InputFiles @Classpath abstract ConfigurableFileCollection getClasspath(); + public @Input abstract ListProperty getArgs(); + public @Nested abstract Property getJavaLauncher(); + public @OutputFile abstract RegularFileProperty getOutput(); + + public @Internal abstract RegularFileProperty getLogFile(); + public @Internal abstract DirectoryProperty getWorkingDir(); + + protected abstract @Inject ProviderFactory getProviders(); + protected abstract @Inject JavaToolchainService getJavaToolchains(); + protected abstract @Inject ObjectFactory getObjects(); + protected abstract @Inject ExecOperations getExecOperations(); + + @Override + public Class> pluginType() { + return RenamerPlugin.class; + } + + @Override + public Class problemsType() { + return RenamerProblems.class; + } + + @TaskAction + protected ExecResult run() throws IOException { + var logger = getLogger(); + + var stdOutLevel = LogLevel.LIFECYCLE; + var stdErrLevel = LogLevel.ERROR; + + var javaLauncher = getJavaLauncher().get(); + + var workingDirectory = this.getWorkingDir().map(problems.ensureFileLocation()).get().getAsFile(); + + try (var log = new PrintWriter(new FileWriter(this.getLogFile().getAsFile().get()), true)) { + return getExecOperations().javaexec(spec -> { + spec.setIgnoreExitValue(true); + spec.setWorkingDir(workingDirectory); + spec.setClasspath(this.getToolClasspath()); + spec.getMainClass().set(this.getMainClass()); + spec.setExecutable(javaLauncher.getExecutablePath().getAsFile().getAbsolutePath()); + spec.setArgs(this.fillArgs()); + //spec.setJvmArgs(jvmArgs); + //spec.setEnvironment(this.environment); + //spec.setSystemProperties(this.systemProperties); + + spec.setStandardOutput(SharedUtil.toLog( + line -> { + logger.log(stdOutLevel, line); + log.println(line); + } + )); + spec.setErrorOutput(SharedUtil.toLog( + line -> { + logger.log(stdErrLevel, line); + log.println(line); + } + )); + + log.print("Java Launcher: "); + log.println(spec.getExecutable()); + log.print("Working directory: "); + log.println(spec.getWorkingDir().getAbsolutePath()); + log.print("Main class: "); + log.println(spec.getMainClass().get()); + log.println("Arguments:"); + for (var s : spec.getArgs()) { + log.print(" "); + log.println(s); + } + log.println("JVM Arguments:"); + for (var s : spec.getAllJvmArgs()) { + log.print(" "); + log.println(s); + } + log.println("Classpath:"); + for (var f : getClasspath()) { + log.print(" "); + log.println(f.getAbsolutePath()); + } + log.println("===================================="); + }); + } + + } + + private File getMapFile() { + try { + return this.getMap().getSingleFile(); + } catch (IllegalStateException exception) { + problems.reportMultipleMapFiles(this); + throw exception; + } + } + + private static String path(RegularFileProperty prop) { + return prop.getAsFile().get().getAbsolutePath(); + } + + private List fillArgs() { + record Value(String value, List values) {} + var map = new HashMap(); + map.put("input", new Value(path(this.getInput()), null)); + map.put("output", new Value(path(this.getOutput()), null)); + map.put("map", new Value(this.getMapFile().getAbsolutePath(), null)); + + var libs = new ArrayList(); + this.getClasspath().forEach(file -> libs.add(file.getAbsolutePath())); + map.put("library", new Value(null, libs)); + + var args = new ArrayList(); + for (var arg : this.getArgs().get()) { + var start = arg.indexOf('{'); + if (start == -1) { + args.add(arg); + continue; + } + + var end = arg.indexOf('}', start); + if (end == -1) + throw new IllegalArgumentException("Unmatched variable replacement in " + this.getArgs().get()); + + var key = arg.substring(start + 1, end); + var value = map.get(key); + if (value == null) + throw new IllegalArgumentException("Unknown variable replacement: " + key); + + var prefix = arg.substring(0, start); + var suffix = arg.substring(end + 1); + + if (value.value != null) + args.add(prefix + value.value + suffix); + else if (value.values != null) { + for (var data : value.values) + args.add(prefix + data + suffix); + } else + throw new IllegalStateException("Replacement " + key + " did not have a value"); + } + return args; + } + + private String getMainClass() { + File tool = null; + try { + tool = this.getToolClasspath().getSingleFile(); + } catch (IllegalStateException exception) { + problems.reportNoMainClass(this); + throw exception; + } + + try (var jar = new JarFile(tool)) { + var manifest = jar.getManifest(); + if (manifest == null) { + problems.reportNoMainClass(this); + throw new IllegalStateException("Tool jar does not have manifest: " + tool.getAbsolutePath()); + } + var mainClass = manifest.getMainAttributes().getValue("Main-Class"); + if (mainClass == null) { + problems.reportNoMainClass(this); + throw new IllegalStateException("Tool jar does not have Main-Class entry in its Manifest: " + tool.getAbsolutePath()); + } + return mainClass; + } catch (IOException e) { + problems.reportNoMainClass(this); + throw new IllegalStateException("Tool jar does not have Main-Class entry in its Manifest: " + tool.getAbsolutePath(), e); + } + } +} diff --git a/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/package-info.java b/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/package-info.java new file mode 100644 index 0000000..faeb8e4 --- /dev/null +++ b/renamer-gradle/src/main/java/net/minecraftforge/renamer/gradle/package-info.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ +/// The Renamer Gradle plugin adds utilities making renaming Jar archives easier. +/// Mainly useful for Minecraft mod development where the runtime environment is +/// different then the development environment. +@NullMarked +package net.minecraftforge.renamer.gradle; + +import org.jspecify.annotations.NullMarked; diff --git a/settings.gradle b/settings.gradle index 367bce9..52daae9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,38 +1,45 @@ -pluginManagement { - repositories { - gradlePluginPortal() - maven { - name = 'MinecraftForge' - url = 'https://maven.minecraftforge.net/' - } - } -} - plugins { - id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0' + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' + id 'net.minecraftforge.gradleutils' version '3.3.39' // https://plugins.gradle.org/plugin/net.minecraftforge.gradleutils } dependencyResolutionManagement { - versionCatalogs { - libs { - library('jopt-simple', 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3') - library('srgutils', 'net.minecraftforge:srgutils:0.6.0') - library('nulls', 'org.jetbrains:annotations:24.0.1') - library('powermock', 'org.powermock:powermock-core:2.0.9') + repositories { + mavenCentral() + maven gradleutils.forgeMaven + maven { url = 'https://maven.moddinglegacy.com/maven' } // Gradle API + //mavenLocal() + } + + //@formatter:off + versionCatalogs.register('libs') { + plugin 'licenser', 'net.minecraftforge.licenser' version '1.2.0' // https://plugins.gradle.org/plugin/net.minecraftforge.licenser + plugin 'gitversion', 'net.minecraftforge.gitversion' version '3.1.0' // https://plugins.gradle.org/plugin/net.minecraftforge.changelog + plugin 'changelog', 'net.minecraftforge.changelog' version '3.1.1' // https://plugins.gradle.org/plugin/net.minecraftforge.changelog + plugin 'plugin-publish', 'com.gradle.plugin-publish' version '1.3.1' // https://plugins.gradle.org/plugin/com.gradle.plugin-publish + plugin 'shadow', 'com.gradleup.shadow' version '9.3.0' // https://plugins.gradle.org/plugin/com.gradleup.shadow + + library('jopt-simple', 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3') + library('srgutils', 'net.minecraftforge:srgutils:0.6.0') + library('nulls', 'org.jetbrains:annotations:24.0.1') + library('powermock', 'org.powermock:powermock-core:2.0.9') - version('asm', '9.7.1') - library('asm', 'org.ow2.asm', 'asm' ).versionRef('asm') - library('asm-commons', 'org.ow2.asm', 'asm-commons').versionRef('asm') - library('asm-tree', 'org.ow2.asm', 'asm-tree' ).versionRef('asm') - bundle('asm', ['asm', 'asm-commons', 'asm-tree']) + version('asm', '9.7.1') + library('asm', 'org.ow2.asm', 'asm' ).versionRef('asm') + library('asm-commons', 'org.ow2.asm', 'asm-commons').versionRef('asm') + library('asm-tree', 'org.ow2.asm', 'asm-tree' ).versionRef('asm') + bundle('asm', ['asm', 'asm-commons', 'asm-tree']) - version('junit', '5.10.1') - library('junit-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit') - library('junit-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef('junit') - library('junit-platform-launcher', 'org.junit.platform:junit-platform-launcher:1.10.1') - bundle('junit-runtime', ['junit-engine', 'junit-platform-launcher']) - } + version('junit', '5.10.1') + library('junit-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit') + library('junit-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef('junit') + library('junit-platform-launcher', 'org.junit.platform:junit-platform-launcher:1.10.1') + bundle('junit-runtime', ['junit-engine', 'junit-platform-launcher']) } + //@formatter:on } rootProject.name = 'ForgeAutoRenamingTool' + +includeBuild 'renamer-gradle' +includeBuild 'renamer-gradle-demo'