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    if let Some(trim_paths) = unit.profile.trim_paths.as_ref() {
987        trim_paths_args_rustdoc(&mut rustdoc, build_runner, unit, trim_paths)?;
988    }
989
990    rustdoc.args(unit.pkg.manifest().lint_rustflags());
991
992    let metadata = build_runner.metadata_for_doc_units[unit];
993    rustdoc
994        .arg("-C")
995        .arg(format!("metadata={}", metadata.c_metadata()));
996
997    if unit.mode.is_doc_scrape() {
998        debug_assert!(build_runner.bcx.scrape_units.contains(unit));
999
1000        if unit.target.is_test() {
1001            rustdoc.arg("--scrape-tests");
1002        }
1003
1004        rustdoc.arg("-Zunstable-options");
1005
1006        rustdoc
1007            .arg("--scrape-examples-output-path")
1008            .arg(scrape_output_path(build_runner, unit)?);
1009
1010        // Only scrape example for items from crates in the workspace, to reduce generated file size
1011        for pkg in build_runner.bcx.packages.packages() {
1012            let names = pkg
1013                .targets()
1014                .iter()
1015                .map(|target| target.crate_name())
1016                .collect::<HashSet<_>>();
1017            for name in names {
1018                rustdoc.arg("--scrape-examples-target-crate").arg(name);
1019            }
1020        }
1021    }
1022
1023    if should_include_scrape_units(build_runner.bcx, unit) {
1024        rustdoc.arg("-Zunstable-options");
1025    }
1026
1027    build_deps_args(&mut rustdoc, build_runner, unit)?;
1028    rustdoc::add_root_urls(build_runner, unit, &mut rustdoc)?;
1029
1030    rustdoc::add_output_format(build_runner, &mut rustdoc)?;
1031
1032    if let Some(args) = build_runner.bcx.extra_args_for(unit) {
1033        rustdoc.args(args);
1034    }
1035    rustdoc.args(&unit.rustdocflags);
1036
1037    if !crate_version_flag_already_present(&rustdoc) {
1038        append_crate_version_flag(unit, &mut rustdoc);
1039    }
1040
1041    Ok(rustdoc)
1042}
1043
1044/// Creates a unit of work invoking `rustdoc` for documenting the `unit`.
1045#[tracing::instrument(skip_all)]
1046fn rustdoc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Work> {
1047    let mut rustdoc = prepare_rustdoc(build_runner, unit)?;
1048
1049    let crate_name = unit.target.crate_name();
1050    let is_json_output = build_runner.bcx.build_config.intent.wants_doc_json_output();
1051    let doc_dir = build_runner.files().output_dir(unit);
1052    // Create the documentation directory ahead of time as rustdoc currently has
1053    // a bug where concurrent invocations will race to create this directory if
1054    // it doesn't already exist.
1055    paths::create_dir_all(&doc_dir)?;
1056
1057    let target_desc = unit.target.description_named();
1058    let name = unit.pkg.name();
1059    let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
1060    let package_id = unit.pkg.package_id();
1061    let target = Target::clone(&unit.target);
1062    let manifest = ManifestErrorContext::new(build_runner, unit);
1063
1064    let rustdoc_dep_info_loc = rustdoc_dep_info_loc(build_runner, unit);
1065    let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
1066    let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
1067    let pkg_root = unit.pkg.root().to_path_buf();
1068    let cwd = rustdoc
1069        .get_cwd()
1070        .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
1071        .to_path_buf();
1072    let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
1073    let is_local = unit.is_local();
1074    let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
1075    let rustdoc_depinfo_enabled = build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo;
1076
1077    let mut output_options = OutputOptions::for_dirty(build_runner, unit);
1078    let script_metadatas = build_runner.find_build_script_metadatas(unit);
1079    let scrape_outputs = if should_include_scrape_units(build_runner.bcx, unit) {
1080        Some(
1081            build_runner
1082                .bcx
1083                .scrape_units
1084                .iter()
1085                .map(|unit| {
1086                    Ok((
1087                        build_runner.files().metadata(unit).unit_id(),
1088                        scrape_output_path(build_runner, unit)?,
1089                    ))
1090                })
1091                .collect::<CargoResult<HashMap<_, _>>>()?,
1092        )
1093    } else {
1094        None
1095    };
1096
1097    let failed_scrape_units = Arc::clone(&build_runner.failed_scrape_units);
1098    let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
1099        && !matches!(
1100            build_runner.bcx.gctx.shell().verbosity(),
1101            Verbosity::Verbose
1102        );
1103    let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
1104        make_failed_scrape_diagnostic(
1105            build_runner,
1106            unit,
1107            format_args!("failed to scan {target_desc} in package `{name}` for example code usage"),
1108        )
1109    });
1110    if hide_diagnostics_for_scrape_unit {
1111        output_options.show_diagnostics = false;
1112    }
1113
1114    Ok(Work::new(move |state| {
1115        add_custom_flags(
1116            &mut rustdoc,
1117            &build_script_outputs.lock().unwrap(),
1118            script_metadatas,
1119        )?;
1120
1121        // Add the output of scraped examples to the rustdoc command.
1122        // This action must happen after the unit's dependencies have finished,
1123        // because some of those deps may be Docscrape units which have failed.
1124        // So we dynamically determine which `--with-examples` flags to pass here.
1125        if let Some(scrape_outputs) = scrape_outputs {
1126            let failed_scrape_units = failed_scrape_units.lock().unwrap();
1127            for (metadata, output_path) in &scrape_outputs {
1128                if !failed_scrape_units.contains(metadata) {
1129                    rustdoc.arg("--with-examples").arg(output_path);
1130                }
1131            }
1132        }
1133
1134        if !is_json_output {
1135            let crate_dir = doc_dir.join(&crate_name);
1136            if crate_dir.exists() {
1137                // Remove output from a previous build. This ensures that stale
1138                // files for removed items are removed.
1139                debug!("removing pre-existing doc directory {:?}", crate_dir);
1140                paths::remove_dir_all(&crate_dir)?;
1141            }
1142        };
1143        state.running(&rustdoc);
1144        let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
1145
1146        let result = rustdoc
1147            .exec_with_streaming(
1148                &mut |line| on_stdout_line(state, line, package_id, &target),
1149                &mut |line| {
1150                    on_stderr_line(
1151                        state,
1152                        line,
1153                        package_id,
1154                        &manifest,
1155                        &target,
1156                        &mut output_options,
1157                    )
1158                },
1159                false,
1160            )
1161            .map_err(verbose_if_simple_exit_code)
1162            .with_context(|| format!("could not document `{}`", name));
1163
1164        if let Err(e) = result {
1165            if let Some(diagnostic) = failed_scrape_diagnostic {
1166                state.warning(diagnostic);
1167            }
1168
1169            return Err(e);
1170        }
1171
1172        if rustdoc_depinfo_enabled && rustdoc_dep_info_loc.exists() {
1173            fingerprint::translate_dep_info(
1174                &rustdoc_dep_info_loc,
1175                &dep_info_loc,
1176                &cwd,
1177                &pkg_root,
1178                &build_dir,
1179                &rustdoc,
1180                // Should we track source file for doc gen?
1181                is_local,
1182                &env_config,
1183            )
1184            .with_context(|| {
1185                internal(format_args!(
1186                    "could not parse/generate dep info at: {}",
1187                    rustdoc_dep_info_loc.display()
1188                ))
1189            })?;
1190            // This mtime shift allows Cargo to detect if a source file was
1191            // modified in the middle of the build.
1192            paths::set_file_time_no_err(dep_info_loc, timestamp);
1193        }
1194
1195        Ok(())
1196    }))
1197}
1198
1199// The --crate-version flag could have already been passed in RUSTDOCFLAGS
1200// or as an extra compiler argument for rustdoc
1201fn crate_version_flag_already_present(rustdoc: &ProcessBuilder) -> bool {
1202    rustdoc.get_args().any(|flag| {
1203        flag.to_str()
1204            .map_or(false, |flag| flag.starts_with(RUSTDOC_CRATE_VERSION_FLAG))
1205    })
1206}
1207
1208fn append_crate_version_flag(unit: &Unit, rustdoc: &mut ProcessBuilder) {
1209    rustdoc
1210        .arg(RUSTDOC_CRATE_VERSION_FLAG)
1211        .arg(unit.pkg.version().to_string());
1212}
1213
1214enum CapLints {
1215    Allow,
1216    Warn,
1217}
1218
1219fn compute_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit) -> Option<CapLints> {
1220    // If this is an upstream dep we don't want warnings from, turn off all
1221    // lints.
1222    if !unit.show_warnings(bcx.gctx) {
1223        Some(CapLints::Allow)
1224    // If this is an upstream dep but we *do* want warnings, make sure that they
1225    // don't fail compilation.
1226    } else if !unit.is_local() {
1227        Some(CapLints::Warn)
1228    } else {
1229        None
1230    }
1231}
1232
1233/// Adds [`--cap-lints`] to the command to execute.
1234///
1235/// [`--cap-lints`]: https://doc.rust-lang.org/nightly/rustc/lints/levels.html#capping-lints
1236fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuilder) {
1237    if let Some(cap_lints) = compute_cap_lints(bcx, unit) {
1238        match cap_lints {
1239            CapLints::Allow => {
1240                cmd.arg("--cap-lints").arg("allow");
1241            }
1242            CapLints::Warn => {
1243                cmd.arg("--cap-lints").arg("warn");
1244            }
1245        }
1246    }
1247}
1248
1249/// Forwards [`-Zallow-features`] if it is set for cargo.
1250///
1251/// [`-Zallow-features`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#allow-features
1252fn add_allow_features(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1253    if let Some(allow) = &build_runner.bcx.gctx.cli_unstable().allow_features {
1254        use std::fmt::Write;
1255        let mut arg = String::from("-Zallow-features=");
1256        for f in allow {
1257            let _ = write!(&mut arg, "{f},");
1258        }
1259        cmd.arg(arg.trim_end_matches(','));
1260    }
1261}
1262
1263/// Adds [`--error-format`] to the command to execute.
1264///
1265/// Cargo always uses JSON output. This has several benefits, such as being
1266/// easier to parse, handles changing formats (for replaying cached messages),
1267/// ensures atomic output (so messages aren't interleaved), allows for
1268/// intercepting messages like rmeta artifacts, etc. rustc includes a
1269/// "rendered" field in the JSON message with the message properly formatted,
1270/// which Cargo will extract and display to the user.
1271///
1272/// [`--error-format`]: https://doc.rust-lang.org/nightly/rustc/command-line-arguments.html#--error-format-control-how-errors-are-produced
1273fn add_error_format_and_color(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1274    let enable_timings =
1275        build_runner.bcx.gctx.cli_unstable().section_timings && build_runner.bcx.logger.is_some();
1276    if enable_timings {
1277        cmd.arg("-Zunstable-options");
1278    }
1279
1280    cmd.arg("--error-format=json");
1281
1282    let mut json = String::from(
1283        "--json=diagnostic-rendered-ansi,artifacts,future-incompat,unused-externs-silent",
1284    );
1285    if let MessageFormat::Short | MessageFormat::Json { short: true, .. } =
1286        build_runner.bcx.build_config.message_format
1287    {
1288        json.push_str(",diagnostic-short");
1289    } else if build_runner.bcx.gctx.shell().err_unicode()
1290        && build_runner.bcx.gctx.cli_unstable().rustc_unicode
1291    {
1292        json.push_str(",diagnostic-unicode");
1293    }
1294    if enable_timings {
1295        json.push_str(",timings");
1296    }
1297    cmd.arg(json);
1298
1299    let gctx = build_runner.bcx.gctx;
1300    if let Some(width) = gctx.shell().err_width().diagnostic_terminal_width() {
1301        cmd.arg(format!("--diagnostic-width={width}"));
1302    }
1303}
1304
1305/// Adds essential rustc flags and environment variables to the command to execute.
1306fn build_base_args(
1307    build_runner: &BuildRunner<'_, '_>,
1308    cmd: &mut ProcessBuilder,
1309    unit: &Unit,
1310) -> CargoResult<()> {
1311    assert!(!unit.mode.is_run_custom_build());
1312
1313    let bcx = build_runner.bcx;
1314    let Profile {
1315        ref opt_level,
1316        codegen_backend,
1317        codegen_units,
1318        debuginfo,
1319        debug_assertions,
1320        split_debuginfo,
1321        overflow_checks,
1322        rpath,
1323        ref panic,
1324        incremental,
1325        strip,
1326        rustflags: profile_rustflags,
1327        trim_paths,
1328        hint_mostly_unused: profile_hint_mostly_unused,
1329        ..
1330    } = unit.profile.clone();
1331    let hints = unit.pkg.hints().cloned().unwrap_or_default();
1332    let test = unit.mode.is_any_test();
1333
1334    let warn = |msg: &str| {
1335        bcx.gctx.shell().warn(format!(
1336            "{}@{}: {msg}",
1337            unit.pkg.package_id().name(),
1338            unit.pkg.package_id().version()
1339        ))
1340    };
1341    let unit_capped_warn = |msg: &str| {
1342        if unit.show_warnings(bcx.gctx) {
1343            warn(msg)
1344        } else {
1345            Ok(())
1346        }
1347    };
1348
1349    cmd.arg("--crate-name").arg(&unit.target.crate_name());
1350
1351    let edition = unit.target.edition();
1352    edition.cmd_edition_arg(cmd);
1353
1354    add_path_args(bcx.ws, unit, cmd);
1355    add_error_format_and_color(build_runner, cmd);
1356    add_allow_features(build_runner, cmd);
1357
1358    let mut contains_dy_lib = false;
1359    if !test {
1360        for crate_type in &unit.target.rustc_crate_types() {
1361            cmd.arg("--crate-type").arg(crate_type.as_str());
1362            contains_dy_lib |= crate_type == &CrateType::Dylib;
1363        }
1364    }
1365
1366    if unit.mode.is_check() {
1367        cmd.arg("--emit=dep-info,metadata");
1368    } else if !build_runner
1369        .bcx
1370        .target_data
1371        .info(unit.kind)
1372        .should_embed_metadata()
1373    {
1374        // Nightly rustc supports the -Zembed-metadata=no flag, which tells it to avoid including
1375        // full metadata in rlib/dylib artifacts, to save space on disk. In this case, metadata
1376        // will only be stored in .rmeta files.
1377        // When we use this flag, we should also pass --emit=metadata to all artifacts that
1378        // contain useful metadata (rlib/dylib/proc macros), so that a .rmeta file is actually
1379        // generated. If we didn't do this, the full metadata would not get written anywhere.
1380        // However, we do not want to pass --emit=metadata to artifacts that never produce useful
1381        // metadata, such as binaries, because that would just unnecessarily create empty .rmeta
1382        // files on disk.
1383        if unit.benefits_from_no_embed_metadata() {
1384            cmd.arg("--emit=dep-info,metadata,link");
1385            cmd.args(&["-Z", "embed-metadata=no"]);
1386        } else {
1387            cmd.arg("--emit=dep-info,link");
1388        }
1389    } else {
1390        // If we don't use -Zembed-metadata=no, we emit .rmeta files only for rlib outputs.
1391        // This metadata may be used in this session for a pipelined compilation, or it may
1392        // be used in a future Cargo session as part of a pipelined compile.
1393        if !unit.requires_upstream_objects() {
1394            cmd.arg("--emit=dep-info,metadata,link");
1395        } else {
1396            cmd.arg("--emit=dep-info,link");
1397        }
1398    }
1399
1400    let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
1401        || (contains_dy_lib && !build_runner.is_primary_package(unit));
1402    if prefer_dynamic {
1403        cmd.arg("-C").arg("prefer-dynamic");
1404    }
1405
1406    if opt_level.as_str() != "0" {
1407        cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
1408    }
1409
1410    if *panic != PanicStrategy::Unwind {
1411        cmd.arg("-C").arg(format!("panic={}", panic));
1412    }
1413    if *panic == PanicStrategy::ImmediateAbort {
1414        cmd.arg("-Z").arg("unstable-options");
1415    }
1416
1417    cmd.args(&lto_args(build_runner, unit));
1418
1419    if let Some(backend) = codegen_backend {
1420        cmd.arg("-Z").arg(&format!("codegen-backend={}", backend));
1421    }
1422
1423    if let Some(n) = codegen_units {
1424        cmd.arg("-C").arg(&format!("codegen-units={}", n));
1425    }
1426
1427    let debuginfo = debuginfo.into_inner();
1428    // Shorten the number of arguments if possible.
1429    if debuginfo != TomlDebugInfo::None {
1430        cmd.arg("-C").arg(format!("debuginfo={debuginfo}"));
1431        // This is generally just an optimization on build time so if we don't
1432        // pass it then it's ok. The values for the flag (off, packed, unpacked)
1433        // may be supported or not depending on the platform, so availability is
1434        // checked per-value. For example, at the time of writing this code, on
1435        // Windows the only stable valid value for split-debuginfo is "packed",
1436        // while on Linux "unpacked" is also stable.
1437        if let Some(split) = split_debuginfo {
1438            if build_runner
1439                .bcx
1440                .target_data
1441                .info(unit.kind)
1442                .supports_debuginfo_split(split)
1443            {
1444                cmd.arg("-C").arg(format!("split-debuginfo={split}"));
1445            }
1446        }
1447    }
1448
1449    if let Some(trim_paths) = trim_paths {
1450        trim_paths_args(cmd, build_runner, unit, &trim_paths)?;
1451    }
1452
1453    match compute_cap_lints(bcx, unit) {
1454        None | Some(CapLints::Warn) => {
1455            cmd.args(unit.pkg.manifest().lint_rustflags());
1456        }
1457        // If we pass --cap-lints=allow, there is no point in making the CLI larger by including
1458        // potentially a lot of --warn lint flags.
1459        Some(CapLints::Allow) => {}
1460    }
1461    cmd.args(&profile_rustflags);
1462
1463    // `-C overflow-checks` is implied by the setting of `-C debug-assertions`,
1464    // so we only need to provide `-C overflow-checks` if it differs from
1465    // the value of `-C debug-assertions` we would provide.
1466    if opt_level.as_str() != "0" {
1467        if debug_assertions {
1468            cmd.args(&["-C", "debug-assertions=on"]);
1469            if !overflow_checks {
1470                cmd.args(&["-C", "overflow-checks=off"]);
1471            }
1472        } else if overflow_checks {
1473            cmd.args(&["-C", "overflow-checks=on"]);
1474        }
1475    } else if !debug_assertions {
1476        cmd.args(&["-C", "debug-assertions=off"]);
1477        if overflow_checks {
1478            cmd.args(&["-C", "overflow-checks=on"]);
1479        }
1480    } else if !overflow_checks {
1481        cmd.args(&["-C", "overflow-checks=off"]);
1482    }
1483
1484    if test && unit.target.harness() {
1485        cmd.arg("--test");
1486
1487        // Cargo has historically never compiled `--test` binaries with
1488        // `panic=abort` because the `test` crate itself didn't support it.
1489        // Support is now upstream, however, but requires an unstable flag to be
1490        // passed when compiling the test. We require, in Cargo, an unstable
1491        // flag to pass to rustc, so register that here. Eventually this flag
1492        // will simply not be needed when the behavior is stabilized in the Rust
1493        // compiler itself.
1494        if *panic == PanicStrategy::Abort || *panic == PanicStrategy::ImmediateAbort {
1495            cmd.arg("-Z").arg("panic-abort-tests");
1496        }
1497    } else if test {
1498        cmd.arg("--cfg").arg("test");
1499    }
1500
1501    cmd.args(&features_args(unit));
1502    cmd.args(&check_cfg_args(unit));
1503
1504    let meta = build_runner.files().metadata(unit);
1505    cmd.arg("-C")
1506        .arg(&format!("metadata={}", meta.c_metadata()));
1507    if let Some(c_extra_filename) = meta.c_extra_filename() {
1508        cmd.arg("-C")
1509            .arg(&format!("extra-filename=-{c_extra_filename}"));
1510    }
1511
1512    if rpath {
1513        cmd.arg("-C").arg("rpath");
1514    }
1515
1516    cmd.arg("--out-dir")
1517        .arg(&build_runner.files().output_dir(unit));
1518
1519    unit.kind.add_target_arg(cmd);
1520
1521    add_codegen_linker(cmd, build_runner, unit, bcx.gctx.target_applies_to_host()?);
1522
1523    if incremental {
1524        add_codegen_incremental(cmd, build_runner, unit)
1525    }
1526
1527    let pkg_hint_mostly_unused = match hints.mostly_unused {
1528        None => None,
1529        Some(toml::Value::Boolean(b)) => Some(b),
1530        Some(v) => {
1531            unit_capped_warn(&format!(
1532                "ignoring unsupported value type ({}) for 'hints.mostly-unused', which expects a boolean",
1533                v.type_str()
1534            ))?;
1535            None
1536        }
1537    };
1538    if profile_hint_mostly_unused
1539        .or(pkg_hint_mostly_unused)
1540        .unwrap_or(false)
1541    {
1542        if bcx.gctx.cli_unstable().profile_hint_mostly_unused {
1543            cmd.arg("-Zhint-mostly-unused");
1544        } else {
1545            if profile_hint_mostly_unused.is_some() {
1546                // Profiles come from the top-level unit, so we don't use `unit_capped_warn` here.
1547                warn(
1548                    "ignoring 'hint-mostly-unused' profile option, pass `-Zprofile-hint-mostly-unused` to enable it",
1549                )?;
1550            } else if pkg_hint_mostly_unused.is_some() {
1551                unit_capped_warn(
1552                    "ignoring 'hints.mostly-unused', pass `-Zprofile-hint-mostly-unused` to enable it",
1553                )?;
1554            }
1555        }
1556    }
1557
1558    let strip = strip.into_inner();
1559    if strip != StripInner::None {
1560        cmd.arg("-C").arg(format!("strip={}", strip));
1561    }
1562
1563    if unit.is_std {
1564        // -Zforce-unstable-if-unmarked prevents the accidental use of
1565        // unstable crates within the sysroot (such as "extern crate libc" or
1566        // any non-public crate in the sysroot).
1567        //
1568        // RUSTC_BOOTSTRAP allows unstable features on stable.
1569        cmd.arg("-Z")
1570            .arg("force-unstable-if-unmarked")
1571            .env("RUSTC_BOOTSTRAP", "1");
1572    }
1573
1574    if let Some(version) = unit.pkg.manifest().rust_version()
1575        && bcx.gctx.cli_unstable().hint_msrv
1576    {
1577        cmd.arg("-Z").arg(format!("hint-msrv={version}"));
1578    }
1579
1580    Ok(())
1581}
1582
1583/// All active features for the unit passed as `--cfg features=<feature-name>`.
1584fn features_args(unit: &Unit) -> Vec<OsString> {
1585    let mut args = Vec::with_capacity(unit.features.len() * 2);
1586
1587    for feat in &unit.features {
1588        args.push(OsString::from("--cfg"));
1589        args.push(OsString::from(format!("feature=\"{}\"", feat)));
1590    }
1591
1592    args
1593}
1594
1595/// Generates the `--check-cfg` arguments for the `unit`.
1596fn check_cfg_args(unit: &Unit) -> Vec<OsString> {
1597    // The routine below generates the --check-cfg arguments. Our goals here are to
1598    // enable the checking of conditionals and pass the list of declared features.
1599    //
1600    // In the simplified case, it would resemble something like this:
1601    //
1602    //   --check-cfg=cfg() --check-cfg=cfg(feature, values(...))
1603    //
1604    // but having `cfg()` is redundant with the second argument (as well-known names
1605    // and values are implicitly enabled when one or more `--check-cfg` argument is
1606    // passed) so we don't emit it and just pass:
1607    //
1608    //   --check-cfg=cfg(feature, values(...))
1609    //
1610    // This way, even if there are no declared features, the config `feature` will
1611    // still be expected, meaning users would get "unexpected value" instead of name.
1612    // This wasn't always the case, see rust-lang#119930 for some details.
1613
1614    let gross_cap_estimation = unit.pkg.summary().features().len() * 7 + 25;
1615    let mut arg_feature = OsString::with_capacity(gross_cap_estimation);
1616
1617    arg_feature.push("cfg(feature, values(");
1618    for (i, feature) in unit.pkg.summary().features().keys().enumerate() {
1619        if i != 0 {
1620            arg_feature.push(", ");
1621        }
1622        arg_feature.push("\"");
1623        arg_feature.push(feature);
1624        arg_feature.push("\"");
1625    }
1626    arg_feature.push("))");
1627
1628    // In addition to the package features, we also include the `test` cfg (since
1629    // compiler-team#785, as to be able to someday apply it conditionally), as well
1630    // the `docsrs` cfg from the docs.rs service.
1631    //
1632    // We include `docsrs` here (in Cargo) instead of rustc, since there is a much closer
1633    // relationship between Cargo and docs.rs than rustc and docs.rs. In particular, all
1634    // users of docs.rs use Cargo, but not all users of rustc (like Rust-for-Linux) use docs.rs.
1635
1636    vec![
1637        OsString::from("--check-cfg"),
1638        OsString::from("cfg(docsrs,test)"),
1639        OsString::from("--check-cfg"),
1640        arg_feature,
1641    ]
1642}
1643
1644/// Adds LTO related codegen flags.
1645fn lto_args(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> Vec<OsString> {
1646    let mut result = Vec::new();
1647    let mut push = |arg: &str| {
1648        result.push(OsString::from("-C"));
1649        result.push(OsString::from(arg));
1650    };
1651    match build_runner.lto[unit] {
1652        lto::Lto::Run(None) => push("lto"),
1653        lto::Lto::Run(Some(s)) => push(&format!("lto={}", s)),
1654        lto::Lto::Off => {
1655            push("lto=off");
1656            push("embed-bitcode=no");
1657        }
1658        lto::Lto::ObjectAndBitcode => {} // this is rustc's default
1659        lto::Lto::OnlyBitcode => push("linker-plugin-lto"),
1660        lto::Lto::OnlyObject => push("embed-bitcode=no"),
1661    }
1662    result
1663}
1664
1665/// Adds dependency-relevant rustc flags and environment variables
1666/// to the command to execute, such as [`-L`] and [`--extern`].
1667///
1668/// [`-L`]: https://doc.rust-lang.org/nightly/rustc/command-line-arguments.html#-l-add-a-directory-to-the-library-search-path
1669/// [`--extern`]: https://doc.rust-lang.org/nightly/rustc/command-line-arguments.html#--extern-specify-where-an-external-library-is-located
1670fn build_deps_args(
1671    cmd: &mut ProcessBuilder,
1672    build_runner: &BuildRunner<'_, '_>,
1673    unit: &Unit,
1674) -> CargoResult<()> {
1675    let bcx = build_runner.bcx;
1676
1677    for arg in lib_search_paths(build_runner, unit)? {
1678        cmd.arg(arg);
1679    }
1680
1681    let deps = build_runner.unit_deps(unit);
1682
1683    // If there is not one linkable target but should, rustc fails later
1684    // on if there is an `extern crate` for it. This may turn into a hard
1685    // error in the future (see PR #4797).
1686    if !deps
1687        .iter()
1688        .any(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_linkable())
1689    {
1690        if let Some(dep) = deps.iter().find(|dep| {
1691            !dep.unit.mode.is_doc() && dep.unit.target.is_lib() && !dep.unit.artifact.is_true()
1692        }) {
1693            let dep_name = dep.unit.target.crate_name();
1694            let name = unit.target.crate_name();
1695            bcx.gctx.shell().print_report(&[
1696                Level::WARNING.secondary_title(format!("the package `{dep_name}` provides no linkable target"))
1697                    .elements([
1698                        Level::NOTE.message(format!("this might cause `{name}` to fail compilation")),
1699                        Level::NOTE.message("this warning might turn into a hard error in the future"),
1700                        Level::HELP.message(format!("consider adding 'dylib' or 'rlib' to key 'crate-type' in `{dep_name}`'s Cargo.toml"))
1701                    ])
1702            ], false)?;
1703        }
1704    }
1705
1706    let mut unstable_opts = false;
1707
1708    // Add `OUT_DIR` environment variables for build scripts
1709    let first_custom_build_dep = deps.iter().find(|dep| dep.unit.mode.is_run_custom_build());
1710    if let Some(dep) = first_custom_build_dep {
1711        let out_dir = if bcx.gctx.cli_unstable().build_dir_new_layout {
1712            build_runner.files().out_dir_new_layout(&dep.unit)
1713        } else {
1714            build_runner.files().build_script_out_dir(&dep.unit)
1715        };
1716        cmd.env("OUT_DIR", &out_dir);
1717    }
1718
1719    // Adding output directory for each build script
1720    let is_multiple_build_scripts_enabled = unit
1721        .pkg
1722        .manifest()
1723        .unstable_features()
1724        .require(Feature::multiple_build_scripts())
1725        .is_ok();
1726
1727    if is_multiple_build_scripts_enabled {
1728        for dep in deps {
1729            if dep.unit.mode.is_run_custom_build() {
1730                let out_dir = if bcx.gctx.cli_unstable().build_dir_new_layout {
1731                    build_runner.files().out_dir_new_layout(&dep.unit)
1732                } else {
1733                    build_runner.files().build_script_out_dir(&dep.unit)
1734                };
1735                let target_name = dep.unit.target.name();
1736                let out_dir_prefix = target_name
1737                    .strip_prefix("build-script-")
1738                    .unwrap_or(target_name);
1739                let out_dir_name = format!("{out_dir_prefix}_OUT_DIR");
1740                cmd.env(&out_dir_name, &out_dir);
1741            }
1742        }
1743    }
1744    for arg in extern_args(build_runner, unit, &mut unstable_opts)? {
1745        cmd.arg(arg);
1746    }
1747
1748    for (var, env) in artifact::get_env(build_runner, unit, deps)? {
1749        cmd.env(&var, env);
1750    }
1751
1752    // This will only be set if we're already using a feature
1753    // requiring nightly rust
1754    if unstable_opts {
1755        cmd.arg("-Z").arg("unstable-options");
1756    }
1757
1758    Ok(())
1759}
1760
1761/// Adds extra rustc flags and environment variables collected from the output
1762/// of a build-script to the command to execute, include custom environment
1763/// variables and `cfg`.
1764fn add_custom_flags(
1765    cmd: &mut ProcessBuilder,
1766    build_script_outputs: &BuildScriptOutputs,
1767    metadata_vec: Option<Vec<UnitHash>>,
1768) -> CargoResult<()> {
1769    if let Some(metadata_vec) = metadata_vec {
1770        for metadata in metadata_vec {
1771            if let Some(output) = build_script_outputs.get(metadata) {
1772                for cfg in output.cfgs.iter() {
1773                    cmd.arg("--cfg").arg(cfg);
1774                }
1775                for check_cfg in &output.check_cfgs {
1776                    cmd.arg("--check-cfg").arg(check_cfg);
1777                }
1778                for (name, value) in output.env.iter() {
1779                    cmd.env(name, value);
1780                }
1781            }
1782        }
1783    }
1784
1785    Ok(())
1786}
1787
1788/// Generate a list of `-L` arguments
1789#[tracing::instrument(skip_all)]
1790pub fn lib_search_paths(
1791    build_runner: &BuildRunner<'_, '_>,
1792    unit: &Unit,
1793) -> CargoResult<Vec<OsString>> {
1794    let mut lib_search_paths = Vec::new();
1795    if build_runner.bcx.gctx.cli_unstable().build_dir_new_layout {
1796        // Add all dependency args to rustc process
1797        let paths = dep_paths_for_args(unit, build_runner);
1798
1799        for path in paths {
1800            let mut deps = OsString::from("dependency=");
1801            deps.push(path);
1802            lib_search_paths.extend(["-L".into(), deps]);
1803        }
1804    } else {
1805        let mut deps = OsString::from("dependency=");
1806        deps.push(build_runner.files().deps_dir(unit));
1807        lib_search_paths.extend(["-L".into(), deps]);
1808    }
1809
1810    // Be sure that the host path is also listed. This'll ensure that proc macro
1811    // dependencies are correctly found (for reexported macros).
1812    if !unit.kind.is_host() {
1813        let mut deps = OsString::from("dependency=");
1814        deps.push(build_runner.files().host_deps(unit));
1815        lib_search_paths.extend(["-L".into(), deps]);
1816    }
1817
1818    Ok(lib_search_paths)
1819}
1820
1821/// Generate a list of paths for the rustc `-L` args.
1822///
1823/// To generate the list of paths we walk the dependency graph using DFS with a stack.
1824///
1825/// We try to avoid bloating the amount of args passed rustc as it can cause issues with OS limits
1826/// for large projects. (and generally just makes the rustc command ugly!).
1827/// Below are the rules for excluding build units from the args.
1828/// 1. Don't include build script out dir in the args to reduce rustc command bloat.
1829/// 2. Proc macros are statically linked, so when including a proc-macro dependency we can skip
1830///    adding it's dependencies. Note that we still do add them when we are compiling the
1831///    proc-macro itself.
1832/// 3. Don't include direct dependencies because they are passed with `--extern` and rustc does not
1833///    need `-L` if it was already passed as `--extern`
1834fn dep_paths_for_args(unit: &Unit, build_runner: &BuildRunner<'_, '_>) -> Vec<PathBuf> {
1835    let direct = build_runner.unit_deps(unit);
1836    if direct.is_empty() {
1837        return Vec::new();
1838    }
1839    let mut indirect = HashSet::default();
1840    let mut visited = HashSet::default();
1841    let mut stack: Vec<&Unit> = direct
1842        .iter()
1843        .filter(|d| !d.unit.target.is_custom_build())
1844        .map(|d| &d.unit)
1845        .collect();
1846    while let Some(u) = stack.pop() {
1847        if !visited.insert(u.clone()) {
1848            continue;
1849        }
1850        if u.target.proc_macro() {
1851            continue;
1852        }
1853        for dep in build_runner.unit_deps(u) {
1854            let v = &dep.unit;
1855            if v.target.is_custom_build() {
1856                continue;
1857            }
1858            indirect.insert(v.clone());
1859            if v.target.proc_macro() {
1860                continue;
1861            }
1862            if !visited.contains(v) {
1863                stack.push(v);
1864            }
1865        }
1866    }
1867
1868    let mut paths: Vec<PathBuf> = indirect
1869        .into_iter()
1870        .map(|u| build_runner.files().deps_dir(&u))
1871        .collect();
1872
1873    // We are in the hot path that needs to always run (even for rebuilds)
1874    // There could be a potentially large amount of paths for large workspaces.
1875    // We want to sort and dedup the paths but doing so is a bit expsensive when comparing PathBufs
1876    // directly. So instead we only sort valid Utf8 paths as &str's which is a bit faster.
1877    // We do not allow non utf8 paths when building, so we take advantage of that by treating them
1878    // as a single value when sorting/deduplicating.
1879    //
1880    // For reference this saves about ~30ms on Zed
1881    paths.sort_unstable_by(|a, b| match (a.to_str(), b.to_str()) {
1882        (Some(a), Some(b)) => a.cmp(b),
1883        (Some(_), None) => std::cmp::Ordering::Less,
1884        (None, Some(_)) => std::cmp::Ordering::Greater,
1885        (None, None) => std::cmp::Ordering::Less,
1886    });
1887    paths.dedup_by(|a, b| matches!((a.to_str(), b.to_str()), (Some(x), Some(y)) if x == y));
1888
1889    paths
1890}
1891
1892fn is_public_dependency_enabled(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> bool {
1893    unit.pkg
1894        .manifest()
1895        .unstable_features()
1896        .require(Feature::public_dependency())
1897        .is_ok()
1898        || build_runner.bcx.gctx.cli_unstable().public_dependency
1899}
1900
1901/// Generates a list of `--extern` arguments.
1902pub fn extern_args(
1903    build_runner: &BuildRunner<'_, '_>,
1904    unit: &Unit,
1905    unstable_opts: &mut bool,
1906) -> CargoResult<Vec<OsString>> {
1907    let mut result = Vec::new();
1908    let deps = build_runner.unit_deps(unit);
1909
1910    let no_embed_metadata = !build_runner
1911        .bcx
1912        .target_data
1913        .info(unit.kind)
1914        .should_embed_metadata();
1915    let public_dependency_enabled = is_public_dependency_enabled(build_runner, unit);
1916
1917    // Closure to add one dependency to `result`.
1918    let mut link_to = |dep: &UnitDep,
1919                       extern_crate_name: InternedString,
1920                       noprelude: bool,
1921                       nounused: bool|
1922     -> CargoResult<()> {
1923        let mut value = OsString::new();
1924        let mut opts = Vec::new();
1925        if !dep.public && unit.target.is_lib() && public_dependency_enabled {
1926            opts.push("priv");
1927            *unstable_opts = true;
1928        }
1929        if noprelude {
1930            opts.push("noprelude");
1931            *unstable_opts = true;
1932        }
1933        if nounused {
1934            opts.push("nounused");
1935            *unstable_opts = true;
1936        }
1937        if !opts.is_empty() {
1938            value.push(opts.join(","));
1939            value.push(":");
1940        }
1941        value.push(extern_crate_name.as_str());
1942        value.push("=");
1943
1944        let mut pass = |file| {
1945            let mut value = value.clone();
1946            value.push(file);
1947            result.push(OsString::from("--extern"));
1948            result.push(value);
1949        };
1950
1951        let outputs = build_runner.outputs(&dep.unit)?;
1952
1953        if build_runner.only_requires_rmeta(unit, &dep.unit) || dep.unit.mode.is_check() {
1954            // Example: rlib dependency for an rlib, rmeta is all that is required.
1955            let output = outputs
1956                .iter()
1957                .find(|output| output.flavor == FileFlavor::Rmeta)
1958                .expect("failed to find rmeta dep for pipelined dep");
1959            pass(&output.path);
1960        } else {
1961            // Example: a bin needs `rlib` for dependencies, it cannot use rmeta.
1962            for output in outputs.iter() {
1963                if output.flavor == FileFlavor::Linkable {
1964                    pass(&output.path);
1965                }
1966                // If we use -Zembed-metadata=no, we also need to pass the path to the
1967                // corresponding .rmeta file to the linkable artifact, because the
1968                // normal dependency (rlib) doesn't contain the full metadata.
1969                else if no_embed_metadata && output.flavor == FileFlavor::Rmeta {
1970                    pass(&output.path);
1971                }
1972            }
1973        }
1974        Ok(())
1975    };
1976
1977    for dep in deps {
1978        if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
1979            link_to(dep, dep.extern_crate_name, dep.noprelude, dep.nounused)?;
1980        }
1981    }
1982    if unit.target.proc_macro() {
1983        // Automatically import `proc_macro`.
1984        result.push(OsString::from("--extern"));
1985        result.push(OsString::from("proc_macro"));
1986    }
1987
1988    Ok(result)
1989}
1990
1991/// Adds `-C linker=<path>` if specified.
1992fn add_codegen_linker(
1993    cmd: &mut ProcessBuilder,
1994    build_runner: &BuildRunner<'_, '_>,
1995    unit: &Unit,
1996    target_applies_to_host: bool,
1997) {
1998    let linker = if unit.target.for_host() && !target_applies_to_host {
1999        build_runner
2000            .compilation
2001            .host_linker()
2002            .map(|s| s.as_os_str())
2003    } else {
2004        build_runner
2005            .compilation
2006            .target_linker(unit.kind)
2007            .map(|s| s.as_os_str())
2008    };
2009
2010    if let Some(linker) = linker {
2011        let mut arg = OsString::from("linker=");
2012        arg.push(linker);
2013        cmd.arg("-C").arg(arg);
2014    }
2015}
2016
2017/// Adds `-C incremental=<path>`.
2018fn add_codegen_incremental(
2019    cmd: &mut ProcessBuilder,
2020    build_runner: &BuildRunner<'_, '_>,
2021    unit: &Unit,
2022) {
2023    let dir = build_runner.files().incremental_dir(&unit);
2024    let mut arg = OsString::from("incremental=");
2025    arg.push(dir.as_os_str());
2026    cmd.arg("-C").arg(arg);
2027}
2028
2029fn envify(s: &str) -> String {
2030    s.chars()
2031        .flat_map(|c| c.to_uppercase())
2032        .map(|c| if c == '-' { '_' } else { c })
2033        .collect()
2034}
2035
2036/// Configuration of the display of messages emitted by the compiler,
2037/// e.g. diagnostics, warnings, errors, and message caching.
2038struct OutputOptions {
2039    /// What format we're emitting from Cargo itself.
2040    format: MessageFormat,
2041    /// Where to write the JSON messages to support playback later if the unit
2042    /// is fresh. The file is created lazily so that in the normal case, lots
2043    /// of empty files are not created. If this is None, the output will not
2044    /// be cached (such as when replaying cached messages).
2045    cache_cell: Option<(PathBuf, OnceCell<File>)>,
2046    /// If `true`, display any diagnostics.
2047    /// Other types of JSON messages are processed regardless
2048    /// of the value of this flag.
2049    ///
2050    /// This is used primarily for cache replay. If you build with `-vv`, the
2051    /// cache will be filled with diagnostics from dependencies. When the
2052    /// cache is replayed without `-vv`, we don't want to show them.
2053    show_diagnostics: bool,
2054    /// Tracks the number of warnings we've seen so far.
2055    warnings_seen: usize,
2056    /// Tracks the number of errors we've seen so far.
2057    errors_seen: usize,
2058}
2059
2060impl OutputOptions {
2061    fn for_dirty(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions {
2062        let path = build_runner.files().message_cache_path(unit);
2063        // Remove old cache, ignore ENOENT, which is the common case.
2064        drop(fs::remove_file(&path));
2065        let cache_cell = Some((path, OnceCell::new()));
2066
2067        let show_diagnostics = true;
2068
2069        let format = build_runner.bcx.build_config.message_format;
2070
2071        OutputOptions {
2072            format,
2073            cache_cell,
2074            show_diagnostics,
2075            warnings_seen: 0,
2076            errors_seen: 0,
2077        }
2078    }
2079
2080    fn for_fresh(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions {
2081        let cache_cell = None;
2082
2083        // We always replay the output cache,
2084        // since it might contain future-incompat-report messages
2085        let show_diagnostics = unit.show_warnings(build_runner.bcx.gctx);
2086
2087        let format = build_runner.bcx.build_config.message_format;
2088
2089        OutputOptions {
2090            format,
2091            cache_cell,
2092            show_diagnostics,
2093            warnings_seen: 0,
2094            errors_seen: 0,
2095        }
2096    }
2097}
2098
2099/// Cloned and sendable context about the manifest file.
2100///
2101/// Sometimes we enrich rustc's errors with some locations in the manifest file; this
2102/// contains a `Send`-able copy of the manifest information that we need for the
2103/// enriched errors.
2104struct ManifestErrorContext {
2105    /// The path to the manifest.
2106    path: PathBuf,
2107    /// The locations of various spans within the manifest.
2108    spans: Option<Arc<toml::Spanned<toml::de::DeTable<'static>>>>,
2109    /// The raw manifest contents.
2110    contents: Option<String>,
2111    /// A lookup for all the unambiguous renamings, mapping from the original package
2112    /// name to the renamed one.
2113    rename_table: HashMap<InternedString, InternedString>,
2114    /// A list of targets we're compiling for, to determine which of the `[target.<something>.dependencies]`
2115    /// tables might be of interest.
2116    requested_kinds: Vec<CompileKind>,
2117    /// A list of all the collections of cfg values, one collection for each target, to determine
2118    /// which of the `[target.'cfg(...)'.dependencies]` tables might be of interest.
2119    cfgs: Vec<Vec<Cfg>>,
2120    host_name: InternedString,
2121    /// Cargo's working directory (for printing out a more friendly manifest path).
2122    cwd: PathBuf,
2123    /// Terminal width for formatting diagnostics.
2124    term_width: usize,
2125}
2126
2127fn on_stdout_line(
2128    state: &JobState<'_, '_>,
2129    line: &str,
2130    _package_id: PackageId,
2131    _target: &Target,
2132) -> CargoResult<()> {
2133    state.stdout(line.to_string())?;
2134    Ok(())
2135}
2136
2137fn on_stderr_line(
2138    state: &JobState<'_, '_>,
2139    line: &str,
2140    package_id: PackageId,
2141    manifest: &ManifestErrorContext,
2142    target: &Target,
2143    options: &mut OutputOptions,
2144) -> CargoResult<()> {
2145    if on_stderr_line_inner(state, line, package_id, manifest, target, options)? {
2146        // Check if caching is enabled.
2147        if let Some((path, cell)) = &mut options.cache_cell {
2148            // Cache the output, which will be replayed later when Fresh.
2149            let f = cell.try_borrow_mut_with(|| paths::create(path))?;
2150            debug_assert!(!line.contains('\n'));
2151            f.write_all(line.as_bytes())?;
2152            f.write_all(&[b'\n'])?;
2153        }
2154    }
2155    Ok(())
2156}
2157
2158/// Returns true if the line should be cached.
2159fn on_stderr_line_inner(
2160    state: &JobState<'_, '_>,
2161    line: &str,
2162    package_id: PackageId,
2163    manifest: &ManifestErrorContext,
2164    target: &Target,
2165    options: &mut OutputOptions,
2166) -> CargoResult<bool> {
2167    // We primarily want to use this function to process JSON messages from
2168    // rustc. The compiler should always print one JSON message per line, and
2169    // otherwise it may have other output intermingled (think RUST_LOG or
2170    // something like that), so skip over everything that doesn't look like a
2171    // JSON message.
2172    if !line.starts_with('{') {
2173        state.stderr(line.to_string())?;
2174        return Ok(true);
2175    }
2176
2177    let mut compiler_message: Box<serde_json::value::RawValue> = match serde_json::from_str(line) {
2178        Ok(msg) => msg,
2179
2180        // If the compiler produced a line that started with `{` but it wasn't
2181        // valid JSON, maybe it wasn't JSON in the first place! Forward it along
2182        // to stderr.
2183        Err(e) => {
2184            debug!("failed to parse json: {:?}", e);
2185            state.stderr(line.to_string())?;
2186            return Ok(true);
2187        }
2188    };
2189
2190    let count_diagnostic = |level, options: &mut OutputOptions| {
2191        if level == "warning" {
2192            options.warnings_seen += 1;
2193        } else if level == "error" {
2194            options.errors_seen += 1;
2195        }
2196    };
2197
2198    if let Ok(report) = serde_json::from_str::<FutureIncompatReport>(compiler_message.get()) {
2199        for item in &report.future_incompat_report {
2200            count_diagnostic(&*item.diagnostic.level, options);
2201        }
2202        state.future_incompat_report(report.future_incompat_report);
2203        return Ok(true);
2204    }
2205
2206    let res = serde_json::from_str::<SectionTiming>(compiler_message.get());
2207    if let Ok(timing_record) = res {
2208        state.on_section_timing_emitted(timing_record);
2209        return Ok(false);
2210    }
2211
2212    // Returns `true` if the diagnostic was modified.
2213    let add_pub_in_priv_diagnostic = |diag: &mut String| -> bool {
2214        // We are parsing the compiler diagnostic here, as this information isn't
2215        // currently exposed elsewhere.
2216        // At the time of writing this comment, rustc emits two different
2217        // "exported_private_dependencies" errors:
2218        //  - type `FromPriv` from private dependency 'priv_dep' in public interface
2219        //  - struct `FromPriv` from private dependency 'priv_dep' is re-exported
2220        // This regex matches them both. To see if it needs to be updated, grep the rust
2221        // source for "EXPORTED_PRIVATE_DEPENDENCIES".
2222        static PRIV_DEP_REGEX: LazyLock<Regex> =
2223            LazyLock::new(|| Regex::new("from private dependency '([A-Za-z0-9-_]+)'").unwrap());
2224        if let Some(crate_name) = PRIV_DEP_REGEX.captures(diag).and_then(|m| m.get(1))
2225            && let Some(ref contents) = manifest.contents
2226            && let Some(span) = manifest.find_crate_span(crate_name.as_str())
2227        {
2228            let rel_path = pathdiff::diff_paths(&manifest.path, &manifest.cwd)
2229                .unwrap_or_else(|| manifest.path.clone())
2230                .display()
2231                .to_string();
2232            let report = [Group::with_title(Level::NOTE.secondary_title(format!(
2233                "dependency `{}` declared here",
2234                crate_name.as_str()
2235            )))
2236            .element(
2237                Snippet::source(contents)
2238                    .path(rel_path)
2239                    .annotation(AnnotationKind::Context.span(span)),
2240            )];
2241
2242            let rendered = Renderer::styled()
2243                .term_width(manifest.term_width)
2244                .render(&report);
2245            diag.push_str(&rendered);
2246            diag.push('\n');
2247            return true;
2248        }
2249        false
2250    };
2251
2252    // Depending on what we're emitting from Cargo itself, we figure out what to
2253    // do with this JSON message.
2254    match options.format {
2255        // In the "human" output formats (human/short) or if diagnostic messages
2256        // from rustc aren't being included in the output of Cargo's JSON
2257        // messages then we extract the diagnostic (if present) here and handle
2258        // it ourselves.
2259        MessageFormat::Human
2260        | MessageFormat::Short
2261        | MessageFormat::Json {
2262            render_diagnostics: true,
2263            ..
2264        } => {
2265            #[derive(serde::Deserialize)]
2266            struct CompilerMessage<'a> {
2267                // `rendered` contains escape sequences, which can't be
2268                // zero-copy deserialized by serde_json.
2269                // See https://github.com/serde-rs/json/issues/742
2270                rendered: String,
2271                #[serde(borrow)]
2272                message: Cow<'a, str>,
2273                #[serde(borrow)]
2274                level: Cow<'a, str>,
2275                children: Vec<PartialDiagnostic>,
2276                code: Option<DiagnosticCode>,
2277            }
2278
2279            // A partial rustfix::diagnostics::Diagnostic. We deserialize only a
2280            // subset of the fields because rustc's output can be extremely
2281            // deeply nested JSON in pathological cases involving macro
2282            // expansion. Rustfix's Diagnostic struct is recursive containing a
2283            // field `children: Vec<Self>`, and it can cause deserialization to
2284            // hit serde_json's default recursion limit, or overflow the stack
2285            // if we turn that off. Cargo only cares about the 1 field listed
2286            // here.
2287            #[derive(serde::Deserialize)]
2288            struct PartialDiagnostic {
2289                spans: Vec<PartialDiagnosticSpan>,
2290            }
2291
2292            // A partial rustfix::diagnostics::DiagnosticSpan.
2293            #[derive(serde::Deserialize)]
2294            struct PartialDiagnosticSpan {
2295                suggestion_applicability: Option<Applicability>,
2296            }
2297
2298            #[derive(serde::Deserialize)]
2299            struct DiagnosticCode {
2300                code: String,
2301            }
2302
2303            if let Ok(mut msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2304            {
2305                if msg.message.starts_with("aborting due to")
2306                    || msg.message.ends_with("warning emitted")
2307                    || msg.message.ends_with("warnings emitted")
2308                {
2309                    // Skip this line; we'll print our own summary at the end.
2310                    return Ok(true);
2311                }
2312                // state.stderr will add a newline
2313                if msg.rendered.ends_with('\n') {
2314                    msg.rendered.pop();
2315                }
2316                let mut rendered = msg.rendered;
2317                if options.show_diagnostics {
2318                    let machine_applicable: bool = msg
2319                        .children
2320                        .iter()
2321                        .map(|child| {
2322                            child
2323                                .spans
2324                                .iter()
2325                                .filter_map(|span| span.suggestion_applicability)
2326                                .any(|app| app == Applicability::MachineApplicable)
2327                        })
2328                        .any(|b| b);
2329                    count_diagnostic(&msg.level, options);
2330                    if msg
2331                        .code
2332                        .as_ref()
2333                        .is_some_and(|c| c.code == "exported_private_dependencies")
2334                        && options.format != MessageFormat::Short
2335                    {
2336                        add_pub_in_priv_diagnostic(&mut rendered);
2337                    }
2338                    let lint = msg.code.is_some();
2339                    state.emit_diag(&msg.level, rendered, lint, machine_applicable)?;
2340                }
2341                return Ok(true);
2342            }
2343        }
2344
2345        MessageFormat::Json { ansi, .. } => {
2346            #[derive(serde::Deserialize, serde::Serialize)]
2347            struct CompilerMessage<'a> {
2348                rendered: String,
2349                #[serde(flatten, borrow)]
2350                other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2351                code: Option<DiagnosticCode<'a>>,
2352            }
2353
2354            #[derive(serde::Deserialize, serde::Serialize)]
2355            struct DiagnosticCode<'a> {
2356                code: String,
2357                #[serde(flatten, borrow)]
2358                other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2359            }
2360
2361            if let Ok(mut error) =
2362                serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2363            {
2364                let modified_diag = if error
2365                    .code
2366                    .as_ref()
2367                    .is_some_and(|c| c.code == "exported_private_dependencies")
2368                {
2369                    add_pub_in_priv_diagnostic(&mut error.rendered)
2370                } else {
2371                    false
2372                };
2373
2374                // Remove color information from the rendered string if color is not
2375                // enabled. Cargo always asks for ANSI colors from rustc. This allows
2376                // cached replay to enable/disable colors without re-invoking rustc.
2377                if !ansi {
2378                    error.rendered = anstream::adapter::strip_str(&error.rendered).to_string();
2379                }
2380                if !ansi || modified_diag {
2381                    let new_line = serde_json::to_string(&error)?;
2382                    compiler_message = serde_json::value::RawValue::from_string(new_line)?;
2383                }
2384            }
2385        }
2386    }
2387
2388    // We always tell rustc to emit messages about artifacts being produced.
2389    // These messages feed into pipelined compilation, as well as timing
2390    // information.
2391    //
2392    // Look for a matching directive and inform Cargo internally that a
2393    // metadata file has been produced.
2394    #[derive(serde::Deserialize)]
2395    struct ArtifactNotification<'a> {
2396        #[serde(borrow)]
2397        artifact: Cow<'a, str>,
2398    }
2399
2400    if let Ok(artifact) = serde_json::from_str::<ArtifactNotification<'_>>(compiler_message.get()) {
2401        trace!("found directive from rustc: `{}`", artifact.artifact);
2402        if artifact.artifact.ends_with(".rmeta") {
2403            debug!("looks like metadata finished early!");
2404            state.rmeta_produced();
2405        }
2406        return Ok(false);
2407    }
2408
2409    #[derive(serde::Deserialize)]
2410    struct UnusedExterns {
2411        unused_extern_names: std::collections::BTreeSet<InternedString>,
2412    }
2413    if let Ok(uext) = serde_json::from_str::<UnusedExterns>(compiler_message.get()) {
2414        trace!(
2415            "obtained unused externs list from rustc: `{:?}`",
2416            uext.unused_extern_names
2417        );
2418        state.unused_externs(uext.unused_extern_names);
2419        return Ok(true);
2420    }
2421
2422    // And failing all that above we should have a legitimate JSON diagnostic
2423    // from the compiler, so wrap it in an external Cargo JSON message
2424    // indicating which package it came from and then emit it.
2425
2426    if !options.show_diagnostics {
2427        return Ok(true);
2428    }
2429
2430    #[derive(serde::Deserialize)]
2431    struct CompilerMessage<'a> {
2432        #[serde(borrow)]
2433        message: Cow<'a, str>,
2434        #[serde(borrow)]
2435        level: Cow<'a, str>,
2436    }
2437
2438    if let Ok(msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get()) {
2439        if msg.message.starts_with("aborting due to")
2440            || msg.message.ends_with("warning emitted")
2441            || msg.message.ends_with("warnings emitted")
2442        {
2443            // Skip this line; we'll print our own summary at the end.
2444            return Ok(true);
2445        }
2446        count_diagnostic(&msg.level, options);
2447    }
2448
2449    let msg = machine_message::FromCompiler {
2450        package_id: package_id.to_spec(),
2451        manifest_path: &manifest.path,
2452        target,
2453        message: compiler_message,
2454    }
2455    .to_json_string();
2456
2457    // Switch json lines from rustc/rustdoc that appear on stderr to stdout
2458    // instead. We want the stdout of Cargo to always be machine parseable as
2459    // stderr has our colorized human-readable messages.
2460    state.stdout(msg)?;
2461    Ok(true)
2462}
2463
2464impl ManifestErrorContext {
2465    fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> ManifestErrorContext {
2466        let mut duplicates = HashSet::default();
2467        let mut rename_table = HashMap::default();
2468
2469        for dep in build_runner.unit_deps(unit) {
2470            let unrenamed_id = dep.unit.pkg.package_id().name();
2471            if duplicates.contains(&unrenamed_id) {
2472                continue;
2473            }
2474            match rename_table.entry(unrenamed_id) {
2475                std::collections::hash_map::Entry::Occupied(occ) => {
2476                    occ.remove_entry();
2477                    duplicates.insert(unrenamed_id);
2478                }
2479                std::collections::hash_map::Entry::Vacant(vac) => {
2480                    vac.insert(dep.extern_crate_name);
2481                }
2482            }
2483        }
2484
2485        let bcx = build_runner.bcx;
2486        ManifestErrorContext {
2487            path: unit.pkg.manifest_path().to_owned(),
2488            spans: unit.pkg.manifest().document_rc(),
2489            contents: unit.pkg.manifest().contents().map(String::from),
2490            requested_kinds: bcx.target_data.requested_kinds().to_owned(),
2491            host_name: bcx.rustc().host,
2492            rename_table,
2493            cwd: path_args(build_runner.bcx.ws, unit).1,
2494            cfgs: bcx
2495                .target_data
2496                .requested_kinds()
2497                .iter()
2498                .map(|k| bcx.target_data.cfg(*k).to_owned())
2499                .collect(),
2500            term_width: bcx
2501                .gctx
2502                .shell()
2503                .err_width()
2504                .diagnostic_terminal_width()
2505                .unwrap_or(cargo_util_terminal::report::renderer::DEFAULT_TERM_WIDTH),
2506        }
2507    }
2508
2509    fn requested_target_names(&self) -> impl Iterator<Item = &str> {
2510        self.requested_kinds.iter().map(|kind| match kind {
2511            CompileKind::Host => &self.host_name,
2512            CompileKind::Target(target) => target.short_name(),
2513        })
2514    }
2515
2516    /// Find a span for the dependency that specifies this unrenamed crate, if it's unique.
2517    ///
2518    /// rustc diagnostics (at least for public-in-private) mention the un-renamed
2519    /// crate: if you have `foo = { package = "bar" }`, the rustc diagnostic will
2520    /// say "bar".
2521    ///
2522    /// This function does its best to find a span for "bar", but it could fail if
2523    /// there are multiple candidates:
2524    ///
2525    /// ```toml
2526    /// foo = { package = "bar" }
2527    /// baz = { path = "../bar", package = "bar" }
2528    /// ```
2529    fn find_crate_span(&self, unrenamed: &str) -> Option<Range<usize>> {
2530        let Some(ref spans) = self.spans else {
2531            return None;
2532        };
2533
2534        let orig_name = self.rename_table.get(unrenamed)?.as_str();
2535
2536        if let Some((k, v)) = get_key_value(&spans, &["dependencies", orig_name]) {
2537            // We make some effort to find the unrenamed text: in
2538            //
2539            // ```
2540            // foo = { package = "bar" }
2541            // ```
2542            //
2543            // we try to find the "bar", but fall back to "foo" if we can't (which might
2544            // happen if the renaming took place in the workspace, for example).
2545            if let Some(package) = v.get_ref().as_table().and_then(|t| t.get("package")) {
2546                return Some(package.span());
2547            } else {
2548                return Some(k.span());
2549            }
2550        }
2551
2552        // The dependency could also be in a target-specific table, like
2553        // [target.x86_64-unknown-linux-gnu.dependencies] or
2554        // [target.'cfg(something)'.dependencies]. We filter out target tables
2555        // that don't match a requested target or a requested cfg.
2556        if let Some(target) = spans
2557            .deref()
2558            .as_ref()
2559            .get("target")
2560            .and_then(|t| t.as_ref().as_table())
2561        {
2562            for (platform, platform_table) in target.iter() {
2563                match platform.as_ref().parse::<Platform>() {
2564                    Ok(Platform::Name(name)) => {
2565                        if !self.requested_target_names().any(|n| n == name) {
2566                            continue;
2567                        }
2568                    }
2569                    Ok(Platform::Cfg(cfg_expr)) => {
2570                        if !self.cfgs.iter().any(|cfgs| cfg_expr.matches(cfgs)) {
2571                            continue;
2572                        }
2573                    }
2574                    Err(_) => continue,
2575                }
2576
2577                let Some(platform_table) = platform_table.as_ref().as_table() else {
2578                    continue;
2579                };
2580
2581                if let Some(deps) = platform_table
2582                    .get("dependencies")
2583                    .and_then(|d| d.as_ref().as_table())
2584                {
2585                    if let Some((k, v)) = deps.get_key_value(orig_name) {
2586                        if let Some(package) = v.get_ref().as_table().and_then(|t| t.get("package"))
2587                        {
2588                            return Some(package.span());
2589                        } else {
2590                            return Some(k.span());
2591                        }
2592                    }
2593                }
2594            }
2595        }
2596        None
2597    }
2598}
2599
2600/// Creates a unit of work that replays the cached compiler message.
2601///
2602/// Usually used when a job is fresh and doesn't need to recompile.
2603fn replay_output_cache(
2604    package_id: PackageId,
2605    manifest: ManifestErrorContext,
2606    target: &Target,
2607    path: PathBuf,
2608    mut output_options: OutputOptions,
2609) -> Work {
2610    let target = target.clone();
2611    Work::new(move |state| {
2612        if !path.exists() {
2613            // No cached output, probably didn't emit anything.
2614            return Ok(());
2615        }
2616        // We sometimes have gigabytes of output from the compiler, so avoid
2617        // loading it all into memory at once, as that can cause OOM where
2618        // otherwise there would be none.
2619        let file = paths::open(&path)?;
2620        let mut reader = std::io::BufReader::new(file);
2621        let mut line = String::new();
2622        loop {
2623            let length = reader.read_line(&mut line)?;
2624            if length == 0 {
2625                break;
2626            }
2627            let trimmed = line.trim_end_matches(&['\n', '\r'][..]);
2628            on_stderr_line(
2629                state,
2630                trimmed,
2631                package_id,
2632                &manifest,
2633                &target,
2634                &mut output_options,
2635            )?;
2636            line.clear();
2637        }
2638        Ok(())
2639    })
2640}
2641
2642/// Provides a package name with descriptive target information,
2643/// e.g., '`foo` (bin "bar" test)', '`foo` (lib doctest)'.
2644fn descriptive_pkg_name(name: &str, target: &Target, mode: &CompileMode) -> String {
2645    let desc_name = target.description_named();
2646    let mode = if mode.is_rustc_test() && !(target.is_test() || target.is_bench()) {
2647        " test"
2648    } else if mode.is_doc_test() {
2649        " doctest"
2650    } else if mode.is_doc() {
2651        " doc"
2652    } else {
2653        ""
2654    };
2655    format!("`{name}` ({desc_name}{mode})")
2656}
2657
2658/// Applies environment variables from config `[env]` to [`ProcessBuilder`].
2659pub(crate) fn apply_env_config(
2660    gctx: &crate::GlobalContext,
2661    cmd: &mut ProcessBuilder,
2662) -> CargoResult<()> {
2663    for (key, value) in gctx.env_config()?.iter() {
2664        // never override a value that has already been set by cargo
2665        if cmd.get_envs().contains_key(key) {
2666            continue;
2667        }
2668        cmd.env(key, value);
2669    }
2670    Ok(())
2671}
2672
2673/// Checks if there are some scrape units waiting to be processed.
2674fn should_include_scrape_units(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
2675    unit.mode.is_doc() && bcx.scrape_units.len() > 0 && bcx.ws.unit_needs_doc_scrape(unit)
2676}
2677
2678/// Gets the file path of function call information output from `rustdoc`.
2679fn scrape_output_path(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<PathBuf> {
2680    assert!(unit.mode.is_doc() || unit.mode.is_doc_scrape());
2681    build_runner
2682        .outputs(unit)
2683        .map(|outputs| outputs[0].path.clone())
2684}
2685
2686/// Gets the dep-info file emitted by rustdoc.
2687fn rustdoc_dep_info_loc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> PathBuf {
2688    let mut loc = build_runner.files().fingerprint_file_path(unit, "");
2689    loc.set_extension("d");
2690    loc
2691}