#
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    /// Output SBOM precursor files.
    52    pub sbom: 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    /// Parses all config files to learn about build configuration. Currently
    63    /// configured options are:
    64    ///
    65    /// * `build.jobs`
    66    /// * `build.target`
    67    /// * `target.$target.ar`
    68    /// * `target.$target.linker`
    69    /// * `target.$target.libfoo.metadata`
    70    pub fn new(
    71        gctx: &GlobalContext,
    72        jobs: Option<JobsConfig>,
    73        keep_going: bool,
    74        requested_targets: &[String],
    75        mode: CompileMode,
    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!(
    98			    format!("could not parse `{j}`. Number of parallel jobs should be `default` or a number."))
    99                    }
    100                },
    101            },
    102        };
    103
    104        // If sbom flag is set, it requires the unstable feature
    105        let sbom = match (cfg.sbom, gctx.cli_unstable().sbom) {
    106            (Some(sbom), true) => sbom,
    107            (Some(_), false) => {
    108                gctx.shell()
    109                    .warn("ignoring 'sbom' config, pass `-Zsbom` to enable it")?;
    110                false
    111            }
    112            (None, _) => false,
    113        };
    114
    115        Ok(BuildConfig {
    116            requested_kinds,
    117            jobs,
    118            keep_going,
    119            requested_profile: InternedString::new("dev"),
    120            mode,
    121            message_format: MessageFormat::Human,
    122            force_rebuild: false,
    123            build_plan: 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        })
    133    }
    134
    135    /// Whether or not the *user* wants JSON output. Whether or not rustc
    136    /// actually uses JSON is decided in `add_error_format`.
    137    pub fn emit_json(&self) -> bool {
    138        matches!(self.message_format, MessageFormat::Json { .. })
    139    }
    140
    141    pub fn test(&self) -> bool {
    142        self.mode == CompileMode::Test || self.mode == CompileMode::Bench
    143    }
    144
    145    pub fn single_requested_kind(&self) -> CargoResult<CompileKind> {
    146        match self.requested_kinds.len() {
    147            1 => Ok(self.requested_kinds[0]),
    148            _ => bail!("only one `--target` argument is supported"),
    149        }
    150    }
    151}
    152
    153#[derive(Clone, Copy, Debug, PartialEq, Eq)]
    154pub enum MessageFormat {
    155    Human,
    156    Json {
    157        /// Whether rustc diagnostics are rendered by cargo or included into the
    158        /// output stream.
    159        render_diagnostics: bool,
    160        /// Whether the `rendered` field of rustc diagnostics are using the
    161        /// "short" rendering.
    162        short: bool,
    163        /// Whether the `rendered` field of rustc diagnostics embed ansi color
    164        /// codes.
    165        ansi: bool,
    166    },
    167    Short,
    168}
    169
    170/// The general "mode" for what to do.
    171///
    172/// This is used for two purposes. The commands themselves pass this in to
    173/// `compile_ws` to tell it the general execution strategy. This influences
    174/// the default targets selected. The other use is in the `Unit` struct
    175/// to indicate what is being done with a specific target.
    176#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash, PartialOrd, Ord)]
    177pub enum CompileMode {
    178    /// A target being built for a test.
    179    Test,
    180    /// Building a target with `rustc` (lib or bin).
    181    Build,
    182    /// Building a target with `rustc` to emit `rmeta` metadata only. If
    183    /// `test` is true, then it is also compiled with `--test` to check it like
    184    /// a test.
    185    Check { test: bool },
    186    /// Used to indicate benchmarks should be built. This is not used in
    187    /// `Unit`, because it is essentially the same as `Test` (indicating
    188    /// `--test` should be passed to rustc) and by using `Test` instead it
    189    /// allows some de-duping of Units to occur.
    190    Bench,
    191    /// A target that will be documented with `rustdoc`.
    192
    193    /// If `deps` is true, then it will also document all dependencies.
    194    /// if `json` is true, the documentation output is in json format.
    195    Doc { deps: bool, json: bool },
    196    /// A target that will be tested with `rustdoc`.
    197    Doctest,
    198    /// An example or library that will be scraped for function calls by `rustdoc`.
    199    Docscrape,
    200    /// A marker for Units that represent the execution of a `build.rs` script.
    201    RunCustomBuild,
    202}
    203
    204impl ser::Serialize for CompileMode {
    205    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
    206    where
    207        S: ser::Serializer,
    208    {
    209        use self::CompileMode::*;
    210        match *self {
    211            Test => "test".serialize(s),
    212            Build => "build".serialize(s),
    213            Check { .. } => "check".serialize(s),
    214            Bench => "bench".serialize(s),
    215            Doc { .. } => "doc".serialize(s),
    216            Doctest => "doctest".serialize(s),
    217            Docscrape => "docscrape".serialize(s),
    218            RunCustomBuild => "run-custom-build".serialize(s),
    219        }
    220    }
    221}
    222
    223impl CompileMode {
    224    /// Returns `true` if the unit is being checked.
    225    pub fn is_check(self) -> bool {
    226        matches!(self, CompileMode::Check { .. })
    227    }
    228
    229    /// Returns `true` if this is generating documentation.
    230    pub fn is_doc(self) -> bool {
    231        matches!(self, CompileMode::Doc { .. })
    232    }
    233
    234    /// Returns `true` if this a doc test.
    235    pub fn is_doc_test(self) -> bool {
    236        self == CompileMode::Doctest
    237    }
    238
    239    /// Returns `true` if this is scraping examples for documentation.
    240    pub fn is_doc_scrape(self) -> bool {
    241        self == CompileMode::Docscrape
    242    }
    243
    244    /// Returns `true` if this is any type of test (test, benchmark, doc test, or
    245    /// check test).
    246    pub fn is_any_test(self) -> bool {
    247        matches!(
    248            self,
    249            CompileMode::Test
    250                | CompileMode::Bench
    251                | CompileMode::Check { test: true }
    252                | CompileMode::Doctest
    253        )
    254    }
    255
    256    /// Returns `true` if this is something that passes `--test` to rustc.
    257    pub fn is_rustc_test(self) -> bool {
    258        matches!(
    259            self,
    260            CompileMode::Test | CompileMode::Bench | CompileMode::Check { test: true }
    261        )
    262    }
    263
    264    /// Returns `true` if this is the *execution* of a `build.rs` script.
    265    pub fn is_run_custom_build(self) -> bool {
    266        self == CompileMode::RunCustomBuild
    267    }
    268
    269    /// Returns `true` if this mode may generate an executable.
    270    ///
    271    /// Note that this also returns `true` for building libraries, so you also
    272    /// have to check the target.
    273    pub fn generates_executable(self) -> bool {
    274        matches!(
    275            self,
    276            CompileMode::Test | CompileMode::Bench | CompileMode::Build
    277        )
    278    }
    279}
    280
    281/// Kinds of build timings we can output.
    282#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash, PartialOrd, Ord)]
    283pub enum TimingOutput {
    284    /// Human-readable HTML report
    285    Html,
    286    /// Machine-readable JSON (unstable)
    287    Json,
    288}