From cf4d7a1197fbf2dae532550f7be9df9f27949d08 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:23 +0200 Subject: [PATCH 01/31] Publish Gradle Module Metadata with additional dependency constraints --- android/guava/pom.xml | 45 ++++++++ android/pom.xml | 16 +++ guava/module.json | 259 ++++++++++++++++++++++++++++++++++++++++++ guava/pom.xml | 45 ++++++++ pom.xml | 16 +++ util/set_version.sh | 10 ++ 6 files changed, 391 insertions(+) create mode 100644 guava/module.json diff --git a/android/guava/pom.xml b/android/guava/pom.xml index 826b88d18182..01681d672b4e 100644 --- a/android/guava/pom.xml +++ b/android/guava/pom.xml @@ -1,6 +1,7 @@ + 4.0.0 com.google.guava @@ -209,6 +210,50 @@ + + maven-resources-plugin + + + gradle-module-metadata + compile + + copy-resources + + + target/publish + + + ../../guava + + module.json + + true + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-gradle-module-metadata + + attach-artifact + + + + + target/publish/module.json + module + + + + + + diff --git a/android/pom.xml b/android/pom.xml index 2ee9ed9d21bb..27320699059f 100644 --- a/android/pom.xml +++ b/android/pom.xml @@ -23,6 +23,13 @@ 1980-02-01T00:00:00Z UTF-8 + integration + HEAD-jre-SNAPSHOT + 6 + "checker-compat-qual", "version": { "requires": "2.5.5" } + HEAD-jre-SNAPSHOT + 8 + "checker-qual" GitHub Issues @@ -262,6 +269,15 @@ maven-enforcer-plugin 3.0.0-M3 + + maven-resources-plugin + 3.1.0 + + + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + diff --git a/guava/module.json b/guava/module.json new file mode 100644 index 000000000000..67fd6c86939a --- /dev/null +++ b/guava/module.json @@ -0,0 +1,259 @@ +{ + "formatVersion": "1.1", + "component": { + "group": "${pom.groupId}", + "module": "${pom.artifactId}", + "version": "${pom.version}", + "attributes": { + "org.gradle.status": "${module.status}" + } + }, + "createdBy": { + "maven": { + "version": "${maven.version}", + "buildId": "${maven.build.version}" + } + }, + "variants": [ + { + "name": "jdk${variant.jvmVersion}ApiElements", + "attributes": { + "org.gradle.category": "library", + "org.gradle.dependency.bundling": "external", + "org.gradle.jvm.version": "${variant.jvmVersion}", + "org.gradle.libraryelements": "jar", + "org.gradle.usage": "java-api" + }, + "dependencies": [ + { + "group": "com.google.guava", + "module": "guava-parent", + "version": { + "requires": "${module.parentVersion}" + }, + "attributes": { + "org.gradle.category": "platform" + } + }, + { + "group": "com.google.guava", + "module": "failureaccess", + "version": { + "requires": "1.0.1" + } + }, + { + "group": "com.google.code.findbugs", + "module": "jsr305" + }, + { + "group": "org.checkerframework", + "module": ${variant.checkerframework} + }, + { + "group": "com.google.errorprone", + "module": "error_prone_annotations" + }, + { + "group": "com.google.j2objc", + "module": "j2objc-annotations" + } + ], + "files": [ + { + "name": "${project.build.finalName}.jar", + "url": "${project.build.finalName}.jar" + } + ], + "capabilities": [ + { + "group": "com.google.guava", + "name": "guava", + "version": "${pom.version}" + }, + { + "group": "com.google.collections", + "name": "google-collections", + "version": "${pom.version}" + }, + { + "group": "com.google.guava", + "name": "listenablefuture", + "version": "1.0" + } + ] + }, + { + "name": "jdk${variant.jvmVersion}RuntimeElements", + "attributes": { + "org.gradle.category": "library", + "org.gradle.dependency.bundling": "external", + "org.gradle.jvm.version": "${variant.jvmVersion}", + "org.gradle.libraryelements": "jar", + "org.gradle.usage": "java-runtime" + }, + "dependencies": [ + { + "group": "com.google.guava", + "module": "guava-parent", + "version": { + "requires": "${module.parentVersion}" + }, + "attributes": { + "org.gradle.category": "platform" + } + }, + { + "group": "com.google.guava", + "module": "failureaccess", + "version": { + "requires": "1.0.1" + } + } + ], + "files": [ + { + "name": "${project.build.finalName}.jar", + "url": "${project.build.finalName}.jar" + } + ], + "capabilities": [ + { + "group": "com.google.guava", + "name": "guava", + "version": "${pom.version}" + }, + { + "group": "com.google.collections", + "name": "google-collections", + "version": "${pom.version}" + }, + { + "group": "com.google.guava", + "name": "listenablefuture", + "version": "1.0" + } + ] + }, + { + "name": "jdk${otherVariant.jvmVersion}ApiElements", + "attributes": { + "org.gradle.category": "library", + "org.gradle.dependency.bundling": "external", + "org.gradle.jvm.version": "${otherVariant.jvmVersion}", + "org.gradle.libraryelements": "jar", + "org.gradle.usage": "java-api" + }, + "dependencies": [ + { + "group": "com.google.guava", + "module": "guava-parent", + "version": { + "requires": "${module.parentVersion}" + }, + "attributes": { + "org.gradle.category": "platform" + } + }, + { + "group": "com.google.guava", + "module": "failureaccess", + "version": { + "requires": "1.0.1" + } + }, + { + "group": "com.google.code.findbugs", + "module": "jsr305" + }, + { + "group": "org.checkerframework", + "module": ${otherVariant.checkerframework} + }, + { + "group": "com.google.errorprone", + "module": "error_prone_annotations" + }, + { + "group": "com.google.j2objc", + "module": "j2objc-annotations" + } + ], + "files": [ + { + "name": "${pom.artifactId}-${otherVariant.version}.jar", + "url": "../${otherVariant.version}/${pom.artifactId}-${otherVariant.version}.jar" + } + ], + "capabilities": [ + { + "group": "com.google.guava", + "name": "guava", + "version": "${pom.version}" + }, + { + "group": "com.google.collections", + "name": "google-collections", + "version": "${pom.version}" + }, + { + "group": "com.google.guava", + "name": "listenablefuture", + "version": "1.0" + } + ] + }, + { + "name": "jdk${otherVariant.jvmVersion}RuntimeElements", + "attributes": { + "org.gradle.category": "library", + "org.gradle.dependency.bundling": "external", + "org.gradle.jvm.version": "${otherVariant.jvmVersion}", + "org.gradle.libraryelements": "jar", + "org.gradle.usage": "java-runtime" + }, + "dependencies": [ + { + "group": "com.google.guava", + "module": "guava-parent", + "version": { + "requires": "${module.parentVersion}" + }, + "attributes": { + "org.gradle.category": "platform" + } + }, + { + "group": "com.google.guava", + "module": "failureaccess", + "version": { + "requires": "1.0.1" + } + } + ], + "files": [ + { + "name": "${pom.artifactId}-${otherVariant.version}.jar", + "url": "../${otherVariant.version}/${pom.artifactId}-${otherVariant.version}.jar" + } + ], + "capabilities": [ + { + "group": "com.google.guava", + "name": "guava", + "version": "${pom.version}" + }, + { + "group": "com.google.collections", + "name": "google-collections", + "version": "${pom.version}" + }, + { + "group": "com.google.guava", + "name": "listenablefuture", + "version": "1.0" + } + ] + } + ] +} diff --git a/guava/pom.xml b/guava/pom.xml index ccbcbd6cb3dd..c7c39a7498c8 100644 --- a/guava/pom.xml +++ b/guava/pom.xml @@ -1,6 +1,7 @@ + 4.0.0 com.google.guava @@ -209,6 +210,50 @@ + + maven-resources-plugin + + + gradle-module-metadata + compile + + copy-resources + + + target/publish + + + . + + module.json + + true + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-gradle-module-metadata + + attach-artifact + + + + + target/publish/module.json + module + + + + + + diff --git a/pom.xml b/pom.xml index 6491e13d9686..1656571c9359 100644 --- a/pom.xml +++ b/pom.xml @@ -23,6 +23,13 @@ 1980-02-01T00:00:00Z UTF-8 + integration + HEAD-jre-SNAPSHOT + 8 + "checker-qual" + HEAD-android-SNAPSHOT + 6 + "checker-compat-qual", "version": { "requires": "2.5.5" } GitHub Issues @@ -256,6 +263,15 @@ maven-enforcer-plugin 3.0.0-M3 + + maven-resources-plugin + 3.1.0 + + + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + diff --git a/util/set_version.sh b/util/set_version.sh index 9db8c7ffaba5..d05866eca660 100755 --- a/util/set_version.sh +++ b/util/set_version.sh @@ -8,7 +8,17 @@ if (( $# != 1 )); then fi version="$1" +status="release" +if [[ $version == *"SNAPSHOT"* ]]; then + status="integration" +fi mvn versions:set versions:commit -DnewVersion="${version}-jre" mvn versions:set versions:commit -DnewVersion="${version}-android" -f android +mvn versions:set-property -Dproperty=otherVariant.version -DnewVersion="${version}-android" +mvn versions:set-property -Dproperty=otherVariant.version -DnewVersion="${version}-jre" -f android +mvn versions:set-property -Dproperty=module.status -DnewVersion="${status}" +mvn versions:set-property -Dproperty=module.status -DnewVersion="${status}" -f android +mvn versions:set-property -Dproperty=module.parentVersion -DnewVersion="${version}-jre" +mvn versions:set-property -Dproperty=module.parentVersion -DnewVersion="${version}-jre" -f android git commit -am "Set version numbers to ${version}" From 31c202687c9287a50b1123cc918826840b953058 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:23 +0200 Subject: [PATCH 02/31] Add integration test for resolving Guava with Gradle - only using POM metadata (Gradle 5) - using Gradle Module Metadata (Gradle 6) --- integration-tests/gradle/.gitignore | 2 + integration-tests/gradle/build.gradle.kts | 124 ++++++++++++ .../gradle/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 55616 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + integration-tests/gradle/gradlew | 188 ++++++++++++++++++ integration-tests/gradle/gradlew.bat | 100 ++++++++++ integration-tests/gradle/settings.gradle.kts | 11 + util/gradle_integration_tests.sh | 9 + 8 files changed, 439 insertions(+) create mode 100644 integration-tests/gradle/.gitignore create mode 100644 integration-tests/gradle/build.gradle.kts create mode 100644 integration-tests/gradle/gradle/wrapper/gradle-wrapper.jar create mode 100644 integration-tests/gradle/gradle/wrapper/gradle-wrapper.properties create mode 100755 integration-tests/gradle/gradlew create mode 100644 integration-tests/gradle/gradlew.bat create mode 100644 integration-tests/gradle/settings.gradle.kts create mode 100755 util/gradle_integration_tests.sh diff --git a/integration-tests/gradle/.gitignore b/integration-tests/gradle/.gitignore new file mode 100644 index 000000000000..f8b92c3aa087 --- /dev/null +++ b/integration-tests/gradle/.gitignore @@ -0,0 +1,2 @@ +.gradle +build diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts new file mode 100644 index 000000000000..945205c59735 --- /dev/null +++ b/integration-tests/gradle/build.gradle.kts @@ -0,0 +1,124 @@ +val expectedReducedRuntimeClasspathJava6 = setOf( + "guava-HEAD-android-SNAPSHOT.jar", + "failureaccess-1.0.1.jar" +) +val expectedReducedRuntimeClasspathJava8 = setOf( + "guava-HEAD-jre-SNAPSHOT.jar", + "failureaccess-1.0.1.jar" +) +val expectedCompileClasspathJava6 = expectedReducedRuntimeClasspathJava6 + setOf( + "jsr305-3.0.2.jar", + "checker-compat-qual-2.5.5.jar", + "error_prone_annotations-2.3.4.jar", + "j2objc-annotations-1.3.jar" +) +val expectedCompileClasspathJava8 = expectedReducedRuntimeClasspathJava8 + setOf( + "jsr305-3.0.2.jar", + "checker-qual-2.11.1.jar", + "error_prone_annotations-2.3.4.jar", + "j2objc-annotations-1.3.jar" +) + +val extraLegacyDependencies = setOf( + "listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar", + "google-collections-1.0.jar" +) + +subprojects { + apply(plugin = "java-library") + + val expectedClasspath = + if (gradle.gradleVersion.startsWith("5.")) { + // without Gradle Module Metadata (only the POM is used) + // - variant decission is made based on version suffix (android/jre) and not on actual Java version + // - runtime classpath equals the compile classpath + // - dependency conflict with Google Collections is not detected and '9999.0' hack is present + if (name.startsWith("android")) { + expectedCompileClasspathJava6 + extraLegacyDependencies + } else { + expectedCompileClasspathJava8 + extraLegacyDependencies + } + } else { + // with Gradle Module Metadata + // - variant is choosend based on Java version used independent of version suffix + // - reduced runtime classpath is used (w/o annotation libraries) + // - capability conflicts are detected between Google Collections and Listenablefuture + if (name.contains("Java6")) { + if (name.contains("runtimeClasspath")) { + expectedReducedRuntimeClasspathJava6 + } else { + expectedCompileClasspathJava6 + } + } else { + if (name.contains("runtimeClasspath")) { + expectedReducedRuntimeClasspathJava8 + } else { + expectedCompileClasspathJava8 + } + } + } + val guavaVersion = if (name.startsWith("jre")) { + "HEAD-jre-SNAPSHOT" + } else { + "HEAD-android-SNAPSHOT" + } + val javaVersion = if (name.contains("Java6")) { + JavaVersion.VERSION_1_6 + } else { + JavaVersion.VERSION_1_8 + } + val classpathConfiguration = if (name.contains("runtimeClasspath")) { + configurations["runtimeClasspath"] + } else { + configurations["compileClasspath"] + } + + repositories { + mavenCentral() + mavenLocal() + } + val java = extensions.getByType() + java.targetCompatibility = javaVersion + java.sourceCompatibility = javaVersion + + if (!gradle.gradleVersion.startsWith("5.")) { + configurations.all { + resolutionStrategy.capabilitiesResolution { + withCapability("com.google.collections:google-collections") { + candidates.find { + val idField = it.javaClass.getDeclaredMethod("getId") // reflective access to make this compile with Gradle 5 + (idField.invoke(it) as ModuleComponentIdentifier).module == "guava" + }?.apply { + select(this) + } + } + withCapability("com.google.guava:listenablefuture") { + candidates.find { + val idField = it.javaClass.getDeclaredMethod("getId") // reflective access to make this compile with Gradle 5 + (idField.invoke(it) as ModuleComponentIdentifier).module == "guava" + }?.apply { + select(this) + } + } + } + } + } + + dependencies { + "api"("com.google.collections:google-collections:1.0") + "api"("com.google.guava:listenablefuture:1.0") + "api"("com.google.guava:guava:$guavaVersion") + } + + tasks.register("testClasspath") { + doLast { + val actualClasspath = classpathConfiguration.files.map { it.name }.toSet() + if (actualClasspath != expectedClasspath) { + throw RuntimeException(""" + Expected: ${expectedClasspath.sorted()} + Actual: ${actualClasspath.sorted()} + """.trimIndent()) + } + } + } +} \ No newline at end of file diff --git a/integration-tests/gradle/gradle/wrapper/gradle-wrapper.jar b/integration-tests/gradle/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..5c2d1cf016b3885f6930543d57b744ea8c220a1a GIT binary patch literal 55616 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/integration-tests/gradle/gradlew.bat b/integration-tests/gradle/gradlew.bat new file mode 100644 index 000000000000..9618d8d9607c --- /dev/null +++ b/integration-tests/gradle/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +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. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +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. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="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! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/integration-tests/gradle/settings.gradle.kts b/integration-tests/gradle/settings.gradle.kts new file mode 100644 index 000000000000..37924562d1c6 --- /dev/null +++ b/integration-tests/gradle/settings.gradle.kts @@ -0,0 +1,11 @@ +rootProject.name = "guava-integration-test" + +include("jreJava8CompileClasspathJava") +include("androidJava8CompileClasspathJava") +include("jreJava8RuntimeClasspathJava") +include("androidJava8RuntimeClasspathJava") + +include("jreJava6CompileClasspathJava") +include("androidJava6CompileClasspathJava") +include("jreJava6RuntimeClasspathJava") +include("androidJava6RuntimeClasspathJava") diff --git a/util/gradle_integration_tests.sh b/util/gradle_integration_tests.sh new file mode 100755 index 000000000000..ce1bf4a7c456 --- /dev/null +++ b/util/gradle_integration_tests.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +mvn clean install -DskipTests +mvn clean install -DskipTests -f android + +integration-tests/gradle/gradlew -p integration-tests/gradle wrapper --gradle-version=5.6.4 +integration-tests/gradle/gradlew -p integration-tests/gradle testClasspath +integration-tests/gradle/gradlew -p integration-tests/gradle wrapper --gradle-version=6.0.1 +integration-tests/gradle/gradlew -p integration-tests/gradle testClasspath From 3db90c4c19093bcc73ebcf5098b374adb17d6831 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:23 +0200 Subject: [PATCH 03/31] Add Gradle wrapper validation for the wrapper in the test project --- .github/workflows/gradle-wrapper-validation.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/workflows/gradle-wrapper-validation.yml diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml new file mode 100644 index 000000000000..405a2b306592 --- /dev/null +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -0,0 +1,10 @@ +name: "Validate Gradle Wrapper" +on: [push, pull_request] + +jobs: + validation: + name: "Validation" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: gradle/wrapper-validation-action@v1 From 643719784e404917981aabf8fca861c061529a2d Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:24 +0200 Subject: [PATCH 04/31] Adjust expectations for 'checker-qual' version See: 1bfbca3 --- integration-tests/gradle/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index 945205c59735..e6885d53ff81 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -14,7 +14,7 @@ val expectedCompileClasspathJava6 = expectedReducedRuntimeClasspathJava6 + setOf ) val expectedCompileClasspathJava8 = expectedReducedRuntimeClasspathJava8 + setOf( "jsr305-3.0.2.jar", - "checker-qual-2.11.1.jar", + "checker-qual-3.5.0.jar", "error_prone_annotations-2.3.4.jar", "j2objc-annotations-1.3.jar" ) From e643f3ffe73c2806f39341384a6b724ffe27d495 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:24 +0200 Subject: [PATCH 05/31] Fixed typos --- integration-tests/gradle/build.gradle.kts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index e6885d53ff81..305226111151 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -30,7 +30,7 @@ subprojects { val expectedClasspath = if (gradle.gradleVersion.startsWith("5.")) { // without Gradle Module Metadata (only the POM is used) - // - variant decission is made based on version suffix (android/jre) and not on actual Java version + // - variant decision is made based on version suffix (android/jre) and not on actual Java version // - runtime classpath equals the compile classpath // - dependency conflict with Google Collections is not detected and '9999.0' hack is present if (name.startsWith("android")) { @@ -40,7 +40,7 @@ subprojects { } } else { // with Gradle Module Metadata - // - variant is choosend based on Java version used independent of version suffix + // - variant is chosen based on Java version used independent of version suffix // - reduced runtime classpath is used (w/o annotation libraries) // - capability conflicts are detected between Google Collections and Listenablefuture if (name.contains("Java6")) { @@ -58,9 +58,9 @@ subprojects { } } val guavaVersion = if (name.startsWith("jre")) { - "HEAD-jre-SNAPSHOT" + guavaVersionJre } else { - "HEAD-android-SNAPSHOT" + guavaVersionJre.replace("jre", "android") } val javaVersion = if (name.contains("Java6")) { JavaVersion.VERSION_1_6 From f9fb5c5f562c005b4be32fb303aba7304b334d85 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:24 +0200 Subject: [PATCH 06/31] Read Guava version under test from pom.xml --- integration-tests/gradle/build.gradle.kts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index 305226111151..90d625713e06 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -1,9 +1,12 @@ +val guavaVersionJre = "(.*)".toRegex().find(file("../../pom.xml").readText()) + ?.groups?.get(1)?.value?: error("version not found in pom") + val expectedReducedRuntimeClasspathJava6 = setOf( - "guava-HEAD-android-SNAPSHOT.jar", + "guava-${guavaVersionJre.replace("jre", "android")}.jar", "failureaccess-1.0.1.jar" ) val expectedReducedRuntimeClasspathJava8 = setOf( - "guava-HEAD-jre-SNAPSHOT.jar", + "guava-${guavaVersionJre}.jar", "failureaccess-1.0.1.jar" ) val expectedCompileClasspathJava6 = expectedReducedRuntimeClasspathJava6 + setOf( From 905909c43e4fd8700e2f0ee89d075c43d23e737b Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:25 +0200 Subject: [PATCH 07/31] Use correct version for parents The 'android' and 'jre' variant each have their own parent and they should use that one in Gradle Module Metadata. --- android/pom.xml | 5 ++--- guava/module.json | 12 ++++++------ pom.xml | 5 ++--- util/set_version.sh | 2 -- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/android/pom.xml b/android/pom.xml index 27320699059f..f92bd16307d1 100644 --- a/android/pom.xml +++ b/android/pom.xml @@ -24,12 +24,11 @@ UTF-8 integration - HEAD-jre-SNAPSHOT 6 - "checker-compat-qual", "version": { "requires": "2.5.5" } + checker-compat-qual HEAD-jre-SNAPSHOT 8 - "checker-qual" + checker-qual GitHub Issues diff --git a/guava/module.json b/guava/module.json index 67fd6c86939a..c9316795e557 100644 --- a/guava/module.json +++ b/guava/module.json @@ -29,7 +29,7 @@ "group": "com.google.guava", "module": "guava-parent", "version": { - "requires": "${module.parentVersion}" + "requires": "${pom.version}" }, "attributes": { "org.gradle.category": "platform" @@ -48,7 +48,7 @@ }, { "group": "org.checkerframework", - "module": ${variant.checkerframework} + "module": "${variant.checkerframework}" }, { "group": "com.google.errorprone", @@ -97,7 +97,7 @@ "group": "com.google.guava", "module": "guava-parent", "version": { - "requires": "${module.parentVersion}" + "requires": "${pom.version}" }, "attributes": { "org.gradle.category": "platform" @@ -149,7 +149,7 @@ "group": "com.google.guava", "module": "guava-parent", "version": { - "requires": "${module.parentVersion}" + "requires": "${otherVariant.version}" }, "attributes": { "org.gradle.category": "platform" @@ -168,7 +168,7 @@ }, { "group": "org.checkerframework", - "module": ${otherVariant.checkerframework} + "module": "${otherVariant.checkerframework}" }, { "group": "com.google.errorprone", @@ -217,7 +217,7 @@ "group": "com.google.guava", "module": "guava-parent", "version": { - "requires": "${module.parentVersion}" + "requires": "${otherVariant.version}" }, "attributes": { "org.gradle.category": "platform" diff --git a/pom.xml b/pom.xml index 1656571c9359..7e40d1d4313c 100644 --- a/pom.xml +++ b/pom.xml @@ -24,12 +24,11 @@ UTF-8 integration - HEAD-jre-SNAPSHOT 8 - "checker-qual" + checker-qual HEAD-android-SNAPSHOT 6 - "checker-compat-qual", "version": { "requires": "2.5.5" } + checker-compat-qual GitHub Issues diff --git a/util/set_version.sh b/util/set_version.sh index d05866eca660..fd94d66399c0 100755 --- a/util/set_version.sh +++ b/util/set_version.sh @@ -19,6 +19,4 @@ mvn versions:set-property -Dproperty=otherVariant.version -DnewVersion="${versio mvn versions:set-property -Dproperty=otherVariant.version -DnewVersion="${version}-jre" -f android mvn versions:set-property -Dproperty=module.status -DnewVersion="${status}" mvn versions:set-property -Dproperty=module.status -DnewVersion="${status}" -f android -mvn versions:set-property -Dproperty=module.parentVersion -DnewVersion="${version}-jre" -mvn versions:set-property -Dproperty=module.parentVersion -DnewVersion="${version}-jre" -f android git commit -am "Set version numbers to ${version}" From 7a2f0a01a17fd7b8217db7fa26b13a028baa37a0 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:25 +0200 Subject: [PATCH 08/31] Fix typo in classpath name. This causes integration tests to fail. The first failure: Execution failed for task ':androidJava6RuntimeClasspathJava:testClasspath'. > Expected: [failureaccess-1.0.1.jar, guava-HEAD-android-SNAPSHOT.jar] Actual: [checker-compat-qual-2.5.5.jar, error_prone_annotations-2.3.4.jar, failureaccess-1.0.1.jar, guava-HEAD-android-SNAPSHOT.jar, j2objc-annotations-1.3.jar, jsr305-3.0.2.jar] --- integration-tests/gradle/build.gradle.kts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index 90d625713e06..b58695de2d46 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -47,16 +47,20 @@ subprojects { // - reduced runtime classpath is used (w/o annotation libraries) // - capability conflicts are detected between Google Collections and Listenablefuture if (name.contains("Java6")) { - if (name.contains("runtimeClasspath")) { + if (name.contains("RuntimeClasspath")) { expectedReducedRuntimeClasspathJava6 - } else { + } else if (name.contains("CompileClasspath")) { expectedCompileClasspathJava6 + } else { + error("unexpected classpath type: " + name) } } else { - if (name.contains("runtimeClasspath")) { + if (name.contains("RuntimeClasspath")) { expectedReducedRuntimeClasspathJava8 - } else { + } else if (name.contains("CompileClasspath")) { expectedCompileClasspathJava8 + } else { + error("unexpected classpath type: " + name) } } } @@ -124,4 +128,4 @@ subprojects { } } } -} \ No newline at end of file +} From 8191fc375b444a0e19066021af52f435cd67eaa8 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:25 +0200 Subject: [PATCH 09/31] Also fix typo when looking up configuration. This makes the test pass again, this time because it's testing what we want :) --- integration-tests/gradle/build.gradle.kts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index b58695de2d46..0ebf712ddd65 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -74,10 +74,12 @@ subprojects { } else { JavaVersion.VERSION_1_8 } - val classpathConfiguration = if (name.contains("runtimeClasspath")) { + val classpathConfiguration = if (name.contains("RuntimeClasspath")) { configurations["runtimeClasspath"] - } else { + } else if (name.contains("CompileClasspath")) { configurations["compileClasspath"] + } else { + error("unexpected classpath type: " + name) } repositories { From 179136bec4c354d6cdb7514363f5761ffefe6a5b Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:26 +0200 Subject: [PATCH 10/31] Satisfy our autoformatters. --- android/guava/pom.xml | 44 ++--- integration-tests/gradle/build.gradle.kts | 210 +++++++++++----------- 2 files changed, 128 insertions(+), 126 deletions(-) diff --git a/android/guava/pom.xml b/android/guava/pom.xml index 01681d672b4e..dbe5533d44d6 100644 --- a/android/guava/pom.xml +++ b/android/guava/pom.xml @@ -211,29 +211,29 @@ - maven-resources-plugin - - - gradle-module-metadata - compile - - copy-resources - - - target/publish - - + maven-resources-plugin + + + gradle-module-metadata + compile + + copy-resources + + + target/publish + + ../../guava - - module.json - - true - - - - - - + + module.json + + true + + + + + + org.codehaus.mojo build-helper-maven-plugin diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index 0ebf712ddd65..530b34fa1350 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -1,133 +1,135 @@ val guavaVersionJre = "(.*)".toRegex().find(file("../../pom.xml").readText()) - ?.groups?.get(1)?.value?: error("version not found in pom") + ?.groups?.get(1)?.value ?: error("version not found in pom") val expectedReducedRuntimeClasspathJava6 = setOf( - "guava-${guavaVersionJre.replace("jre", "android")}.jar", - "failureaccess-1.0.1.jar" + "guava-${guavaVersionJre.replace("jre", "android")}.jar", + "failureaccess-1.0.1.jar" ) val expectedReducedRuntimeClasspathJava8 = setOf( - "guava-${guavaVersionJre}.jar", - "failureaccess-1.0.1.jar" + "guava-${guavaVersionJre}.jar", + "failureaccess-1.0.1.jar" ) val expectedCompileClasspathJava6 = expectedReducedRuntimeClasspathJava6 + setOf( - "jsr305-3.0.2.jar", - "checker-compat-qual-2.5.5.jar", - "error_prone_annotations-2.3.4.jar", - "j2objc-annotations-1.3.jar" + "jsr305-3.0.2.jar", + "checker-compat-qual-2.5.5.jar", + "error_prone_annotations-2.3.4.jar", + "j2objc-annotations-1.3.jar" ) val expectedCompileClasspathJava8 = expectedReducedRuntimeClasspathJava8 + setOf( - "jsr305-3.0.2.jar", - "checker-qual-3.5.0.jar", - "error_prone_annotations-2.3.4.jar", - "j2objc-annotations-1.3.jar" + "jsr305-3.0.2.jar", + "checker-qual-3.5.0.jar", + "error_prone_annotations-2.3.4.jar", + "j2objc-annotations-1.3.jar" ) val extraLegacyDependencies = setOf( - "listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar", - "google-collections-1.0.jar" + "listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar", + "google-collections-1.0.jar" ) subprojects { - apply(plugin = "java-library") + apply(plugin = "java-library") - val expectedClasspath = - if (gradle.gradleVersion.startsWith("5.")) { - // without Gradle Module Metadata (only the POM is used) - // - variant decision is made based on version suffix (android/jre) and not on actual Java version - // - runtime classpath equals the compile classpath - // - dependency conflict with Google Collections is not detected and '9999.0' hack is present - if (name.startsWith("android")) { - expectedCompileClasspathJava6 + extraLegacyDependencies - } else { - expectedCompileClasspathJava8 + extraLegacyDependencies - } + val expectedClasspath = + if (gradle.gradleVersion.startsWith("5.")) { + // without Gradle Module Metadata (only the POM is used) + // - variant decision is made based on version suffix (android/jre) and not on actual Java version + // - runtime classpath equals the compile classpath + // - dependency conflict with Google Collections is not detected and '9999.0' hack is present + if (name.startsWith("android")) { + expectedCompileClasspathJava6 + extraLegacyDependencies + } else { + expectedCompileClasspathJava8 + extraLegacyDependencies + } + } else { + // with Gradle Module Metadata + // - variant is chosen based on Java version used independent of version suffix + // - reduced runtime classpath is used (w/o annotation libraries) + // - capability conflicts are detected between Google Collections and Listenablefuture + if (name.contains("Java6")) { + if (name.contains("RuntimeClasspath")) { + expectedReducedRuntimeClasspathJava6 + } else if (name.contains("CompileClasspath")) { + expectedCompileClasspathJava6 } else { - // with Gradle Module Metadata - // - variant is chosen based on Java version used independent of version suffix - // - reduced runtime classpath is used (w/o annotation libraries) - // - capability conflicts are detected between Google Collections and Listenablefuture - if (name.contains("Java6")) { - if (name.contains("RuntimeClasspath")) { - expectedReducedRuntimeClasspathJava6 - } else if (name.contains("CompileClasspath")) { - expectedCompileClasspathJava6 - } else { - error("unexpected classpath type: " + name) - } - } else { - if (name.contains("RuntimeClasspath")) { - expectedReducedRuntimeClasspathJava8 - } else if (name.contains("CompileClasspath")) { - expectedCompileClasspathJava8 - } else { - error("unexpected classpath type: " + name) - } - } + error("unexpected classpath type: " + name) } - val guavaVersion = if (name.startsWith("jre")) { - guavaVersionJre - } else { - guavaVersionJre.replace("jre", "android") - } - val javaVersion = if (name.contains("Java6")) { - JavaVersion.VERSION_1_6 - } else { - JavaVersion.VERSION_1_8 - } - val classpathConfiguration = if (name.contains("RuntimeClasspath")) { - configurations["runtimeClasspath"] - } else if (name.contains("CompileClasspath")) { - configurations["compileClasspath"] - } else { - error("unexpected classpath type: " + name) + } else { + if (name.contains("RuntimeClasspath")) { + expectedReducedRuntimeClasspathJava8 + } else if (name.contains("CompileClasspath")) { + expectedCompileClasspathJava8 + } else { + error("unexpected classpath type: " + name) + } + } } + val guavaVersion = if (name.startsWith("jre")) { + guavaVersionJre + } else { + guavaVersionJre.replace("jre", "android") + } + val javaVersion = if (name.contains("Java6")) { + JavaVersion.VERSION_1_6 + } else { + JavaVersion.VERSION_1_8 + } + val classpathConfiguration = if (name.contains("RuntimeClasspath")) { + configurations["runtimeClasspath"] + } else if (name.contains("CompileClasspath")) { + configurations["compileClasspath"] + } else { + error("unexpected classpath type: " + name) + } - repositories { - mavenCentral() - mavenLocal() - } - val java = extensions.getByType() - java.targetCompatibility = javaVersion - java.sourceCompatibility = javaVersion + repositories { + mavenCentral() + mavenLocal() + } + val java = extensions.getByType() + java.targetCompatibility = javaVersion + java.sourceCompatibility = javaVersion - if (!gradle.gradleVersion.startsWith("5.")) { - configurations.all { - resolutionStrategy.capabilitiesResolution { - withCapability("com.google.collections:google-collections") { - candidates.find { - val idField = it.javaClass.getDeclaredMethod("getId") // reflective access to make this compile with Gradle 5 - (idField.invoke(it) as ModuleComponentIdentifier).module == "guava" - }?.apply { - select(this) - } - } - withCapability("com.google.guava:listenablefuture") { - candidates.find { - val idField = it.javaClass.getDeclaredMethod("getId") // reflective access to make this compile with Gradle 5 - (idField.invoke(it) as ModuleComponentIdentifier).module == "guava" - }?.apply { - select(this) - } - } - } + if (!gradle.gradleVersion.startsWith("5.")) { + configurations.all { + resolutionStrategy.capabilitiesResolution { + withCapability("com.google.collections:google-collections") { + candidates.find { + val idField = it.javaClass.getDeclaredMethod("getId") // reflective access to make this compile with Gradle 5 + (idField.invoke(it) as ModuleComponentIdentifier).module == "guava" + }?.apply { + select(this) + } } + withCapability("com.google.guava:listenablefuture") { + candidates.find { + val idField = it.javaClass.getDeclaredMethod("getId") // reflective access to make this compile with Gradle 5 + (idField.invoke(it) as ModuleComponentIdentifier).module == "guava" + }?.apply { + select(this) + } + } + } } + } - dependencies { - "api"("com.google.collections:google-collections:1.0") - "api"("com.google.guava:listenablefuture:1.0") - "api"("com.google.guava:guava:$guavaVersion") - } + dependencies { + "api"("com.google.collections:google-collections:1.0") + "api"("com.google.guava:listenablefuture:1.0") + "api"("com.google.guava:guava:$guavaVersion") + } - tasks.register("testClasspath") { - doLast { - val actualClasspath = classpathConfiguration.files.map { it.name }.toSet() - if (actualClasspath != expectedClasspath) { - throw RuntimeException(""" + tasks.register("testClasspath") { + doLast { + val actualClasspath = classpathConfiguration.files.map { it.name }.toSet() + if (actualClasspath != expectedClasspath) { + throw RuntimeException( + """ Expected: ${expectedClasspath.sorted()} Actual: ${actualClasspath.sorted()} - """.trimIndent()) - } - } + """.trimIndent() + ) + } } + } } From 41dbc9735708326b95a510a7252683a61287ade0 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:26 +0200 Subject: [PATCH 11/31] Fail if any test run fails. --- util/gradle_integration_tests.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/gradle_integration_tests.sh b/util/gradle_integration_tests.sh index ce1bf4a7c456..a48ceb105d49 100755 --- a/util/gradle_integration_tests.sh +++ b/util/gradle_integration_tests.sh @@ -1,5 +1,7 @@ #!/bin/bash +set -eu + mvn clean install -DskipTests mvn clean install -DskipTests -f android From c5e364f4775b2ac50d8c6565a203a261387124a4 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:26 +0200 Subject: [PATCH 12/31] Conservatively keep most deps on the runtime classpath. As of this commit, we'll remove only j2objc-annotations. The other artifacts contain at least some annotations with RUNTIME visibility (IIRC). (Even this change could theoretically affect users who assume that they can read CLASS-retention annotations (of which j2objc-annotations has some) from bytecode and find them in the runtime classpath. But that seems unlikely, especially for j2objc annotations.) We may consider being more aggressive in the future. --- guava/module.json | 24 +++++++++++++++++++++++ integration-tests/gradle/build.gradle.kts | 18 ++++++++--------- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/guava/module.json b/guava/module.json index c9316795e557..613d9af03982 100644 --- a/guava/module.json +++ b/guava/module.json @@ -109,6 +109,18 @@ "version": { "requires": "1.0.1" } + }, + { + "group": "com.google.code.findbugs", + "module": "jsr305" + }, + { + "group": "org.checkerframework", + "module": "${variant.checkerframework}" + }, + { + "group": "com.google.errorprone", + "module": "error_prone_annotations" } ], "files": [ @@ -229,6 +241,18 @@ "version": { "requires": "1.0.1" } + }, + { + "group": "com.google.code.findbugs", + "module": "jsr305" + }, + { + "group": "org.checkerframework", + "module": "${otherVariant.checkerframework}" + }, + { + "group": "com.google.errorprone", + "module": "error_prone_annotations" } ], "files": [ diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index 530b34fa1350..4d3c2641c2f2 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -3,22 +3,22 @@ val guavaVersionJre = "(.*)".toRegex().find(file("../../pom.x val expectedReducedRuntimeClasspathJava6 = setOf( "guava-${guavaVersionJre.replace("jre", "android")}.jar", - "failureaccess-1.0.1.jar" + "failureaccess-1.0.1.jar", + "jsr305-3.0.2.jar", + "checker-compat-qual-2.5.5.jar", + "error_prone_annotations-2.3.4.jar" ) val expectedReducedRuntimeClasspathJava8 = setOf( - "guava-${guavaVersionJre}.jar", - "failureaccess-1.0.1.jar" + "guava-$guavaVersionJre.jar", + "failureaccess-1.0.1.jar", + "jsr305-3.0.2.jar", + "checker-qual-3.5.0.jar", + "error_prone_annotations-2.3.4.jar" ) val expectedCompileClasspathJava6 = expectedReducedRuntimeClasspathJava6 + setOf( - "jsr305-3.0.2.jar", - "checker-compat-qual-2.5.5.jar", - "error_prone_annotations-2.3.4.jar", "j2objc-annotations-1.3.jar" ) val expectedCompileClasspathJava8 = expectedReducedRuntimeClasspathJava8 + setOf( - "jsr305-3.0.2.jar", - "checker-qual-3.5.0.jar", - "error_prone_annotations-2.3.4.jar", "j2objc-annotations-1.3.jar" ) From 0346bc32d58b19b2407b71cbd691bb7a99011838 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:27 +0200 Subject: [PATCH 13/31] One more tiny bit of formatting. --- android/guava/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/guava/pom.xml b/android/guava/pom.xml index dbe5533d44d6..c6f1f38303f3 100644 --- a/android/guava/pom.xml +++ b/android/guava/pom.xml @@ -223,7 +223,7 @@ target/publish - ../../guava + ../../guava module.json From ad2ca91a8b1768c044f48bbf714e908005b6a1ec Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:27 +0200 Subject: [PATCH 14/31] Make Android projects always select a 'android' variant by default This is done by setting 'com.android.build.api.attributes.BuildTypeAttr' to 'jre' in the 'jre' variants. Which indicates that these variants are unsuited for the usual Android build types (release, debug, ...). --- android/pom.xml | 2 ++ guava/module.json | 4 ++++ pom.xml | 2 ++ 3 files changed, 8 insertions(+) diff --git a/android/pom.xml b/android/pom.xml index f92bd16307d1..549274e1dcba 100644 --- a/android/pom.xml +++ b/android/pom.xml @@ -25,9 +25,11 @@ integration 6 + checker-compat-qual HEAD-jre-SNAPSHOT 8 + "com.android.build.api.attributes.BuildTypeAttr": "jre", checker-qual diff --git a/guava/module.json b/guava/module.json index 613d9af03982..2b6ae7640a1c 100644 --- a/guava/module.json +++ b/guava/module.json @@ -18,6 +18,7 @@ { "name": "jdk${variant.jvmVersion}ApiElements", "attributes": { + ${variant.additionalAttribute} "org.gradle.category": "library", "org.gradle.dependency.bundling": "external", "org.gradle.jvm.version": "${variant.jvmVersion}", @@ -86,6 +87,7 @@ { "name": "jdk${variant.jvmVersion}RuntimeElements", "attributes": { + ${variant.additionalAttribute} "org.gradle.category": "library", "org.gradle.dependency.bundling": "external", "org.gradle.jvm.version": "${variant.jvmVersion}", @@ -150,6 +152,7 @@ { "name": "jdk${otherVariant.jvmVersion}ApiElements", "attributes": { + ${otherVariant.additionalAttribute} "org.gradle.category": "library", "org.gradle.dependency.bundling": "external", "org.gradle.jvm.version": "${otherVariant.jvmVersion}", @@ -218,6 +221,7 @@ { "name": "jdk${otherVariant.jvmVersion}RuntimeElements", "attributes": { + ${otherVariant.additionalAttribute} "org.gradle.category": "library", "org.gradle.dependency.bundling": "external", "org.gradle.jvm.version": "${otherVariant.jvmVersion}", diff --git a/pom.xml b/pom.xml index 7e40d1d4313c..468a51306342 100644 --- a/pom.xml +++ b/pom.xml @@ -25,9 +25,11 @@ integration 8 + "com.android.build.api.attributes.BuildTypeAttr": "jre", checker-qual HEAD-android-SNAPSHOT 6 + checker-compat-qual From 536a5a376c71be17b8129a1bdfa628cc5b7f73d2 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:27 +0200 Subject: [PATCH 15/31] Expand integration test to test with Android projects --- integration-tests/gradle/build.gradle.kts | 37 ++++++++++++++------ integration-tests/gradle/settings.gradle.kts | 10 ++++++ 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index 4d3c2641c2f2..f5e920a93554 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -27,8 +27,23 @@ val extraLegacyDependencies = setOf( "google-collections-1.0.jar" ) +buildscript { + repositories { + google() + jcenter() + } + dependencies { + classpath("com.android.tools.build:gradle:3.6.3") + } +} + subprojects { - apply(plugin = "java-library") + if (name.endsWith("Java")) { + apply(plugin = "java-library") + } else { + apply(plugin = "com.android.application") + the().compileSdkVersion(30) + } val expectedClasspath = if (gradle.gradleVersion.startsWith("5.")) { @@ -44,9 +59,10 @@ subprojects { } else { // with Gradle Module Metadata // - variant is chosen based on Java version used independent of version suffix + // (for Android projects, the 'android' variant is always chosen) // - reduced runtime classpath is used (w/o annotation libraries) // - capability conflicts are detected between Google Collections and Listenablefuture - if (name.contains("Java6")) { + if (name.contains("Java6") || name.endsWith("Android")) { if (name.contains("RuntimeClasspath")) { expectedReducedRuntimeClasspathJava6 } else if (name.contains("CompileClasspath")) { @@ -74,19 +90,12 @@ subprojects { } else { JavaVersion.VERSION_1_8 } - val classpathConfiguration = if (name.contains("RuntimeClasspath")) { - configurations["runtimeClasspath"] - } else if (name.contains("CompileClasspath")) { - configurations["compileClasspath"] - } else { - error("unexpected classpath type: " + name) - } repositories { mavenCentral() mavenLocal() } - val java = extensions.getByType() + val java = the() java.targetCompatibility = javaVersion java.sourceCompatibility = javaVersion @@ -121,6 +130,14 @@ subprojects { tasks.register("testClasspath") { doLast { + val classpathConfiguration = if (project.name.contains("RuntimeClasspath")) { + if (project.name.endsWith("Java")) configurations["runtimeClasspath"] else configurations["debugRuntimeClasspath"] + } else if (project.name.contains("CompileClasspath")) { + if (project.name.endsWith("Java")) configurations["compileClasspath"] else configurations["debugCompileClasspath"] + } else { + error("unexpected classpath type: " + project.name) + } + val actualClasspath = classpathConfiguration.files.map { it.name }.toSet() if (actualClasspath != expectedClasspath) { throw RuntimeException( diff --git a/integration-tests/gradle/settings.gradle.kts b/integration-tests/gradle/settings.gradle.kts index 37924562d1c6..abaa6e6db36d 100644 --- a/integration-tests/gradle/settings.gradle.kts +++ b/integration-tests/gradle/settings.gradle.kts @@ -9,3 +9,13 @@ include("jreJava6CompileClasspathJava") include("androidJava6CompileClasspathJava") include("jreJava6RuntimeClasspathJava") include("androidJava6RuntimeClasspathJava") + +include("jreJava8CompileClasspathAndroid") +include("androidJava8CompileClasspathAndroid") +include("jreJava8RuntimeClasspathAndroid") +include("androidJava8RuntimeClasspathAndroid") + +include("jreJava6CompileClasspathAndroid") +include("androidJava6CompileClasspathAndroid") +include("jreJava6RuntimeClasspathAndroid") +include("androidJava6RuntimeClasspathAndroid") From 941ff66cf58e67812652a0bb4c77db3c1fa00a15 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:28 +0200 Subject: [PATCH 16/31] Add test cases for selecting a different variant via constraint This shows that version constraints can be used to: - Select the 'jre' variant in an Android project - Select the 'android' variant in a Java project, even if the jvm target version is 8+ --- integration-tests/gradle/build.gradle.kts | 99 ++++++++++++++++---- integration-tests/gradle/settings.gradle.kts | 8 ++ 2 files changed, 90 insertions(+), 17 deletions(-) diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index f5e920a93554..57c8801f4ab8 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -62,21 +62,29 @@ subprojects { // (for Android projects, the 'android' variant is always chosen) // - reduced runtime classpath is used (w/o annotation libraries) // - capability conflicts are detected between Google Collections and Listenablefuture - if (name.contains("Java6") || name.endsWith("Android")) { - if (name.contains("RuntimeClasspath")) { - expectedReducedRuntimeClasspathJava6 - } else if (name.contains("CompileClasspath")) { - expectedCompileClasspathJava6 - } else { - error("unexpected classpath type: " + name) + if (name.contains("Java6") || (name.endsWith("Android") && !name.contains("Java8Constraint")) ) { + when { + name.contains("RuntimeClasspath") -> { + expectedReducedRuntimeClasspathJava6 + } + name.contains("CompileClasspath") -> { + expectedCompileClasspathJava6 + } + else -> { + error("unexpected classpath type: $name") + } } } else { - if (name.contains("RuntimeClasspath")) { - expectedReducedRuntimeClasspathJava8 - } else if (name.contains("CompileClasspath")) { - expectedCompileClasspathJava8 - } else { - error("unexpected classpath type: " + name) + when { + name.contains("RuntimeClasspath") -> { + expectedReducedRuntimeClasspathJava8 + } + name.contains("CompileClasspath") -> { + expectedCompileClasspathJava8 + } + else -> { + error("unexpected classpath type: $name") + } } } } @@ -85,10 +93,19 @@ subprojects { } else { guavaVersionJre.replace("jre", "android") } - val javaVersion = if (name.contains("Java6")) { - JavaVersion.VERSION_1_6 - } else { - JavaVersion.VERSION_1_8 + val javaVersion = when { + name.contains("Java8Constraint") -> { + JavaVersion.VERSION_1_6 + } + name.contains("Java6Constraint") -> { + JavaVersion.VERSION_1_8 + } + name.contains("Java6") -> { + JavaVersion.VERSION_1_6 + } + else -> { + JavaVersion.VERSION_1_8 + } } repositories { @@ -120,6 +137,54 @@ subprojects { } } } + + if (name.contains("Java6Constraint")) { + dependencies { + constraints { + "api"("com.google.guava:guava") { + attributes { + attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 6) + } + } + } + } + configurations.all { + resolutionStrategy.capabilitiesResolution { + withCapability("com.google.guava:guava") { + candidates.find { + val variantName = it.javaClass.getDeclaredMethod("getVariantName") + (variantName.invoke(it) as String).contains("6") + }?.apply { + select(this) + } + } + } + } + } + + if (name.contains("Java8Constraint")) { + dependencies { + constraints { + "api"("com.google.guava:guava") { + attributes { + attribute(Attribute.of("com.android.build.api.attributes.BuildTypeAttr", String::class.java), "jre") + } + } + } + } + configurations.all { + resolutionStrategy.capabilitiesResolution { + withCapability("com.google.guava:guava") { + candidates.find { + val variantName = it.javaClass.getDeclaredMethod("getVariantName") + (variantName.invoke(it) as String).contains("8") + }?.apply { + select(this) + } + } + } + } + } } dependencies { diff --git a/integration-tests/gradle/settings.gradle.kts b/integration-tests/gradle/settings.gradle.kts index abaa6e6db36d..662c76b89acd 100644 --- a/integration-tests/gradle/settings.gradle.kts +++ b/integration-tests/gradle/settings.gradle.kts @@ -19,3 +19,11 @@ include("jreJava6CompileClasspathAndroid") include("androidJava6CompileClasspathAndroid") include("jreJava6RuntimeClasspathAndroid") include("androidJava6RuntimeClasspathAndroid") + +// Enforce 'android' variant in Java projects via constraint +include("jreJava6ConstraintCompileClasspathJava") +include("androidJava6ConstraintCompileClasspathJava") + +// Enforce 'jre' variant in Android projects via constraint +include("jreJava8ConstraintCompileClasspathAndroid") +include("androidJava8ConstraintCompileClasspathAndroid") From 9eb99964f5a35866e200d243119f3acbbbec9bbf Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:28 +0200 Subject: [PATCH 17/31] Use 'org.gradle.jvm.environment' attribute This attribute is supported by Gradle 7 and AGP 7 See: - https://github.com/gradle/gradle/issues/15430 - https://issuetracker.google.com/issues/179488433 --- android/pom.xml | 4 ++-- guava/module.json | 8 ++++---- integration-tests/gradle/build.gradle.kts | 20 +++++++++++++++---- .../gradle/wrapper/gradle-wrapper.properties | 2 +- pom.xml | 4 ++-- util/gradle_integration_tests.sh | 2 +- 6 files changed, 26 insertions(+), 14 deletions(-) diff --git a/android/pom.xml b/android/pom.xml index 549274e1dcba..796e5d6d6b41 100644 --- a/android/pom.xml +++ b/android/pom.xml @@ -25,11 +25,11 @@ integration 6 - + android checker-compat-qual HEAD-jre-SNAPSHOT 8 - "com.android.build.api.attributes.BuildTypeAttr": "jre", + standard-jvm checker-qual diff --git a/guava/module.json b/guava/module.json index 2b6ae7640a1c..7b75201d533a 100644 --- a/guava/module.json +++ b/guava/module.json @@ -18,10 +18,10 @@ { "name": "jdk${variant.jvmVersion}ApiElements", "attributes": { - ${variant.additionalAttribute} "org.gradle.category": "library", "org.gradle.dependency.bundling": "external", "org.gradle.jvm.version": "${variant.jvmVersion}", + "org.gradle.jvm.environment": "${variant.jvmEnvironment}", "org.gradle.libraryelements": "jar", "org.gradle.usage": "java-api" }, @@ -87,10 +87,10 @@ { "name": "jdk${variant.jvmVersion}RuntimeElements", "attributes": { - ${variant.additionalAttribute} "org.gradle.category": "library", "org.gradle.dependency.bundling": "external", "org.gradle.jvm.version": "${variant.jvmVersion}", + "org.gradle.jvm.environment": "${variant.jvmEnvironment}", "org.gradle.libraryelements": "jar", "org.gradle.usage": "java-runtime" }, @@ -152,10 +152,10 @@ { "name": "jdk${otherVariant.jvmVersion}ApiElements", "attributes": { - ${otherVariant.additionalAttribute} "org.gradle.category": "library", "org.gradle.dependency.bundling": "external", "org.gradle.jvm.version": "${otherVariant.jvmVersion}", + "org.gradle.jvm.environment": "${otherVariant.jvmEnvironment}", "org.gradle.libraryelements": "jar", "org.gradle.usage": "java-api" }, @@ -221,10 +221,10 @@ { "name": "jdk${otherVariant.jvmVersion}RuntimeElements", "attributes": { - ${otherVariant.additionalAttribute} "org.gradle.category": "library", "org.gradle.dependency.bundling": "external", "org.gradle.jvm.version": "${otherVariant.jvmVersion}", + "org.gradle.jvm.environment": "${otherVariant.jvmEnvironment}", "org.gradle.libraryelements": "jar", "org.gradle.usage": "java-runtime" }, diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index 57c8801f4ab8..82b46a566f1f 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -28,12 +28,15 @@ val extraLegacyDependencies = setOf( ) buildscript { + val agpVersion = if (gradle.gradleVersion.startsWith("5.")) "3.6.4" else "7.0.0-alpha08" repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:3.6.3") + classpath("com.android.tools.build:gradle:$agpVersion") { + exclude(group = "org.jetbrains.trove4j") // Might not be available on Maven Central and not needed for this test + } } } @@ -43,6 +46,13 @@ subprojects { } else { apply(plugin = "com.android.application") the().compileSdkVersion(30) + // === TODO Remove this when https://issuetracker.google.com/issues/179488433 is fixed in AGP 7.0.0 + configurations.whenObjectAdded { + if (name in listOf("releaseRuntimeClasspath", "debugRuntimeClasspath", "releaseCompileClasspath", "debugCompileClasspath")) { + attributes.attribute(Attribute.of("org.gradle.jvm.environment", String::class.java), "android") + } + } + // === } val expectedClasspath = @@ -143,7 +153,8 @@ subprojects { constraints { "api"("com.google.guava:guava") { attributes { - attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 6) + // if the Gradle version is 7+, you can use TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE + attribute(Attribute.of("org.gradle.jvm.environment", String::class.java), "android") } } } @@ -167,7 +178,8 @@ subprojects { constraints { "api"("com.google.guava:guava") { attributes { - attribute(Attribute.of("com.android.build.api.attributes.BuildTypeAttr", String::class.java), "jre") + // if the Gradle version is 7+, you can use TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE + attribute(Attribute.of("org.gradle.jvm.environment", String::class.java), "standard-jvm") } } } diff --git a/integration-tests/gradle/gradle/wrapper/gradle-wrapper.properties b/integration-tests/gradle/gradle/wrapper/gradle-wrapper.properties index 94920145f34e..e7e7b6766c30 100644 --- a/integration-tests/gradle/gradle/wrapper/gradle-wrapper.properties +++ b/integration-tests/gradle/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-milestone-2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/pom.xml b/pom.xml index 468a51306342..651ac397e79a 100644 --- a/pom.xml +++ b/pom.xml @@ -25,11 +25,11 @@ integration 8 - "com.android.build.api.attributes.BuildTypeAttr": "jre", + standard-jvm checker-qual HEAD-android-SNAPSHOT 6 - + android checker-compat-qual diff --git a/util/gradle_integration_tests.sh b/util/gradle_integration_tests.sh index a48ceb105d49..76aec53282c3 100755 --- a/util/gradle_integration_tests.sh +++ b/util/gradle_integration_tests.sh @@ -7,5 +7,5 @@ mvn clean install -DskipTests -f android integration-tests/gradle/gradlew -p integration-tests/gradle wrapper --gradle-version=5.6.4 integration-tests/gradle/gradlew -p integration-tests/gradle testClasspath -integration-tests/gradle/gradlew -p integration-tests/gradle wrapper --gradle-version=6.0.1 +integration-tests/gradle/gradlew -p integration-tests/gradle wrapper --gradle-version=7.0-milestone-2 integration-tests/gradle/gradlew -p integration-tests/gradle testClasspath From cf7d4c1042e920a879c9c40bc2c1a0661a372066 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:28 +0200 Subject: [PATCH 18/31] Adjust integration test names to reflect better what they check --- integration-tests/gradle/build.gradle.kts | 16 ++++++------- integration-tests/gradle/settings.gradle.kts | 24 ++++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index 82b46a566f1f..ade0c86705eb 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -72,7 +72,7 @@ subprojects { // (for Android projects, the 'android' variant is always chosen) // - reduced runtime classpath is used (w/o annotation libraries) // - capability conflicts are detected between Google Collections and Listenablefuture - if (name.contains("Java6") || (name.endsWith("Android") && !name.contains("Java8Constraint")) ) { + if (name.contains("Java6") || (name.contains("Android") && !name.contains("JreConstraint")) ) { when { name.contains("RuntimeClasspath") -> { expectedReducedRuntimeClasspathJava6 @@ -98,16 +98,16 @@ subprojects { } } } - val guavaVersion = if (name.startsWith("jre")) { - guavaVersionJre - } else { + val guavaVersion = if (name.startsWith("android")) { guavaVersionJre.replace("jre", "android") + } else { + guavaVersionJre } val javaVersion = when { - name.contains("Java8Constraint") -> { + name.contains("JreConstraint") -> { JavaVersion.VERSION_1_6 } - name.contains("Java6Constraint") -> { + name.contains("AndroidConstraint") -> { JavaVersion.VERSION_1_8 } name.contains("Java6") -> { @@ -148,7 +148,7 @@ subprojects { } } - if (name.contains("Java6Constraint")) { + if (name.contains("AndroidConstraint")) { dependencies { constraints { "api"("com.google.guava:guava") { @@ -173,7 +173,7 @@ subprojects { } } - if (name.contains("Java8Constraint")) { + if (name.contains("JreConstraint")) { dependencies { constraints { "api"("com.google.guava:guava") { diff --git a/integration-tests/gradle/settings.gradle.kts b/integration-tests/gradle/settings.gradle.kts index 662c76b89acd..49e4b1209a0c 100644 --- a/integration-tests/gradle/settings.gradle.kts +++ b/integration-tests/gradle/settings.gradle.kts @@ -1,29 +1,29 @@ rootProject.name = "guava-integration-test" -include("jreJava8CompileClasspathJava") +include("standardJvmJava8CompileClasspathJava") include("androidJava8CompileClasspathJava") -include("jreJava8RuntimeClasspathJava") +include("standardJvmJava8RuntimeClasspathJava") include("androidJava8RuntimeClasspathJava") -include("jreJava6CompileClasspathJava") +include("standardJvmJava6CompileClasspathJava") include("androidJava6CompileClasspathJava") -include("jreJava6RuntimeClasspathJava") +include("standardJvmJava6RuntimeClasspathJava") include("androidJava6RuntimeClasspathJava") -include("jreJava8CompileClasspathAndroid") +include("standardJvmJava8CompileClasspathAndroid") include("androidJava8CompileClasspathAndroid") -include("jreJava8RuntimeClasspathAndroid") +include("standardJvmJava8RuntimeClasspathAndroid") include("androidJava8RuntimeClasspathAndroid") -include("jreJava6CompileClasspathAndroid") +include("standardJvmJava6CompileClasspathAndroid") include("androidJava6CompileClasspathAndroid") -include("jreJava6RuntimeClasspathAndroid") +include("standardJvmJava6RuntimeClasspathAndroid") include("androidJava6RuntimeClasspathAndroid") // Enforce 'android' variant in Java projects via constraint -include("jreJava6ConstraintCompileClasspathJava") -include("androidJava6ConstraintCompileClasspathJava") +include("standardJvmAndroidConstraintCompileClasspathJava") +include("androidAndroidConstraintCompileClasspathJava") // Enforce 'jre' variant in Android projects via constraint -include("jreJava8ConstraintCompileClasspathAndroid") -include("androidJava8ConstraintCompileClasspathAndroid") +include("standardJvmJreConstraintCompileClasspathAndroid") +include("androidJreConstraintCompileClasspathAndroid") From 8c5e9d3bd19b5da39458ec1f769b9a3fc2151c12 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:29 +0200 Subject: [PATCH 19/31] Update expected versions to reflect latest version upgrades --- integration-tests/gradle/build.gradle.kts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index ade0c86705eb..9f522bb28039 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -6,14 +6,14 @@ val expectedReducedRuntimeClasspathJava6 = setOf( "failureaccess-1.0.1.jar", "jsr305-3.0.2.jar", "checker-compat-qual-2.5.5.jar", - "error_prone_annotations-2.3.4.jar" + "error_prone_annotations-2.5.1.jar" ) val expectedReducedRuntimeClasspathJava8 = setOf( "guava-$guavaVersionJre.jar", "failureaccess-1.0.1.jar", "jsr305-3.0.2.jar", - "checker-qual-3.5.0.jar", - "error_prone_annotations-2.3.4.jar" + "checker-qual-3.8.0.jar", + "error_prone_annotations-2.5.1.jar" ) val expectedCompileClasspathJava6 = expectedReducedRuntimeClasspathJava6 + setOf( "j2objc-annotations-1.3.jar" From 8e01521ec3d1d63a9695373313e5833d7e2eaaff Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:29 +0200 Subject: [PATCH 20/31] Add 'Integration Test' step to GitHub Actions CI workflow --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1b7303b9e00..452b08dfd5d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,6 +50,9 @@ jobs: - name: 'Test' shell: bash run: mvn -B -P!standard-with-extra-repos verify -U -Dmaven.javadoc.skip=true -f $ROOT_POM + - name: 'Integration Test' + shell: bash + run: util/gradle_integration_tests.sh - name: 'Print Surefire reports' # Note: Normally a step won't run if the job has failed, but this causes it to if: ${{ failure() }} From e07bcf1af5875a0908967f7a0d0dacf34f010da3 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:29 +0200 Subject: [PATCH 21/31] Upgrade test expectations: error prone is now on 2.6.0 --- integration-tests/gradle/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index 9f522bb28039..183dbb41e0da 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -6,14 +6,14 @@ val expectedReducedRuntimeClasspathJava6 = setOf( "failureaccess-1.0.1.jar", "jsr305-3.0.2.jar", "checker-compat-qual-2.5.5.jar", - "error_prone_annotations-2.5.1.jar" + "error_prone_annotations-2.6.0.jar" ) val expectedReducedRuntimeClasspathJava8 = setOf( "guava-$guavaVersionJre.jar", "failureaccess-1.0.1.jar", "jsr305-3.0.2.jar", "checker-qual-3.8.0.jar", - "error_prone_annotations-2.5.1.jar" + "error_prone_annotations-2.6.0.jar" ) val expectedCompileClasspathJava6 = expectedReducedRuntimeClasspathJava6 + setOf( "j2objc-annotations-1.3.jar" From 4ea61e9458c038e7f48b6f040a981ee829a1e852 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:30 +0200 Subject: [PATCH 22/31] Upgrade test expectations: checker qual is now on 3.12.0 --- integration-tests/gradle/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index 183dbb41e0da..e5cca5571ead 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -12,7 +12,7 @@ val expectedReducedRuntimeClasspathJava8 = setOf( "guava-$guavaVersionJre.jar", "failureaccess-1.0.1.jar", "jsr305-3.0.2.jar", - "checker-qual-3.8.0.jar", + "checker-qual-3.12.0.jar", "error_prone_annotations-2.6.0.jar" ) val expectedCompileClasspathJava6 = expectedReducedRuntimeClasspathJava6 + setOf( From 7300d6884712631dac05d0967fdf8e4b44cf93ed Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:30 +0200 Subject: [PATCH 23/31] Checker-qual is now also a dependency of the Android variant --- android/pom.xml | 3 ++- guava/module.json | 12 ++++++++---- integration-tests/gradle/build.gradle.kts | 14 +++++++++++--- pom.xml | 4 ++-- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/android/pom.xml b/android/pom.xml index 796e5d6d6b41..650406872426 100644 --- a/android/pom.xml +++ b/android/pom.xml @@ -26,11 +26,12 @@ integration 6 android - checker-compat-qual + { "group": "org.checkerframework", "module": "checker-compat-qual" }, HEAD-jre-SNAPSHOT 8 standard-jvm checker-qual + GitHub Issues diff --git a/guava/module.json b/guava/module.json index 7b75201d533a..6ea14b19d5be 100644 --- a/guava/module.json +++ b/guava/module.json @@ -49,8 +49,9 @@ }, { "group": "org.checkerframework", - "module": "${variant.checkerframework}" + "module": "checker-qual" }, + ${variant.checkerCompatQual} { "group": "com.google.errorprone", "module": "error_prone_annotations" @@ -118,8 +119,9 @@ }, { "group": "org.checkerframework", - "module": "${variant.checkerframework}" + "module": "checker-qual" }, + ${variant.checkerCompatQual} { "group": "com.google.errorprone", "module": "error_prone_annotations" @@ -183,8 +185,9 @@ }, { "group": "org.checkerframework", - "module": "${otherVariant.checkerframework}" + "module": "checker-qual" }, + ${otherVariant.checkerCompatQual} { "group": "com.google.errorprone", "module": "error_prone_annotations" @@ -252,8 +255,9 @@ }, { "group": "org.checkerframework", - "module": "${otherVariant.checkerframework}" + "module": "checker-qual" }, + ${otherVariant.checkerCompatQual} { "group": "com.google.errorprone", "module": "error_prone_annotations" diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index e5cca5571ead..e0f0a1e53867 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -1,3 +1,5 @@ +val runningGradle5 = gradle.gradleVersion.startsWith("5.") + val guavaVersionJre = "(.*)".toRegex().find(file("../../pom.xml").readText()) ?.groups?.get(1)?.value ?: error("version not found in pom") @@ -6,6 +8,7 @@ val expectedReducedRuntimeClasspathJava6 = setOf( "failureaccess-1.0.1.jar", "jsr305-3.0.2.jar", "checker-compat-qual-2.5.5.jar", + "checker-qual-3.12.0.jar", "error_prone_annotations-2.6.0.jar" ) val expectedReducedRuntimeClasspathJava8 = setOf( @@ -55,8 +58,8 @@ subprojects { // === } - val expectedClasspath = - if (gradle.gradleVersion.startsWith("5.")) { + var expectedClasspath = + if (runningGradle5) { // without Gradle Module Metadata (only the POM is used) // - variant decision is made based on version suffix (android/jre) and not on actual Java version // - runtime classpath equals the compile classpath @@ -126,7 +129,7 @@ subprojects { java.targetCompatibility = javaVersion java.sourceCompatibility = javaVersion - if (!gradle.gradleVersion.startsWith("5.")) { + if (!runningGradle5) { configurations.all { resolutionStrategy.capabilitiesResolution { withCapability("com.google.collections:google-collections") { @@ -214,6 +217,11 @@ subprojects { } else { error("unexpected classpath type: " + project.name) } + if (javaVersion == JavaVersion.VERSION_1_6 && !runningGradle5) { + // Hack: There is no Java 6/7 compatible variant of 'checker-qual' so we exclude it + classpathConfiguration.exclude(group = "org.checkerframework", module = "checker-qual") + expectedClasspath = expectedClasspath - "checker-qual-3.12.0.jar" + } val actualClasspath = classpathConfiguration.files.map { it.name }.toSet() if (actualClasspath != expectedClasspath) { diff --git a/pom.xml b/pom.xml index 651ac397e79a..7fb2f81b5e4e 100644 --- a/pom.xml +++ b/pom.xml @@ -26,11 +26,11 @@ integration 8 standard-jvm - checker-qual + HEAD-android-SNAPSHOT 6 android - checker-compat-qual + { "group": "org.checkerframework", "module": "checker-compat-qual" }, GitHub Issues From 2180a0413b46b64c2b3989131c2bc53bc4879d90 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:30 +0200 Subject: [PATCH 24/31] Use Gradle 7.0 final --- .../gradle/gradle/wrapper/gradle-wrapper.properties | 2 +- util/gradle_integration_tests.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/integration-tests/gradle/gradle/wrapper/gradle-wrapper.properties b/integration-tests/gradle/gradle/wrapper/gradle-wrapper.properties index e7e7b6766c30..f371643eed77 100644 --- a/integration-tests/gradle/gradle/wrapper/gradle-wrapper.properties +++ b/integration-tests/gradle/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-milestone-2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/util/gradle_integration_tests.sh b/util/gradle_integration_tests.sh index 76aec53282c3..eadcdb215c1b 100755 --- a/util/gradle_integration_tests.sh +++ b/util/gradle_integration_tests.sh @@ -7,5 +7,5 @@ mvn clean install -DskipTests -f android integration-tests/gradle/gradlew -p integration-tests/gradle wrapper --gradle-version=5.6.4 integration-tests/gradle/gradlew -p integration-tests/gradle testClasspath -integration-tests/gradle/gradlew -p integration-tests/gradle wrapper --gradle-version=7.0-milestone-2 +integration-tests/gradle/gradlew -p integration-tests/gradle wrapper --gradle-version=7.0 integration-tests/gradle/gradlew -p integration-tests/gradle testClasspath From 4758c9c912d831ae1d620b32dd7ee0e83a41f284 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:30 +0200 Subject: [PATCH 25/31] Use 7.0.0-alpha15 of Android Gradle Plugin --- integration-tests/gradle/build.gradle.kts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index e0f0a1e53867..782cbb51757b 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -31,7 +31,7 @@ val extraLegacyDependencies = setOf( ) buildscript { - val agpVersion = if (gradle.gradleVersion.startsWith("5.")) "3.6.4" else "7.0.0-alpha08" + val agpVersion = if (gradle.gradleVersion.startsWith("5.")) "3.6.4" else "7.0.0-alpha15" repositories { google() mavenCentral() @@ -49,13 +49,6 @@ subprojects { } else { apply(plugin = "com.android.application") the().compileSdkVersion(30) - // === TODO Remove this when https://issuetracker.google.com/issues/179488433 is fixed in AGP 7.0.0 - configurations.whenObjectAdded { - if (name in listOf("releaseRuntimeClasspath", "debugRuntimeClasspath", "releaseCompileClasspath", "debugCompileClasspath")) { - attributes.attribute(Attribute.of("org.gradle.jvm.environment", String::class.java), "android") - } - } - // === } var expectedClasspath = From eae386c6189302492265d1d4d60eb2565cf4ec83 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:31 +0200 Subject: [PATCH 26/31] Do not run Gradle integration test on Java 8 The Android Gradle Plugin 7.0.0 requires Java 11 --- .github/workflows/ci.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 452b08dfd5d8..2338191a3a55 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,14 +50,15 @@ jobs: - name: 'Test' shell: bash run: mvn -B -P!standard-with-extra-repos verify -U -Dmaven.javadoc.skip=true -f $ROOT_POM - - name: 'Integration Test' - shell: bash - run: util/gradle_integration_tests.sh - name: 'Print Surefire reports' # Note: Normally a step won't run if the job has failed, but this causes it to if: ${{ failure() }} shell: bash run: ./util/print_surefire_reports.sh + - name: 'Integration Test' + if: matrix.java == 11 + shell: bash + run: util/gradle_integration_tests.sh publish_snapshot: name: 'Publish snapshot' From 4d05df4abead4a02b566a09f9023a3456a9c073e Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:31 +0200 Subject: [PATCH 27/31] All variants are now targeting Java 8 or higher The support for Java versions < 8 in the Android variants has been removed. Jre and Android can now be distinguished by the 'org.gradle.jvm.environment' attribute only. The dependencies are also more similar now which allows some simplification. In particular the removal of the 'checker-compat-qual' dependency in the Android variants. --- android/pom.xml | 5 -- guava/module.json | 20 +++---- integration-tests/gradle/build.gradle.kts | 53 ++++++------------- .../gradle/wrapper/gradle-wrapper.properties | 2 +- integration-tests/gradle/settings.gradle.kts | 26 +++------ pom.xml | 4 -- util/gradle_integration_tests.sh | 2 +- 7 files changed, 35 insertions(+), 77 deletions(-) diff --git a/android/pom.xml b/android/pom.xml index 650406872426..2e75a880b3c5 100644 --- a/android/pom.xml +++ b/android/pom.xml @@ -24,14 +24,9 @@ UTF-8 integration - 6 android - { "group": "org.checkerframework", "module": "checker-compat-qual" }, HEAD-jre-SNAPSHOT - 8 standard-jvm - checker-qual - GitHub Issues diff --git a/guava/module.json b/guava/module.json index 6ea14b19d5be..bf3ee35ba1bf 100644 --- a/guava/module.json +++ b/guava/module.json @@ -16,11 +16,11 @@ }, "variants": [ { - "name": "jdk${variant.jvmVersion}ApiElements", + "name": "${variant.jvmEnvironment}ApiElements", "attributes": { "org.gradle.category": "library", "org.gradle.dependency.bundling": "external", - "org.gradle.jvm.version": "${variant.jvmVersion}", + "org.gradle.jvm.version": "8", "org.gradle.jvm.environment": "${variant.jvmEnvironment}", "org.gradle.libraryelements": "jar", "org.gradle.usage": "java-api" @@ -51,7 +51,6 @@ "group": "org.checkerframework", "module": "checker-qual" }, - ${variant.checkerCompatQual} { "group": "com.google.errorprone", "module": "error_prone_annotations" @@ -86,11 +85,11 @@ ] }, { - "name": "jdk${variant.jvmVersion}RuntimeElements", + "name": "${variant.jvmEnvironment}RuntimeElements", "attributes": { "org.gradle.category": "library", "org.gradle.dependency.bundling": "external", - "org.gradle.jvm.version": "${variant.jvmVersion}", + "org.gradle.jvm.version": "8", "org.gradle.jvm.environment": "${variant.jvmEnvironment}", "org.gradle.libraryelements": "jar", "org.gradle.usage": "java-runtime" @@ -121,7 +120,6 @@ "group": "org.checkerframework", "module": "checker-qual" }, - ${variant.checkerCompatQual} { "group": "com.google.errorprone", "module": "error_prone_annotations" @@ -152,11 +150,11 @@ ] }, { - "name": "jdk${otherVariant.jvmVersion}ApiElements", + "name": "${otherVariant.jvmEnvironment}ApiElements", "attributes": { "org.gradle.category": "library", "org.gradle.dependency.bundling": "external", - "org.gradle.jvm.version": "${otherVariant.jvmVersion}", + "org.gradle.jvm.version": "8", "org.gradle.jvm.environment": "${otherVariant.jvmEnvironment}", "org.gradle.libraryelements": "jar", "org.gradle.usage": "java-api" @@ -187,7 +185,6 @@ "group": "org.checkerframework", "module": "checker-qual" }, - ${otherVariant.checkerCompatQual} { "group": "com.google.errorprone", "module": "error_prone_annotations" @@ -222,11 +219,11 @@ ] }, { - "name": "jdk${otherVariant.jvmVersion}RuntimeElements", + "name": "${otherVariant.jvmEnvironment}RuntimeElements", "attributes": { "org.gradle.category": "library", "org.gradle.dependency.bundling": "external", - "org.gradle.jvm.version": "${otherVariant.jvmVersion}", + "org.gradle.jvm.version": "8", "org.gradle.jvm.environment": "${otherVariant.jvmEnvironment}", "org.gradle.libraryelements": "jar", "org.gradle.usage": "java-runtime" @@ -257,7 +254,6 @@ "group": "org.checkerframework", "module": "checker-qual" }, - ${otherVariant.checkerCompatQual} { "group": "com.google.errorprone", "module": "error_prone_annotations" diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index 782cbb51757b..b9597afc08e1 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -3,25 +3,24 @@ val runningGradle5 = gradle.gradleVersion.startsWith("5.") val guavaVersionJre = "(.*)".toRegex().find(file("../../pom.xml").readText()) ?.groups?.get(1)?.value ?: error("version not found in pom") -val expectedReducedRuntimeClasspathJava6 = setOf( +val expectedReducedRuntimeClasspathAndroidVersion= setOf( "guava-${guavaVersionJre.replace("jre", "android")}.jar", "failureaccess-1.0.1.jar", "jsr305-3.0.2.jar", - "checker-compat-qual-2.5.5.jar", "checker-qual-3.12.0.jar", - "error_prone_annotations-2.6.0.jar" + "error_prone_annotations-2.7.1.jar" ) -val expectedReducedRuntimeClasspathJava8 = setOf( +val expectedReducedRuntimeClasspathJreVersion = setOf( "guava-$guavaVersionJre.jar", "failureaccess-1.0.1.jar", "jsr305-3.0.2.jar", "checker-qual-3.12.0.jar", - "error_prone_annotations-2.6.0.jar" + "error_prone_annotations-2.7.1.jar" ) -val expectedCompileClasspathJava6 = expectedReducedRuntimeClasspathJava6 + setOf( +val expectedCompileClasspathAndroidVersion = expectedReducedRuntimeClasspathAndroidVersion + setOf( "j2objc-annotations-1.3.jar" ) -val expectedCompileClasspathJava8 = expectedReducedRuntimeClasspathJava8 + setOf( +val expectedCompileClasspathJreVersion = expectedReducedRuntimeClasspathJreVersion + setOf( "j2objc-annotations-1.3.jar" ) @@ -31,7 +30,7 @@ val extraLegacyDependencies = setOf( ) buildscript { - val agpVersion = if (gradle.gradleVersion.startsWith("5.")) "3.6.4" else "7.0.0-alpha15" + val agpVersion = if (gradle.gradleVersion.startsWith("5.")) "3.6.4" else "7.0.4" repositories { google() mavenCentral() @@ -58,9 +57,9 @@ subprojects { // - runtime classpath equals the compile classpath // - dependency conflict with Google Collections is not detected and '9999.0' hack is present if (name.startsWith("android")) { - expectedCompileClasspathJava6 + extraLegacyDependencies + expectedCompileClasspathAndroidVersion + extraLegacyDependencies } else { - expectedCompileClasspathJava8 + extraLegacyDependencies + expectedCompileClasspathJreVersion + extraLegacyDependencies } } else { // with Gradle Module Metadata @@ -68,13 +67,13 @@ subprojects { // (for Android projects, the 'android' variant is always chosen) // - reduced runtime classpath is used (w/o annotation libraries) // - capability conflicts are detected between Google Collections and Listenablefuture - if (name.contains("Java6") || (name.contains("Android") && !name.contains("JreConstraint")) ) { + if (name.contains("Android") && !name.contains("JreConstraint") ) { when { name.contains("RuntimeClasspath") -> { - expectedReducedRuntimeClasspathJava6 + expectedReducedRuntimeClasspathAndroidVersion } name.contains("CompileClasspath") -> { - expectedCompileClasspathJava6 + expectedCompileClasspathAndroidVersion } else -> { error("unexpected classpath type: $name") @@ -83,10 +82,10 @@ subprojects { } else { when { name.contains("RuntimeClasspath") -> { - expectedReducedRuntimeClasspathJava8 + expectedReducedRuntimeClasspathJreVersion } name.contains("CompileClasspath") -> { - expectedCompileClasspathJava8 + expectedCompileClasspathJreVersion } else -> { error("unexpected classpath type: $name") @@ -99,20 +98,7 @@ subprojects { } else { guavaVersionJre } - val javaVersion = when { - name.contains("JreConstraint") -> { - JavaVersion.VERSION_1_6 - } - name.contains("AndroidConstraint") -> { - JavaVersion.VERSION_1_8 - } - name.contains("Java6") -> { - JavaVersion.VERSION_1_6 - } - else -> { - JavaVersion.VERSION_1_8 - } - } + val javaVersion = JavaVersion.VERSION_1_8 repositories { mavenCentral() @@ -160,7 +146,7 @@ subprojects { withCapability("com.google.guava:guava") { candidates.find { val variantName = it.javaClass.getDeclaredMethod("getVariantName") - (variantName.invoke(it) as String).contains("6") + (variantName.invoke(it) as String).contains("android") }?.apply { select(this) } @@ -185,7 +171,7 @@ subprojects { withCapability("com.google.guava:guava") { candidates.find { val variantName = it.javaClass.getDeclaredMethod("getVariantName") - (variantName.invoke(it) as String).contains("8") + (variantName.invoke(it) as String).contains("standard-jvm") }?.apply { select(this) } @@ -210,11 +196,6 @@ subprojects { } else { error("unexpected classpath type: " + project.name) } - if (javaVersion == JavaVersion.VERSION_1_6 && !runningGradle5) { - // Hack: There is no Java 6/7 compatible variant of 'checker-qual' so we exclude it - classpathConfiguration.exclude(group = "org.checkerframework", module = "checker-qual") - expectedClasspath = expectedClasspath - "checker-qual-3.12.0.jar" - } val actualClasspath = classpathConfiguration.files.map { it.name }.toSet() if (actualClasspath != expectedClasspath) { diff --git a/integration-tests/gradle/gradle/wrapper/gradle-wrapper.properties b/integration-tests/gradle/gradle/wrapper/gradle-wrapper.properties index f371643eed77..0f80bbf516ce 100644 --- a/integration-tests/gradle/gradle/wrapper/gradle-wrapper.properties +++ b/integration-tests/gradle/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/integration-tests/gradle/settings.gradle.kts b/integration-tests/gradle/settings.gradle.kts index 49e4b1209a0c..7d8eafbcf096 100644 --- a/integration-tests/gradle/settings.gradle.kts +++ b/integration-tests/gradle/settings.gradle.kts @@ -1,24 +1,14 @@ rootProject.name = "guava-integration-test" -include("standardJvmJava8CompileClasspathJava") -include("androidJava8CompileClasspathJava") -include("standardJvmJava8RuntimeClasspathJava") -include("androidJava8RuntimeClasspathJava") +include("standardJvmCompileClasspathJava") +include("androidCompileClasspathJava") +include("standardJvmRuntimeClasspathJava") +include("androidRuntimeClasspathJava") -include("standardJvmJava6CompileClasspathJava") -include("androidJava6CompileClasspathJava") -include("standardJvmJava6RuntimeClasspathJava") -include("androidJava6RuntimeClasspathJava") - -include("standardJvmJava8CompileClasspathAndroid") -include("androidJava8CompileClasspathAndroid") -include("standardJvmJava8RuntimeClasspathAndroid") -include("androidJava8RuntimeClasspathAndroid") - -include("standardJvmJava6CompileClasspathAndroid") -include("androidJava6CompileClasspathAndroid") -include("standardJvmJava6RuntimeClasspathAndroid") -include("androidJava6RuntimeClasspathAndroid") +include("standardJvmCompileClasspathAndroid") +include("androidCompileClasspathAndroid") +include("standardJvmRuntimeClasspathAndroid") +include("androidRuntimeClasspathAndroid") // Enforce 'android' variant in Java projects via constraint include("standardJvmAndroidConstraintCompileClasspathJava") diff --git a/pom.xml b/pom.xml index 7fb2f81b5e4e..15fbd8b49ef6 100644 --- a/pom.xml +++ b/pom.xml @@ -24,13 +24,9 @@ UTF-8 integration - 8 standard-jvm - HEAD-android-SNAPSHOT - 6 android - { "group": "org.checkerframework", "module": "checker-compat-qual" }, GitHub Issues diff --git a/util/gradle_integration_tests.sh b/util/gradle_integration_tests.sh index eadcdb215c1b..bf0dc8322db6 100755 --- a/util/gradle_integration_tests.sh +++ b/util/gradle_integration_tests.sh @@ -7,5 +7,5 @@ mvn clean install -DskipTests -f android integration-tests/gradle/gradlew -p integration-tests/gradle wrapper --gradle-version=5.6.4 integration-tests/gradle/gradlew -p integration-tests/gradle testClasspath -integration-tests/gradle/gradlew -p integration-tests/gradle wrapper --gradle-version=7.0 +integration-tests/gradle/gradlew -p integration-tests/gradle wrapper --gradle-version=7.0.2 integration-tests/gradle/gradlew -p integration-tests/gradle testClasspath From 0d00efacad6cdb50b7ed4f0aa20f2271af202940 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Wed, 28 Jun 2023 08:43:31 +0200 Subject: [PATCH 28/31] Adjust expectations to versions upgrades in Guava 32 --- integration-tests/gradle/build.gradle.kts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index b9597afc08e1..a35d2c063290 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -7,21 +7,21 @@ val expectedReducedRuntimeClasspathAndroidVersion= setOf( "guava-${guavaVersionJre.replace("jre", "android")}.jar", "failureaccess-1.0.1.jar", "jsr305-3.0.2.jar", - "checker-qual-3.12.0.jar", - "error_prone_annotations-2.7.1.jar" + "checker-qual-3.33.0.jar", + "error_prone_annotations-2.18.0.jar" ) val expectedReducedRuntimeClasspathJreVersion = setOf( "guava-$guavaVersionJre.jar", "failureaccess-1.0.1.jar", "jsr305-3.0.2.jar", - "checker-qual-3.12.0.jar", - "error_prone_annotations-2.7.1.jar" + "checker-qual-3.33.0.jar", + "error_prone_annotations-2.18.0.jar" ) val expectedCompileClasspathAndroidVersion = expectedReducedRuntimeClasspathAndroidVersion + setOf( - "j2objc-annotations-1.3.jar" + "j2objc-annotations-2.8.jar" ) val expectedCompileClasspathJreVersion = expectedReducedRuntimeClasspathJreVersion + setOf( - "j2objc-annotations-1.3.jar" + "j2objc-annotations-2.8.jar" ) val extraLegacyDependencies = setOf( From c14d55aacb39e6d4a9f57161a65366f839d0015f Mon Sep 17 00:00:00 2001 From: Chris Povirk Date: Tue, 27 Jun 2023 16:59:06 -0400 Subject: [PATCH 29/31] Bump plugin versions. --- android/pom.xml | 4 ++-- pom.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/pom.xml b/android/pom.xml index 2e75a880b3c5..ed5ac3b4b493 100644 --- a/android/pom.xml +++ b/android/pom.xml @@ -268,12 +268,12 @@ maven-resources-plugin - 3.1.0 + 3.3.1 org.codehaus.mojo build-helper-maven-plugin - 3.0.0 + 3.4.0 diff --git a/pom.xml b/pom.xml index 15fbd8b49ef6..d4056dbe2f95 100644 --- a/pom.xml +++ b/pom.xml @@ -262,12 +262,12 @@ maven-resources-plugin - 3.1.0 + 3.3.1 org.codehaus.mojo build-helper-maven-plugin - 3.0.0 + 3.4.0 From 5f54f4d085ed93d18de819a940e91538e9f9f6f6 Mon Sep 17 00:00:00 2001 From: Chris Povirk Date: Tue, 27 Jun 2023 17:01:47 -0400 Subject: [PATCH 30/31] Commit (i.e., remove backups) only after all pom edits. --- util/set_version.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/util/set_version.sh b/util/set_version.sh index fd94d66399c0..6f9abb524bbb 100755 --- a/util/set_version.sh +++ b/util/set_version.sh @@ -13,10 +13,12 @@ if [[ $version == *"SNAPSHOT"* ]]; then status="integration" fi -mvn versions:set versions:commit -DnewVersion="${version}-jre" -mvn versions:set versions:commit -DnewVersion="${version}-android" -f android +mvn versions:set -DnewVersion="${version}-jre" +mvn versions:set -DnewVersion="${version}-android" -f android mvn versions:set-property -Dproperty=otherVariant.version -DnewVersion="${version}-android" mvn versions:set-property -Dproperty=otherVariant.version -DnewVersion="${version}-jre" -f android mvn versions:set-property -Dproperty=module.status -DnewVersion="${status}" mvn versions:set-property -Dproperty=module.status -DnewVersion="${status}" -f android +mvn versions:commit +mvn versions:commit -f android git commit -am "Set version numbers to ${version}" From 985d143a26ace6b224f6f36937f6463659014ec5 Mon Sep 17 00:00:00 2001 From: Chris Povirk Date: Tue, 27 Jun 2023 17:19:39 -0400 Subject: [PATCH 31/31] Update comments now that we are only about JVM/Android, not Java version. --- integration-tests/gradle/build.gradle.kts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts index a35d2c063290..4def9dd56f9d 100644 --- a/integration-tests/gradle/build.gradle.kts +++ b/integration-tests/gradle/build.gradle.kts @@ -53,7 +53,7 @@ subprojects { var expectedClasspath = if (runningGradle5) { // without Gradle Module Metadata (only the POM is used) - // - variant decision is made based on version suffix (android/jre) and not on actual Java version + // - variant decision is made based on version suffix (android/jre) and not on the actual environment // - runtime classpath equals the compile classpath // - dependency conflict with Google Collections is not detected and '9999.0' hack is present if (name.startsWith("android")) { @@ -63,10 +63,9 @@ subprojects { } } else { // with Gradle Module Metadata - // - variant is chosen based on Java version used independent of version suffix - // (for Android projects, the 'android' variant is always chosen) + // - variant is chosen based on the actual environment, independent of version suffix // - reduced runtime classpath is used (w/o annotation libraries) - // - capability conflicts are detected between Google Collections and Listenablefuture + // - capability conflicts are detected between Google Collections and listenablefuture if (name.contains("Android") && !name.contains("JreConstraint") ) { when { name.contains("RuntimeClasspath") -> {