1 mod build_config;
2 mod build_context;
3 mod build_plan;
4 mod compilation;
5 mod compile_kind;
6 mod context;
7 mod crate_type;
8 mod custom_build;
9 mod fingerprint;
10 pub mod future_incompat;
11 mod job;
12 mod job_queue;
13 mod layout;
14 mod links;
15 mod lto;
16 mod output_depinfo;
17 pub mod rustdoc;
18 pub mod standard_lib;
19 mod timings;
20 mod unit;
21 pub mod unit_dependencies;
22 pub mod unit_graph;
23 
24 use std::env;
25 use std::ffi::{OsStr, OsString};
26 use std::fs::{self, File};
27 use std::io::{BufRead, Write};
28 use std::path::{Path, PathBuf};
29 use std::sync::Arc;
30 
31 use anyhow::{Context as _, Error};
32 use lazycell::LazyCell;
33 use log::debug;
34 
35 pub use self::build_config::{BuildConfig, CompileMode, MessageFormat};
36 pub use self::build_context::{
37     BuildContext, FileFlavor, FileType, RustDocFingerprint, RustcTargetData, TargetInfo,
38 };
39 use self::build_plan::BuildPlan;
40 pub use self::compilation::{Compilation, Doctest, UnitOutput};
41 pub use self::compile_kind::{CompileKind, CompileTarget};
42 pub use self::context::{Context, Metadata};
43 pub use self::crate_type::CrateType;
44 pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts};
45 pub use self::job::Freshness;
46 use self::job::{Job, Work};
47 use self::job_queue::{JobQueue, JobState};
48 pub(crate) use self::layout::Layout;
49 pub use self::lto::Lto;
50 use self::output_depinfo::output_depinfo;
51 use self::unit_graph::UnitDep;
52 use crate::core::compiler::future_incompat::FutureIncompatReport;
53 pub use crate::core::compiler::unit::{Unit, UnitInterner};
54 use crate::core::manifest::TargetSourcePath;
55 use crate::core::profiles::{PanicStrategy, Profile, Strip};
56 use crate::core::{Feature, PackageId, Target};
57 use crate::util::errors::{CargoResult, VerboseError};
58 use crate::util::interning::InternedString;
59 use crate::util::machine_message::{self, Message};
60 use crate::util::{add_path_args, internal, iter_join_onto, profile};
61 use cargo_util::{paths, ProcessBuilder, ProcessError};
62 
63 const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version";
64 
65 #[derive(Clone, Hash, Debug, PartialEq, Eq)]
66 pub enum LinkType {
67     All,
68     Cdylib,
69     Bin,
70     SingleBin(String),
71     Test,
72     Bench,
73     Example,
74 }
75 
76 impl LinkType {
applies_to(&self, target: &Target) -> bool77     pub fn applies_to(&self, target: &Target) -> bool {
78         match self {
79             LinkType::All => true,
80             LinkType::Cdylib => target.is_cdylib(),
81             LinkType::Bin => target.is_bin(),
82             LinkType::SingleBin(name) => target.is_bin() && target.name() == name,
83             LinkType::Test => target.is_test(),
84             LinkType::Bench => target.is_bench(),
85             LinkType::Example => target.is_exe_example(),
86         }
87     }
88 }
89 
90 /// A glorified callback for executing calls to rustc. Rather than calling rustc
91 /// directly, we'll use an `Executor`, giving clients an opportunity to intercept
92 /// the build calls.
93 pub trait Executor: Send + Sync + 'static {
94     /// Called after a rustc process invocation is prepared up-front for a given
95     /// unit of work (may still be modified for runtime-known dependencies, when
96     /// the work is actually executed).
init(&self, _cx: &Context<'_, '_>, _unit: &Unit)97     fn init(&self, _cx: &Context<'_, '_>, _unit: &Unit) {}
98 
99     /// In case of an `Err`, Cargo will not continue with the build process for
100     /// this package.
exec( &self, cmd: &ProcessBuilder, id: PackageId, target: &Target, mode: CompileMode, on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>, on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>, ) -> CargoResult<()>101     fn exec(
102         &self,
103         cmd: &ProcessBuilder,
104         id: PackageId,
105         target: &Target,
106         mode: CompileMode,
107         on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
108         on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
109     ) -> CargoResult<()>;
110 
111     /// Queried when queuing each unit of work. If it returns true, then the
112     /// unit will always be rebuilt, independent of whether it needs to be.
force_rebuild(&self, _unit: &Unit) -> bool113     fn force_rebuild(&self, _unit: &Unit) -> bool {
114         false
115     }
116 }
117 
118 /// A `DefaultExecutor` calls rustc without doing anything else. It is Cargo's
119 /// default behaviour.
120 #[derive(Copy, Clone)]
121 pub struct DefaultExecutor;
122 
123 impl Executor for DefaultExecutor {
exec( &self, cmd: &ProcessBuilder, _id: PackageId, _target: &Target, _mode: CompileMode, on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>, on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>, ) -> CargoResult<()>124     fn exec(
125         &self,
126         cmd: &ProcessBuilder,
127         _id: PackageId,
128         _target: &Target,
129         _mode: CompileMode,
130         on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
131         on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
132     ) -> CargoResult<()> {
133         cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
134             .map(drop)
135     }
136 }
137 
compile<'cfg>( cx: &mut Context<'_, 'cfg>, jobs: &mut JobQueue<'cfg>, plan: &mut BuildPlan, unit: &Unit, exec: &Arc<dyn Executor>, force_rebuild: bool, ) -> CargoResult<()>138 fn compile<'cfg>(
139     cx: &mut Context<'_, 'cfg>,
140     jobs: &mut JobQueue<'cfg>,
141     plan: &mut BuildPlan,
142     unit: &Unit,
143     exec: &Arc<dyn Executor>,
144     force_rebuild: bool,
145 ) -> CargoResult<()> {
146     let bcx = cx.bcx;
147     let build_plan = bcx.build_config.build_plan;
148     if !cx.compiled.insert(unit.clone()) {
149         return Ok(());
150     }
151 
152     // Build up the work to be done to compile this unit, enqueuing it once
153     // we've got everything constructed.
154     let p = profile::start(format!("preparing: {}/{}", unit.pkg, unit.target.name()));
155     fingerprint::prepare_init(cx, unit)?;
156 
157     let job = if unit.mode.is_run_custom_build() {
158         custom_build::prepare(cx, unit)?
159     } else if unit.mode.is_doc_test() {
160         // We run these targets later, so this is just a no-op for now.
161         Job::new_fresh()
162     } else if build_plan {
163         Job::new_dirty(rustc(cx, unit, &exec.clone())?)
164     } else {
165         let force = exec.force_rebuild(unit) || force_rebuild;
166         let mut job = fingerprint::prepare_target(cx, unit, force)?;
167         job.before(if job.freshness() == Freshness::Dirty {
168             let work = if unit.mode.is_doc() {
169                 rustdoc(cx, unit)?
170             } else {
171                 rustc(cx, unit, exec)?
172             };
173             work.then(link_targets(cx, unit, false)?)
174         } else {
175             // We always replay the output cache,
176             // since it might contain future-incompat-report messages
177             let work = replay_output_cache(
178                 unit.pkg.package_id(),
179                 PathBuf::from(unit.pkg.manifest_path()),
180                 &unit.target,
181                 cx.files().message_cache_path(unit),
182                 cx.bcx.build_config.message_format,
183                 cx.bcx.config.shell().err_supports_color(),
184                 unit.show_warnings(bcx.config),
185             );
186             // Need to link targets on both the dirty and fresh.
187             work.then(link_targets(cx, unit, true)?)
188         });
189 
190         job
191     };
192     jobs.enqueue(cx, unit, job)?;
193     drop(p);
194 
195     // Be sure to compile all dependencies of this target as well.
196     let deps = Vec::from(cx.unit_deps(unit)); // Create vec due to mutable borrow.
197     for dep in deps {
198         compile(cx, jobs, plan, &dep.unit, exec, false)?;
199     }
200     if build_plan {
201         plan.add(cx, unit)?;
202     }
203 
204     Ok(())
205 }
206 
rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> CargoResult<Work>207 fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> CargoResult<Work> {
208     let mut rustc = prepare_rustc(cx, &unit.target.rustc_crate_types(), unit)?;
209     let build_plan = cx.bcx.build_config.build_plan;
210 
211     let name = unit.pkg.name().to_string();
212     let buildkey = unit.buildkey();
213 
214     add_cap_lints(cx.bcx, unit, &mut rustc);
215 
216     let outputs = cx.outputs(unit)?;
217     let root = cx.files().out_dir(unit);
218 
219     // Prepare the native lib state (extra `-L` and `-l` flags).
220     let build_script_outputs = Arc::clone(&cx.build_script_outputs);
221     let current_id = unit.pkg.package_id();
222     let manifest_path = PathBuf::from(unit.pkg.manifest_path());
223     let build_scripts = cx.build_scripts.get(unit).cloned();
224 
225     // If we are a binary and the package also contains a library, then we
226     // don't pass the `-l` flags.
227     let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib());
228 
229     let dep_info_name = if cx.files().use_extra_filename(unit) {
230         format!(
231             "{}-{}.d",
232             unit.target.crate_name(),
233             cx.files().metadata(unit)
234         )
235     } else {
236         format!("{}.d", unit.target.crate_name())
237     };
238     let rustc_dep_info_loc = root.join(dep_info_name);
239     let dep_info_loc = fingerprint::dep_info_loc(cx, unit);
240 
241     rustc.args(cx.bcx.rustflags_args(unit));
242     if cx.bcx.config.cli_unstable().binary_dep_depinfo {
243         rustc.arg("-Z").arg("binary-dep-depinfo");
244     }
245     let mut output_options = OutputOptions::new(cx, unit);
246     let package_id = unit.pkg.package_id();
247     let target = Target::clone(&unit.target);
248     let mode = unit.mode;
249 
250     exec.init(cx, unit);
251     let exec = exec.clone();
252 
253     let root_output = cx.files().host_dest().to_path_buf();
254     let target_dir = cx.bcx.ws.target_dir().into_path_unlocked();
255     let pkg_root = unit.pkg.root().to_path_buf();
256     let cwd = rustc
257         .get_cwd()
258         .unwrap_or_else(|| cx.bcx.config.cwd())
259         .to_path_buf();
260     let fingerprint_dir = cx.files().fingerprint_dir(unit);
261     let script_metadata = cx.find_build_script_metadata(unit);
262     let is_local = unit.is_local();
263 
264     return Ok(Work::new(move |state| {
265         // Only at runtime have we discovered what the extra -L and -l
266         // arguments are for native libraries, so we process those here. We
267         // also need to be sure to add any -L paths for our plugins to the
268         // dynamic library load path as a plugin's dynamic library may be
269         // located somewhere in there.
270         // Finally, if custom environment variables have been produced by
271         // previous build scripts, we include them in the rustc invocation.
272         if let Some(build_scripts) = build_scripts {
273             let script_outputs = build_script_outputs.lock().unwrap();
274             if !build_plan {
275                 add_native_deps(
276                     &mut rustc,
277                     &script_outputs,
278                     &build_scripts,
279                     pass_l_flag,
280                     &target,
281                     current_id,
282                 )?;
283                 add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?;
284             }
285             add_custom_env(&mut rustc, &script_outputs, script_metadata);
286         }
287 
288         for output in outputs.iter() {
289             // If there is both an rmeta and rlib, rustc will prefer to use the
290             // rlib, even if it is older. Therefore, we must delete the rlib to
291             // force using the new rmeta.
292             if output.path.extension() == Some(OsStr::new("rmeta")) {
293                 let dst = root.join(&output.path).with_extension("rlib");
294                 if dst.exists() {
295                     paths::remove_file(&dst)?;
296                 }
297             }
298         }
299 
300         fn verbose_if_simple_exit_code(err: Error) -> Error {
301             // If a signal on unix (`code == None`) or an abnormal termination
302             // on Windows (codes like `0xC0000409`), don't hide the error details.
303             match err
304                 .downcast_ref::<ProcessError>()
305                 .as_ref()
306                 .and_then(|perr| perr.code)
307             {
308                 Some(n) if cargo_util::is_simple_exit_code(n) => VerboseError::new(err).into(),
309                 _ => err,
310             }
311         }
312 
313         state.running(&rustc);
314         let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
315         if build_plan {
316             state.build_plan(buildkey, rustc.clone(), outputs.clone());
317         } else {
318             exec.exec(
319                 &rustc,
320                 package_id,
321                 &target,
322                 mode,
323                 &mut |line| on_stdout_line(state, line, package_id, &target),
324                 &mut |line| {
325                     on_stderr_line(
326                         state,
327                         line,
328                         package_id,
329                         &manifest_path,
330                         &target,
331                         &mut output_options,
332                     )
333                 },
334             )
335             .map_err(verbose_if_simple_exit_code)
336             .with_context(|| format!("could not compile `{}`", name))?;
337         }
338 
339         if rustc_dep_info_loc.exists() {
340             fingerprint::translate_dep_info(
341                 &rustc_dep_info_loc,
342                 &dep_info_loc,
343                 &cwd,
344                 &pkg_root,
345                 &target_dir,
346                 &rustc,
347                 // Do not track source files in the fingerprint for registry dependencies.
348                 is_local,
349             )
350             .with_context(|| {
351                 internal(format!(
352                     "could not parse/generate dep info at: {}",
353                     rustc_dep_info_loc.display()
354                 ))
355             })?;
356             // This mtime shift allows Cargo to detect if a source file was
357             // modified in the middle of the build.
358             paths::set_file_time_no_err(dep_info_loc, timestamp);
359         }
360 
361         Ok(())
362     }));
363 
364     // Add all relevant `-L` and `-l` flags from dependencies (now calculated and
365     // present in `state`) to the command provided.
366     fn add_native_deps(
367         rustc: &mut ProcessBuilder,
368         build_script_outputs: &BuildScriptOutputs,
369         build_scripts: &BuildScripts,
370         pass_l_flag: bool,
371         target: &Target,
372         current_id: PackageId,
373     ) -> CargoResult<()> {
374         for key in build_scripts.to_link.iter() {
375             let output = build_script_outputs.get(key.1).ok_or_else(|| {
376                 internal(format!(
377                     "couldn't find build script output for {}/{}",
378                     key.0, key.1
379                 ))
380             })?;
381             for path in output.library_paths.iter() {
382                 rustc.arg("-L").arg(path);
383             }
384 
385             if key.0 == current_id {
386                 for cfg in &output.cfgs {
387                     rustc.arg("--cfg").arg(cfg);
388                 }
389                 if pass_l_flag {
390                     for name in output.library_links.iter() {
391                         rustc.arg("-l").arg(name);
392                     }
393                 }
394             }
395 
396             for (lt, arg) in &output.linker_args {
397                 // There was an unintentional change where cdylibs were
398                 // allowed to be passed via transitive dependencies. This
399                 // clause should have been kept in the `if` block above. For
400                 // now, continue allowing it for cdylib only.
401                 // See https://github.com/rust-lang/cargo/issues/9562
402                 if lt.applies_to(target) && (key.0 == current_id || *lt == LinkType::Cdylib) {
403                     rustc.arg("-C").arg(format!("link-arg={}", arg));
404                 }
405             }
406         }
407         Ok(())
408     }
409 
410     // Add all custom environment variables present in `state` (after they've
411     // been put there by one of the `build_scripts`) to the command provided.
412     fn add_custom_env(
413         rustc: &mut ProcessBuilder,
414         build_script_outputs: &BuildScriptOutputs,
415         metadata: Option<Metadata>,
416     ) {
417         if let Some(metadata) = metadata {
418             if let Some(output) = build_script_outputs.get(metadata) {
419                 for &(ref name, ref value) in output.env.iter() {
420                     rustc.env(name, value);
421                 }
422             }
423         }
424     }
425 }
426 
427 /// Link the compiled target (often of form `foo-{metadata_hash}`) to the
428 /// final target. This must happen during both "Fresh" and "Compile".
link_targets(cx: &mut Context<'_, '_>, unit: &Unit, fresh: bool) -> CargoResult<Work>429 fn link_targets(cx: &mut Context<'_, '_>, unit: &Unit, fresh: bool) -> CargoResult<Work> {
430     let bcx = cx.bcx;
431     let outputs = cx.outputs(unit)?;
432     let export_dir = cx.files().export_dir();
433     let package_id = unit.pkg.package_id();
434     let manifest_path = PathBuf::from(unit.pkg.manifest_path());
435     let profile = unit.profile;
436     let unit_mode = unit.mode;
437     let features = unit.features.iter().map(|s| s.to_string()).collect();
438     let json_messages = bcx.build_config.emit_json();
439     let executable = cx.get_executable(unit)?;
440     let mut target = Target::clone(&unit.target);
441     if let TargetSourcePath::Metabuild = target.src_path() {
442         // Give it something to serialize.
443         let path = unit.pkg.manifest().metabuild_path(cx.bcx.ws.target_dir());
444         target.set_src_path(TargetSourcePath::Path(path));
445     }
446 
447     Ok(Work::new(move |state| {
448         // If we're a "root crate", e.g., the target of this compilation, then we
449         // hard link our outputs out of the `deps` directory into the directory
450         // above. This means that `cargo build` will produce binaries in
451         // `target/debug` which one probably expects.
452         let mut destinations = vec![];
453         for output in outputs.iter() {
454             let src = &output.path;
455             // This may have been a `cargo rustc` command which changes the
456             // output, so the source may not actually exist.
457             if !src.exists() {
458                 continue;
459             }
460             let dst = match output.hardlink.as_ref() {
461                 Some(dst) => dst,
462                 None => {
463                     destinations.push(src.clone());
464                     continue;
465                 }
466             };
467             destinations.push(dst.clone());
468             paths::link_or_copy(src, dst)?;
469             if let Some(ref path) = output.export_path {
470                 let export_dir = export_dir.as_ref().unwrap();
471                 paths::create_dir_all(export_dir)?;
472 
473                 paths::link_or_copy(src, path)?;
474             }
475         }
476 
477         if json_messages {
478             let art_profile = machine_message::ArtifactProfile {
479                 opt_level: profile.opt_level.as_str(),
480                 debuginfo: profile.debuginfo,
481                 debug_assertions: profile.debug_assertions,
482                 overflow_checks: profile.overflow_checks,
483                 test: unit_mode.is_any_test(),
484             };
485 
486             let msg = machine_message::Artifact {
487                 package_id,
488                 manifest_path,
489                 target: &target,
490                 profile: art_profile,
491                 features,
492                 filenames: destinations,
493                 executable,
494                 fresh,
495             }
496             .to_json_string();
497             state.stdout(msg)?;
498         }
499         Ok(())
500     }))
501 }
502 
503 // For all plugin dependencies, add their -L paths (now calculated and present
504 // in `build_script_outputs`) to the dynamic library load path for the command
505 // to execute.
add_plugin_deps( rustc: &mut ProcessBuilder, build_script_outputs: &BuildScriptOutputs, build_scripts: &BuildScripts, root_output: &Path, ) -> CargoResult<()>506 fn add_plugin_deps(
507     rustc: &mut ProcessBuilder,
508     build_script_outputs: &BuildScriptOutputs,
509     build_scripts: &BuildScripts,
510     root_output: &Path,
511 ) -> CargoResult<()> {
512     let var = paths::dylib_path_envvar();
513     let search_path = rustc.get_env(var).unwrap_or_default();
514     let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>();
515     for (pkg_id, metadata) in &build_scripts.plugins {
516         let output = build_script_outputs
517             .get(*metadata)
518             .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?;
519         search_path.append(&mut filter_dynamic_search_path(
520             output.library_paths.iter(),
521             root_output,
522         ));
523     }
524     let search_path = paths::join_paths(&search_path, var)?;
525     rustc.env(var, &search_path);
526     Ok(())
527 }
528 
529 // Determine paths to add to the dynamic search path from -L entries
530 //
531 // Strip off prefixes like "native=" or "framework=" and filter out directories
532 // **not** inside our output directory since they are likely spurious and can cause
533 // clashes with system shared libraries (issue #3366).
filter_dynamic_search_path<'a, I>(paths: I, root_output: &Path) -> Vec<PathBuf> where I: Iterator<Item = &'a PathBuf>,534 fn filter_dynamic_search_path<'a, I>(paths: I, root_output: &Path) -> Vec<PathBuf>
535 where
536     I: Iterator<Item = &'a PathBuf>,
537 {
538     let mut search_path = vec![];
539     for dir in paths {
540         let dir = match dir.to_str() {
541             Some(s) => {
542                 let mut parts = s.splitn(2, '=');
543                 match (parts.next(), parts.next()) {
544                     (Some("native"), Some(path))
545                     | (Some("crate"), Some(path))
546                     | (Some("dependency"), Some(path))
547                     | (Some("framework"), Some(path))
548                     | (Some("all"), Some(path)) => path.into(),
549                     _ => dir.clone(),
550                 }
551             }
552             None => dir.clone(),
553         };
554         if dir.starts_with(&root_output) {
555             search_path.push(dir);
556         } else {
557             debug!(
558                 "Not including path {} in runtime library search path because it is \
559                  outside target root {}",
560                 dir.display(),
561                 root_output.display()
562             );
563         }
564     }
565     search_path
566 }
567 
prepare_rustc( cx: &mut Context<'_, '_>, crate_types: &[CrateType], unit: &Unit, ) -> CargoResult<ProcessBuilder>568 fn prepare_rustc(
569     cx: &mut Context<'_, '_>,
570     crate_types: &[CrateType],
571     unit: &Unit,
572 ) -> CargoResult<ProcessBuilder> {
573     let is_primary = cx.is_primary_package(unit);
574     let is_workspace = cx.bcx.ws.is_member(&unit.pkg);
575 
576     let mut base = cx
577         .compilation
578         .rustc_process(unit, is_primary, is_workspace)?;
579 
580     if is_primary {
581         base.env("CARGO_PRIMARY_PACKAGE", "1");
582     }
583 
584     if unit.target.is_test() || unit.target.is_bench() {
585         let tmp = cx.files().layout(unit.kind).prepare_tmp()?;
586         base.env("CARGO_TARGET_TMPDIR", tmp.display().to_string());
587     }
588 
589     if cx.bcx.config.cli_unstable().jobserver_per_rustc {
590         let client = cx.new_jobserver()?;
591         base.inherit_jobserver(&client);
592         base.arg("-Z").arg("jobserver-token-requests");
593         assert!(cx.rustc_clients.insert(unit.clone(), client).is_none());
594     } else {
595         base.inherit_jobserver(&cx.jobserver);
596     }
597     build_base_args(cx, &mut base, unit, crate_types)?;
598     build_deps_args(&mut base, cx, unit)?;
599     Ok(base)
600 }
601 
rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work>602 fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> {
603     let bcx = cx.bcx;
604     // script_metadata is not needed here, it is only for tests.
605     let mut rustdoc = cx.compilation.rustdoc_process(unit, None)?;
606     rustdoc.inherit_jobserver(&cx.jobserver);
607     let crate_name = unit.target.crate_name();
608     rustdoc.arg("--crate-name").arg(&crate_name);
609     add_path_args(bcx.ws, unit, &mut rustdoc);
610     add_cap_lints(bcx, unit, &mut rustdoc);
611 
612     if let CompileKind::Target(target) = unit.kind {
613         rustdoc.arg("--target").arg(target.rustc_target());
614     }
615     let doc_dir = cx.files().out_dir(unit);
616 
617     // Create the documentation directory ahead of time as rustdoc currently has
618     // a bug where concurrent invocations will race to create this directory if
619     // it doesn't already exist.
620     paths::create_dir_all(&doc_dir)?;
621 
622     rustdoc.arg("-o").arg(&doc_dir);
623 
624     for feat in &unit.features {
625         rustdoc.arg("--cfg").arg(&format!("feature=\"{}\"", feat));
626     }
627 
628     add_error_format_and_color(cx, &mut rustdoc, false);
629     add_allow_features(cx, &mut rustdoc);
630 
631     if let Some(args) = cx.bcx.extra_args_for(unit) {
632         rustdoc.args(args);
633     }
634 
635     build_deps_args(&mut rustdoc, cx, unit)?;
636     rustdoc::add_root_urls(cx, unit, &mut rustdoc)?;
637 
638     rustdoc.args(bcx.rustdocflags_args(unit));
639 
640     if !crate_version_flag_already_present(&rustdoc) {
641         append_crate_version_flag(unit, &mut rustdoc);
642     }
643 
644     let name = unit.pkg.name().to_string();
645     let build_script_outputs = Arc::clone(&cx.build_script_outputs);
646     let package_id = unit.pkg.package_id();
647     let manifest_path = PathBuf::from(unit.pkg.manifest_path());
648     let target = Target::clone(&unit.target);
649     let mut output_options = OutputOptions::new(cx, unit);
650     let script_metadata = cx.find_build_script_metadata(unit);
651     Ok(Work::new(move |state| {
652         if let Some(script_metadata) = script_metadata {
653             if let Some(output) = build_script_outputs.lock().unwrap().get(script_metadata) {
654                 for cfg in output.cfgs.iter() {
655                     rustdoc.arg("--cfg").arg(cfg);
656                 }
657                 for &(ref name, ref value) in output.env.iter() {
658                     rustdoc.env(name, value);
659                 }
660             }
661         }
662         let crate_dir = doc_dir.join(&crate_name);
663         if crate_dir.exists() {
664             // Remove output from a previous build. This ensures that stale
665             // files for removed items are removed.
666             log::debug!("removing pre-existing doc directory {:?}", crate_dir);
667             paths::remove_dir_all(crate_dir)?;
668         }
669         state.running(&rustdoc);
670 
671         rustdoc
672             .exec_with_streaming(
673                 &mut |line| on_stdout_line(state, line, package_id, &target),
674                 &mut |line| {
675                     on_stderr_line(
676                         state,
677                         line,
678                         package_id,
679                         &manifest_path,
680                         &target,
681                         &mut output_options,
682                     )
683                 },
684                 false,
685             )
686             .with_context(|| format!("could not document `{}`", name))?;
687         Ok(())
688     }))
689 }
690 
691 // The --crate-version flag could have already been passed in RUSTDOCFLAGS
692 // or as an extra compiler argument for rustdoc
crate_version_flag_already_present(rustdoc: &ProcessBuilder) -> bool693 fn crate_version_flag_already_present(rustdoc: &ProcessBuilder) -> bool {
694     rustdoc.get_args().iter().any(|flag| {
695         flag.to_str()
696             .map_or(false, |flag| flag.starts_with(RUSTDOC_CRATE_VERSION_FLAG))
697     })
698 }
699 
append_crate_version_flag(unit: &Unit, rustdoc: &mut ProcessBuilder)700 fn append_crate_version_flag(unit: &Unit, rustdoc: &mut ProcessBuilder) {
701     rustdoc
702         .arg(RUSTDOC_CRATE_VERSION_FLAG)
703         .arg(unit.pkg.version().to_string());
704 }
705 
add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuilder)706 fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuilder) {
707     // If this is an upstream dep we don't want warnings from, turn off all
708     // lints.
709     if !unit.show_warnings(bcx.config) {
710         cmd.arg("--cap-lints").arg("allow");
711 
712     // If this is an upstream dep but we *do* want warnings, make sure that they
713     // don't fail compilation.
714     } else if !unit.is_local() {
715         cmd.arg("--cap-lints").arg("warn");
716     }
717 }
718 
719 /// Forward -Zallow-features if it is set for cargo.
add_allow_features(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder)720 fn add_allow_features(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder) {
721     if let Some(allow) = &cx.bcx.config.cli_unstable().allow_features {
722         let mut arg = String::from("-Zallow-features=");
723         let _ = iter_join_onto(&mut arg, allow, ",");
724         cmd.arg(&arg);
725     }
726 }
727 
728 /// Add error-format flags to the command.
729 ///
730 /// Cargo always uses JSON output. This has several benefits, such as being
731 /// easier to parse, handles changing formats (for replaying cached messages),
732 /// ensures atomic output (so messages aren't interleaved), allows for
733 /// intercepting messages like rmeta artifacts, etc. rustc includes a
734 /// "rendered" field in the JSON message with the message properly formatted,
735 /// which Cargo will extract and display to the user.
add_error_format_and_color(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder, pipelined: bool)736 fn add_error_format_and_color(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder, pipelined: bool) {
737     cmd.arg("--error-format=json");
738     let mut json = String::from("--json=diagnostic-rendered-ansi");
739     if pipelined {
740         // Pipelining needs to know when rmeta files are finished. Tell rustc
741         // to emit a message that cargo will intercept.
742         json.push_str(",artifacts");
743     }
744 
745     match cx.bcx.build_config.message_format {
746         MessageFormat::Short | MessageFormat::Json { short: true, .. } => {
747             json.push_str(",diagnostic-short");
748         }
749         _ => {}
750     }
751     cmd.arg(json);
752 
753     let config = cx.bcx.config;
754     if config.nightly_features_allowed {
755         match (
756             config.cli_unstable().terminal_width,
757             config.shell().err_width().diagnostic_terminal_width(),
758         ) {
759             // Terminal width explicitly provided - only useful for testing.
760             (Some(Some(width)), _) => {
761                 cmd.arg(format!("-Zterminal-width={}", width));
762             }
763             // Terminal width was not explicitly provided but flag was provided - common case.
764             (Some(None), Some(width)) => {
765                 cmd.arg(format!("-Zterminal-width={}", width));
766             }
767             // User didn't opt-in.
768             _ => (),
769         }
770     }
771 }
772 
build_base_args( cx: &mut Context<'_, '_>, cmd: &mut ProcessBuilder, unit: &Unit, crate_types: &[CrateType], ) -> CargoResult<()>773 fn build_base_args(
774     cx: &mut Context<'_, '_>,
775     cmd: &mut ProcessBuilder,
776     unit: &Unit,
777     crate_types: &[CrateType],
778 ) -> CargoResult<()> {
779     assert!(!unit.mode.is_run_custom_build());
780 
781     let bcx = cx.bcx;
782     let Profile {
783         ref opt_level,
784         codegen_units,
785         debuginfo,
786         debug_assertions,
787         split_debuginfo,
788         overflow_checks,
789         rpath,
790         ref panic,
791         incremental,
792         strip,
793         ..
794     } = unit.profile;
795     let test = unit.mode.is_any_test();
796 
797     cmd.arg("--crate-name").arg(&unit.target.crate_name());
798 
799     let edition = unit.target.edition();
800     edition.cmd_edition_arg(cmd);
801 
802     add_path_args(bcx.ws, unit, cmd);
803     add_error_format_and_color(cx, cmd, cx.rmeta_required(unit));
804     add_allow_features(cx, cmd);
805 
806     if !test {
807         for crate_type in crate_types.iter() {
808             cmd.arg("--crate-type").arg(crate_type.as_str());
809         }
810     }
811 
812     if unit.mode.is_check() {
813         cmd.arg("--emit=dep-info,metadata");
814     } else if !unit.requires_upstream_objects() {
815         // Always produce metadata files for rlib outputs. Metadata may be used
816         // in this session for a pipelined compilation, or it may be used in a
817         // future Cargo session as part of a pipelined compile.
818         cmd.arg("--emit=dep-info,metadata,link");
819     } else {
820         cmd.arg("--emit=dep-info,link");
821     }
822 
823     let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
824         || (crate_types.contains(&CrateType::Dylib) && !cx.is_primary_package(unit));
825     if prefer_dynamic {
826         cmd.arg("-C").arg("prefer-dynamic");
827     }
828 
829     if opt_level.as_str() != "0" {
830         cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
831     }
832 
833     if *panic != PanicStrategy::Unwind {
834         cmd.arg("-C").arg(format!("panic={}", panic));
835     }
836 
837     cmd.args(&lto_args(cx, unit));
838 
839     // This is generally just an optimization on build time so if we don't pass
840     // it then it's ok. As of the time of this writing it's a very new flag, so
841     // we need to dynamically check if it's available.
842     if cx.bcx.target_data.info(unit.kind).supports_split_debuginfo {
843         if let Some(split) = split_debuginfo {
844             cmd.arg("-C").arg(format!("split-debuginfo={}", split));
845         }
846     }
847 
848     if let Some(n) = codegen_units {
849         cmd.arg("-C").arg(&format!("codegen-units={}", n));
850     }
851 
852     if let Some(debuginfo) = debuginfo {
853         cmd.arg("-C").arg(format!("debuginfo={}", debuginfo));
854     }
855 
856     if let Some(args) = cx.bcx.extra_args_for(unit) {
857         cmd.args(args);
858     }
859 
860     // `-C overflow-checks` is implied by the setting of `-C debug-assertions`,
861     // so we only need to provide `-C overflow-checks` if it differs from
862     // the value of `-C debug-assertions` we would provide.
863     if opt_level.as_str() != "0" {
864         if debug_assertions {
865             cmd.args(&["-C", "debug-assertions=on"]);
866             if !overflow_checks {
867                 cmd.args(&["-C", "overflow-checks=off"]);
868             }
869         } else if overflow_checks {
870             cmd.args(&["-C", "overflow-checks=on"]);
871         }
872     } else if !debug_assertions {
873         cmd.args(&["-C", "debug-assertions=off"]);
874         if overflow_checks {
875             cmd.args(&["-C", "overflow-checks=on"]);
876         }
877     } else if !overflow_checks {
878         cmd.args(&["-C", "overflow-checks=off"]);
879     }
880 
881     if test && unit.target.harness() {
882         cmd.arg("--test");
883 
884         // Cargo has historically never compiled `--test` binaries with
885         // `panic=abort` because the `test` crate itself didn't support it.
886         // Support is now upstream, however, but requires an unstable flag to be
887         // passed when compiling the test. We require, in Cargo, an unstable
888         // flag to pass to rustc, so register that here. Eventually this flag
889         // will simply not be needed when the behavior is stabilized in the Rust
890         // compiler itself.
891         if *panic == PanicStrategy::Abort {
892             cmd.arg("-Z").arg("panic-abort-tests");
893         }
894     } else if test {
895         cmd.arg("--cfg").arg("test");
896     }
897 
898     for feat in &unit.features {
899         cmd.arg("--cfg").arg(&format!("feature=\"{}\"", feat));
900     }
901 
902     let meta = cx.files().metadata(unit);
903     cmd.arg("-C").arg(&format!("metadata={}", meta));
904     if cx.files().use_extra_filename(unit) {
905         cmd.arg("-C").arg(&format!("extra-filename=-{}", meta));
906     }
907 
908     if rpath {
909         cmd.arg("-C").arg("rpath");
910     }
911 
912     cmd.arg("--out-dir").arg(&cx.files().out_dir(unit));
913 
914     fn opt(cmd: &mut ProcessBuilder, key: &str, prefix: &str, val: Option<&OsStr>) {
915         if let Some(val) = val {
916             let mut joined = OsString::from(prefix);
917             joined.push(val);
918             cmd.arg(key).arg(joined);
919         }
920     }
921 
922     if let CompileKind::Target(n) = unit.kind {
923         cmd.arg("--target").arg(n.rustc_target());
924     }
925 
926     opt(
927         cmd,
928         "-C",
929         "linker=",
930         bcx.linker(unit.kind).as_ref().map(|s| s.as_ref()),
931     );
932     if incremental {
933         let dir = cx.files().layout(unit.kind).incremental().as_os_str();
934         opt(cmd, "-C", "incremental=", Some(dir));
935     }
936 
937     if strip != Strip::None {
938         cmd.arg("-Z").arg(format!("strip={}", strip));
939     }
940 
941     if unit.is_std {
942         // -Zforce-unstable-if-unmarked prevents the accidental use of
943         // unstable crates within the sysroot (such as "extern crate libc" or
944         // any non-public crate in the sysroot).
945         //
946         // RUSTC_BOOTSTRAP allows unstable features on stable.
947         cmd.arg("-Z")
948             .arg("force-unstable-if-unmarked")
949             .env("RUSTC_BOOTSTRAP", "1");
950     }
951 
952     if bcx.config.cli_unstable().future_incompat_report {
953         cmd.arg("-Z").arg("emit-future-incompat-report");
954     }
955 
956     // Add `CARGO_BIN_` environment variables for building tests.
957     if unit.target.is_test() || unit.target.is_bench() {
958         for bin_target in unit
959             .pkg
960             .manifest()
961             .targets()
962             .iter()
963             .filter(|target| target.is_bin())
964         {
965             let exe_path = cx
966                 .files()
967                 .bin_link_for_target(bin_target, unit.kind, cx.bcx)?;
968             let key = format!("CARGO_BIN_EXE_{}", bin_target.name());
969             cmd.env(&key, exe_path);
970         }
971     }
972     Ok(())
973 }
974 
lto_args(cx: &Context<'_, '_>, unit: &Unit) -> Vec<OsString>975 fn lto_args(cx: &Context<'_, '_>, unit: &Unit) -> Vec<OsString> {
976     let mut result = Vec::new();
977     let mut push = |arg: &str| {
978         result.push(OsString::from("-C"));
979         result.push(OsString::from(arg));
980     };
981     match cx.lto[unit] {
982         lto::Lto::Run(None) => push("lto"),
983         lto::Lto::Run(Some(s)) => push(&format!("lto={}", s)),
984         lto::Lto::Off => {
985             push("lto=off");
986             push("embed-bitcode=no");
987         }
988         lto::Lto::ObjectAndBitcode => {} // this is rustc's default
989         lto::Lto::OnlyBitcode => push("linker-plugin-lto"),
990         lto::Lto::OnlyObject => push("embed-bitcode=no"),
991     }
992     result
993 }
994 
build_deps_args( cmd: &mut ProcessBuilder, cx: &mut Context<'_, '_>, unit: &Unit, ) -> CargoResult<()>995 fn build_deps_args(
996     cmd: &mut ProcessBuilder,
997     cx: &mut Context<'_, '_>,
998     unit: &Unit,
999 ) -> CargoResult<()> {
1000     let bcx = cx.bcx;
1001     cmd.arg("-L").arg(&{
1002         let mut deps = OsString::from("dependency=");
1003         deps.push(cx.files().deps_dir(unit));
1004         deps
1005     });
1006 
1007     // Be sure that the host path is also listed. This'll ensure that proc macro
1008     // dependencies are correctly found (for reexported macros).
1009     if !unit.kind.is_host() {
1010         cmd.arg("-L").arg(&{
1011             let mut deps = OsString::from("dependency=");
1012             deps.push(cx.files().host_deps());
1013             deps
1014         });
1015     }
1016 
1017     let deps = cx.unit_deps(unit);
1018 
1019     // If there is not one linkable target but should, rustc fails later
1020     // on if there is an `extern crate` for it. This may turn into a hard
1021     // error in the future (see PR #4797).
1022     if !deps
1023         .iter()
1024         .any(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_linkable())
1025     {
1026         if let Some(dep) = deps
1027             .iter()
1028             .find(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_lib())
1029         {
1030             bcx.config.shell().warn(format!(
1031                 "The package `{}` \
1032                  provides no linkable target. The compiler might raise an error while compiling \
1033                  `{}`. Consider adding 'dylib' or 'rlib' to key `crate-type` in `{}`'s \
1034                  Cargo.toml. This warning might turn into a hard error in the future.",
1035                 dep.unit.target.crate_name(),
1036                 unit.target.crate_name(),
1037                 dep.unit.target.crate_name()
1038             ))?;
1039         }
1040     }
1041 
1042     let mut unstable_opts = false;
1043 
1044     for dep in deps {
1045         if dep.unit.mode.is_run_custom_build() {
1046             cmd.env("OUT_DIR", &cx.files().build_script_out_dir(&dep.unit));
1047         }
1048     }
1049 
1050     for arg in extern_args(cx, unit, &mut unstable_opts)? {
1051         cmd.arg(arg);
1052     }
1053 
1054     // This will only be set if we're already using a feature
1055     // requiring nightly rust
1056     if unstable_opts {
1057         cmd.arg("-Z").arg("unstable-options");
1058     }
1059 
1060     Ok(())
1061 }
1062 
1063 /// Generates a list of `--extern` arguments.
extern_args( cx: &Context<'_, '_>, unit: &Unit, unstable_opts: &mut bool, ) -> CargoResult<Vec<OsString>>1064 pub fn extern_args(
1065     cx: &Context<'_, '_>,
1066     unit: &Unit,
1067     unstable_opts: &mut bool,
1068 ) -> CargoResult<Vec<OsString>> {
1069     let mut result = Vec::new();
1070     let deps = cx.unit_deps(unit);
1071 
1072     // Closure to add one dependency to `result`.
1073     let mut link_to =
1074         |dep: &UnitDep, extern_crate_name: InternedString, noprelude: bool| -> CargoResult<()> {
1075             let mut value = OsString::new();
1076             let mut opts = Vec::new();
1077             if unit
1078                 .pkg
1079                 .manifest()
1080                 .unstable_features()
1081                 .require(Feature::public_dependency())
1082                 .is_ok()
1083                 && !dep.public
1084             {
1085                 opts.push("priv");
1086                 *unstable_opts = true;
1087             }
1088             if noprelude {
1089                 opts.push("noprelude");
1090                 *unstable_opts = true;
1091             }
1092             if !opts.is_empty() {
1093                 value.push(opts.join(","));
1094                 value.push(":");
1095             }
1096             value.push(extern_crate_name.as_str());
1097             value.push("=");
1098 
1099             let mut pass = |file| {
1100                 let mut value = value.clone();
1101                 value.push(file);
1102                 result.push(OsString::from("--extern"));
1103                 result.push(value);
1104             };
1105 
1106             let outputs = cx.outputs(&dep.unit)?;
1107 
1108             if cx.only_requires_rmeta(unit, &dep.unit) || dep.unit.mode.is_check() {
1109                 // Example: rlib dependency for an rlib, rmeta is all that is required.
1110                 let output = outputs
1111                     .iter()
1112                     .find(|output| output.flavor == FileFlavor::Rmeta)
1113                     .expect("failed to find rmeta dep for pipelined dep");
1114                 pass(&output.path);
1115             } else {
1116                 // Example: a bin needs `rlib` for dependencies, it cannot use rmeta.
1117                 for output in outputs.iter() {
1118                     if output.flavor == FileFlavor::Linkable {
1119                         pass(&output.path);
1120                     }
1121                 }
1122             }
1123             Ok(())
1124         };
1125 
1126     for dep in deps {
1127         if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
1128             link_to(dep, dep.extern_crate_name, dep.noprelude)?;
1129         }
1130     }
1131     if unit.target.proc_macro() {
1132         // Automatically import `proc_macro`.
1133         result.push(OsString::from("--extern"));
1134         result.push(OsString::from("proc_macro"));
1135     }
1136 
1137     Ok(result)
1138 }
1139 
envify(s: &str) -> String1140 fn envify(s: &str) -> String {
1141     s.chars()
1142         .flat_map(|c| c.to_uppercase())
1143         .map(|c| if c == '-' { '_' } else { c })
1144         .collect()
1145 }
1146 
1147 struct OutputOptions {
1148     /// What format we're emitting from Cargo itself.
1149     format: MessageFormat,
1150     /// Look for JSON message that indicates .rmeta file is available for
1151     /// pipelined compilation.
1152     look_for_metadata_directive: bool,
1153     /// Whether or not to display messages in color.
1154     color: bool,
1155     /// Where to write the JSON messages to support playback later if the unit
1156     /// is fresh. The file is created lazily so that in the normal case, lots
1157     /// of empty files are not created. If this is None, the output will not
1158     /// be cached (such as when replaying cached messages).
1159     cache_cell: Option<(PathBuf, LazyCell<File>)>,
1160     /// If `true`, display any recorded warning messages.
1161     /// Other types of messages are processed regardless
1162     /// of the value of this flag
1163     show_warnings: bool,
1164 }
1165 
1166 impl OutputOptions {
new(cx: &Context<'_, '_>, unit: &Unit) -> OutputOptions1167     fn new(cx: &Context<'_, '_>, unit: &Unit) -> OutputOptions {
1168         let look_for_metadata_directive = cx.rmeta_required(unit);
1169         let color = cx.bcx.config.shell().err_supports_color();
1170         let path = cx.files().message_cache_path(unit);
1171         // Remove old cache, ignore ENOENT, which is the common case.
1172         drop(fs::remove_file(&path));
1173         let cache_cell = Some((path, LazyCell::new()));
1174         OutputOptions {
1175             format: cx.bcx.build_config.message_format,
1176             look_for_metadata_directive,
1177             color,
1178             cache_cell,
1179             show_warnings: true,
1180         }
1181     }
1182 }
1183 
on_stdout_line( state: &JobState<'_>, line: &str, _package_id: PackageId, _target: &Target, ) -> CargoResult<()>1184 fn on_stdout_line(
1185     state: &JobState<'_>,
1186     line: &str,
1187     _package_id: PackageId,
1188     _target: &Target,
1189 ) -> CargoResult<()> {
1190     state.stdout(line.to_string())?;
1191     Ok(())
1192 }
1193 
on_stderr_line( state: &JobState<'_>, line: &str, package_id: PackageId, manifest_path: &std::path::Path, target: &Target, options: &mut OutputOptions, ) -> CargoResult<()>1194 fn on_stderr_line(
1195     state: &JobState<'_>,
1196     line: &str,
1197     package_id: PackageId,
1198     manifest_path: &std::path::Path,
1199     target: &Target,
1200     options: &mut OutputOptions,
1201 ) -> CargoResult<()> {
1202     if on_stderr_line_inner(state, line, package_id, manifest_path, target, options)? {
1203         // Check if caching is enabled.
1204         if let Some((path, cell)) = &mut options.cache_cell {
1205             // Cache the output, which will be replayed later when Fresh.
1206             let f = cell.try_borrow_mut_with(|| paths::create(path))?;
1207             debug_assert!(!line.contains('\n'));
1208             f.write_all(line.as_bytes())?;
1209             f.write_all(&[b'\n'])?;
1210         }
1211     }
1212     Ok(())
1213 }
1214 
1215 /// Returns true if the line should be cached.
on_stderr_line_inner( state: &JobState<'_>, line: &str, package_id: PackageId, manifest_path: &std::path::Path, target: &Target, options: &mut OutputOptions, ) -> CargoResult<bool>1216 fn on_stderr_line_inner(
1217     state: &JobState<'_>,
1218     line: &str,
1219     package_id: PackageId,
1220     manifest_path: &std::path::Path,
1221     target: &Target,
1222     options: &mut OutputOptions,
1223 ) -> CargoResult<bool> {
1224     // We primarily want to use this function to process JSON messages from
1225     // rustc. The compiler should always print one JSON message per line, and
1226     // otherwise it may have other output intermingled (think RUST_LOG or
1227     // something like that), so skip over everything that doesn't look like a
1228     // JSON message.
1229     if !line.starts_with('{') {
1230         state.stderr(line.to_string())?;
1231         return Ok(true);
1232     }
1233 
1234     let mut compiler_message: Box<serde_json::value::RawValue> = match serde_json::from_str(line) {
1235         Ok(msg) => msg,
1236 
1237         // If the compiler produced a line that started with `{` but it wasn't
1238         // valid JSON, maybe it wasn't JSON in the first place! Forward it along
1239         // to stderr.
1240         Err(e) => {
1241             debug!("failed to parse json: {:?}", e);
1242             state.stderr(line.to_string())?;
1243             return Ok(true);
1244         }
1245     };
1246 
1247     if let Ok(report) = serde_json::from_str::<FutureIncompatReport>(compiler_message.get()) {
1248         state.future_incompat_report(report.future_incompat_report);
1249         return Ok(true);
1250     }
1251 
1252     // Depending on what we're emitting from Cargo itself, we figure out what to
1253     // do with this JSON message.
1254     match options.format {
1255         // In the "human" output formats (human/short) or if diagnostic messages
1256         // from rustc aren't being included in the output of Cargo's JSON
1257         // messages then we extract the diagnostic (if present) here and handle
1258         // it ourselves.
1259         MessageFormat::Human
1260         | MessageFormat::Short
1261         | MessageFormat::Json {
1262             render_diagnostics: true,
1263             ..
1264         } => {
1265             #[derive(serde::Deserialize)]
1266             struct CompilerMessage {
1267                 rendered: String,
1268             }
1269             if let Ok(mut error) = serde_json::from_str::<CompilerMessage>(compiler_message.get()) {
1270                 // state.stderr will add a newline
1271                 if error.rendered.ends_with('\n') {
1272                     error.rendered.pop();
1273                 }
1274                 let rendered = if options.color {
1275                     error.rendered
1276                 } else {
1277                     // Strip only fails if the the Writer fails, which is Cursor
1278                     // on a Vec, which should never fail.
1279                     strip_ansi_escapes::strip(&error.rendered)
1280                         .map(|v| String::from_utf8(v).expect("utf8"))
1281                         .expect("strip should never fail")
1282                 };
1283                 if options.show_warnings {
1284                     state.stderr(rendered)?;
1285                 }
1286                 return Ok(true);
1287             }
1288         }
1289 
1290         // Remove color information from the rendered string if color is not
1291         // enabled. Cargo always asks for ANSI colors from rustc. This allows
1292         // cached replay to enable/disable colors without re-invoking rustc.
1293         MessageFormat::Json { ansi: false, .. } => {
1294             #[derive(serde::Deserialize, serde::Serialize)]
1295             struct CompilerMessage {
1296                 rendered: String,
1297                 #[serde(flatten)]
1298                 other: std::collections::BTreeMap<String, serde_json::Value>,
1299             }
1300             if let Ok(mut error) = serde_json::from_str::<CompilerMessage>(compiler_message.get()) {
1301                 error.rendered = strip_ansi_escapes::strip(&error.rendered)
1302                     .map(|v| String::from_utf8(v).expect("utf8"))
1303                     .unwrap_or(error.rendered);
1304                 let new_line = serde_json::to_string(&error)?;
1305                 let new_msg: Box<serde_json::value::RawValue> = serde_json::from_str(&new_line)?;
1306                 compiler_message = new_msg;
1307             }
1308         }
1309 
1310         // If ansi colors are desired then we should be good to go! We can just
1311         // pass through this message as-is.
1312         MessageFormat::Json { ansi: true, .. } => {}
1313     }
1314 
1315     // In some modes of execution we will execute rustc with `-Z
1316     // emit-artifact-notifications` to look for metadata files being produced. When this
1317     // happens we may be able to start subsequent compilations more quickly than
1318     // waiting for an entire compile to finish, possibly using more parallelism
1319     // available to complete a compilation session more quickly.
1320     //
1321     // In these cases look for a matching directive and inform Cargo internally
1322     // that a metadata file has been produced.
1323     if options.look_for_metadata_directive {
1324         #[derive(serde::Deserialize)]
1325         struct ArtifactNotification {
1326             artifact: String,
1327         }
1328         if let Ok(artifact) = serde_json::from_str::<ArtifactNotification>(compiler_message.get()) {
1329             log::trace!("found directive from rustc: `{}`", artifact.artifact);
1330             if artifact.artifact.ends_with(".rmeta") {
1331                 log::debug!("looks like metadata finished early!");
1332                 state.rmeta_produced();
1333             }
1334             return Ok(false);
1335         }
1336     }
1337 
1338     #[derive(serde::Deserialize)]
1339     struct JobserverNotification {
1340         jobserver_event: Event,
1341     }
1342 
1343     #[derive(Debug, serde::Deserialize)]
1344     enum Event {
1345         WillAcquire,
1346         Release,
1347     }
1348 
1349     if let Ok(JobserverNotification { jobserver_event }) =
1350         serde_json::from_str::<JobserverNotification>(compiler_message.get())
1351     {
1352         log::info!(
1353             "found jobserver directive from rustc: `{:?}`",
1354             jobserver_event
1355         );
1356         match jobserver_event {
1357             Event::WillAcquire => state.will_acquire(),
1358             Event::Release => state.release_token(),
1359         }
1360         return Ok(false);
1361     }
1362 
1363     // And failing all that above we should have a legitimate JSON diagnostic
1364     // from the compiler, so wrap it in an external Cargo JSON message
1365     // indicating which package it came from and then emit it.
1366 
1367     if !options.show_warnings {
1368         return Ok(true);
1369     }
1370 
1371     let msg = machine_message::FromCompiler {
1372         package_id,
1373         manifest_path,
1374         target,
1375         message: compiler_message,
1376     }
1377     .to_json_string();
1378 
1379     // Switch json lines from rustc/rustdoc that appear on stderr to stdout
1380     // instead. We want the stdout of Cargo to always be machine parseable as
1381     // stderr has our colorized human-readable messages.
1382     state.stdout(msg)?;
1383     Ok(true)
1384 }
1385 
replay_output_cache( package_id: PackageId, manifest_path: PathBuf, target: &Target, path: PathBuf, format: MessageFormat, color: bool, show_warnings: bool, ) -> Work1386 fn replay_output_cache(
1387     package_id: PackageId,
1388     manifest_path: PathBuf,
1389     target: &Target,
1390     path: PathBuf,
1391     format: MessageFormat,
1392     color: bool,
1393     show_warnings: bool,
1394 ) -> Work {
1395     let target = target.clone();
1396     let mut options = OutputOptions {
1397         format,
1398         look_for_metadata_directive: true,
1399         color,
1400         cache_cell: None,
1401         show_warnings,
1402     };
1403     Work::new(move |state| {
1404         if !path.exists() {
1405             // No cached output, probably didn't emit anything.
1406             return Ok(());
1407         }
1408         // We sometimes have gigabytes of output from the compiler, so avoid
1409         // loading it all into memory at once, as that can cause OOM where
1410         // otherwise there would be none.
1411         let file = paths::open(&path)?;
1412         let mut reader = std::io::BufReader::new(file);
1413         let mut line = String::new();
1414         loop {
1415             let length = reader.read_line(&mut line)?;
1416             if length == 0 {
1417                 break;
1418             }
1419             let trimmed = line.trim_end_matches(&['\n', '\r'][..]);
1420             on_stderr_line(
1421                 state,
1422                 trimmed,
1423                 package_id,
1424                 &manifest_path,
1425                 &target,
1426                 &mut options,
1427             )?;
1428             line.clear();
1429         }
1430         Ok(())
1431     })
1432 }
1433