Skip to main content

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