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. 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//! [`ops::cargo_compile::compile`]: crate::ops::compile
30
31pub mod artifact;
32mod build_config;
33pub(crate) mod build_context;
34pub(crate) mod build_runner;
35mod compilation;
36mod compile_kind;
37mod crate_type;
38mod custom_build;
39pub(crate) mod fingerprint;
40pub mod future_incompat;
41pub(crate) mod job_queue;
42pub(crate) mod layout;
43mod links;
44mod lto;
45mod output_depinfo;
46mod output_sbom;
47pub mod rustdoc;
48pub mod standard_lib;
49pub mod timings;
50mod unit;
51pub mod unit_dependencies;
52pub mod unit_graph;
53
54use std::borrow::Cow;
55use std::cell::OnceCell;
56use std::collections::{BTreeMap, HashMap, HashSet};
57use std::env;
58use std::ffi::{OsStr, OsString};
59use std::fmt::Display;
60use std::fs::{self, File};
61use std::io::{BufRead, BufWriter, Write};
62use std::ops::Range;
63use std::path::{Path, PathBuf};
64use std::sync::{Arc, LazyLock};
65
66use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
67use anyhow::{Context as _, Error};
68use cargo_platform::{Cfg, Platform};
69use itertools::Itertools;
70use regex::Regex;
71use tracing::{debug, instrument, trace};
72
73pub use self::build_config::UserIntent;
74pub use self::build_config::{BuildConfig, CompileMode, MessageFormat, TimingOutput};
75pub use self::build_context::BuildContext;
76pub use self::build_context::FileFlavor;
77pub use self::build_context::FileType;
78pub use self::build_context::RustcTargetData;
79pub use self::build_context::TargetInfo;
80pub use self::build_runner::{BuildRunner, Metadata, UnitHash};
81pub use self::compilation::{Compilation, Doctest, UnitOutput};
82pub use self::compile_kind::{CompileKind, CompileKindFallback, CompileTarget};
83pub use self::crate_type::CrateType;
84pub use self::custom_build::LinkArgTarget;
85pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts, LibraryPath};
86pub(crate) use self::fingerprint::DirtyReason;
87pub use self::fingerprint::RustdocFingerprint;
88pub use self::job_queue::Freshness;
89use self::job_queue::{Job, JobQueue, JobState, Work};
90pub(crate) use self::layout::Layout;
91pub use self::lto::Lto;
92use self::output_depinfo::output_depinfo;
93use self::output_sbom::build_sbom;
94use self::unit_graph::UnitDep;
95
96use crate::core::compiler::future_incompat::FutureIncompatReport;
97use crate::core::compiler::timings::SectionTiming;
98pub use crate::core::compiler::unit::{Unit, UnitInterner};
99use crate::core::manifest::TargetSourcePath;
100use crate::core::profiles::{PanicStrategy, Profile, StripInner};
101use crate::core::{Feature, PackageId, Target, Verbosity};
102use crate::lints::get_key_value;
103use crate::util::OnceExt;
104use crate::util::context::WarningHandling;
105use crate::util::errors::{CargoResult, VerboseError};
106use crate::util::interning::InternedString;
107use crate::util::machine_message::{self, Message};
108use crate::util::{add_path_args, internal, path_args};
109
110use cargo_util::{ProcessBuilder, ProcessError, paths};
111use cargo_util_schemas::manifest::TomlDebugInfo;
112use cargo_util_schemas::manifest::TomlTrimPaths;
113use cargo_util_schemas::manifest::TomlTrimPathsValue;
114use rustfix::diagnostics::Applicability;
115pub(crate) use timings::CompilationSection;
116
117const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version";
118
119/// A glorified callback for executing calls to rustc. Rather than calling rustc
120/// directly, we'll use an `Executor`, giving clients an opportunity to intercept
121/// the build calls.
122pub trait Executor: Send + Sync + 'static {
123    /// Called after a rustc process invocation is prepared up-front for a given
124    /// unit of work (may still be modified for runtime-known dependencies, when
125    /// the work is actually executed).
126    fn init(&self, _build_runner: &BuildRunner<'_, '_>, _unit: &Unit) {}
127
128    /// In case of an `Err`, Cargo will not continue with the build process for
129    /// this package.
130    fn exec(
131        &self,
132        cmd: &ProcessBuilder,
133        id: PackageId,
134        target: &Target,
135        mode: CompileMode,
136        on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
137        on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
138    ) -> CargoResult<()>;
139
140    /// Queried when queuing each unit of work. If it returns true, then the
141    /// unit will always be rebuilt, independent of whether it needs to be.
142    fn force_rebuild(&self, _unit: &Unit) -> bool {
143        false
144    }
145}
146
147/// A `DefaultExecutor` calls rustc without doing anything else. It is Cargo's
148/// default behaviour.
149#[derive(Copy, Clone)]
150pub struct DefaultExecutor;
151
152impl Executor for DefaultExecutor {
153    #[instrument(name = "rustc", skip_all, fields(package = id.name().as_str(), process = cmd.to_string()))]
154    fn exec(
155        &self,
156        cmd: &ProcessBuilder,
157        id: PackageId,
158        _target: &Target,
159        _mode: CompileMode,
160        on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
161        on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
162    ) -> CargoResult<()> {
163        cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
164            .map(drop)
165    }
166}
167
168/// Builds up and enqueue a list of pending jobs onto the `job` queue.
169///
170/// Starting from the `unit`, this function recursively calls itself to build
171/// all jobs for dependencies of the `unit`. Each of these jobs represents
172/// compiling a particular package.
173///
174/// Note that **no actual work is executed as part of this**, that's all done
175/// next as part of [`JobQueue::execute`] function which will run everything
176/// in order with proper parallelism.
177#[tracing::instrument(skip(build_runner, jobs, exec))]
178fn compile<'gctx>(
179    build_runner: &mut BuildRunner<'_, 'gctx>,
180    jobs: &mut JobQueue<'gctx>,
181    unit: &Unit,
182    exec: &Arc<dyn Executor>,
183    force_rebuild: bool,
184) -> CargoResult<()> {
185    let bcx = build_runner.bcx;
186    if !build_runner.compiled.insert(unit.clone()) {
187        return Ok(());
188    }
189
190    // If we are in `--compile-time-deps` and the given unit is not a compile time
191    // dependency, skip compiling the unit and jumps to dependencies, which still
192    // have chances to be compile time dependencies
193    if !unit.skip_non_compile_time_dep {
194        // Build up the work to be done to compile this unit, enqueuing it once
195        // we've got everything constructed.
196        fingerprint::prepare_init(build_runner, unit)?;
197
198        let job = if unit.mode.is_run_custom_build() {
199            custom_build::prepare(build_runner, unit)?
200        } else if unit.mode.is_doc_test() {
201            // We run these targets later, so this is just a no-op for now.
202            Job::new_fresh()
203        } else {
204            let force = exec.force_rebuild(unit) || force_rebuild;
205            let mut job = fingerprint::prepare_target(build_runner, unit, force)?;
206            job.before(if job.freshness().is_dirty() {
207                let work = if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
208                    rustdoc(build_runner, unit)?
209                } else {
210                    rustc(build_runner, unit, exec)?
211                };
212                work.then(link_targets(build_runner, unit, false)?)
213            } else {
214                // We always replay the output cache,
215                // since it might contain future-incompat-report messages
216                let show_diagnostics = unit.show_warnings(bcx.gctx)
217                    && build_runner.bcx.gctx.warning_handling()? != WarningHandling::Allow;
218                let manifest = ManifestErrorContext::new(build_runner, unit);
219                let work = replay_output_cache(
220                    unit.pkg.package_id(),
221                    manifest,
222                    &unit.target,
223                    build_runner.files().message_cache_path(unit),
224                    build_runner.bcx.build_config.message_format,
225                    show_diagnostics,
226                );
227                // Need to link targets on both the dirty and fresh.
228                work.then(link_targets(build_runner, unit, true)?)
229            });
230
231            job
232        };
233        jobs.enqueue(build_runner, unit, job)?;
234    }
235
236    // Be sure to compile all dependencies of this target as well.
237    let deps = Vec::from(build_runner.unit_deps(unit)); // Create vec due to mutable borrow.
238    for dep in deps {
239        compile(build_runner, jobs, &dep.unit, exec, false)?;
240    }
241
242    Ok(())
243}
244
245/// Generates the warning message used when fallible doc-scrape units fail,
246/// either for rustdoc or rustc.
247fn make_failed_scrape_diagnostic(
248    build_runner: &BuildRunner<'_, '_>,
249    unit: &Unit,
250    top_line: impl Display,
251) -> String {
252    let manifest_path = unit.pkg.manifest_path();
253    let relative_manifest_path = manifest_path
254        .strip_prefix(build_runner.bcx.ws.root())
255        .unwrap_or(&manifest_path);
256
257    format!(
258        "\
259{top_line}
260    Try running with `--verbose` to see the error message.
261    If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in {}",
262        relative_manifest_path.display()
263    )
264}
265
266/// Creates a unit of work invoking `rustc` for building the `unit`.
267fn rustc(
268    build_runner: &mut BuildRunner<'_, '_>,
269    unit: &Unit,
270    exec: &Arc<dyn Executor>,
271) -> CargoResult<Work> {
272    let mut rustc = prepare_rustc(build_runner, unit)?;
273
274    let name = unit.pkg.name();
275
276    let outputs = build_runner.outputs(unit)?;
277    let root = build_runner.files().out_dir(unit);
278
279    // Prepare the native lib state (extra `-L` and `-l` flags).
280    let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
281    let current_id = unit.pkg.package_id();
282    let manifest = ManifestErrorContext::new(build_runner, unit);
283    let build_scripts = build_runner.build_scripts.get(unit).cloned();
284
285    // If we are a binary and the package also contains a library, then we
286    // don't pass the `-l` flags.
287    let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib());
288
289    let dep_info_name =
290        if let Some(c_extra_filename) = build_runner.files().metadata(unit).c_extra_filename() {
291            format!("{}-{}.d", unit.target.crate_name(), c_extra_filename)
292        } else {
293            format!("{}.d", unit.target.crate_name())
294        };
295    let rustc_dep_info_loc = root.join(dep_info_name);
296    let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
297
298    let mut output_options = OutputOptions::new(build_runner, unit);
299    let package_id = unit.pkg.package_id();
300    let target = Target::clone(&unit.target);
301    let mode = unit.mode;
302
303    exec.init(build_runner, unit);
304    let exec = exec.clone();
305
306    let root_output = build_runner.files().host_dest().map(|v| v.to_path_buf());
307    let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
308    let pkg_root = unit.pkg.root().to_path_buf();
309    let cwd = rustc
310        .get_cwd()
311        .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
312        .to_path_buf();
313    let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
314    let script_metadatas = build_runner.find_build_script_metadatas(unit);
315    let is_local = unit.is_local();
316    let artifact = unit.artifact;
317    let sbom_files = build_runner.sbom_output_files(unit)?;
318    let sbom = build_sbom(build_runner, unit)?;
319
320    let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
321        && !matches!(
322            build_runner.bcx.gctx.shell().verbosity(),
323            Verbosity::Verbose
324        );
325    let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
326        // If this unit is needed for doc-scraping, then we generate a diagnostic that
327        // describes the set of reverse-dependencies that cause the unit to be needed.
328        let target_desc = unit.target.description_named();
329        let mut for_scrape_units = build_runner
330            .bcx
331            .scrape_units_have_dep_on(unit)
332            .into_iter()
333            .map(|unit| unit.target.description_named())
334            .collect::<Vec<_>>();
335        for_scrape_units.sort();
336        let for_scrape_units = for_scrape_units.join(", ");
337        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}"))
338    });
339    if hide_diagnostics_for_scrape_unit {
340        output_options.show_diagnostics = false;
341    }
342    let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
343    return Ok(Work::new(move |state| {
344        // Artifacts are in a different location than typical units,
345        // hence we must assure the crate- and target-dependent
346        // directory is present.
347        if artifact.is_true() {
348            paths::create_dir_all(&root)?;
349        }
350
351        // Only at runtime have we discovered what the extra -L and -l
352        // arguments are for native libraries, so we process those here. We
353        // also need to be sure to add any -L paths for our plugins to the
354        // dynamic library load path as a plugin's dynamic library may be
355        // located somewhere in there.
356        // Finally, if custom environment variables have been produced by
357        // previous build scripts, we include them in the rustc invocation.
358        if let Some(build_scripts) = build_scripts {
359            let script_outputs = build_script_outputs.lock().unwrap();
360            add_native_deps(
361                &mut rustc,
362                &script_outputs,
363                &build_scripts,
364                pass_l_flag,
365                &target,
366                current_id,
367                mode,
368            )?;
369            if let Some(ref root_output) = root_output {
370                add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, root_output)?;
371            }
372            add_custom_flags(&mut rustc, &script_outputs, script_metadatas)?;
373        }
374
375        for output in outputs.iter() {
376            // If there is both an rmeta and rlib, rustc will prefer to use the
377            // rlib, even if it is older. Therefore, we must delete the rlib to
378            // force using the new rmeta.
379            if output.path.extension() == Some(OsStr::new("rmeta")) {
380                let dst = root.join(&output.path).with_extension("rlib");
381                if dst.exists() {
382                    paths::remove_file(&dst)?;
383                }
384            }
385
386            // Some linkers do not remove the executable, but truncate and modify it.
387            // That results in the old hard-link being modified even after renamed.
388            // We delete the old artifact here to prevent this behavior from confusing users.
389            // See rust-lang/cargo#8348.
390            if output.hardlink.is_some() && output.path.exists() {
391                _ = paths::remove_file(&output.path).map_err(|e| {
392                    tracing::debug!(
393                        "failed to delete previous output file `{:?}`: {e:?}",
394                        output.path
395                    );
396                });
397            }
398        }
399
400        state.running(&rustc);
401        let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
402        for file in sbom_files {
403            tracing::debug!("writing sbom to {}", file.display());
404            let outfile = BufWriter::new(paths::create(&file)?);
405            serde_json::to_writer(outfile, &sbom)?;
406        }
407
408        let result = exec
409            .exec(
410                &rustc,
411                package_id,
412                &target,
413                mode,
414                &mut |line| on_stdout_line(state, line, package_id, &target),
415                &mut |line| {
416                    on_stderr_line(
417                        state,
418                        line,
419                        package_id,
420                        &manifest,
421                        &target,
422                        &mut output_options,
423                    )
424                },
425            )
426            .map_err(|e| {
427                if output_options.errors_seen == 0 {
428                    // If we didn't expect an error, do not require --verbose to fail.
429                    // This is intended to debug
430                    // https://github.com/rust-lang/crater/issues/733, where we are seeing
431                    // Cargo exit unsuccessfully while seeming to not show any errors.
432                    e
433                } else {
434                    verbose_if_simple_exit_code(e)
435                }
436            })
437            .with_context(|| {
438                // adapted from rustc_errors/src/lib.rs
439                let warnings = match output_options.warnings_seen {
440                    0 => String::new(),
441                    1 => "; 1 warning emitted".to_string(),
442                    count => format!("; {} warnings emitted", count),
443                };
444                let errors = match output_options.errors_seen {
445                    0 => String::new(),
446                    1 => " due to 1 previous error".to_string(),
447                    count => format!(" due to {} previous errors", count),
448                };
449                let name = descriptive_pkg_name(&name, &target, &mode);
450                format!("could not compile {name}{errors}{warnings}")
451            });
452
453        if let Err(e) = result {
454            if let Some(diagnostic) = failed_scrape_diagnostic {
455                state.warning(diagnostic);
456            }
457
458            return Err(e);
459        }
460
461        // Exec should never return with success *and* generate an error.
462        debug_assert_eq!(output_options.errors_seen, 0);
463
464        if rustc_dep_info_loc.exists() {
465            fingerprint::translate_dep_info(
466                &rustc_dep_info_loc,
467                &dep_info_loc,
468                &cwd,
469                &pkg_root,
470                &build_dir,
471                &rustc,
472                // Do not track source files in the fingerprint for registry dependencies.
473                is_local,
474                &env_config,
475            )
476            .with_context(|| {
477                internal(format!(
478                    "could not parse/generate dep info at: {}",
479                    rustc_dep_info_loc.display()
480                ))
481            })?;
482            // This mtime shift allows Cargo to detect if a source file was
483            // modified in the middle of the build.
484            paths::set_file_time_no_err(dep_info_loc, timestamp);
485        }
486
487        // This mtime shift for .rmeta is a workaround as rustc incremental build
488        // since rust-lang/rust#114669 (1.90.0) skips unnecessary rmeta generation.
489        //
490        // The situation is like this:
491        //
492        // 1. When build script execution's external dependendies
493        //    (rerun-if-changed, rerun-if-env-changed) got updated,
494        //    the execution unit reran and got a newer mtime.
495        // 2. rustc type-checked the associated crate, though with incremental
496        //    compilation, no rmeta regeneration. Its `.rmeta` stays old.
497        // 3. Run `cargo check` again. Cargo found build script execution had
498        //    a new mtime than existing crate rmeta, so re-checking the crate.
499        //    However the check is a no-op (input has no change), so stuck.
500        if mode.is_check() {
501            for output in outputs.iter() {
502                paths::set_file_time_no_err(&output.path, timestamp);
503            }
504        }
505
506        Ok(())
507    }));
508
509    // Add all relevant `-L` and `-l` flags from dependencies (now calculated and
510    // present in `state`) to the command provided.
511    fn add_native_deps(
512        rustc: &mut ProcessBuilder,
513        build_script_outputs: &BuildScriptOutputs,
514        build_scripts: &BuildScripts,
515        pass_l_flag: bool,
516        target: &Target,
517        current_id: PackageId,
518        mode: CompileMode,
519    ) -> CargoResult<()> {
520        let mut library_paths = vec![];
521
522        for key in build_scripts.to_link.iter() {
523            let output = build_script_outputs.get(key.1).ok_or_else(|| {
524                internal(format!(
525                    "couldn't find build script output for {}/{}",
526                    key.0, key.1
527                ))
528            })?;
529            library_paths.extend(output.library_paths.iter());
530        }
531
532        // NOTE: This very intentionally does not use the derived ord from LibraryPath because we need to
533        // retain relative ordering within the same type (i.e. not lexicographic). The use of a stable sort
534        // is also important here because it ensures that paths of the same type retain the same relative
535        // ordering (for an unstable sort to work here, the list would need to retain the idx of each element
536        // and then sort by that idx when the type is equivalent.
537        library_paths.sort_by_key(|p| match p {
538            LibraryPath::CargoArtifact(_) => 0,
539            LibraryPath::External(_) => 1,
540        });
541
542        for path in library_paths.iter() {
543            rustc.arg("-L").arg(path.as_ref());
544        }
545
546        for key in build_scripts.to_link.iter() {
547            let output = build_script_outputs.get(key.1).ok_or_else(|| {
548                internal(format!(
549                    "couldn't find build script output for {}/{}",
550                    key.0, key.1
551                ))
552            })?;
553
554            if key.0 == current_id {
555                if pass_l_flag {
556                    for name in output.library_links.iter() {
557                        rustc.arg("-l").arg(name);
558                    }
559                }
560            }
561
562            for (lt, arg) in &output.linker_args {
563                // There was an unintentional change where cdylibs were
564                // allowed to be passed via transitive dependencies. This
565                // clause should have been kept in the `if` block above. For
566                // now, continue allowing it for cdylib only.
567                // See https://github.com/rust-lang/cargo/issues/9562
568                if lt.applies_to(target, mode)
569                    && (key.0 == current_id || *lt == LinkArgTarget::Cdylib)
570                {
571                    rustc.arg("-C").arg(format!("link-arg={}", arg));
572                }
573            }
574        }
575        Ok(())
576    }
577}
578
579fn verbose_if_simple_exit_code(err: Error) -> Error {
580    // If a signal on unix (`code == None`) or an abnormal termination
581    // on Windows (codes like `0xC0000409`), don't hide the error details.
582    match err
583        .downcast_ref::<ProcessError>()
584        .as_ref()
585        .and_then(|perr| perr.code)
586    {
587        Some(n) if cargo_util::is_simple_exit_code(n) => VerboseError::new(err).into(),
588        _ => err,
589    }
590}
591
592/// Link the compiled target (often of form `foo-{metadata_hash}`) to the
593/// final target. This must happen during both "Fresh" and "Compile".
594fn link_targets(
595    build_runner: &mut BuildRunner<'_, '_>,
596    unit: &Unit,
597    fresh: bool,
598) -> CargoResult<Work> {
599    let bcx = build_runner.bcx;
600    let outputs = build_runner.outputs(unit)?;
601    let export_dir = build_runner.files().export_dir();
602    let package_id = unit.pkg.package_id();
603    let manifest_path = PathBuf::from(unit.pkg.manifest_path());
604    let profile = unit.profile.clone();
605    let unit_mode = unit.mode;
606    let features = unit.features.iter().map(|s| s.to_string()).collect();
607    let json_messages = bcx.build_config.emit_json();
608    let executable = build_runner.get_executable(unit)?;
609    let mut target = Target::clone(&unit.target);
610    if let TargetSourcePath::Metabuild = target.src_path() {
611        // Give it something to serialize.
612        let path = unit
613            .pkg
614            .manifest()
615            .metabuild_path(build_runner.bcx.ws.build_dir());
616        target.set_src_path(TargetSourcePath::Path(path));
617    }
618
619    Ok(Work::new(move |state| {
620        // If we're a "root crate", e.g., the target of this compilation, then we
621        // hard link our outputs out of the `deps` directory into the directory
622        // above. This means that `cargo build` will produce binaries in
623        // `target/debug` which one probably expects.
624        let mut destinations = vec![];
625        for output in outputs.iter() {
626            let src = &output.path;
627            // This may have been a `cargo rustc` command which changes the
628            // output, so the source may not actually exist.
629            if !src.exists() {
630                continue;
631            }
632            let Some(dst) = output.hardlink.as_ref() else {
633                destinations.push(src.clone());
634                continue;
635            };
636            destinations.push(dst.clone());
637            paths::link_or_copy(src, dst)?;
638            if let Some(ref path) = output.export_path {
639                let export_dir = export_dir.as_ref().unwrap();
640                paths::create_dir_all(export_dir)?;
641
642                paths::link_or_copy(src, path)?;
643            }
644        }
645
646        if json_messages {
647            let debuginfo = match profile.debuginfo.into_inner() {
648                TomlDebugInfo::None => machine_message::ArtifactDebuginfo::Int(0),
649                TomlDebugInfo::Limited => machine_message::ArtifactDebuginfo::Int(1),
650                TomlDebugInfo::Full => machine_message::ArtifactDebuginfo::Int(2),
651                TomlDebugInfo::LineDirectivesOnly => {
652                    machine_message::ArtifactDebuginfo::Named("line-directives-only")
653                }
654                TomlDebugInfo::LineTablesOnly => {
655                    machine_message::ArtifactDebuginfo::Named("line-tables-only")
656                }
657            };
658            let art_profile = machine_message::ArtifactProfile {
659                opt_level: profile.opt_level.as_str(),
660                debuginfo: Some(debuginfo),
661                debug_assertions: profile.debug_assertions,
662                overflow_checks: profile.overflow_checks,
663                test: unit_mode.is_any_test(),
664            };
665
666            let msg = machine_message::Artifact {
667                package_id: package_id.to_spec(),
668                manifest_path,
669                target: &target,
670                profile: art_profile,
671                features,
672                filenames: destinations,
673                executable,
674                fresh,
675            }
676            .to_json_string();
677            state.stdout(msg)?;
678        }
679        Ok(())
680    }))
681}
682
683// For all plugin dependencies, add their -L paths (now calculated and present
684// in `build_script_outputs`) to the dynamic library load path for the command
685// to execute.
686fn add_plugin_deps(
687    rustc: &mut ProcessBuilder,
688    build_script_outputs: &BuildScriptOutputs,
689    build_scripts: &BuildScripts,
690    root_output: &Path,
691) -> CargoResult<()> {
692    let var = paths::dylib_path_envvar();
693    let search_path = rustc.get_env(var).unwrap_or_default();
694    let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>();
695    for (pkg_id, metadata) in &build_scripts.plugins {
696        let output = build_script_outputs
697            .get(*metadata)
698            .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?;
699        search_path.append(&mut filter_dynamic_search_path(
700            output.library_paths.iter().map(AsRef::as_ref),
701            root_output,
702        ));
703    }
704    let search_path = paths::join_paths(&search_path, var)?;
705    rustc.env(var, &search_path);
706    Ok(())
707}
708
709fn get_dynamic_search_path(path: &Path) -> &Path {
710    match path.to_str().and_then(|s| s.split_once("=")) {
711        Some(("native" | "crate" | "dependency" | "framework" | "all", path)) => Path::new(path),
712        _ => path,
713    }
714}
715
716// Determine paths to add to the dynamic search path from -L entries
717//
718// Strip off prefixes like "native=" or "framework=" and filter out directories
719// **not** inside our output directory since they are likely spurious and can cause
720// clashes with system shared libraries (issue #3366).
721fn filter_dynamic_search_path<'a, I>(paths: I, root_output: &Path) -> Vec<PathBuf>
722where
723    I: Iterator<Item = &'a PathBuf>,
724{
725    let mut search_path = vec![];
726    for dir in paths {
727        let dir = get_dynamic_search_path(dir);
728        if dir.starts_with(&root_output) {
729            search_path.push(dir.to_path_buf());
730        } else {
731            debug!(
732                "Not including path {} in runtime library search path because it is \
733                 outside target root {}",
734                dir.display(),
735                root_output.display()
736            );
737        }
738    }
739    search_path
740}
741
742/// Prepares flags and environments we can compute for a `rustc` invocation
743/// before the job queue starts compiling any unit.
744///
745/// This builds a static view of the invocation. Flags depending on the
746/// completion of other units will be added later in runtime, such as flags
747/// from build scripts.
748fn prepare_rustc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
749    let gctx = build_runner.bcx.gctx;
750    let is_primary = build_runner.is_primary_package(unit);
751    let is_workspace = build_runner.bcx.ws.is_member(&unit.pkg);
752
753    let mut base = build_runner
754        .compilation
755        .rustc_process(unit, is_primary, is_workspace)?;
756    build_base_args(build_runner, &mut base, unit)?;
757    if unit.pkg.manifest().is_embedded() {
758        if !gctx.cli_unstable().script {
759            anyhow::bail!(
760                "parsing `{}` requires `-Zscript`",
761                unit.pkg.manifest_path().display()
762            );
763        }
764        base.arg("-Z").arg("crate-attr=feature(frontmatter)");
765    }
766
767    base.inherit_jobserver(&build_runner.jobserver);
768    build_deps_args(&mut base, build_runner, unit)?;
769    add_cap_lints(build_runner.bcx, unit, &mut base);
770    if let Some(args) = build_runner.bcx.extra_args_for(unit) {
771        base.args(args);
772    }
773    base.args(&unit.rustflags);
774    if gctx.cli_unstable().binary_dep_depinfo {
775        base.arg("-Z").arg("binary-dep-depinfo");
776    }
777    if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
778        base.arg("-Z").arg("checksum-hash-algorithm=blake3");
779    }
780
781    if is_primary {
782        base.env("CARGO_PRIMARY_PACKAGE", "1");
783        let file_list = std::env::join_paths(build_runner.sbom_output_files(unit)?)?;
784        base.env("CARGO_SBOM_PATH", file_list);
785    }
786
787    if unit.target.is_test() || unit.target.is_bench() {
788        let tmp = build_runner
789            .files()
790            .layout(unit.kind)
791            .build_dir()
792            .prepare_tmp()?;
793        base.env("CARGO_TARGET_TMPDIR", tmp.display().to_string());
794    }
795
796    Ok(base)
797}
798
799/// Prepares flags and environments we can compute for a `rustdoc` invocation
800/// before the job queue starts compiling any unit.
801///
802/// This builds a static view of the invocation. Flags depending on the
803/// completion of other units will be added later in runtime, such as flags
804/// from build scripts.
805fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
806    let bcx = build_runner.bcx;
807    // script_metadata is not needed here, it is only for tests.
808    let mut rustdoc = build_runner.compilation.rustdoc_process(unit, None)?;
809    if unit.pkg.manifest().is_embedded() {
810        if !bcx.gctx.cli_unstable().script {
811            anyhow::bail!(
812                "parsing `{}` requires `-Zscript`",
813                unit.pkg.manifest_path().display()
814            );
815        }
816        rustdoc.arg("-Z").arg("crate-attr=feature(frontmatter)");
817    }
818    rustdoc.inherit_jobserver(&build_runner.jobserver);
819    let crate_name = unit.target.crate_name();
820    rustdoc.arg("--crate-name").arg(&crate_name);
821    add_path_args(bcx.ws, unit, &mut rustdoc);
822    add_cap_lints(bcx, unit, &mut rustdoc);
823
824    if let CompileKind::Target(target) = unit.kind {
825        rustdoc.arg("--target").arg(target.rustc_target());
826    }
827    let doc_dir = build_runner.files().out_dir(unit);
828    rustdoc.arg("-o").arg(&doc_dir);
829    rustdoc.args(&features_args(unit));
830    rustdoc.args(&check_cfg_args(unit));
831
832    add_error_format_and_color(build_runner, &mut rustdoc);
833    add_allow_features(build_runner, &mut rustdoc);
834
835    if build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo {
836        // toolchain-shared-resources is required for keeping the shared styling resources
837        // invocation-specific is required for keeping the original rustdoc emission
838        let mut arg = if build_runner.bcx.gctx.cli_unstable().rustdoc_mergeable_info {
839            // toolchain resources are written at the end, at the same time as merging
840            OsString::from("--emit=invocation-specific,dep-info=")
841        } else {
842            // if not using mergeable CCI, everything is written every time
843            OsString::from("--emit=toolchain-shared-resources,invocation-specific,dep-info=")
844        };
845        arg.push(rustdoc_dep_info_loc(build_runner, unit));
846        rustdoc.arg(arg);
847
848        if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
849            rustdoc.arg("-Z").arg("checksum-hash-algorithm=blake3");
850        }
851
852        rustdoc.arg("-Zunstable-options");
853    } else if build_runner.bcx.gctx.cli_unstable().rustdoc_mergeable_info {
854        // toolchain resources are written at the end, at the same time as merging
855        rustdoc.arg("--emit=invocation-specific");
856        rustdoc.arg("-Zunstable-options");
857    }
858
859    if build_runner.bcx.gctx.cli_unstable().rustdoc_mergeable_info {
860        // write out mergeable data to be imported
861        rustdoc.arg("--merge=none");
862        let mut arg = OsString::from("--parts-out-dir=");
863        // `-Zrustdoc-mergeable-info` always uses the new layout.
864        arg.push(build_runner.files().deps_dir_new_layout(unit));
865        rustdoc.arg(arg);
866    }
867
868    if let Some(trim_paths) = unit.profile.trim_paths.as_ref() {
869        trim_paths_args_rustdoc(&mut rustdoc, build_runner, unit, trim_paths)?;
870    }
871
872    rustdoc.args(unit.pkg.manifest().lint_rustflags());
873
874    let metadata = build_runner.metadata_for_doc_units[unit];
875    rustdoc
876        .arg("-C")
877        .arg(format!("metadata={}", metadata.c_metadata()));
878
879    if unit.mode.is_doc_scrape() {
880        debug_assert!(build_runner.bcx.scrape_units.contains(unit));
881
882        if unit.target.is_test() {
883            rustdoc.arg("--scrape-tests");
884        }
885
886        rustdoc.arg("-Zunstable-options");
887
888        rustdoc
889            .arg("--scrape-examples-output-path")
890            .arg(scrape_output_path(build_runner, unit)?);
891
892        // Only scrape example for items from crates in the workspace, to reduce generated file size
893        for pkg in build_runner.bcx.packages.packages() {
894            let names = pkg
895                .targets()
896                .iter()
897                .map(|target| target.crate_name())
898                .collect::<HashSet<_>>();
899            for name in names {
900                rustdoc.arg("--scrape-examples-target-crate").arg(name);
901            }
902        }
903    }
904
905    if should_include_scrape_units(build_runner.bcx, unit) {
906        rustdoc.arg("-Zunstable-options");
907    }
908
909    build_deps_args(&mut rustdoc, build_runner, unit)?;
910    rustdoc::add_root_urls(build_runner, unit, &mut rustdoc)?;
911
912    rustdoc::add_output_format(build_runner, &mut rustdoc)?;
913
914    if let Some(args) = build_runner.bcx.extra_args_for(unit) {
915        rustdoc.args(args);
916    }
917    rustdoc.args(&unit.rustdocflags);
918
919    if !crate_version_flag_already_present(&rustdoc) {
920        append_crate_version_flag(unit, &mut rustdoc);
921    }
922
923    Ok(rustdoc)
924}
925
926/// Creates a unit of work invoking `rustdoc` for documenting the `unit`.
927fn rustdoc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Work> {
928    let mut rustdoc = prepare_rustdoc(build_runner, unit)?;
929
930    let crate_name = unit.target.crate_name();
931    let doc_dir = build_runner.files().out_dir(unit);
932    // Create the documentation directory ahead of time as rustdoc currently has
933    // a bug where concurrent invocations will race to create this directory if
934    // it doesn't already exist.
935    paths::create_dir_all(&doc_dir)?;
936
937    let target_desc = unit.target.description_named();
938    let name = unit.pkg.name();
939    let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
940    let package_id = unit.pkg.package_id();
941    let target = Target::clone(&unit.target);
942    let manifest = ManifestErrorContext::new(build_runner, unit);
943
944    let rustdoc_dep_info_loc = rustdoc_dep_info_loc(build_runner, unit);
945    let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
946    let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
947    let pkg_root = unit.pkg.root().to_path_buf();
948    let cwd = rustdoc
949        .get_cwd()
950        .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
951        .to_path_buf();
952    let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
953    let is_local = unit.is_local();
954    let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
955    let rustdoc_depinfo_enabled = build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo;
956
957    let mut output_options = OutputOptions::new(build_runner, unit);
958    let script_metadatas = build_runner.find_build_script_metadatas(unit);
959    let scrape_outputs = if should_include_scrape_units(build_runner.bcx, unit) {
960        Some(
961            build_runner
962                .bcx
963                .scrape_units
964                .iter()
965                .map(|unit| {
966                    Ok((
967                        build_runner.files().metadata(unit).unit_id(),
968                        scrape_output_path(build_runner, unit)?,
969                    ))
970                })
971                .collect::<CargoResult<HashMap<_, _>>>()?,
972        )
973    } else {
974        None
975    };
976
977    let failed_scrape_units = Arc::clone(&build_runner.failed_scrape_units);
978    let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
979        && !matches!(
980            build_runner.bcx.gctx.shell().verbosity(),
981            Verbosity::Verbose
982        );
983    let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
984        make_failed_scrape_diagnostic(
985            build_runner,
986            unit,
987            format_args!("failed to scan {target_desc} in package `{name}` for example code usage"),
988        )
989    });
990    if hide_diagnostics_for_scrape_unit {
991        output_options.show_diagnostics = false;
992    }
993
994    Ok(Work::new(move |state| {
995        add_custom_flags(
996            &mut rustdoc,
997            &build_script_outputs.lock().unwrap(),
998            script_metadatas,
999        )?;
1000
1001        // Add the output of scraped examples to the rustdoc command.
1002        // This action must happen after the unit's dependencies have finished,
1003        // because some of those deps may be Docscrape units which have failed.
1004        // So we dynamically determine which `--with-examples` flags to pass here.
1005        if let Some(scrape_outputs) = scrape_outputs {
1006            let failed_scrape_units = failed_scrape_units.lock().unwrap();
1007            for (metadata, output_path) in &scrape_outputs {
1008                if !failed_scrape_units.contains(metadata) {
1009                    rustdoc.arg("--with-examples").arg(output_path);
1010                }
1011            }
1012        }
1013
1014        let crate_dir = doc_dir.join(&crate_name);
1015        if crate_dir.exists() {
1016            // Remove output from a previous build. This ensures that stale
1017            // files for removed items are removed.
1018            debug!("removing pre-existing doc directory {:?}", crate_dir);
1019            paths::remove_dir_all(crate_dir)?;
1020        }
1021        state.running(&rustdoc);
1022        let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
1023
1024        let result = rustdoc
1025            .exec_with_streaming(
1026                &mut |line| on_stdout_line(state, line, package_id, &target),
1027                &mut |line| {
1028                    on_stderr_line(
1029                        state,
1030                        line,
1031                        package_id,
1032                        &manifest,
1033                        &target,
1034                        &mut output_options,
1035                    )
1036                },
1037                false,
1038            )
1039            .map_err(verbose_if_simple_exit_code)
1040            .with_context(|| format!("could not document `{}`", name));
1041
1042        if let Err(e) = result {
1043            if let Some(diagnostic) = failed_scrape_diagnostic {
1044                state.warning(diagnostic);
1045            }
1046
1047            return Err(e);
1048        }
1049
1050        if rustdoc_depinfo_enabled && rustdoc_dep_info_loc.exists() {
1051            fingerprint::translate_dep_info(
1052                &rustdoc_dep_info_loc,
1053                &dep_info_loc,
1054                &cwd,
1055                &pkg_root,
1056                &build_dir,
1057                &rustdoc,
1058                // Should we track source file for doc gen?
1059                is_local,
1060                &env_config,
1061            )
1062            .with_context(|| {
1063                internal(format_args!(
1064                    "could not parse/generate dep info at: {}",
1065                    rustdoc_dep_info_loc.display()
1066                ))
1067            })?;
1068            // This mtime shift allows Cargo to detect if a source file was
1069            // modified in the middle of the build.
1070            paths::set_file_time_no_err(dep_info_loc, timestamp);
1071        }
1072
1073        Ok(())
1074    }))
1075}
1076
1077// The --crate-version flag could have already been passed in RUSTDOCFLAGS
1078// or as an extra compiler argument for rustdoc
1079fn crate_version_flag_already_present(rustdoc: &ProcessBuilder) -> bool {
1080    rustdoc.get_args().any(|flag| {
1081        flag.to_str()
1082            .map_or(false, |flag| flag.starts_with(RUSTDOC_CRATE_VERSION_FLAG))
1083    })
1084}
1085
1086fn append_crate_version_flag(unit: &Unit, rustdoc: &mut ProcessBuilder) {
1087    rustdoc
1088        .arg(RUSTDOC_CRATE_VERSION_FLAG)
1089        .arg(unit.pkg.version().to_string());
1090}
1091
1092/// Adds [`--cap-lints`] to the command to execute.
1093///
1094/// [`--cap-lints`]: https://doc.rust-lang.org/nightly/rustc/lints/levels.html#capping-lints
1095fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuilder) {
1096    // If this is an upstream dep we don't want warnings from, turn off all
1097    // lints.
1098    if !unit.show_warnings(bcx.gctx) {
1099        cmd.arg("--cap-lints").arg("allow");
1100
1101    // If this is an upstream dep but we *do* want warnings, make sure that they
1102    // don't fail compilation.
1103    } else if !unit.is_local() {
1104        cmd.arg("--cap-lints").arg("warn");
1105    }
1106}
1107
1108/// Forwards [`-Zallow-features`] if it is set for cargo.
1109///
1110/// [`-Zallow-features`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#allow-features
1111fn add_allow_features(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1112    if let Some(allow) = &build_runner.bcx.gctx.cli_unstable().allow_features {
1113        use std::fmt::Write;
1114        let mut arg = String::from("-Zallow-features=");
1115        for f in allow {
1116            let _ = write!(&mut arg, "{f},");
1117        }
1118        cmd.arg(arg.trim_end_matches(','));
1119    }
1120}
1121
1122/// Adds [`--error-format`] to the command to execute.
1123///
1124/// Cargo always uses JSON output. This has several benefits, such as being
1125/// easier to parse, handles changing formats (for replaying cached messages),
1126/// ensures atomic output (so messages aren't interleaved), allows for
1127/// intercepting messages like rmeta artifacts, etc. rustc includes a
1128/// "rendered" field in the JSON message with the message properly formatted,
1129/// which Cargo will extract and display to the user.
1130///
1131/// [`--error-format`]: https://doc.rust-lang.org/nightly/rustc/command-line-arguments.html#--error-format-control-how-errors-are-produced
1132fn add_error_format_and_color(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1133    let enable_timings = build_runner.bcx.gctx.cli_unstable().section_timings
1134        && (!build_runner.bcx.build_config.timing_outputs.is_empty()
1135            || build_runner.bcx.logger.is_some());
1136    if enable_timings {
1137        cmd.arg("-Zunstable-options");
1138    }
1139
1140    cmd.arg("--error-format=json");
1141    let mut json = String::from("--json=diagnostic-rendered-ansi,artifacts,future-incompat");
1142
1143    if let MessageFormat::Short | MessageFormat::Json { short: true, .. } =
1144        build_runner.bcx.build_config.message_format
1145    {
1146        json.push_str(",diagnostic-short");
1147    } else if build_runner.bcx.gctx.shell().err_unicode()
1148        && build_runner.bcx.gctx.cli_unstable().rustc_unicode
1149    {
1150        json.push_str(",diagnostic-unicode");
1151    }
1152
1153    if enable_timings {
1154        json.push_str(",timings");
1155    }
1156
1157    cmd.arg(json);
1158
1159    let gctx = build_runner.bcx.gctx;
1160    if let Some(width) = gctx.shell().err_width().diagnostic_terminal_width() {
1161        cmd.arg(format!("--diagnostic-width={width}"));
1162    }
1163}
1164
1165/// Adds essential rustc flags and environment variables to the command to execute.
1166fn build_base_args(
1167    build_runner: &BuildRunner<'_, '_>,
1168    cmd: &mut ProcessBuilder,
1169    unit: &Unit,
1170) -> CargoResult<()> {
1171    assert!(!unit.mode.is_run_custom_build());
1172
1173    let bcx = build_runner.bcx;
1174    let Profile {
1175        ref opt_level,
1176        codegen_backend,
1177        codegen_units,
1178        debuginfo,
1179        debug_assertions,
1180        split_debuginfo,
1181        overflow_checks,
1182        rpath,
1183        ref panic,
1184        incremental,
1185        strip,
1186        rustflags: profile_rustflags,
1187        trim_paths,
1188        hint_mostly_unused: profile_hint_mostly_unused,
1189        ..
1190    } = unit.profile.clone();
1191    let hints = unit.pkg.hints().cloned().unwrap_or_default();
1192    let test = unit.mode.is_any_test();
1193
1194    let warn = |msg: &str| {
1195        bcx.gctx.shell().warn(format!(
1196            "{}@{}: {msg}",
1197            unit.pkg.package_id().name(),
1198            unit.pkg.package_id().version()
1199        ))
1200    };
1201    let unit_capped_warn = |msg: &str| {
1202        if unit.show_warnings(bcx.gctx) {
1203            warn(msg)
1204        } else {
1205            Ok(())
1206        }
1207    };
1208
1209    cmd.arg("--crate-name").arg(&unit.target.crate_name());
1210
1211    let edition = unit.target.edition();
1212    edition.cmd_edition_arg(cmd);
1213
1214    add_path_args(bcx.ws, unit, cmd);
1215    add_error_format_and_color(build_runner, cmd);
1216    add_allow_features(build_runner, cmd);
1217
1218    let mut contains_dy_lib = false;
1219    if !test {
1220        for crate_type in &unit.target.rustc_crate_types() {
1221            cmd.arg("--crate-type").arg(crate_type.as_str());
1222            contains_dy_lib |= crate_type == &CrateType::Dylib;
1223        }
1224    }
1225
1226    if unit.mode.is_check() {
1227        cmd.arg("--emit=dep-info,metadata");
1228    } else if build_runner.bcx.gctx.cli_unstable().no_embed_metadata {
1229        // Nightly rustc supports the -Zembed-metadata=no flag, which tells it to avoid including
1230        // full metadata in rlib/dylib artifacts, to save space on disk. In this case, metadata
1231        // will only be stored in .rmeta files.
1232        // When we use this flag, we should also pass --emit=metadata to all artifacts that
1233        // contain useful metadata (rlib/dylib/proc macros), so that a .rmeta file is actually
1234        // generated. If we didn't do this, the full metadata would not get written anywhere.
1235        // However, we do not want to pass --emit=metadata to artifacts that never produce useful
1236        // metadata, such as binaries, because that would just unnecessarily create empty .rmeta
1237        // files on disk.
1238        if unit.benefits_from_no_embed_metadata() {
1239            cmd.arg("--emit=dep-info,metadata,link");
1240            cmd.args(&["-Z", "embed-metadata=no"]);
1241        } else {
1242            cmd.arg("--emit=dep-info,link");
1243        }
1244    } else {
1245        // If we don't use -Zembed-metadata=no, we emit .rmeta files only for rlib outputs.
1246        // This metadata may be used in this session for a pipelined compilation, or it may
1247        // be used in a future Cargo session as part of a pipelined compile.
1248        if !unit.requires_upstream_objects() {
1249            cmd.arg("--emit=dep-info,metadata,link");
1250        } else {
1251            cmd.arg("--emit=dep-info,link");
1252        }
1253    }
1254
1255    let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
1256        || (contains_dy_lib && !build_runner.is_primary_package(unit));
1257    if prefer_dynamic {
1258        cmd.arg("-C").arg("prefer-dynamic");
1259    }
1260
1261    if opt_level.as_str() != "0" {
1262        cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
1263    }
1264
1265    if *panic != PanicStrategy::Unwind {
1266        cmd.arg("-C").arg(format!("panic={}", panic));
1267    }
1268    if *panic == PanicStrategy::ImmediateAbort {
1269        cmd.arg("-Z").arg("unstable-options");
1270    }
1271
1272    cmd.args(&lto_args(build_runner, unit));
1273
1274    if let Some(backend) = codegen_backend {
1275        cmd.arg("-Z").arg(&format!("codegen-backend={}", backend));
1276    }
1277
1278    if let Some(n) = codegen_units {
1279        cmd.arg("-C").arg(&format!("codegen-units={}", n));
1280    }
1281
1282    let debuginfo = debuginfo.into_inner();
1283    // Shorten the number of arguments if possible.
1284    if debuginfo != TomlDebugInfo::None {
1285        cmd.arg("-C").arg(format!("debuginfo={debuginfo}"));
1286        // This is generally just an optimization on build time so if we don't
1287        // pass it then it's ok. The values for the flag (off, packed, unpacked)
1288        // may be supported or not depending on the platform, so availability is
1289        // checked per-value. For example, at the time of writing this code, on
1290        // Windows the only stable valid value for split-debuginfo is "packed",
1291        // while on Linux "unpacked" is also stable.
1292        if let Some(split) = split_debuginfo {
1293            if build_runner
1294                .bcx
1295                .target_data
1296                .info(unit.kind)
1297                .supports_debuginfo_split(split)
1298            {
1299                cmd.arg("-C").arg(format!("split-debuginfo={split}"));
1300            }
1301        }
1302    }
1303
1304    if let Some(trim_paths) = trim_paths {
1305        trim_paths_args(cmd, build_runner, unit, &trim_paths)?;
1306    }
1307
1308    cmd.args(unit.pkg.manifest().lint_rustflags());
1309    cmd.args(&profile_rustflags);
1310
1311    // `-C overflow-checks` is implied by the setting of `-C debug-assertions`,
1312    // so we only need to provide `-C overflow-checks` if it differs from
1313    // the value of `-C debug-assertions` we would provide.
1314    if opt_level.as_str() != "0" {
1315        if debug_assertions {
1316            cmd.args(&["-C", "debug-assertions=on"]);
1317            if !overflow_checks {
1318                cmd.args(&["-C", "overflow-checks=off"]);
1319            }
1320        } else if overflow_checks {
1321            cmd.args(&["-C", "overflow-checks=on"]);
1322        }
1323    } else if !debug_assertions {
1324        cmd.args(&["-C", "debug-assertions=off"]);
1325        if overflow_checks {
1326            cmd.args(&["-C", "overflow-checks=on"]);
1327        }
1328    } else if !overflow_checks {
1329        cmd.args(&["-C", "overflow-checks=off"]);
1330    }
1331
1332    if test && unit.target.harness() {
1333        cmd.arg("--test");
1334
1335        // Cargo has historically never compiled `--test` binaries with
1336        // `panic=abort` because the `test` crate itself didn't support it.
1337        // Support is now upstream, however, but requires an unstable flag to be
1338        // passed when compiling the test. We require, in Cargo, an unstable
1339        // flag to pass to rustc, so register that here. Eventually this flag
1340        // will simply not be needed when the behavior is stabilized in the Rust
1341        // compiler itself.
1342        if *panic == PanicStrategy::Abort || *panic == PanicStrategy::ImmediateAbort {
1343            cmd.arg("-Z").arg("panic-abort-tests");
1344        }
1345    } else if test {
1346        cmd.arg("--cfg").arg("test");
1347    }
1348
1349    cmd.args(&features_args(unit));
1350    cmd.args(&check_cfg_args(unit));
1351
1352    let meta = build_runner.files().metadata(unit);
1353    cmd.arg("-C")
1354        .arg(&format!("metadata={}", meta.c_metadata()));
1355    if let Some(c_extra_filename) = meta.c_extra_filename() {
1356        cmd.arg("-C")
1357            .arg(&format!("extra-filename=-{c_extra_filename}"));
1358    }
1359
1360    if rpath {
1361        cmd.arg("-C").arg("rpath");
1362    }
1363
1364    cmd.arg("--out-dir")
1365        .arg(&build_runner.files().out_dir(unit));
1366
1367    fn opt(cmd: &mut ProcessBuilder, key: &str, prefix: &str, val: Option<&OsStr>) {
1368        if let Some(val) = val {
1369            let mut joined = OsString::from(prefix);
1370            joined.push(val);
1371            cmd.arg(key).arg(joined);
1372        }
1373    }
1374
1375    if let CompileKind::Target(n) = unit.kind {
1376        cmd.arg("--target").arg(n.rustc_target());
1377    }
1378
1379    opt(
1380        cmd,
1381        "-C",
1382        "linker=",
1383        build_runner
1384            .compilation
1385            .target_linker(unit.kind)
1386            .as_ref()
1387            .map(|s| s.as_ref()),
1388    );
1389    if incremental {
1390        let dir = build_runner.files().incremental_dir(&unit);
1391        opt(cmd, "-C", "incremental=", Some(dir.as_os_str()));
1392    }
1393
1394    let pkg_hint_mostly_unused = match hints.mostly_unused {
1395        None => None,
1396        Some(toml::Value::Boolean(b)) => Some(b),
1397        Some(v) => {
1398            unit_capped_warn(&format!(
1399                "ignoring unsupported value type ({}) for 'hints.mostly-unused', which expects a boolean",
1400                v.type_str()
1401            ))?;
1402            None
1403        }
1404    };
1405    if profile_hint_mostly_unused
1406        .or(pkg_hint_mostly_unused)
1407        .unwrap_or(false)
1408    {
1409        if bcx.gctx.cli_unstable().profile_hint_mostly_unused {
1410            cmd.arg("-Zhint-mostly-unused");
1411        } else {
1412            if profile_hint_mostly_unused.is_some() {
1413                // Profiles come from the top-level unit, so we don't use `unit_capped_warn` here.
1414                warn(
1415                    "ignoring 'hint-mostly-unused' profile option, pass `-Zprofile-hint-mostly-unused` to enable it",
1416                )?;
1417            } else if pkg_hint_mostly_unused.is_some() {
1418                unit_capped_warn(
1419                    "ignoring 'hints.mostly-unused', pass `-Zprofile-hint-mostly-unused` to enable it",
1420                )?;
1421            }
1422        }
1423    }
1424
1425    let strip = strip.into_inner();
1426    if strip != StripInner::None {
1427        cmd.arg("-C").arg(format!("strip={}", strip));
1428    }
1429
1430    if unit.is_std {
1431        // -Zforce-unstable-if-unmarked prevents the accidental use of
1432        // unstable crates within the sysroot (such as "extern crate libc" or
1433        // any non-public crate in the sysroot).
1434        //
1435        // RUSTC_BOOTSTRAP allows unstable features on stable.
1436        cmd.arg("-Z")
1437            .arg("force-unstable-if-unmarked")
1438            .env("RUSTC_BOOTSTRAP", "1");
1439    }
1440
1441    // Add `CARGO_BIN_EXE_` environment variables for building tests.
1442    if unit.target.is_test() || unit.target.is_bench() {
1443        for bin_target in unit
1444            .pkg
1445            .manifest()
1446            .targets()
1447            .iter()
1448            .filter(|target| target.is_bin())
1449        {
1450            // For `cargo check` builds we do not uplift the CARGO_BIN_EXE_ artifacts to the
1451            // artifact-dir. We do not want to provide a path to a non-existent binary but we still
1452            // need to provide *something* so `env!("CARGO_BIN_EXE_...")` macros will compile.
1453            let exe_path = build_runner
1454                .files()
1455                .bin_link_for_target(bin_target, unit.kind, build_runner.bcx)?
1456                .map(|path| path.as_os_str().to_os_string())
1457                .unwrap_or_else(|| OsString::from(format!("placeholder:{}", bin_target.name())));
1458
1459            let name = bin_target
1460                .binary_filename()
1461                .unwrap_or(bin_target.name().to_string());
1462            let key = format!("CARGO_BIN_EXE_{}", name);
1463            cmd.env(&key, exe_path);
1464        }
1465    }
1466    Ok(())
1467}
1468
1469/// All active features for the unit passed as `--cfg features=<feature-name>`.
1470fn features_args(unit: &Unit) -> Vec<OsString> {
1471    let mut args = Vec::with_capacity(unit.features.len() * 2);
1472
1473    for feat in &unit.features {
1474        args.push(OsString::from("--cfg"));
1475        args.push(OsString::from(format!("feature=\"{}\"", feat)));
1476    }
1477
1478    args
1479}
1480
1481/// Like [`trim_paths_args`] but for rustdoc invocations.
1482fn trim_paths_args_rustdoc(
1483    cmd: &mut ProcessBuilder,
1484    build_runner: &BuildRunner<'_, '_>,
1485    unit: &Unit,
1486    trim_paths: &TomlTrimPaths,
1487) -> CargoResult<()> {
1488    match trim_paths {
1489        // rustdoc supports diagnostics trimming only.
1490        TomlTrimPaths::Values(values) if !values.contains(&TomlTrimPathsValue::Diagnostics) => {
1491            return Ok(());
1492        }
1493        _ => {}
1494    }
1495
1496    // feature gate was checked during manifest/config parsing.
1497    cmd.arg("-Zunstable-options");
1498
1499    // Order of `--remap-path-prefix` flags is important for `-Zbuild-std`.
1500    // We want to show `/rustc/<hash>/library/std` instead of `std-0.0.0`.
1501    cmd.arg(package_remap(build_runner, unit));
1502    cmd.arg(build_dir_remap(build_runner));
1503    cmd.arg(sysroot_remap(build_runner, unit));
1504
1505    Ok(())
1506}
1507
1508/// Generates the `--remap-path-scope` and `--remap-path-prefix` for [RFC 3127].
1509/// See also unstable feature [`-Ztrim-paths`].
1510///
1511/// [RFC 3127]: https://rust-lang.github.io/rfcs/3127-trim-paths.html
1512/// [`-Ztrim-paths`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-trim-paths-option
1513fn trim_paths_args(
1514    cmd: &mut ProcessBuilder,
1515    build_runner: &BuildRunner<'_, '_>,
1516    unit: &Unit,
1517    trim_paths: &TomlTrimPaths,
1518) -> CargoResult<()> {
1519    if trim_paths.is_none() {
1520        return Ok(());
1521    }
1522
1523    // feature gate was checked during manifest/config parsing.
1524    cmd.arg("-Zunstable-options");
1525    cmd.arg(format!("-Zremap-path-scope={trim_paths}"));
1526
1527    // Order of `--remap-path-prefix` flags is important for `-Zbuild-std`.
1528    // We want to show `/rustc/<hash>/library/std` instead of `std-0.0.0`.
1529    cmd.arg(package_remap(build_runner, unit));
1530    cmd.arg(build_dir_remap(build_runner));
1531    cmd.arg(sysroot_remap(build_runner, unit));
1532
1533    Ok(())
1534}
1535
1536/// Path prefix remap rules for sysroot.
1537///
1538/// This remap logic aligns with rustc:
1539/// <https://github.com/rust-lang/rust/blob/c2ef3516/src/bootstrap/src/lib.rs#L1113-L1116>
1540fn sysroot_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1541    let mut remap = OsString::from("--remap-path-prefix=");
1542    remap.push({
1543        // See also `detect_sysroot_src_path()`.
1544        let mut sysroot = build_runner.bcx.target_data.info(unit.kind).sysroot.clone();
1545        sysroot.push("lib");
1546        sysroot.push("rustlib");
1547        sysroot.push("src");
1548        sysroot.push("rust");
1549        sysroot
1550    });
1551    remap.push("=");
1552    remap.push("/rustc/");
1553    if let Some(commit_hash) = build_runner.bcx.rustc().commit_hash.as_ref() {
1554        remap.push(commit_hash);
1555    } else {
1556        remap.push(build_runner.bcx.rustc().version.to_string());
1557    }
1558    remap
1559}
1560
1561/// Path prefix remap rules for dependencies.
1562///
1563/// * Git dependencies: remove `~/.cargo/git/checkouts` prefix.
1564/// * Registry dependencies: remove `~/.cargo/registry/src` prefix.
1565/// * Others (e.g. path dependencies):
1566///     * relative paths to workspace root if inside the workspace directory.
1567///     * otherwise remapped to `<pkg>-<version>`.
1568fn package_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1569    let pkg_root = unit.pkg.root();
1570    let ws_root = build_runner.bcx.ws.root();
1571    let mut remap = OsString::from("--remap-path-prefix=");
1572    let source_id = unit.pkg.package_id().source_id();
1573    if source_id.is_git() {
1574        remap.push(
1575            build_runner
1576                .bcx
1577                .gctx
1578                .git_checkouts_path()
1579                .as_path_unlocked(),
1580        );
1581        remap.push("=");
1582    } else if source_id.is_registry() {
1583        remap.push(
1584            build_runner
1585                .bcx
1586                .gctx
1587                .registry_source_path()
1588                .as_path_unlocked(),
1589        );
1590        remap.push("=");
1591    } else if pkg_root.strip_prefix(ws_root).is_ok() {
1592        remap.push(ws_root);
1593        remap.push("=."); // remap to relative rustc work dir explicitly
1594    } else {
1595        remap.push(pkg_root);
1596        remap.push("=");
1597        remap.push(unit.pkg.name());
1598        remap.push("-");
1599        remap.push(unit.pkg.version().to_string());
1600    }
1601    remap
1602}
1603
1604/// Remap all paths pointing to `build.build-dir`,
1605/// i.e., `[BUILD_DIR]/debug/deps/foo-[HASH].dwo` would be remapped to
1606/// `/cargo/build-dir/debug/deps/foo-[HASH].dwo`
1607/// (note the `/cargo/build-dir` prefix).
1608///
1609/// This covers scenarios like:
1610///
1611/// * Build script generated code. For example, a build script may call `file!`
1612///   macros, and the associated crate uses [`include!`] to include the expanded
1613///   [`file!`] macro in-place via the `OUT_DIR` environment.
1614/// * On Linux, `DW_AT_GNU_dwo_name` that contains paths to split debuginfo
1615///   files (dwp and dwo).
1616fn build_dir_remap(build_runner: &BuildRunner<'_, '_>) -> OsString {
1617    let build_dir = build_runner.bcx.ws.build_dir();
1618    let mut remap = OsString::from("--remap-path-prefix=");
1619    remap.push(build_dir.as_path_unlocked());
1620    remap.push("=/cargo/build-dir");
1621    remap
1622}
1623
1624/// Generates the `--check-cfg` arguments for the `unit`.
1625fn check_cfg_args(unit: &Unit) -> Vec<OsString> {
1626    // The routine below generates the --check-cfg arguments. Our goals here are to
1627    // enable the checking of conditionals and pass the list of declared features.
1628    //
1629    // In the simplified case, it would resemble something like this:
1630    //
1631    //   --check-cfg=cfg() --check-cfg=cfg(feature, values(...))
1632    //
1633    // but having `cfg()` is redundant with the second argument (as well-known names
1634    // and values are implicitly enabled when one or more `--check-cfg` argument is
1635    // passed) so we don't emit it and just pass:
1636    //
1637    //   --check-cfg=cfg(feature, values(...))
1638    //
1639    // This way, even if there are no declared features, the config `feature` will
1640    // still be expected, meaning users would get "unexpected value" instead of name.
1641    // This wasn't always the case, see rust-lang#119930 for some details.
1642
1643    let gross_cap_estimation = unit.pkg.summary().features().len() * 7 + 25;
1644    let mut arg_feature = OsString::with_capacity(gross_cap_estimation);
1645
1646    arg_feature.push("cfg(feature, values(");
1647    for (i, feature) in unit.pkg.summary().features().keys().enumerate() {
1648        if i != 0 {
1649            arg_feature.push(", ");
1650        }
1651        arg_feature.push("\"");
1652        arg_feature.push(feature);
1653        arg_feature.push("\"");
1654    }
1655    arg_feature.push("))");
1656
1657    // In addition to the package features, we also include the `test` cfg (since
1658    // compiler-team#785, as to be able to someday apply it conditionally), as well
1659    // the `docsrs` cfg from the docs.rs service.
1660    //
1661    // We include `docsrs` here (in Cargo) instead of rustc, since there is a much closer
1662    // relationship between Cargo and docs.rs than rustc and docs.rs. In particular, all
1663    // users of docs.rs use Cargo, but not all users of rustc (like Rust-for-Linux) use docs.rs.
1664
1665    vec![
1666        OsString::from("--check-cfg"),
1667        OsString::from("cfg(docsrs,test)"),
1668        OsString::from("--check-cfg"),
1669        arg_feature,
1670    ]
1671}
1672
1673/// Adds LTO related codegen flags.
1674fn lto_args(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> Vec<OsString> {
1675    let mut result = Vec::new();
1676    let mut push = |arg: &str| {
1677        result.push(OsString::from("-C"));
1678        result.push(OsString::from(arg));
1679    };
1680    match build_runner.lto[unit] {
1681        lto::Lto::Run(None) => push("lto"),
1682        lto::Lto::Run(Some(s)) => push(&format!("lto={}", s)),
1683        lto::Lto::Off => {
1684            push("lto=off");
1685            push("embed-bitcode=no");
1686        }
1687        lto::Lto::ObjectAndBitcode => {} // this is rustc's default
1688        lto::Lto::OnlyBitcode => push("linker-plugin-lto"),
1689        lto::Lto::OnlyObject => push("embed-bitcode=no"),
1690    }
1691    result
1692}
1693
1694/// Adds dependency-relevant rustc flags and environment variables
1695/// to the command to execute, such as [`-L`] and [`--extern`].
1696///
1697/// [`-L`]: https://doc.rust-lang.org/nightly/rustc/command-line-arguments.html#-l-add-a-directory-to-the-library-search-path
1698/// [`--extern`]: https://doc.rust-lang.org/nightly/rustc/command-line-arguments.html#--extern-specify-where-an-external-library-is-located
1699fn build_deps_args(
1700    cmd: &mut ProcessBuilder,
1701    build_runner: &BuildRunner<'_, '_>,
1702    unit: &Unit,
1703) -> CargoResult<()> {
1704    let bcx = build_runner.bcx;
1705
1706    for arg in lib_search_paths(build_runner, unit)? {
1707        cmd.arg(arg);
1708    }
1709
1710    let deps = build_runner.unit_deps(unit);
1711
1712    // If there is not one linkable target but should, rustc fails later
1713    // on if there is an `extern crate` for it. This may turn into a hard
1714    // error in the future (see PR #4797).
1715    if !deps
1716        .iter()
1717        .any(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_linkable())
1718    {
1719        if let Some(dep) = deps.iter().find(|dep| {
1720            !dep.unit.mode.is_doc() && dep.unit.target.is_lib() && !dep.unit.artifact.is_true()
1721        }) {
1722            let dep_name = dep.unit.target.crate_name();
1723            let name = unit.target.crate_name();
1724            bcx.gctx.shell().print_report(&[
1725                Level::WARNING.secondary_title(format!("the package `{dep_name}` provides no linkable target"))
1726                    .elements([
1727                        Level::NOTE.message(format!("this might cause `{name}` to fail compilation")),
1728                        Level::NOTE.message("this warning might turn into a hard error in the future"),
1729                        Level::HELP.message(format!("consider adding 'dylib' or 'rlib' to key 'crate-type' in `{dep_name}`'s Cargo.toml"))
1730                    ])
1731            ], false)?;
1732        }
1733    }
1734
1735    let mut unstable_opts = false;
1736
1737    // Add `OUT_DIR` environment variables for build scripts
1738    let first_custom_build_dep = deps.iter().find(|dep| dep.unit.mode.is_run_custom_build());
1739    if let Some(dep) = first_custom_build_dep {
1740        let out_dir = &build_runner.files().build_script_out_dir(&dep.unit);
1741        cmd.env("OUT_DIR", &out_dir);
1742    }
1743
1744    // Adding output directory for each build script
1745    let is_multiple_build_scripts_enabled = unit
1746        .pkg
1747        .manifest()
1748        .unstable_features()
1749        .require(Feature::multiple_build_scripts())
1750        .is_ok();
1751
1752    if is_multiple_build_scripts_enabled {
1753        for dep in deps {
1754            if dep.unit.mode.is_run_custom_build() {
1755                let out_dir = &build_runner.files().build_script_out_dir(&dep.unit);
1756                let target_name = dep.unit.target.name();
1757                let out_dir_prefix = target_name
1758                    .strip_prefix("build-script-")
1759                    .unwrap_or(target_name);
1760                let out_dir_name = format!("{out_dir_prefix}_OUT_DIR");
1761                cmd.env(&out_dir_name, &out_dir);
1762            }
1763        }
1764    }
1765    for arg in extern_args(build_runner, unit, &mut unstable_opts)? {
1766        cmd.arg(arg);
1767    }
1768
1769    for (var, env) in artifact::get_env(build_runner, deps)? {
1770        cmd.env(&var, env);
1771    }
1772
1773    // This will only be set if we're already using a feature
1774    // requiring nightly rust
1775    if unstable_opts {
1776        cmd.arg("-Z").arg("unstable-options");
1777    }
1778
1779    Ok(())
1780}
1781
1782fn add_dep_arg<'a, 'b: 'a>(
1783    map: &mut BTreeMap<&'a Unit, PathBuf>,
1784    build_runner: &'b BuildRunner<'b, '_>,
1785    unit: &'a Unit,
1786) {
1787    if map.contains_key(&unit) {
1788        return;
1789    }
1790    map.insert(&unit, build_runner.files().deps_dir(&unit));
1791
1792    for dep in build_runner.unit_deps(unit) {
1793        add_dep_arg(map, build_runner, &dep.unit);
1794    }
1795}
1796
1797/// Adds extra rustc flags and environment variables collected from the output
1798/// of a build-script to the command to execute, include custom environment
1799/// variables and `cfg`.
1800fn add_custom_flags(
1801    cmd: &mut ProcessBuilder,
1802    build_script_outputs: &BuildScriptOutputs,
1803    metadata_vec: Option<Vec<UnitHash>>,
1804) -> CargoResult<()> {
1805    if let Some(metadata_vec) = metadata_vec {
1806        for metadata in metadata_vec {
1807            if let Some(output) = build_script_outputs.get(metadata) {
1808                for cfg in output.cfgs.iter() {
1809                    cmd.arg("--cfg").arg(cfg);
1810                }
1811                for check_cfg in &output.check_cfgs {
1812                    cmd.arg("--check-cfg").arg(check_cfg);
1813                }
1814                for (name, value) in output.env.iter() {
1815                    cmd.env(name, value);
1816                }
1817            }
1818        }
1819    }
1820
1821    Ok(())
1822}
1823
1824/// Generate a list of `-L` arguments
1825pub fn lib_search_paths(
1826    build_runner: &BuildRunner<'_, '_>,
1827    unit: &Unit,
1828) -> CargoResult<Vec<OsString>> {
1829    let mut lib_search_paths = Vec::new();
1830    if build_runner.bcx.gctx.cli_unstable().build_dir_new_layout {
1831        let mut map = BTreeMap::new();
1832
1833        // Recursively add all dependency args to rustc process
1834        add_dep_arg(&mut map, build_runner, unit);
1835
1836        let paths = map.into_iter().map(|(_, path)| path).sorted_unstable();
1837
1838        for path in paths {
1839            let mut deps = OsString::from("dependency=");
1840            deps.push(path);
1841            lib_search_paths.extend(["-L".into(), deps]);
1842        }
1843    } else {
1844        let mut deps = OsString::from("dependency=");
1845        deps.push(build_runner.files().deps_dir(unit));
1846        lib_search_paths.extend(["-L".into(), deps]);
1847    }
1848
1849    // Be sure that the host path is also listed. This'll ensure that proc macro
1850    // dependencies are correctly found (for reexported macros).
1851    if !unit.kind.is_host() {
1852        let mut deps = OsString::from("dependency=");
1853        deps.push(build_runner.files().host_deps(unit));
1854        lib_search_paths.extend(["-L".into(), deps]);
1855    }
1856
1857    Ok(lib_search_paths)
1858}
1859
1860/// Generates a list of `--extern` arguments.
1861pub fn extern_args(
1862    build_runner: &BuildRunner<'_, '_>,
1863    unit: &Unit,
1864    unstable_opts: &mut bool,
1865) -> CargoResult<Vec<OsString>> {
1866    let mut result = Vec::new();
1867    let deps = build_runner.unit_deps(unit);
1868
1869    let no_embed_metadata = build_runner.bcx.gctx.cli_unstable().no_embed_metadata;
1870
1871    // Closure to add one dependency to `result`.
1872    let mut link_to =
1873        |dep: &UnitDep, extern_crate_name: InternedString, noprelude: bool| -> CargoResult<()> {
1874            let mut value = OsString::new();
1875            let mut opts = Vec::new();
1876            let is_public_dependency_enabled = unit
1877                .pkg
1878                .manifest()
1879                .unstable_features()
1880                .require(Feature::public_dependency())
1881                .is_ok()
1882                || build_runner.bcx.gctx.cli_unstable().public_dependency;
1883            if !dep.public && unit.target.is_lib() && is_public_dependency_enabled {
1884                opts.push("priv");
1885                *unstable_opts = true;
1886            }
1887            if noprelude {
1888                opts.push("noprelude");
1889                *unstable_opts = true;
1890            }
1891            if !opts.is_empty() {
1892                value.push(opts.join(","));
1893                value.push(":");
1894            }
1895            value.push(extern_crate_name.as_str());
1896            value.push("=");
1897
1898            let mut pass = |file| {
1899                let mut value = value.clone();
1900                value.push(file);
1901                result.push(OsString::from("--extern"));
1902                result.push(value);
1903            };
1904
1905            let outputs = build_runner.outputs(&dep.unit)?;
1906
1907            if build_runner.only_requires_rmeta(unit, &dep.unit) || dep.unit.mode.is_check() {
1908                // Example: rlib dependency for an rlib, rmeta is all that is required.
1909                let output = outputs
1910                    .iter()
1911                    .find(|output| output.flavor == FileFlavor::Rmeta)
1912                    .expect("failed to find rmeta dep for pipelined dep");
1913                pass(&output.path);
1914            } else {
1915                // Example: a bin needs `rlib` for dependencies, it cannot use rmeta.
1916                for output in outputs.iter() {
1917                    if output.flavor == FileFlavor::Linkable {
1918                        pass(&output.path);
1919                    }
1920                    // If we use -Zembed-metadata=no, we also need to pass the path to the
1921                    // corresponding .rmeta file to the linkable artifact, because the
1922                    // normal dependency (rlib) doesn't contain the full metadata.
1923                    else if no_embed_metadata && output.flavor == FileFlavor::Rmeta {
1924                        pass(&output.path);
1925                    }
1926                }
1927            }
1928            Ok(())
1929        };
1930
1931    for dep in deps {
1932        if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
1933            link_to(dep, dep.extern_crate_name, dep.noprelude)?;
1934        }
1935    }
1936    if unit.target.proc_macro() {
1937        // Automatically import `proc_macro`.
1938        result.push(OsString::from("--extern"));
1939        result.push(OsString::from("proc_macro"));
1940    }
1941
1942    Ok(result)
1943}
1944
1945fn envify(s: &str) -> String {
1946    s.chars()
1947        .flat_map(|c| c.to_uppercase())
1948        .map(|c| if c == '-' { '_' } else { c })
1949        .collect()
1950}
1951
1952/// Configuration of the display of messages emitted by the compiler,
1953/// e.g. diagnostics, warnings, errors, and message caching.
1954struct OutputOptions {
1955    /// What format we're emitting from Cargo itself.
1956    format: MessageFormat,
1957    /// Where to write the JSON messages to support playback later if the unit
1958    /// is fresh. The file is created lazily so that in the normal case, lots
1959    /// of empty files are not created. If this is None, the output will not
1960    /// be cached (such as when replaying cached messages).
1961    cache_cell: Option<(PathBuf, OnceCell<File>)>,
1962    /// If `true`, display any diagnostics.
1963    /// Other types of JSON messages are processed regardless
1964    /// of the value of this flag.
1965    ///
1966    /// This is used primarily for cache replay. If you build with `-vv`, the
1967    /// cache will be filled with diagnostics from dependencies. When the
1968    /// cache is replayed without `-vv`, we don't want to show them.
1969    show_diagnostics: bool,
1970    /// Tracks the number of warnings we've seen so far.
1971    warnings_seen: usize,
1972    /// Tracks the number of errors we've seen so far.
1973    errors_seen: usize,
1974}
1975
1976impl OutputOptions {
1977    fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions {
1978        let path = build_runner.files().message_cache_path(unit);
1979        // Remove old cache, ignore ENOENT, which is the common case.
1980        drop(fs::remove_file(&path));
1981        let cache_cell = Some((path, OnceCell::new()));
1982        let show_diagnostics =
1983            build_runner.bcx.gctx.warning_handling().unwrap_or_default() != WarningHandling::Allow;
1984        OutputOptions {
1985            format: build_runner.bcx.build_config.message_format,
1986            cache_cell,
1987            show_diagnostics,
1988            warnings_seen: 0,
1989            errors_seen: 0,
1990        }
1991    }
1992}
1993
1994/// Cloned and sendable context about the manifest file.
1995///
1996/// Sometimes we enrich rustc's errors with some locations in the manifest file; this
1997/// contains a `Send`-able copy of the manifest information that we need for the
1998/// enriched errors.
1999struct ManifestErrorContext {
2000    /// The path to the manifest.
2001    path: PathBuf,
2002    /// The locations of various spans within the manifest.
2003    spans: toml::Spanned<toml::de::DeTable<'static>>,
2004    /// The raw manifest contents.
2005    contents: String,
2006    /// A lookup for all the unambiguous renamings, mapping from the original package
2007    /// name to the renamed one.
2008    rename_table: HashMap<InternedString, InternedString>,
2009    /// A list of targets we're compiling for, to determine which of the `[target.<something>.dependencies]`
2010    /// tables might be of interest.
2011    requested_kinds: Vec<CompileKind>,
2012    /// A list of all the collections of cfg values, one collection for each target, to determine
2013    /// which of the `[target.'cfg(...)'.dependencies]` tables might be of interest.
2014    cfgs: Vec<Vec<Cfg>>,
2015    host_name: InternedString,
2016    /// Cargo's working directory (for printing out a more friendly manifest path).
2017    cwd: PathBuf,
2018    /// Terminal width for formatting diagnostics.
2019    term_width: usize,
2020}
2021
2022fn on_stdout_line(
2023    state: &JobState<'_, '_>,
2024    line: &str,
2025    _package_id: PackageId,
2026    _target: &Target,
2027) -> CargoResult<()> {
2028    state.stdout(line.to_string())?;
2029    Ok(())
2030}
2031
2032fn on_stderr_line(
2033    state: &JobState<'_, '_>,
2034    line: &str,
2035    package_id: PackageId,
2036    manifest: &ManifestErrorContext,
2037    target: &Target,
2038    options: &mut OutputOptions,
2039) -> CargoResult<()> {
2040    if on_stderr_line_inner(state, line, package_id, manifest, target, options)? {
2041        // Check if caching is enabled.
2042        if let Some((path, cell)) = &mut options.cache_cell {
2043            // Cache the output, which will be replayed later when Fresh.
2044            let f = cell.try_borrow_mut_with(|| paths::create(path))?;
2045            debug_assert!(!line.contains('\n'));
2046            f.write_all(line.as_bytes())?;
2047            f.write_all(&[b'\n'])?;
2048        }
2049    }
2050    Ok(())
2051}
2052
2053/// Returns true if the line should be cached.
2054fn on_stderr_line_inner(
2055    state: &JobState<'_, '_>,
2056    line: &str,
2057    package_id: PackageId,
2058    manifest: &ManifestErrorContext,
2059    target: &Target,
2060    options: &mut OutputOptions,
2061) -> CargoResult<bool> {
2062    // We primarily want to use this function to process JSON messages from
2063    // rustc. The compiler should always print one JSON message per line, and
2064    // otherwise it may have other output intermingled (think RUST_LOG or
2065    // something like that), so skip over everything that doesn't look like a
2066    // JSON message.
2067    if !line.starts_with('{') {
2068        state.stderr(line.to_string())?;
2069        return Ok(true);
2070    }
2071
2072    let mut compiler_message: Box<serde_json::value::RawValue> = match serde_json::from_str(line) {
2073        Ok(msg) => msg,
2074
2075        // If the compiler produced a line that started with `{` but it wasn't
2076        // valid JSON, maybe it wasn't JSON in the first place! Forward it along
2077        // to stderr.
2078        Err(e) => {
2079            debug!("failed to parse json: {:?}", e);
2080            state.stderr(line.to_string())?;
2081            return Ok(true);
2082        }
2083    };
2084
2085    let count_diagnostic = |level, options: &mut OutputOptions| {
2086        if level == "warning" {
2087            options.warnings_seen += 1;
2088        } else if level == "error" {
2089            options.errors_seen += 1;
2090        }
2091    };
2092
2093    if let Ok(report) = serde_json::from_str::<FutureIncompatReport>(compiler_message.get()) {
2094        for item in &report.future_incompat_report {
2095            count_diagnostic(&*item.diagnostic.level, options);
2096        }
2097        state.future_incompat_report(report.future_incompat_report);
2098        return Ok(true);
2099    }
2100
2101    let res = serde_json::from_str::<SectionTiming>(compiler_message.get());
2102    if let Ok(timing_record) = res {
2103        state.on_section_timing_emitted(timing_record);
2104        return Ok(false);
2105    }
2106
2107    // Returns `true` if the diagnostic was modified.
2108    let add_pub_in_priv_diagnostic = |diag: &mut String| -> bool {
2109        // We are parsing the compiler diagnostic here, as this information isn't
2110        // currently exposed elsewhere.
2111        // At the time of writing this comment, rustc emits two different
2112        // "exported_private_dependencies" errors:
2113        //  - type `FromPriv` from private dependency 'priv_dep' in public interface
2114        //  - struct `FromPriv` from private dependency 'priv_dep' is re-exported
2115        // This regex matches them both. To see if it needs to be updated, grep the rust
2116        // source for "EXPORTED_PRIVATE_DEPENDENCIES".
2117        static PRIV_DEP_REGEX: LazyLock<Regex> =
2118            LazyLock::new(|| Regex::new("from private dependency '([A-Za-z0-9-_]+)'").unwrap());
2119        if let Some(crate_name) = PRIV_DEP_REGEX.captures(diag).and_then(|m| m.get(1))
2120            && let Some(span) = manifest.find_crate_span(crate_name.as_str())
2121        {
2122            let rel_path = pathdiff::diff_paths(&manifest.path, &manifest.cwd)
2123                .unwrap_or_else(|| manifest.path.clone())
2124                .display()
2125                .to_string();
2126            let report = [Group::with_title(Level::NOTE.secondary_title(format!(
2127                "dependency `{}` declared here",
2128                crate_name.as_str()
2129            )))
2130            .element(
2131                Snippet::source(&manifest.contents)
2132                    .path(rel_path)
2133                    .annotation(AnnotationKind::Context.span(span)),
2134            )];
2135
2136            let rendered = Renderer::styled()
2137                .term_width(manifest.term_width)
2138                .render(&report);
2139            diag.push_str(&rendered);
2140            diag.push('\n');
2141            return true;
2142        }
2143        false
2144    };
2145
2146    // Depending on what we're emitting from Cargo itself, we figure out what to
2147    // do with this JSON message.
2148    match options.format {
2149        // In the "human" output formats (human/short) or if diagnostic messages
2150        // from rustc aren't being included in the output of Cargo's JSON
2151        // messages then we extract the diagnostic (if present) here and handle
2152        // it ourselves.
2153        MessageFormat::Human
2154        | MessageFormat::Short
2155        | MessageFormat::Json {
2156            render_diagnostics: true,
2157            ..
2158        } => {
2159            #[derive(serde::Deserialize)]
2160            struct CompilerMessage<'a> {
2161                // `rendered` contains escape sequences, which can't be
2162                // zero-copy deserialized by serde_json.
2163                // See https://github.com/serde-rs/json/issues/742
2164                rendered: String,
2165                #[serde(borrow)]
2166                message: Cow<'a, str>,
2167                #[serde(borrow)]
2168                level: Cow<'a, str>,
2169                children: Vec<PartialDiagnostic>,
2170                code: Option<DiagnosticCode>,
2171            }
2172
2173            // A partial rustfix::diagnostics::Diagnostic. We deserialize only a
2174            // subset of the fields because rustc's output can be extremely
2175            // deeply nested JSON in pathological cases involving macro
2176            // expansion. Rustfix's Diagnostic struct is recursive containing a
2177            // field `children: Vec<Self>`, and it can cause deserialization to
2178            // hit serde_json's default recursion limit, or overflow the stack
2179            // if we turn that off. Cargo only cares about the 1 field listed
2180            // here.
2181            #[derive(serde::Deserialize)]
2182            struct PartialDiagnostic {
2183                spans: Vec<PartialDiagnosticSpan>,
2184            }
2185
2186            // A partial rustfix::diagnostics::DiagnosticSpan.
2187            #[derive(serde::Deserialize)]
2188            struct PartialDiagnosticSpan {
2189                suggestion_applicability: Option<Applicability>,
2190            }
2191
2192            #[derive(serde::Deserialize)]
2193            struct DiagnosticCode {
2194                code: String,
2195            }
2196
2197            if let Ok(mut msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2198            {
2199                if msg.message.starts_with("aborting due to")
2200                    || msg.message.ends_with("warning emitted")
2201                    || msg.message.ends_with("warnings emitted")
2202                {
2203                    // Skip this line; we'll print our own summary at the end.
2204                    return Ok(true);
2205                }
2206                // state.stderr will add a newline
2207                if msg.rendered.ends_with('\n') {
2208                    msg.rendered.pop();
2209                }
2210                let mut rendered = msg.rendered;
2211                if options.show_diagnostics {
2212                    let machine_applicable: bool = msg
2213                        .children
2214                        .iter()
2215                        .map(|child| {
2216                            child
2217                                .spans
2218                                .iter()
2219                                .filter_map(|span| span.suggestion_applicability)
2220                                .any(|app| app == Applicability::MachineApplicable)
2221                        })
2222                        .any(|b| b);
2223                    count_diagnostic(&msg.level, options);
2224                    if msg
2225                        .code
2226                        .as_ref()
2227                        .is_some_and(|c| c.code == "exported_private_dependencies")
2228                        && options.format != MessageFormat::Short
2229                    {
2230                        add_pub_in_priv_diagnostic(&mut rendered);
2231                    }
2232                    let lint = msg.code.is_some();
2233                    state.emit_diag(&msg.level, rendered, lint, machine_applicable)?;
2234                }
2235                return Ok(true);
2236            }
2237        }
2238
2239        MessageFormat::Json { ansi, .. } => {
2240            #[derive(serde::Deserialize, serde::Serialize)]
2241            struct CompilerMessage<'a> {
2242                rendered: String,
2243                #[serde(flatten, borrow)]
2244                other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2245                code: Option<DiagnosticCode<'a>>,
2246            }
2247
2248            #[derive(serde::Deserialize, serde::Serialize)]
2249            struct DiagnosticCode<'a> {
2250                code: String,
2251                #[serde(flatten, borrow)]
2252                other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2253            }
2254
2255            if let Ok(mut error) =
2256                serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2257            {
2258                let modified_diag = if error
2259                    .code
2260                    .as_ref()
2261                    .is_some_and(|c| c.code == "exported_private_dependencies")
2262                {
2263                    add_pub_in_priv_diagnostic(&mut error.rendered)
2264                } else {
2265                    false
2266                };
2267
2268                // Remove color information from the rendered string if color is not
2269                // enabled. Cargo always asks for ANSI colors from rustc. This allows
2270                // cached replay to enable/disable colors without re-invoking rustc.
2271                if !ansi {
2272                    error.rendered = anstream::adapter::strip_str(&error.rendered).to_string();
2273                }
2274                if !ansi || modified_diag {
2275                    let new_line = serde_json::to_string(&error)?;
2276                    compiler_message = serde_json::value::RawValue::from_string(new_line)?;
2277                }
2278            }
2279        }
2280    }
2281
2282    // We always tell rustc to emit messages about artifacts being produced.
2283    // These messages feed into pipelined compilation, as well as timing
2284    // information.
2285    //
2286    // Look for a matching directive and inform Cargo internally that a
2287    // metadata file has been produced.
2288    #[derive(serde::Deserialize)]
2289    struct ArtifactNotification<'a> {
2290        #[serde(borrow)]
2291        artifact: Cow<'a, str>,
2292    }
2293
2294    if let Ok(artifact) = serde_json::from_str::<ArtifactNotification<'_>>(compiler_message.get()) {
2295        trace!("found directive from rustc: `{}`", artifact.artifact);
2296        if artifact.artifact.ends_with(".rmeta") {
2297            debug!("looks like metadata finished early!");
2298            state.rmeta_produced();
2299        }
2300        return Ok(false);
2301    }
2302
2303    // And failing all that above we should have a legitimate JSON diagnostic
2304    // from the compiler, so wrap it in an external Cargo JSON message
2305    // indicating which package it came from and then emit it.
2306
2307    if !options.show_diagnostics {
2308        return Ok(true);
2309    }
2310
2311    #[derive(serde::Deserialize)]
2312    struct CompilerMessage<'a> {
2313        #[serde(borrow)]
2314        message: Cow<'a, str>,
2315        #[serde(borrow)]
2316        level: Cow<'a, str>,
2317    }
2318
2319    if let Ok(msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get()) {
2320        if msg.message.starts_with("aborting due to")
2321            || msg.message.ends_with("warning emitted")
2322            || msg.message.ends_with("warnings emitted")
2323        {
2324            // Skip this line; we'll print our own summary at the end.
2325            return Ok(true);
2326        }
2327        count_diagnostic(&msg.level, options);
2328    }
2329
2330    let msg = machine_message::FromCompiler {
2331        package_id: package_id.to_spec(),
2332        manifest_path: &manifest.path,
2333        target,
2334        message: compiler_message,
2335    }
2336    .to_json_string();
2337
2338    // Switch json lines from rustc/rustdoc that appear on stderr to stdout
2339    // instead. We want the stdout of Cargo to always be machine parseable as
2340    // stderr has our colorized human-readable messages.
2341    state.stdout(msg)?;
2342    Ok(true)
2343}
2344
2345impl ManifestErrorContext {
2346    fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> ManifestErrorContext {
2347        let mut duplicates = HashSet::new();
2348        let mut rename_table = HashMap::new();
2349
2350        for dep in build_runner.unit_deps(unit) {
2351            let unrenamed_id = dep.unit.pkg.package_id().name();
2352            if duplicates.contains(&unrenamed_id) {
2353                continue;
2354            }
2355            match rename_table.entry(unrenamed_id) {
2356                std::collections::hash_map::Entry::Occupied(occ) => {
2357                    occ.remove_entry();
2358                    duplicates.insert(unrenamed_id);
2359                }
2360                std::collections::hash_map::Entry::Vacant(vac) => {
2361                    vac.insert(dep.extern_crate_name);
2362                }
2363            }
2364        }
2365
2366        let bcx = build_runner.bcx;
2367        ManifestErrorContext {
2368            path: unit.pkg.manifest_path().to_owned(),
2369            spans: unit.pkg.manifest().document().clone(),
2370            contents: unit.pkg.manifest().contents().to_owned(),
2371            requested_kinds: bcx.target_data.requested_kinds().to_owned(),
2372            host_name: bcx.rustc().host,
2373            rename_table,
2374            cwd: path_args(build_runner.bcx.ws, unit).1,
2375            cfgs: bcx
2376                .target_data
2377                .requested_kinds()
2378                .iter()
2379                .map(|k| bcx.target_data.cfg(*k).to_owned())
2380                .collect(),
2381            term_width: bcx
2382                .gctx
2383                .shell()
2384                .err_width()
2385                .diagnostic_terminal_width()
2386                .unwrap_or(annotate_snippets::renderer::DEFAULT_TERM_WIDTH),
2387        }
2388    }
2389
2390    fn requested_target_names(&self) -> impl Iterator<Item = &str> {
2391        self.requested_kinds.iter().map(|kind| match kind {
2392            CompileKind::Host => &self.host_name,
2393            CompileKind::Target(target) => target.short_name(),
2394        })
2395    }
2396
2397    /// Find a span for the dependency that specifies this unrenamed crate, if it's unique.
2398    ///
2399    /// rustc diagnostics (at least for public-in-private) mention the un-renamed
2400    /// crate: if you have `foo = { package = "bar" }`, the rustc diagnostic will
2401    /// say "bar".
2402    ///
2403    /// This function does its best to find a span for "bar", but it could fail if
2404    /// there are multiple candidates:
2405    ///
2406    /// ```toml
2407    /// foo = { package = "bar" }
2408    /// baz = { path = "../bar", package = "bar" }
2409    /// ```
2410    fn find_crate_span(&self, unrenamed: &str) -> Option<Range<usize>> {
2411        let orig_name = self.rename_table.get(unrenamed)?.as_str();
2412
2413        if let Some((k, v)) = get_key_value(&self.spans, &["dependencies", orig_name]) {
2414            // We make some effort to find the unrenamed text: in
2415            //
2416            // ```
2417            // foo = { package = "bar" }
2418            // ```
2419            //
2420            // we try to find the "bar", but fall back to "foo" if we can't (which might
2421            // happen if the renaming took place in the workspace, for example).
2422            if let Some(package) = v.get_ref().as_table().and_then(|t| t.get("package")) {
2423                return Some(package.span());
2424            } else {
2425                return Some(k.span());
2426            }
2427        }
2428
2429        // The dependency could also be in a target-specific table, like
2430        // [target.x86_64-unknown-linux-gnu.dependencies] or
2431        // [target.'cfg(something)'.dependencies]. We filter out target tables
2432        // that don't match a requested target or a requested cfg.
2433        if let Some(target) = self
2434            .spans
2435            .as_ref()
2436            .get("target")
2437            .and_then(|t| t.as_ref().as_table())
2438        {
2439            for (platform, platform_table) in target.iter() {
2440                match platform.as_ref().parse::<Platform>() {
2441                    Ok(Platform::Name(name)) => {
2442                        if !self.requested_target_names().any(|n| n == name) {
2443                            continue;
2444                        }
2445                    }
2446                    Ok(Platform::Cfg(cfg_expr)) => {
2447                        if !self.cfgs.iter().any(|cfgs| cfg_expr.matches(cfgs)) {
2448                            continue;
2449                        }
2450                    }
2451                    Err(_) => continue,
2452                }
2453
2454                let Some(platform_table) = platform_table.as_ref().as_table() else {
2455                    continue;
2456                };
2457
2458                if let Some(deps) = platform_table
2459                    .get("dependencies")
2460                    .and_then(|d| d.as_ref().as_table())
2461                {
2462                    if let Some((k, v)) = deps.get_key_value(orig_name) {
2463                        if let Some(package) = v.get_ref().as_table().and_then(|t| t.get("package"))
2464                        {
2465                            return Some(package.span());
2466                        } else {
2467                            return Some(k.span());
2468                        }
2469                    }
2470                }
2471            }
2472        }
2473        None
2474    }
2475}
2476
2477/// Creates a unit of work that replays the cached compiler message.
2478///
2479/// Usually used when a job is fresh and doesn't need to recompile.
2480fn replay_output_cache(
2481    package_id: PackageId,
2482    manifest: ManifestErrorContext,
2483    target: &Target,
2484    path: PathBuf,
2485    format: MessageFormat,
2486    show_diagnostics: bool,
2487) -> Work {
2488    let target = target.clone();
2489    let mut options = OutputOptions {
2490        format,
2491        cache_cell: None,
2492        show_diagnostics,
2493        warnings_seen: 0,
2494        errors_seen: 0,
2495    };
2496    Work::new(move |state| {
2497        if !path.exists() {
2498            // No cached output, probably didn't emit anything.
2499            return Ok(());
2500        }
2501        // We sometimes have gigabytes of output from the compiler, so avoid
2502        // loading it all into memory at once, as that can cause OOM where
2503        // otherwise there would be none.
2504        let file = paths::open(&path)?;
2505        let mut reader = std::io::BufReader::new(file);
2506        let mut line = String::new();
2507        loop {
2508            let length = reader.read_line(&mut line)?;
2509            if length == 0 {
2510                break;
2511            }
2512            let trimmed = line.trim_end_matches(&['\n', '\r'][..]);
2513            on_stderr_line(state, trimmed, package_id, &manifest, &target, &mut options)?;
2514            line.clear();
2515        }
2516        Ok(())
2517    })
2518}
2519
2520/// Provides a package name with descriptive target information,
2521/// e.g., '`foo` (bin "bar" test)', '`foo` (lib doctest)'.
2522fn descriptive_pkg_name(name: &str, target: &Target, mode: &CompileMode) -> String {
2523    let desc_name = target.description_named();
2524    let mode = if mode.is_rustc_test() && !(target.is_test() || target.is_bench()) {
2525        " test"
2526    } else if mode.is_doc_test() {
2527        " doctest"
2528    } else if mode.is_doc() {
2529        " doc"
2530    } else {
2531        ""
2532    };
2533    format!("`{name}` ({desc_name}{mode})")
2534}
2535
2536/// Applies environment variables from config `[env]` to [`ProcessBuilder`].
2537pub(crate) fn apply_env_config(
2538    gctx: &crate::GlobalContext,
2539    cmd: &mut ProcessBuilder,
2540) -> CargoResult<()> {
2541    for (key, value) in gctx.env_config()?.iter() {
2542        // never override a value that has already been set by cargo
2543        if cmd.get_envs().contains_key(key) {
2544            continue;
2545        }
2546        cmd.env(key, value);
2547    }
2548    Ok(())
2549}
2550
2551/// Checks if there are some scrape units waiting to be processed.
2552fn should_include_scrape_units(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
2553    unit.mode.is_doc() && bcx.scrape_units.len() > 0 && bcx.ws.unit_needs_doc_scrape(unit)
2554}
2555
2556/// Gets the file path of function call information output from `rustdoc`.
2557fn scrape_output_path(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<PathBuf> {
2558    assert!(unit.mode.is_doc() || unit.mode.is_doc_scrape());
2559    build_runner
2560        .outputs(unit)
2561        .map(|outputs| outputs[0].path.clone())
2562}
2563
2564/// Gets the dep-info file emitted by rustdoc.
2565fn rustdoc_dep_info_loc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> PathBuf {
2566    let mut loc = build_runner.files().fingerprint_file_path(unit, "");
2567    loc.set_extension("d");
2568    loc
2569}