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

Provided by KDAB

Privacy Policy
Learn Rust with the experts
Find out more