Skip to main content

cargo_binutils/
tool.rs

1use std::env::consts::EXE_SUFFIX;
2use std::path::PathBuf;
3use std::process::Command;
4use std::{env, process};
5
6use anyhow::Result;
7
8use crate::rustc::rustlib;
9
10#[derive(Clone, Copy, PartialEq, Eq)]
11pub enum Tool {
12    Ar,
13    As,
14    Cov,
15    Lld,
16    Nm,
17    Objcopy,
18    Objdump,
19    Profdata,
20    Readobj,
21    Size,
22    Strip,
23}
24
25impl Tool {
26    pub fn name(self) -> &'static str {
27        match self {
28            Tool::Ar => "ar",
29            Tool::As => "as",
30            Tool::Cov => "cov",
31            Tool::Lld => "lld",
32            Tool::Nm => "nm",
33            Tool::Objcopy => "objcopy",
34            Tool::Objdump => "objdump",
35            Tool::Profdata => "profdata",
36            Tool::Readobj => "readobj",
37            Tool::Size => "size",
38            Tool::Strip => "strip",
39        }
40    }
41
42    pub fn exe(self) -> String {
43        match self {
44            Tool::Lld => format!("rust-lld{EXE_SUFFIX}"),
45            _ => format!("llvm-{}{}", self.name(), EXE_SUFFIX),
46        }
47    }
48
49    pub fn path(self) -> Result<PathBuf> {
50        let mut path = rustlib()?;
51        path.push(self.exe());
52        Ok(path)
53    }
54
55    /// Forwards execution to the specified tool.
56    /// If the tool fails to start or is not found this process exits with
57    /// status code 101 the same as if the process has a panic!
58    pub fn rust_exec(self) -> ! {
59        let path = match self.path() {
60            Err(e) => {
61                eprintln!("Failed to find tool: {}\n{}", self.name(), e);
62                process::exit(101)
63            }
64            Ok(p) => p,
65        };
66
67        if !path.exists() {
68            eprintln!(
69                "Could not find tool: {}\nat: {}\nConsider `rustup component add llvm-tools`",
70                self.name(),
71                path.to_string_lossy()
72            );
73            process::exit(102)
74        };
75
76        // Note: The first argument is the name of the binary (e.g. `rust-nm`)
77        let args = env::args().skip(1);
78
79        // Spawn the process and check if the process did spawn
80        let status = match Command::new(path).args(args).status() {
81            Err(e) => {
82                eprintln!("Failed to execute tool: {}\n{}", self.name(), e);
83                process::exit(101)
84            }
85            Ok(s) => s,
86        };
87
88        // Forward the exit code from the tool
89        process::exit(status.code().unwrap_or(101));
90    }
91
92    /// Parses arguments for `cargo $tool` and then if needed executes `cargo build`
93    /// before parsing the required arguments to `rust-$tool`.
94    /// If the tool fails to start or is not found this process exits with
95    /// status code 101 the same as if the process has a panic!
96    pub fn cargo_exec(self, examples: Option<&str>) -> ! {
97        let matches = crate::args(self, examples);
98
99        match crate::run(self, matches) {
100            Err(e) => {
101                eprintln!("error: {e}");
102                process::exit(101)
103            }
104            Ok(ec) => process::exit(ec),
105        }
106    }
107
108    // Whether this tool requires the project to be previously built
109    pub fn needs_build(self) -> bool {
110        match self {
111            Tool::Ar | Tool::As | Tool::Cov | Tool::Lld | Tool::Profdata => false,
112            Tool::Nm | Tool::Objcopy | Tool::Objdump | Tool::Readobj | Tool::Size | Tool::Strip => {
113                true
114            }
115        }
116    }
117}