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