• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::env;
6 use std::path::Path;
7 use std::path::PathBuf;
8 
9 use anyhow::anyhow;
10 use anyhow::Result;
11 use named_lock::NamedLock;
12 
13 mod sys;
14 
15 static BASE_URL: &str = "https://storage.googleapis.com/chromeos-localmirror/distfiles/prebuilts/";
16 static DOWNLOAD_RETRIES: usize = 3;
17 
18 // Returns `deps` directory for the current build.
get_deps_directory() -> Result<PathBuf>19 fn get_deps_directory() -> Result<PathBuf> {
20     let out_dir = env::var("OUT_DIR")
21         .ok()
22         .ok_or_else(|| anyhow!("OUT_DIR is not set"))?;
23 
24     let dest = PathBuf::from(&out_dir)
25         .parent()
26         .ok_or_else(|| anyhow!("../ not found for {:?}", out_dir))?
27         .parent()
28         .ok_or_else(|| anyhow!("../../ not found for {:?}", out_dir))?
29         .parent()
30         .ok_or_else(|| anyhow!("../../../ not found for {:?}", out_dir))?
31         .join("deps");
32     if dest.is_dir() {
33         Ok(dest)
34     } else {
35         Err(anyhow!(
36             "deps({:?}) directory not found OUT_DIR: {:?}",
37             dest,
38             out_dir
39         ))
40     }
41 }
42 
43 // We download the prebuilt into deps directory and create a symlink to the downloaded prebuilt in
44 // deps parent directory.
45 // The symlink will help windows find the dll when an executable is manually run.
46 // For example, `file` is downloaded in
47 // `target/x86_64-pc-windows-gnu/release/deps/` and a `link` will be crated in
48 // `target/x86_64-pc-windows-gnu/release/`.
49 // Any executable in those two directories will be able to find the dlls they depend as in the same
50 // directory.
51 struct PrebuiltPath {
52     file: PathBuf,
53     link: PathBuf,
54 }
55 
get_dest_path(filename: &str) -> Result<PrebuiltPath>56 fn get_dest_path(filename: &str) -> Result<PrebuiltPath> {
57     let deps = get_deps_directory()?;
58 
59     Ok(PrebuiltPath {
60         file: deps.join(filename),
61         link: deps.parent().unwrap().join(filename),
62     })
63 }
64 
get_url(library: &str, filename: &str, version: u32) -> String65 fn get_url(library: &str, filename: &str, version: u32) -> String {
66     let build_type = if env::var("DEBUG").is_ok() {
67         "debug"
68     } else {
69         "release"
70     };
71     let platform = env::var("CARGO_CFG_TARGET_FAMILY").unwrap();
72     let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
73     let toolchain = env::var("CARGO_CFG_TARGET_ENV").unwrap();
74 
75     format!("{BASE_URL}{platform}/{arch}/{toolchain}/{library}/{build_type}/{version}/{filename}",)
76 }
77 
download_file(url: &str, destination: &Path) -> Result<()>78 pub fn download_file(url: &str, destination: &Path) -> Result<()> {
79     let lock = NamedLock::create("crosvm_prebuilts_download")?;
80     let _guard = lock.lock()?;
81 
82     // Another process may have already downloaded this since we last checked.
83     if destination.exists() {
84         println!("Prebuilt {destination:?} has already been downloaded by another process.");
85         return Ok(());
86     }
87 
88     println!("Downloading prebuilt {url} to {destination:?}");
89     let mut attempts_left = DOWNLOAD_RETRIES + 1;
90     loop {
91         attempts_left -= 1;
92         let mut cmd = sys::download_command(url, destination);
93         match cmd.status() {
94             Ok(exit_code) => {
95                 if !exit_code.success() {
96                     if attempts_left == 0 {
97                         return Err(anyhow!("Cannot download {}", url));
98                     } else {
99                         println!("Failed to download {url}. Retrying.");
100                     }
101                 } else {
102                     return Ok(());
103                 }
104             }
105             Err(error) => {
106                 if attempts_left == 0 {
107                     return Err(anyhow!(error));
108                 } else {
109                     println!("Failed to download {url}: {error:?}");
110                 }
111             }
112         }
113     }
114 }
115 
116 /// Downloads a prebuilt file, with name `filename` of `version` from the `library` into target's
117 /// `deps` directory.
download_prebuilt(library: &str, version: u32, filename: &str) -> Result<PathBuf>118 pub fn download_prebuilt(library: &str, version: u32, filename: &str) -> Result<PathBuf> {
119     let dest_path = get_dest_path(filename)?;
120     let url = get_url(library, filename, version);
121 
122     println!("downloading prebuilt:{} to:{:?}", url, dest_path.file);
123     download_file(&url, Path::new(&dest_path.file))?;
124     println!(
125         "creating symlink:{:?} linking to:{:?}",
126         dest_path.link, dest_path.file
127     );
128     let _ = std::fs::remove_file(&dest_path.link);
129     #[cfg(unix)]
130     std::os::unix::fs::symlink(&dest_path.file, &dest_path.link)?;
131     #[cfg(windows)]
132     let _ = std::fs::copy(&dest_path.file, &dest_path.link)?;
133     Ok(dest_path.file)
134 }
135 
136 /// Downloads a list of prebuilt file, with names in `filenames` of `version` from the `library`
137 /// into target's `deps` directory.
download_prebuilts(library: &str, version: u32, filenames: &[&str]) -> Result<Vec<PathBuf>>138 pub fn download_prebuilts(library: &str, version: u32, filenames: &[&str]) -> Result<Vec<PathBuf>> {
139     let mut paths = vec![];
140     for filename in filenames {
141         paths.push(download_prebuilt(library, version, filename)?);
142     }
143     Ok(paths)
144 }
145