|  | // Copyright (c) 2019, 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. | 
|  | package tasks; | 
|  |  | 
|  | import java.io.BufferedReader; | 
|  | import java.io.File; | 
|  | import java.io.IOException; | 
|  | import java.io.InputStreamReader; | 
|  | import java.nio.charset.StandardCharsets; | 
|  | import java.util.ArrayList; | 
|  | import java.util.Arrays; | 
|  | import java.util.Collection; | 
|  | import java.util.List; | 
|  | import java.util.stream.Collectors; | 
|  | import javax.inject.Inject; | 
|  | import org.gradle.api.DefaultTask; | 
|  | import org.gradle.api.tasks.InputFiles; | 
|  | import org.gradle.api.tasks.OutputDirectory; | 
|  | import org.gradle.api.tasks.TaskAction; | 
|  | import org.gradle.internal.os.OperatingSystem; | 
|  | import org.gradle.workers.IsolationMode; | 
|  | import org.gradle.workers.WorkerExecutor; | 
|  |  | 
|  | public class DownloadDependency extends DefaultTask { | 
|  |  | 
|  | public enum Type { | 
|  | GOOGLE_STORAGE, | 
|  | X20 | 
|  | } | 
|  |  | 
|  | private final WorkerExecutor workerExecutor; | 
|  |  | 
|  | private Type type; | 
|  | private File outputDir; | 
|  | private File tarGzFile; | 
|  | private File sha1File; | 
|  |  | 
|  | @Inject | 
|  | public DownloadDependency(WorkerExecutor workerExecutor) { | 
|  | this.workerExecutor = workerExecutor; | 
|  | } | 
|  |  | 
|  | public void setType(Type type) { | 
|  | this.type = type; | 
|  | } | 
|  |  | 
|  | public void setDependency(String dependency) { | 
|  | outputDir = new File(dependency); | 
|  | tarGzFile = new File(dependency + ".tar.gz"); | 
|  | sha1File = new File(dependency + ".tar.gz.sha1"); | 
|  | } | 
|  |  | 
|  | @InputFiles | 
|  | public Collection<File> getInputFiles() { | 
|  | return Arrays.asList(sha1File, tarGzFile); | 
|  | } | 
|  |  | 
|  | @OutputDirectory | 
|  | public File getOutputDir() { | 
|  | return outputDir; | 
|  | } | 
|  |  | 
|  | public File getSha1File() { | 
|  | return sha1File; | 
|  | } | 
|  |  | 
|  | public File getTarGzFile() { | 
|  | return tarGzFile; | 
|  | } | 
|  |  | 
|  | @TaskAction | 
|  | public void execute() throws IOException, InterruptedException { | 
|  | // 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().length > 0 | 
|  | && tarGzFile.exists() | 
|  | && sha1File.lastModified() <= tarGzFile.lastModified()) { | 
|  | return; | 
|  | } | 
|  | if (outputDir.exists() && outputDir.isDirectory()) { | 
|  | outputDir.delete(); | 
|  | } | 
|  | workerExecutor.submit(RunDownload.class, config -> { | 
|  | config.setIsolationMode(IsolationMode.NONE); | 
|  | config.params(type, sha1File); | 
|  | }); | 
|  | } | 
|  |  | 
|  | public static class RunDownload implements Runnable { | 
|  | private final Type type; | 
|  | private final File sha1File; | 
|  |  | 
|  | @Inject | 
|  | public RunDownload(Type type, File sha1File) { | 
|  | this.type = type; | 
|  | this.sha1File = sha1File; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void run() { | 
|  | try { | 
|  | if (type == Type.GOOGLE_STORAGE) { | 
|  | downloadFromGoogleStorage(); | 
|  | } else if (type == Type.X20) { | 
|  | downloadFromX20(); | 
|  | } else { | 
|  | throw new RuntimeException("Unexpected or missing dependency type: " + type); | 
|  | } | 
|  | } catch (Exception e) { | 
|  | throw new RuntimeException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | private void downloadFromGoogleStorage() throws IOException, InterruptedException { | 
|  | List<String> args = Arrays.asList("-n", "-b", "r8-deps", "-s", "-u", sha1File.toString()); | 
|  | if (OperatingSystem.current().isWindows()) { | 
|  | List<String> command = new ArrayList<>(); | 
|  | command.add("download_from_google_storage.bat"); | 
|  | command.addAll(args); | 
|  | runProcess(new ProcessBuilder().command(command)); | 
|  | } else { | 
|  | runProcess( | 
|  | new ProcessBuilder() | 
|  | .command("bash", "-c", "download_from_google_storage " + String.join(" ", args))); | 
|  | } | 
|  | } | 
|  |  | 
|  | private void downloadFromX20() throws IOException, InterruptedException { | 
|  | if (OperatingSystem.current().isWindows()) { | 
|  | throw new RuntimeException("Downloading from x20 unsupported on windows"); | 
|  | } | 
|  | runProcess( | 
|  | new ProcessBuilder() | 
|  | .command("bash", "-c", "tools/download_from_x20.py " + sha1File.toString())); | 
|  | } | 
|  |  | 
|  | private static void runProcess(ProcessBuilder builder) | 
|  | throws IOException, InterruptedException { | 
|  | String command = String.join(" ", builder.command()); | 
|  | Process p = builder.start(); | 
|  | int exit = p.waitFor(); | 
|  | if (exit != 0) { | 
|  | throw new IOException( | 
|  | "Process failed for " | 
|  | + command | 
|  | + "\n" | 
|  | + new BufferedReader( | 
|  | new InputStreamReader(p.getErrorStream(), StandardCharsets.UTF_8)) | 
|  | .lines() | 
|  | .collect(Collectors.joining("\n"))); | 
|  | } | 
|  | } | 
|  | } | 
|  | } |