xref: /aosp_15_r20/external/bazelbuild-rules_rust/crate_universe/tools/urls_generator/src/main.rs (revision d4726bddaa87cc4778e7472feed243fa4b6c267f)
1 //! A helper tool for generating urls and sha256 checksums of cargo-bazel binaries and writing them to a module.
2 
3 use std::collections::BTreeMap;
4 use std::io::{BufRead, BufReader};
5 use std::path::{Path, PathBuf};
6 use std::process::Command;
7 use std::{env, fs};
8 
9 use clap::Parser;
10 use hex::ToHex;
11 use sha2::{Digest, Sha256};
12 
13 #[derive(Parser, Debug)]
14 struct Options {
15     /// The path to an artifacts directory expecting to contain directories
16     /// named after platform tripes with binaries inside.
17     #[clap(long)]
18     pub(crate) artifacts_dir: PathBuf,
19 
20     /// A url prefix where the artifacts can be found
21     #[clap(long)]
22     pub(crate) url_prefix: String,
23 
24     /// The path to a buildifier binary. If set, it will be ran on the module
25     #[clap(long)]
26     pub(crate) buildifier: Option<PathBuf>,
27 }
28 
29 struct Artifact {
30     pub(crate) url: String,
31     pub(crate) triple: String,
32     pub(crate) sha256: String,
33 }
34 
calculate_sha256(file_path: &Path) -> String35 fn calculate_sha256(file_path: &Path) -> String {
36     let file = fs::File::open(file_path).unwrap();
37     let mut reader = BufReader::new(file);
38     let mut hasher = Sha256::new();
39 
40     loop {
41         let consummed = {
42             let buffer = reader.fill_buf().unwrap();
43             if buffer.is_empty() {
44                 break;
45             }
46             hasher.update(buffer);
47             buffer.len()
48         };
49         reader.consume(consummed);
50     }
51 
52     let digest = hasher.finalize();
53     digest.encode_hex::<String>()
54 }
55 
locate_artifacts(artifacts_dir: &Path, url_prefix: &str) -> Vec<Artifact>56 fn locate_artifacts(artifacts_dir: &Path, url_prefix: &str) -> Vec<Artifact> {
57     let artifact_dirs: Vec<PathBuf> = artifacts_dir
58         .read_dir()
59         .unwrap()
60         .flatten()
61         .filter(|entry| entry.path().is_dir())
62         .map(|entry| entry.path())
63         .collect();
64 
65     artifact_dirs
66         .iter()
67         .map(|path| {
68             let triple = path.file_name().unwrap().to_string_lossy();
69             let mut artifacts: Vec<Artifact> = path
70                 .read_dir()
71                 .unwrap()
72                 .flatten()
73                 .map(|f_entry| {
74                     let f_path = f_entry.path();
75                     let stem = f_path.file_stem().unwrap().to_string_lossy();
76                     let extension = f_path
77                         .extension()
78                         .map(|ext| format!(".{}", ext.to_string_lossy()))
79                         .unwrap_or_default();
80                     Artifact {
81                         url: format!("{url_prefix}/{stem}-{triple}{extension}"),
82                         triple: triple.to_string(),
83                         sha256: calculate_sha256(&f_entry.path()),
84                     }
85                 })
86                 .collect();
87             if artifacts.len() > 1 {
88                 panic!("Too many artifacts given for {}", triple)
89             }
90             artifacts.pop().unwrap()
91         })
92         .collect()
93 }
94 
95 const TEMPLATE: &str = r#""""A file containing urls and associated sha256 values for cargo-bazel binaries
96 
97 This file is auto-generated for each release to match the urls and sha256s of
98 the binaries produced for it.
99 """
100 
101 # Example:
102 # {
103 #     "x86_64-unknown-linux-gnu": "https://domain.com/downloads/cargo-bazel-x86_64-unknown-linux-gnu",
104 #     "x86_64-apple-darwin": "https://domain.com/downloads/cargo-bazel-x86_64-apple-darwin",
105 #     "x86_64-pc-windows-msvc": "https://domain.com/downloads/cargo-bazel-x86_64-pc-windows-msvc",
106 # }
107 CARGO_BAZEL_URLS = {}
108 
109 # Example:
110 # {
111 #     "x86_64-unknown-linux-gnu": "1d687fcc860dc8a1aa6198e531f0aee0637ed506d6a412fe2b9884ff5b2b17c0",
112 #     "x86_64-apple-darwin": "0363e450125002f581d29cf632cc876225d738cfa433afa85ca557afb671eafa",
113 #     "x86_64-pc-windows-msvc": "f5647261d989f63dafb2c3cb8e131b225338a790386c06cf7112e43dd9805882",
114 # }
115 CARGO_BAZEL_SHA256S = {}
116 
117 # Example:
118 # Label("//crate_universe:cargo_bazel_bin")
119 CARGO_BAZEL_LABEL = Label("@cargo_bazel_bootstrap//:binary")
120 "#;
121 
render_module(artifacts: &[Artifact]) -> String122 fn render_module(artifacts: &[Artifact]) -> String {
123     let urls: BTreeMap<&String, &String> = artifacts
124         .iter()
125         .map(|artifact| (&artifact.triple, &artifact.url))
126         .collect();
127 
128     let sha256s: BTreeMap<&String, &String> = artifacts
129         .iter()
130         .map(|artifact| (&artifact.triple, &artifact.sha256))
131         .collect();
132 
133     TEMPLATE
134         .replace(
135             "CARGO_BAZEL_URLS = {}",
136             &format!(
137                 "CARGO_BAZEL_URLS = {}",
138                 serde_json::to_string_pretty(&urls).unwrap()
139             ),
140         )
141         .replace(
142             "CARGO_BAZEL_SHA256S = {}",
143             &format!(
144                 "CARGO_BAZEL_SHA256S = {}",
145                 serde_json::to_string_pretty(&sha256s).unwrap()
146             ),
147         )
148         .replace(
149             "CARGO_BAZEL_LABEL = Label(\"@cargo_bazel_bootstrap//:binary\")",
150             "CARGO_BAZEL_LABEL = Label(\"//crate_universe:cargo_bazel_bin\")",
151         )
152 }
153 
write_module(content: &str) -> PathBuf154 fn write_module(content: &str) -> PathBuf {
155     let dest = PathBuf::from(
156         env::var("BUILD_WORKSPACE_DIRECTORY").expect("This binary is required to run under Bazel"),
157     )
158     .join(env!("MODULE_ROOT_PATH"));
159 
160     fs::write(&dest, content).unwrap();
161 
162     dest
163 }
164 
run_buildifier(buildifier_path: &Path, module: &Path)165 fn run_buildifier(buildifier_path: &Path, module: &Path) {
166     Command::new(buildifier_path)
167         .arg("-lint=fix")
168         .arg("-mode=fix")
169         .arg("-warnings=all")
170         .arg(module)
171         .output()
172         .unwrap();
173 }
174 
main()175 fn main() {
176     let opt = Options::parse();
177 
178     let artifacts = locate_artifacts(&opt.artifacts_dir, &opt.url_prefix);
179 
180     let content = render_module(&artifacts);
181 
182     let path = write_module(&content);
183 
184     if let Some(buildifier_path) = opt.buildifier {
185         run_buildifier(&buildifier_path, &path);
186     }
187 }
188