cargo/core/compiler/
mod.rs

1//! # Interact with the compiler
2//!
3//! If you consider [`ops::cargo_compile::compile`] as a `rustc` driver but on
4//! Cargo side, this module is kinda the `rustc_interface` for that merits.
5//! It contains all the interaction between Cargo and the rustc compiler,
6//! from preparing the context for the entire build process, to scheduling
7//! and executing each unit of work (e.g. running `rustc`), to managing and
8//! caching the output artifact of a build.
9//!
10//! However, it hasn't yet exposed a clear definition of each phase or session,
11//! like what rustc has done[^1]. Also, no one knows if Cargo really needs that.
12//! To be pragmatic, here we list a handful of items you may want to learn:
13//!
14//! * [`BuildContext`] is a static context containing all information you need
15//!   before a build gets started.
16//! * [`BuildRunner`] is the center of the world, coordinating a running build and
17//!   collecting information from it.
18//! * [`custom_build`] is the home of build script executions and output parsing.
19//! * [`fingerprint`] not only defines but also executes a set of rules to
20//!   determine if a re-compile is needed.
21//! * [`job_queue`] is where the parallelism, job scheduling, and communication
22//!   machinery happen between Cargo and the compiler.
23//! * [`layout`] defines and manages output artifacts of a build in the filesystem.
24//! * [`unit_dependencies`] is for building a dependency graph for compilation
25//!   from a result of dependency resolution.
26//! * [`Unit`] contains sufficient information to build something, usually
27//!   turning into a compiler invocation in a later phase.
28//!
29//! [^1]: Maybe [`-Zbuild-plan`](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-plan)
30//!   was designed to serve that purpose but still [in flux](https://github.com/rust-lang/cargo/issues/7614).
31//!
32//! [`ops::cargo_compile::compile`]: crate::ops::compile
33
34pub mod artifact;
35mod build_config;
36pub(crate) mod build_context;
37mod build_plan;
38pub(crate) mod build_runner;
39mod compilation;
40mod compile_kind;
41mod crate_type;
42mod custom_build;
43pub(crate) mod fingerprint;
44pub mod future_incompat;
45pub(crate) mod job_queue;
46pub(crate) mod layout;
47mod links;
48mod lto;
49mod output_depinfo;
50pub mod rustdoc;
51pub mod standard_lib;
52mod timings;
53mod unit;
54pub mod unit_dependencies;
55pub mod unit_graph;
56
57use std::borrow::Cow;
58use std::collections::{HashMap, HashSet};
59use std::env;
60use std::ffi::{OsStr, OsString};
61use std::fmt::Display;
62use std::fs::{self, File};
63use std::io::{BufRead, Write};
64use std::path::{Path, PathBuf};
65use std::sync::Arc;
66
67use anyhow::{Context as _, Error};
68use lazycell::LazyCell;
69use tracing::{debug, trace};
70
71pub use self::build_config::{BuildConfig, CompileMode, MessageFormat, TimingOutput};
72pub use self::build_context::{
73    BuildContext, FileFlavor, FileType, RustDocFingerprint, RustcTargetData, TargetInfo,
74};
75use self::build_plan::BuildPlan;
76pub use self::build_runner::{BuildRunner, Metadata, UnitHash};
77pub use self::compilation::{Compilation, Doctest, UnitOutput};
78pub use self::compile_kind::{CompileKind, CompileTarget};
79pub use self::crate_type::CrateType;
80pub use self::custom_build::LinkArgTarget;
81pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts};
82pub(crate) use self::fingerprint::DirtyReason;
83pub use self::job_queue::Freshness;
84use self::job_queue::{Job, JobQueue, JobState, Work};
85pub(crate) use self::layout::Layout;
86pub use self::lto::Lto;
87use self::output_depinfo::output_depinfo;
88use self::unit_graph::UnitDep;
89use crate::core::compiler::future_incompat::FutureIncompatReport;
90pub use crate::core::compiler::unit::{Unit, UnitInterner};
91use crate::core::manifest::TargetSourcePath;
92use crate::core::profiles::{PanicStrategy, Profile, StripInner};
93use crate::core::{Feature, PackageId, Target, Verbosity};
94use crate::util::context::WarningHandling;
95use crate::util::errors::{CargoResult, VerboseError};
96use crate::util::interning::InternedString;
97use crate::util::machine_message::{self, Message};
98use crate::util::{add_path_args, internal};
99use cargo_util::{paths, ProcessBuilder, ProcessError};
100use cargo_util_schemas::manifest::TomlDebugInfo;
101use cargo_util_schemas::manifest::TomlTrimPaths;
102use cargo_util_schemas::manifest::TomlTrimPathsValue;
103use rustfix::diagnostics::Applicability;
104
105const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version";
106
107/// A glorified callback for executing calls to rustc. Rather than calling rustc
108/// directly, we'll use an `Executor`, giving clients an opportunity to intercept
109/// the build calls.
110pub trait Executor: Send + Sync + 'static {
111    /// Called after a rustc process invocation is prepared up-front for a given
112    /// unit of work (may still be modified for runtime-known dependencies, when
113    /// the work is actually executed).
114    fn init(&self, _build_runner: &BuildRunner<'_, '_>, _unit: &Unit) {}
115
116    /// In case of an `Err`, Cargo will not continue with the build process for
117    /// this package.
118    fn exec(
119        &self,
120        cmd: &ProcessBuilder,
121        id: PackageId,
122        target: &Target,
123        mode: CompileMode,
124        on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
125        on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
126    ) -> CargoResult<()>;
127
128    /// Queried when queuing each unit of work. If it returns true, then the
129    /// unit will always be rebuilt, independent of whether it needs to be.
130    fn force_rebuild(&self, _unit: &Unit) -> bool {
131        false
132    }
133}
134
135/// A `DefaultExecutor` calls rustc without doing anything else. It is Cargo's
136/// default behaviour.
137#[derive(Copy, Clone)]
138pub struct DefaultExecutor;
139
140impl Executor for DefaultExecutor {
141    fn exec(
142        &self,
143        cmd: &ProcessBuilder,
144        _id: PackageId,
145        _target: &Target,
146        _mode: CompileMode,
147        on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
148        on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
149    ) -> CargoResult<()> {
150        cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
151            .map(drop)
152    }
153}
154
155/// Builds up and enqueue a list of pending jobs onto the `job` queue.
156///
157/// Starting from the `unit`, this function recursively calls itself to build
158/// all jobs for dependencies of the `unit`. Each of these jobs represents
159/// compiling a particular package.
160///
161/// Note that **no actual work is executed as part of this**, that's all done
162/// next as part of [`JobQueue::execute`] function which will run everything
163/// in order with proper parallelism.
164#[tracing::instrument(skip(build_runner, jobs, plan, exec))]
165fn compile<'gctx>(
166    build_runner: &mut BuildRunner<'_, 'gctx>,
167    jobs: &mut JobQueue<'gctx>,
168    plan: &mut BuildPlan,
169    unit: &Unit,
170    exec: &Arc<dyn Executor>,
171    force_rebuild: bool,
172) -> CargoResult<()> {
173    let bcx = build_runner.bcx;
174    let build_plan = bcx.build_config.build_plan;
175    if !build_runner.compiled.insert(unit.clone()) {
176        return Ok(());
177    }
178
179    // Build up the work to be done to compile this unit, enqueuing it once
180    // we've got everything constructed.
181    fingerprint::prepare_init(build_runner, unit)?;
182
183    let job = if unit.mode.is_run_custom_build() {
184        custom_build::prepare(build_runner, unit)?
185    } else if unit.mode.is_doc_test() {
186        // We run these targets later, so this is just a no-op for now.
187        Job::new_fresh()
188    } else if build_plan {
189        Job::new_dirty(
190            rustc(build_runner, unit, &exec.clone())?,
191            DirtyReason::FreshBuild,
192        )
193    } else {
194        let force = exec.force_rebuild(unit) || force_rebuild;
195        let mut job = fingerprint::prepare_target(build_runner, unit, force)?;
196        job.before(if job.freshness().is_dirty() {
197            let work = if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
198                rustdoc(build_runner, unit)?
199            } else {
200                rustc(build_runner, unit, exec)?
201            };
202            work.then(link_targets(build_runner, unit, false)?)
203        } else {
204            // We always replay the output cache,
205            // since it might contain future-incompat-report messages
206            let show_diagnostics = unit.show_warnings(bcx.gctx)
207                && build_runner.bcx.gctx.warning_handling()? != WarningHandling::Allow;
208            let work = replay_output_cache(
209                unit.pkg.package_id(),
210                PathBuf::from(unit.pkg.manifest_path()),
211                &unit.target,
212                build_runner.files().message_cache_path(unit),
213                build_runner.bcx.build_config.message_format,
214                show_diagnostics,
215            );
216            // Need to link targets on both the dirty and fresh.
217            work.then(link_targets(build_runner, unit, true)?)
218        });
219
220        job
221    };
222    jobs.enqueue(build_runner, unit, job)?;
223
224    // Be sure to compile all dependencies of this target as well.
225    let deps = Vec::from(build_runner.unit_deps(unit)); // Create vec due to mutable borrow.
226    for dep in deps {
227        compile(build_runner, jobs, plan, &dep.unit, exec, false)?;
228    }
229    if build_plan {
230        plan.add(build_runner, unit)?;
231    }
232
233    Ok(())
234}
235
236/// Generates the warning message used when fallible doc-scrape units fail,
237/// either for rustdoc or rustc.
238fn make_failed_scrape_diagnostic(
239    build_runner: &BuildRunner<'_, '_>,
240    unit: &Unit,
241    top_line: impl Display,
242) -> String {
243    let manifest_path = unit.pkg.manifest_path();
244    let relative_manifest_path = manifest_path
245        .strip_prefix(build_runner.bcx.ws.root())
246        .unwrap_or(&manifest_path);
247
248    format!(
249        "\
250{top_line}
251    Try running with `--verbose` to see the error message.
252    If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in {}",
253        relative_manifest_path.display()
254    )
255}
256
257/// Creates a unit of work invoking `rustc` for building the `unit`.
258fn rustc(
259    build_runner: &mut BuildRunner<'_, '_>,
260    unit: &Unit,
261    exec: &Arc<dyn Executor>,
262) -> CargoResult<Work> {
263    let mut rustc = prepare_rustc(build_runner, unit)?;
264    let build_plan = build_runner.bcx.build_config.build_plan;
265
266    let name = unit.pkg.name();
267    let buildkey = unit.buildkey();
268
269    let outputs = build_runner.outputs(unit)?;
270    let root = build_runner.files().out_dir(unit);
271
272    // Prepare the native lib state (extra `-L` and `-l` flags).
273    let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
274    let current_id = unit.pkg.package_id();
275    let manifest_path = PathBuf::from(unit.pkg.manifest_path());
276    let build_scripts = build_runner.build_scripts.get(unit).cloned();
277
278    // If we are a binary and the package also contains a library, then we
279    // don't pass the `-l` flags.
280    let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib());
281
282    let dep_info_name =
283        if let Some(c_extra_filename) = build_runner.files().metadata(unit).c_extra_filename() {
284            format!("{}-{}.d", unit.target.crate_name(), c_extra_filename)
285        } else {
286            format!("{}.d", unit.target.crate_name())
287        };
288    let rustc_dep_info_loc = root.join(dep_info_name);
289    let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
290
291    let mut output_options = OutputOptions::new(build_runner, unit);
292    let package_id = unit.pkg.package_id();
293    let target = Target::clone(&unit.target);
294    let mode = unit.mode;
295
296    exec.init(build_runner, unit);
297    let exec = exec.clone();
298
299    let root_output = build_runner.files().host_dest().to_path_buf();
300    let target_dir = build_runner.bcx.ws.target_dir().into_path_unlocked();
301    let pkg_root = unit.pkg.root().to_path_buf();
302    let cwd = rustc
303        .get_cwd()
304        .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
305        .to_path_buf();
306    let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
307    let script_metadata = build_runner.find_build_script_metadata(unit);
308    let is_local = unit.is_local();
309    let artifact = unit.artifact;
310
311    let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
312        && !matches!(
313            build_runner.bcx.gctx.shell().verbosity(),
314            Verbosity::Verbose
315        );
316    let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
317        // If this unit is needed for doc-scraping, then we generate a diagnostic that
318        // describes the set of reverse-dependencies that cause the unit to be needed.
319        let target_desc = unit.target.description_named();
320        let mut for_scrape_units = build_runner
321            .bcx
322            .scrape_units_have_dep_on(unit)
323            .into_iter()
324            .map(|unit| unit.target.description_named())
325            .collect::<Vec<_>>();
326        for_scrape_units.sort();
327        let for_scrape_units = for_scrape_units.join(", ");
328        make_failed_scrape_diagnostic(build_runner, unit, format_args!("failed to check {target_desc} in package `{name}` as a prerequisite for scraping examples from: {for_scrape_units}"))
329    });
330    if hide_diagnostics_for_scrape_unit {
331        output_options.show_diagnostics = false;
332    }
333    let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
334    return Ok(Work::new(move |state| {
335        // Artifacts are in a different location than typical units,
336        // hence we must assure the crate- and target-dependent
337        // directory is present.
338        if artifact.is_true() {
339            paths::create_dir_all(&root)?;
340        }
341
342        // Only at runtime have we discovered what the extra -L and -l
343        // arguments are for native libraries, so we process those here. We
344        // also need to be sure to add any -L paths for our plugins to the
345        // dynamic library load path as a plugin's dynamic library may be
346        // located somewhere in there.
347        // Finally, if custom environment variables have been produced by
348        // previous build scripts, we include them in the rustc invocation.
349        if let Some(build_scripts) = build_scripts {
350            let script_outputs = build_script_outputs.lock().unwrap();
351            if !build_plan {
352                add_native_deps(
353                    &mut rustc,
354                    &script_outputs,
355                    &build_scripts,
356                    pass_l_flag,
357                    &target,
358                    current_id,
359                )?;
360                add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?;
361            }
362            add_custom_flags(&mut rustc, &script_outputs, script_metadata)?;
363        }
364
365        for output in outputs.iter() {
366            // If there is both an rmeta and rlib, rustc will prefer to use the
367            // rlib, even if it is older. Therefore, we must delete the rlib to
368            // force using the new rmeta.
369            if output.path.extension() == Some(OsStr::new("rmeta")) {
370                let dst = root.join(&output.path).with_extension("rlib");
371                if dst.exists() {
372                    paths::remove_file(&dst)?;
373                }
374            }
375
376            // Some linkers do not remove the executable, but truncate and modify it.
377            // That results in the old hard-link being modified even after renamed.
378            // We delete the old artifact here to prevent this behavior from confusing users.
379            // See rust-lang/cargo#8348.
380            if output.hardlink.is_some() && output.path.exists() {
381                _ = paths::remove_file(&output.path).map_err(|e| {
382                    tracing::debug!(
383                        "failed to delete previous output file `{:?}`: {e:?}",
384                        output.path
385                    );
386                });
387            }
388        }
389
390        state.running(&rustc);
391        let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
392        if build_plan {
393            state.build_plan(buildkey, rustc.clone(), outputs.clone());
394        } else {
395            let result = exec
396                .exec(
397                    &rustc,
398                    package_id,
399                    &target,
400                    mode,
401                    &mut |line| on_stdout_line(state, line, package_id, &target),
402                    &mut |line| {
403                        on_stderr_line(
404                            state,
405                            line,
406                            package_id,
407                            &manifest_path,
408                            &target,
409                            &mut output_options,
410                        )
411                    },
412                )
413                .map_err(|e| {
414                    if output_options.errors_seen == 0 {
415                        // If we didn't expect an error, do not require --verbose to fail.
416                        // This is intended to debug
417                        // https://github.com/rust-lang/crater/issues/733, where we are seeing
418                        // Cargo exit unsuccessfully while seeming to not show any errors.
419                        e
420                    } else {
421                        verbose_if_simple_exit_code(e)
422                    }
423                })
424                .with_context(|| {
425                    // adapted from rustc_errors/src/lib.rs
426                    let warnings = match output_options.warnings_seen {
427                        0 => String::new(),
428                        1 => "; 1 warning emitted".to_string(),
429                        count => format!("; {} warnings emitted", count),
430                    };
431                    let errors = match output_options.errors_seen {
432                        0 => String::new(),
433                        1 => " due to 1 previous error".to_string(),
434                        count => format!(" due to {} previous errors", count),
435                    };
436                    let name = descriptive_pkg_name(&name, &target, &mode);
437                    format!("could not compile {name}{errors}{warnings}")
438                });
439
440            if let Err(e) = result {
441                if let Some(diagnostic) = failed_scrape_diagnostic {
442                    state.warning(diagnostic)?;
443                }
444
445                return Err(e);
446            }
447
448            // Exec should never return with success *and* generate an error.
449            debug_assert_eq!(output_options.errors_seen, 0);
450        }
451
452        if rustc_dep_info_loc.exists() {
453            fingerprint::translate_dep_info(
454                &rustc_dep_info_loc,
455                &dep_info_loc,
456                &cwd,
457                &pkg_root,
458                &target_dir,
459                &rustc,
460                // Do not track source files in the fingerprint for registry dependencies.
461                is_local,
462                &env_config,
463            )
464            .with_context(|| {
465                internal(format!(
466                    "could not parse/generate dep info at: {}",
467                    rustc_dep_info_loc.display()
468                ))
469            })?;
470            // This mtime shift allows Cargo to detect if a source file was
471            // modified in the middle of the build.
472            paths::set_file_time_no_err(dep_info_loc, timestamp);
473        }
474
475        Ok(())
476    }));
477
478    // Add all relevant `-L` and `-l` flags from dependencies (now calculated and
479    // present in `state`) to the command provided.
480    fn add_native_deps(
481        rustc: &mut ProcessBuilder,
482        build_script_outputs: &BuildScriptOutputs,
483        build_scripts: &BuildScripts,
484        pass_l_flag: bool,
485        target: &Target,
486        current_id: PackageId,
487    ) -> CargoResult<()> {
488        for key in build_scripts.to_link.iter() {
489            let output = build_script_outputs.get(key.1).ok_or_else(|| {
490                internal(format!(
491                    "couldn't find build script output for {}/{}",
492                    key.0, key.1
493                ))
494            })?;
495            for path in output.library_paths.iter() {
496                rustc.arg("-L").arg(path);
497            }
498
499            if key.0 == current_id {
500                if pass_l_flag {
501                    for name in output.library_links.iter() {
502                        rustc.arg("-l").arg(name);
503                    }
504                }
505            }
506
507            for (lt, arg) in &output.linker_args {
508                // There was an unintentional change where cdylibs were
509                // allowed to be passed via transitive dependencies. This
510                // clause should have been kept in the `if` block above. For
511                // now, continue allowing it for cdylib only.
512                // See https://github.com/rust-lang/cargo/issues/9562
513                if lt.applies_to(target) && (key.0 == current_id || *lt == LinkArgTarget::Cdylib) {
514                    rustc.arg("-C").arg(format!("link-arg={}", arg));
515                }
516            }
517        }
518        Ok(())
519    }
520}
521
522fn verbose_if_simple_exit_code(err: Error) -> Error {
523    // If a signal on unix (`code == None`) or an abnormal termination
524    // on Windows (codes like `0xC0000409`), don't hide the error details.
525    match err
526        .downcast_ref::<ProcessError>()
527        .as_ref()
528        .and_then(|perr| perr.code)
529    {
530        Some(n) if cargo_util::is_simple_exit_code(n) => VerboseError::new(err).into(),
531        _ => err,
532    }
533}
534
535/// Link the compiled target (often of form `foo-{metadata_hash}`) to the
536/// final target. This must happen during both "Fresh" and "Compile".
537fn link_targets(
538    build_runner: &mut BuildRunner<'_, '_>,
539    unit: &Unit,
540    fresh: bool,
541) -> CargoResult<Work> {
542    let bcx = build_runner.bcx;
543    let outputs = build_runner.outputs(unit)?;
544    let export_dir = build_runner.files().export_dir();
545    let package_id = unit.pkg.package_id();
546    let manifest_path = PathBuf::from(unit.pkg.manifest_path());
547    let profile = unit.profile.clone();
548    let unit_mode = unit.mode;
549    let features = unit.features.iter().map(|s| s.to_string()).collect();
550    let json_messages = bcx.build_config.emit_json();
551    let executable = build_runner.get_executable(unit)?;
552    let mut target = Target::clone(&unit.target);
553    if let TargetSourcePath::Metabuild = target.src_path() {
554        // Give it something to serialize.
555        let path = unit
556            .pkg
557            .manifest()
558            .metabuild_path(build_runner.bcx.ws.target_dir());
559        target.set_src_path(TargetSourcePath::Path(path));
560    }
561
562    Ok(Work::new(move |state| {
563        // If we're a "root crate", e.g., the target of this compilation, then we
564        // hard link our outputs out of the `deps` directory into the directory
565        // above. This means that `cargo build` will produce binaries in
566        // `target/debug` which one probably expects.
567        let mut destinations = vec![];
568        for output in outputs.iter() {
569            let src = &output.path;
570            // This may have been a `cargo rustc` command which changes the
571            // output, so the source may not actually exist.
572            if !src.exists() {
573                continue;
574            }
575            let Some(dst) = output.hardlink.as_ref() else {
576                destinations.push(src.clone());
577                continue;
578            };
579            destinations.push(dst.clone());
580            paths::link_or_copy(src, dst)?;
581            if let Some(ref path) = output.export_path {
582                let export_dir = export_dir.as_ref().unwrap();
583                paths::create_dir_all(export_dir)?;
584
585                paths::link_or_copy(src, path)?;
586            }
587        }
588
589        if json_messages {
590            let debuginfo = match profile.debuginfo.into_inner() {
591                TomlDebugInfo::None => machine_message::ArtifactDebuginfo::Int(0),
592                TomlDebugInfo::Limited => machine_message::ArtifactDebuginfo::Int(1),
593                TomlDebugInfo::Full => machine_message::ArtifactDebuginfo::Int(2),
594                TomlDebugInfo::LineDirectivesOnly => {
595                    machine_message::ArtifactDebuginfo::Named("line-directives-only")
596                }
597                TomlDebugInfo::LineTablesOnly => {
598                    machine_message::ArtifactDebuginfo::Named("line-tables-only")
599                }
600            };
601            let art_profile = machine_message::ArtifactProfile {
602                opt_level: profile.opt_level.as_str(),
603                debuginfo: Some(debuginfo),
604                debug_assertions: profile.debug_assertions,
605                overflow_checks: profile.overflow_checks,
606                test: unit_mode.is_any_test(),
607            };
608
609            let msg = machine_message::Artifact {
610                package_id: package_id.to_spec(),
611                manifest_path,
612                target: &target,
613                profile: art_profile,
614                features,
615                filenames: destinations,
616                executable,
617                fresh,
618            }
619            .to_json_string();
620            state.stdout(msg)?;
621        }
622        Ok(())
623    }))
624}
625
626// For all plugin dependencies, add their -L paths (now calculated and present
627// in `build_script_outputs`) to the dynamic library load path for the command
628// to execute.
629fn add_plugin_deps(
630    rustc: &mut ProcessBuilder,
631    build_script_outputs: &BuildScriptOutputs,
632    build_scripts: &BuildScripts,
633    root_output: &Path,
634) -> CargoResult<()> {
635    let var = paths::dylib_path_envvar();
636    let search_path = rustc.get_env(var).unwrap_or_default();
637    let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>();
638    for (pkg_id, metadata) in &build_scripts.plugins {
639        let output = build_script_outputs
640            .get(*metadata)
641            .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?;
642        search_path.append(&mut filter_dynamic_search_path(
643            output.library_paths.iter(),
644            root_output,
645        ));
646    }
647    let search_path = paths::join_paths(&search_path, var)?;
648    rustc.env(var, &search_path);
649    Ok(())
650}
651
652// Determine paths to add to the dynamic search path from -L entries
653//
654// Strip off prefixes like "native=" or "framework=" and filter out directories
655// **not** inside our output directory since they are likely spurious and can cause
656// clashes with system shared libraries (issue #3366).
657fn filter_dynamic_search_path<'a, I>(paths: I, root_output: &Path) -> Vec<PathBuf>
658where
659    I: Iterator<Item = &'a PathBuf>,
660{
661    let mut search_path = vec![];
662    for dir in paths {
663        let dir = match dir.to_str().and_then(|s| s.split_once("=")) {
664            Some(("native" | "crate" | "dependency" | "framework" | "all", path)) => path.into(),
665            _ => dir.clone(),
666        };
667        if dir.starts_with(&root_output) {
668            search_path.push(dir);
669        } else {
670            debug!(
671                "Not including path {} in runtime library search path because it is \
672                 outside target root {}",
673                dir.display(),
674                root_output.display()
675            );
676        }
677    }
678    search_path
679}
680
681/// Prepares flags and environments we can compute for a `rustc` invocation
682/// before the job queue starts compiling any unit.
683///
684/// This builds a static view of the invocation. Flags depending on the
685/// completion of other units will be added later in runtime, such as flags
686/// from build scripts.
687fn prepare_rustc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
688    let is_primary = build_runner.is_primary_package(unit);
689    let is_workspace = build_runner.bcx.ws.is_member(&unit.pkg);
690
691    let mut base = build_runner
692        .compilation
693        .rustc_process(unit, is_primary, is_workspace)?;
694    build_base_args(build_runner, &mut base, unit)?;
695
696    base.inherit_jobserver(&build_runner.jobserver);
697    build_deps_args(&mut base, build_runner, unit)?;
698    add_cap_lints(build_runner.bcx, unit, &mut base);
699    if let Some(args) = build_runner.bcx.extra_args_for(unit) {
700        base.args(args);
701    }
702    base.args(&unit.rustflags);
703    if build_runner.bcx.gctx.cli_unstable().binary_dep_depinfo {
704        base.arg("-Z").arg("binary-dep-depinfo");
705    }
706    if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
707        base.arg("-Z").arg("checksum-hash-algorithm=blake3");
708    }
709
710    if is_primary {
711        base.env("CARGO_PRIMARY_PACKAGE", "1");
712    }
713
714    if unit.target.is_test() || unit.target.is_bench() {
715        let tmp = build_runner.files().layout(unit.kind).prepare_tmp()?;
716        base.env("CARGO_TARGET_TMPDIR", tmp.display().to_string());
717    }
718
719    Ok(base)
720}
721
722/// Prepares flags and environments we can compute for a `rustdoc` invocation
723/// before the job queue starts compiling any unit.
724///
725/// This builds a static view of the invocation. Flags depending on the
726/// completion of other units will be added later in runtime, such as flags
727/// from build scripts.
728fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
729    let bcx = build_runner.bcx;
730    // script_metadata is not needed here, it is only for tests.
731    let mut rustdoc = build_runner.compilation.rustdoc_process(unit, None)?;
732    rustdoc.inherit_jobserver(&build_runner.jobserver);
733    let crate_name = unit.target.crate_name();
734    rustdoc.arg("--crate-name").arg(&crate_name);
735    add_path_args(bcx.ws, unit, &mut rustdoc);
736    add_cap_lints(bcx, unit, &mut rustdoc);
737
738    if let CompileKind::Target(target) = unit.kind {
739        rustdoc.arg("--target").arg(target.rustc_target());
740    }
741    let doc_dir = build_runner.files().out_dir(unit);
742    rustdoc.arg("-o").arg(&doc_dir);
743    rustdoc.args(&features_args(unit));
744    rustdoc.args(&check_cfg_args(unit));
745
746    add_error_format_and_color(build_runner, &mut rustdoc);
747    add_allow_features(build_runner, &mut rustdoc);
748
749    if let Some(trim_paths) = unit.profile.trim_paths.as_ref() {
750        trim_paths_args_rustdoc(&mut rustdoc, build_runner, unit, trim_paths)?;
751    }
752
753    rustdoc.args(unit.pkg.manifest().lint_rustflags());
754
755    let metadata = build_runner.metadata_for_doc_units[unit];
756    rustdoc
757        .arg("-C")
758        .arg(format!("metadata={}", metadata.c_metadata()));
759
760    if unit.mode.is_doc_scrape() {
761        debug_assert!(build_runner.bcx.scrape_units.contains(unit));
762
763        if unit.target.is_test() {
764            rustdoc.arg("--scrape-tests");
765        }
766
767        rustdoc.arg("-Zunstable-options");
768
769        rustdoc
770            .arg("--scrape-examples-output-path")
771            .arg(scrape_output_path(build_runner, unit)?);
772
773        // Only scrape example for items from crates in the workspace, to reduce generated file size
774        for pkg in build_runner.bcx.packages.packages() {
775            let names = pkg
776                .targets()
777                .iter()
778                .map(|target| target.crate_name())
779                .collect::<HashSet<_>>();
780            for name in names {
781                rustdoc.arg("--scrape-examples-target-crate").arg(name);
782            }
783        }
784    }
785
786    if should_include_scrape_units(build_runner.bcx, unit) {
787        rustdoc.arg("-Zunstable-options");
788    }
789
790    build_deps_args(&mut rustdoc, build_runner, unit)?;
791    rustdoc::add_root_urls(build_runner, unit, &mut rustdoc)?;
792
793    rustdoc::add_output_format(build_runner, unit, &mut rustdoc)?;
794
795    if let Some(args) = build_runner.bcx.extra_args_for(unit) {
796        rustdoc.args(args);
797    }
798    rustdoc.args(&unit.rustdocflags);
799
800    if !crate_version_flag_already_present(&rustdoc) {
801        append_crate_version_flag(unit, &mut rustdoc);
802    }
803
804    Ok(rustdoc)
805}
806
807/// Creates a unit of work invoking `rustdoc` for documenting the `unit`.
808fn rustdoc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Work> {
809    let mut rustdoc = prepare_rustdoc(build_runner, unit)?;
810
811    let crate_name = unit.target.crate_name();
812    let doc_dir = build_runner.files().out_dir(unit);
813    // Create the documentation directory ahead of time as rustdoc currently has
814    // a bug where concurrent invocations will race to create this directory if
815    // it doesn't already exist.
816    paths::create_dir_all(&doc_dir)?;
817
818    let target_desc = unit.target.description_named();
819    let name = unit.pkg.name();
820    let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
821    let package_id = unit.pkg.package_id();
822    let manifest_path = PathBuf::from(unit.pkg.manifest_path());
823    let target = Target::clone(&unit.target);
824    let mut output_options = OutputOptions::new(build_runner, unit);
825    let script_metadata = build_runner.find_build_script_metadata(unit);
826    let scrape_outputs = if should_include_scrape_units(build_runner.bcx, unit) {
827        Some(
828            build_runner
829                .bcx
830                .scrape_units
831                .iter()
832                .map(|unit| {
833                    Ok((
834                        build_runner.files().metadata(unit).unit_id(),
835                        scrape_output_path(build_runner, unit)?,
836                    ))
837                })
838                .collect::<CargoResult<HashMap<_, _>>>()?,
839        )
840    } else {
841        None
842    };
843
844    let failed_scrape_units = Arc::clone(&build_runner.failed_scrape_units);
845    let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
846        && !matches!(
847            build_runner.bcx.gctx.shell().verbosity(),
848            Verbosity::Verbose
849        );
850    let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
851        make_failed_scrape_diagnostic(
852            build_runner,
853            unit,
854            format_args!("failed to scan {target_desc} in package `{name}` for example code usage"),
855        )
856    });
857    if hide_diagnostics_for_scrape_unit {
858        output_options.show_diagnostics = false;
859    }
860
861    Ok(Work::new(move |state| {
862        add_custom_flags(
863            &mut rustdoc,
864            &build_script_outputs.lock().unwrap(),
865            script_metadata,
866        )?;
867
868        // Add the output of scraped examples to the rustdoc command.
869        // This action must happen after the unit's dependencies have finished,
870        // because some of those deps may be Docscrape units which have failed.
871        // So we dynamically determine which `--with-examples` flags to pass here.
872        if let Some(scrape_outputs) = scrape_outputs {
873            let failed_scrape_units = failed_scrape_units.lock().unwrap();
874            for (metadata, output_path) in &scrape_outputs {
875                if !failed_scrape_units.contains(metadata) {
876                    rustdoc.arg("--with-examples").arg(output_path);
877                }
878            }
879        }
880
881        let crate_dir = doc_dir.join(&crate_name);
882        if crate_dir.exists() {
883            // Remove output from a previous build. This ensures that stale
884            // files for removed items are removed.
885            debug!("removing pre-existing doc directory {:?}", crate_dir);
886            paths::remove_dir_all(crate_dir)?;
887        }
888        state.running(&rustdoc);
889
890        let result = rustdoc
891            .exec_with_streaming(
892                &mut |line| on_stdout_line(state, line, package_id, &target),
893                &mut |line| {
894                    on_stderr_line(
895                        state,
896                        line,
897                        package_id,
898                        &manifest_path,
899                        &target,
900                        &mut output_options,
901                    )
902                },
903                false,
904            )
905            .map_err(verbose_if_simple_exit_code)
906            .with_context(|| format!("could not document `{}`", name));
907
908        if let Err(e) = result {
909            if let Some(diagnostic) = failed_scrape_diagnostic {
910                state.warning(diagnostic)?;
911            }
912
913            return Err(e);
914        }
915
916        Ok(())
917    }))
918}
919
920// The --crate-version flag could have already been passed in RUSTDOCFLAGS
921// or as an extra compiler argument for rustdoc
922fn crate_version_flag_already_present(rustdoc: &ProcessBuilder) -> bool {
923    rustdoc.get_args().any(|flag| {
924        flag.to_str()
925            .map_or(false, |flag| flag.starts_with(RUSTDOC_CRATE_VERSION_FLAG))
926    })
927}
928
929fn append_crate_version_flag(unit: &Unit, rustdoc: &mut ProcessBuilder) {
930    rustdoc
931        .arg(RUSTDOC_CRATE_VERSION_FLAG)
932        .arg(unit.pkg.version().to_string());
933}
934
935/// Adds [`--cap-lints`] to the command to execute.
936///
937/// [`--cap-lints`]: https://doc.rust-lang.org/nightly/rustc/lints/levels.html#capping-lints
938fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuilder) {
939    // If this is an upstream dep we don't want warnings from, turn off all
940    // lints.
941    if !unit.show_warnings(bcx.gctx) {
942        cmd.arg("--cap-lints").arg("allow");
943
944    // If this is an upstream dep but we *do* want warnings, make sure that they
945    // don't fail compilation.
946    } else if !unit.is_local() {
947        cmd.arg("--cap-lints").arg("warn");
948    }
949}
950
951/// Forwards [`-Zallow-features`] if it is set for cargo.
952///
953/// [`-Zallow-features`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#allow-features
954fn add_allow_features(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
955    if let Some(allow) = &build_runner.bcx.gctx.cli_unstable().allow_features {
956        use std::fmt::Write;
957        let mut arg = String::from("-Zallow-features=");
958        for f in allow {
959            let _ = write!(&mut arg, "{f},");
960        }
961        cmd.arg(arg.trim_end_matches(','));
962    }
963}
964
965/// Adds [`--error-format`] to the command to execute.
966///
967/// Cargo always uses JSON output. This has several benefits, such as being
968/// easier to parse, handles changing formats (for replaying cached messages),
969/// ensures atomic output (so messages aren't interleaved), allows for
970/// intercepting messages like rmeta artifacts, etc. rustc includes a
971/// "rendered" field in the JSON message with the message properly formatted,
972/// which Cargo will extract and display to the user.
973///
974/// [`--error-format`]: https://doc.rust-lang.org/nightly/rustc/command-line-arguments.html#--error-format-control-how-errors-are-produced
975fn add_error_format_and_color(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
976    cmd.arg("--error-format=json");
977    let mut json = String::from("--json=diagnostic-rendered-ansi,artifacts,future-incompat");
978
979    match build_runner.bcx.build_config.message_format {
980        MessageFormat::Short | MessageFormat::Json { short: true, .. } => {
981            json.push_str(",diagnostic-short");
982        }
983        _ => {}
984    }
985    cmd.arg(json);
986
987    let gctx = build_runner.bcx.gctx;
988    if let Some(width) = gctx.shell().err_width().diagnostic_terminal_width() {
989        cmd.arg(format!("--diagnostic-width={width}"));
990    }
991}
992
993/// Adds essential rustc flags and environment variables to the command to execute.
994fn build_base_args(
995    build_runner: &BuildRunner<'_, '_>,
996    cmd: &mut ProcessBuilder,
997    unit: &Unit,
998) -> CargoResult<()> {
999    assert!(!unit.mode.is_run_custom_build());
1000
1001    let bcx = build_runner.bcx;
1002    let Profile {
1003        ref opt_level,
1004        codegen_backend,
1005        codegen_units,
1006        debuginfo,
1007        debug_assertions,
1008        split_debuginfo,
1009        overflow_checks,
1010        rpath,
1011        ref panic,
1012        incremental,
1013        strip,
1014        rustflags: profile_rustflags,
1015        trim_paths,
1016        ..
1017    } = unit.profile.clone();
1018    let test = unit.mode.is_any_test();
1019
1020    cmd.arg("--crate-name").arg(&unit.target.crate_name());
1021
1022    let edition = unit.target.edition();
1023    edition.cmd_edition_arg(cmd);
1024
1025    add_path_args(bcx.ws, unit, cmd);
1026    add_error_format_and_color(build_runner, cmd);
1027    add_allow_features(build_runner, cmd);
1028
1029    let mut contains_dy_lib = false;
1030    if !test {
1031        for crate_type in &unit.target.rustc_crate_types() {
1032            cmd.arg("--crate-type").arg(crate_type.as_str());
1033            contains_dy_lib |= crate_type == &CrateType::Dylib;
1034        }
1035    }
1036
1037    if unit.mode.is_check() {
1038        cmd.arg("--emit=dep-info,metadata");
1039    } else if !unit.requires_upstream_objects() {
1040        // Always produce metadata files for rlib outputs. Metadata may be used
1041        // in this session for a pipelined compilation, or it may be used in a
1042        // future Cargo session as part of a pipelined compile.
1043        cmd.arg("--emit=dep-info,metadata,link");
1044    } else {
1045        cmd.arg("--emit=dep-info,link");
1046    }
1047
1048    let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
1049        || (contains_dy_lib && !build_runner.is_primary_package(unit));
1050    if prefer_dynamic {
1051        cmd.arg("-C").arg("prefer-dynamic");
1052    }
1053
1054    if opt_level.as_str() != "0" {
1055        cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
1056    }
1057
1058    if *panic != PanicStrategy::Unwind {
1059        cmd.arg("-C").arg(format!("panic={}", panic));
1060    }
1061
1062    cmd.args(&lto_args(build_runner, unit));
1063
1064    if let Some(backend) = codegen_backend {
1065        cmd.arg("-Z").arg(&format!("codegen-backend={}", backend));
1066    }
1067
1068    if let Some(n) = codegen_units {
1069        cmd.arg("-C").arg(&format!("codegen-units={}", n));
1070    }
1071
1072    let debuginfo = debuginfo.into_inner();
1073    // Shorten the number of arguments if possible.
1074    if debuginfo != TomlDebugInfo::None {
1075        cmd.arg("-C").arg(format!("debuginfo={debuginfo}"));
1076        // This is generally just an optimization on build time so if we don't
1077        // pass it then it's ok. The values for the flag (off, packed, unpacked)
1078        // may be supported or not depending on the platform, so availability is
1079        // checked per-value. For example, at the time of writing this code, on
1080        // Windows the only stable valid value for split-debuginfo is "packed",
1081        // while on Linux "unpacked" is also stable.
1082        if let Some(split) = split_debuginfo {
1083            if build_runner
1084                .bcx
1085                .target_data
1086                .info(unit.kind)
1087                .supports_debuginfo_split(split)
1088            {
1089                cmd.arg("-C").arg(format!("split-debuginfo={split}"));
1090            }
1091        }
1092    }
1093
1094    if let Some(trim_paths) = trim_paths {
1095        trim_paths_args(cmd, build_runner, unit, &trim_paths)?;
1096    }
1097
1098    cmd.args(unit.pkg.manifest().lint_rustflags());
1099    cmd.args(&profile_rustflags);
1100
1101    // `-C overflow-checks` is implied by the setting of `-C debug-assertions`,
1102    // so we only need to provide `-C overflow-checks` if it differs from
1103    // the value of `-C debug-assertions` we would provide.
1104    if opt_level.as_str() != "0" {
1105        if debug_assertions {
1106            cmd.args(&["-C", "debug-assertions=on"]);
1107            if !overflow_checks {
1108                cmd.args(&["-C", "overflow-checks=off"]);
1109            }
1110        } else if overflow_checks {
1111            cmd.args(&["-C", "overflow-checks=on"]);
1112        }
1113    } else if !debug_assertions {
1114        cmd.args(&["-C", "debug-assertions=off"]);
1115        if overflow_checks {
1116            cmd.args(&["-C", "overflow-checks=on"]);
1117        }
1118    } else if !overflow_checks {
1119        cmd.args(&["-C", "overflow-checks=off"]);
1120    }
1121
1122    if test && unit.target.harness() {
1123        cmd.arg("--test");
1124
1125        // Cargo has historically never compiled `--test` binaries with
1126        // `panic=abort` because the `test` crate itself didn't support it.
1127        // Support is now upstream, however, but requires an unstable flag to be
1128        // passed when compiling the test. We require, in Cargo, an unstable
1129        // flag to pass to rustc, so register that here. Eventually this flag
1130        // will simply not be needed when the behavior is stabilized in the Rust
1131        // compiler itself.
1132        if *panic == PanicStrategy::Abort {
1133            cmd.arg("-Z").arg("panic-abort-tests");
1134        }
1135    } else if test {
1136        cmd.arg("--cfg").arg("test");
1137    }
1138
1139    cmd.args(&features_args(unit));
1140    cmd.args(&check_cfg_args(unit));
1141
1142    let meta = build_runner.files().metadata(unit);
1143    cmd.arg("-C")
1144        .arg(&format!("metadata={}", meta.c_metadata()));
1145    if let Some(c_extra_filename) = meta.c_extra_filename() {
1146        cmd.arg("-C")
1147            .arg(&format!("extra-filename=-{c_extra_filename}"));
1148    }
1149
1150    if rpath {
1151        cmd.arg("-C").arg("rpath");
1152    }
1153
1154    cmd.arg("--out-dir")
1155        .arg(&build_runner.files().out_dir(unit));
1156
1157    fn opt(cmd: &mut ProcessBuilder, key: &str, prefix: &str, val: Option<&OsStr>) {
1158        if let Some(val) = val {
1159            let mut joined = OsString::from(prefix);
1160            joined.push(val);
1161            cmd.arg(key).arg(joined);
1162        }
1163    }
1164
1165    if let CompileKind::Target(n) = unit.kind {
1166        cmd.arg("--target").arg(n.rustc_target());
1167    }
1168
1169    opt(
1170        cmd,
1171        "-C",
1172        "linker=",
1173        build_runner
1174            .compilation
1175            .target_linker(unit.kind)
1176            .as_ref()
1177            .map(|s| s.as_ref()),
1178    );
1179    if incremental {
1180        let dir = build_runner
1181            .files()
1182            .layout(unit.kind)
1183            .incremental()
1184            .as_os_str();
1185        opt(cmd, "-C", "incremental=", Some(dir));
1186    }
1187
1188    let strip = strip.into_inner();
1189    if strip != StripInner::None {
1190        cmd.arg("-C").arg(format!("strip={}", strip));
1191    }
1192
1193    if unit.is_std {
1194        // -Zforce-unstable-if-unmarked prevents the accidental use of
1195        // unstable crates within the sysroot (such as "extern crate libc" or
1196        // any non-public crate in the sysroot).
1197        //
1198        // RUSTC_BOOTSTRAP allows unstable features on stable.
1199        cmd.arg("-Z")
1200            .arg("force-unstable-if-unmarked")
1201            .env("RUSTC_BOOTSTRAP", "1");
1202    }
1203
1204    // Add `CARGO_BIN_EXE_` environment variables for building tests.
1205    if unit.target.is_test() || unit.target.is_bench() {
1206        for bin_target in unit
1207            .pkg
1208            .manifest()
1209            .targets()
1210            .iter()
1211            .filter(|target| target.is_bin())
1212        {
1213            let exe_path = build_runner.files().bin_link_for_target(
1214                bin_target,
1215                unit.kind,
1216                build_runner.bcx,
1217            )?;
1218            let name = bin_target
1219                .binary_filename()
1220                .unwrap_or(bin_target.name().to_string());
1221            let key = format!("CARGO_BIN_EXE_{}", name);
1222            cmd.env(&key, exe_path);
1223        }
1224    }
1225    Ok(())
1226}
1227
1228/// All active features for the unit passed as `--cfg features=<feature-name>`.
1229fn features_args(unit: &Unit) -> Vec<OsString> {
1230    let mut args = Vec::with_capacity(unit.features.len() * 2);
1231
1232    for feat in &unit.features {
1233        args.push(OsString::from("--cfg"));
1234        args.push(OsString::from(format!("feature=\"{}\"", feat)));
1235    }
1236
1237    args
1238}
1239
1240/// Like [`trim_paths_args`] but for rustdoc invocations.
1241fn trim_paths_args_rustdoc(
1242    cmd: &mut ProcessBuilder,
1243    build_runner: &BuildRunner<'_, '_>,
1244    unit: &Unit,
1245    trim_paths: &TomlTrimPaths,
1246) -> CargoResult<()> {
1247    match trim_paths {
1248        // rustdoc supports diagnostics trimming only.
1249        TomlTrimPaths::Values(values) if !values.contains(&TomlTrimPathsValue::Diagnostics) => {
1250            return Ok(())
1251        }
1252        _ => {}
1253    }
1254
1255    // feature gate was checked during manifest/config parsing.
1256    cmd.arg("-Zunstable-options");
1257
1258    // Order of `--remap-path-prefix` flags is important for `-Zbuild-std`.
1259    // We want to show `/rustc/<hash>/library/std` instead of `std-0.0.0`.
1260    cmd.arg(package_remap(build_runner, unit));
1261    cmd.arg(sysroot_remap(build_runner, unit));
1262
1263    Ok(())
1264}
1265
1266/// Generates the `--remap-path-scope` and `--remap-path-prefix` for [RFC 3127].
1267/// See also unstable feature [`-Ztrim-paths`].
1268///
1269/// [RFC 3127]: https://rust-lang.github.io/rfcs/3127-trim-paths.html
1270/// [`-Ztrim-paths`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-trim-paths-option
1271fn trim_paths_args(
1272    cmd: &mut ProcessBuilder,
1273    build_runner: &BuildRunner<'_, '_>,
1274    unit: &Unit,
1275    trim_paths: &TomlTrimPaths,
1276) -> CargoResult<()> {
1277    if trim_paths.is_none() {
1278        return Ok(());
1279    }
1280
1281    // feature gate was checked during manifest/config parsing.
1282    cmd.arg("-Zunstable-options");
1283    cmd.arg(format!("-Zremap-path-scope={trim_paths}"));
1284
1285    // Order of `--remap-path-prefix` flags is important for `-Zbuild-std`.
1286    // We want to show `/rustc/<hash>/library/std` instead of `std-0.0.0`.
1287    cmd.arg(package_remap(build_runner, unit));
1288    cmd.arg(sysroot_remap(build_runner, unit));
1289
1290    Ok(())
1291}
1292
1293/// Path prefix remap rules for sysroot.
1294///
1295/// This remap logic aligns with rustc:
1296/// <https://github.com/rust-lang/rust/blob/c2ef3516/src/bootstrap/src/lib.rs#L1113-L1116>
1297fn sysroot_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1298    let mut remap = OsString::from("--remap-path-prefix=");
1299    remap.push({
1300        // See also `detect_sysroot_src_path()`.
1301        let mut sysroot = build_runner.bcx.target_data.info(unit.kind).sysroot.clone();
1302        sysroot.push("lib");
1303        sysroot.push("rustlib");
1304        sysroot.push("src");
1305        sysroot.push("rust");
1306        sysroot
1307    });
1308    remap.push("=");
1309    remap.push("/rustc/");
1310    if let Some(commit_hash) = build_runner.bcx.rustc().commit_hash.as_ref() {
1311        remap.push(commit_hash);
1312    } else {
1313        remap.push(build_runner.bcx.rustc().version.to_string());
1314    }
1315    remap
1316}
1317
1318/// Path prefix remap rules for dependencies.
1319///
1320/// * Git dependencies: remove `~/.cargo/git/checkouts` prefix.
1321/// * Registry dependencies: remove `~/.cargo/registry/src` prefix.
1322/// * Others (e.g. path dependencies):
1323///     * relative paths to workspace root if inside the workspace directory.
1324///     * otherwise remapped to `<pkg>-<version>`.
1325fn package_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1326    let pkg_root = unit.pkg.root();
1327    let ws_root = build_runner.bcx.ws.root();
1328    let mut remap = OsString::from("--remap-path-prefix=");
1329    let source_id = unit.pkg.package_id().source_id();
1330    if source_id.is_git() {
1331        remap.push(
1332            build_runner
1333                .bcx
1334                .gctx
1335                .git_checkouts_path()
1336                .as_path_unlocked(),
1337        );
1338        remap.push("=");
1339    } else if source_id.is_registry() {
1340        remap.push(
1341            build_runner
1342                .bcx
1343                .gctx
1344                .registry_source_path()
1345                .as_path_unlocked(),
1346        );
1347        remap.push("=");
1348    } else if pkg_root.strip_prefix(ws_root).is_ok() {
1349        remap.push(ws_root);
1350        remap.push("=."); // remap to relative rustc work dir explicitly
1351    } else {
1352        remap.push(pkg_root);
1353        remap.push("=");
1354        remap.push(unit.pkg.name());
1355        remap.push("-");
1356        remap.push(unit.pkg.version().to_string());
1357    }
1358    remap
1359}
1360
1361/// Generates the `--check-cfg` arguments for the `unit`.
1362fn check_cfg_args(unit: &Unit) -> Vec<OsString> {
1363    // The routine below generates the --check-cfg arguments. Our goals here are to
1364    // enable the checking of conditionals and pass the list of declared features.
1365    //
1366    // In the simplified case, it would resemble something like this:
1367    //
1368    //   --check-cfg=cfg() --check-cfg=cfg(feature, values(...))
1369    //
1370    // but having `cfg()` is redundant with the second argument (as well-known names
1371    // and values are implicitly enabled when one or more `--check-cfg` argument is
1372    // passed) so we don't emit it and just pass:
1373    //
1374    //   --check-cfg=cfg(feature, values(...))
1375    //
1376    // This way, even if there are no declared features, the config `feature` will
1377    // still be expected, meaning users would get "unexpected value" instead of name.
1378    // This wasn't always the case, see rust-lang#119930 for some details.
1379
1380    let gross_cap_estimation = unit.pkg.summary().features().len() * 7 + 25;
1381    let mut arg_feature = OsString::with_capacity(gross_cap_estimation);
1382
1383    arg_feature.push("cfg(feature, values(");
1384    for (i, feature) in unit.pkg.summary().features().keys().enumerate() {
1385        if i != 0 {
1386            arg_feature.push(", ");
1387        }
1388        arg_feature.push("\"");
1389        arg_feature.push(feature);
1390        arg_feature.push("\"");
1391    }
1392    arg_feature.push("))");
1393
1394    // In addition to the package features, we also include the `test` cfg (since
1395    // compiler-team#785, as to be able to someday apply yt conditionaly), as well
1396    // the `docsrs` cfg from the docs.rs service.
1397    //
1398    // We include `docsrs` here (in Cargo) instead of rustc, since there is a much closer
1399    // relationship between Cargo and docs.rs than rustc and docs.rs. In particular, all
1400    // users of docs.rs use Cargo, but not all users of rustc (like Rust-for-Linux) use docs.rs.
1401
1402    vec![
1403        OsString::from("--check-cfg"),
1404        OsString::from("cfg(docsrs,test)"),
1405        OsString::from("--check-cfg"),
1406        arg_feature,
1407    ]
1408}
1409
1410/// Adds LTO related codegen flags.
1411fn lto_args(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> Vec<OsString> {
1412    let mut result = Vec::new();
1413    let mut push = |arg: &str| {
1414        result.push(OsString::from("-C"));
1415        result.push(OsString::from(arg));
1416    };
1417    match build_runner.lto[unit] {
1418        lto::Lto::Run(None) => push("lto"),
1419        lto::Lto::Run(Some(s)) => push(&format!("lto={}", s)),
1420        lto::Lto::Off => {
1421            push("lto=off");
1422            push("embed-bitcode=no");
1423        }
1424        lto::Lto::ObjectAndBitcode => {} // this is rustc's default
1425        lto::Lto::OnlyBitcode => push("linker-plugin-lto"),
1426        lto::Lto::OnlyObject => push("embed-bitcode=no"),
1427    }
1428    result
1429}
1430
1431/// Adds dependency-relevant rustc flags and environment variables
1432/// to the command to execute, such as [`-L`] and [`--extern`].
1433///
1434/// [`-L`]: https://doc.rust-lang.org/nightly/rustc/command-line-arguments.html#-l-add-a-directory-to-the-library-search-path
1435/// [`--extern`]: https://doc.rust-lang.org/nightly/rustc/command-line-arguments.html#--extern-specify-where-an-external-library-is-located
1436fn build_deps_args(
1437    cmd: &mut ProcessBuilder,
1438    build_runner: &BuildRunner<'_, '_>,
1439    unit: &Unit,
1440) -> CargoResult<()> {
1441    let bcx = build_runner.bcx;
1442    cmd.arg("-L").arg(&{
1443        let mut deps = OsString::from("dependency=");
1444        deps.push(build_runner.files().deps_dir(unit));
1445        deps
1446    });
1447
1448    // Be sure that the host path is also listed. This'll ensure that proc macro
1449    // dependencies are correctly found (for reexported macros).
1450    if !unit.kind.is_host() {
1451        cmd.arg("-L").arg(&{
1452            let mut deps = OsString::from("dependency=");
1453            deps.push(build_runner.files().host_deps());
1454            deps
1455        });
1456    }
1457
1458    let deps = build_runner.unit_deps(unit);
1459
1460    // If there is not one linkable target but should, rustc fails later
1461    // on if there is an `extern crate` for it. This may turn into a hard
1462    // error in the future (see PR #4797).
1463    if !deps
1464        .iter()
1465        .any(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_linkable())
1466    {
1467        if let Some(dep) = deps.iter().find(|dep| {
1468            !dep.unit.mode.is_doc() && dep.unit.target.is_lib() && !dep.unit.artifact.is_true()
1469        }) {
1470            bcx.gctx.shell().warn(format!(
1471                "The package `{}` \
1472                 provides no linkable target. The compiler might raise an error while compiling \
1473                 `{}`. Consider adding 'dylib' or 'rlib' to key `crate-type` in `{}`'s \
1474                 Cargo.toml. This warning might turn into a hard error in the future.",
1475                dep.unit.target.crate_name(),
1476                unit.target.crate_name(),
1477                dep.unit.target.crate_name()
1478            ))?;
1479        }
1480    }
1481
1482    let mut unstable_opts = false;
1483
1484    for dep in deps {
1485        if dep.unit.mode.is_run_custom_build() {
1486            cmd.env(
1487                "OUT_DIR",
1488                &build_runner.files().build_script_out_dir(&dep.unit),
1489            );
1490        }
1491    }
1492
1493    for arg in extern_args(build_runner, unit, &mut unstable_opts)? {
1494        cmd.arg(arg);
1495    }
1496
1497    for (var, env) in artifact::get_env(build_runner, deps)? {
1498        cmd.env(&var, env);
1499    }
1500
1501    // This will only be set if we're already using a feature
1502    // requiring nightly rust
1503    if unstable_opts {
1504        cmd.arg("-Z").arg("unstable-options");
1505    }
1506
1507    Ok(())
1508}
1509
1510/// Adds extra rustc flags and environment variables collected from the output
1511/// of a build-script to the command to execute, include custom environment
1512/// variables and `cfg`.
1513fn add_custom_flags(
1514    cmd: &mut ProcessBuilder,
1515    build_script_outputs: &BuildScriptOutputs,
1516    metadata: Option<UnitHash>,
1517) -> CargoResult<()> {
1518    if let Some(metadata) = metadata {
1519        if let Some(output) = build_script_outputs.get(metadata) {
1520            for cfg in output.cfgs.iter() {
1521                cmd.arg("--cfg").arg(cfg);
1522            }
1523            for check_cfg in &output.check_cfgs {
1524                cmd.arg("--check-cfg").arg(check_cfg);
1525            }
1526            for (name, value) in output.env.iter() {
1527                cmd.env(name, value);
1528            }
1529        }
1530    }
1531
1532    Ok(())
1533}
1534
1535/// Generates a list of `--extern` arguments.
1536pub fn extern_args(
1537    build_runner: &BuildRunner<'_, '_>,
1538    unit: &Unit,
1539    unstable_opts: &mut bool,
1540) -> CargoResult<Vec<OsString>> {
1541    let mut result = Vec::new();
1542    let deps = build_runner.unit_deps(unit);
1543
1544    // Closure to add one dependency to `result`.
1545    let mut link_to =
1546        |dep: &UnitDep, extern_crate_name: InternedString, noprelude: bool| -> CargoResult<()> {
1547            let mut value = OsString::new();
1548            let mut opts = Vec::new();
1549            let is_public_dependency_enabled = unit
1550                .pkg
1551                .manifest()
1552                .unstable_features()
1553                .require(Feature::public_dependency())
1554                .is_ok()
1555                || build_runner.bcx.gctx.cli_unstable().public_dependency;
1556            if !dep.public && unit.target.is_lib() && is_public_dependency_enabled {
1557                opts.push("priv");
1558                *unstable_opts = true;
1559            }
1560            if noprelude {
1561                opts.push("noprelude");
1562                *unstable_opts = true;
1563            }
1564            if !opts.is_empty() {
1565                value.push(opts.join(","));
1566                value.push(":");
1567            }
1568            value.push(extern_crate_name.as_str());
1569            value.push("=");
1570
1571            let mut pass = |file| {
1572                let mut value = value.clone();
1573                value.push(file);
1574                result.push(OsString::from("--extern"));
1575                result.push(value);
1576            };
1577
1578            let outputs = build_runner.outputs(&dep.unit)?;
1579
1580            if build_runner.only_requires_rmeta(unit, &dep.unit) || dep.unit.mode.is_check() {
1581                // Example: rlib dependency for an rlib, rmeta is all that is required.
1582                let output = outputs
1583                    .iter()
1584                    .find(|output| output.flavor == FileFlavor::Rmeta)
1585                    .expect("failed to find rmeta dep for pipelined dep");
1586                pass(&output.path);
1587            } else {
1588                // Example: a bin needs `rlib` for dependencies, it cannot use rmeta.
1589                for output in outputs.iter() {
1590                    if output.flavor == FileFlavor::Linkable {
1591                        pass(&output.path);
1592                    }
1593                }
1594            }
1595            Ok(())
1596        };
1597
1598    for dep in deps {
1599        if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
1600            link_to(dep, dep.extern_crate_name, dep.noprelude)?;
1601        }
1602    }
1603    if unit.target.proc_macro() {
1604        // Automatically import `proc_macro`.
1605        result.push(OsString::from("--extern"));
1606        result.push(OsString::from("proc_macro"));
1607    }
1608
1609    Ok(result)
1610}
1611
1612fn envify(s: &str) -> String {
1613    s.chars()
1614        .flat_map(|c| c.to_uppercase())
1615        .map(|c| if c == '-' { '_' } else { c })
1616        .collect()
1617}
1618
1619/// Configuration of the display of messages emitted by the compiler,
1620/// e.g. diagnostics, warnings, errors, and message caching.
1621struct OutputOptions {
1622    /// What format we're emitting from Cargo itself.
1623    format: MessageFormat,
1624    /// Where to write the JSON messages to support playback later if the unit
1625    /// is fresh. The file is created lazily so that in the normal case, lots
1626    /// of empty files are not created. If this is None, the output will not
1627    /// be cached (such as when replaying cached messages).
1628    cache_cell: Option<(PathBuf, LazyCell<File>)>,
1629    /// If `true`, display any diagnostics.
1630    /// Other types of JSON messages are processed regardless
1631    /// of the value of this flag.
1632    ///
1633    /// This is used primarily for cache replay. If you build with `-vv`, the
1634    /// cache will be filled with diagnostics from dependencies. When the
1635    /// cache is replayed without `-vv`, we don't want to show them.
1636    show_diagnostics: bool,
1637    /// Tracks the number of warnings we've seen so far.
1638    warnings_seen: usize,
1639    /// Tracks the number of errors we've seen so far.
1640    errors_seen: usize,
1641}
1642
1643impl OutputOptions {
1644    fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions {
1645        let path = build_runner.files().message_cache_path(unit);
1646        // Remove old cache, ignore ENOENT, which is the common case.
1647        drop(fs::remove_file(&path));
1648        let cache_cell = Some((path, LazyCell::new()));
1649        let show_diagnostics =
1650            build_runner.bcx.gctx.warning_handling().unwrap_or_default() != WarningHandling::Allow;
1651        OutputOptions {
1652            format: build_runner.bcx.build_config.message_format,
1653            cache_cell,
1654            show_diagnostics,
1655            warnings_seen: 0,
1656            errors_seen: 0,
1657        }
1658    }
1659}
1660
1661fn on_stdout_line(
1662    state: &JobState<'_, '_>,
1663    line: &str,
1664    _package_id: PackageId,
1665    _target: &Target,
1666) -> CargoResult<()> {
1667    state.stdout(line.to_string())?;
1668    Ok(())
1669}
1670
1671fn on_stderr_line(
1672    state: &JobState<'_, '_>,
1673    line: &str,
1674    package_id: PackageId,
1675    manifest_path: &std::path::Path,
1676    target: &Target,
1677    options: &mut OutputOptions,
1678) -> CargoResult<()> {
1679    if on_stderr_line_inner(state, line, package_id, manifest_path, target, options)? {
1680        // Check if caching is enabled.
1681        if let Some((path, cell)) = &mut options.cache_cell {
1682            // Cache the output, which will be replayed later when Fresh.
1683            let f = cell.try_borrow_mut_with(|| paths::create(path))?;
1684            debug_assert!(!line.contains('\n'));
1685            f.write_all(line.as_bytes())?;
1686            f.write_all(&[b'\n'])?;
1687        }
1688    }
1689    Ok(())
1690}
1691
1692/// Returns true if the line should be cached.
1693fn on_stderr_line_inner(
1694    state: &JobState<'_, '_>,
1695    line: &str,
1696    package_id: PackageId,
1697    manifest_path: &std::path::Path,
1698    target: &Target,
1699    options: &mut OutputOptions,
1700) -> CargoResult<bool> {
1701    // We primarily want to use this function to process JSON messages from
1702    // rustc. The compiler should always print one JSON message per line, and
1703    // otherwise it may have other output intermingled (think RUST_LOG or
1704    // something like that), so skip over everything that doesn't look like a
1705    // JSON message.
1706    if !line.starts_with('{') {
1707        state.stderr(line.to_string())?;
1708        return Ok(true);
1709    }
1710
1711    let mut compiler_message: Box<serde_json::value::RawValue> = match serde_json::from_str(line) {
1712        Ok(msg) => msg,
1713
1714        // If the compiler produced a line that started with `{` but it wasn't
1715        // valid JSON, maybe it wasn't JSON in the first place! Forward it along
1716        // to stderr.
1717        Err(e) => {
1718            debug!("failed to parse json: {:?}", e);
1719            state.stderr(line.to_string())?;
1720            return Ok(true);
1721        }
1722    };
1723
1724    let count_diagnostic = |level, options: &mut OutputOptions| {
1725        if level == "warning" {
1726            options.warnings_seen += 1;
1727        } else if level == "error" {
1728            options.errors_seen += 1;
1729        }
1730    };
1731
1732    if let Ok(report) = serde_json::from_str::<FutureIncompatReport>(compiler_message.get()) {
1733        for item in &report.future_incompat_report {
1734            count_diagnostic(&*item.diagnostic.level, options);
1735        }
1736        state.future_incompat_report(report.future_incompat_report);
1737        return Ok(true);
1738    }
1739
1740    // Depending on what we're emitting from Cargo itself, we figure out what to
1741    // do with this JSON message.
1742    match options.format {
1743        // In the "human" output formats (human/short) or if diagnostic messages
1744        // from rustc aren't being included in the output of Cargo's JSON
1745        // messages then we extract the diagnostic (if present) here and handle
1746        // it ourselves.
1747        MessageFormat::Human
1748        | MessageFormat::Short
1749        | MessageFormat::Json {
1750            render_diagnostics: true,
1751            ..
1752        } => {
1753            #[derive(serde::Deserialize)]
1754            struct CompilerMessage<'a> {
1755                // `rendered` contains escape sequences, which can't be
1756                // zero-copy deserialized by serde_json.
1757                // See https://github.com/serde-rs/json/issues/742
1758                rendered: String,
1759                #[serde(borrow)]
1760                message: Cow<'a, str>,
1761                #[serde(borrow)]
1762                level: Cow<'a, str>,
1763                children: Vec<PartialDiagnostic>,
1764            }
1765
1766            // A partial rustfix::diagnostics::Diagnostic. We deserialize only a
1767            // subset of the fields because rustc's output can be extremely
1768            // deeply nested JSON in pathological cases involving macro
1769            // expansion. Rustfix's Diagnostic struct is recursive containing a
1770            // field `children: Vec<Self>`, and it can cause deserialization to
1771            // hit serde_json's default recursion limit, or overflow the stack
1772            // if we turn that off. Cargo only cares about the 1 field listed
1773            // here.
1774            #[derive(serde::Deserialize)]
1775            struct PartialDiagnostic {
1776                spans: Vec<PartialDiagnosticSpan>,
1777            }
1778
1779            // A partial rustfix::diagnostics::DiagnosticSpan.
1780            #[derive(serde::Deserialize)]
1781            struct PartialDiagnosticSpan {
1782                suggestion_applicability: Option<Applicability>,
1783            }
1784
1785            if let Ok(mut msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
1786            {
1787                if msg.message.starts_with("aborting due to")
1788                    || msg.message.ends_with("warning emitted")
1789                    || msg.message.ends_with("warnings emitted")
1790                {
1791                    // Skip this line; we'll print our own summary at the end.
1792                    return Ok(true);
1793                }
1794                // state.stderr will add a newline
1795                if msg.rendered.ends_with('\n') {
1796                    msg.rendered.pop();
1797                }
1798                let rendered = msg.rendered;
1799                if options.show_diagnostics {
1800                    let machine_applicable: bool = msg
1801                        .children
1802                        .iter()
1803                        .map(|child| {
1804                            child
1805                                .spans
1806                                .iter()
1807                                .filter_map(|span| span.suggestion_applicability)
1808                                .any(|app| app == Applicability::MachineApplicable)
1809                        })
1810                        .any(|b| b);
1811                    count_diagnostic(&msg.level, options);
1812                    state.emit_diag(&msg.level, rendered, machine_applicable)?;
1813                }
1814                return Ok(true);
1815            }
1816        }
1817
1818        // Remove color information from the rendered string if color is not
1819        // enabled. Cargo always asks for ANSI colors from rustc. This allows
1820        // cached replay to enable/disable colors without re-invoking rustc.
1821        MessageFormat::Json { ansi: false, .. } => {
1822            #[derive(serde::Deserialize, serde::Serialize)]
1823            struct CompilerMessage<'a> {
1824                rendered: String,
1825                #[serde(flatten, borrow)]
1826                other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
1827            }
1828            if let Ok(mut error) =
1829                serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
1830            {
1831                error.rendered = anstream::adapter::strip_str(&error.rendered).to_string();
1832                let new_line = serde_json::to_string(&error)?;
1833                compiler_message = serde_json::value::RawValue::from_string(new_line)?;
1834            }
1835        }
1836
1837        // If ansi colors are desired then we should be good to go! We can just
1838        // pass through this message as-is.
1839        MessageFormat::Json { ansi: true, .. } => {}
1840    }
1841
1842    // We always tell rustc to emit messages about artifacts being produced.
1843    // These messages feed into pipelined compilation, as well as timing
1844    // information.
1845    //
1846    // Look for a matching directive and inform Cargo internally that a
1847    // metadata file has been produced.
1848    #[derive(serde::Deserialize)]
1849    struct ArtifactNotification<'a> {
1850        #[serde(borrow)]
1851        artifact: Cow<'a, str>,
1852    }
1853
1854    if let Ok(artifact) = serde_json::from_str::<ArtifactNotification<'_>>(compiler_message.get()) {
1855        trace!("found directive from rustc: `{}`", artifact.artifact);
1856        if artifact.artifact.ends_with(".rmeta") {
1857            debug!("looks like metadata finished early!");
1858            state.rmeta_produced();
1859        }
1860        return Ok(false);
1861    }
1862
1863    // And failing all that above we should have a legitimate JSON diagnostic
1864    // from the compiler, so wrap it in an external Cargo JSON message
1865    // indicating which package it came from and then emit it.
1866
1867    if !options.show_diagnostics {
1868        return Ok(true);
1869    }
1870
1871    #[derive(serde::Deserialize)]
1872    struct CompilerMessage<'a> {
1873        #[serde(borrow)]
1874        message: Cow<'a, str>,
1875        #[serde(borrow)]
1876        level: Cow<'a, str>,
1877    }
1878
1879    if let Ok(msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get()) {
1880        if msg.message.starts_with("aborting due to")
1881            || msg.message.ends_with("warning emitted")
1882            || msg.message.ends_with("warnings emitted")
1883        {
1884            // Skip this line; we'll print our own summary at the end.
1885            return Ok(true);
1886        }
1887        count_diagnostic(&msg.level, options);
1888    }
1889
1890    let msg = machine_message::FromCompiler {
1891        package_id: package_id.to_spec(),
1892        manifest_path,
1893        target,
1894        message: compiler_message,
1895    }
1896    .to_json_string();
1897
1898    // Switch json lines from rustc/rustdoc that appear on stderr to stdout
1899    // instead. We want the stdout of Cargo to always be machine parseable as
1900    // stderr has our colorized human-readable messages.
1901    state.stdout(msg)?;
1902    Ok(true)
1903}
1904
1905/// Creates a unit of work that replays the cached compiler message.
1906///
1907/// Usually used when a job is fresh and doesn't need to recompile.
1908fn replay_output_cache(
1909    package_id: PackageId,
1910    manifest_path: PathBuf,
1911    target: &Target,
1912    path: PathBuf,
1913    format: MessageFormat,
1914    show_diagnostics: bool,
1915) -> Work {
1916    let target = target.clone();
1917    let mut options = OutputOptions {
1918        format,
1919        cache_cell: None,
1920        show_diagnostics,
1921        warnings_seen: 0,
1922        errors_seen: 0,
1923    };
1924    Work::new(move |state| {
1925        if !path.exists() {
1926            // No cached output, probably didn't emit anything.
1927            return Ok(());
1928        }
1929        // We sometimes have gigabytes of output from the compiler, so avoid
1930        // loading it all into memory at once, as that can cause OOM where
1931        // otherwise there would be none.
1932        let file = paths::open(&path)?;
1933        let mut reader = std::io::BufReader::new(file);
1934        let mut line = String::new();
1935        loop {
1936            let length = reader.read_line(&mut line)?;
1937            if length == 0 {
1938                break;
1939            }
1940            let trimmed = line.trim_end_matches(&['\n', '\r'][..]);
1941            on_stderr_line(
1942                state,
1943                trimmed,
1944                package_id,
1945                &manifest_path,
1946                &target,
1947                &mut options,
1948            )?;
1949            line.clear();
1950        }
1951        Ok(())
1952    })
1953}
1954
1955/// Provides a package name with descriptive target information,
1956/// e.g., '`foo` (bin "bar" test)', '`foo` (lib doctest)'.
1957fn descriptive_pkg_name(name: &str, target: &Target, mode: &CompileMode) -> String {
1958    let desc_name = target.description_named();
1959    let mode = if mode.is_rustc_test() && !(target.is_test() || target.is_bench()) {
1960        " test"
1961    } else if mode.is_doc_test() {
1962        " doctest"
1963    } else if mode.is_doc() {
1964        " doc"
1965    } else {
1966        ""
1967    };
1968    format!("`{name}` ({desc_name}{mode})")
1969}
1970
1971/// Applies environment variables from config `[env]` to [`ProcessBuilder`].
1972pub(crate) fn apply_env_config(
1973    gctx: &crate::GlobalContext,
1974    cmd: &mut ProcessBuilder,
1975) -> CargoResult<()> {
1976    for (key, value) in gctx.env_config()?.iter() {
1977        // never override a value that has already been set by cargo
1978        if cmd.get_envs().contains_key(key) {
1979            continue;
1980        }
1981        cmd.env(key, value);
1982    }
1983    Ok(())
1984}
1985
1986/// Checks if there are some scrape units waiting to be processed.
1987fn should_include_scrape_units(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
1988    unit.mode.is_doc() && bcx.scrape_units.len() > 0 && bcx.ws.unit_needs_doc_scrape(unit)
1989}
1990
1991/// Gets the file path of function call information output from `rustdoc`.
1992fn scrape_output_path(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<PathBuf> {
1993    assert!(unit.mode.is_doc() || unit.mode.is_doc_scrape());
1994    build_runner
1995        .outputs(unit)
1996        .map(|outputs| outputs[0].path.clone())
1997}