diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 418b80c9..f4b66fec 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -3,36 +3,42 @@ name: Build and test on: pull_request: - branches: [ master, master-fork, main ] + branches: [ master, main, v1.x ] push: - branches: [ master, master-fork, main ] + branches: [ master, main, v1.x ] jobs: build-and-test: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v3 + uses: actions/checkout@v5 with: fetch-depth: 0 - - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@v3 - - - name: Set up JDK 8 and 17 - uses: actions/setup-java@v3 + - name: Set up JDK versions + uses: actions/setup-java@v5 with: java-version: | + 21 8 17 distribution: 'zulu' cache: gradle + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v5 + with: + build-scan-publish: true + build-scan-terms-of-use-url: "https://gradle.com/terms-of-service" + build-scan-terms-of-use-agree: "yes" + validate-wrappers: true + - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build the Gradle plugin - run: ./gradlew --info --stacktrace assemble + run: ./gradlew --build-cache --info --stacktrace assemble - name: Test the plugin - run: ./gradlew --info --stacktrace check + run: ./gradlew --build-cache --info --stacktrace check diff --git a/.github/workflows/publish-javadoc.yml b/.github/workflows/publish-javadoc.yml index 15d389b9..8a34858f 100644 --- a/.github/workflows/publish-javadoc.yml +++ b/.github/workflows/publish-javadoc.yml @@ -17,24 +17,30 @@ jobs: javadoc: concurrency: javadoc-publish runs-on: ubuntu-latest - outputs: - result_hash: ${{ steps.deploy.outputs.hash }} steps: - - uses: actions/checkout@v3 + - name: Checkout repo + uses: actions/checkout@v5 with: fetch-depth: 0 - - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@v3 - - - uses: actions/setup-java@v3 + - name: Set up JDK versions + uses: actions/setup-java@v5 with: java-version: | + 21 8 17 distribution: 'zulu' cache: gradle + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v5 + with: + build-scan-publish: true + build-scan-terms-of-use-url: "https://gradle.com/terms-of-service" + build-scan-terms-of-use-agree: "yes" + validate-wrappers: true + - name: Grant execute permission for gradlew run: chmod +x gradlew @@ -62,8 +68,7 @@ jobs: git config user.email "${GITHUB_ACTOR}@users.noreply.github.com" git commit -m "Deploy javadoc from ref $TARGET_FOLDER" git push - echo "::set-output name=hash::$(git rev-parse HEAD)" env: TARGET_FOLDER: ${{ github.ref_name }} - COMMIT_AUTHOR: ${{ github.pusher.name }} - COMMIT_EMAIL: ${{ github.pusher.email }} + COMMIT_AUTHOR: ${{ github.event.push.pusher.name }} + COMMIT_EMAIL: ${{ github.event.push.pusher.email }} diff --git a/.github/workflows/release-tags.yml b/.github/workflows/release-tags.yml index bfaf4b05..330d9c70 100644 --- a/.github/workflows/release-tags.yml +++ b/.github/workflows/release-tags.yml @@ -13,29 +13,35 @@ jobs: release-build: runs-on: ubuntu-latest steps: - - name: Checkout mod repo - uses: actions/checkout@v3 + - name: Checkout repo + uses: actions/checkout@v5 with: fetch-depth: 0 - - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@v3 - - - name: Set release version - run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - - - name: Set up JDK 8 and 17 - uses: actions/setup-java@v3 + - name: Set up JDK versions + uses: actions/setup-java@v5 with: java-version: | + 21 8 17 distribution: 'zulu' cache: gradle + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v5 + with: + build-scan-publish: true + build-scan-terms-of-use-url: "https://gradle.com/terms-of-service" + build-scan-terms-of-use-agree: "yes" + validate-wrappers: true + - name: Grant execute permission for gradlew run: chmod +x gradlew + - name: Set release version + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + - name: Build the Gradle plugin run: ./gradlew --info --stacktrace assemble diff --git a/.github/workflows/validate-gradle-wrapper.yml b/.github/workflows/validate-gradle-wrapper.yml deleted file mode 100644 index f8463eac..00000000 --- a/.github/workflows/validate-gradle-wrapper.yml +++ /dev/null @@ -1,27 +0,0 @@ -# Validates the integrity of the Gradle Wrapper -name: Validate Gradle Wrapper - -on: - push: - branches: - - master - paths: - - 'gradle/**' - pull_request: - paths: - - 'gradle/**' - -concurrency: - group: gradle-wrapper-validation-${{ github.head_ref || github.ref }} - cancel-in-progress: true - -jobs: - Validation: - runs-on: ubuntu-latest - - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@v3 diff --git a/.gitignore b/.gitignore index 6a02b970..29b04a8e 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ whitelist.json *.bat *.DS_Store !gradlew.bat +/.vs/ diff --git a/build.gradle.kts b/build.gradle.kts index 1671c9c1..d4b2f498 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,9 +1,9 @@ plugins { `java-gradle-plugin` - id("com.palantir.git-version") version "3.0.0" + id("com.palantir.git-version") version "3.4.0" `maven-publish` - id("com.diffplug.spotless") version "6.25.0" - id("com.github.gmazzo.buildconfig") version "5.3.5" + id("com.diffplug.spotless") version "8.0.0" + id("com.github.gmazzo.buildconfig") version "5.6.8" } val gitVersion: groovy.lang.Closure by extra @@ -22,6 +22,7 @@ repositories { } mavenCentral() gradlePluginPortal() + mavenLocal() } fun pluginDep(name: String, version: String): String { @@ -29,34 +30,36 @@ fun pluginDep(name: String, version: String): String { } dependencies { + // JDOM2 for XML processing + implementation("org.jdom:jdom2:2.0.6.1") + annotationProcessor("com.github.bsideup.jabel:jabel-javac-plugin:1.0.1") testAnnotationProcessor("com.github.bsideup.jabel:jabel-javac-plugin:1.0.1") compileOnly("com.github.bsideup.jabel:jabel-javac-plugin:1.0.1") { isTransitive = false } // workaround for https://github.com/bsideup/jabel/issues/174 - annotationProcessor("net.java.dev.jna:jna-platform:5.13.0") + annotationProcessor("net.java.dev.jna:jna-platform:5.18.1") // All these plugins will be present in the classpath of the project using our plugin, but not activated until explicitly applied - api(pluginDep("com.gtnewhorizons.retrofuturagradle","1.4.2")) + api(pluginDep("com.gtnewhorizons.retrofuturagradle","1.4.9")) // Settings plugins api(pluginDep("com.diffplug.blowdryerSetup", "1.7.1")) api(pluginDep("org.gradle.toolchains.foojay-resolver-convention", "0.9.0")) // Project plugins - api(pluginDep("com.github.johnrengelman.shadow", "8.1.1")) - api(pluginDep("com.palantir.git-version", "3.0.0")) - api(pluginDep("org.jetbrains.gradle.plugin.idea-ext", "1.1.8")) - api(pluginDep("org.jetbrains.kotlin.jvm", "2.0.10")) - api(pluginDep("org.jetbrains.kotlin.kapt", "2.0.10")) - api(pluginDep("com.google.devtools.ksp", "2.0.10-1.0.24")) + api(pluginDep("com.gradleup.shadow", "8.3.9")) + api(pluginDep("com.palantir.git-version", "3.4.0")) + api(pluginDep("org.jetbrains.gradle.plugin.idea-ext", "1.1.10")) + api(pluginDep("org.jetbrains.kotlin.jvm", "2.1.10")) + api(pluginDep("org.jetbrains.kotlin.kapt", "2.1.10")) + api(pluginDep("com.google.devtools.ksp", "2.1.10-1.0.29")) // 1.0.29 is the last jvm8 supporting version api(pluginDep("org.ajoberstar.grgit", "4.1.1")) // 4.1.1 is the last jvm8 supporting version, unused, available for addon.gradle - api(pluginDep("io.github.goooler.shadow", "8.1.7")) api(pluginDep("de.undercouch.download", "5.6.0")) - api(pluginDep("com.github.gmazzo.buildconfig", "3.1.0")) // Unused, available for addon.gradle - api(pluginDep("com.modrinth.minotaur", "2.8.7")) - api(pluginDep("net.darkhax.curseforgegradle", "1.1.24")) + api(pluginDep("com.github.gmazzo.buildconfig", "5.5.4")) // 5.5.4 is the last jvm8 supporting version, unused, available for addon.gradle + api(pluginDep("com.modrinth.minotaur", "2.8.8")) + api(pluginDep("net.darkhax.curseforgegradle", "1.1.26")) - testImplementation("org.junit.jupiter:junit-jupiter:5.9.3") + testImplementation("org.junit.jupiter:junit-jupiter:5.14.0") testRuntimeOnly("org.junit.platform:junit-platform-launcher") } @@ -99,7 +102,7 @@ spotless { target(".gitignore") trimTrailingWhitespace() - indentWithSpaces(4) + leadingTabsToSpaces(4) endWithNewline() } java { @@ -107,6 +110,7 @@ spotless { toggleOffOn() removeUnusedImports() + forbidWildcardImports() trimTrailingWhitespace() eclipse("4.19").configFile("spotless.eclipseformat.xml") } @@ -129,32 +133,37 @@ java { } tasks.javadoc { javadocTool.set(javaToolchains.javadocToolFor { - languageVersion.set(JavaLanguageVersion.of(17)) + languageVersion.set(JavaLanguageVersion.of(21)) vendor.set(JvmVendorSpec.AZUL) }) with(options as StandardJavadocDocletOptions) { links( "https://docs.gradle.org/${gradle.gradleVersion}/javadoc/", - "https://docs.oracle.com/en/java/javase/17/docs/api/" + "https://docs.oracle.com/en/java/javase/21/docs/api/" ) } } tasks.withType { - sourceCompatibility = "17" // for the IDE support + sourceCompatibility = "21" // for the IDE support options.release.set(8) options.encoding = "UTF-8" javaCompiler.set(javaToolchains.compilerFor { - languageVersion.set(JavaLanguageVersion.of(17)) + languageVersion.set(JavaLanguageVersion.of(21)) vendor.set(JvmVendorSpec.AZUL) }) } tasks.wrapper.configure { - gradleVersion = "8.5" + gradleVersion = "8.14.3" distributionType = Wrapper.DistributionType.ALL } +tasks.updateDaemonJvm.configure { + languageVersion = JavaLanguageVersion.of(21) + vendor.set(JvmVendorSpec.AZUL) +} + configurations["functionalTestRuntimeOnly"].extendsFrom(configurations["testRuntimeOnly"]) configurations["functionalTestImplementation"].extendsFrom(configurations["testImplementation"]) configurations["functionalTestAnnotationProcessor"].extendsFrom(configurations["testAnnotationProcessor"]) @@ -224,10 +233,10 @@ publishing { */ maven { name = "vogRepository" - url = uri("https://mvn.taskeren.cn/snapshots") + url = uri("https://maven.elytra.cn") credentials { - username = System.getenv("MAVEN_USER") ?: "NONE" - password = System.getenv("MAVEN_PASSWORD") ?: "NONE" + username = project.findProperty("MAVEN_USERNAME") as? String ?: "NONE" + password = project.findProperty("MAVEN_PASSWORD") as? String ?: "NONE" } } } diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties new file mode 100644 index 00000000..7383a9ca --- /dev/null +++ b/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,13 @@ +#This file is generated by updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/b7af3dd6719c6b2ce0922ab7745cf065/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/ae0e7983130f400b4eeba0c21a169149/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/b7af3dd6719c6b2ce0922ab7745cf065/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/ae0e7983130f400b4eeba0c21a169149/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/178ef5b6ab94e21a230e9192a08218b6/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/e0839da937c009a667e9601d79d14c0e/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/b7af3dd6719c6b2ce0922ab7745cf065/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/ae0e7983130f400b4eeba0c21a169149/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e7f07493b690e7efd5d5bcce5f684777/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/b10efa41b5993aec43f4cd5f94d3583c/redirect +toolchainVendor=AZUL +toolchainVersion=21 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b95..1b33c55b 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 c1d5e018..7705927e 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.11.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f5feea6d..23d15a93 100755 --- a/gradlew +++ b/gradlew @@ -86,8 +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 -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || 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 @@ -115,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. @@ -206,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. @@ -214,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 9d21a218..db3a6ac2 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,11 @@ 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/settings.gradle.kts b/settings.gradle.kts index 36d20865..0951ad70 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -6,12 +6,10 @@ pluginManagement { maven { // RetroFuturaGradle name = "GTNH Maven" - url = uri("http://jenkins.usrv.eu:8081/nexus/content/groups/public/") - isAllowInsecureProtocol = true + url = uri("https://nexus.gtnewhorizons.com/repository/public/") mavenContent { includeGroup("com.gtnewhorizons") - includeGroup("com.gtnewhorizons.retrofuturagradle") - includeGroup("com.gtnewhorizons.gtnhgradle") + includeGroupByRegex("com\\.gtnewhorizons\\..+") } } gradlePluginPortal() @@ -22,5 +20,5 @@ pluginManagement { plugins { // Automatic toolchain provisioning - id("org.gradle.toolchains.foojay-resolver-convention") version "0.4.0" + id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" } diff --git a/src/main/java/com/gtnewhorizons/gtnhgradle/PropertiesConfiguration.java b/src/main/java/com/gtnewhorizons/gtnhgradle/PropertiesConfiguration.java index 0c6ef3da..a52c5737 100644 --- a/src/main/java/com/gtnewhorizons/gtnhgradle/PropertiesConfiguration.java +++ b/src/main/java/com/gtnewhorizons/gtnhgradle/PropertiesConfiguration.java @@ -265,9 +265,9 @@ ExampleMod tag to use as Blowdryer (Spotless, etc.) settings version, leave empt required = false, docComment = """ Whether to use modGroup as the maven publishing group. - Due to a history of using JitPack, the default is com.github.GTNewHorizons for all mods. + When false, com.github.GTNewHorizons is used. """) - public boolean useModGroupForPublishing = false; + public boolean useModGroupForPublishing = true; /** See annotation */ @Prop(name = "autoUpdateBuildScript", isSettings = false, preferPopulated = true, required = false, docComment = """ @@ -468,14 +468,11 @@ Enables using modern Java syntax (up to version 17) via Jabel, while still targe public @NotNull String mixinPlugin = ""; /** See annotation */ - @Prop( - name = "mixinsPackage", - isSettings = false, - preferPopulated = true, - required = false, - docComment = """ - Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail! - """) + @Prop(name = "mixinsPackage", isSettings = false, preferPopulated = true, required = false, docComment = """ + Specify the package that contains all of your Mixins. The package must exist or + the build will fail. If you have a package property defined in your mixins..json, + it must match with this or the build will fail. + """) public @NotNull String mixinsPackage = ""; /** See annotation */ @@ -620,7 +617,7 @@ list of strings, with the acceptable keys being(case does not matter): type can be one of [project, version], and the name is the Modrinth project or version slug/id of the other mod. Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech - Note: GTNH Mixins is automatically set as a required dependency if usesMixins = true + Note: UniMixins is automatically set as a required dependency if usesMixins = true. """) public @NotNull String modrinthRelations = ""; @@ -661,6 +658,28 @@ list of strings, with the acceptable keys being(case does not matter): """) public @NotNull String customArchiveBaseName = ""; + /** See annotation */ + @Prop( + name = "runClientWorkingDirectory", + isSettings = false, + preferPopulated = false, + required = false, + docComment = """ + Optional parameter to customize the default working directory used by the runClient* tasks. Relative to the project directory. + """) + public @NotNull String runClientDirectory = "run/client"; + + /** See annotation */ + @Prop( + name = "runServerWorkingDirectory", + isSettings = false, + preferPopulated = false, + required = false, + docComment = """ + Optional parameter to customize the default working directory used by the runServer* tasks. Relative to the project directory. + """) + public @NotNull String runServerDirectory = "run/server"; + /** See annotation */ @Prop(name = "versionPattern", isSettings = false, preferPopulated = false, required = false, docComment = """ Optional parameter to have the build automatically fail if an illegal version is used. @@ -1009,7 +1028,7 @@ public static void printPropertyDocs(final PrintStream out) { @NotNull String name(); - /** @return Is the property is used globally across many projects from a settings.gradle context? */ + /** @return Is the property used globally across many projects from a settings.gradle context? */ boolean isSettings() default false; /** @return Should the property's value be frozen in the properties file on plugin update? */ diff --git a/src/main/java/com/gtnewhorizons/gtnhgradle/UpdateableConstants.java b/src/main/java/com/gtnewhorizons/gtnhgradle/UpdateableConstants.java index 1ddbc67c..b7e2e1fd 100644 --- a/src/main/java/com/gtnewhorizons/gtnhgradle/UpdateableConstants.java +++ b/src/main/java/com/gtnewhorizons/gtnhgradle/UpdateableConstants.java @@ -10,26 +10,27 @@ public class UpdateableConstants { /** Latest Gradle version to update to. */ // https://github.com/gradle/gradle/releases @SuppressWarnings("unused") // Used via reflection - public static final @NotNull String NEWEST_GRADLE_VERSION = "8.11.1"; + public static final @NotNull String NEWEST_GRADLE_VERSION = "8.14.3"; /** Latest tag of ExampleMod with blowdryer settings */ // https://github.com/GTNewHorizons/ExampleMod1.7.10/releases public static final @NotNull String NEWEST_BLOWDRYER_TAG = "0.2.2"; /** Latest version of UniMixins */ - public static final String NEWEST_UNIMIXINS = "io.github.legacymoddingmc:unimixins:0.1.19"; + // https://github.com/LegacyModdingMC/UniMixins/releases + public static final String NEWEST_UNIMIXINS = "io.github.legacymoddingmc:unimixins:0.1.23"; /** Latest version of Jabel for modern Java support */ public static final @NotNull String NEWEST_JABEL = "com.github.bsideup.jabel:jabel-javac-plugin:1.0.1"; /** Latest version of GTNHLib for modern Java support */ // https://github.com/GTNewHorizons/GTNHLib/releases - public static final @NotNull String NEWEST_GTNH_LIB = "com.github.GTNewHorizons:GTNHLib:0.5.21"; + public static final @NotNull String NEWEST_GTNH_LIB = "com.github.GTNewHorizons:GTNHLib:0.7.3"; /** Latest version of GTNHLib for modern Java support */ // https://github.com/GTNewHorizons/lwjgl3ify/releases - public static final @NotNull String NEWEST_LWJGL3IFY = "com.github.GTNewHorizons:lwjgl3ify:2.1.5"; + public static final @NotNull String NEWEST_LWJGL3IFY = "com.github.GTNewHorizons:lwjgl3ify:2.1.17"; /** Latest version of GTNHLib for modern Java support */ // https://github.com/GTNewHorizons/Hodgepodge/releases - public static final @NotNull String NEWEST_HODGEPODGE = "com.github.GTNewHorizons:Hodgepodge:2.5.82"; + public static final @NotNull String NEWEST_HODGEPODGE = "com.github.GTNewHorizons:Hodgepodge:2.7.9"; /** Latest version of LWJGL3 for modern Java support */ // https://github.com/LWJGL/lwjgl3/releases - but check what latest Minecraft uses too public static final @NotNull String NEWEST_LWJGL3 = "3.3.3"; @@ -38,11 +39,11 @@ public class UpdateableConstants { public static final @NotNull String NEWEST_SPOTLESS_JAVA8 = "6.13.0"; /** Latest version of Spotless compatible with modern Java versions */ // https://github.com/diffplug/spotless/blob/main/plugin-gradle/CHANGES.md - public static final @NotNull String NEWEST_SPOTLESS = "6.25.0"; + public static final @NotNull String NEWEST_SPOTLESS = "8.0.0"; /** Latest HotSwapAgent release jar URL */ // https://github.com/HotswapProjects/HotswapAgent/releases - public static final @NotNull String NEWEST_HOTSWAPAGENT = "https://github.com/HotswapProjects/HotswapAgent/releases/download/1.4.2-SNAPSHOT/hotswap-agent-1.4.2-SNAPSHOT.jar"; + public static final @NotNull String NEWEST_HOTSWAPAGENT = "https://github.com/HotswapProjects/HotswapAgent/releases/download/RELEASE-2.0.1/hotswap-agent-2.0.1.jar"; /** Specifier for the latest GTNHGradle version to update to */ // Only update once a new major change is made, ensure a backport to the previous major's updater is released first. diff --git a/src/main/java/com/gtnewhorizons/gtnhgradle/modules/IdeIntegrationModule.java b/src/main/java/com/gtnewhorizons/gtnhgradle/modules/IdeIntegrationModule.java index f4612bb1..c4f1f3d3 100644 --- a/src/main/java/com/gtnewhorizons/gtnhgradle/modules/IdeIntegrationModule.java +++ b/src/main/java/com/gtnewhorizons/gtnhgradle/modules/IdeIntegrationModule.java @@ -1,5 +1,6 @@ package com.gtnewhorizons.gtnhgradle.modules; +import com.gtnewhorizons.gtnhgradle.modules.ideintegration.IdeaMiscXmlUpdater; import com.gtnewhorizons.retrofuturagradle.shadow.com.google.common.collect.ImmutableList; import com.gtnewhorizons.retrofuturagradle.shadow.com.google.common.collect.ImmutableMap; import com.gtnewhorizons.gtnhgradle.GTNHGradlePlugin; @@ -25,20 +26,8 @@ import org.jetbrains.gradle.ext.IdeaExtPlugin; import org.jetbrains.gradle.ext.ProjectSettings; import org.jetbrains.gradle.ext.RunConfigurationContainer; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; import java.io.File; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.util.Collection; import java.util.Locale; import java.util.Map; @@ -47,16 +36,6 @@ /** Provides better integration for IntelliJ and Eclipse */ public class IdeIntegrationModule implements GTNHModule { - /** The default misc.xml for IntelliJ */ - private final @NotNull String MISC_XML_DEFAULT = """ - - - - - - - """; - @Override public boolean isEnabled(@NotNull PropertiesConfiguration configuration) { return configuration.moduleIdeIntegration; @@ -184,6 +163,7 @@ public void apply(GTNHGradlePlugin.@NotNull GTNHExtension gtnh, @NotNull Project run.setJvmArgs( quotedJoin(runClient.calculateJvmArgs()) + ' ' + quotedPropJoin(runClient.getSystemProperties())); }); + final var ijServerRun = runs.register("Run Server (IJ Native)", Application.class, run -> { run.setMainClass("GradleStartServer"); run.setModuleName(project.getName() + ".ideVirtualMain"); @@ -209,83 +189,9 @@ public void apply(GTNHGradlePlugin.@NotNull GTNHExtension gtnh, @NotNull Project ideaDir = new File(ideaDir, ".idea"); } if (ideaDir.isDirectory()) { - final File miscFile = new File(ideaDir, "misc.xml"); - if (miscFile.isFile()) { - boolean dirty = false; - final DocumentBuilder builder = DocumentBuilderFactory.newInstance() - .newDocumentBuilder(); - final Document doc = builder.parse(miscFile); - Element docRoot = doc.getDocumentElement(); - if (docRoot == null) { - docRoot = doc.createElement("project"); - docRoot.setAttribute("version", "4"); - docRoot.appendChild(docRoot); - } - final NodeList components = docRoot.getElementsByTagName("component"); - Element foundComponent = null; - if (components != null) { - for (int i = 0; i < components.getLength(); i++) { - final Node node = components.item(i); - if (!(node instanceof Element)) { - continue; - } - if (!node.hasAttributes()) { - continue; - } - final Node name = node.getAttributes() - .getNamedItem("name"); - if ("ProjectRootManager".equals(name.getNodeValue())) { - foundComponent = (Element) node; - break; - } - } - } - if (foundComponent == null) { - dirty = true; - Element e = doc.createElement("component"); - e.setAttribute("name", "ProjectRootManager"); - e.setAttribute("version", "2"); - docRoot.appendChild(e); - foundComponent = e; - } - final NodeList outputs = foundComponent.getElementsByTagName("output"); - Element foundOutput = null; - for (int i = 0; i < outputs.getLength(); i++) { - final Node node = outputs.item(i); - if (!(node instanceof Element)) { - continue; - } - if (!node.hasAttributes()) { - continue; - } - foundOutput = (Element) node; - break; - } - if (foundOutput == null) { - dirty = true; - Element e = doc.createElement("output"); - foundComponent.appendChild(e); - foundOutput = e; - } - if (foundOutput.getAttribute("url") - .isEmpty()) { - // Only modify the output url if it doesn't yet have one, or if the existing one is blank - // somehow. - // This is a sensible default for most setups - dirty = true; - foundOutput.setAttribute("url", "file://$PROJECT_DIR$/build/ideaBuild"); - } - - if (dirty) { - final DOMSource domSrc = new DOMSource(doc); - final Transformer xform = TransformerFactory.newInstance() - .newTransformer(); - final StreamResult result = new StreamResult(miscFile); - xform.transform(domSrc, result); - } - } else { - Files.write(miscFile.toPath(), MISC_XML_DEFAULT.getBytes(StandardCharsets.UTF_8)); - } + IdeaMiscXmlUpdater.mergeOrCreate( + ideaDir.toPath() + .resolve("misc.xml")); } } catch (Throwable e) { if (e instanceof RuntimeException ex) { diff --git a/src/main/java/com/gtnewhorizons/gtnhgradle/modules/MixinModule.java b/src/main/java/com/gtnewhorizons/gtnhgradle/modules/MixinModule.java index 91c272a6..e3f81255 100644 --- a/src/main/java/com/gtnewhorizons/gtnhgradle/modules/MixinModule.java +++ b/src/main/java/com/gtnewhorizons/gtnhgradle/modules/MixinModule.java @@ -104,9 +104,9 @@ public void apply(GTNHGradlePlugin.@NotNull GTNHExtension gtnh, @NotNull Project final DependencyHandler deps = project.getDependencies(); if (gtnh.configuration.usesMixins) { final String apConfiguration = mixinSourceSet.getAnnotationProcessorConfigurationName(); - deps.add(apConfiguration, "org.ow2.asm:asm-debug-all:5.0.3"); + deps.add(apConfiguration, "org.ow2.asm:asm-debug-all:5.0.4"); deps.add(apConfiguration, "com.google.guava:guava:24.1.1-jre"); - deps.add(apConfiguration, "com.google.code.gson:gson:2.8.6"); + deps.add(apConfiguration, "com.google.code.gson:gson:2.13.2"); deps.add(apConfiguration, mixinProviderSpec); if (gtnh.configuration.usesMixinDebug) { deps.add("runtimeOnlyNonPublishable", "org.jetbrains:intellij-fernflower:1.2.1.16"); diff --git a/src/main/java/com/gtnewhorizons/gtnhgradle/modules/ModernJavaModule.java b/src/main/java/com/gtnewhorizons/gtnhgradle/modules/ModernJavaModule.java index 5dcc63ac..e25650c7 100644 --- a/src/main/java/com/gtnewhorizons/gtnhgradle/modules/ModernJavaModule.java +++ b/src/main/java/com/gtnewhorizons/gtnhgradle/modules/ModernJavaModule.java @@ -140,6 +140,7 @@ public void apply(GTNHGradlePlugin.@NotNull GTNHExtension gtnh, @NotNull Project t.setup(project, gtnh); t.getJavaLauncher() .set(getToolchainService().launcherFor(java17Toolchain)); + t.setWorkingDir(gtnh.configuration.runClientDirectory); }); final TaskProvider runServer17Task = tasks .register("runServer17", RunHotswappableMinecraftTask.class, Distribution.DEDICATED_SERVER, "runServer"); @@ -148,6 +149,7 @@ public void apply(GTNHGradlePlugin.@NotNull GTNHExtension gtnh, @NotNull Project t.setup(project, gtnh); t.getJavaLauncher() .set(getToolchainService().launcherFor(java17Toolchain)); + t.setWorkingDir(gtnh.configuration.runServerDirectory); }); final TaskProvider runClient21Task = tasks @@ -157,6 +159,7 @@ public void apply(GTNHGradlePlugin.@NotNull GTNHExtension gtnh, @NotNull Project t.setup(project, gtnh); t.getJavaLauncher() .set(getToolchainService().launcherFor(java21Toolchain)); + t.setWorkingDir(gtnh.configuration.runClientDirectory); }); final TaskProvider runServer21Task = tasks .register("runServer21", RunHotswappableMinecraftTask.class, Distribution.DEDICATED_SERVER, "runServer"); @@ -165,6 +168,7 @@ public void apply(GTNHGradlePlugin.@NotNull GTNHExtension gtnh, @NotNull Project t.setup(project, gtnh); t.getJavaLauncher() .set(getToolchainService().launcherFor(java21Toolchain)); + t.setWorkingDir(gtnh.configuration.runServerDirectory); }); } } diff --git a/src/main/java/com/gtnewhorizons/gtnhgradle/modules/PublishingModule.java b/src/main/java/com/gtnewhorizons/gtnhgradle/modules/PublishingModule.java index a899f664..951c8992 100644 --- a/src/main/java/com/gtnewhorizons/gtnhgradle/modules/PublishingModule.java +++ b/src/main/java/com/gtnewhorizons/gtnhgradle/modules/PublishingModule.java @@ -1,5 +1,6 @@ package com.gtnewhorizons.gtnhgradle.modules; +import com.gtnewhorizons.gtnhgradle.BuildConfig; import com.gtnewhorizons.gtnhgradle.GTNHConstants; import com.gtnewhorizons.gtnhgradle.GTNHGradlePlugin; import com.gtnewhorizons.gtnhgradle.GTNHModule; @@ -8,9 +9,13 @@ import com.gtnewhorizons.retrofuturagradle.shadow.org.apache.commons.lang3.ObjectUtils; import com.modrinth.minotaur.Minotaur; import com.modrinth.minotaur.ModrinthExtension; +import com.modrinth.minotaur.TaskModrinthUpload; import com.modrinth.minotaur.dependencies.Dependency; import com.modrinth.minotaur.dependencies.ModDependency; import com.modrinth.minotaur.dependencies.VersionDependency; +import masecla.modrinth4j.client.agent.UserAgent; +import masecla.modrinth4j.main.ModrinthAPI; +import masecla.modrinth4j.model.version.ProjectVersion; import net.darkhax.curseforgegradle.CurseForgeGradlePlugin; import net.darkhax.curseforgegradle.TaskPublishCurseForge; import org.gradle.api.Project; @@ -51,38 +56,41 @@ public void apply(GTNHGradlePlugin.@NotNull GTNHExtension gtnh, @NotNull Project .toString()); // Maven - publishing.getPublications() - .register("maven", MavenPublication.class, mvn -> { - mvn.from( - project.getComponents() - .findByName("java")); - if (!gtnh.configuration.apiPackage.isEmpty()) { - mvn.artifact( - project.getTasks() - .findByName("apiJar")); - } - mvn.setGroupId( - ObjectUtils.firstNonNull( - System.getenv("ARTIFACT_GROUP_ID"), - project.getGroup() - .toString())); - mvn.setArtifactId(ObjectUtils.firstNonNull(System.getenv("ARTIFACT_ID"), project.getName())); - project.afterEvaluate( - _p -> mvn.setVersion(ObjectUtils.firstNonNull(System.getenv("RELEASE_VERSION"), modVersion.get()))); - }); - final String mavenUser = System.getenv("MAVEN_USER"); - final String mavenPass = System.getenv("MAVEN_PASSWORD"); - if (gtnh.configuration.usesMavenPublishing && mavenUser != null) { - publishing.getRepositories() - .maven(mvn -> { - mvn.setName("main"); - mvn.setUrl(gtnh.configuration.mavenPublishUrl); - mvn.setAllowInsecureProtocol(gtnh.configuration.mavenPublishUrl.startsWith("http://")); - mvn.getCredentials() - .setUsername(ObjectUtils.firstNonNull(mavenUser, "NONE")); - mvn.getCredentials() - .setPassword(ObjectUtils.firstNonNull(mavenPass, "NONE")); + if (gtnh.configuration.usesMavenPublishing) { + final String mavenUser = System.getenv("MAVEN_USER"); + final String mavenPass = System.getenv("MAVEN_PASSWORD"); + publishing.getPublications() + .register("maven", MavenPublication.class, mvn -> { + mvn.from( + project.getComponents() + .findByName("java")); + if (!gtnh.configuration.apiPackage.isEmpty()) { + mvn.artifact( + project.getTasks() + .findByName("apiJar")); + } + mvn.setGroupId( + ObjectUtils.firstNonNull( + System.getenv("ARTIFACT_GROUP_ID"), + project.getGroup() + .toString())); + mvn.setArtifactId(ObjectUtils.firstNonNull(System.getenv("ARTIFACT_ID"), project.getName())); + project.afterEvaluate( + _p -> mvn + .setVersion(ObjectUtils.firstNonNull(System.getenv("RELEASE_VERSION"), modVersion.get()))); }); + if (mavenUser != null) { + publishing.getRepositories() + .maven(mvn -> { + mvn.setName("main"); + mvn.setUrl(gtnh.configuration.mavenPublishUrl); + mvn.setAllowInsecureProtocol(gtnh.configuration.mavenPublishUrl.startsWith("http://")); + mvn.getCredentials() + .setUsername(ObjectUtils.firstNonNull(mavenUser, "NONE")); + mvn.getCredentials() + .setPassword(ObjectUtils.firstNonNull(mavenPass, "NONE")); + }); + } } final File changelogFile = new File(ObjectUtils.firstNonNull(System.getenv("CHANGELOG_FILE"), "CHANGELOG.md")); @@ -135,9 +143,27 @@ public void apply(GTNHGradlePlugin.@NotNull GTNHExtension gtnh, @NotNull Project if (gtnh.configuration.usesMixins) { addModrinthDep(project, "required", "project", "unimixins"); } + final PropertiesConfiguration props = gtnh.configuration; project.getTasks() - .named("modrinth") - .configure(t -> t.dependsOn("build")); + .named("modrinth", TaskModrinthUpload.class) + .configure(t -> { + t.dependsOn("build"); + t.onlyIf("Version was already published", _t -> { + final String actualVersion = modVersion.get(); + final ModrinthAPI api = ModrinthAPI.rateLimited( + UserAgent.builder() + .authorUsername("eigenraven") + .projectName("GTNHGradle") + .projectVersion(BuildConfig.VERSION) + .contact("GTNewHorizons/GTNHGradle") + .build(), + mrToken); + final ProjectVersion existingVersion = api.versions() + .getVersionByNumber(props.modrinthProjectId, actualVersion) + .join(); + return existingVersion == null; + }); + }); project.getTasks() .named("publish") .configure(t -> t.dependsOn("modrinth")); diff --git a/src/main/java/com/gtnewhorizons/gtnhgradle/modules/ToolchainModule.java b/src/main/java/com/gtnewhorizons/gtnhgradle/modules/ToolchainModule.java index 0eb52a53..8783a892 100644 --- a/src/main/java/com/gtnewhorizons/gtnhgradle/modules/ToolchainModule.java +++ b/src/main/java/com/gtnewhorizons/gtnhgradle/modules/ToolchainModule.java @@ -1,5 +1,6 @@ package com.gtnewhorizons.gtnhgradle.modules; +import com.gtnewhorizons.retrofuturagradle.minecraft.RunMinecraftTask; import com.gtnewhorizons.retrofuturagradle.modutils.ModUtils; import com.gtnewhorizons.retrofuturagradle.shadow.com.google.common.collect.ImmutableMap; import com.gtnewhorizons.retrofuturagradle.shadow.com.google.common.collect.ImmutableSet; @@ -13,6 +14,7 @@ import com.gtnewhorizons.retrofuturagradle.mcp.InjectTagsTask; import com.gtnewhorizons.retrofuturagradle.mcp.MCPTasks; import com.gtnewhorizons.retrofuturagradle.mcp.ReobfuscatedJar; +import com.gtnewhorizons.retrofuturagradle.util.ProviderToStringWrapper; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; @@ -41,7 +43,7 @@ import org.gradle.jvm.toolchain.JvmVendorSpec; import org.gradle.language.jvm.tasks.ProcessResources; import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension; +import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension; import javax.inject.Inject; import java.nio.charset.StandardCharsets; @@ -148,7 +150,7 @@ public void apply(GTNHGradlePlugin.@NotNull GTNHExtension gtnh, @NotNull Project ((ModuleDependency) deps.add(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME, UpdateableConstants.NEWEST_JABEL)) .setTransitive(false); // Workaround for https://github.com/bsideup/jabel/issues/174 - deps.add(JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME, "net.java.dev.jna:jna-platform:5.13.0"); + deps.add(JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME, "net.java.dev.jna:jna-platform:5.18.1"); // Allow using jdk.unsupported classes like sun.misc.Unsafe in the compiled code, working around // JDK-8206937. deps.add( @@ -181,7 +183,7 @@ public void apply(GTNHGradlePlugin.@NotNull GTNHExtension gtnh, @NotNull Project // Set up Kotlin if enabled project.getPlugins() .withId("org.jetbrains.kotlin.jvm", plugin -> { - final KotlinTopLevelExtension kotlin = (KotlinTopLevelExtension) project.getExtensions() + final KotlinBaseExtension kotlin = (KotlinBaseExtension) project.getExtensions() .getByName("kotlin"); kotlin.jvmToolchain(8); final Set disabledKotlinTasks = ImmutableSet.of( @@ -207,6 +209,19 @@ public void apply(GTNHGradlePlugin.@NotNull GTNHExtension gtnh, @NotNull Project // Set up basic project settings project.setGroup( gtnh.configuration.useModGroupForPublishing ? gtnh.configuration.modGroup : "com.github.GTNewHorizons"); + // Default project.version to modVersion if no version has been set manually + if (project.getVersion() == Project.DEFAULT_VERSION) { + var ext = project.getExtensions() + .getExtraProperties(); + project + .setVersion( + new ProviderToStringWrapper( + project.provider( + () -> ext.has(GTNHConstants.MOD_VERSION_PROPERTY) + ? ext.get(GTNHConstants.MOD_VERSION_PROPERTY) + .toString() + : Project.DEFAULT_VERSION))); + } final BasePluginExtension base = project.getExtensions() .getByType(BasePluginExtension.class); if (!gtnh.configuration.customArchiveBaseName.isEmpty()) { @@ -429,5 +444,15 @@ public void apply(GTNHGradlePlugin.@NotNull GTNHExtension gtnh, @NotNull Project project.getArtifacts() .add("archives", tasks.named("apiJar")); } + + // run dir config for RFG run tasks + for (String clientTaskName : ImmutableSet.of("runClient", "runVanillaClient", "runObfClient")) { + tasks.named(clientTaskName, RunMinecraftTask.class) + .configure(t -> t.setWorkingDir(gtnh.configuration.runClientDirectory)); + } + for (String serverTaskName : ImmutableSet.of("runServer", "runVanillaServer", "runObfServer")) { + tasks.named(serverTaskName, RunMinecraftTask.class) + .configure(t -> t.setWorkingDir(gtnh.configuration.runServerDirectory)); + } } } diff --git a/src/main/java/com/gtnewhorizons/gtnhgradle/modules/UpdaterModule.java b/src/main/java/com/gtnewhorizons/gtnhgradle/modules/UpdaterModule.java index 929d6d21..1df86613 100644 --- a/src/main/java/com/gtnewhorizons/gtnhgradle/modules/UpdaterModule.java +++ b/src/main/java/com/gtnewhorizons/gtnhgradle/modules/UpdaterModule.java @@ -8,23 +8,45 @@ import com.gtnewhorizons.gtnhgradle.UpdateableConstants; import com.gtnewhorizons.gtnhgradle.tasks.UpdateBuildscriptTask; import com.gtnewhorizons.gtnhgradle.tasks.UpdateDependenciesTask; +import com.gtnewhorizons.gtnhgradle.tasks.V2UpgradeTask; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ResolvedArtifact; import org.gradle.api.artifacts.ResolvedConfiguration; +import org.gradle.api.internal.project.ProjectInternal; +import org.gradle.api.problems.Problems; import org.gradle.api.provider.Provider; import org.gradle.api.tasks.TaskContainer; +import org.gradle.api.tasks.TaskProvider; import org.gradle.api.tasks.wrapper.Wrapper; +import org.gradle.buildconfiguration.tasks.UpdateDaemonJvm; +import org.gradle.internal.Pair; +import org.gradle.internal.buildconfiguration.resolvers.UnconfiguredToolchainRepositoriesResolver; +import org.gradle.internal.deprecation.Documentation; +import org.gradle.jvm.toolchain.JavaLanguageVersion; +import org.gradle.jvm.toolchain.JavaToolchainDownload; +import org.gradle.jvm.toolchain.JavaToolchainSpec; +import org.gradle.jvm.toolchain.JvmVendorSpec; +import org.gradle.jvm.toolchain.internal.DefaultJavaToolchainRequest; +import org.gradle.jvm.toolchain.internal.DefaultJvmVendorSpec; +import org.gradle.jvm.toolchain.internal.DefaultToolchainSpec; +import org.gradle.jvm.toolchain.internal.JavaToolchainResolverService; +import org.gradle.platform.BuildPlatform; import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.net.MalformedURLException; +import java.net.URI; import java.net.URL; import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; /** Allows automatic buildscript updates */ public class UpdaterModule implements GTNHModule { @@ -86,6 +108,12 @@ public void apply(GTNHGradlePlugin.@NotNull GTNHExtension gtnh, @NotNull Project } }); + if (!isOffline) { + gtnh.logger.warn( + "Build script major version upgrade from {} to 2.x available! Run ./gradlew upgradeBuildScriptMajor", + BuildConfig.VERSION); + } + final TaskContainer tasks = project.getTasks(); tasks.named("wrapper", Wrapper.class) .configure(t -> { @@ -138,6 +166,139 @@ public void apply(GTNHGradlePlugin.@NotNull GTNHExtension gtnh, @NotNull Project .set( project.getLayout() .file(project.provider(() -> dependenciesGradle)))); + + final TaskProvider v2WrapperTask = tasks.register("buildScriptV2Wrapper", Wrapper.class, t -> { + t.setGroup("GTNH Buildscript Internal"); + t.setGradleVersion("9.4.0"); + t.getValidateDistributionUrl() + .set(true); + t.getNetworkTimeout() + .set(30_000); + }); + + @SuppressWarnings("UnstableApiUsage") + final TaskProvider v2DaemonJvmTask = tasks + .register("buildScriptV2DaemonJvm", UpdateDaemonJvm.class, t -> { + @SuppressWarnings("UnstableApiUsage") + final UpdateDaemonJvm mainDaemonJvmTask = tasks.named("updateDaemonJvm", UpdateDaemonJvm.class) + .get(); + t.setGroup("GTNH Buildscript Internal"); + t.getPropertiesFile() + .set( + project.getLayout() + .getProjectDirectory() + .file("gradle/gradle-daemon-jvm.properties")); + t.getLanguageVersion() + .set(JavaLanguageVersion.of(25)); + t.getNativeImageCapable() + .set(false); + t.getToolchainPlatforms() + .set(mainDaemonJvmTask.getToolchainPlatforms()); + t.getToolchainDownloadUrls() + .set(mainDaemonJvmTask.getToolchainDownloadUrls()); + t.getToolchainDownloadUrls() + .convention( + t.getToolchainPlatforms() + .zip( + t.getLanguageVersion() + .zip( + t.getVendor() + .orElse(DefaultJvmVendorSpec.any()), + Pair::of) + .zip(t.getNativeImageCapable(), Pair::of), + (platforms, versionVendorNative) -> { + JvmVendorSpec vendor = versionVendorNative.getLeft() + .getRight(); + JavaToolchainSpec toolchainSpec = project.getObjects() + .newInstance(DefaultToolchainSpec.class); + toolchainSpec.getLanguageVersion() + .set( + versionVendorNative.getLeft() + .getLeft()); + if (!vendor.equals(DefaultJvmVendorSpec.any())) { + toolchainSpec.getVendor() + .set(vendor); + } + if (versionVendorNative.getRight()) { + toolchainSpec.getNativeImageCapable() + .set(true); + } + if (platforms.isEmpty()) { + return new HashMap<>(0); + } + + var reporter = ((ProjectInternal) project).getServices() + .get(Problems.class) + .getReporter(); + JavaToolchainResolverService resolverService = ((ProjectInternal) project) + .getServices() + .get(JavaToolchainResolverService.class); + if (!resolverService.hasConfiguredToolchainRepositories()) { + UnconfiguredToolchainRepositoriesResolver exception = new UnconfiguredToolchainRepositoriesResolver(); + throw reporter.throwing( + exception, + UpdateDaemonJvm.TASK_CONFIGURATION_PROBLEM_ID, + problemSpec -> { + problemSpec.solution( + "Learn more about toolchain repositories at " + + Documentation + .userManual("toolchains", "sub:download_repositories") + .getUrl() + + "."); + }); + } + Map> buildPlatformOptionalUriMap = platforms.stream() + .collect( + Collectors.toMap( + platform -> platform, + platform -> resolverService + .tryResolve( + new DefaultJavaToolchainRequest(toolchainSpec, platform)) + .map(JavaToolchainDownload::getUri))); + Map platformToDownloadUri = buildPlatformOptionalUriMap + .entrySet() + .stream() + .filter( + e -> e.getValue() + .isPresent()) + .collect( + Collectors.toMap( + Map.Entry::getKey, + e -> e.getValue() + .get())); + if (platformToDownloadUri.isEmpty()) { + throw reporter.throwing( + new IllegalStateException( + "Toolchain resolvers did not return download URLs providing a JDK matching " + + toolchainSpec + + " for any of the requested platforms " + + platforms), + UpdateDaemonJvm.TASK_CONFIGURATION_PROBLEM_ID, + problemSpec -> { + problemSpec.solution( + "Use a toolchain download repository capable of resolving the toolchain spec for the given platforms"); + problemSpec.documentedAt( + Documentation + .userManual("gradle_daemon", "sec:daemon_jvm_provisioning") + .getUrl()); + }); + } + return platformToDownloadUri; + })); + }); + + tasks.register("upgradeBuildScriptMajor", V2UpgradeTask.class, t -> { + t.setGroup("GTNH Buildscript"); + t.finalizedBy(v2WrapperTask, v2DaemonJvmTask); + t.getSettingsGradle() + .set( + project.getLayout() + .file(project.provider(() -> settingsGradle))); + t.getPropertiesGradle() + .set( + project.getLayout() + .file(project.provider(() -> propertiesGradle))); + }); } private static String getGradleVersionFromPlugin(final ResolvedArtifact artifact) { diff --git a/src/main/java/com/gtnewhorizons/gtnhgradle/modules/ideintegration/IdeaMiscXmlUpdater.java b/src/main/java/com/gtnewhorizons/gtnhgradle/modules/ideintegration/IdeaMiscXmlUpdater.java new file mode 100644 index 00000000..ab28fd49 --- /dev/null +++ b/src/main/java/com/gtnewhorizons/gtnhgradle/modules/ideintegration/IdeaMiscXmlUpdater.java @@ -0,0 +1,158 @@ +package com.gtnewhorizons.gtnhgradle.modules.ideintegration; + +import org.jdom2.Attribute; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.input.SAXBuilder; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; +import org.jetbrains.annotations.NotNull; + +import java.io.BufferedWriter; +import java.io.StringReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +public final class IdeaMiscXmlUpdater { + + /** The default {@code .idea/misc.xml} for IntelliJ */ + private static final @NotNull String MISC_XML_DEFAULT = """ + + + + + + + + + + + + + + + + """; + + private static final String TAG_COMPONENT = "component"; + private static final String ATT_NAME = "name"; + + public static void mergeOrCreate(Path target) throws Exception { + final SAXBuilder builder = new SAXBuilder(); + final Document refDoc = builder.build(new StringReader(MISC_XML_DEFAULT)); + final Document targetDoc; + + if (Files.exists(target)) { + targetDoc = builder.build(Files.newInputStream(target)); + } else { + targetDoc = refDoc.clone(); + writeDocument(targetDoc, target); + return; + } + + Element refRoot = refDoc.getRootElement(); + Element tgtRoot = targetDoc.getRootElement(); + + for (Element refComp : refRoot.getChildren(TAG_COMPONENT)) { + String refCompName = refComp.getAttributeValue(ATT_NAME); + Element tgtComp = getTargetComponent(tgtRoot, refCompName); + + if (tgtComp == null) { + tgtRoot.addContent(refComp.clone()); + } else { + switch (refCompName) { + case "EntryPointsManager" -> updateEntryPointsManager(refComp, tgtComp); + case "ProjectRootManager" -> updateProjectRootManager(refComp, tgtComp); + } + } + } + + writeDocument(targetDoc, target); + } + + private static Element getTargetComponent(Element tgtRoot, String refCompName) { + for (Element c : tgtRoot.getChildren(TAG_COMPONENT)) { + if (c.getAttributeValue(ATT_NAME) + .equals(refCompName)) { + return c; + } + } + return null; + } + + private static final String TAG_OUTPUT = "output"; + private static final String ATT_URL = "url"; + + private static void updateProjectRootManager(Element refComp, Element tgtComp) { + final Element refOutput = refComp.getChild(TAG_OUTPUT); + if (refOutput == null) return; + final Attribute refUrl = refOutput.getAttribute(ATT_URL); + if (refUrl == null) return; + + final Element tgtOutput = tgtComp.getChild(TAG_OUTPUT); + if (tgtOutput == null) { + tgtComp.addContent(refOutput.clone()); + return; + } + + // Only modify the output url if it doesn't yet have one, + // or if the existing one is blank somehow. + // This is a sensible default for most setups + final Attribute tgtUrl = tgtOutput.getAttribute(ATT_URL); + if (tgtUrl == null || tgtUrl.getValue() + .isEmpty()) { + tgtOutput.setAttribute(refUrl); + } + } + + private static final String TAG_LIST = "list"; + private static final String TAG_ITEM = "item"; + private static final String ATT_ITEMVALUE = "itemvalue"; + + private static void updateEntryPointsManager(Element refComp, Element tgeComp) { + + final Element modelList = refComp.getChild(TAG_LIST); + if (modelList == null) return; + + final Element targetList = tgeComp.getChild(TAG_LIST); + if (targetList == null) { + tgeComp.addContent(modelList.clone()); + return; + } + + for (Element modelItem : modelList.getChildren(TAG_ITEM)) { + addItemIfAbsent(targetList, modelItem); + } + + final List targetItems = targetList.getChildren(TAG_ITEM); + final int targetItemsSize = targetItems.size(); + for (int i = 0; i < targetItemsSize; i++) { + targetItems.get(i) + .setAttribute("index", java.lang.String.valueOf(i)); + } + targetList.setAttribute("size", java.lang.String.valueOf(targetItemsSize)); + } + + private static void addItemIfAbsent(Element targetList, Element modelItem) { + + for (Element item : targetList.getChildren(TAG_ITEM)) { + if (modelItem.getAttributeValue(ATT_ITEMVALUE) + .equals(item.getAttributeValue(ATT_ITEMVALUE))) { + return; + } + } + targetList.addContent(modelItem.clone()); + } + + private static final XMLOutputter PRETTY_XML_OUTPUTTER = new XMLOutputter( + Format.getPrettyFormat() + .setIndent(" ") + .setLineSeparator("\n")); + + private static void writeDocument(Document doc, Path target) throws Exception { + try (BufferedWriter writer = Files.newBufferedWriter(target)) { + PRETTY_XML_OUTPUTTER.output(doc, writer); + } + } +} diff --git a/src/main/java/com/gtnewhorizons/gtnhgradle/tasks/SettingsUpdater.java b/src/main/java/com/gtnewhorizons/gtnhgradle/tasks/SettingsUpdater.java index 4ecff7ab..ccb248a2 100644 --- a/src/main/java/com/gtnewhorizons/gtnhgradle/tasks/SettingsUpdater.java +++ b/src/main/java/com/gtnewhorizons/gtnhgradle/tasks/SettingsUpdater.java @@ -23,6 +23,10 @@ public class SettingsUpdater { */ @SuppressWarnings("unused") // used by reflection public void update(Path settingsPath) throws Throwable { + update(settingsPath, BuildConfig.VERSION); + } + + public void update(Path settingsPath, String newVersion) throws Throwable { final String oldSettings = new String(Files.readAllBytes(settingsPath), StandardCharsets.UTF_8); String newSettings = oldSettings; @@ -37,7 +41,6 @@ public void update(Path settingsPath) throws Throwable { final String preVersion = versionMatcher.group(1); final String oldVersion = versionMatcher.group(2); final String postVersion = versionMatcher.group(3); - final String newVersion = BuildConfig.VERSION; if (oldVersion.equals(newVersion)) { System.out.println("Settings.gradle plugin already at the newest version of " + newVersion); } else { diff --git a/src/main/java/com/gtnewhorizons/gtnhgradle/tasks/V2UpgradeTask.java b/src/main/java/com/gtnewhorizons/gtnhgradle/tasks/V2UpgradeTask.java new file mode 100644 index 00000000..83c237dc --- /dev/null +++ b/src/main/java/com/gtnewhorizons/gtnhgradle/tasks/V2UpgradeTask.java @@ -0,0 +1,91 @@ +package com.gtnewhorizons.gtnhgradle.tasks; + +import com.gtnewhorizons.gtnhgradle.PropertiesConfiguration; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.specs.Specs; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; + +import javax.inject.Inject; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.Properties; +import java.util.TreeMap; + +public abstract class V2UpgradeTask extends DefaultTask { + + /** + * @return The settings.gradle[.kts] file to update + */ + @InputFile + @PathSensitive(PathSensitivity.NAME_ONLY) + public abstract RegularFileProperty getSettingsGradle(); + + /** + * @return The properties.gradle[.kts] file to update + */ + @InputFile + @PathSensitive(PathSensitivity.NAME_ONLY) + public abstract RegularFileProperty getPropertiesGradle(); + + @Inject + public V2UpgradeTask() { + this.setGroup("GTNH Buildscript"); + this.setDescription("Upgrades the build script to the 2.x major version"); + // Ensure the task always runs + this.getOutputs() + .upToDateWhen(Specs.satisfyNone()); + } + + @TaskAction + public void performUpgrade() throws Throwable { + updateProperties(); + updateSettings(); + getLogger().warn( + "Make sure to run ./gradlew updateBuildScript after this command finishes to finish the v2 upgrade process."); + } + + private void updateProperties() throws Throwable { + final Path settingsPath = getSettingsGradle().getAsFile() + .get() + .toPath(); + PropertiesConfiguration propsObject = new PropertiesConfiguration(); + + final Properties p = new Properties(); + final Path propertiesPath = getPropertiesGradle().getAsFile() + .get() + .toPath(); + try (final Reader rdr = Files.newBufferedReader(propertiesPath, StandardCharsets.UTF_8)) { + p.load(rdr); + } + final Map originalProps = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + for (Map.Entry entry : p.entrySet()) { + originalProps.put( + entry.getKey() + .toString(), + entry.getValue() + .toString()); + } + + // Enable parallel builds and configuration cache + originalProps.put("org.gradle.configuration-cache", "true"); + originalProps.put("org.gradle.parallel", "true"); + + final String newProps = propsObject.generateUpdatedProperties(settingsPath, originalProps); + Files.write(propertiesPath, newProps.getBytes(StandardCharsets.UTF_8)); + } + + private void updateSettings() throws Throwable { + final Path settingsPath = getSettingsGradle().getAsFile() + .get() + .toPath(); + SettingsUpdater updater = new SettingsUpdater(); + updater.update(settingsPath, "2.0.22"); + } +}