Upgrade to Gradle 9.3.1

based on https://r8-review.git.corp.google.com/c/r8/+/119260

Change-Id: I5e9821368a2e0904b1c3cf7944c248c409affd88
diff --git a/build.gradle.kts b/build.gradle.kts
index 95e9072..11ee977 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -4,7 +4,7 @@
 
 plugins {
   // Kotlin version is fixed by create_local_maven_dependencies.py
-  id("org.jetbrains.kotlin.jvm") version "2.0.21" apply false
+  id("org.jetbrains.kotlin.jvm") version "2.2.21" apply false
   id("dependencies-plugin")
 }
 
diff --git a/d8_r8/dist/build.gradle.kts b/d8_r8/dist/build.gradle.kts
index d68bfb8..b8d34d5 100644
--- a/d8_r8/dist/build.gradle.kts
+++ b/d8_r8/dist/build.gradle.kts
@@ -148,18 +148,6 @@
   sharedTestDepsScope(project(":shared", "sharedTestDepsFiles"))
 }
 
-fun mainJarDependencies(): FileCollection {
-  return project.files(
-    Callable {
-      r8Deps.filter({
-        "$it".contains("third_party") &&
-          "$it".contains("dependencies") &&
-          !"$it".contains("errorprone")
-      })
-    }
-  )
-}
-
 val depsJarFilesScope by configurations.dependencyScope("depsJarFilesScope")
 
 val depsJarFiles by configurations.consumable("depsJarFiles") { extendsFrom(depsJarFilesScope) }
