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