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