blob: 4d43b4004f76b1ee439b496ff1658f7a0358fcae [file] [log] [blame]
// 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.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.options.Option
import org.gradle.internal.os.OperatingSystem
import org.gradle.workers.WorkAction
import org.gradle.workers.WorkParameters
import org.gradle.workers.WorkerExecutor
abstract class DownloadDependencyTask : DefaultTask() {
private var dependencyType: DependencyType = DependencyType.GOOGLE_STORAGE
private var _outputDir: File? = null
private var _tarGzFile: File? = null
private var _sha1File: File? = null
@OutputDirectory
fun getOutputDir(): File? {
return _outputDir
}
@Inject
protected abstract fun getWorkerExecutor(): WorkerExecutor?
@Option(
option = "dependency",
description = "Sets the dependency information for a cloud stored file")
fun setDependency(
dependencyName: String, sha1File: File, outputDir: File, dependencyType: DependencyType) {
_outputDir = outputDir
_sha1File = sha1File
_tarGzFile = sha1File.resolveSibling(sha1File.name.replace(".sha1", ""))
this.dependencyType = dependencyType
}
@TaskAction
fun execute() {
val sha1File = _sha1File!!
val outputDir = _outputDir!!
val tarGzFile = _tarGzFile!!
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()) {
return
}
if (outputDir.exists() && outputDir.isDirectory) {
outputDir.delete()
}
getWorkerExecutor()!!
.noIsolation()
.submit(RunDownload::class.java) {
type.set(dependencyType)
this.sha1File.set(sha1File)
}
}
interface RunDownloadParameters : WorkParameters {
val type : Property<DependencyType>
val sha1File : RegularFileProperty
}
abstract class RunDownload : WorkAction<RunDownloadParameters> {
override fun execute() {
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")
}
} catch (e: Exception) {
throw RuntimeException(e)
}
}
@Throws(IOException::class, InterruptedException::class)
private fun downloadFromGoogleStorage(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))
} else {
runProcess(
ProcessBuilder()
.command("bash",
"-c",
"download_from_google_storage " + java.lang.String.join(" ", args)))
}
}
@Throws(IOException::class, InterruptedException::class)
private fun downloadFromX20(sha1File: File) {
if (OperatingSystem.current().isWindows) {
throw RuntimeException("Downloading from x20 unsupported on windows")
}
runProcess(
ProcessBuilder()
.command("bash", "-c", "tools/download_from_x20.py $sha1File"))
}
@Throws(IOException::class, InterruptedException::class)
private fun runProcess(builder: ProcessBuilder) {
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")))
}
}
}
}