Skip to main content

cargo/compiler/
mod.rs

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