| // Copyright (c) 2023, 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 java.io.BufferedReader |
| import java.io.File |
| import java.io.IOException |
| import java.io.InputStreamReader |
| import java.nio.charset.StandardCharsets |
| import java.util.Arrays |
| import java.util.stream.Collectors |
| import javax.inject.Inject |
| import org.gradle.api.DefaultTask |
| import org.gradle.api.file.RegularFileProperty |
| import org.gradle.api.provider.Property |
| import org.gradle.api.tasks.InputFiles |
| import org.gradle.api.tasks.OutputDirectories |
| import org.gradle.api.tasks.OutputFiles |
| import org.gradle.api.tasks.TaskAction |
| import org.gradle.internal.os.OperatingSystem |
| import org.gradle.workers.WorkAction |
| import org.gradle.workers.WorkParameters |
| import org.gradle.workers.WorkerExecutor |
| |
| abstract class DownloadAllDependenciesTask : DefaultTask() { |
| |
| private var _root: File? = null |
| private var _thirdPartyDeps: List<ThirdPartyDependency>? = null; |
| |
| @InputFiles |
| fun getInputFile(): List<File> { |
| return _thirdPartyDeps!!.map { _root!!.resolve(it.sha1File) } |
| } |
| |
| @OutputDirectories |
| fun getOutputDir(): List<File> { |
| return _thirdPartyDeps!!.map { _root!!.resolve(it.path) } |
| } |
| |
| @OutputFiles |
| fun getOutputFiles(): List<File> { |
| return _thirdPartyDeps!!.map { |
| _root!!.resolve(it.sha1File.resolveSibling(it.sha1File.name.replace(".sha1", ""))) |
| } |
| } |
| |
| @Inject |
| protected abstract fun getWorkerExecutor(): WorkerExecutor? |
| |
| fun setDependencies(root: File, thirdPartyDeps: List<ThirdPartyDependency>) { |
| this._root = root |
| this._thirdPartyDeps = thirdPartyDeps; |
| } |
| |
| @TaskAction |
| fun execute() { |
| val noIsolation = getWorkerExecutor()!!.noIsolation() |
| _thirdPartyDeps?.forEach { |
| val root = _root!! |
| val sha1File = root.resolve(it.sha1File) |
| val tarGzFile = sha1File.resolveSibling(sha1File.name.replace(".sha1", "")) |
| val outputDir = root.resolve(it.path) |
| if (!sha1File.exists()) { |
| throw RuntimeException("Missing sha1 file: $sha1File") |
| } |
| if (shouldExecute(outputDir, tarGzFile, sha1File)) { |
| println("Downloading ${it}") |
| noIsolation.submit(RunDownload::class.java) { |
| type.set(it.type) |
| this.sha1File.set(sha1File) |
| this.outputDir.set(outputDir) |
| this.tarGzFile.set(tarGzFile) |
| this.root.set(root) |
| } |
| } |
| } |
| } |
| |
| interface RunDownloadParameters : WorkParameters { |
| val type : Property<DependencyType> |
| val sha1File : RegularFileProperty |
| val outputDir : RegularFileProperty |
| val tarGzFile : RegularFileProperty |
| val root : RegularFileProperty |
| } |
| |
| abstract class RunDownload : WorkAction<RunDownloadParameters> { |
| override fun execute() { |
| val sha1File = parameters.sha1File.asFile.get() |
| val outputDir = parameters.outputDir.asFile.get() |
| val tarGzFile = parameters.tarGzFile.asFile.get() |
| 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) |
| } |
| } |
| } |
| |
| @Throws(IOException::class, InterruptedException::class) |
| 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(parameters, ProcessBuilder().command(command)) |
| } else { |
| runProcess( |
| parameters, |
| ProcessBuilder() |
| .command("bash", |
| "-c", |
| "download_from_google_storage " + java.lang.String.join(" ", args))) |
| } |
| } |
| |
| @Throws(IOException::class, InterruptedException::class) |
| private fun downloadFromX20(parameters: RunDownloadParameters, sha1File: File) { |
| if (OperatingSystem.current().isWindows) { |
| throw RuntimeException("Downloading from x20 unsupported on windows") |
| } |
| runProcess(parameters, |
| ProcessBuilder() |
| .command("bash", "-c", "tools/download_from_x20.py $sha1File")) |
| } |
| |
| @Throws(IOException::class, InterruptedException::class) |
| private fun runProcess(parameters: RunDownloadParameters, builder: ProcessBuilder) { |
| builder.directory(parameters.root.asFile.get()) |
| val command = java.lang.String.join(" ", builder.command()) |
| val p = builder.start() |
| val exit = p.waitFor() |
| if (exit != 0) { |
| throw IOException("Process failed for $command\n" |
| + BufferedReader( |
| InputStreamReader(p.errorStream, StandardCharsets.UTF_8)) |
| .lines() |
| .collect(Collectors.joining("\n"))) |
| } |
| } |
| } |
| |
| 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 |
| } |
| } |
| } |