1use crate::utils::run_command;
2use anyhow::{anyhow, Result};
3use regex::Regex;
4use std::sync::Once;
5
6static LLVM_TOOLING: Once = Once::new();
7static mut LLVM_TOOLING_RESULT: Option<LlvmToolingResult> = None;
8pub fn get_tooling() -> Result<&'static LlvmToolingResult> {
9 LLVM_TOOLING.call_once(|| unsafe {
10 LLVM_TOOLING_RESULT = Some(find_tooling().expect("Failed to initialize LLVM tooling"));
11 });
12 unsafe { Ok(LLVM_TOOLING_RESULT.as_ref().unwrap()) }
13}
14
15pub fn check_rustc_version() -> Result<(bool, String)> {
16 let output_str = run_command("rustc", &["--version", "--verbose"], None)?;
17 let is_nightly = output_str.contains("nightly");
18 let llvm_major_version = Regex::new(r"LLVM version: (\d+)")
19 .unwrap()
20 .captures(&output_str)
21 .and_then(|cap| cap.get(1).map(|m| m.as_str()))
22 .map(String::from)
23 .ok_or(anyhow!("Failed to parse rustc output: {}", output_str))?;
24 Ok((is_nightly, llvm_major_version))
25}
26
27pub fn check_llvm_tool_version(command: &str) -> Result<String> {
28 let output = run_command(&command, &["--version"], None)?;
29 let llvm_major_version = Regex::new(r"version (\d+)")
30 .unwrap()
31 .captures(&output)
32 .and_then(|cap| cap.get(1).map(|m| m.as_str()))
33 .map(String::from)
34 .ok_or(anyhow!("Failed to parse {command} output:\n{output}"))?;
35 Ok(llvm_major_version)
36}
37
38pub fn find_llvm_tool(tool: &str, major_version: &str) -> Result<String> {
39 let version = check_llvm_tool_version(&format!("{tool}-{major_version}"));
40 if version.is_err() {
41 let version = check_llvm_tool_version(&tool);
42 if version.is_err() {
43 return Err(anyhow!("Failed to find {tool}-{major_version}"));
44 }
45 let version = version.unwrap();
46 if version != major_version {
47 Err(anyhow!(
48 "Found {tool} version {version}, but expected {major_version}"
49 ))
50 } else {
51 Ok(tool.to_string())
52 }
53 } else {
54 Ok(format!("{tool}-{major_version}"))
55 }
56}
57
58pub struct LlvmToolingResult {
59 pub rustc_is_nightly: bool,
60 pub llvm_major_version: String,
61 pub clang: String,
62 pub llvm_cov: String,
63 pub llvm_profdata: String,
64}
65
66pub fn find_tooling() -> Result<LlvmToolingResult> {
67 let (rustc_is_nightly, llvm_major_version) = check_rustc_version()?;
68 let clang = find_llvm_tool("clang", &llvm_major_version)?;
69 let llvm_cov = find_llvm_tool("llvm-cov", &llvm_major_version)?;
70 let llvm_profdata = find_llvm_tool("llvm-profdata", &llvm_major_version)?;
71
72 Ok(LlvmToolingResult {
73 rustc_is_nightly,
74 llvm_major_version,
75 clang,
76 llvm_cov,
77 llvm_profdata,
78 })
79}
80
81