compiler_llvm_builder/
utils.rs1use std::fs::File;
6use std::path::Path;
7use std::path::PathBuf;
8use std::process::Command;
9use std::process::Stdio;
10use std::time::Duration;
11
12use path_slash::PathBufExt;
13
14pub const LLVM_HOST_SOURCE_URL: &str = "https://github.com/llvm/llvm-project";
16
17pub const LLVM_HOST_SOURCE_TAG: &str = "llvmorg-17.0.6";
19
20pub const XCODE_MIN_VERSION: u32 = 11;
22
23pub const XCODE_VERSION_15: u32 = 15;
25
26pub const DOWNLOAD_RETRIES: u16 = 16;
28
29pub const DOWNLOAD_PARALLEL_REQUESTS: u16 = 1;
31
32pub const DOWNLOAD_TIMEOUT_SECONDS: u64 = 300;
34
35pub const MUSL_SNAPSHOTS_URL: &str = "https://git.musl-libc.org/cgit/musl/snapshot";
37
38pub fn command(command: &mut Command, description: &str) -> anyhow::Result<()> {
44 if std::env::var("VERBOSE").is_ok() {
45 println!("\ndescription: {}; command: {:?}", description, command);
46 }
47 if std::env::var("DRY_RUN").is_ok() {
48 println!("\tOnly a dry run; not executing the command.");
49 } else {
50 let status = command
51 .status()
52 .map_err(|error| anyhow::anyhow!("{} process: {}", description, error))?;
53 if !status.success() {
54 anyhow::bail!("{} failed", description);
55 }
56 }
57 Ok(())
58}
59
60pub fn download(url: &str, path: &str) -> anyhow::Result<()> {
64 let mut downloader = downloader::Downloader::builder()
65 .download_folder(Path::new(path))
66 .parallel_requests(DOWNLOAD_PARALLEL_REQUESTS)
67 .retries(DOWNLOAD_RETRIES)
68 .timeout(Duration::from_secs(DOWNLOAD_TIMEOUT_SECONDS))
69 .build()?;
70 while let Err(error) = downloader.download(&[downloader::Download::new(url)]) {
71 eprintln!("MUSL download from `{url}` failed: {error}");
72 }
73 Ok(())
74}
75
76pub fn unpack_tar(filename: PathBuf, path: &str) -> anyhow::Result<()> {
80 let tar_gz = File::open(filename)?;
81 let tar = flate2::read::GzDecoder::new(tar_gz);
82 let mut archive = tar::Archive::new(tar);
83 archive.unpack(path)?;
84 Ok(())
85}
86
87pub fn download_musl(name: &str) -> anyhow::Result<()> {
91 let tar_file_name = format!("{name}.tar.gz");
92 let url = format!("{}/{tar_file_name}", MUSL_SNAPSHOTS_URL);
93 download(url.as_str(), crate::LLVMPath::DIRECTORY_LLVM_TARGET)?;
94 let musl_tarball = crate::LLVMPath::musl_source(tar_file_name.as_str())?;
95 unpack_tar(musl_tarball, crate::LLVMPath::DIRECTORY_LLVM_TARGET)?;
96 Ok(())
97}
98
99pub fn ninja(build_dir: &Path) -> anyhow::Result<()> {
101 let mut ninja = Command::new("ninja");
102 ninja.args(["-C", build_dir.to_string_lossy().as_ref()]);
103 if std::env::var("DRY_RUN").is_ok() {
104 ninja.arg("-n");
105 }
106 command(ninja.arg("install"), "Running ninja install")?;
107 Ok(())
108}
109
110pub fn absolute_path<P: AsRef<Path>>(path: P) -> anyhow::Result<PathBuf> {
114 let mut full_path = std::env::current_dir()?;
115 full_path.push(path);
116 Ok(full_path)
117}
118
119pub fn path_windows_to_unix<P: AsRef<Path> + PathBufExt>(path: P) -> anyhow::Result<PathBuf> {
123 path.to_slash()
124 .map(|pathbuf| PathBuf::from(pathbuf.to_string()))
125 .ok_or_else(|| anyhow::anyhow!("Windows-to-Unix path conversion error"))
126}
127
128pub fn check_presence(name: &str) -> anyhow::Result<()> {
132 let status = Command::new("which")
133 .arg(name)
134 .status()
135 .map_err(|error| anyhow::anyhow!("`which {}` process: {}", name, error))?;
136 if !status.success() {
137 anyhow::bail!("Tool `{}` is missing. Please install", name);
138 }
139 Ok(())
140}
141
142pub fn get_xcode_version() -> anyhow::Result<u32> {
146 let pkgutil = Command::new("pkgutil")
147 .args(["--pkg-info", "com.apple.pkg.CLTools_Executables"])
148 .stdout(Stdio::piped())
149 .spawn()
150 .map_err(|error| anyhow::anyhow!("`pkgutil` process: {}", error))?;
151 let grep_version = Command::new("grep")
152 .arg("version")
153 .stdin(Stdio::from(pkgutil.stdout.expect(
154 "Failed to identify XCode version - XCode or CLI tools are not installed",
155 )))
156 .output()
157 .map_err(|error| anyhow::anyhow!("`grep` process: {}", error))?;
158 let version_string = String::from_utf8(grep_version.stdout)?;
159 let version_regex = regex::Regex::new(r"version: (\d+)\..*")?;
160 let captures = version_regex
161 .captures(version_string.as_str())
162 .ok_or(anyhow::anyhow!(
163 "Failed to parse XCode version: {version_string}"
164 ))?;
165 let xcode_version: u32 = captures
166 .get(1)
167 .expect("Always has a major version")
168 .as_str()
169 .parse()
170 .map_err(|error| anyhow::anyhow!("Failed to parse XCode version: {error}"))?;
171 Ok(xcode_version)
172}