Ensure synchronized downloading of dependencies

This CL also defines a third party dependency on a JDK for all modules.

Bug: b/270105162
Change-Id: I1e180a8aea210e45de86a6d953794b643d45f0e4
diff --git a/.gitignore b/.gitignore
index a71b445..c113321 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,7 @@
 tests/2016-12-19/art.tar.gz
 tests/2017-10-04/art
 tests/2017-10-04/art.tar.gz
+*.download_deps_lock
 third_party/aapt2/
 third_party/aapt2.tar.gz
 third_party/api_database/api_database
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
index c1107e6..5ecf7f6 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
@@ -3,8 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import java.io.File
+import java.lang.Thread.sleep
 import java.net.URI
 import java.nio.file.Paths
+import java.util.concurrent.ConcurrentHashMap
 import org.gradle.api.JavaVersion
 import org.gradle.api.Plugin
 import org.gradle.api.Project
@@ -45,24 +47,20 @@
   fun getThirdPartyDependency() : ThirdPartyDependency {
     val os: OperatingSystem = DefaultNativePlatform.getCurrentOperatingSystem()
     val subFolder : String
-    val fileName : String
     if (os.isLinux) {
       subFolder = if(isJdk8()) "linux-x86" else "linux"
-      fileName = "java"
     } else if (os.isMacOsX) {
       subFolder = if(isJdk8()) "darwin-x86" else "osx"
-      fileName = "java"
     } else {
       assert(os.isWindows())
       if (isJdk8()) {
         throw RuntimeException("No Jdk8 on Windows")
       }
       subFolder = "windows"
-      fileName = "java.bat"
     }
     return ThirdPartyDependency(
       name,
-      Paths.get("third_party", "openjdk", folder, subFolder, "bin", fileName).toFile(),
+      Paths.get("third_party", "openjdk", folder, subFolder).toFile(),
       Paths.get("third_party", "openjdk", folder, "$subFolder.tar.gz.sha1").toFile())
   }
 }
@@ -81,17 +79,17 @@
 
 fun Project.ensureThirdPartyDependencies(name : String, deps : List<ThirdPartyDependency>) : Task {
   val outputFiles : MutableList<File> = mutableListOf()
-  val depsTasks = deps.map({
-      tasks.register<DownloadDependencyTask>("download-third-party-${it.packageName}") {
-        setDependency(
-          it.packageName,
-          getRoot().resolve(it.sha1File),
-          getRoot().resolve(it.path).parentFile,
-          it.type)
-        outputFiles.add(it.path)
-      }})
+  val depsTasks = deps.map { tpd ->
+    val projectAndTaskName = "${project.name}-$name"
+    val downloadTaskName = "download-third-party-$projectAndTaskName-${tpd.packageName}"
+    val downloadTask = tasks.register<DownloadDependencyTask>(downloadTaskName) {
+      setDependency(getRoot().resolve(tpd.sha1File), getRoot().resolve(tpd.path), tpd.type)
+    }.get()
+    outputFiles.add(tpd.path)
+    downloadTask
+  }
   return tasks.register("ensure-third-party-$name") {
-    dependsOn(depsTasks)
+    dependsOn(*depsTasks.toTypedArray())
     outputs.files(outputFiles)
   }.get()
 }
@@ -161,8 +159,8 @@
   return "build-example-jars-$name"
 }
 
