1use std::env;
6use std::path::Path;
7use std::path::PathBuf;
8
9use anyhow::anyhow;
10use anyhow::Result;
11use named_lock::NamedLock;
12
13mod sys;
14
15static BASE_URL: &str = "https://storage.googleapis.com/chromeos-localmirror/distfiles/prebuilts/";
16static DOWNLOAD_RETRIES: usize = 3;
17
18fn 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
43struct PrebuiltPath {
52 file: PathBuf,
53 link: PathBuf,
54}
55
56fn 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
65fn 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
78pub fn download_file(url: &str, destination: &Path) -> Result<()> {
79 let lock = NamedLock::create("crosvm_prebuilts_download")?;
80 let _guard = lock.lock()?;
81
82 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
116pub 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(any(target_os = "android", target_os = "linux"))]
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
136pub 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}