1use std::path::PathBuf;
2use std::process::Command;
3
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6
7use crate::heading;
8use clap::{ArgAction, Parser};
9
10#[derive(Clone, Debug, Default, Parser)]
12#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
13pub struct CommonOptions {
14 #[arg(short = 'q', long)]
16 #[cfg_attr(feature = "serde", serde(default))]
17 pub quiet: bool,
18
19 #[arg(
21 short = 'j',
22 long,
23 value_name = "N",
24 help_heading = heading::COMPILATION_OPTIONS,
25 )]
26 #[cfg_attr(feature = "serde", serde(default))]
27 pub jobs: Option<usize>,
28
29 #[arg(long, help_heading = heading::COMPILATION_OPTIONS)]
31 #[cfg_attr(feature = "serde", serde(default))]
32 pub keep_going: bool,
33
34 #[arg(
36 long,
37 value_name = "PROFILE-NAME",
38 help_heading = heading::COMPILATION_OPTIONS,
39 )]
40 #[cfg_attr(feature = "serde", serde(default))]
41 pub profile: Option<String>,
42
43 #[arg(
45 short = 'F',
46 long,
47 action = ArgAction::Append,
48 help_heading = heading::FEATURE_SELECTION,
49 )]
50 #[cfg_attr(feature = "serde", serde(default))]
51 pub features: Vec<String>,
52
53 #[arg(long, help_heading = heading::FEATURE_SELECTION)]
55 #[cfg_attr(feature = "serde", serde(default))]
56 pub all_features: bool,
57
58 #[arg(long, help_heading = heading::FEATURE_SELECTION)]
60 #[cfg_attr(feature = "serde", serde(default))]
61 pub no_default_features: bool,
62
63 #[arg(
65 long,
66 value_name = "TRIPLE",
67 env = "CARGO_BUILD_TARGET",
68 action = ArgAction::Append,
69 help_heading = heading::COMPILATION_OPTIONS,
70 )]
71 #[cfg_attr(feature = "serde", serde(default))]
72 pub target: Vec<String>,
73
74 #[arg(
76 long,
77 value_name = "DIRECTORY",
78 help_heading = heading::COMPILATION_OPTIONS,
79 )]
80 #[cfg_attr(feature = "serde", serde(default))]
81 pub target_dir: Option<PathBuf>,
82
83 #[arg(long, value_name = "FMT", action = ArgAction::Append)]
85 #[cfg_attr(feature = "serde", serde(default))]
86 pub message_format: Vec<String>,
87
88 #[arg(short = 'v', long, action = ArgAction::Count)]
90 #[cfg_attr(feature = "serde", serde(default))]
91 pub verbose: u8,
92
93 #[arg(long, value_name = "WHEN")]
95 #[cfg_attr(feature = "serde", serde(default))]
96 pub color: Option<String>,
97
98 #[arg(long, help_heading = heading::MANIFEST_OPTIONS)]
100 #[cfg_attr(feature = "serde", serde(default))]
101 pub frozen: bool,
102
103 #[arg(long, help_heading = heading::MANIFEST_OPTIONS)]
105 #[cfg_attr(feature = "serde", serde(default))]
106 pub locked: bool,
107
108 #[arg(long, help_heading = heading::MANIFEST_OPTIONS)]
110 #[cfg_attr(feature = "serde", serde(default))]
111 pub offline: bool,
112
113 #[arg(long, value_name = "KEY=VALUE", action = ArgAction::Append)]
115 #[cfg_attr(feature = "serde", serde(default))]
116 pub config: Vec<String>,
117
118 #[arg(short = 'Z', value_name = "FLAG", action = ArgAction::Append)]
120 #[cfg_attr(feature = "serde", serde(default))]
121 pub unstable_flags: Vec<String>,
122
123 #[arg(
125 long,
126 value_name = "FMTS",
127 num_args = 0..,
128 value_delimiter = ',',
129 require_equals = true,
130 help_heading = heading::COMPILATION_OPTIONS,
131 )]
132 #[cfg_attr(feature = "serde", serde(default))]
133 pub timings: Option<Vec<String>>,
134}
135
136impl CommonOptions {
137 pub fn apply(&self, cmd: &mut Command) {
139 if self.quiet {
140 cmd.arg("--quiet");
141 }
142 if let Some(jobs) = self.jobs {
143 cmd.arg("--jobs").arg(jobs.to_string());
144 }
145 if self.keep_going {
146 cmd.arg("--keep-going");
147 }
148 if let Some(profile) = self.profile.as_ref() {
149 cmd.arg("--profile").arg(profile);
150 }
151 for feature in &self.features {
152 cmd.arg("--features").arg(feature);
153 }
154 if self.all_features {
155 cmd.arg("--all-features");
156 }
157 if self.no_default_features {
158 cmd.arg("--no-default-features");
159 }
160
161 let rust_targets = self
164 .target
165 .iter()
166 .map(|target| target.split_once('.').map(|(t, _)| t).unwrap_or(target))
167 .collect::<Vec<&str>>();
168 rust_targets.iter().for_each(|target| {
169 cmd.arg("--target").arg(target);
170 });
171
172 if let Some(dir) = self.target_dir.as_ref() {
173 cmd.arg("--target-dir").arg(dir);
174 }
175 for fmt in &self.message_format {
176 cmd.arg("--message-format").arg(fmt);
177 }
178 if self.verbose > 0 {
179 cmd.arg(format!("-{}", "v".repeat(self.verbose.into())));
180 }
181 if let Some(color) = self.color.as_ref() {
182 cmd.arg("--color").arg(color);
183 }
184 if self.frozen {
185 cmd.arg("--frozen");
186 }
187 if self.locked {
188 cmd.arg("--locked");
189 }
190 if self.offline {
191 cmd.arg("--offline");
192 }
193 for config in &self.config {
194 cmd.arg("--config").arg(config);
195 }
196 for flag in &self.unstable_flags {
197 cmd.arg("-Z").arg(flag);
198 }
199 if let Some(timings) = &self.timings {
200 if timings.is_empty() {
201 cmd.arg("--timings");
202 } else {
203 let timings: Vec<_> = timings.iter().map(|x| x.as_str()).collect();
204 cmd.arg(format!("--timings={}", timings.join(",")));
205 }
206 }
207 }
208
209 pub(crate) fn cargo_command() -> Command {
210 let cargo = match std::env::var_os("CARGO") {
211 Some(cargo) => cargo.into(),
212 None => PathBuf::from("cargo"),
213 };
214 let mut cmd = Command::new(cargo);
215 cmd.env_remove("CARGO");
216 cmd
217 }
218}