diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml index eb1ab81..0361334 100644 --- a/.github/workflows/compatibility.yml +++ b/.github/workflows/compatibility.yml @@ -14,8 +14,18 @@ jobs: strategy: fail-fast: false matrix: - java: ['11', '14', '15', '24'] - name: Java ${{ matrix.java }} / Gradle 8.14.6 + include: + - java: '11' + gradle: '8.14.6' + - java: '14' + gradle: '8.14.6' + - java: '15' + gradle: '8.14.6' + - java: '24' + gradle: '9.8.1' + - java: '27' + gradle: '9.8.1' + name: Java ${{ matrix.java }} / Gradle ${{ matrix.gradle }} steps: - uses: actions/checkout@v7 with: @@ -25,5 +35,28 @@ jobs: distribution: zulu java-version: ${{ matrix.java }} - uses: gradle/actions/setup-gradle@v6 + - run: python3 tests/wrapper_compatibility.py ${{ matrix.java }} ${{ matrix.gradle }} - run: ./gradlew build compileSamplesKotlin dokka --console=plain - run: ./template/gradlew -p template --include-build "$PWD" tasks --all --console=plain + + windows-wrapper: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + include: + - java: '11' + gradle: '8.14.6' + - java: '14' + gradle: '8.14.6' + - java: '27' + gradle: '9.8.1' + name: Windows launcher / Java ${{ matrix.java }} + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-java@v6 + with: + distribution: zulu + java-version: ${{ matrix.java }} + - uses: gradle/actions/setup-gradle@v6 + - run: python tests/wrapper_compatibility.py ${{ matrix.java }} ${{ matrix.gradle }} diff --git a/README.md b/README.md index 92d1e81..fb7b391 100644 --- a/README.md +++ b/README.md @@ -217,7 +217,7 @@ ftppass=password To use SubKt you will need: -* A [JDK](https://www.oracle.com/java/technologies/downloads/), Java 11–24 (tested on Java 11, 14, 15, and 24). The included wrapper downloads Gradle 8.14.6 automatically. +* A [JDK](https://www.oracle.com/java/technologies/downloads/), Java 11–27 (tested on Java 11, 14, 15, 24, and 27). The included `gradlew` selects and downloads a compatible Gradle version automatically. * [mkvmerge](https://mkvtoolnix.download/downloads.html) Ensure that both `java` and `mkvmerge` are available on the PATH. @@ -235,9 +235,22 @@ Future runs will be much quicker. ## Working alongside older projects -The updated SubKt source and template use Gradle 8.14.6 on Java 11–24. Collaborators can run the same project's `gradlew` with different supported Java installations, without editing the wrapper configuration or installing another JDK. The wrapper downloads Gradle and verifies its SHA-256 checksum. +Collaborators using Java 11–16 and Java 27 can use the same updated project checkout and run the same commands (`./gradlew` on macOS/Linux, `gradlew` on Windows). The launcher checks the Java selected by `JAVA_HOME`, or `PATH` when `JAVA_HOME` is unset, and chooses: -Keep older projects on their existing SubKt and wrapper versions. For an updated project, copy both launchers and the `gradle/wrapper` directory from the template and select the updated SubKt source. Until this upgrade is released, use `--include-build` as described below; the template's default `subktVersion=0.1.19` still selects the old release. Gradle 8.14.6 cannot run on Java 25 or newer. +| Installed Java | Gradle downloaded and run | +| --- | --- | +| 11–16 | 8.14.6 | +| 17–27 | 9.8.1 | + +No additional JDK, manual Gradle installation, or per-user project edits are needed. Selection does not modify either wrapper configuration. Each Gradle distribution is cached after its first download, with its SHA-256 checksum verified on installation. + +You can keep Java 11, 14, or 15 as your default and leave existing projects on their existing SubKt and Gradle versions. The new launchers only affect projects that contain them; they do not change your default Java or upgrade older projects. + +When updating a shared project, the maintainer must update its SubKt version and copy **both launchers and the entire `gradle/wrapper` directory** from the template. This includes `gradle-wrapper-legacy.jar` and `gradle-wrapper-legacy.properties`, alongside the regular wrapper files. Both JARs are identical, unmodified Gradle wrapper binaries; the different filenames let Gradle load the appropriate properties file without rewriting it. Commit all six files so collaborators get the same automatic selection. While these changes are unreleased, use `--include-build` as described below; the template's default `subktVersion=0.1.19` still selects the old release. + +The consuming project's launcher selects Gradle for SubKt too, including when SubKt is built from Git or through `--include-build`; SubKt's own launcher is only used when you invoke it directly. Build scripts shared by both groups must use APIs available in Gradle 8.14 as well as Gradle 9.8. + +IDE integrations that bypass `gradlew` do not perform this selection. Configure those locally with Gradle 8.14.6 on Java 11–16, or Gradle 9.8.1 on Java 17–27; alternatively, run the project's launcher in a terminal. Avoid putting a Java home override in the shared `~/.gradle/gradle.properties`, since that affects other projects. ## Building and using a local checkout @@ -248,7 +261,7 @@ git submodule update --init --recursive ./gradlew build compileSamplesKotlin ``` -Build plugins come from the Gradle Plugin Portal and dependencies from Maven Central; JCenter is no longer used. Java and Kotlin compile to Java 11 bytecode without downloading a separate JDK. Kotlin dependencies match the host Gradle's embedded Kotlin runtime, and Kotlin language/API version 2.0 keeps the plugin readable by Gradle 8.14's Kotlin DSL compiler. +The launcher automatically selects Gradle for your installed JDK. Build plugins come from the Gradle Plugin Portal and dependencies from Maven Central; JCenter is no longer used. Java and Kotlin compile to Java 11 bytecode without downloading a separate JDK. Kotlin dependencies match the host Gradle's embedded Kotlin runtime, and Kotlin language/API version 2.0 keeps the plugin readable by Gradle 8.14's Kotlin DSL compiler. To use this checkout from the included template: @@ -258,19 +271,28 @@ cd template ./gradlew --include-build .. mux.03 ``` -For a template copied elsewhere, pass `--include-build /absolute/path/to/SubKt`. Gradle substitutes the local checkout for the `myaa:subkt` dependency, including uncommitted changes. Keep `subktRepo` and `subktVersion` configured: without `--include-build`, they select the Git repository and version as usual. +For a template copied elsewhere, pass `--include-build /absolute/path/to/SubKt`. Gradle substitutes the local checkout for the `myaa:subkt` dependency, including uncommitted changes. Keep `subktRepo` and `subktVersion` configured: without `--include-build`, they select the Git repository and version as usual. The old release selected by the template does not contain this upgrade. Run `./gradlew dokka` to generate Markdown API documentation in `build/dokka/gfm`. The checked-in documentation is not overwritten. Projects using FTPS with `sslSessionReuse` enabled also need the two `--add-opens` JVM arguments in the template's `gradle.properties`. They allow the existing session-reuse implementation to access the JDK's SSL session cache on Java 17+. Merge these arguments into any existing `org.gradle.jvmargs` setting. -The compatibility workflow builds the plugin and samples, runs the functional tests, generates documentation, and loads the template on Java 11, 14, 15, and 24. Select a supported JDK with `JAVA_HOME`, then run: +The compatibility workflow exercises the launchers, builds the plugin and samples, runs the functional tests, generates documentation, and loads the template on Java 11, 14, 15, 24, and 27. It also checks the Windows launcher on Java 11, 14, and 27. To reproduce a build lane locally, select its JDK with `JAVA_HOME`, then run the same commands: ```sh ./gradlew build compileSamplesKotlin dokka ./template/gradlew -p template --include-build "$PWD" tasks --all ``` +The launcher regression checks need Python 3, which is only a test dependency: + +```sh +# With Java 11 selected (use 14/8.14.6, 15/8.14.6, 24/9.8.1, or 27/9.8.1 for the other lanes): +python3 tests/wrapper_compatibility.py 11 8.14.6 +``` + +When regenerating the wrapper, preserve the Java-selection changes in `gradlew` and `gradlew.bat`, synchronize the copies in `template`, and keep the two JARs identical. Update each distribution's properties and checksum together, and update the stock-JAR checksum in the regression checks if the wrapper binary changes. + # Manual A detailed description of the different tasks, functions and classes provided can be found in [the documentation](docs/subkt/index.md). diff --git a/gradle/wrapper/gradle-wrapper-legacy.jar b/gradle/wrapper/gradle-wrapper-legacy.jar new file mode 100644 index 0000000..9fa7f28 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper-legacy.jar differ diff --git a/gradle/wrapper/gradle-wrapper-legacy.properties b/gradle/wrapper/gradle-wrapper-legacy.properties new file mode 100644 index 0000000..a6c4f7b --- /dev/null +++ b/gradle/wrapper/gradle-wrapper-legacy.properties @@ -0,0 +1,10 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionSha256Sum=7988ed071b2a07900e2ec715fca15c6ab72bce6db433af301fa7fa7e9407bd24 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.6-bin.zip +networkTimeout=10000 +retries=0 +retryBackOffMs=500 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 02f06ab..9fa7f28 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 f900c20..a78c88e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,8 +1,10 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=7988ed071b2a07900e2ec715fca15c6ab72bce6db433af301fa7fa7e9407bd24 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.6-bin.zip +distributionSha256Sum=dce76f55f8e251a3a1f130eb120f30b3d271de2b76c9b0729d316b5a1b6dc01f +distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.1-bin.zip networkTimeout=10000 +retries=0 +retryBackOffMs=500 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 23d15a9..608904b 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. @@ -20,7 +20,7 @@ ############################################################################## # -# Gradle start up script for POSIX generated by Gradle. +# gradlew start up script for POSIX generated by Gradle. # # Important for running: # @@ -29,7 +29,7 @@ # bash, then to run this script, type that shell name before the whole # command line, like: # -# ksh Gradle +# ksh gradlew # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: @@ -57,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/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/. @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -201,6 +199,19 @@ if "$cygwin" || "$msys" ; then fi +# SubKt supports collaborators using either old or new Java installations. +# Keep both wrapper configurations immutable so switching JDKs never changes Git files. +SUBKT_JAVA_VERSION=$( "$JAVACMD" -XshowSettings:properties -version 2>&1 | + sed -n 's/^[[:space:]]*java.specification.version = //p' | + tr -d '\r' ) +case $SUBKT_JAVA_VERSION in + 11|12|13|14|15|16) SUBKT_WRAPPER_JAR=gradle-wrapper-legacy.jar ;; + ''|*[!0-9]*) die "Could not detect a supported Java version. SubKt requires JDK 11 or newer." ;; + *) + [ "$SUBKT_JAVA_VERSION" -ge 17 ] || die "SubKt requires JDK 11 or newer (found Java $SUBKT_JAVA_VERSION)." + SUBKT_WRAPPER_JAR=gradle-wrapper.jar ;; +esac + # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' @@ -212,8 +223,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + -jar "$APP_HOME/gradle/wrapper/$SUBKT_WRAPPER_JAR" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 5eed7ee..28e72a2 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -19,12 +19,39 @@ @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem -@rem Gradle startup script for Windows +@rem gradlew startup script for Windows @rem @rem ########################################################################## -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal +@rem Set local scope for the variables, and ensure extensions are enabled +setlocal EnableExtensions + +@rem Catch executions from older scripts and ensure they exit cleanly. +@rem This can be removed once we can be reasonably confident that few people +@rem will be migrating directly to this new wrapper. +goto afterSafetyNet +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +goto exitWithErrorLevel +:afterSafetyNet set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @@ -45,13 +72,14 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -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 +1>&2 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. -goto fail +"%COMSPEC%" /c exit 1 +goto exitWithErrorLevel :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -59,36 +87,52 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -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 +1>&2 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. -goto fail +"%COMSPEC%" /c exit 1 +goto exitWithErrorLevel :execute +@rem SubKt chooses Gradle for the installed Java without changing either properties file. +set SUBKT_JAVA_VERSION= +@rem The outer quotes protect the redirection until the nested command runs. +for /f "tokens=1,2,3" %%a in ('""%JAVA_EXE%" -XshowSettings:properties -version 2>&1"') do if "%%a"=="java.specification.version" set "SUBKT_JAVA_VERSION=%%c" +set SUBKT_WRAPPER_JAR=gradle-wrapper.jar +if "%SUBKT_JAVA_VERSION%"=="11" goto legacyWrapper +if "%SUBKT_JAVA_VERSION%"=="12" goto legacyWrapper +if "%SUBKT_JAVA_VERSION%"=="13" goto legacyWrapper +if "%SUBKT_JAVA_VERSION%"=="14" goto legacyWrapper +if "%SUBKT_JAVA_VERSION%"=="15" goto legacyWrapper +if "%SUBKT_JAVA_VERSION%"=="16" goto legacyWrapper +@rem Reject missing, non-numeric, and pre-11 versions before using numeric comparison. +if not defined SUBKT_JAVA_VERSION goto unsupportedJava +for /f "delims=0123456789" %%v in ("%SUBKT_JAVA_VERSION%") do goto unsupportedJava +if %SUBKT_JAVA_VERSION% LSS 17 goto unsupportedJava +goto launchWrapper + +:unsupportedJava +1>&2 echo ERROR: SubKt requires JDK 11 or newer. Detected version: %SUBKT_JAVA_VERSION% +"%COMSPEC%" /c exit 1 +goto exitWithErrorLevel + +:legacyWrapper +set SUBKT_WRAPPER_JAR=gradle-wrapper-legacy.jar + +:launchWrapper @rem Setup the command line -set CLASSPATH= -@rem Execute Gradle -"%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" %* +@rem Execute gradlew +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\%SUBKT_WRAPPER_JAR%" %* & call :exitWithErrorLevel & goto exitWithErrorLevel -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem This label must not be changed. We rely on old scripts being able to jump to this point. +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/template/gradle/wrapper/gradle-wrapper-legacy.jar b/template/gradle/wrapper/gradle-wrapper-legacy.jar new file mode 100644 index 0000000..9fa7f28 Binary files /dev/null and b/template/gradle/wrapper/gradle-wrapper-legacy.jar differ diff --git a/template/gradle/wrapper/gradle-wrapper-legacy.properties b/template/gradle/wrapper/gradle-wrapper-legacy.properties new file mode 100644 index 0000000..a6c4f7b --- /dev/null +++ b/template/gradle/wrapper/gradle-wrapper-legacy.properties @@ -0,0 +1,10 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionSha256Sum=7988ed071b2a07900e2ec715fca15c6ab72bce6db433af301fa7fa7e9407bd24 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.6-bin.zip +networkTimeout=10000 +retries=0 +retryBackOffMs=500 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/template/gradle/wrapper/gradle-wrapper.jar b/template/gradle/wrapper/gradle-wrapper.jar index 02f06ab..9fa7f28 100644 Binary files a/template/gradle/wrapper/gradle-wrapper.jar and b/template/gradle/wrapper/gradle-wrapper.jar differ diff --git a/template/gradle/wrapper/gradle-wrapper.properties b/template/gradle/wrapper/gradle-wrapper.properties index f900c20..a78c88e 100644 --- a/template/gradle/wrapper/gradle-wrapper.properties +++ b/template/gradle/wrapper/gradle-wrapper.properties @@ -1,8 +1,10 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=7988ed071b2a07900e2ec715fca15c6ab72bce6db433af301fa7fa7e9407bd24 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.6-bin.zip +distributionSha256Sum=dce76f55f8e251a3a1f130eb120f30b3d271de2b76c9b0729d316b5a1b6dc01f +distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.1-bin.zip networkTimeout=10000 +retries=0 +retryBackOffMs=500 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/template/gradlew b/template/gradlew index 23d15a9..608904b 100755 --- a/template/gradlew +++ b/template/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. @@ -20,7 +20,7 @@ ############################################################################## # -# Gradle start up script for POSIX generated by Gradle. +# gradlew start up script for POSIX generated by Gradle. # # Important for running: # @@ -29,7 +29,7 @@ # bash, then to run this script, type that shell name before the whole # command line, like: # -# ksh Gradle +# ksh gradlew # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: @@ -57,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/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/. @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -201,6 +199,19 @@ if "$cygwin" || "$msys" ; then fi +# SubKt supports collaborators using either old or new Java installations. +# Keep both wrapper configurations immutable so switching JDKs never changes Git files. +SUBKT_JAVA_VERSION=$( "$JAVACMD" -XshowSettings:properties -version 2>&1 | + sed -n 's/^[[:space:]]*java.specification.version = //p' | + tr -d '\r' ) +case $SUBKT_JAVA_VERSION in + 11|12|13|14|15|16) SUBKT_WRAPPER_JAR=gradle-wrapper-legacy.jar ;; + ''|*[!0-9]*) die "Could not detect a supported Java version. SubKt requires JDK 11 or newer." ;; + *) + [ "$SUBKT_JAVA_VERSION" -ge 17 ] || die "SubKt requires JDK 11 or newer (found Java $SUBKT_JAVA_VERSION)." + SUBKT_WRAPPER_JAR=gradle-wrapper.jar ;; +esac + # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' @@ -212,8 +223,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + -jar "$APP_HOME/gradle/wrapper/$SUBKT_WRAPPER_JAR" \ "$@" # Stop when "xargs" is not available. diff --git a/template/gradlew.bat b/template/gradlew.bat index 5eed7ee..28e72a2 100644 --- a/template/gradlew.bat +++ b/template/gradlew.bat @@ -19,12 +19,39 @@ @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem -@rem Gradle startup script for Windows +@rem gradlew startup script for Windows @rem @rem ########################################################################## -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal +@rem Set local scope for the variables, and ensure extensions are enabled +setlocal EnableExtensions + +@rem Catch executions from older scripts and ensure they exit cleanly. +@rem This can be removed once we can be reasonably confident that few people +@rem will be migrating directly to this new wrapper. +goto afterSafetyNet +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +goto exitWithErrorLevel +:afterSafetyNet set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @@ -45,13 +72,14 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -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 +1>&2 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. -goto fail +"%COMSPEC%" /c exit 1 +goto exitWithErrorLevel :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -59,36 +87,52 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -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 +1>&2 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. -goto fail +"%COMSPEC%" /c exit 1 +goto exitWithErrorLevel :execute +@rem SubKt chooses Gradle for the installed Java without changing either properties file. +set SUBKT_JAVA_VERSION= +@rem The outer quotes protect the redirection until the nested command runs. +for /f "tokens=1,2,3" %%a in ('""%JAVA_EXE%" -XshowSettings:properties -version 2>&1"') do if "%%a"=="java.specification.version" set "SUBKT_JAVA_VERSION=%%c" +set SUBKT_WRAPPER_JAR=gradle-wrapper.jar +if "%SUBKT_JAVA_VERSION%"=="11" goto legacyWrapper +if "%SUBKT_JAVA_VERSION%"=="12" goto legacyWrapper +if "%SUBKT_JAVA_VERSION%"=="13" goto legacyWrapper +if "%SUBKT_JAVA_VERSION%"=="14" goto legacyWrapper +if "%SUBKT_JAVA_VERSION%"=="15" goto legacyWrapper +if "%SUBKT_JAVA_VERSION%"=="16" goto legacyWrapper +@rem Reject missing, non-numeric, and pre-11 versions before using numeric comparison. +if not defined SUBKT_JAVA_VERSION goto unsupportedJava +for /f "delims=0123456789" %%v in ("%SUBKT_JAVA_VERSION%") do goto unsupportedJava +if %SUBKT_JAVA_VERSION% LSS 17 goto unsupportedJava +goto launchWrapper + +:unsupportedJava +1>&2 echo ERROR: SubKt requires JDK 11 or newer. Detected version: %SUBKT_JAVA_VERSION% +"%COMSPEC%" /c exit 1 +goto exitWithErrorLevel + +:legacyWrapper +set SUBKT_WRAPPER_JAR=gradle-wrapper-legacy.jar + +:launchWrapper @rem Setup the command line -set CLASSPATH= -@rem Execute Gradle -"%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" %* +@rem Execute gradlew +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\%SUBKT_WRAPPER_JAR%" %* & call :exitWithErrorLevel & goto exitWithErrorLevel -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem This label must not be changed. We rely on old scripts being able to jump to this point. +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/tests/wrapper_compatibility.py b/tests/wrapper_compatibility.py new file mode 100644 index 0000000..e5d5b44 --- /dev/null +++ b/tests/wrapper_compatibility.py @@ -0,0 +1,110 @@ +"""Exercise the actual launchers with the JDK selected by JAVA_HOME (or PATH). + +Run once per JDK, e.g. python3 tests/wrapper_compatibility.py 11 8.14.6. +Only the Python standard library is needed; Gradle downloads through the wrapper. +""" + +import hashlib +import os +from pathlib import Path +import shutil +import subprocess +import sys +import tempfile + + +ROOT = Path(__file__).resolve().parents[1] +WRAPPER_FILES = [ + "gradlew", + "gradlew.bat", + "gradle/wrapper/gradle-wrapper.jar", + "gradle/wrapper/gradle-wrapper.properties", + "gradle/wrapper/gradle-wrapper-legacy.jar", + "gradle/wrapper/gradle-wrapper-legacy.properties", +] +STOCK_JAR_SHA256 = "3b8a25775a69158b5ad2b1d17a80a88dc7b40a352a73eb27d06c612a7ce68e98" + + +def check_wrappers(): + for name in WRAPPER_FILES: + data = (ROOT / name).read_bytes() + assert data == (ROOT / "template" / name).read_bytes(), name + if name.endswith(".jar"): + assert hashlib.sha256(data).hexdigest() == STOCK_JAR_SHA256, name + + +def run(project, env, *arguments, success=True): + launcher = project / ("gradlew.bat" if os.name == "nt" else "gradlew") + result = subprocess.run( + [str(launcher), "--project-dir", str(project), "--console=plain", *arguments], + cwd=project.parent, # Invocation outside the project must also work. + env=env, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + ) + print(result.stdout, flush=True) + assert (result.returncode == 0) == success, result.returncode + return result.stdout + + +def main(): + java_version, gradle_version = sys.argv[1:] + check_wrappers() + with tempfile.TemporaryDirectory(prefix="subkt wrapper ") as temporary: + project = Path(temporary) / "project with spaces" + for name in WRAPPER_FILES: + target = project / name + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(ROOT / "template" / name, target) + original = {name: (project / name).read_bytes() for name in WRAPPER_FILES} + (project / "settings.gradle").write_text("rootProject.name = 'wrapper-test'\n") + (project / "build.gradle").write_text(""" +tasks.register('verifyWrapper') { + doLast { + assert gradle.gradleVersion == providers.gradleProperty('expectedGradle').get() + assert System.getProperty('java.specification.version') == providers.gradleProperty('expectedJava').get() + assert providers.gradleProperty('message').get() == 'argument with spaces' + assert System.getProperty('subkt.wrapper.javaOpt') == 'java-value' + assert System.getProperty('subkt.wrapper.gradleOpt') == 'gradle-value' + println 'WRAPPER_VERIFIED' + } +} +tasks.register('failWrapper') { + doLast { throw new GradleException('EXPECTED_WRAPPER_FAILURE') } +} +""") + env = dict(os.environ) + env["JAVA_OPTS"] = env.get("JAVA_OPTS", "") + " -Dsubkt.wrapper.javaOpt=java-value" + env["GRADLE_OPTS"] = env.get("GRADLE_OPTS", "") + " -Dsubkt.wrapper.gradleOpt=gradle-value" + java_home = env.get("JAVA_HOME") + if java_home: + # Exercise a JAVA_HOME containing spaces as well as project paths. + alias = Path(temporary) / "java home" + if os.name == "nt": + # Directory junctions do not require Windows symlink privileges. + subprocess.run( + [env["COMSPEC"], "/d", "/c", "mklink", "/J", str(alias), java_home], + check=True, + ) + else: + alias.symlink_to(java_home, target_is_directory=True) + env["JAVA_HOME"] = str(alias) + env["PATH"] = str(alias / "bin") + os.pathsep + env["PATH"] + arguments = [ + "verifyWrapper", + "-PexpectedJava=" + java_version, + "-PexpectedGradle=" + gradle_version, + "-Pmessage=argument with spaces", + ] + assert "WRAPPER_VERIFIED" in run(project, env, *arguments) + env.pop("JAVA_HOME", None) + assert "WRAPPER_VERIFIED" in run(project, env, *arguments) + assert "EXPECTED_WRAPPER_FAILURE" in run(project, env, "failWrapper", success=False) + for name, data in original.items(): + assert (project / name).read_bytes() == data, name + " was modified" + print("Wrapper checks passed for Java " + java_version, flush=True) + + +if __name__ == "__main__": + main()