@@ -218,6 +206,22 @@
 tasks {
   withType<Exec> { doFirst { println("Executing command: ${commandLine.joinToString(" ")}") } }
 
+  val filterR8Deps by
+    registering(Copy::class) {
+      from(r8Deps)
+      into(layout.buildDirectory.dir("filtered-deps"))
+      eachFile {
+        val path = file.absolutePath
+        if (
+          !(path.contains("third_party") &&
+            path.contains("dependencies") &&
+            !path.contains("errorprone"))
+        ) {
+          exclude()
+        }
+      }
+    }
+
   withType<SpdxSbomTask> {
     taskExtension.set(
       object : DefaultSpdxSbomTaskExtension() {
@@ -263,14 +267,14 @@
   val consolidatedLicense by registering {
     dependsOn(sharedDepsConfig)
     dependsOn(sharedTestDepsConfig)
+    dependsOn(filterR8Deps)
     val root = getRoot()
     val r8License = root.resolve("LICENSE")
     val libraryLicense = root.resolve("LIBRARY-LICENSE")
     val libraryLicenseFiles = fileTree(root.resolve("library-licensing"))
     inputs.files(
       Callable {
-        listOf(r8License, libraryLicense, libraryLicenseFiles) +
-          mainJarDependencies().map(::zipTree)
+        listOf(r8License, libraryLicense, libraryLicenseFiles) + files(filterR8Deps).map(::zipTree)
       }
     )
 
@@ -333,20 +337,25 @@
     exclude("androidx/annotation/keep/**")
   }
 
+  val configsToMerge =
+    listOf(
+      assistantJarConfig,
+      blastRadiusWithoutProtoJarConfig,
+      keepAnnoJarConfig,
+      libanalyzerJarConfig,
+      mainJarConfig,
+      resourceShrinkerJarConfig,
+    )
+
   val swissArmyKnifeJarFiles =
-    objects.fileCollection().apply {
-      from(assistantJarConfig)
-      from(blastRadiusWithoutProtoJarConfig)
-      from(keepAnnoJarConfig)
-      from(libanalyzerJarConfig)
-      from(mainJarConfig)
-      from(resourceShrinkerJarConfig)
-    }
+    objects.fileCollection().apply { configsToMerge.forEach { from(it) } }
 
   val swissArmyKnife by
     registering(Jar::class) {
-      dependsOn(swissArmyKnifeJarFiles)
-      from(swissArmyKnifeJarFiles.map { zipTree(it).matching(swissArmyKnifeExcludeRules) })
+      configsToMerge.forEach { config ->
+        dependsOn(config)
+        from(config.elements.map { it.map { zipTree(it).matching(swissArmyKnifeExcludeRules) } })
+      }
       from(getRoot().resolve("LICENSE"))
       entryCompression = ZipEntryCompression.STORED
       manifest { attributes["Main-Class"] = "com.android.tools.r8.SwissArmyKnife" }
@@ -373,7 +382,7 @@
     }
 
   dependencies {
-    add(depsJarFilesScope.name, mainJarDependencies())
+    add(depsJarFilesScope.name, fileTree(filterR8Deps.map { it.destinationDir }))
     add(depsJarFilesScope.name, project(":resourceshrinker", "resourceshrinkerDepsJar"))
     add(depsJarFilesScope.name, files(threadingModuleBlockingJar))
     add(depsJarFilesScope.name, files(threadingModuleSingleThreadedJar))
@@ -409,8 +418,8 @@
   val protoJar by
     registering(Zip::class) {
       dependsOn(blastRadiusProtoJarConfig, libanalyzerProtoJarConfig)
-      from(blastRadiusProtoJarConfig.map(::zipTree))
-      from(libanalyzerProtoJarConfig.map(::zipTree))
+      from(blastRadiusProtoJarConfig.elements.map { it.map { zipTree(it) } })
+      from(libanalyzerProtoJarConfig.elements.map { it.map { zipTree(it) } })
       exclude("META-INF/MANIFEST.MF")
       archiveFileName.set("proto.jar")
       destinationDirectory.set(getRoot().resolveAll("build", "libs"))
diff --git a/d8_r8/keepanno/build.gradle.kts b/d8_r8/keepanno/build.gradle.kts
index c07a687..e10a8e8 100644
--- a/d8_r8/keepanno/build.gradle.kts
+++ b/d8_r8/keepanno/build.gradle.kts
@@ -11,7 +11,7 @@
 
 plugins {
   // Kotlin version is fixed by create_local_maven_dependencies.py
-  id("org.jetbrains.kotlin.jvm") version "2.0.21"
+  id("org.jetbrains.kotlin.jvm")
   id("dependencies-plugin")
 }
 
diff --git a/d8_r8/resourceshrinker/build.gradle.kts b/d8_r8/resourceshrinker/build.gradle.kts
index 093d7ef..1432193 100644
--- a/d8_r8/resourceshrinker/build.gradle.kts
+++ b/d8_r8/resourceshrinker/build.gradle.kts
@@ -9,7 +9,7 @@
 
 plugins {
   // Kotlin version is fixed by create_local_maven_dependencies.py
-  id("org.jetbrains.kotlin.jvm") version "2.0.21"
+  id("org.jetbrains.kotlin.jvm")
   id("dependencies-plugin")
 }
 
diff --git a/third_party/dependencies.tar.gz.sha1 b/third_party/dependencies.tar.gz.sha1
index 5df05cd..1c58ede 100644
--- a/third_party/dependencies.tar.gz.sha1
+++ b/third_party/dependencies.tar.gz.sha1
@@ -1 +1 @@
-b67f5f5fba660728b5da14d4b9ee806e3a6b872f
\ No newline at end of file
+642b9e8303045dfad45de9c7cc385312c8eb1790
\ No newline at end of file
diff --git a/third_party/dependencies_plugin.tar.gz.sha1 b/third_party/dependencies_plugin.tar.gz.sha1
index 508f2a6..3121229 100644
--- a/third_party/dependencies_plugin.tar.gz.sha1
+++ b/third_party/dependencies_plugin.tar.gz.sha1
@@ -1 +1 @@
-30dad370fc3487aa3742c920a6de7cde02fdf75c
\ No newline at end of file
+6cb09c1ac5bdb586304005d088386762a9690d79
\ No newline at end of file
diff --git a/third_party/gradle.tar.gz.sha1 b/third_party/gradle.tar.gz.sha1
index 0ce5161..e4c7515 100644
--- a/third_party/gradle.tar.gz.sha1
+++ b/third_party/gradle.tar.gz.sha1
@@ -1 +1 @@
-12007d0082ec36a4f60d849534ae11423862f9b3
\ No newline at end of file
+2d43226242a05c6db32a56bbfb8d29d56526d173
\ No newline at end of file
diff --git a/tools/create_local_maven_with_dependencies.py b/tools/create_local_maven_with_dependencies.py
index 8ff7678..b866559 100755
--- a/tools/create_local_maven_with_dependencies.py
+++ b/tools/create_local_maven_with_dependencies.py
@@ -28,7 +28,7 @@
 # This version is both our kotlin compiler and Gradle's (current) kotlin compiler.
 # If Gradle is upgraded, then this version might have to split into two and
 # the dependencies untangled.
-KOTLIN_VERSION = '2.0.21'
+KOTLIN_VERSION = '2.2.21'
 GUAVA_VERSION = '32.1.2-jre'
 GSON_VERSION = '2.10.1'
 JAVASSIST_VERSION = '3.29.2-GA'
@@ -121,9 +121,10 @@
 ]
 
 PLUGIN_DEPENDENCIES = [
-    'org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:pom:2.0.21',
+    'org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:pom:{version}'
+    .format(version=KOTLIN_VERSION),
     'com.google.protobuf:protobuf-gradle-plugin:0.9.4',
-    'org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:pom:5.1.2',
+    'org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:pom:6.4.2',
     'org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.9.10',
     'net.ltgt.errorprone:net.ltgt.errorprone.gradle.plugin:pom:3.0.1',
     'org.spdx.sbom:org.spdx.sbom.gradle.plugin:pom:0.4.0',
diff --git a/tools/update_gradle.py b/tools/update_gradle.py
new file mode 100755
index 0000000..fda318d
--- /dev/null
+++ b/tools/update_gradle.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+# Copyright (c) 2026, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import argparse
+import os
+import shutil
+import stat
+import sys
+import urllib.request
+import zipfile
+
+import utils
+
+
+def parse_options():
+    parser = argparse.ArgumentParser(
+        description='Update Gradle distribution in third_party.')
+    parser.add_argument('--version',
+                        required=True,
+                        help='Gradle version to download.')
+    return parser.parse_args()
+
+
+def main():
+    args = parse_options()
+    version = args.version
+    gradle_url = f'https://services.gradle.org/distributions/gradle-{version}-bin.zip'
+    zip_name = f'gradle-{version}-bin.zip'
+    zip_path = os.path.join(utils.THIRD_PARTY, zip_name)
+    gradle_dir = os.path.join(utils.THIRD_PARTY, 'gradle')
+    unzipped_gradle_dir = os.path.join(utils.THIRD_PARTY, f'gradle-{version}')
+
+    # Download gradle
+    print(f'Downloading {gradle_url} to {zip_path}...')
+    urllib.request.urlretrieve(gradle_url, zip_path)
+
+    # Remove existing gradle directory
+    if os.path.exists(gradle_dir):
+        print(f'Removing existing {gradle_dir}...')
+        shutil.rmtree(gradle_dir)
+
+    # Unzip gradle
+    print(f'Unzipping {zip_path} into {utils.THIRD_PARTY}...')
+    with zipfile.ZipFile(zip_path, 'r') as zip_ref:
+        zip_ref.extractall(utils.THIRD_PARTY)
+
+    # Move new gradle directory
+    print(f'Moving {unzipped_gradle_dir} to {gradle_dir}...')
+    shutil.move(unzipped_gradle_dir, gradle_dir)
+
+    # Remove zip file
+    print(f'Removing {zip_path}...')
+    os.remove(zip_path)
+
+    # Make gradle executable
+    gradle_bin = os.path.join(gradle_dir, 'bin', 'gradle')
+    print(f'Making {gradle_bin} executable...')
+    st = os.stat(gradle_bin)
+    os.chmod(gradle_bin, st.st_mode | stat.S_IXUSR)
+
+    # Upload to Google Storage
+    print('If you want to upload this run:')
+    print(
+        '  (cd {dir}; upload_to_google_storage.py -a --bucket r8-deps gradle)'.
+        format(dir=utils.THIRD_PARTY))
+
+
+if __name__ == '__main__':
+    sys.exit(main())