1 use crate::core::compiler::CompileKind; 2 use crate::util::interning::InternedString; 3 use crate::util::{CargoResult, Config, RustfixDiagnosticServer}; 4 use anyhow::bail; 5 use cargo_util::ProcessBuilder; 6 use serde::ser; 7 use std::cell::RefCell; 8 use std::path::PathBuf; 9 10 /// Configuration information for a rustc build. 11 #[derive(Debug)] 12 pub struct BuildConfig { 13 /// The requested kind of compilation for this session 14 pub requested_kinds: Vec<CompileKind>, 15 /// Number of rustc jobs to run in parallel. 16 pub jobs: u32, 17 /// Build profile 18 pub requested_profile: InternedString, 19 /// The mode we are compiling in. 20 pub mode: CompileMode, 21 /// `true` to print stdout in JSON format (for machine reading). 22 pub message_format: MessageFormat, 23 /// Force Cargo to do a full rebuild and treat each target as changed. 24 pub force_rebuild: bool, 25 /// Output a build plan to stdout instead of actually compiling. 26 pub build_plan: bool, 27 /// Output the unit graph to stdout instead of actually compiling. 28 pub unit_graph: bool, 29 /// An optional override of the rustc process for primary units 30 pub primary_unit_rustc: Option<ProcessBuilder>, 31 /// A thread used by `cargo fix` to receive messages on a socket regarding 32 /// the success/failure of applying fixes. 33 pub rustfix_diagnostic_server: RefCell<Option<RustfixDiagnosticServer>>, 34 /// The directory to copy final artifacts to. Note that even if `out_dir` is 35 /// set, a copy of artifacts still could be found a `target/(debug\release)` 36 /// as usual. 37 // Note that, although the cmd-line flag name is `out-dir`, in code we use 38 // `export_dir`, to avoid confusion with out dir at `target/debug/deps`. 39 pub export_dir: Option<PathBuf>, 40 /// `true` to output a future incompatibility report at the end of the build 41 pub future_incompat_report: bool, 42 } 43 44 impl BuildConfig { 45 /// Parses all config files to learn about build configuration. Currently 46 /// configured options are: 47 /// 48 /// * `build.jobs` 49 /// * `build.target` 50 /// * `target.$target.ar` 51 /// * `target.$target.linker` 52 /// * `target.$target.libfoo.metadata` new( config: &Config, jobs: Option<u32>, requested_targets: &[String], mode: CompileMode, ) -> CargoResult<BuildConfig>53 pub fn new( 54 config: &Config, 55 jobs: Option<u32>, 56 requested_targets: &[String], 57 mode: CompileMode, 58 ) -> CargoResult<BuildConfig> { 59 let cfg = config.build_config()?; 60 let requested_kinds = CompileKind::from_requested_targets(config, requested_targets)?; 61 if jobs == Some(0) { 62 anyhow::bail!("jobs must be at least 1") 63 } 64 if jobs.is_some() && config.jobserver_from_env().is_some() { 65 config.shell().warn( 66 "a `-j` argument was passed to Cargo but Cargo is \ 67 also configured with an external jobserver in \ 68 its environment, ignoring the `-j` parameter", 69 )?; 70 } 71 let jobs = jobs.or(cfg.jobs).unwrap_or(::num_cpus::get() as u32); 72 73 Ok(BuildConfig { 74 requested_kinds, 75 jobs, 76 requested_profile: InternedString::new("dev"), 77 mode, 78 message_format: MessageFormat::Human, 79 force_rebuild: false, 80 build_plan: false, 81 unit_graph: false, 82 primary_unit_rustc: None, 83 rustfix_diagnostic_server: RefCell::new(None), 84 export_dir: None, 85 future_incompat_report: false, 86 }) 87 } 88 89 /// Whether or not the *user* wants JSON output. Whether or not rustc 90 /// actually uses JSON is decided in `add_error_format`. emit_json(&self) -> bool91 pub fn emit_json(&self) -> bool { 92 matches!(self.message_format, MessageFormat::Json { .. }) 93 } 94 test(&self) -> bool95 pub fn test(&self) -> bool { 96 self.mode == CompileMode::Test || self.mode == CompileMode::Bench 97 } 98 single_requested_kind(&self) -> CargoResult<CompileKind>99 pub fn single_requested_kind(&self) -> CargoResult<CompileKind> { 100 match self.requested_kinds.len() { 101 1 => Ok(self.requested_kinds[0]), 102 _ => bail!("only one `--target` argument is supported"), 103 } 104 } 105 } 106 107 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 108 pub enum MessageFormat { 109 Human, 110 Json { 111 /// Whether rustc diagnostics are rendered by cargo or included into the 112 /// output stream. 113 render_diagnostics: bool, 114 /// Whether the `rendered` field of rustc diagnostics are using the 115 /// "short" rendering. 116 short: bool, 117 /// Whether the `rendered` field of rustc diagnostics embed ansi color 118 /// codes. 119 ansi: bool, 120 }, 121 Short, 122 } 123 124 /// The general "mode" for what to do. 125 /// This is used for two purposes. The commands themselves pass this in to 126 /// `compile_ws` to tell it the general execution strategy. This influences 127 /// the default targets selected. The other use is in the `Unit` struct 128 /// to indicate what is being done with a specific target. 129 #[derive(Clone, Copy, PartialEq, Debug, Eq, Hash, PartialOrd, Ord)] 130 pub enum CompileMode { 131 /// A target being built for a test. 132 Test, 133 /// Building a target with `rustc` (lib or bin). 134 Build, 135 /// Building a target with `rustc` to emit `rmeta` metadata only. If 136 /// `test` is true, then it is also compiled with `--test` to check it like 137 /// a test. 138 Check { test: bool }, 139 /// Used to indicate benchmarks should be built. This is not used in 140 /// `Unit`, because it is essentially the same as `Test` (indicating 141 /// `--test` should be passed to rustc) and by using `Test` instead it 142 /// allows some de-duping of Units to occur. 143 Bench, 144 /// A target that will be documented with `rustdoc`. 145 /// If `deps` is true, then it will also document all dependencies. 146 Doc { deps: bool }, 147 /// A target that will be tested with `rustdoc`. 148 Doctest, 149 /// A marker for Units that represent the execution of a `build.rs` script. 150 RunCustomBuild, 151 } 152 153 impl ser::Serialize for CompileMode { serialize<S>(&self, s: S) -> Result<S::Ok, S::Error> where S: ser::Serializer,154 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error> 155 where 156 S: ser::Serializer, 157 { 158 use self::CompileMode::*; 159 match *self { 160 Test => "test".serialize(s), 161 Build => "build".serialize(s), 162 Check { .. } => "check".serialize(s), 163 Bench => "bench".serialize(s), 164 Doc { .. } => "doc".serialize(s), 165 Doctest => "doctest".serialize(s), 166 RunCustomBuild => "run-custom-build".serialize(s), 167 } 168 } 169 } 170 171 impl CompileMode { 172 /// Returns `true` if the unit is being checked. is_check(self) -> bool173 pub fn is_check(self) -> bool { 174 matches!(self, CompileMode::Check { .. }) 175 } 176 177 /// Returns `true` if this is generating documentation. is_doc(self) -> bool178 pub fn is_doc(self) -> bool { 179 matches!(self, CompileMode::Doc { .. }) 180 } 181 182 /// Returns `true` if this a doc test. is_doc_test(self) -> bool183 pub fn is_doc_test(self) -> bool { 184 self == CompileMode::Doctest 185 } 186 187 /// Returns `true` if this is any type of test (test, benchmark, doc test, or 188 /// check test). is_any_test(self) -> bool189 pub fn is_any_test(self) -> bool { 190 matches!( 191 self, 192 CompileMode::Test 193 | CompileMode::Bench 194 | CompileMode::Check { test: true } 195 | CompileMode::Doctest 196 ) 197 } 198 199 /// Returns `true` if this is something that passes `--test` to rustc. is_rustc_test(self) -> bool200 pub fn is_rustc_test(self) -> bool { 201 matches!( 202 self, 203 CompileMode::Test | CompileMode::Bench | CompileMode::Check { test: true } 204 ) 205 } 206 207 /// Returns `true` if this is the *execution* of a `build.rs` script. is_run_custom_build(self) -> bool208 pub fn is_run_custom_build(self) -> bool { 209 self == CompileMode::RunCustomBuild 210 } 211 } 212