1use crate::core::compiler::CompileKind;
2use crate::util::context::JobsConfig;
3use crate::util::interning::InternedString;
4use crate::util::{CargoResult, GlobalContext, RustfixDiagnosticServer};
5use anyhow::{Context as _, bail};
6use cargo_util::ProcessBuilder;
7use serde::ser;
8use std::cell::RefCell;
9use std::path::PathBuf;
10use std::rc::Rc;
11use std::thread::available_parallelism;
12
13#[derive(Debug, Clone)]
15pub struct BuildConfig {
16 pub requested_kinds: Vec<CompileKind>,
18 pub jobs: u32,
20 pub keep_going: bool,
22 pub requested_profile: InternedString,
24 pub intent: UserIntent,
26 pub message_format: MessageFormat,
28 pub force_rebuild: bool,
30 pub unit_graph: bool,
32 pub dry_run: bool,
34 pub primary_unit_rustc: Option<ProcessBuilder>,
36 pub rustfix_diagnostic_server: Rc<RefCell<Option<RustfixDiagnosticServer>>>,
39 pub export_dir: Option<PathBuf>,
45 pub future_incompat_report: bool,
47 pub timing_outputs: Vec<TimingOutput>,
49 pub sbom: bool,
51 pub compile_time_deps_only: bool,
53}
54
55fn default_parallelism() -> CargoResult<u32> {
56 Ok(available_parallelism()
57 .context("failed to determine the amount of parallelism available")?
58 .get() as u32)
59}
60
61impl BuildConfig {
62 pub fn new(
71 gctx: &GlobalContext,
72 jobs: Option<JobsConfig>,
73 keep_going: bool,
74 requested_targets: &[String],
75 intent: UserIntent,
76 ) -> CargoResult<BuildConfig> {
77 let cfg = gctx.build_config()?;
78 let requested_kinds = CompileKind::from_requested_targets(gctx, requested_targets)?;
79 if jobs.is_some() && gctx.jobserver_from_env().is_some() {
80 gctx.shell().warn(
81 "a `-j` argument was passed to Cargo but Cargo is \
82 also configured with an external jobserver in \
83 its environment, ignoring the `-j` parameter",
84 )?;
85 }
86 let jobs = match jobs.or(cfg.jobs.clone()) {
87 None => default_parallelism()?,
88 Some(value) => match value {
89 JobsConfig::Integer(j) => match j {
90 0 => anyhow::bail!("jobs may not be 0"),
91 j if j < 0 => (default_parallelism()? as i32 + j).max(1) as u32,
92 j => j as u32,
93 },
94 JobsConfig::String(j) => match j.as_str() {
95 "default" => default_parallelism()?,
96 _ => {
97 anyhow::bail!(format!(
98 "could not parse `{j}`. Number of parallel jobs should be `default` or a number."
99 ))
100 }
101 },
102 },
103 };
104
105 let sbom = match (cfg.sbom, gctx.cli_unstable().sbom) {
107 (Some(sbom), true) => sbom,
108 (Some(_), false) => {
109 gctx.shell()
110 .warn("ignoring 'sbom' config, pass `-Zsbom` to enable it")?;
111 false
112 }
113 (None, _) => false,
114 };
115
116 Ok(BuildConfig {
117 requested_kinds,
118 jobs,
119 keep_going,
120 requested_profile: "dev".into(),
121 intent,
122 message_format: MessageFormat::Human,
123 force_rebuild: false,
124 unit_graph: false,
125 dry_run: false,
126 primary_unit_rustc: None,
127 rustfix_diagnostic_server: Rc::new(RefCell::new(None)),
128 export_dir: None,
129 future_incompat_report: false,
130 timing_outputs: Vec::new(),
131 sbom,
132 compile_time_deps_only: false,
133 })
134 }
135
136 pub fn emit_json(&self) -> bool {
139 matches!(self.message_format, MessageFormat::Json { .. })
140 }
141
142 pub fn single_requested_kind(&self) -> CargoResult<CompileKind> {
143 match self.requested_kinds.len() {
144 1 => Ok(self.requested_kinds[0]),
145 _ => bail!("only one `--target` argument is supported"),
146 }
147 }
148}
149
150#[derive(Clone, Copy, Debug, PartialEq, Eq)]
151pub enum MessageFormat {
152 Human,
153 Json {
154 render_diagnostics: bool,
157 short: bool,
160 ansi: bool,
163 },
164 Short,
165}
166
167#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash, PartialOrd, Ord)]
169pub enum CompileMode {
170 Test,
172 Build,
174 Check { test: bool },
179 Doc,
181 Doctest,
183 Docscrape,
185 RunCustomBuild,
187}
188
189impl ser::Serialize for CompileMode {
190 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
191 where
192 S: ser::Serializer,
193 {
194 use self::CompileMode::*;
195 match *self {
196 Test => "test".serialize(s),
197 Build => "build".serialize(s),
198 Check { .. } => "check".serialize(s),
199 Doc { .. } => "doc".serialize(s),
200 Doctest => "doctest".serialize(s),
201 Docscrape => "docscrape".serialize(s),
202 RunCustomBuild => "run-custom-build".serialize(s),
203 }
204 }
205}
206
207impl<'de> serde::Deserialize<'de> for CompileMode {
208 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
209 where
210 D: serde::Deserializer<'de>,
211 {
212 let s = String::deserialize(deserializer)?;
213 match s.as_str() {
214 "test" => Ok(CompileMode::Test),
215 "build" => Ok(CompileMode::Build),
216 "check" => Ok(CompileMode::Check { test: false }),
217 "doc" => Ok(CompileMode::Doc),
218 "doctest" => Ok(CompileMode::Doctest),
219 "docscrape" => Ok(CompileMode::Docscrape),
220 "run-custom-build" => Ok(CompileMode::RunCustomBuild),
221 other => Err(serde::de::Error::unknown_variant(
222 other,
223 &[
224 "test",
225 "build",
226 "check",
227 "doc",
228 "doctest",
229 "docscrape",
230 "run-custom-build",
231 ],
232 )),
233 }
234 }
235}
236
237impl CompileMode {
238 pub fn is_check(self) -> bool {
240 matches!(self, CompileMode::Check { .. })
241 }
242
243 pub fn is_doc(self) -> bool {
245 matches!(self, CompileMode::Doc { .. })
246 }
247
248 pub fn is_doc_test(self) -> bool {
250 self == CompileMode::Doctest
251 }
252
253 pub fn is_doc_scrape(self) -> bool {
255 self == CompileMode::Docscrape
256 }
257
258 pub fn is_any_test(self) -> bool {
261 matches!(
262 self,
263 CompileMode::Test | CompileMode::Check { test: true } | CompileMode::Doctest
264 )
265 }
266
267 pub fn is_rustc_test(self) -> bool {
269 matches!(self, CompileMode::Test | CompileMode::Check { test: true })
270 }
271
272 pub fn is_run_custom_build(self) -> bool {
274 self == CompileMode::RunCustomBuild
275 }
276
277 pub fn generates_executable(self) -> bool {
282 matches!(self, CompileMode::Test | CompileMode::Build)
283 }
284}
285
286#[derive(Clone, Copy, Debug)]
299pub enum UserIntent {
300 Bench,
302 Build,
304 Check { test: bool },
306 Doc { deps: bool, json: bool },
311 Doctest,
313 Test,
315}
316
317impl UserIntent {
318 pub fn is_doc(self) -> bool {
320 matches!(self, UserIntent::Doc { .. })
321 }
322
323 pub fn wants_doc_json_output(self) -> bool {
325 matches!(self, UserIntent::Doc { json: true, .. })
326 }
327
328 pub fn wants_deps_docs(self) -> bool {
330 matches!(self, UserIntent::Doc { deps: true, .. })
331 }
332
333 pub fn is_any_test(self) -> bool {
336 matches!(
337 self,
338 UserIntent::Test
339 | UserIntent::Bench
340 | UserIntent::Check { test: true }
341 | UserIntent::Doctest
342 )
343 }
344
345 pub fn is_rustc_test(self) -> bool {
347 matches!(
348 self,
349 UserIntent::Test | UserIntent::Bench | UserIntent::Check { test: true }
350 )
351 }
352}
353
354#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash, PartialOrd, Ord)]
356pub enum TimingOutput {
357 Html,
359 Json,
361}