-fun Project.resolve(thirdPartyDependency: ThirdPartyDependency) : ConfigurableFileCollection {
-  return files(project.getRoot().resolve(thirdPartyDependency.path))
+fun Project.resolve(thirdPartyDependency: ThirdPartyDependency, vararg paths: String) : ConfigurableFileCollection {
+  return files(project.getRoot().resolve(thirdPartyDependency.path).resolveAll(*paths))
 }
 
 /**
@@ -301,55 +299,62 @@
   val androidVMs = getThirdPartyAndroidVms()
   val apiDatabase = ThirdPartyDependency(
     "apiDatabase",
-    Paths.get(
-      "third_party",
-      "api_database",
-      "api_database",
-      "resources",
-      "new_api_database.ser").toFile(),
+    Paths.get("third_party", "api_database", "api_database").toFile(),
     Paths.get("third_party", "api_database", "api_database.tar.gz.sha1").toFile())
   val compilerApi = ThirdPartyDependency(
     "compiler-api",
     Paths.get(
-      "third_party",
-      "binary_compatibility_tests",
-      "compiler_api_tests",
-      "tests.jar").toFile(),
+      "third_party", "binary_compatibility_tests", "compiler_api_tests").toFile(),
     Paths.get(
       "third_party",
       "binary_compatibility_tests",
       "compiler_api_tests.tar.gz.sha1").toFile())
   val dagger = ThirdPartyDependency(
     "dagger",
-    Paths.get("third_party", "dagger", "2.41", "dagger-2.41.jar").toFile(),
+    Paths.get("third_party", "dagger", "2.41").toFile(),
     Paths.get("third_party", "dagger", "2.41.tar.gz.sha1").toFile())
   val ddmLib = ThirdPartyDependency(
     "ddmlib",
-    Paths.get("third_party", "ddmlib", "ddmlib.jar").toFile(),
+    Paths.get("third_party", "ddmlib").toFile(),
     Paths.get("third_party", "ddmlib.tar.gz.sha1").toFile())
+  val desugarJdkLibs = ThirdPartyDependency(
+    "desugar-jdk-libs",
+    Paths.get("third_party", "openjdk", "desugar_jdk_libs").toFile(),
+    Paths.get("third_party", "openjdk", "desugar_jdk_libs.tar.gz.sha1").toFile()
+  )
+  // TODO(b/289363570): This could probably be removed.
+  val iosched2019 = ThirdPartyDependency(
+    "iosched-2019",
+    Paths.get("third_party", "iosched_2019").toFile(),
+    Paths.get("third_party", "iosched_2019.tar.gz.sha1").toFile())
+  val desugarJdkLibs11 = ThirdPartyDependency(
+    "desugar-jdk-libs-11",
+    Paths.get("third_party", "openjdk", "desugar_jdk_libs_11").toFile(),
+    Paths.get("third_party", "openjdk", "desugar_jdk_libs_11.tar.gz.sha1").toFile()
+  )
   val jacoco = ThirdPartyDependency(
     "jacoco",
-    Paths.get("third_party", "jacoco", "0.8.6", "lib", "jacocoagent.jar").toFile(),
+    Paths.get("third_party", "jacoco", "0.8.6").toFile(),
     Paths.get("third_party", "jacoco", "0.8.6.tar.gz.sha1").toFile()
   )
   val jasmin = ThirdPartyDependency(
     "jasmin",
-    Paths.get("third_party", "jasmin", "jasmin-2.4.jar").toFile(),
+    Paths.get("third_party", "jasmin").toFile(),
     Paths.get("third_party", "jasmin.tar.gz.sha1").toFile())
   val java8Runtime = ThirdPartyDependency(
     "openjdk-rt-1.8",
-    Paths.get("third_party", "openjdk", "openjdk-rt-1.8", "rt.jar").toFile(),
+    Paths.get("third_party", "openjdk", "openjdk-rt-1.8").toFile(),
     Paths.get("third_party", "openjdk", "openjdk-rt-1.8.tar.gz.sha1").toFile()
   )
   val jdks = getJdks()
   val jdk11Test = ThirdPartyDependency(
     "jdk-11-test",
-    Paths.get("third_party", "openjdk", "jdk-11-test", "Makefile").toFile(),
+    Paths.get("third_party", "openjdk", "jdk-11-test").toFile(),
     Paths.get("third_party", "openjdk", "jdk-11-test.tar.gz.sha1").toFile()
   )
   val jdwpTests = ThirdPartyDependency(
     "jdwp-tests",
-    Paths.get("third_party", "jdwp-tests", "apache-harmony-jdwp-tests-host.jar").toFile(),
+    Paths.get("third_party", "jdwp-tests").toFile(),
     Paths.get("third_party", "jdwp-tests.tar.gz.sha1").toFile())
   val kotlinCompilers = getThirdPartyKotlinCompilers()
   val proguards = getThirdPartyProguards()
@@ -382,7 +387,7 @@
 fun getThirdPartyAndroidJar(version : String) : ThirdPartyDependency {
   return ThirdPartyDependency(
     version,
-    Paths.get("third_party", "android_jar", version, "android.jar").toFile(),
+    Paths.get("third_party", "android_jar", version).toFile(),
     Paths.get("third_party", "android_jar", "$version.tar.gz.sha1").toFile())
 }
 
@@ -407,7 +412,11 @@
   val output = Paths.get("tools", "linux", *version.toTypedArray(), "bin", "art").toFile()
   return ThirdPartyDependency(
     version.last(),
-    output,
+    Paths.get(
+      "tools",
+      "linux",
+      *version.slice(0..version.size - 2).toTypedArray(),
+      version.last()).toFile(),
     Paths.get(
       "tools",
       "linux",
@@ -428,14 +437,9 @@
   val os: OperatingSystem = DefaultNativePlatform.getCurrentOperatingSystem()
   return listOf("proguard5.2.1", "proguard6.0.1", "proguard-7.0.0")
     .map { ThirdPartyDependency(
-        it,
-        Paths.get(
-          "third_party",
-          "proguard",
-          it,
-          "bin",
-          if (os.isWindows) "proguard.bat" else "proguard.sh").toFile(),
-        Paths.get("third_party", "proguard", "${it}.tar.gz.sha1").toFile())}
+      it,
+      Paths.get("third_party", "proguard", it).toFile(),
+      Paths.get("third_party", "proguard", "${it}.tar.gz.sha1").toFile())}
 }
 
 fun getThirdPartyKotlinCompilers() : List<ThirdPartyDependency> {
@@ -449,12 +453,6 @@
     "kotlin-compiler-dev")
     .map { ThirdPartyDependency(
       it,
-      Paths.get(
-        "third_party",
-        "kotlin",
-        it,
-        "kotlinc",
-        "lib",
-        "kotlin-stdlib.jar").toFile(),
+      Paths.get("third_party", "kotlin", it).toFile(),
       Paths.get("third_party", "kotlin", "${it}.tar.gz.sha1").toFile())}
 }
\ No newline at end of file
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/DownloadDependencyTask.kt b/d8_r8/commonBuildSrc/src/main/kotlin/DownloadDependencyTask.kt
index 4d43b40..c40d857 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/DownloadDependencyTask.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/DownloadDependencyTask.kt
@@ -6,6 +6,11 @@
 import java.io.File
 import java.io.IOException
 import java.io.InputStreamReader
+import java.io.RandomAccessFile
+import java.lang.Thread.sleep
+import java.nio.channels.FileChannel
+import java.nio.channels.FileLock
+import java.nio.channels.OverlappingFileLockException
 import java.nio.charset.StandardCharsets
 import java.util.Arrays
 import java.util.stream.Collectors
@@ -39,8 +44,7 @@
   @Option(
     option = "dependency",
     description = "Sets the dependency information for a cloud stored file")
-  fun setDependency(
-    dependencyName: String, sha1File: File, outputDir: File, dependencyType: DependencyType) {
+  fun setDependency(sha1File: File, outputDir: File, dependencyType: DependencyType) {
     _outputDir = outputDir
     _sha1File = sha1File
     _tarGzFile = sha1File.resolveSibling(sha1File.name.replace(".sha1", ""))
@@ -55,59 +59,87 @@
     if (!sha1File.exists()) {
       throw RuntimeException("Missing sha1 file: $sha1File")
     }
-    // First run will write the tar.gz file, causing the second run to still be out-of-date.
-    // Check if the modification time of the tar is newer than the sha in which case we are done.
-    // Also, check the contents of the out directory because gradle appears to create it for us...
-    if (outputDir.exists()
-      && outputDir.isDirectory
-      && outputDir.list().isNotEmpty() && tarGzFile.exists()
-      && sha1File.lastModified() <= tarGzFile.lastModified()) {
+    if (!shouldExecute(outputDir, tarGzFile, sha1File)) {
       return
     }
-    if (outputDir.exists() && outputDir.isDirectory) {
-      outputDir.delete()
+    // Create a lock to ensure sequential a single downloader per third party dependency.
+    val lockFile = sha1File.parentFile.resolve(sha1File.name + ".download_deps_lock")
+    if (!lockFile.exists()) {
+      lockFile.createNewFile()
     }
     getWorkerExecutor()!!
       .noIsolation()
       .submit(RunDownload::class.java) {
         type.set(dependencyType)
         this.sha1File.set(sha1File)
+        this.outputDir.set(outputDir)
+        this.tarGzFile.set(tarGzFile)
+        this.lockFile.set(lockFile)
       }
   }
 
   interface RunDownloadParameters : WorkParameters {
     val type : Property<DependencyType>
     val sha1File : RegularFileProperty
+    val outputDir : RegularFileProperty
+    val tarGzFile : RegularFileProperty
+    val lockFile : RegularFileProperty
   }
 
   abstract class RunDownload : WorkAction<RunDownloadParameters> {
     override fun execute() {
+      var lock : FileLock? = null
       try {
-        val parameters: RunDownloadParameters = parameters
-        val type: DependencyType = parameters.type.get()
-        val sha1File: File = parameters.sha1File.asFile.get()
-        if (type == DependencyType.GOOGLE_STORAGE) {
-          downloadFromGoogleStorage(sha1File)
-        } else if (type == DependencyType.X20) {
-          downloadFromX20(sha1File)
-        } else {
-          throw RuntimeException("Unexpected or missing dependency type: $type")
+        val sha1File = parameters.sha1File.asFile.get()
+        val outputDir = parameters.outputDir.asFile.get()
+        val tarGzFile = parameters.tarGzFile.asFile.get()
+        if (!shouldExecute(outputDir, tarGzFile, sha1File)) {
+          return;
+        }
+        val lockFile = parameters.lockFile.asFile.get()
+        val channel: FileChannel = RandomAccessFile(lockFile, "rw").getChannel()
+        // Block until we have the lock.
+        var couldTakeLock = false
+        while (!couldTakeLock) {
+          try {
+            lock = channel.lock()
+            couldTakeLock = true;
+          } catch (ignored: OverlappingFileLockException) {
+            sleep(50);
+          }
+        }
+        if (!shouldExecute(outputDir, tarGzFile, sha1File)) {
+          return;
+        }
+        if (outputDir.exists() && outputDir.isDirectory) {
+          outputDir.delete()
+        }
+        when (parameters.type.get()) {
+          DependencyType.GOOGLE_STORAGE -> {
+            downloadFromGoogleStorage(parameters, sha1File)
+          }
+          DependencyType.X20 -> {
+            downloadFromX20(parameters, sha1File)
+          }
         }
       } catch (e: Exception) {
         throw RuntimeException(e)
+      } finally {
+        lock?.release()
       }
     }
 
     @Throws(IOException::class, InterruptedException::class)
-    private fun downloadFromGoogleStorage(sha1File: File) {
+    private fun downloadFromGoogleStorage(parameters: RunDownloadParameters, sha1File: File) {
       val args = Arrays.asList("-n", "-b", "r8-deps", "-s", "-u", sha1File.toString())
       if (OperatingSystem.current().isWindows) {
         val command: MutableList<String> = ArrayList()
         command.add("download_from_google_storage.bat")
         command.addAll(args)
-        runProcess(ProcessBuilder().command(command))
+        runProcess(parameters, ProcessBuilder().command(command))
       } else {
         runProcess(
+          parameters,
           ProcessBuilder()
             .command("bash",
                      "-c",
@@ -116,17 +148,17 @@
     }
 
     @Throws(IOException::class, InterruptedException::class)
-    private fun downloadFromX20(sha1File: File) {
+    private fun downloadFromX20(parameters: RunDownloadParameters, sha1File: File) {
       if (OperatingSystem.current().isWindows) {
         throw RuntimeException("Downloading from x20 unsupported on windows")
       }
-      runProcess(
+      runProcess(parameters,
         ProcessBuilder()
           .command("bash", "-c", "tools/download_from_x20.py $sha1File"))
     }
 
     @Throws(IOException::class, InterruptedException::class)
-    private fun runProcess(builder: ProcessBuilder) {
+    private fun runProcess(parameters: RunDownloadParameters, builder: ProcessBuilder) {
       val command = java.lang.String.join(" ", builder.command())
       val p = builder.start()
       val exit = p.waitFor()
@@ -139,4 +171,19 @@
       }
     }
   }
+
+  companion object {
+    fun shouldExecute(outputDir: File, tarGzFile: File, sha1File: File) : Boolean {
+      // First run will write the tar.gz file, causing the second run to still be out-of-date.
+      // Check if the modification time of the tar is newer than the sha in which case we are done.
+      if (outputDir.exists()
+        && outputDir.isDirectory
+        && outputDir.list().isNotEmpty()
+        && tarGzFile.exists()
+        && sha1File.lastModified() <= tarGzFile.lastModified()) {
+        return false
+      }
+      return true
+    }
+  }
 }
\ No newline at end of file
diff --git a/d8_r8/keepanno/build.gradle.kts b/d8_r8/keepanno/build.gradle.kts
index b7a2acf..d04408d 100644
--- a/d8_r8/keepanno/build.gradle.kts
+++ b/d8_r8/keepanno/build.gradle.kts
@@ -19,3 +19,13 @@
   compileOnly(Deps.asm)
   compileOnly(Deps.guava)
 }
+
+val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
+  "compileDeps",
+  listOf(Jdk.JDK_11.getThirdPartyDependency()))
+
+tasks {
+  withType<JavaCompile> {
+    dependsOn(thirdPartyCompileDependenciesTask)
+  }
+}
\ No newline at end of file
diff --git a/d8_r8/library_desugar/build.gradle.kts b/d8_r8/library_desugar/build.gradle.kts
index 7483d80..768690a 100644
--- a/d8_r8/library_desugar/build.gradle.kts
+++ b/d8_r8/library_desugar/build.gradle.kts
@@ -2,9 +2,6 @@
 // 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 java.nio.file.Paths
-// import net.ltgt.gradle.errorprone.errorprone
-
 plugins {
   `kotlin-dsl`
   id("dependencies-plugin")
@@ -22,8 +19,13 @@
 dependencies {
 }
 
+val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
+  "compileDeps",
+  listOf(Jdk.JDK_11.getThirdPartyDependency()))
+
 tasks {
   withType<JavaCompile> {
+    dependsOn(thirdPartyCompileDependenciesTask)
     options.setFork(true)
     options.forkOptions.memoryMaximumSize = "3g"
     options.forkOptions.jvmArgs = listOf(
diff --git a/d8_r8/main/build.gradle.kts b/d8_r8/main/build.gradle.kts
index 49e79b5..614414d 100644
--- a/d8_r8/main/build.gradle.kts
+++ b/d8_r8/main/build.gradle.kts
@@ -33,6 +33,10 @@
   errorprone(Deps.errorprone)
 }
 
+val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
+  "compileDeps",
+  listOf(Jdk.JDK_11.getThirdPartyDependency()))
+
 val thirdPartyResourceDependenciesTask = ensureThirdPartyDependencies(
   "resourceDeps",
   listOf(ThirdPartyDeps.apiDatabase))
@@ -134,6 +138,7 @@
 }
 
 tasks.withType<JavaCompile> {
+  dependsOn(thirdPartyCompileDependenciesTask)
   println("NOTE: Running with JDK: " + org.gradle.internal.jvm.Jvm.current().javaHome)
 
   // Enable error prone for D8/R8 main sources and make all warnings errors.
diff --git a/d8_r8/test_modules/tests_java_10/build.gradle.kts b/d8_r8/test_modules/tests_java_10/build.gradle.kts
index aec1b33..7d653b1 100644
--- a/d8_r8/test_modules/tests_java_10/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_10/build.gradle.kts
@@ -26,8 +26,13 @@
 // We just need to register the examples jars for it to be referenced by other modules.
 val buildExampleJars = buildExampleJars("examplesJava10")
 
+val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
+  "compileDeps",
+  listOf(Jdk.JDK_11.getThirdPartyDependency()))
+
 tasks {
   withType<JavaCompile> {
+    dependsOn(thirdPartyCompileDependenciesTask)
     options.setFork(true)
     options.forkOptions.memoryMaximumSize = "3g"
     options.forkOptions.executable = getCompilerPath(Jdk.JDK_11)
diff --git a/d8_r8/test_modules/tests_java_11/build.gradle.kts b/d8_r8/test_modules/tests_java_11/build.gradle.kts
index bbf8d9c..f056220 100644
--- a/d8_r8/test_modules/tests_java_11/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_11/build.gradle.kts
@@ -26,8 +26,13 @@
 // We just need to register the examples jars for it to be referenced by other modules.
 val buildExampleJars = buildExampleJars("examplesJava11")
 
+val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
+  "compileDeps",
+  listOf(Jdk.JDK_11.getThirdPartyDependency()))
+
 tasks {
   withType<JavaCompile> {
+    dependsOn(thirdPartyCompileDependenciesTask)
     options.setFork(true)
     options.forkOptions.memoryMaximumSize = "3g"
     options.forkOptions.executable = getCompilerPath(Jdk.JDK_11)
diff --git a/d8_r8/test_modules/tests_java_17/build.gradle.kts b/d8_r8/test_modules/tests_java_17/build.gradle.kts
index e68b356..005cb87 100644
--- a/d8_r8/test_modules/tests_java_17/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_17/build.gradle.kts
@@ -26,8 +26,13 @@
 // We just need to register the examples jars for it to be referenced by other modules.
 val buildExampleJars = buildExampleJars("examplesJava17")
 
+val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
+  "compileDeps",
+  listOf(Jdk.JDK_17.getThirdPartyDependency()))
+
 tasks {
   withType<JavaCompile> {
+    dependsOn(thirdPartyCompileDependenciesTask)
     options.setFork(true)
     options.forkOptions.memoryMaximumSize = "3g"
     options.forkOptions.executable = getCompilerPath(Jdk.JDK_17)
diff --git a/d8_r8/test_modules/tests_java_20/build.gradle.kts b/d8_r8/test_modules/tests_java_20/build.gradle.kts
index 5c348ed..f2d2e31 100644
--- a/d8_r8/test_modules/tests_java_20/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_20/build.gradle.kts
@@ -26,8 +26,13 @@
 // We just need to register the examples jars for it to be referenced by other modules.
 val buildExampleJars = buildExampleJars("examplesJava20")
 
+val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
+  "compileDeps",
+  listOf(Jdk.JDK_20.getThirdPartyDependency()))
+
 tasks {
   withType<JavaCompile> {
+    dependsOn(thirdPartyCompileDependenciesTask)
     options.setFork(true)
     options.forkOptions.memoryMaximumSize = "3g"
     options.forkOptions.executable = getCompilerPath(Jdk.JDK_20)
diff --git a/d8_r8/test_modules/tests_java_8/build.gradle.kts b/d8_r8/test_modules/tests_java_8/build.gradle.kts
index 669b394..953098f 100644
--- a/d8_r8/test_modules/tests_java_8/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_8/build.gradle.kts
@@ -38,9 +38,9 @@
   implementation(Deps.kotlinStdLib)
   implementation(Deps.kotlinReflect)
   implementation(Deps.kotlinMetadata)
-  implementation(resolve(ThirdPartyDeps.ddmLib))
-  implementation(resolve(ThirdPartyDeps.jasmin))
-  implementation(resolve(ThirdPartyDeps.jdwpTests))
+  implementation(resolve(ThirdPartyDeps.ddmLib,"ddmlib.jar"))
+  implementation(resolve(ThirdPartyDeps.jasmin,"jasmin-2.4.jar"))
+  implementation(resolve(ThirdPartyDeps.jdwpTests,"apache-harmony-jdwp-tests-host.jar"))
   implementation(Deps.fastUtil)
   implementation(Deps.smali)
 }
@@ -58,6 +58,9 @@
   listOf(
     ThirdPartyDeps.compilerApi,
     ThirdPartyDeps.dagger,
+    ThirdPartyDeps.desugarJdkLibs,
+    ThirdPartyDeps.desugarJdkLibs11,
+    ThirdPartyDeps.iosched2019,
     ThirdPartyDeps.jacoco,
     ThirdPartyDeps.java8Runtime,
     ThirdPartyDeps.jdk11Test)
@@ -93,6 +96,9 @@
 }
 
 tasks {
+  "compileTestJava" {
+    dependsOn(thirdPartyCompileDependenciesTask)
+  }
   withType<JavaCompile> {
     dependsOn(gradle.includedBuild("keepanno").task(":jar"))
     dependsOn(gradle.includedBuild("main").task(":jar"))
diff --git a/d8_r8/test_modules/tests_java_9/build.gradle.kts b/d8_r8/test_modules/tests_java_9/build.gradle.kts
index e7a55e1..bda4b77 100644
--- a/d8_r8/test_modules/tests_java_9/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_9/build.gradle.kts
@@ -26,8 +26,13 @@
 // We just need to register the examples jars for it to be referenced by other modules.
 val buildExampleJars = buildExampleJars("examplesJava9")
 
+val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
+  "compileDeps",
+  listOf(Jdk.JDK_9.getThirdPartyDependency()))
+
 tasks {
   withType<JavaCompile> {
+    dependsOn(thirdPartyCompileDependenciesTask)
     options.setFork(true)
     options.forkOptions.memoryMaximumSize = "3g"
     options.forkOptions.executable = getCompilerPath(Jdk.JDK_9)
diff --git a/d8_r8/test_modules/tests_java_examples/build.gradle.kts b/d8_r8/test_modules/tests_java_examples/build.gradle.kts
index 16fec67..1169c9a 100644
--- a/d8_r8/test_modules/tests_java_examples/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_examples/build.gradle.kts
@@ -29,8 +29,13 @@
 // We just need to register the examples jars for it to be referenced by other modules.
 val buildExampleJars = buildExampleJars("examples")
 
+val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
+  "compileDeps",
+  listOf(Jdk.JDK_11.getThirdPartyDependency()))
+
 tasks {
   withType<JavaCompile> {
+    dependsOn(thirdPartyCompileDependenciesTask)
     options.setFork(true)
     options.forkOptions.memoryMaximumSize = "3g"
     options.forkOptions.jvmArgs = listOf(
diff --git a/d8_r8/test_modules/tests_java_examplesAndroidN/build.gradle.kts b/d8_r8/test_modules/tests_java_examplesAndroidN/build.gradle.kts
index 54b5a45..093919e 100644
--- a/d8_r8/test_modules/tests_java_examplesAndroidN/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_examplesAndroidN/build.gradle.kts
@@ -29,8 +29,13 @@
 // We just need to register the examples jars for it to be referenced by other modules.
 val buildExampleJars = buildExampleJars("examplesAndroidN")
 
+val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
+  "compileDeps",
+  listOf(Jdk.JDK_11.getThirdPartyDependency()))
+
 tasks {
   withType<JavaCompile> {
+    dependsOn(thirdPartyCompileDependenciesTask)
     options.setFork(true)
     options.forkOptions.memoryMaximumSize = "3g"
     options.forkOptions.jvmArgs = listOf(
diff --git a/d8_r8/test_modules/tests_java_examplesAndroidO/build.gradle.kts b/d8_r8/test_modules/tests_java_examplesAndroidO/build.gradle.kts
index 43020e3..6be9cce 100644
--- a/d8_r8/test_modules/tests_java_examplesAndroidO/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_examplesAndroidO/build.gradle.kts
@@ -29,8 +29,13 @@
 // We just need to register the examples jars for it to be referenced by other modules.
 val buildExampleJars = buildExampleJars("examplesAndroidO")
 
+val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
+  "compileDeps",
+  listOf(Jdk.JDK_11.getThirdPartyDependency()))
+
 tasks {
   withType<JavaCompile> {
+    dependsOn(thirdPartyCompileDependenciesTask)
     options.setFork(true)
     options.compilerArgs.add("-Xlint:-options")
     options.compilerArgs.add("-parameters")
diff --git a/d8_r8/test_modules/tests_java_examplesAndroidP/build.gradle.kts b/d8_r8/test_modules/tests_java_examplesAndroidP/build.gradle.kts
index 13c2530..56cb851 100644
--- a/d8_r8/test_modules/tests_java_examplesAndroidP/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_examplesAndroidP/build.gradle.kts
@@ -29,8 +29,13 @@
 // We just need to register the examples jars for it to be referenced by other modules.
 val buildExampleJars = buildExampleJars("examplesAndroidP")
 
+val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
+  "compileDeps",
+  listOf(Jdk.JDK_11.getThirdPartyDependency()))
+
 tasks {
   withType<JavaCompile> {
+    dependsOn(thirdPartyCompileDependenciesTask)
     options.setFork(true)
     options.forkOptions.memoryMaximumSize = "3g"
     options.forkOptions.jvmArgs = listOf(
diff --git a/d8_r8/test_modules/tests_java_kotlinR8TestResources/build.gradle.kts b/d8_r8/test_modules/tests_java_kotlinR8TestResources/build.gradle.kts
index 353d621..7ccf7cd 100644
--- a/d8_r8/test_modules/tests_java_kotlinR8TestResources/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_kotlinR8TestResources/build.gradle.kts
@@ -29,8 +29,13 @@
 // We just need to register the examples jars for it to be referenced by other modules.
 val buildExampleJars = buildExampleJars("kotlinR8TestResources")
 
+val thirdPartyCompileDependenciesTask = ensureThirdPartyDependencies(
+  "compileDeps",
+  listOf(Jdk.JDK_11.getThirdPartyDependency()))
+
 tasks {
   withType<JavaCompile> {
+    dependsOn(thirdPartyCompileDependenciesTask)
     options.setFork(true)
     options.forkOptions.memoryMaximumSize = "3g"
     options.forkOptions.jvmArgs = listOf(