cargo/core/compiler/
build_config.rs

1use crate::core::compiler::CompileKind;
2use crate::util::context::JobsConfig;
3use crate::util::interning::InternedString;
4use crate::util::{CargoResult, GlobalContext, RustfixDiagnosticServer};
5use anyhow::{bail, Context as _};
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/// Configuration information for a rustc build.
14#[derive(Debug, Clone)]
15pub struct BuildConfig {
16    /// The requested kind of compilation for this session
17    pub requested_kinds: Vec<CompileKind>,
18    /// Number of rustc jobs to run in parallel.
19    pub jobs: u32,
20    /// Do not abort the build as soon as there is an error.
21    pub keep_going: bool,
22    /// Build profile
23    pub requested_profile: InternedString,
24    /// The mode we are compiling in.
25    pub mode: CompileMode,
26    /// `true` to print stdout in JSON format (for machine reading).
27    pub message_format: MessageFormat,
28    /// Force Cargo to do a full rebuild and treat each target as changed.
29    pub force_rebuild: bool,
30    /// Output a build plan to stdout instead of actually compiling.
31    pub build_plan: bool,
32    /// Output the unit graph to stdout instead of actually compiling.
33    pub unit_graph: bool,
34    /// `true` to avoid really compiling.
35    pub dry_run: bool,
36    /// An optional override of the rustc process for primary units
37    pub primary_unit_rustc: Option<ProcessBuilder>,
38    /// A thread used by `cargo fix` to receive messages on a socket regarding
39    /// the success/failure of applying fixes.
40    pub rustfix_diagnostic_server: Rc<RefCell<Option<RustfixDiagnosticServer>>>,
41    /// The directory to copy final artifacts to. Note that even if
42    /// `artifact-dir` is set, a copy of artifacts still can be found at
43    /// `target/(debug\release)` as usual.
44    /// Named `export_dir` to avoid confusion with
45    /// `CompilationFiles::artifact_dir`.
46    pub export_dir: Option<PathBuf>,
47    /// `true` to output a future incompatibility report at the end of the build
48    pub future_incompat_report: bool,
49    /// Which kinds of build timings to output (empty if none).
50    pub timing_outputs: Vec<TimingOutput>,
51}
52
53fn default_parallelism() -> CargoResult<u32> {
54    Ok(available_parallelism()
55        .context("failed to determine the amount of parallelism available")?
56        .get() as u32)
57}
58
59impl BuildConfig {
60    /// Parses all config files to learn about build configuration. Currently
61    /// configured options are:
62    ///
63    /// * `build.jobs`
64    /// * `build.target`
65    /// * `target.$target.ar`
66    /// * `target.$target.linker`
67    /// * `target.$target.libfoo.metadata`
68    pub fn new(
69        gctx: &GlobalContext,
70        jobs: Option<JobsConfig>,
71        keep_going: bool,
72        requested_targets: &[String],
73        mode: CompileMode,
74    ) -> CargoResult<BuildConfig> {
75        let cfg = gctx.build_config()?;
76        let requested_kinds = CompileKind::from_requested_targets(gctx, requested_targets)?;
77        if jobs.is_some() && gctx.jobserver_from_env().is_some() {
78            gctx.shell().warn(
79                "a `-j` argument was passed to Cargo but Cargo is \
80                 also configured with an external jobserver in \
81                 its environment, ignoring the `-j` parameter",
82            )?;
83        }
84        let jobs = match jobs.or(cfg.jobs.clone()) {
85            None => default_parallelism()?,
86            Some(value) => match value {
87                JobsConfig::Integer(j) => match j {
88                    0 => anyhow::bail!("jobs may not be 0"),
89                    j if j < 0 => (default_parallelism()? as i32 + j).max(1) as u32,
90                    j => j as u32,
91                },
92                JobsConfig::String(j) => match j.as_str() {
93                    "default" => default_parallelism()?,
94                    _ => {
95                        anyhow::bail!(
96			    format!("could not parse `{j}`. Number of parallel jobs should be `default` or a number."))
97                    }
98                },
99            },
100        };
101
102        Ok(BuildConfig {
103            requested_kinds,
104            jobs,
105            keep_going,
106            requested_profile: InternedString::new("dev"),
107            mode,
108            message_format: MessageFormat::Human,
109            force_rebuild: false,
110            build_plan: false,
111            unit_graph: false,
112            dry_run: false,
113            primary_unit_rustc: None,
114            rustfix_diagnostic_server: Rc::new(RefCell::new(None)),
115            export_dir: None,
116            future_incompat_report: false,
117            timing_outputs: Vec::new(),
118        })
119    }
120
121    /// Whether or not the *user* wants JSON output. Whether or not rustc
122    /// actually uses JSON is decided in `add_error_format`.
123    pub fn emit_json(&self) -> bool {
124        matches!(self.message_format, MessageFormat::Json { .. })
125    }
126
127    pub fn test(&self) -> bool {
128        self.mode == CompileMode::Test || self.mode == CompileMode::Bench
129    }
130
131    pub fn single_requested_kind(&self) -> CargoResult<CompileKind> {
132        match self.requested_kinds.len() {
133            1 => Ok(self.requested_kinds[0]),
134            _ => bail!("only one `--target` argument is supported"),
135        }
136    }
137}
138
139#[derive(Clone, Copy, Debug, PartialEq, Eq)]
140pub enum MessageFormat {
141    Human,
142    Json {
143        /// Whether rustc diagnostics are rendered by cargo or included into the
144        /// output stream.
145        render_diagnostics: bool,
146        /// Whether the `rendered` field of rustc diagnostics are using the
147        /// "short" rendering.
148        short: bool,
149        /// Whether the `rendered` field of rustc diagnostics embed ansi color
150        /// codes.
151        ansi: bool,
152    },
153    Short,
154}
155
156/// The general "mode" for what to do.
157///
158/// This is used for two purposes. The commands themselves pass this in to
159/// `compile_ws` to tell it the general execution strategy. This influences
160/// the default targets selected. The other use is in the `Unit` struct
161/// to indicate what is being done with a specific target.
162#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash, PartialOrd, Ord)]
163pub enum CompileMode {
164    /// A target being built for a test.
165    Test,
166    /// Building a target with `rustc` (lib or bin).
167    Build,
168    /// Building a target with `rustc` to emit `rmeta` metadata only. If
169    /// `test` is true, then it is also compiled with `--test` to check it like
170    /// a test.
171    Check { test: bool },
172    /// Used to indicate benchmarks should be built. This is not used in
173    /// `Unit`, because it is essentially the same as `Test` (indicating
174    /// `--test` should be passed to rustc) and by using `Test` instead it
175    /// allows some de-duping of Units to occur.
176    Bench,
177    /// A target that will be documented with `rustdoc`.
178
179    /// If `deps` is true, then it will also document all dependencies.
180    /// if `json` is true, the documentation output is in json format.
181    Doc { deps: bool, json: bool },
182    /// A target that will be tested with `rustdoc`.
183    Doctest,
184    /// An example or library that will be scraped for function calls by `rustdoc`.
185    Docscrape,
186    /// A marker for Units that represent the execution of a `build.rs` script.
187    RunCustomBuild,
188}
189
190impl ser::Serialize for CompileMode {
191    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
192    where
193        S: ser::Serializer,
194    {
195        use self::CompileMode::*;
196        match *self {
197            Test => "test".serialize(s),
198            Build => "build".serialize(s),
199            Check { .. } => "check".serialize(s),
200            Bench => "bench".serialize(s),
201            Doc { .. } => "doc".serialize(s),
202            Doctest => "doctest".serialize(s),
203            Docscrape => "docscrape".serialize(s),
204            RunCustomBuild => "run-custom-build".serialize(s),
205        }
206    }
207}
208
209impl CompileMode {
210    /// Returns `true` if the unit is being checked.
211    pub fn is_check(self) -> bool {
212        matches!(self, CompileMode::Check { .. })
213    }
214
215    /// Returns `true` if this is generating documentation.
216    pub fn is_doc(self) -> bool {
217        matches!(self, CompileMode::Doc { .. })
218    }
219
220    /// Returns `true` if this a doc test.
221    pub fn is_doc_test(self) -> bool {
222        self == CompileMode::Doctest
223    }
224
225    /// Returns `true` if this is scraping examples for documentation.
226    pub fn is_doc_scrape(self) -> bool {
227        self == CompileMode::Docscrape
228    }
229
230    /// Returns `true` if this is any type of test (test, benchmark, doc test, or
231    /// check test).
232    pub fn is_any_test(self) -> bool {
233        matches!(
234            self,
235            CompileMode::Test
236                | CompileMode::Bench
237                | CompileMode::Check { test: true }
238                | CompileMode::Doctest
239        )
240    }
241
242    /// Returns `true` if this is something that passes `--test` to rustc.
243    pub fn is_rustc_test(self) -> bool {
244        matches!(
245            self,
246            CompileMode::Test | CompileMode::Bench | CompileMode::Check { test: true }
247        )
248    }
249
250    /// Returns `true` if this is the *execution* of a `build.rs` script.
251    pub fn is_run_custom_build(self) -> bool {
252        self == CompileMode::RunCustomBuild
253    }
254
255    /// Returns `true` if this mode may generate an executable.
256    ///
257    /// Note that this also returns `true` for building libraries, so you also
258    /// have to check the target.
259    pub fn generates_executable(self) -> bool {
260        matches!(
261            self,
262            CompileMode::Test | CompileMode::Bench | CompileMode::Build
263        )
264    }
265}
266
267/// Kinds of build timings we can output.
268#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash, PartialOrd, Ord)]
269pub enum TimingOutput {
270    /// Human-readable HTML report
271    Html,
272    /// Machine-readable JSON (unstable)
273    Json,
274}