1#![deny(missing_docs)]
4
5use std::{borrow::Cow, io, ops::Deref, path::PathBuf, process::Command, string::FromUtf8Error};
6
7#[derive(Debug)]
9pub struct LlvmTools {
10 bin_dir: PathBuf,
11}
12
13impl LlvmTools {
14 pub fn new() -> Result<Self, Error> {
18 let sysroot = {
19 let mut rustc_cmd = Command::new("rustc");
20 rustc_cmd.arg("--print").arg("sysroot");
21 let rustc_cmd_string = format!("{:?}", rustc_cmd);
22 let out = rustc_cmd
23 .output()
24 .map_err(|e| Error::CommandExecution(rustc_cmd_string, e))?;
25 if !out.status.success() {
26 return Err(Error::RetrieveSysroot(String::from_utf8(out.stderr)));
27 }
28
29 let sysroot_string =
30 String::from_utf8(out.stdout).map_err(Error::SysrootInvalidUtf8)?;
31 PathBuf::from(sysroot_string.trim())
32 };
33
34 let example_tool_name = exe("llvm-objdump");
35 let rustlib = sysroot.join("lib").join("rustlib");
36 for entry in rustlib.read_dir().map_err(Error::ReadDirFailed)? {
37 let bin_dir = entry.map_err(Error::ReadEntryFailed)?.path().join("bin");
38 let tool_path = bin_dir.join(example_tool_name.deref());
39 if tool_path.exists() {
40 return Ok(Self { bin_dir });
41 }
42 }
43
44 Err(Error::NotFound)
45 }
46
47 pub fn tool(&self, tool_name: &str) -> Option<PathBuf> {
51 let tool_path = self.bin_dir.join(&tool_name);
52
53 if tool_path.exists() {
54 Some(tool_path)
55 } else {
56 None
57 }
58 }
59}
60
61#[derive(Debug)]
63pub enum Error {
64 CommandExecution(String, io::Error),
66 RetrieveSysroot(Result<String, FromUtf8Error>),
68 SysrootInvalidUtf8(FromUtf8Error),
70 ReadDirFailed(io::Error),
72 ReadEntryFailed(io::Error),
74 NotFound,
79}
80
81pub fn exe(executable_name: &str) -> Cow<str> {
83 if cfg!(target_os = "windows") {
84 Cow::Owned(format!("{}.exe", executable_name))
85 } else {
86 Cow::Borrowed(executable_name)
87 }
88}