1 | use std::{ |
2 | ffi::OsString, |
3 | path::{Path, PathBuf}, |
4 | process::Command, |
5 | }; |
6 | |
7 | #[derive (Debug, Clone)] |
8 | /// A command, its args and its environment. Used for |
9 | /// the main command, the dependency builder and the cfg-reader. |
10 | pub struct CommandBuilder { |
11 | /// Path to the binary. |
12 | pub program: PathBuf, |
13 | /// Arguments to the binary. |
14 | pub args: Vec<OsString>, |
15 | /// A flag to prefix before the path to where output files should be written. |
16 | pub out_dir_flag: Option<OsString>, |
17 | /// A flag to set as the last flag in the command, so the `build` caller can |
18 | /// append the filename themselves. |
19 | pub input_file_flag: Option<OsString>, |
20 | /// Environment variables passed to the binary that is executed. |
21 | /// The environment variable is removed if the second tuple field is `None` |
22 | pub envs: Vec<(OsString, Option<OsString>)>, |
23 | } |
24 | |
25 | impl CommandBuilder { |
26 | /// Uses the `CARGO` env var or just a program named `cargo` and the argument `build`. |
27 | pub fn cargo() -> Self { |
28 | Self { |
29 | program: PathBuf::from(std::env::var_os("CARGO" ).unwrap_or_else(|| "cargo" .into())), |
30 | args: vec!["build" .into()], |
31 | out_dir_flag: Some("--target-dir" .into()), |
32 | input_file_flag: Some("--manifest-path" .into()), |
33 | envs: vec![], |
34 | } |
35 | } |
36 | |
37 | /// Uses the `RUSTC` env var or just a program named `rustc` and the argument `--error-format=json`. |
38 | /// |
39 | /// Take care to only append unless you actually meant to overwrite the defaults. |
40 | /// Overwriting the defaults may make `//~ ERROR` style comments stop working. |
41 | pub fn rustc() -> Self { |
42 | Self { |
43 | program: PathBuf::from(std::env::var_os("RUSTC" ).unwrap_or_else(|| "rustc" .into())), |
44 | args: vec!["--error-format=json" .into()], |
45 | out_dir_flag: Some("--out-dir" .into()), |
46 | input_file_flag: None, |
47 | envs: vec![], |
48 | } |
49 | } |
50 | |
51 | /// Same as [`CommandBuilder::rustc`], but with arguments for obtaining the cfgs. |
52 | pub fn cfgs() -> Self { |
53 | Self { |
54 | args: vec!["--print" .into(), "cfg" .into()], |
55 | ..Self::rustc() |
56 | } |
57 | } |
58 | |
59 | /// Build a `CommandBuilder` for a command without any argumemnts. |
60 | /// You can still add arguments later. |
61 | pub fn cmd(cmd: impl Into<PathBuf>) -> Self { |
62 | Self { |
63 | program: cmd.into(), |
64 | args: vec![], |
65 | out_dir_flag: None, |
66 | input_file_flag: None, |
67 | envs: vec![], |
68 | } |
69 | } |
70 | |
71 | /// Render the command like you'd use it on a command line. |
72 | pub fn display(&self) -> impl std::fmt::Display + '_ { |
73 | struct Display<'a>(&'a CommandBuilder); |
74 | impl std::fmt::Display for Display<'_> { |
75 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
76 | for (var, val) in &self.0.envs { |
77 | if let Some(val) = val { |
78 | write!(f, " {var:?}= {val:?} " )?; |
79 | } |
80 | } |
81 | self.0.program.display().fmt(f)?; |
82 | for arg in &self.0.args { |
83 | write!(f, " {arg:?}" )?; |
84 | } |
85 | if let Some(flag) = &self.0.out_dir_flag { |
86 | write!(f, " {flag:?} OUT_DIR" )?; |
87 | } |
88 | if let Some(flag) = &self.0.input_file_flag { |
89 | write!(f, " {flag:?}" )?; |
90 | } |
91 | Ok(()) |
92 | } |
93 | } |
94 | Display(self) |
95 | } |
96 | |
97 | /// Create a command with the given settings. |
98 | pub fn build(&self, out_dir: &Path) -> Command { |
99 | let mut cmd = Command::new(&self.program); |
100 | cmd.args(self.args.iter()); |
101 | if let Some(flag) = &self.out_dir_flag { |
102 | cmd.arg(flag).arg(out_dir); |
103 | } |
104 | if let Some(flag) = &self.input_file_flag { |
105 | cmd.arg(flag); |
106 | } |
107 | self.apply_env(&mut cmd); |
108 | cmd |
109 | } |
110 | |
111 | pub(crate) fn apply_env(&self, cmd: &mut Command) { |
112 | for (var, val) in self.envs.iter() { |
113 | if let Some(val) = val { |
114 | cmd.env(var, val); |
115 | } else { |
116 | cmd.env_remove(var); |
117 | } |
118 | } |
119 | } |
120 | } |
121 | |