1pub mod artifact;
32mod build_config;
33pub(crate) mod build_context;
34pub(crate) mod build_runner;
35mod compilation;
36mod compile_kind;
37mod crate_type;
38mod custom_build;
39pub(crate) mod fingerprint;
40pub mod future_incompat;
41pub(crate) mod job_queue;
42pub(crate) mod layout;
43mod links;
44mod locking;
45mod lto;
46mod output_depinfo;
47mod output_sbom;
48pub mod rustdoc;
49pub mod standard_lib;
50pub mod timings;
51pub(crate) mod trim_paths;
52mod unit;
53pub mod unit_dependencies;
54pub mod unit_graph;
55pub mod unused_deps;
56
57use crate::util::data_structures::{HashMap, HashSet};
58use std::borrow::Cow;
59use std::cell::OnceCell;
60use std::env;
61use std::ffi::{OsStr, OsString};
62use std::fmt::Display;
63use std::fs::{self, File};
64use std::io::{BufRead, BufWriter, Write};
65use std::ops::{Deref, Range};
66use std::path::{Path, PathBuf};
67use std::sync::{Arc, LazyLock};
68
69use anyhow::{Context as _, Error};
70use cargo_platform::{Cfg, Platform};
71use cargo_util_terminal::report::{AnnotationKind, Group, Level, Renderer, Snippet};
72use regex::Regex;
73use tracing::{debug, instrument, trace};
74
75pub use self::build_config::UserIntent;
76pub use self::build_config::{BuildConfig, CompileMode, MessageFormat};
77pub use self::build_context::BuildContext;
78pub use self::build_context::DepKindSet;
79pub use self::build_context::FileFlavor;
80pub use self::build_context::FileType;
81pub use self::build_context::RustcTargetData;
82pub use self::build_context::TargetInfo;
83pub use self::build_runner::{BuildRunner, Metadata, UnitHash};
84pub use self::compilation::{Compilation, Doctest, UnitOutput};
85pub use self::compile_kind::{CompileKind, CompileKindFallback, CompileTarget};
86pub use self::crate_type::CrateType;
87pub use self::custom_build::LinkArgTarget;
88pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts, LibraryPath};
89pub(crate) use self::fingerprint::DirtyReason;
90pub use self::fingerprint::RustdocFingerprint;
91pub use self::job_queue::Freshness;
92use self::job_queue::{Job, JobQueue, JobState, Work};
93pub(crate) use self::layout::Layout;
94pub use self::lto::Lto;
95use self::output_depinfo::output_depinfo;
96use self::output_sbom::build_sbom;
97use self::trim_paths::trim_paths_args;
98use self::trim_paths::trim_paths_args_rustdoc;
99use self::unit_graph::UnitDep;
100
101use crate::compiler::future_incompat::FutureIncompatReport;
102use crate::compiler::locking::LockKey;
103use crate::compiler::timings::SectionTiming;
104pub use crate::compiler::unit::Unit;
105pub use crate::compiler::unit::UnitIndex;
106pub use crate::compiler::unit::UnitInterner;
107use crate::context::FingerprintMethod;
108use crate::diagnostics::get_key_value;
109use crate::util::OnceExt;
110use crate::util::errors::{CargoResult, VerboseError};
111use crate::util::interning::InternedString;
112use crate::util::machine_message::{self, Message};
113use crate::util::{add_path_args, internal, path_args};
114use crate::workspace::manifest::TargetSourcePath;
115use crate::workspace::profiles::{PanicStrategy, Profile, StripInner};
116use crate::workspace::{Feature, PackageId, Target};
117
118use cargo_util::{ExecStats, ProcessBuilder, ProcessError, paths};
119use cargo_util_schemas::manifest::TomlDebugInfo;
120use cargo_util_terminal::Verbosity;
121use rustfix::diagnostics::Applicability;
122
123const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version";
124
125pub trait Executor: Send + Sync + 'static {
129 fn init(&self, _build_runner: &BuildRunner<'_, '_>, _unit: &Unit) {}
133
134 fn exec(
140 &self,
141 cmd: &ProcessBuilder,
142 id: PackageId,
143 target: &Target,
144 mode: CompileMode,
145 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
146 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
147 capture_rusage: bool,
148 ) -> (ExecStats, CargoResult<()>);
149
150 fn force_rebuild(&self, _unit: &Unit) -> bool {
153 false
154 }
155}
156
157#[derive(Copy, Clone)]
160pub struct DefaultExecutor;
161
162impl Executor for DefaultExecutor {
163 #[instrument(name = "rustc", skip_all, fields(package = id.name().as_str(), process = cmd.to_string()))]
164 fn exec(
165 &self,
166 cmd: &ProcessBuilder,
167 id: PackageId,
168 _target: &Target,
169 _mode: CompileMode,
170 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
171 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
172 capture_rusage: bool,
173 ) -> (ExecStats, CargoResult<()>) {
174 let (stats, result) =
175 cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false, capture_rusage);
176 (stats, result.map(drop))
177 }
178}
179
180#[tracing::instrument(skip(build_runner, jobs, exec))]
190fn compile<'gctx>(
191 build_runner: &mut BuildRunner<'_, 'gctx>,
192 jobs: &mut JobQueue<'gctx>,
193 unit: &Unit,
194 exec: &Arc<dyn Executor>,
195 force_rebuild: bool,
196) -> CargoResult<()> {
197 if !build_runner.compiled.insert(unit.clone()) {
198 return Ok(());
199 }
200
201 let lock = if build_runner.bcx.gctx.cli_unstable().fine_grain_locking {
202 Some(build_runner.lock_manager.lock_shared(build_runner, unit)?)
203 } else {
204 None
205 };
206
207 match build_runner
208 .bcx
209 .gctx
210 .build_config()?
211 .fingerprint
212 .unwrap_or_default()
213 {
214 FingerprintMethod::Content => {
215 if !build_runner.bcx.gctx.cli_unstable().checksum_freshness {
216 build_runner.bcx.gctx.shell().warn(
217 r#"ignoring `build.fingerprint = "content"` without `-Zchecksum-freshness`"#,
218 )?;
219 }
220 }
221 FingerprintMethod::Mtime => {}
222 }
223
224 if !unit.skip_non_compile_time_dep {
228 fingerprint::prepare_init(build_runner, unit)?;
231
232 let job = if unit.mode.is_run_custom_build() {
233 custom_build::prepare(build_runner, unit)?
234 } else if unit.mode.is_doc_test() {
235 Job::new_fresh()
237 } else {
238 let force = exec.force_rebuild(unit) || force_rebuild;
239 let mut job = fingerprint::prepare_target(build_runner, unit, force)?;
240 job.before(if job.freshness().is_dirty() {
241 let work = if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
242 rustdoc(build_runner, unit)?
243 } else {
244 rustc(build_runner, unit, exec)?
245 };
246 work.then(link_targets(build_runner, unit, false)?)
247 } else {
248 let output_options = OutputOptions::for_fresh(build_runner, unit);
249 let manifest = ManifestErrorContext::new(build_runner, unit);
250 let work = replay_output_cache(
251 unit.pkg.package_id(),
252 manifest,
253 &unit.target,
254 build_runner.files().message_cache_path(unit),
255 output_options,
256 );
257 work.then(link_targets(build_runner, unit, true)?)
259 });
260
261 if build_runner.bcx.gctx.cli_unstable().fine_grain_locking && job.freshness().is_dirty()
264 {
265 if let Some(lock) = lock {
266 build_runner.lock_manager.unlock(&lock)?;
273 job.before(prebuild_lock_exclusive(lock.clone()));
274 job.after(downgrade_lock_to_shared(lock));
275 }
276 }
277
278 job
279 };
280 jobs.enqueue(build_runner, unit, job)?;
281 }
282
283 let deps = Vec::from(build_runner.unit_deps(unit)); for dep in deps {
286 compile(build_runner, jobs, &dep.unit, exec, false)?;
287 }
288
289 Ok(())
290}
291
292fn make_failed_scrape_diagnostic(
295 build_runner: &BuildRunner<'_, '_>,
296 unit: &Unit,
297 top_line: impl Display,
298) -> String {
299 let manifest_path = unit.pkg.manifest_path();
300 let relative_manifest_path = manifest_path
301 .strip_prefix(build_runner.bcx.ws.root())
302 .unwrap_or(&manifest_path);
303
304 format!(
305 "\
306{top_line}
307 Try running with `--verbose` to see the error message.
308 If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in {}",
309 relative_manifest_path.display()
310 )
311}
312
313#[tracing::instrument(skip_all)]
315fn rustc(
316 build_runner: &mut BuildRunner<'_, '_>,
317 unit: &Unit,
318 exec: &Arc<dyn Executor>,
319) -> CargoResult<Work> {
320 let mut rustc = prepare_rustc(build_runner, unit)?;
321
322 let name = unit.pkg.name();
323
324 let outputs = build_runner.outputs(unit)?;
325 let root = build_runner.files().output_dir(unit);
326
327 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
329 let current_id = unit.pkg.package_id();
330 let manifest = ManifestErrorContext::new(build_runner, unit);
331 let build_scripts = build_runner.build_scripts.get(unit).cloned();
332
333 let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib());
336
337 let dep_info_name =
338 if let Some(c_extra_filename) = build_runner.files().metadata(unit).c_extra_filename() {
339 format!("{}-{}.d", unit.target.crate_name(), c_extra_filename)
340 } else {
341 format!("{}.d", unit.target.crate_name())
342 };
343 let rustc_dep_info_loc = root.join(dep_info_name);
344 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
345
346 let mut output_options = OutputOptions::for_dirty(build_runner, unit);
347 let package_id = unit.pkg.package_id();
348 let target = Target::clone(&unit.target);
349 let mode = unit.mode;
350 let capture_rusage = build_runner.bcx.capture_rusage();
351
352 exec.init(build_runner, unit);
353 let exec = exec.clone();
354
355 let root_output = build_runner.files().host_dest().map(|v| v.to_path_buf());
356 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
357 let pkg_root = unit.pkg.root().to_path_buf();
358 let ws_root = build_runner.bcx.ws.root().to_path_buf();
359 let cwd = rustc
360 .get_cwd()
361 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
362 .to_path_buf();
363 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
364 let script_metadatas = build_runner.find_build_script_metadatas(unit);
365 let is_local = unit.is_local();
366 let artifact = unit.artifact;
367 let sbom_files = build_runner.sbom_output_files(unit)?;
368 let sbom = if !sbom_files.is_empty() {
369 Some(build_sbom(build_runner, unit)?)
370 } else {
371 None
372 };
373
374 let unremap_files = build_runner.unremap_output_files(unit)?;
375 let unremap_content = if unremap_files.is_empty() {
376 None
377 } else {
378 let mut buf = Vec::new();
379 trim_paths::write_unremap_file(&mut buf, build_runner, unit)?;
380 Some(buf)
381 };
382
383 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
384 && !matches!(
385 build_runner.bcx.gctx.shell().verbosity(),
386 Verbosity::Verbose
387 );
388 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
389 let target_desc = unit.target.description_named();
392 let mut for_scrape_units = build_runner
393 .bcx
394 .scrape_units_have_dep_on(unit)
395 .into_iter()
396 .map(|unit| unit.target.description_named())
397 .collect::<Vec<_>>();
398 for_scrape_units.sort();
399 let for_scrape_units = for_scrape_units.join(", ");
400 make_failed_scrape_diagnostic(build_runner, unit, format_args!("failed to check {target_desc} in package `{name}` as a prerequisite for scraping examples from: {for_scrape_units}"))
401 });
402 if hide_diagnostics_for_scrape_unit {
403 output_options.show_diagnostics = false;
404 }
405 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
406 return Ok(Work::new(move |state| {
407 if artifact.is_true() {
411 paths::create_dir_all(&root)?;
412 }
413
414 if let Some(build_scripts) = build_scripts {
422 let script_outputs = build_script_outputs.lock().unwrap();
423 add_native_deps(
424 &mut rustc,
425 &script_outputs,
426 &build_scripts,
427 pass_l_flag,
428 &target,
429 current_id,
430 mode,
431 )?;
432 if let Some(ref root_output) = root_output {
433 add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, root_output)?;
434 }
435 add_custom_flags(&mut rustc, &script_outputs, script_metadatas)?;
436 }
437
438 for output in outputs.iter() {
439 if output.path.extension() == Some(OsStr::new("rmeta")) {
443 let dst = root.join(&output.path).with_extension("rlib");
444 if dst.exists() {
445 paths::remove_file(&dst)?;
446 }
447 }
448
449 if output.hardlink.is_some() && output.path.exists() {
454 _ = paths::remove_file(&output.path).map_err(|e| {
455 tracing::debug!(
456 "failed to delete previous output file `{:?}`: {e:?}",
457 output.path
458 );
459 });
460 }
461 }
462
463 state.running(&rustc);
464 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
465 if let Some(sbom) = sbom {
466 for file in sbom_files {
467 tracing::debug!("writing sbom to {}", file.display());
468 let outfile = BufWriter::new(paths::create(&file)?);
469 serde_json::to_writer(outfile, &sbom)?;
470 }
471 }
472
473 if let Some(content) = &unremap_content {
474 for file in &unremap_files {
475 tracing::debug!("writing unremap file to {}", file.display());
476 paths::write_atomic(file, content)?;
477 }
478 }
479
480 let (stats, result) = exec.exec(
481 &rustc,
482 package_id,
483 &target,
484 mode,
485 &mut |line| on_stdout_line(state, line, package_id, &target),
486 &mut |line| {
487 on_stderr_line(
488 state,
489 line,
490 package_id,
491 &manifest,
492 &target,
493 &mut output_options,
494 )
495 },
496 capture_rusage,
497 );
498 let result = result
499 .map_err(|e| {
500 if output_options.errors_seen == 0 {
501 e
506 } else {
507 verbose_if_simple_exit_code(e)
508 }
509 })
510 .with_context(|| {
511 let warnings = match output_options.warnings_seen {
513 0 => String::new(),
514 1 => "; 1 warning emitted".to_string(),
515 count => format!("; {} warnings emitted", count),
516 };
517 let errors = match output_options.errors_seen {
518 0 => String::new(),
519 1 => " due to 1 previous error".to_string(),
520 count => format!(" due to {} previous errors", count),
521 };
522 let name = descriptive_pkg_name(&name, &target, &mode);
523 format!("could not compile {name}{errors}{warnings}")
524 });
525
526 if let Some(peak_memory) = stats.peak_rss {
527 state.peak_memory(peak_memory);
528 }
529
530 if let Err(e) = result {
531 if let Some(diagnostic) = failed_scrape_diagnostic {
532 state.warning(diagnostic);
533 }
534
535 return Err(e);
536 }
537
538 debug_assert_eq!(output_options.errors_seen, 0);
540
541 if rustc_dep_info_loc.exists() {
542 fingerprint::translate_dep_info(
543 &rustc_dep_info_loc,
544 &dep_info_loc,
545 &cwd,
546 &pkg_root,
547 &build_dir,
548 &ws_root,
549 &rustc,
550 is_local,
552 &env_config,
553 )
554 .with_context(|| {
555 internal(format!(
556 "could not parse/generate dep info at: {}",
557 rustc_dep_info_loc.display()
558 ))
559 })?;
560 paths::set_file_time_no_err(dep_info_loc, timestamp);
563 }
564
565 if mode.is_check() {
579 for output in outputs.iter() {
580 paths::set_file_time_no_err(&output.path, timestamp);
581 }
582 }
583
584 Ok(())
585 }));
586
587 fn add_native_deps(
590 rustc: &mut ProcessBuilder,
591 build_script_outputs: &BuildScriptOutputs,
592 build_scripts: &BuildScripts,
593 pass_l_flag: bool,
594 target: &Target,
595 current_id: PackageId,
596 mode: CompileMode,
597 ) -> CargoResult<()> {
598 let mut library_paths = vec![];
599
600 for key in build_scripts.to_link.iter() {
601 let output = build_script_outputs.get(key.1).ok_or_else(|| {
602 internal(format!(
603 "couldn't find build script output for {}/{}",
604 key.0, key.1
605 ))
606 })?;
607 library_paths.extend(output.library_paths.iter());
608 }
609
610 library_paths.sort_by_key(|p| match p {
616 LibraryPath::CargoArtifact(_) => 0,
617 LibraryPath::External(_) => 1,
618 });
619
620 for path in library_paths.iter() {
621 rustc.arg("-L").arg(path.as_ref());
622 }
623
624 for key in build_scripts.to_link.iter() {
625 let output = build_script_outputs.get(key.1).ok_or_else(|| {
626 internal(format!(
627 "couldn't find build script output for {}/{}",
628 key.0, key.1
629 ))
630 })?;
631
632 if key.0 == current_id {
633 if pass_l_flag {
634 for name in output.library_links.iter() {
635 rustc.arg("-l").arg(name);
636 }
637 }
638 }
639
640 for (lt, arg) in &output.linker_args {
641 if lt.applies_to(target, mode)
647 && (key.0 == current_id || *lt == LinkArgTarget::Cdylib)
648 {
649 rustc.arg("-C").arg(format!("link-arg={}", arg));
650 }
651 }
652 }
653 Ok(())
654 }
655}
656
657fn verbose_if_simple_exit_code(err: Error) -> Error {
658 match err
661 .downcast_ref::<ProcessError>()
662 .as_ref()
663 .and_then(|perr| perr.code)
664 {
665 Some(n) if cargo_util::is_simple_exit_code(n) => VerboseError::new(err).into(),
666 _ => err,
667 }
668}
669
670fn prebuild_lock_exclusive(lock: LockKey) -> Work {
671 Work::new(move |state| {
672 state.lock_exclusive(&lock)?;
673 Ok(())
674 })
675}
676
677fn downgrade_lock_to_shared(lock: LockKey) -> Work {
678 Work::new(move |state| {
679 state.downgrade_to_shared(&lock)?;
680 Ok(())
681 })
682}
683
684fn link_targets(
687 build_runner: &mut BuildRunner<'_, '_>,
688 unit: &Unit,
689 fresh: bool,
690) -> CargoResult<Work> {
691 let bcx = build_runner.bcx;
692 let outputs = build_runner.outputs(unit)?;
693 let export_dir = build_runner.files().export_dir();
694 let package_id = unit.pkg.package_id();
695 let manifest_path = PathBuf::from(unit.pkg.manifest_path());
696 let profile = unit.profile.clone();
697 let unit_mode = unit.mode;
698 let features = unit.features.iter().map(|s| s.to_string()).collect();
699 let json_messages = bcx.build_config.emit_json();
700 let executable = build_runner.get_executable(unit)?;
701 let mut target = Target::clone(&unit.target);
702 if let TargetSourcePath::Metabuild = target.src_path() {
703 let path = unit
705 .pkg
706 .manifest()
707 .metabuild_path(build_runner.bcx.ws.build_dir());
708 target.set_src_path(TargetSourcePath::Path(path));
709 }
710
711 Ok(Work::new(move |state| {
712 let mut destinations = vec![];
717 for output in outputs.iter() {
718 let src = &output.path;
719 if !src.exists() {
722 continue;
723 }
724 let Some(dst) = output.hardlink.as_ref() else {
725 destinations.push(src.clone());
726 continue;
727 };
728 destinations.push(dst.clone());
729 paths::link_or_copy(src, dst)?;
730 if let Some(ref path) = output.export_path {
731 let export_dir = export_dir.as_ref().unwrap();
732 paths::create_dir_all(export_dir)?;
733
734 paths::link_or_copy(src, path)?;
735 }
736 }
737
738 if json_messages {
739 let debuginfo = match profile.debuginfo.into_inner() {
740 TomlDebugInfo::None => machine_message::ArtifactDebuginfo::Int(0),
741 TomlDebugInfo::Limited => machine_message::ArtifactDebuginfo::Int(1),
742 TomlDebugInfo::Full => machine_message::ArtifactDebuginfo::Int(2),
743 TomlDebugInfo::LineDirectivesOnly => {
744 machine_message::ArtifactDebuginfo::Named("line-directives-only")
745 }
746 TomlDebugInfo::LineTablesOnly => {
747 machine_message::ArtifactDebuginfo::Named("line-tables-only")
748 }
749 };
750 let art_profile = machine_message::ArtifactProfile {
751 opt_level: profile.opt_level.as_str(),
752 debuginfo: Some(debuginfo),
753 debug_assertions: profile.debug_assertions,
754 overflow_checks: profile.overflow_checks,
755 test: unit_mode.is_any_test(),
756 };
757
758 let msg = machine_message::Artifact {
759 package_id: package_id.to_spec(),
760 manifest_path,
761 target: &target,
762 profile: art_profile,
763 features,
764 filenames: destinations,
765 executable,
766 fresh,
767 }
768 .to_json_string();
769 state.stdout(msg)?;
770 }
771 Ok(())
772 }))
773}
774
775fn add_plugin_deps(
779 rustc: &mut ProcessBuilder,
780 build_script_outputs: &BuildScriptOutputs,
781 build_scripts: &BuildScripts,
782 root_output: &Path,
783) -> CargoResult<()> {
784 let var = paths::dylib_path_envvar();
785 let search_path = rustc.get_env(var).unwrap_or_default();
786 let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>();
787 for (pkg_id, metadata) in &build_scripts.plugins {
788 let output = build_script_outputs
789 .get(*metadata)
790 .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?;
791 search_path.append(&mut filter_dynamic_search_path(
792 output.library_paths.iter().map(AsRef::as_ref),
793 root_output,
794 ));
795 }
796 let search_path = paths::join_paths(&search_path, var)?;
797 rustc.env(var, &search_path);
798 Ok(())
799}
800
801fn get_dynamic_search_path(path: &Path) -> &Path {
802 match path.to_str().and_then(|s| s.split_once("=")) {
803 Some(("native" | "crate" | "dependency" | "framework" | "all", path)) => Path::new(path),
804 _ => path,
805 }
806}
807
808fn filter_dynamic_search_path<'a, I>(paths: I, root_output: &Path) -> Vec<PathBuf>
814where
815 I: Iterator<Item = &'a PathBuf>,
816{
817 let mut search_path = vec![];
818 for dir in paths {
819 let dir = get_dynamic_search_path(dir);
820 if dir.starts_with(&root_output) {
821 search_path.push(dir.to_path_buf());
822 } else {
823 debug!(
824 "Not including path {} in runtime library search path because it is \
825 outside target root {}",
826 dir.display(),
827 root_output.display()
828 );
829 }
830 }
831 search_path
832}
833
834#[tracing::instrument(skip_all)]
841fn prepare_rustc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
842 let gctx = build_runner.bcx.gctx;
843 let is_primary = build_runner.is_primary_package(unit);
844 let is_workspace = build_runner.bcx.ws.is_member(&unit.pkg);
845
846 let mut base = build_runner
847 .compilation
848 .rustc_process(unit, is_primary, is_workspace)?;
849 build_base_args(build_runner, &mut base, unit)?;
850 if unit.pkg.manifest().is_embedded() {
851 if !gctx.cli_unstable().script {
852 anyhow::bail!(
853 "parsing `{}` requires `-Zscript`",
854 unit.pkg.manifest_path().display()
855 );
856 }
857 base.arg("-Z").arg("crate-attr=feature(frontmatter)");
858 base.arg("-Z").arg("crate-attr=allow(unused_features)");
859 }
860
861 base.inherit_jobserver(&build_runner.jobserver);
862 build_deps_args(&mut base, build_runner, unit)?;
863 add_cap_lints(build_runner.bcx, unit, &mut base);
864 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
865 base.args(args);
866 }
867 base.args(&unit.rustflags);
868 if gctx.cli_unstable().binary_dep_depinfo {
869 base.arg("-Z").arg("binary-dep-depinfo");
870 }
871 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
872 match build_runner
873 .bcx
874 .gctx
875 .build_config()?
876 .fingerprint
877 .unwrap_or_default()
878 {
879 FingerprintMethod::Content => {
880 base.arg("-Z").arg("checksum-hash-algorithm=blake3");
881 }
882 FingerprintMethod::Mtime => {}
883 }
884 }
885
886 if is_primary {
887 base.env("CARGO_PRIMARY_PACKAGE", "1");
888 let file_list = build_runner.sbom_output_files(unit)?;
889 if !file_list.is_empty() {
890 let file_list = std::env::join_paths(file_list)?;
891 base.env("CARGO_SBOM_PATH", file_list);
892 }
893 }
894
895 if unit.target.is_test() || unit.target.is_bench() {
896 let tmp = build_runner
897 .files()
898 .layout(unit.kind)
899 .build_dir()
900 .prepare_tmp()?;
901 base.env("CARGO_TARGET_TMPDIR", tmp.display().to_string());
902 }
903
904 base.arg("--force-warn=unused_crate_dependencies");
907
908 Ok(base)
909}
910
911#[tracing::instrument(skip_all)]
918fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
919 let bcx = build_runner.bcx;
920 let mut rustdoc = build_runner.compilation.rustdoc_process(unit, None)?;
922 let wants_json_output = build_runner.bcx.build_config.intent.wants_doc_json_output();
923 if unit.pkg.manifest().is_embedded() {
924 if !bcx.gctx.cli_unstable().script {
925 anyhow::bail!(
926 "parsing `{}` requires `-Zscript`",
927 unit.pkg.manifest_path().display()
928 );
929 }
930 rustdoc.arg("-Z").arg("crate-attr=feature(frontmatter)");
931 rustdoc.arg("-Z").arg("crate-attr=allow(unused_features)");
932 }
933 rustdoc.inherit_jobserver(&build_runner.jobserver);
934 let crate_name = unit.target.crate_name();
935 rustdoc.arg("--crate-name").arg(&crate_name);
936 add_path_args(bcx.ws, unit, &mut rustdoc);
937 add_cap_lints(bcx, unit, &mut rustdoc);
938
939 unit.kind.add_target_arg(&mut rustdoc);
940
941 let doc_dir = if wants_json_output {
942 build_runner.files().out_dir_new_layout(unit)
946 } else {
947 build_runner.files().output_dir(unit)
948 };
949
950 rustdoc.arg("-o").arg(&doc_dir);
951 rustdoc.args(&features_args(unit));
952 rustdoc.args(&check_cfg_args(unit));
953
954 add_error_format_and_color(build_runner, &mut rustdoc);
955 add_allow_features(build_runner, &mut rustdoc);
956
957 if build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo {
958 let mut arg = if wants_json_output {
961 OsString::from("--emit=dep-info=")
962 } else if build_runner.bcx.gctx.cli_unstable().rustdoc_mergeable_info {
963 OsString::from("--emit=html-non-static-files,dep-info=")
965 } else {
966 OsString::from("--emit=html-static-files,html-non-static-files,dep-info=")
968 };
969 arg.push(rustdoc_dep_info_loc(build_runner, unit));
970 rustdoc.arg(arg);
971
972 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
973 match build_runner
974 .bcx
975 .gctx
976 .build_config()?
977 .fingerprint
978 .unwrap_or_default()
979 {
980 FingerprintMethod::Content => {
981 rustdoc.arg("-Z").arg("checksum-hash-algorithm=blake3");
982 }
983 FingerprintMethod::Mtime => {}
984 }
985 }
986 } else if build_runner.bcx.gctx.cli_unstable().rustdoc_mergeable_info && !wants_json_output {
987 rustdoc.arg("--emit=html-non-static-files");
989 }
990
991 if build_runner.bcx.gctx.cli_unstable().rustdoc_mergeable_info && !wants_json_output {
992 rustdoc.arg("-Zunstable-options");
994 let mut arg = OsString::from("--write-doc-meta-dir=");
995 arg.push(build_runner.files().out_dir_new_layout(unit));
997 rustdoc.arg(arg);
998 }
999
1000 trim_paths_args_rustdoc(&mut rustdoc, build_runner, unit, &unit.profile.trim_paths)?;
1001
1002 rustdoc.args(unit.pkg.manifest().lint_rustflags());
1003
1004 let metadata = build_runner.metadata_for_doc_units[unit];
1005 rustdoc
1006 .arg("-C")
1007 .arg(format!("metadata={}", metadata.c_metadata()));
1008
1009 if unit.mode.is_doc_scrape() {
1010 debug_assert!(build_runner.bcx.scrape_units.contains(unit));
1011
1012 if unit.target.is_test() {
1013 rustdoc.arg("--scrape-tests");
1014 }
1015
1016 rustdoc.arg("-Zunstable-options");
1017
1018 rustdoc
1019 .arg("--scrape-examples-output-path")
1020 .arg(scrape_output_path(build_runner, unit)?);
1021
1022 for pkg in build_runner.bcx.packages.packages() {
1024 let names = pkg
1025 .targets()
1026 .iter()
1027 .map(|target| target.crate_name())
1028 .collect::<HashSet<_>>();
1029 for name in names {
1030 rustdoc.arg("--scrape-examples-target-crate").arg(name);
1031 }
1032 }
1033 }
1034
1035 if should_include_scrape_units(build_runner.bcx, unit) {
1036 rustdoc.arg("-Zunstable-options");
1037 }
1038
1039 build_deps_args(&mut rustdoc, build_runner, unit)?;
1040 rustdoc::add_root_urls(build_runner, unit, &mut rustdoc)?;
1041
1042 rustdoc::add_output_format(build_runner, &mut rustdoc)?;
1043
1044 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
1045 rustdoc.args(args);
1046 }
1047 rustdoc.args(&unit.rustdocflags);
1048
1049 if !crate_version_flag_already_present(&rustdoc) {
1050 append_crate_version_flag(unit, &mut rustdoc);
1051 }
1052
1053 Ok(rustdoc)
1054}
1055
1056#[tracing::instrument(skip_all)]
1058fn rustdoc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Work> {
1059 let mut rustdoc = prepare_rustdoc(build_runner, unit)?;
1060
1061 let crate_name = unit.target.crate_name();
1062 let is_json_output = build_runner.bcx.build_config.intent.wants_doc_json_output();
1063 let doc_dir = build_runner.files().output_dir(unit);
1064 paths::create_dir_all(&doc_dir)?;
1068
1069 let target_desc = unit.target.description_named();
1070 let name = unit.pkg.name();
1071 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
1072 let package_id = unit.pkg.package_id();
1073 let target = Target::clone(&unit.target);
1074 let manifest = ManifestErrorContext::new(build_runner, unit);
1075
1076 let rustdoc_dep_info_loc = rustdoc_dep_info_loc(build_runner, unit);
1077 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
1078 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
1079 let pkg_root = unit.pkg.root().to_path_buf();
1080 let ws_root = build_runner.bcx.ws.root().to_path_buf();
1081 let cwd = rustdoc
1082 .get_cwd()
1083 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
1084 .to_path_buf();
1085 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
1086 let is_local = unit.is_local();
1087 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
1088 let capture_rusage = build_runner.bcx.capture_rusage();
1089 let rustdoc_depinfo_enabled = build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo;
1090
1091 let mut output_options = OutputOptions::for_dirty(build_runner, unit);
1092 let script_metadatas = build_runner.find_build_script_metadatas(unit);
1093 let scrape_outputs = if should_include_scrape_units(build_runner.bcx, unit) {
1094 Some(
1095 build_runner
1096 .bcx
1097 .scrape_units
1098 .iter()
1099 .map(|unit| {
1100 Ok((
1101 build_runner.files().metadata(unit).unit_id(),
1102 scrape_output_path(build_runner, unit)?,
1103 ))
1104 })
1105 .collect::<CargoResult<HashMap<_, _>>>()?,
1106 )
1107 } else {
1108 None
1109 };
1110
1111 let failed_scrape_units = Arc::clone(&build_runner.failed_scrape_units);
1112 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
1113 && !matches!(
1114 build_runner.bcx.gctx.shell().verbosity(),
1115 Verbosity::Verbose
1116 );
1117 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
1118 make_failed_scrape_diagnostic(
1119 build_runner,
1120 unit,
1121 format_args!("failed to scan {target_desc} in package `{name}` for example code usage"),
1122 )
1123 });
1124 if hide_diagnostics_for_scrape_unit {
1125 output_options.show_diagnostics = false;
1126 }
1127
1128 Ok(Work::new(move |state| {
1129 add_custom_flags(
1130 &mut rustdoc,
1131 &build_script_outputs.lock().unwrap(),
1132 script_metadatas,
1133 )?;
1134
1135 if let Some(scrape_outputs) = scrape_outputs {
1140 let failed_scrape_units = failed_scrape_units.lock().unwrap();
1141 for (metadata, output_path) in &scrape_outputs {
1142 if !failed_scrape_units.contains(metadata) {
1143 rustdoc.arg("--with-examples").arg(output_path);
1144 }
1145 }
1146 }
1147
1148 if !is_json_output {
1149 let crate_dir = doc_dir.join(&crate_name);
1150 if crate_dir.exists() {
1151 debug!("removing pre-existing doc directory {:?}", crate_dir);
1154 paths::remove_dir_all(&crate_dir)?;
1155 }
1156 };
1157 state.running(&rustdoc);
1158 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
1159
1160 let (stats, result) = rustdoc.exec_with_streaming(
1161 &mut |line| on_stdout_line(state, line, package_id, &target),
1162 &mut |line| {
1163 on_stderr_line(
1164 state,
1165 line,
1166 package_id,
1167 &manifest,
1168 &target,
1169 &mut output_options,
1170 )
1171 },
1172 false,
1173 capture_rusage,
1174 );
1175 let result = result
1176 .map_err(verbose_if_simple_exit_code)
1177 .with_context(|| format!("could not document `{}`", name));
1178
1179 if let Some(peak_memory) = stats.peak_rss {
1180 state.peak_memory(peak_memory);
1181 }
1182
1183 if let Err(e) = result {
1184 if let Some(diagnostic) = failed_scrape_diagnostic {
1185 state.warning(diagnostic);
1186 }
1187
1188 return Err(e);
1189 }
1190 if rustdoc_depinfo_enabled && rustdoc_dep_info_loc.exists() {
1191 fingerprint::translate_dep_info(
1192 &rustdoc_dep_info_loc,
1193 &dep_info_loc,
1194 &cwd,
1195 &pkg_root,
1196 &build_dir,
1197 &ws_root,
1198 &rustdoc,
1199 is_local,
1201 &env_config,
1202 )
1203 .with_context(|| {
1204 internal(format_args!(
1205 "could not parse/generate dep info at: {}",
1206 rustdoc_dep_info_loc.display()
1207 ))
1208 })?;
1209 paths::set_file_time_no_err(dep_info_loc, timestamp);
1212 }
1213
1214 Ok(())
1215 }))
1216}
1217
1218fn crate_version_flag_already_present(rustdoc: &ProcessBuilder) -> bool {
1221 rustdoc.get_args().any(|flag| {
1222 flag.to_str()
1223 .map_or(false, |flag| flag.starts_with(RUSTDOC_CRATE_VERSION_FLAG))
1224 })
1225}
1226
1227fn append_crate_version_flag(unit: &Unit, rustdoc: &mut ProcessBuilder) {
1228 rustdoc
1229 .arg(RUSTDOC_CRATE_VERSION_FLAG)
1230 .arg(unit.pkg.version().to_string());
1231}
1232
1233enum CapLints {
1234 Allow,
1235 Warn,
1236}
1237
1238fn compute_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit) -> Option<CapLints> {
1239 if !unit.show_warnings(bcx.gctx) {
1242 Some(CapLints::Allow)
1243 } else if !unit.is_local() {
1246 Some(CapLints::Warn)
1247 } else {
1248 None
1249 }
1250}
1251
1252fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuilder) {
1256 if let Some(cap_lints) = compute_cap_lints(bcx, unit) {
1257 match cap_lints {
1258 CapLints::Allow => {
1259 cmd.arg("--cap-lints").arg("allow");
1260 }
1261 CapLints::Warn => {
1262 cmd.arg("--cap-lints").arg("warn");
1263 }
1264 }
1265 }
1266}
1267
1268fn add_allow_features(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1272 if let Some(allow) = &build_runner.bcx.gctx.cli_unstable().allow_features {
1273 use std::fmt::Write;
1274 let mut arg = String::from("-Zallow-features=");
1275 for f in allow {
1276 let _ = write!(&mut arg, "{f},");
1277 }
1278 cmd.arg(arg.trim_end_matches(','));
1279 }
1280}
1281
1282fn add_error_format_and_color(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1293 let enable_timings =
1294 build_runner.bcx.gctx.cli_unstable().section_timings && build_runner.bcx.logger.is_some();
1295 if enable_timings {
1296 cmd.arg("-Zunstable-options");
1297 }
1298
1299 cmd.arg("--error-format=json");
1300
1301 let mut json = String::from(
1302 "--json=diagnostic-rendered-ansi,artifacts,future-incompat,unused-externs-silent",
1303 );
1304 if let MessageFormat::Short | MessageFormat::Json { short: true, .. } =
1305 build_runner.bcx.build_config.message_format
1306 {
1307 json.push_str(",diagnostic-short");
1308 } else if build_runner.bcx.gctx.shell().err_unicode()
1309 && build_runner.bcx.gctx.cli_unstable().rustc_unicode
1310 {
1311 json.push_str(",diagnostic-unicode");
1312 }
1313 if enable_timings {
1314 json.push_str(",timings");
1315 }
1316 cmd.arg(json);
1317
1318 let gctx = build_runner.bcx.gctx;
1319 if let Some(width) = gctx.shell().err_width().diagnostic_terminal_width() {
1320 cmd.arg(format!("--diagnostic-width={width}"));
1321 }
1322}
1323
1324fn build_base_args(
1326 build_runner: &BuildRunner<'_, '_>,
1327 cmd: &mut ProcessBuilder,
1328 unit: &Unit,
1329) -> CargoResult<()> {
1330 assert!(!unit.mode.is_run_custom_build());
1331
1332 let bcx = build_runner.bcx;
1333 let Profile {
1334 ref opt_level,
1335 codegen_backend,
1336 codegen_units,
1337 debuginfo,
1338 debug_assertions,
1339 split_debuginfo,
1340 overflow_checks,
1341 rpath,
1342 ref panic,
1343 incremental,
1344 strip,
1345 rustflags: profile_rustflags,
1346 trim_paths,
1347 hint_mostly_unused: profile_hint_mostly_unused,
1348 ..
1349 } = unit.profile.clone();
1350 let hints = unit.pkg.hints().cloned().unwrap_or_default();
1351 let test = unit.mode.is_any_test();
1352
1353 cmd.arg("--crate-name").arg(&unit.target.crate_name());
1354
1355 let edition = unit.target.edition();
1356 edition.cmd_edition_arg(cmd);
1357
1358 add_path_args(bcx.ws, unit, cmd);
1359 add_error_format_and_color(build_runner, cmd);
1360 add_allow_features(build_runner, cmd);
1361
1362 let mut contains_dy_lib = false;
1363 if !test {
1364 for crate_type in &unit.target.rustc_crate_types() {
1365 cmd.arg("--crate-type").arg(crate_type.as_str());
1366 contains_dy_lib |= crate_type == &CrateType::Dylib;
1367 }
1368 }
1369
1370 if unit.mode.is_check() {
1371 cmd.arg("--emit=dep-info,metadata");
1372 } else if !build_runner
1373 .bcx
1374 .target_data
1375 .info(unit.kind)
1376 .should_embed_metadata()
1377 {
1378 if unit.benefits_from_no_embed_metadata() {
1388 cmd.arg("--emit=dep-info,metadata,link");
1389 cmd.args(&["-Z", "embed-metadata=no"]);
1390 } else {
1391 cmd.arg("--emit=dep-info,link");
1392 }
1393 } else {
1394 if !unit.requires_upstream_objects() {
1398 cmd.arg("--emit=dep-info,metadata,link");
1399 } else {
1400 cmd.arg("--emit=dep-info,link");
1401 }
1402 }
1403
1404 let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
1405 || (contains_dy_lib && !build_runner.is_primary_package(unit));
1406 if prefer_dynamic {
1407 cmd.arg("-C").arg("prefer-dynamic");
1408 }
1409
1410 if opt_level.as_str() != "0" {
1411 cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
1412 }
1413
1414 if *panic != PanicStrategy::Unwind {
1415 cmd.arg("-C").arg(format!("panic={}", panic));
1416 }
1417 if *panic == PanicStrategy::ImmediateAbort {
1418 cmd.arg("-Z").arg("unstable-options");
1419 }
1420
1421 cmd.args(<o_args(build_runner, unit));
1422
1423 if let Some(backend) = codegen_backend {
1424 cmd.arg("-Z").arg(&format!("codegen-backend={}", backend));
1425 }
1426
1427 if let Some(n) = codegen_units {
1428 cmd.arg("-C").arg(&format!("codegen-units={}", n));
1429 }
1430
1431 let debuginfo = debuginfo.into_inner();
1432 if debuginfo != TomlDebugInfo::None {
1434 cmd.arg("-C").arg(format!("debuginfo={debuginfo}"));
1435 if let Some(split) = split_debuginfo {
1442 if build_runner
1443 .bcx
1444 .target_data
1445 .info(unit.kind)
1446 .supports_debuginfo_split(split)
1447 {
1448 cmd.arg("-C").arg(format!("split-debuginfo={split}"));
1449 }
1450 }
1451 }
1452
1453 trim_paths_args(cmd, build_runner, unit, &trim_paths)?;
1454
1455 match compute_cap_lints(bcx, unit) {
1456 None | Some(CapLints::Warn) => {
1457 cmd.args(unit.pkg.manifest().lint_rustflags());
1458 }
1459 Some(CapLints::Allow) => {}
1462 }
1463 cmd.args(&profile_rustflags);
1464
1465 if opt_level.as_str() != "0" {
1469 if debug_assertions {
1470 cmd.args(&["-C", "debug-assertions=on"]);
1471 if !overflow_checks {
1472 cmd.args(&["-C", "overflow-checks=off"]);
1473 }
1474 } else if overflow_checks {
1475 cmd.args(&["-C", "overflow-checks=on"]);
1476 }
1477 } else if !debug_assertions {
1478 cmd.args(&["-C", "debug-assertions=off"]);
1479 if overflow_checks {
1480 cmd.args(&["-C", "overflow-checks=on"]);
1481 }
1482 } else if !overflow_checks {
1483 cmd.args(&["-C", "overflow-checks=off"]);
1484 }
1485
1486 if test && unit.target.harness() {
1487 cmd.arg("--test");
1488
1489 if *panic == PanicStrategy::Abort || *panic == PanicStrategy::ImmediateAbort {
1497 cmd.arg("-Z").arg("panic-abort-tests");
1498 }
1499 } else if test {
1500 cmd.arg("--cfg").arg("test");
1501 }
1502
1503 cmd.args(&features_args(unit));
1504 cmd.args(&check_cfg_args(unit));
1505
1506 let meta = build_runner.files().metadata(unit);
1507 cmd.arg("-C")
1508 .arg(&format!("metadata={}", meta.c_metadata()));
1509 if let Some(c_extra_filename) = meta.c_extra_filename() {
1510 cmd.arg("-C")
1511 .arg(&format!("extra-filename=-{c_extra_filename}"));
1512 }
1513
1514 if rpath {
1515 cmd.arg("-C").arg("rpath");
1516 }
1517
1518 cmd.arg("--out-dir")
1519 .arg(&build_runner.files().output_dir(unit));
1520
1521 unit.kind.add_target_arg(cmd);
1522
1523 add_codegen_linker(cmd, build_runner, unit, bcx.gctx.target_applies_to_host()?);
1524
1525 if incremental {
1526 add_codegen_incremental(cmd, build_runner, unit)
1527 }
1528
1529 let pkg_hint_mostly_unused = match hints.mostly_unused {
1530 Some(toml::Value::Boolean(b)) => Some(b),
1531 _ => None,
1534 };
1535 if profile_hint_mostly_unused
1536 .or(pkg_hint_mostly_unused)
1537 .unwrap_or(false)
1538 && bcx.gctx.cli_unstable().profile_hint_mostly_unused
1539 {
1540 cmd.arg("-Zhint-mostly-unused");
1541 }
1542
1543 let strip = strip.into_inner();
1544 if strip != StripInner::None {
1545 cmd.arg("-C").arg(format!("strip={}", strip));
1546 }
1547
1548 if unit.is_std {
1549 cmd.arg("-Z")
1555 .arg("force-unstable-if-unmarked")
1556 .env("RUSTC_BOOTSTRAP", "1");
1557 }
1558
1559 if let Some(version) = unit.pkg.manifest().rust_version()
1560 && bcx.gctx.cli_unstable().hint_msrv
1561 {
1562 cmd.arg("-Z").arg(format!("hint-msrv={version}"));
1563 }
1564
1565 Ok(())
1566}
1567
1568fn features_args(unit: &Unit) -> Vec<OsString> {
1570 let mut args = Vec::with_capacity(unit.features.len() * 2);
1571
1572 for feat in &unit.features {
1573 args.push(OsString::from("--cfg"));
1574 args.push(OsString::from(format!("feature=\"{}\"", feat)));
1575 }
1576
1577 args
1578}
1579
1580fn check_cfg_args(unit: &Unit) -> Vec<OsString> {
1582 let gross_cap_estimation = unit.pkg.summary().features().len() * 7 + 25;
1600 let mut arg_feature = OsString::with_capacity(gross_cap_estimation);
1601
1602 arg_feature.push("cfg(feature, values(");
1603 for (i, feature) in unit.pkg.summary().features().keys().enumerate() {
1604 if i != 0 {
1605 arg_feature.push(", ");
1606 }
1607 arg_feature.push("\"");
1608 arg_feature.push(feature);
1609 arg_feature.push("\"");
1610 }
1611 arg_feature.push("))");
1612
1613 vec![
1622 OsString::from("--check-cfg"),
1623 OsString::from("cfg(docsrs,test)"),
1624 OsString::from("--check-cfg"),
1625 arg_feature,
1626 ]
1627}
1628
1629fn lto_args(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> Vec<OsString> {
1631 let mut result = Vec::new();
1632 let mut push = |arg: &str| {
1633 result.push(OsString::from("-C"));
1634 result.push(OsString::from(arg));
1635 };
1636 match build_runner.lto[unit] {
1637 lto::Lto::Run(None) => push("lto"),
1638 lto::Lto::Run(Some(s)) => push(&format!("lto={}", s)),
1639 lto::Lto::Off => {
1640 push("lto=off");
1641 push("embed-bitcode=no");
1642 }
1643 lto::Lto::ObjectAndBitcode => {} lto::Lto::OnlyBitcode => push("linker-plugin-lto"),
1645 lto::Lto::OnlyObject => push("embed-bitcode=no"),
1646 }
1647 result
1648}
1649
1650fn build_deps_args(
1656 cmd: &mut ProcessBuilder,
1657 build_runner: &BuildRunner<'_, '_>,
1658 unit: &Unit,
1659) -> CargoResult<()> {
1660 let bcx = build_runner.bcx;
1661
1662 for arg in lib_search_paths(build_runner, unit)? {
1663 cmd.arg(arg);
1664 }
1665
1666 let deps = build_runner.unit_deps(unit);
1667
1668 if !deps
1672 .iter()
1673 .any(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_linkable())
1674 {
1675 if let Some(dep) = deps.iter().find(|dep| {
1676 !dep.unit.mode.is_doc() && dep.unit.target.is_lib() && !dep.unit.artifact.is_true()
1677 }) {
1678 let dep_name = dep.unit.target.crate_name();
1679 let name = unit.target.crate_name();
1680 bcx.gctx.shell().print_report(&[
1681 Level::WARNING.secondary_title(format!("the package `{dep_name}` provides no linkable target"))
1682 .elements([
1683 Level::NOTE.message(format!("this might cause `{name}` to fail compilation")),
1684 Level::NOTE.message("this warning might turn into a hard error in the future"),
1685 Level::HELP.message(format!("consider adding 'dylib' or 'rlib' to key 'crate-type' in `{dep_name}`'s Cargo.toml"))
1686 ])
1687 ], false)?;
1688 }
1689 }
1690
1691 let mut unstable_opts = false;
1692
1693 let first_custom_build_dep = deps.iter().find(|dep| dep.unit.mode.is_run_custom_build());
1695 if let Some(dep) = first_custom_build_dep {
1696 let out_dir = if bcx.gctx.cli_unstable().build_dir_new_layout {
1697 build_runner.files().out_dir_new_layout(&dep.unit)
1698 } else {
1699 build_runner.files().build_script_out_dir(&dep.unit)
1700 };
1701 cmd.env("OUT_DIR", &out_dir);
1702 }
1703
1704 let is_multiple_build_scripts_enabled = unit
1706 .pkg
1707 .manifest()
1708 .unstable_features()
1709 .require(Feature::multiple_build_scripts())
1710 .is_ok();
1711
1712 if is_multiple_build_scripts_enabled {
1713 for dep in deps {
1714 if dep.unit.mode.is_run_custom_build() {
1715 let out_dir = if bcx.gctx.cli_unstable().build_dir_new_layout {
1716 build_runner.files().out_dir_new_layout(&dep.unit)
1717 } else {
1718 build_runner.files().build_script_out_dir(&dep.unit)
1719 };
1720 let target_name = dep.unit.target.name();
1721 let out_dir_prefix = target_name
1722 .strip_prefix("build-script-")
1723 .unwrap_or(target_name);
1724 let out_dir_name = format!("{out_dir_prefix}_OUT_DIR");
1725 cmd.env(&out_dir_name, &out_dir);
1726 }
1727 }
1728 }
1729 for arg in extern_args(build_runner, unit, &mut unstable_opts)? {
1730 cmd.arg(arg);
1731 }
1732
1733 for (var, env) in artifact::get_env(build_runner, unit, deps)? {
1734 cmd.env(&var, env);
1735 }
1736
1737 if unstable_opts {
1740 cmd.arg("-Z").arg("unstable-options");
1741 }
1742
1743 Ok(())
1744}
1745
1746fn add_custom_flags(
1750 cmd: &mut ProcessBuilder,
1751 build_script_outputs: &BuildScriptOutputs,
1752 metadata_vec: Option<Vec<UnitHash>>,
1753) -> CargoResult<()> {
1754 if let Some(metadata_vec) = metadata_vec {
1755 for metadata in metadata_vec {
1756 if let Some(output) = build_script_outputs.get(metadata) {
1757 for cfg in output.cfgs.iter() {
1758 cmd.arg("--cfg").arg(cfg);
1759 }
1760 for check_cfg in &output.check_cfgs {
1761 cmd.arg("--check-cfg").arg(check_cfg);
1762 }
1763 for (name, value) in output.env.iter() {
1764 cmd.env(name, value);
1765 }
1766 }
1767 }
1768 }
1769
1770 Ok(())
1771}
1772
1773#[tracing::instrument(skip_all)]
1775pub fn lib_search_paths(
1776 build_runner: &BuildRunner<'_, '_>,
1777 unit: &Unit,
1778) -> CargoResult<Vec<OsString>> {
1779 let mut lib_search_paths = Vec::new();
1780 if build_runner.bcx.gctx.cli_unstable().build_dir_new_layout {
1781 let paths = dep_paths_for_args(unit, build_runner);
1783
1784 for path in paths {
1785 let mut deps = OsString::from("dependency=");
1786 deps.push(path);
1787 lib_search_paths.extend(["-L".into(), deps]);
1788 }
1789 } else {
1790 let mut deps = OsString::from("dependency=");
1791 deps.push(build_runner.files().deps_dir(unit));
1792 lib_search_paths.extend(["-L".into(), deps]);
1793 }
1794
1795 if !unit.kind.is_host() {
1798 let mut deps = OsString::from("dependency=");
1799 deps.push(build_runner.files().host_deps(unit));
1800 lib_search_paths.extend(["-L".into(), deps]);
1801 }
1802
1803 Ok(lib_search_paths)
1804}
1805
1806fn dep_paths_for_args(unit: &Unit, build_runner: &BuildRunner<'_, '_>) -> Vec<PathBuf> {
1820 let direct = build_runner.unit_deps(unit);
1821 if direct.is_empty() {
1822 return Vec::new();
1823 }
1824 let mut indirect = HashSet::default();
1825 let mut visited = HashSet::default();
1826 let mut stack: Vec<&Unit> = direct
1827 .iter()
1828 .filter(|d| !d.unit.target.is_custom_build())
1829 .map(|d| &d.unit)
1830 .collect();
1831 while let Some(u) = stack.pop() {
1832 if !visited.insert(u.clone()) {
1833 continue;
1834 }
1835 if u.target.proc_macro() {
1836 continue;
1837 }
1838 for dep in build_runner.unit_deps(u) {
1839 let v = &dep.unit;
1840 if v.target.is_custom_build() {
1841 continue;
1842 }
1843 indirect.insert(v.clone());
1844 if v.target.proc_macro() {
1845 continue;
1846 }
1847 if !visited.contains(v) {
1848 stack.push(v);
1849 }
1850 }
1851 }
1852
1853 let mut paths: Vec<PathBuf> = indirect
1854 .into_iter()
1855 .map(|u| build_runner.files().deps_dir(&u))
1856 .collect();
1857
1858 paths.sort_unstable_by(|a, b| match (a.to_str(), b.to_str()) {
1867 (Some(a), Some(b)) => a.cmp(b),
1868 (Some(_), None) => std::cmp::Ordering::Less,
1869 (None, Some(_)) => std::cmp::Ordering::Greater,
1870 (None, None) => std::cmp::Ordering::Less,
1871 });
1872 paths.dedup_by(|a, b| matches!((a.to_str(), b.to_str()), (Some(x), Some(y)) if x == y));
1873
1874 paths
1875}
1876
1877fn is_public_dependency_enabled(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> bool {
1878 unit.pkg
1879 .manifest()
1880 .unstable_features()
1881 .require(Feature::public_dependency())
1882 .is_ok()
1883 || build_runner.bcx.gctx.cli_unstable().public_dependency
1884}
1885
1886pub fn extern_args(
1888 build_runner: &BuildRunner<'_, '_>,
1889 unit: &Unit,
1890 unstable_opts: &mut bool,
1891) -> CargoResult<Vec<OsString>> {
1892 let mut result = Vec::new();
1893 let deps = build_runner.unit_deps(unit);
1894
1895 let no_embed_metadata = !build_runner
1896 .bcx
1897 .target_data
1898 .info(unit.kind)
1899 .should_embed_metadata();
1900 let public_dependency_enabled = is_public_dependency_enabled(build_runner, unit);
1901
1902 let mut link_to = |dep: &UnitDep,
1904 extern_crate_name: InternedString,
1905 noprelude: bool,
1906 nounused: bool|
1907 -> CargoResult<()> {
1908 let mut value = OsString::new();
1909 let mut opts = Vec::new();
1910 if !dep.public && unit.target.is_lib() && public_dependency_enabled {
1911 opts.push("priv");
1912 *unstable_opts = true;
1913 }
1914 if noprelude {
1915 opts.push("noprelude");
1916 *unstable_opts = true;
1917 }
1918 if nounused {
1919 opts.push("nounused");
1920 *unstable_opts = true;
1921 }
1922 if !opts.is_empty() {
1923 value.push(opts.join(","));
1924 value.push(":");
1925 }
1926 value.push(extern_crate_name.as_str());
1927 value.push("=");
1928
1929 let mut pass = |file| {
1930 let mut value = value.clone();
1931 value.push(file);
1932 result.push(OsString::from("--extern"));
1933 result.push(value);
1934 };
1935
1936 let outputs = build_runner.outputs(&dep.unit)?;
1937
1938 if build_runner.only_requires_rmeta(unit, &dep.unit) || dep.unit.mode.is_check() {
1939 let output = outputs
1941 .iter()
1942 .find(|output| output.flavor == FileFlavor::Rmeta)
1943 .expect("failed to find rmeta dep for pipelined dep");
1944 pass(&output.path);
1945 } else {
1946 for output in outputs.iter() {
1948 if output.flavor == FileFlavor::Linkable {
1949 pass(&output.path);
1950 }
1951 else if no_embed_metadata && output.flavor == FileFlavor::Rmeta {
1955 pass(&output.path);
1956 }
1957 }
1958 }
1959 Ok(())
1960 };
1961
1962 for dep in deps {
1963 if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
1964 link_to(dep, dep.extern_crate_name, dep.noprelude, dep.nounused)?;
1965 }
1966 }
1967 if unit.target.proc_macro() {
1968 result.push(OsString::from("--extern"));
1970 result.push(OsString::from("proc_macro"));
1971 }
1972
1973 Ok(result)
1974}
1975
1976fn add_codegen_linker(
1978 cmd: &mut ProcessBuilder,
1979 build_runner: &BuildRunner<'_, '_>,
1980 unit: &Unit,
1981 target_applies_to_host: bool,
1982) {
1983 let linker = if unit.target.for_host() && !target_applies_to_host {
1984 build_runner
1985 .compilation
1986 .host_linker()
1987 .map(|s| s.as_os_str())
1988 } else {
1989 build_runner
1990 .compilation
1991 .target_linker(unit.kind)
1992 .map(|s| s.as_os_str())
1993 };
1994
1995 if let Some(linker) = linker {
1996 let mut arg = OsString::from("linker=");
1997 arg.push(linker);
1998 cmd.arg("-C").arg(arg);
1999 }
2000}
2001
2002fn add_codegen_incremental(
2004 cmd: &mut ProcessBuilder,
2005 build_runner: &BuildRunner<'_, '_>,
2006 unit: &Unit,
2007) {
2008 let dir = build_runner.files().incremental_dir(&unit);
2009 let mut arg = OsString::from("incremental=");
2010 arg.push(dir.as_os_str());
2011 cmd.arg("-C").arg(arg);
2012}
2013
2014fn envify(s: &str) -> String {
2015 s.chars()
2016 .flat_map(|c| c.to_uppercase())
2017 .map(|c| if c == '-' { '_' } else { c })
2018 .collect()
2019}
2020
2021struct OutputOptions {
2024 format: MessageFormat,
2026 cache_cell: Option<(PathBuf, OnceCell<File>)>,
2031 show_diagnostics: bool,
2039 warnings_seen: usize,
2041 errors_seen: usize,
2043}
2044
2045impl OutputOptions {
2046 fn for_dirty(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions {
2047 let path = build_runner.files().message_cache_path(unit);
2048 drop(fs::remove_file(&path));
2050 let cache_cell = Some((path, OnceCell::new()));
2051
2052 let show_diagnostics = true;
2053
2054 let format = build_runner.bcx.build_config.message_format;
2055
2056 OutputOptions {
2057 format,
2058 cache_cell,
2059 show_diagnostics,
2060 warnings_seen: 0,
2061 errors_seen: 0,
2062 }
2063 }
2064
2065 fn for_fresh(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions {
2066 let cache_cell = None;
2067
2068 let show_diagnostics = unit.show_warnings(build_runner.bcx.gctx);
2071
2072 let format = build_runner.bcx.build_config.message_format;
2073
2074 OutputOptions {
2075 format,
2076 cache_cell,
2077 show_diagnostics,
2078 warnings_seen: 0,
2079 errors_seen: 0,
2080 }
2081 }
2082}
2083
2084struct ManifestErrorContext {
2090 path: PathBuf,
2092 spans: Option<Arc<toml::Spanned<toml::de::DeTable<'static>>>>,
2094 contents: Option<String>,
2096 rename_table: HashMap<InternedString, InternedString>,
2099 requested_kinds: Vec<CompileKind>,
2102 cfgs: Vec<Vec<Cfg>>,
2105 host_name: InternedString,
2106 cwd: PathBuf,
2108 term_width: usize,
2110}
2111
2112fn on_stdout_line(
2113 state: &JobState<'_, '_>,
2114 line: &str,
2115 _package_id: PackageId,
2116 _target: &Target,
2117) -> CargoResult<()> {
2118 state.stdout(line.to_string())?;
2119 Ok(())
2120}
2121
2122fn on_stderr_line(
2123 state: &JobState<'_, '_>,
2124 line: &str,
2125 package_id: PackageId,
2126 manifest: &ManifestErrorContext,
2127 target: &Target,
2128 options: &mut OutputOptions,
2129) -> CargoResult<()> {
2130 if on_stderr_line_inner(state, line, package_id, manifest, target, options)? {
2131 if let Some((path, cell)) = &mut options.cache_cell {
2133 let f = cell.try_borrow_mut_with(|| paths::create(path))?;
2135 debug_assert!(!line.contains('\n'));
2136 f.write_all(line.as_bytes())?;
2137 f.write_all(&[b'\n'])?;
2138 }
2139 }
2140 Ok(())
2141}
2142
2143fn on_stderr_line_inner(
2145 state: &JobState<'_, '_>,
2146 line: &str,
2147 package_id: PackageId,
2148 manifest: &ManifestErrorContext,
2149 target: &Target,
2150 options: &mut OutputOptions,
2151) -> CargoResult<bool> {
2152 if !line.starts_with('{') {
2158 state.stderr(line.to_string())?;
2159 return Ok(true);
2160 }
2161
2162 let mut compiler_message: Box<serde_json::value::RawValue> = match serde_json::from_str(line) {
2163 Ok(msg) => msg,
2164
2165 Err(e) => {
2169 debug!("failed to parse json: {:?}", e);
2170 state.stderr(line.to_string())?;
2171 return Ok(true);
2172 }
2173 };
2174
2175 let count_diagnostic = |level, options: &mut OutputOptions| {
2176 if level == "warning" {
2177 options.warnings_seen += 1;
2178 } else if level == "error" {
2179 options.errors_seen += 1;
2180 }
2181 };
2182
2183 if let Ok(report) = serde_json::from_str::<FutureIncompatReport>(compiler_message.get()) {
2184 for item in &report.future_incompat_report {
2185 count_diagnostic(&*item.diagnostic.level, options);
2186 }
2187 state.future_incompat_report(report.future_incompat_report);
2188 return Ok(true);
2189 }
2190
2191 let res = serde_json::from_str::<SectionTiming>(compiler_message.get());
2192 if let Ok(timing_record) = res {
2193 state.on_section_timing_emitted(timing_record);
2194 return Ok(false);
2195 }
2196
2197 let add_pub_in_priv_diagnostic = |diag: &mut String| -> bool {
2199 static PRIV_DEP_REGEX: LazyLock<Regex> =
2208 LazyLock::new(|| Regex::new("from private dependency '([A-Za-z0-9-_]+)'").unwrap());
2209 if let Some(crate_name) = PRIV_DEP_REGEX.captures(diag).and_then(|m| m.get(1))
2210 && let Some(ref contents) = manifest.contents
2211 && let Some(span) = manifest.find_crate_span(crate_name.as_str())
2212 {
2213 let rel_path = pathdiff::diff_paths(&manifest.path, &manifest.cwd)
2214 .unwrap_or_else(|| manifest.path.clone())
2215 .display()
2216 .to_string();
2217 let report = [Group::with_title(Level::NOTE.secondary_title(format!(
2218 "dependency `{}` declared here",
2219 crate_name.as_str()
2220 )))
2221 .element(
2222 Snippet::source(contents)
2223 .path(rel_path)
2224 .annotation(AnnotationKind::Context.span(span)),
2225 )];
2226
2227 let rendered = Renderer::styled()
2228 .term_width(manifest.term_width)
2229 .render(&report);
2230 diag.push_str(&rendered);
2231 diag.push('\n');
2232 return true;
2233 }
2234 false
2235 };
2236
2237 match options.format {
2240 MessageFormat::Human
2245 | MessageFormat::Short
2246 | MessageFormat::Json {
2247 render_diagnostics: true,
2248 ..
2249 } => {
2250 #[derive(serde::Deserialize)]
2251 struct CompilerMessage<'a> {
2252 rendered: String,
2256 #[serde(borrow)]
2257 message: Cow<'a, str>,
2258 #[serde(borrow)]
2259 level: Cow<'a, str>,
2260 children: Vec<PartialDiagnostic>,
2261 code: Option<DiagnosticCode>,
2262 }
2263
2264 #[derive(serde::Deserialize)]
2273 struct PartialDiagnostic {
2274 spans: Vec<PartialDiagnosticSpan>,
2275 }
2276
2277 #[derive(serde::Deserialize)]
2279 struct PartialDiagnosticSpan {
2280 suggestion_applicability: Option<Applicability>,
2281 }
2282
2283 #[derive(serde::Deserialize)]
2284 struct DiagnosticCode {
2285 code: String,
2286 }
2287
2288 if let Ok(mut msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2289 {
2290 if msg.message.starts_with("aborting due to")
2291 || msg.message.ends_with("warning emitted")
2292 || msg.message.ends_with("warnings emitted")
2293 {
2294 return Ok(true);
2296 }
2297 if msg.rendered.ends_with('\n') {
2299 msg.rendered.pop();
2300 }
2301 let mut rendered = msg.rendered;
2302 if options.show_diagnostics {
2303 let machine_applicable: bool = msg
2304 .children
2305 .iter()
2306 .map(|child| {
2307 child
2308 .spans
2309 .iter()
2310 .filter_map(|span| span.suggestion_applicability)
2311 .any(|app| app == Applicability::MachineApplicable)
2312 })
2313 .any(|b| b);
2314 count_diagnostic(&msg.level, options);
2315 if msg
2316 .code
2317 .as_ref()
2318 .is_some_and(|c| c.code == "exported_private_dependencies")
2319 && options.format != MessageFormat::Short
2320 {
2321 add_pub_in_priv_diagnostic(&mut rendered);
2322 }
2323 let lint = msg.code.is_some();
2324 state.emit_diag(&msg.level, rendered, lint, machine_applicable)?;
2325 }
2326 return Ok(true);
2327 }
2328 }
2329
2330 MessageFormat::Json { ansi, .. } => {
2331 #[derive(serde::Deserialize, serde::Serialize)]
2332 struct CompilerMessage<'a> {
2333 rendered: String,
2334 #[serde(flatten, borrow)]
2335 other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2336 code: Option<DiagnosticCode<'a>>,
2337 }
2338
2339 #[derive(serde::Deserialize, serde::Serialize)]
2340 struct DiagnosticCode<'a> {
2341 code: String,
2342 #[serde(flatten, borrow)]
2343 other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2344 }
2345
2346 if let Ok(mut error) =
2347 serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2348 {
2349 let modified_diag = if error
2350 .code
2351 .as_ref()
2352 .is_some_and(|c| c.code == "exported_private_dependencies")
2353 {
2354 add_pub_in_priv_diagnostic(&mut error.rendered)
2355 } else {
2356 false
2357 };
2358
2359 if !ansi {
2363 error.rendered = anstream::adapter::strip_str(&error.rendered).to_string();
2364 }
2365 if !ansi || modified_diag {
2366 let new_line = serde_json::to_string(&error)?;
2367 compiler_message = serde_json::value::RawValue::from_string(new_line)?;
2368 }
2369 }
2370 }
2371 }
2372
2373 #[derive(serde::Deserialize)]
2380 struct ArtifactNotification<'a> {
2381 #[serde(borrow)]
2382 artifact: Cow<'a, str>,
2383 }
2384
2385 if let Ok(artifact) = serde_json::from_str::<ArtifactNotification<'_>>(compiler_message.get()) {
2386 trace!("found directive from rustc: `{}`", artifact.artifact);
2387 if artifact.artifact.ends_with(".rmeta") {
2388 debug!("looks like metadata finished early!");
2389 state.rmeta_produced();
2390 }
2391 return Ok(false);
2392 }
2393
2394 #[derive(serde::Deserialize)]
2395 struct UnusedExterns {
2396 unused_extern_names: std::collections::BTreeSet<InternedString>,
2397 }
2398 if let Ok(uext) = serde_json::from_str::<UnusedExterns>(compiler_message.get()) {
2399 trace!(
2400 "obtained unused externs list from rustc: `{:?}`",
2401 uext.unused_extern_names
2402 );
2403 state.unused_externs(uext.unused_extern_names);
2404 return Ok(true);
2405 }
2406
2407 if !options.show_diagnostics {
2412 return Ok(true);
2413 }
2414
2415 #[derive(serde::Deserialize)]
2416 struct CompilerMessage<'a> {
2417 #[serde(borrow)]
2418 message: Cow<'a, str>,
2419 #[serde(borrow)]
2420 level: Cow<'a, str>,
2421 }
2422
2423 if let Ok(msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get()) {
2424 if msg.message.starts_with("aborting due to")
2425 || msg.message.ends_with("warning emitted")
2426 || msg.message.ends_with("warnings emitted")
2427 {
2428 return Ok(true);
2430 }
2431 count_diagnostic(&msg.level, options);
2432 }
2433
2434 let msg = machine_message::FromCompiler {
2435 package_id: package_id.to_spec(),
2436 manifest_path: &manifest.path,
2437 target,
2438 message: compiler_message,
2439 }
2440 .to_json_string();
2441
2442 state.stdout(msg)?;
2446 Ok(true)
2447}
2448
2449impl ManifestErrorContext {
2450 fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> ManifestErrorContext {
2451 let mut duplicates = HashSet::default();
2452 let mut rename_table = HashMap::default();
2453
2454 for dep in build_runner.unit_deps(unit) {
2455 let unrenamed_id = dep.unit.pkg.package_id().name();
2456 if duplicates.contains(&unrenamed_id) {
2457 continue;
2458 }
2459 match rename_table.entry(unrenamed_id) {
2460 std::collections::hash_map::Entry::Occupied(occ) => {
2461 occ.remove_entry();
2462 duplicates.insert(unrenamed_id);
2463 }
2464 std::collections::hash_map::Entry::Vacant(vac) => {
2465 vac.insert(dep.extern_crate_name);
2466 }
2467 }
2468 }
2469
2470 let bcx = build_runner.bcx;
2471 ManifestErrorContext {
2472 path: unit.pkg.manifest_path().to_owned(),
2473 spans: unit.pkg.manifest().document_rc(),
2474 contents: unit.pkg.manifest().contents().map(String::from),
2475 requested_kinds: bcx.target_data.requested_kinds().to_owned(),
2476 host_name: bcx.rustc().host,
2477 rename_table,
2478 cwd: path_args(build_runner.bcx.ws, unit).1,
2479 cfgs: bcx
2480 .target_data
2481 .requested_kinds()
2482 .iter()
2483 .map(|k| bcx.target_data.cfg(*k).to_owned())
2484 .collect(),
2485 term_width: bcx
2486 .gctx
2487 .shell()
2488 .err_width()
2489 .diagnostic_terminal_width()
2490 .unwrap_or(cargo_util_terminal::report::renderer::DEFAULT_TERM_WIDTH),
2491 }
2492 }
2493
2494 fn requested_target_names(&self) -> impl Iterator<Item = &str> {
2495 self.requested_kinds.iter().map(|kind| match kind {
2496 CompileKind::Host => &self.host_name,
2497 CompileKind::Target(target) => target.short_name(),
2498 })
2499 }
2500
2501 fn find_crate_span(&self, unrenamed: &str) -> Option<Range<usize>> {
2515 let Some(ref spans) = self.spans else {
2516 return None;
2517 };
2518
2519 let orig_name = self.rename_table.get(unrenamed)?.as_str();
2520
2521 if let Some((k, v)) = get_key_value(&spans, &["dependencies", orig_name]) {
2522 if let Some(package) = v.get_ref().as_table().and_then(|t| t.get("package")) {
2531 return Some(package.span());
2532 } else {
2533 return Some(k.span());
2534 }
2535 }
2536
2537 if let Some(target) = spans
2542 .deref()
2543 .as_ref()
2544 .get("target")
2545 .and_then(|t| t.as_ref().as_table())
2546 {
2547 for (platform, platform_table) in target.iter() {
2548 match platform.as_ref().parse::<Platform>() {
2549 Ok(Platform::Name(name)) => {
2550 if !self.requested_target_names().any(|n| n == name) {
2551 continue;
2552 }
2553 }
2554 Ok(Platform::Cfg(cfg_expr)) => {
2555 if !self.cfgs.iter().any(|cfgs| cfg_expr.matches(cfgs)) {
2556 continue;
2557 }
2558 }
2559 Err(_) => continue,
2560 }
2561
2562 let Some(platform_table) = platform_table.as_ref().as_table() else {
2563 continue;
2564 };
2565
2566 if let Some(deps) = platform_table
2567 .get("dependencies")
2568 .and_then(|d| d.as_ref().as_table())
2569 {
2570 if let Some((k, v)) = deps.get_key_value(orig_name) {
2571 if let Some(package) = v.get_ref().as_table().and_then(|t| t.get("package"))
2572 {
2573 return Some(package.span());
2574 } else {
2575 return Some(k.span());
2576 }
2577 }
2578 }
2579 }
2580 }
2581 None
2582 }
2583}
2584
2585fn replay_output_cache(
2589 package_id: PackageId,
2590 manifest: ManifestErrorContext,
2591 target: &Target,
2592 path: PathBuf,
2593 mut output_options: OutputOptions,
2594) -> Work {
2595 let target = target.clone();
2596 Work::new(move |state| {
2597 if !path.exists() {
2598 return Ok(());
2600 }
2601 let file = paths::open(&path)?;
2605 let mut reader = std::io::BufReader::new(file);
2606 let mut line = String::new();
2607 loop {
2608 let length = reader.read_line(&mut line)?;
2609 if length == 0 {
2610 break;
2611 }
2612 let trimmed = line.trim_end_matches(&['\n', '\r'][..]);
2613 on_stderr_line(
2614 state,
2615 trimmed,
2616 package_id,
2617 &manifest,
2618 &target,
2619 &mut output_options,
2620 )?;
2621 line.clear();
2622 }
2623 Ok(())
2624 })
2625}
2626
2627fn descriptive_pkg_name(name: &str, target: &Target, mode: &CompileMode) -> String {
2630 let desc_name = target.description_named();
2631 let mode = if mode.is_rustc_test() && !(target.is_test() || target.is_bench()) {
2632 " test"
2633 } else if mode.is_doc_test() {
2634 " doctest"
2635 } else if mode.is_doc() {
2636 " doc"
2637 } else {
2638 ""
2639 };
2640 format!("`{name}` ({desc_name}{mode})")
2641}
2642
2643pub(crate) fn apply_env_config(
2645 gctx: &crate::GlobalContext,
2646 cmd: &mut ProcessBuilder,
2647) -> CargoResult<()> {
2648 for (key, value) in gctx.env_config()?.iter() {
2649 if cmd.get_envs().contains_key(key) {
2651 continue;
2652 }
2653 cmd.env(key, value);
2654 }
2655 Ok(())
2656}
2657
2658fn should_include_scrape_units(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
2660 unit.mode.is_doc() && bcx.scrape_units.len() > 0 && bcx.ws.unit_needs_doc_scrape(unit)
2661}
2662
2663fn scrape_output_path(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<PathBuf> {
2665 assert!(unit.mode.is_doc() || unit.mode.is_doc_scrape());
2666 build_runner
2667 .outputs(unit)
2668 .map(|outputs| outputs[0].path.clone())
2669}
2670
2671fn rustdoc_dep_info_loc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> PathBuf {
2673 let mut loc = build_runner.files().fingerprint_file_path(unit, "");
2674 loc.set_extension("d");
2675 loc
2676}