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::collections::BTreeMap;
61use std::env;
62use std::ffi::{OsStr, OsString};
63use std::fmt::Display;
64use std::fs::{self, File};
65use std::io::{BufRead, BufWriter, Write};
66use std::ops::{Deref, Range};
67use std::path::{Path, PathBuf};
68use std::sync::{Arc, LazyLock};
69
70use anyhow::{Context as _, Error};
71use cargo_platform::{Cfg, Platform};
72use cargo_util_terminal::report::{AnnotationKind, Group, Level, Renderer, Snippet};
73use itertools::Itertools;
74use regex::Regex;
75use tracing::{debug, instrument, trace};
76
77pub use self::build_config::UserIntent;
78pub use self::build_config::{BuildConfig, CompileMode, MessageFormat};
79pub use self::build_context::BuildContext;
80pub use self::build_context::DepKindSet;
81pub use self::build_context::FileFlavor;
82pub use self::build_context::FileType;
83pub use self::build_context::RustcTargetData;
84pub use self::build_context::TargetInfo;
85pub use self::build_runner::{BuildRunner, Metadata, UnitHash};
86pub use self::compilation::{Compilation, Doctest, UnitOutput};
87pub use self::compile_kind::{CompileKind, CompileKindFallback, CompileTarget};
88pub use self::crate_type::CrateType;
89pub use self::custom_build::LinkArgTarget;
90pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts, LibraryPath};
91pub(crate) use self::fingerprint::DirtyReason;
92pub use self::fingerprint::RustdocFingerprint;
93pub use self::job_queue::Freshness;
94use self::job_queue::{Job, JobQueue, JobState, Work};
95pub(crate) use self::layout::Layout;
96pub use self::lto::Lto;
97use self::output_depinfo::output_depinfo;
98use self::output_sbom::build_sbom;
99use self::trim_paths::trim_paths_args;
100use self::trim_paths::trim_paths_args_rustdoc;
101use self::unit_graph::UnitDep;
102
103use crate::compiler::future_incompat::FutureIncompatReport;
104use crate::compiler::locking::LockKey;
105use crate::compiler::timings::SectionTiming;
106pub use crate::compiler::unit::Unit;
107pub use crate::compiler::unit::UnitIndex;
108pub use crate::compiler::unit::UnitInterner;
109use crate::diagnostics::get_key_value;
110use crate::util::OnceExt;
111use crate::util::errors::{CargoResult, VerboseError};
112use crate::util::interning::InternedString;
113use crate::util::machine_message::{self, Message};
114use crate::util::{add_path_args, internal, path_args};
115use crate::workspace::manifest::TargetSourcePath;
116use crate::workspace::profiles::{PanicStrategy, Profile, StripInner};
117use crate::workspace::{Feature, PackageId, Target};
118
119use cargo_util::{ProcessBuilder, ProcessError, paths};
120use cargo_util_schemas::manifest::TomlDebugInfo;
121use cargo_util_terminal::Verbosity;
122use rustfix::diagnostics::Applicability;
123
124const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version";
125
126pub trait Executor: Send + Sync + 'static {
130 fn init(&self, _build_runner: &BuildRunner<'_, '_>, _unit: &Unit) {}
134
135 fn exec(
138 &self,
139 cmd: &ProcessBuilder,
140 id: PackageId,
141 target: &Target,
142 mode: CompileMode,
143 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
144 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
145 ) -> CargoResult<()>;
146
147 fn force_rebuild(&self, _unit: &Unit) -> bool {
150 false
151 }
152}
153
154#[derive(Copy, Clone)]
157pub struct DefaultExecutor;
158
159impl Executor for DefaultExecutor {
160 #[instrument(name = "rustc", skip_all, fields(package = id.name().as_str(), process = cmd.to_string()))]
161 fn exec(
162 &self,
163 cmd: &ProcessBuilder,
164 id: PackageId,
165 _target: &Target,
166 _mode: CompileMode,
167 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
168 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
169 ) -> CargoResult<()> {
170 cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
171 .map(drop)
172 }
173}
174
175#[tracing::instrument(skip(build_runner, jobs, exec))]
185fn compile<'gctx>(
186 build_runner: &mut BuildRunner<'_, 'gctx>,
187 jobs: &mut JobQueue<'gctx>,
188 unit: &Unit,
189 exec: &Arc<dyn Executor>,
190 force_rebuild: bool,
191) -> CargoResult<()> {
192 if !build_runner.compiled.insert(unit.clone()) {
193 return Ok(());
194 }
195
196 let lock = if build_runner.bcx.gctx.cli_unstable().fine_grain_locking {
197 Some(build_runner.lock_manager.lock_shared(build_runner, unit)?)
198 } else {
199 None
200 };
201
202 if !unit.skip_non_compile_time_dep {
206 fingerprint::prepare_init(build_runner, unit)?;
209
210 let job = if unit.mode.is_run_custom_build() {
211 custom_build::prepare(build_runner, unit)?
212 } else if unit.mode.is_doc_test() {
213 Job::new_fresh()
215 } else {
216 let force = exec.force_rebuild(unit) || force_rebuild;
217 let mut job = fingerprint::prepare_target(build_runner, unit, force)?;
218 job.before(if job.freshness().is_dirty() {
219 let work = if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
220 rustdoc(build_runner, unit)?
221 } else {
222 rustc(build_runner, unit, exec)?
223 };
224 work.then(link_targets(build_runner, unit, false)?)
225 } else {
226 let output_options = OutputOptions::for_fresh(build_runner, unit);
227 let manifest = ManifestErrorContext::new(build_runner, unit);
228 let work = replay_output_cache(
229 unit.pkg.package_id(),
230 manifest,
231 &unit.target,
232 build_runner.files().message_cache_path(unit),
233 output_options,
234 );
235 work.then(link_targets(build_runner, unit, true)?)
237 });
238
239 if build_runner.bcx.gctx.cli_unstable().fine_grain_locking && job.freshness().is_dirty()
242 {
243 if let Some(lock) = lock {
244 build_runner.lock_manager.unlock(&lock)?;
251 job.before(prebuild_lock_exclusive(lock.clone()));
252 job.after(downgrade_lock_to_shared(lock));
253 }
254 }
255
256 job
257 };
258 jobs.enqueue(build_runner, unit, job)?;
259 }
260
261 let deps = Vec::from(build_runner.unit_deps(unit)); for dep in deps {
264 compile(build_runner, jobs, &dep.unit, exec, false)?;
265 }
266
267 Ok(())
268}
269
270fn make_failed_scrape_diagnostic(
273 build_runner: &BuildRunner<'_, '_>,
274 unit: &Unit,
275 top_line: impl Display,
276) -> String {
277 let manifest_path = unit.pkg.manifest_path();
278 let relative_manifest_path = manifest_path
279 .strip_prefix(build_runner.bcx.ws.root())
280 .unwrap_or(&manifest_path);
281
282 format!(
283 "\
284{top_line}
285 Try running with `--verbose` to see the error message.
286 If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in {}",
287 relative_manifest_path.display()
288 )
289}
290
291fn rustc(
293 build_runner: &mut BuildRunner<'_, '_>,
294 unit: &Unit,
295 exec: &Arc<dyn Executor>,
296) -> CargoResult<Work> {
297 let mut rustc = prepare_rustc(build_runner, unit)?;
298
299 let name = unit.pkg.name();
300
301 let outputs = build_runner.outputs(unit)?;
302 let root = build_runner.files().output_dir(unit);
303
304 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
306 let current_id = unit.pkg.package_id();
307 let manifest = ManifestErrorContext::new(build_runner, unit);
308 let build_scripts = build_runner.build_scripts.get(unit).cloned();
309
310 let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib());
313
314 let dep_info_name =
315 if let Some(c_extra_filename) = build_runner.files().metadata(unit).c_extra_filename() {
316 format!("{}-{}.d", unit.target.crate_name(), c_extra_filename)
317 } else {
318 format!("{}.d", unit.target.crate_name())
319 };
320 let rustc_dep_info_loc = root.join(dep_info_name);
321 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
322
323 let mut output_options = OutputOptions::for_dirty(build_runner, unit);
324 let package_id = unit.pkg.package_id();
325 let target = Target::clone(&unit.target);
326 let mode = unit.mode;
327
328 exec.init(build_runner, unit);
329 let exec = exec.clone();
330
331 let root_output = build_runner.files().host_dest().map(|v| v.to_path_buf());
332 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
333 let pkg_root = unit.pkg.root().to_path_buf();
334 let cwd = rustc
335 .get_cwd()
336 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
337 .to_path_buf();
338 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
339 let script_metadatas = build_runner.find_build_script_metadatas(unit);
340 let is_local = unit.is_local();
341 let artifact = unit.artifact;
342 let sbom_files = build_runner.sbom_output_files(unit)?;
343 let sbom = build_sbom(build_runner, unit)?;
344
345 let unremap_files = build_runner.unremap_output_files(unit)?;
346 let unremap_content = if unremap_files.is_empty() {
347 None
348 } else {
349 let mut buf = Vec::new();
350 trim_paths::write_unremap_file(&mut buf, build_runner, unit)?;
351 Some(buf)
352 };
353
354 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
355 && !matches!(
356 build_runner.bcx.gctx.shell().verbosity(),
357 Verbosity::Verbose
358 );
359 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
360 let target_desc = unit.target.description_named();
363 let mut for_scrape_units = build_runner
364 .bcx
365 .scrape_units_have_dep_on(unit)
366 .into_iter()
367 .map(|unit| unit.target.description_named())
368 .collect::<Vec<_>>();
369 for_scrape_units.sort();
370 let for_scrape_units = for_scrape_units.join(", ");
371 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}"))
372 });
373 if hide_diagnostics_for_scrape_unit {
374 output_options.show_diagnostics = false;
375 }
376 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
377 return Ok(Work::new(move |state| {
378 if artifact.is_true() {
382 paths::create_dir_all(&root)?;
383 }
384
385 if let Some(build_scripts) = build_scripts {
393 let script_outputs = build_script_outputs.lock().unwrap();
394 add_native_deps(
395 &mut rustc,
396 &script_outputs,
397 &build_scripts,
398 pass_l_flag,
399 &target,
400 current_id,
401 mode,
402 )?;
403 if let Some(ref root_output) = root_output {
404 add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, root_output)?;
405 }
406 add_custom_flags(&mut rustc, &script_outputs, script_metadatas)?;
407 }
408
409 for output in outputs.iter() {
410 if output.path.extension() == Some(OsStr::new("rmeta")) {
414 let dst = root.join(&output.path).with_extension("rlib");
415 if dst.exists() {
416 paths::remove_file(&dst)?;
417 }
418 }
419
420 if output.hardlink.is_some() && output.path.exists() {
425 _ = paths::remove_file(&output.path).map_err(|e| {
426 tracing::debug!(
427 "failed to delete previous output file `{:?}`: {e:?}",
428 output.path
429 );
430 });
431 }
432 }
433
434 state.running(&rustc);
435 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
436 for file in sbom_files {
437 tracing::debug!("writing sbom to {}", file.display());
438 let outfile = BufWriter::new(paths::create(&file)?);
439 serde_json::to_writer(outfile, &sbom)?;
440 }
441
442 if let Some(content) = &unremap_content {
443 for file in &unremap_files {
444 tracing::debug!("writing unremap file to {}", file.display());
445 paths::write_atomic(file, content)?;
446 }
447 }
448
449 let result = exec
450 .exec(
451 &rustc,
452 package_id,
453 &target,
454 mode,
455 &mut |line| on_stdout_line(state, line, package_id, &target),
456 &mut |line| {
457 on_stderr_line(
458 state,
459 line,
460 package_id,
461 &manifest,
462 &target,
463 &mut output_options,
464 )
465 },
466 )
467 .map_err(|e| {
468 if output_options.errors_seen == 0 {
469 e
474 } else {
475 verbose_if_simple_exit_code(e)
476 }
477 })
478 .with_context(|| {
479 let warnings = match output_options.warnings_seen {
481 0 => String::new(),
482 1 => "; 1 warning emitted".to_string(),
483 count => format!("; {} warnings emitted", count),
484 };
485 let errors = match output_options.errors_seen {
486 0 => String::new(),
487 1 => " due to 1 previous error".to_string(),
488 count => format!(" due to {} previous errors", count),
489 };
490 let name = descriptive_pkg_name(&name, &target, &mode);
491 format!("could not compile {name}{errors}{warnings}")
492 });
493
494 if let Err(e) = result {
495 if let Some(diagnostic) = failed_scrape_diagnostic {
496 state.warning(diagnostic);
497 }
498
499 return Err(e);
500 }
501
502 debug_assert_eq!(output_options.errors_seen, 0);
504
505 if rustc_dep_info_loc.exists() {
506 fingerprint::translate_dep_info(
507 &rustc_dep_info_loc,
508 &dep_info_loc,
509 &cwd,
510 &pkg_root,
511 &build_dir,
512 &rustc,
513 is_local,
515 &env_config,
516 )
517 .with_context(|| {
518 internal(format!(
519 "could not parse/generate dep info at: {}",
520 rustc_dep_info_loc.display()
521 ))
522 })?;
523 paths::set_file_time_no_err(dep_info_loc, timestamp);
526 }
527
528 if mode.is_check() {
542 for output in outputs.iter() {
543 paths::set_file_time_no_err(&output.path, timestamp);
544 }
545 }
546
547 Ok(())
548 }));
549
550 fn add_native_deps(
553 rustc: &mut ProcessBuilder,
554 build_script_outputs: &BuildScriptOutputs,
555 build_scripts: &BuildScripts,
556 pass_l_flag: bool,
557 target: &Target,
558 current_id: PackageId,
559 mode: CompileMode,
560 ) -> CargoResult<()> {
561 let mut library_paths = vec![];
562
563 for key in build_scripts.to_link.iter() {
564 let output = build_script_outputs.get(key.1).ok_or_else(|| {
565 internal(format!(
566 "couldn't find build script output for {}/{}",
567 key.0, key.1
568 ))
569 })?;
570 library_paths.extend(output.library_paths.iter());
571 }
572
573 library_paths.sort_by_key(|p| match p {
579 LibraryPath::CargoArtifact(_) => 0,
580 LibraryPath::External(_) => 1,
581 });
582
583 for path in library_paths.iter() {
584 rustc.arg("-L").arg(path.as_ref());
585 }
586
587 for key in build_scripts.to_link.iter() {
588 let output = build_script_outputs.get(key.1).ok_or_else(|| {
589 internal(format!(
590 "couldn't find build script output for {}/{}",
591 key.0, key.1
592 ))
593 })?;
594
595 if key.0 == current_id {
596 if pass_l_flag {
597 for name in output.library_links.iter() {
598 rustc.arg("-l").arg(name);
599 }
600 }
601 }
602
603 for (lt, arg) in &output.linker_args {
604 if lt.applies_to(target, mode)
610 && (key.0 == current_id || *lt == LinkArgTarget::Cdylib)
611 {
612 rustc.arg("-C").arg(format!("link-arg={}", arg));
613 }
614 }
615 }
616 Ok(())
617 }
618}
619
620fn verbose_if_simple_exit_code(err: Error) -> Error {
621 match err
624 .downcast_ref::<ProcessError>()
625 .as_ref()
626 .and_then(|perr| perr.code)
627 {
628 Some(n) if cargo_util::is_simple_exit_code(n) => VerboseError::new(err).into(),
629 _ => err,
630 }
631}
632
633fn prebuild_lock_exclusive(lock: LockKey) -> Work {
634 Work::new(move |state| {
635 state.lock_exclusive(&lock)?;
636 Ok(())
637 })
638}
639
640fn downgrade_lock_to_shared(lock: LockKey) -> Work {
641 Work::new(move |state| {
642 state.downgrade_to_shared(&lock)?;
643 Ok(())
644 })
645}
646
647fn link_targets(
650 build_runner: &mut BuildRunner<'_, '_>,
651 unit: &Unit,
652 fresh: bool,
653) -> CargoResult<Work> {
654 let bcx = build_runner.bcx;
655 let outputs = build_runner.outputs(unit)?;
656 let export_dir = build_runner.files().export_dir();
657 let package_id = unit.pkg.package_id();
658 let manifest_path = PathBuf::from(unit.pkg.manifest_path());
659 let profile = unit.profile.clone();
660 let unit_mode = unit.mode;
661 let features = unit.features.iter().map(|s| s.to_string()).collect();
662 let json_messages = bcx.build_config.emit_json();
663 let executable = build_runner.get_executable(unit)?;
664 let mut target = Target::clone(&unit.target);
665 if let TargetSourcePath::Metabuild = target.src_path() {
666 let path = unit
668 .pkg
669 .manifest()
670 .metabuild_path(build_runner.bcx.ws.build_dir());
671 target.set_src_path(TargetSourcePath::Path(path));
672 }
673
674 Ok(Work::new(move |state| {
675 let mut destinations = vec![];
680 for output in outputs.iter() {
681 let src = &output.path;
682 if !src.exists() {
685 continue;
686 }
687 let Some(dst) = output.hardlink.as_ref() else {
688 destinations.push(src.clone());
689 continue;
690 };
691 destinations.push(dst.clone());
692 paths::link_or_copy(src, dst)?;
693 if let Some(ref path) = output.export_path {
694 let export_dir = export_dir.as_ref().unwrap();
695 paths::create_dir_all(export_dir)?;
696
697 paths::link_or_copy(src, path)?;
698 }
699 }
700
701 if json_messages {
702 let debuginfo = match profile.debuginfo.into_inner() {
703 TomlDebugInfo::None => machine_message::ArtifactDebuginfo::Int(0),
704 TomlDebugInfo::Limited => machine_message::ArtifactDebuginfo::Int(1),
705 TomlDebugInfo::Full => machine_message::ArtifactDebuginfo::Int(2),
706 TomlDebugInfo::LineDirectivesOnly => {
707 machine_message::ArtifactDebuginfo::Named("line-directives-only")
708 }
709 TomlDebugInfo::LineTablesOnly => {
710 machine_message::ArtifactDebuginfo::Named("line-tables-only")
711 }
712 };
713 let art_profile = machine_message::ArtifactProfile {
714 opt_level: profile.opt_level.as_str(),
715 debuginfo: Some(debuginfo),
716 debug_assertions: profile.debug_assertions,
717 overflow_checks: profile.overflow_checks,
718 test: unit_mode.is_any_test(),
719 };
720
721 let msg = machine_message::Artifact {
722 package_id: package_id.to_spec(),
723 manifest_path,
724 target: &target,
725 profile: art_profile,
726 features,
727 filenames: destinations,
728 executable,
729 fresh,
730 }
731 .to_json_string();
732 state.stdout(msg)?;
733 }
734 Ok(())
735 }))
736}
737
738fn add_plugin_deps(
742 rustc: &mut ProcessBuilder,
743 build_script_outputs: &BuildScriptOutputs,
744 build_scripts: &BuildScripts,
745 root_output: &Path,
746) -> CargoResult<()> {
747 let var = paths::dylib_path_envvar();
748 let search_path = rustc.get_env(var).unwrap_or_default();
749 let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>();
750 for (pkg_id, metadata) in &build_scripts.plugins {
751 let output = build_script_outputs
752 .get(*metadata)
753 .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?;
754 search_path.append(&mut filter_dynamic_search_path(
755 output.library_paths.iter().map(AsRef::as_ref),
756 root_output,
757 ));
758 }
759 let search_path = paths::join_paths(&search_path, var)?;
760 rustc.env(var, &search_path);
761 Ok(())
762}
763
764fn get_dynamic_search_path(path: &Path) -> &Path {
765 match path.to_str().and_then(|s| s.split_once("=")) {
766 Some(("native" | "crate" | "dependency" | "framework" | "all", path)) => Path::new(path),
767 _ => path,
768 }
769}
770
771fn filter_dynamic_search_path<'a, I>(paths: I, root_output: &Path) -> Vec<PathBuf>
777where
778 I: Iterator<Item = &'a PathBuf>,
779{
780 let mut search_path = vec![];
781 for dir in paths {
782 let dir = get_dynamic_search_path(dir);
783 if dir.starts_with(&root_output) {
784 search_path.push(dir.to_path_buf());
785 } else {
786 debug!(
787 "Not including path {} in runtime library search path because it is \
788 outside target root {}",
789 dir.display(),
790 root_output.display()
791 );
792 }
793 }
794 search_path
795}
796
797fn prepare_rustc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
804 let gctx = build_runner.bcx.gctx;
805 let is_primary = build_runner.is_primary_package(unit);
806 let is_workspace = build_runner.bcx.ws.is_member(&unit.pkg);
807
808 let mut base = build_runner
809 .compilation
810 .rustc_process(unit, is_primary, is_workspace)?;
811 build_base_args(build_runner, &mut base, unit)?;
812 if unit.pkg.manifest().is_embedded() {
813 if !gctx.cli_unstable().script {
814 anyhow::bail!(
815 "parsing `{}` requires `-Zscript`",
816 unit.pkg.manifest_path().display()
817 );
818 }
819 base.arg("-Z").arg("crate-attr=feature(frontmatter)");
820 base.arg("-Z").arg("crate-attr=allow(unused_features)");
821 }
822
823 base.inherit_jobserver(&build_runner.jobserver);
824 build_deps_args(&mut base, build_runner, unit)?;
825 add_cap_lints(build_runner.bcx, unit, &mut base);
826 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
827 base.args(args);
828 }
829 base.args(&unit.rustflags);
830 if gctx.cli_unstable().binary_dep_depinfo {
831 base.arg("-Z").arg("binary-dep-depinfo");
832 }
833 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
834 base.arg("-Z").arg("checksum-hash-algorithm=blake3");
835 }
836
837 if is_primary {
838 base.env("CARGO_PRIMARY_PACKAGE", "1");
839 let file_list = build_runner.sbom_output_files(unit)?;
840 if !file_list.is_empty() {
841 let file_list = std::env::join_paths(file_list)?;
842 base.env("CARGO_SBOM_PATH", file_list);
843 }
844 }
845
846 if unit.target.is_test() || unit.target.is_bench() {
847 let tmp = build_runner
848 .files()
849 .layout(unit.kind)
850 .build_dir()
851 .prepare_tmp()?;
852 base.env("CARGO_TARGET_TMPDIR", tmp.display().to_string());
853 }
854
855 if build_runner.bcx.gctx.cli_unstable().cargo_lints {
856 base.arg("--force-warn=unused_crate_dependencies");
859 }
860
861 Ok(base)
862}
863
864fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
871 let bcx = build_runner.bcx;
872 let mut rustdoc = build_runner.compilation.rustdoc_process(unit, None)?;
874 let wants_json_output = build_runner.bcx.build_config.intent.wants_doc_json_output();
875 if unit.pkg.manifest().is_embedded() {
876 if !bcx.gctx.cli_unstable().script {
877 anyhow::bail!(
878 "parsing `{}` requires `-Zscript`",
879 unit.pkg.manifest_path().display()
880 );
881 }
882 rustdoc.arg("-Z").arg("crate-attr=feature(frontmatter)");
883 rustdoc.arg("-Z").arg("crate-attr=allow(unused_features)");
884 }
885 rustdoc.inherit_jobserver(&build_runner.jobserver);
886 let crate_name = unit.target.crate_name();
887 rustdoc.arg("--crate-name").arg(&crate_name);
888 add_path_args(bcx.ws, unit, &mut rustdoc);
889 add_cap_lints(bcx, unit, &mut rustdoc);
890
891 unit.kind.add_target_arg(&mut rustdoc);
892
893 let doc_dir = if wants_json_output {
894 build_runner.files().out_dir_new_layout(unit)
898 } else {
899 build_runner.files().output_dir(unit)
900 };
901
902 rustdoc.arg("-o").arg(&doc_dir);
903 rustdoc.args(&features_args(unit));
904 rustdoc.args(&check_cfg_args(unit));
905
906 add_error_format_and_color(build_runner, &mut rustdoc);
907 add_allow_features(build_runner, &mut rustdoc);
908
909 if build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo {
910 let mut arg = if wants_json_output {
913 OsString::from("--emit=dep-info=")
914 } else if build_runner.bcx.gctx.cli_unstable().rustdoc_mergeable_info {
915 OsString::from("--emit=html-non-static-files,dep-info=")
917 } else {
918 OsString::from("--emit=html-static-files,html-non-static-files,dep-info=")
920 };
921 arg.push(rustdoc_dep_info_loc(build_runner, unit));
922 rustdoc.arg(arg);
923
924 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
925 rustdoc.arg("-Z").arg("checksum-hash-algorithm=blake3");
926 }
927 } else if build_runner.bcx.gctx.cli_unstable().rustdoc_mergeable_info && !wants_json_output {
928 rustdoc.arg("--emit=html-non-static-files");
930 }
931
932 if build_runner.bcx.gctx.cli_unstable().rustdoc_mergeable_info && !wants_json_output {
933 rustdoc.arg("-Zunstable-options");
935 let mut arg = OsString::from("--write-doc-meta-dir=");
936 arg.push(build_runner.files().out_dir_new_layout(unit));
938 rustdoc.arg(arg);
939 }
940
941 if let Some(trim_paths) = unit.profile.trim_paths.as_ref() {
942 trim_paths_args_rustdoc(&mut rustdoc, build_runner, unit, trim_paths)?;
943 }
944
945 rustdoc.args(unit.pkg.manifest().lint_rustflags());
946
947 let metadata = build_runner.metadata_for_doc_units[unit];
948 rustdoc
949 .arg("-C")
950 .arg(format!("metadata={}", metadata.c_metadata()));
951
952 if unit.mode.is_doc_scrape() {
953 debug_assert!(build_runner.bcx.scrape_units.contains(unit));
954
955 if unit.target.is_test() {
956 rustdoc.arg("--scrape-tests");
957 }
958
959 rustdoc.arg("-Zunstable-options");
960
961 rustdoc
962 .arg("--scrape-examples-output-path")
963 .arg(scrape_output_path(build_runner, unit)?);
964
965 for pkg in build_runner.bcx.packages.packages() {
967 let names = pkg
968 .targets()
969 .iter()
970 .map(|target| target.crate_name())
971 .collect::<HashSet<_>>();
972 for name in names {
973 rustdoc.arg("--scrape-examples-target-crate").arg(name);
974 }
975 }
976 }
977
978 if should_include_scrape_units(build_runner.bcx, unit) {
979 rustdoc.arg("-Zunstable-options");
980 }
981
982 build_deps_args(&mut rustdoc, build_runner, unit)?;
983 rustdoc::add_root_urls(build_runner, unit, &mut rustdoc)?;
984
985 rustdoc::add_output_format(build_runner, &mut rustdoc)?;
986
987 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
988 rustdoc.args(args);
989 }
990 rustdoc.args(&unit.rustdocflags);
991
992 if !crate_version_flag_already_present(&rustdoc) {
993 append_crate_version_flag(unit, &mut rustdoc);
994 }
995
996 Ok(rustdoc)
997}
998
999fn rustdoc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Work> {
1001 let mut rustdoc = prepare_rustdoc(build_runner, unit)?;
1002
1003 let crate_name = unit.target.crate_name();
1004 let is_json_output = build_runner.bcx.build_config.intent.wants_doc_json_output();
1005 let doc_dir = build_runner.files().output_dir(unit);
1006 paths::create_dir_all(&doc_dir)?;
1010
1011 let target_desc = unit.target.description_named();
1012 let name = unit.pkg.name();
1013 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
1014 let package_id = unit.pkg.package_id();
1015 let target = Target::clone(&unit.target);
1016 let manifest = ManifestErrorContext::new(build_runner, unit);
1017
1018 let rustdoc_dep_info_loc = rustdoc_dep_info_loc(build_runner, unit);
1019 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
1020 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
1021 let pkg_root = unit.pkg.root().to_path_buf();
1022 let cwd = rustdoc
1023 .get_cwd()
1024 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
1025 .to_path_buf();
1026 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
1027 let is_local = unit.is_local();
1028 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
1029 let rustdoc_depinfo_enabled = build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo;
1030
1031 let mut output_options = OutputOptions::for_dirty(build_runner, unit);
1032 let script_metadatas = build_runner.find_build_script_metadatas(unit);
1033 let scrape_outputs = if should_include_scrape_units(build_runner.bcx, unit) {
1034 Some(
1035 build_runner
1036 .bcx
1037 .scrape_units
1038 .iter()
1039 .map(|unit| {
1040 Ok((
1041 build_runner.files().metadata(unit).unit_id(),
1042 scrape_output_path(build_runner, unit)?,
1043 ))
1044 })
1045 .collect::<CargoResult<HashMap<_, _>>>()?,
1046 )
1047 } else {
1048 None
1049 };
1050
1051 let failed_scrape_units = Arc::clone(&build_runner.failed_scrape_units);
1052 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
1053 && !matches!(
1054 build_runner.bcx.gctx.shell().verbosity(),
1055 Verbosity::Verbose
1056 );
1057 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
1058 make_failed_scrape_diagnostic(
1059 build_runner,
1060 unit,
1061 format_args!("failed to scan {target_desc} in package `{name}` for example code usage"),
1062 )
1063 });
1064 if hide_diagnostics_for_scrape_unit {
1065 output_options.show_diagnostics = false;
1066 }
1067
1068 Ok(Work::new(move |state| {
1069 add_custom_flags(
1070 &mut rustdoc,
1071 &build_script_outputs.lock().unwrap(),
1072 script_metadatas,
1073 )?;
1074
1075 if let Some(scrape_outputs) = scrape_outputs {
1080 let failed_scrape_units = failed_scrape_units.lock().unwrap();
1081 for (metadata, output_path) in &scrape_outputs {
1082 if !failed_scrape_units.contains(metadata) {
1083 rustdoc.arg("--with-examples").arg(output_path);
1084 }
1085 }
1086 }
1087
1088 if !is_json_output {
1089 let crate_dir = doc_dir.join(&crate_name);
1090 if crate_dir.exists() {
1091 debug!("removing pre-existing doc directory {:?}", crate_dir);
1094 paths::remove_dir_all(&crate_dir)?;
1095 }
1096 };
1097 state.running(&rustdoc);
1098 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
1099
1100 let result = rustdoc
1101 .exec_with_streaming(
1102 &mut |line| on_stdout_line(state, line, package_id, &target),
1103 &mut |line| {
1104 on_stderr_line(
1105 state,
1106 line,
1107 package_id,
1108 &manifest,
1109 &target,
1110 &mut output_options,
1111 )
1112 },
1113 false,
1114 )
1115 .map_err(verbose_if_simple_exit_code)
1116 .with_context(|| format!("could not document `{}`", name));
1117
1118 if let Err(e) = result {
1119 if let Some(diagnostic) = failed_scrape_diagnostic {
1120 state.warning(diagnostic);
1121 }
1122
1123 return Err(e);
1124 }
1125
1126 if rustdoc_depinfo_enabled && rustdoc_dep_info_loc.exists() {
1127 fingerprint::translate_dep_info(
1128 &rustdoc_dep_info_loc,
1129 &dep_info_loc,
1130 &cwd,
1131 &pkg_root,
1132 &build_dir,
1133 &rustdoc,
1134 is_local,
1136 &env_config,
1137 )
1138 .with_context(|| {
1139 internal(format_args!(
1140 "could not parse/generate dep info at: {}",
1141 rustdoc_dep_info_loc.display()
1142 ))
1143 })?;
1144 paths::set_file_time_no_err(dep_info_loc, timestamp);
1147 }
1148
1149 Ok(())
1150 }))
1151}
1152
1153fn crate_version_flag_already_present(rustdoc: &ProcessBuilder) -> bool {
1156 rustdoc.get_args().any(|flag| {
1157 flag.to_str()
1158 .map_or(false, |flag| flag.starts_with(RUSTDOC_CRATE_VERSION_FLAG))
1159 })
1160}
1161
1162fn append_crate_version_flag(unit: &Unit, rustdoc: &mut ProcessBuilder) {
1163 rustdoc
1164 .arg(RUSTDOC_CRATE_VERSION_FLAG)
1165 .arg(unit.pkg.version().to_string());
1166}
1167
1168enum CapLints {
1169 Allow,
1170 Warn,
1171}
1172
1173fn compute_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit) -> Option<CapLints> {
1174 if !unit.show_warnings(bcx.gctx) {
1177 Some(CapLints::Allow)
1178 } else if !unit.is_local() {
1181 Some(CapLints::Warn)
1182 } else {
1183 None
1184 }
1185}
1186
1187fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuilder) {
1191 if let Some(cap_lints) = compute_cap_lints(bcx, unit) {
1192 match cap_lints {
1193 CapLints::Allow => {
1194 cmd.arg("--cap-lints").arg("allow");
1195 }
1196 CapLints::Warn => {
1197 cmd.arg("--cap-lints").arg("warn");
1198 }
1199 }
1200 }
1201}
1202
1203fn add_allow_features(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1207 if let Some(allow) = &build_runner.bcx.gctx.cli_unstable().allow_features {
1208 use std::fmt::Write;
1209 let mut arg = String::from("-Zallow-features=");
1210 for f in allow {
1211 let _ = write!(&mut arg, "{f},");
1212 }
1213 cmd.arg(arg.trim_end_matches(','));
1214 }
1215}
1216
1217fn add_error_format_and_color(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1228 let enable_timings =
1229 build_runner.bcx.gctx.cli_unstable().section_timings && build_runner.bcx.logger.is_some();
1230 if enable_timings {
1231 cmd.arg("-Zunstable-options");
1232 }
1233
1234 cmd.arg("--error-format=json");
1235
1236 let mut json = String::from("--json=diagnostic-rendered-ansi,artifacts,future-incompat");
1237 if build_runner.bcx.gctx.cli_unstable().cargo_lints {
1238 json.push_str(",unused-externs-silent");
1239 }
1240 if let MessageFormat::Short | MessageFormat::Json { short: true, .. } =
1241 build_runner.bcx.build_config.message_format
1242 {
1243 json.push_str(",diagnostic-short");
1244 } else if build_runner.bcx.gctx.shell().err_unicode()
1245 && build_runner.bcx.gctx.cli_unstable().rustc_unicode
1246 {
1247 json.push_str(",diagnostic-unicode");
1248 }
1249 if enable_timings {
1250 json.push_str(",timings");
1251 }
1252 cmd.arg(json);
1253
1254 let gctx = build_runner.bcx.gctx;
1255 if let Some(width) = gctx.shell().err_width().diagnostic_terminal_width() {
1256 cmd.arg(format!("--diagnostic-width={width}"));
1257 }
1258}
1259
1260fn build_base_args(
1262 build_runner: &BuildRunner<'_, '_>,
1263 cmd: &mut ProcessBuilder,
1264 unit: &Unit,
1265) -> CargoResult<()> {
1266 assert!(!unit.mode.is_run_custom_build());
1267
1268 let bcx = build_runner.bcx;
1269 let Profile {
1270 ref opt_level,
1271 codegen_backend,
1272 codegen_units,
1273 debuginfo,
1274 debug_assertions,
1275 split_debuginfo,
1276 overflow_checks,
1277 rpath,
1278 ref panic,
1279 incremental,
1280 strip,
1281 rustflags: profile_rustflags,
1282 trim_paths,
1283 hint_mostly_unused: profile_hint_mostly_unused,
1284 ..
1285 } = unit.profile.clone();
1286 let hints = unit.pkg.hints().cloned().unwrap_or_default();
1287 let test = unit.mode.is_any_test();
1288
1289 let warn = |msg: &str| {
1290 bcx.gctx.shell().warn(format!(
1291 "{}@{}: {msg}",
1292 unit.pkg.package_id().name(),
1293 unit.pkg.package_id().version()
1294 ))
1295 };
1296 let unit_capped_warn = |msg: &str| {
1297 if unit.show_warnings(bcx.gctx) {
1298 warn(msg)
1299 } else {
1300 Ok(())
1301 }
1302 };
1303
1304 cmd.arg("--crate-name").arg(&unit.target.crate_name());
1305
1306 let edition = unit.target.edition();
1307 edition.cmd_edition_arg(cmd);
1308
1309 add_path_args(bcx.ws, unit, cmd);
1310 add_error_format_and_color(build_runner, cmd);
1311 add_allow_features(build_runner, cmd);
1312
1313 let mut contains_dy_lib = false;
1314 if !test {
1315 for crate_type in &unit.target.rustc_crate_types() {
1316 cmd.arg("--crate-type").arg(crate_type.as_str());
1317 contains_dy_lib |= crate_type == &CrateType::Dylib;
1318 }
1319 }
1320
1321 if unit.mode.is_check() {
1322 cmd.arg("--emit=dep-info,metadata");
1323 } else if !build_runner.bcx.gctx.should_embed_metadata() {
1324 if unit.benefits_from_no_embed_metadata() {
1334 cmd.arg("--emit=dep-info,metadata,link");
1335 cmd.args(&["-Z", "embed-metadata=no"]);
1336 } else {
1337 cmd.arg("--emit=dep-info,link");
1338 }
1339 } else {
1340 if !unit.requires_upstream_objects() {
1344 cmd.arg("--emit=dep-info,metadata,link");
1345 } else {
1346 cmd.arg("--emit=dep-info,link");
1347 }
1348 }
1349
1350 let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
1351 || (contains_dy_lib && !build_runner.is_primary_package(unit));
1352 if prefer_dynamic {
1353 cmd.arg("-C").arg("prefer-dynamic");
1354 }
1355
1356 if opt_level.as_str() != "0" {
1357 cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
1358 }
1359
1360 if *panic != PanicStrategy::Unwind {
1361 cmd.arg("-C").arg(format!("panic={}", panic));
1362 }
1363 if *panic == PanicStrategy::ImmediateAbort {
1364 cmd.arg("-Z").arg("unstable-options");
1365 }
1366
1367 cmd.args(<o_args(build_runner, unit));
1368
1369 if let Some(backend) = codegen_backend {
1370 cmd.arg("-Z").arg(&format!("codegen-backend={}", backend));
1371 }
1372
1373 if let Some(n) = codegen_units {
1374 cmd.arg("-C").arg(&format!("codegen-units={}", n));
1375 }
1376
1377 let debuginfo = debuginfo.into_inner();
1378 if debuginfo != TomlDebugInfo::None {
1380 cmd.arg("-C").arg(format!("debuginfo={debuginfo}"));
1381 if let Some(split) = split_debuginfo {
1388 if build_runner
1389 .bcx
1390 .target_data
1391 .info(unit.kind)
1392 .supports_debuginfo_split(split)
1393 {
1394 cmd.arg("-C").arg(format!("split-debuginfo={split}"));
1395 }
1396 }
1397 }
1398
1399 if let Some(trim_paths) = trim_paths {
1400 trim_paths_args(cmd, build_runner, unit, &trim_paths)?;
1401 }
1402
1403 match compute_cap_lints(bcx, unit) {
1404 None | Some(CapLints::Warn) => {
1405 cmd.args(unit.pkg.manifest().lint_rustflags());
1406 }
1407 Some(CapLints::Allow) => {}
1410 }
1411 cmd.args(&profile_rustflags);
1412
1413 if opt_level.as_str() != "0" {
1417 if debug_assertions {
1418 cmd.args(&["-C", "debug-assertions=on"]);
1419 if !overflow_checks {
1420 cmd.args(&["-C", "overflow-checks=off"]);
1421 }
1422 } else if overflow_checks {
1423 cmd.args(&["-C", "overflow-checks=on"]);
1424 }
1425 } else if !debug_assertions {
1426 cmd.args(&["-C", "debug-assertions=off"]);
1427 if overflow_checks {
1428 cmd.args(&["-C", "overflow-checks=on"]);
1429 }
1430 } else if !overflow_checks {
1431 cmd.args(&["-C", "overflow-checks=off"]);
1432 }
1433
1434 if test && unit.target.harness() {
1435 cmd.arg("--test");
1436
1437 if *panic == PanicStrategy::Abort || *panic == PanicStrategy::ImmediateAbort {
1445 cmd.arg("-Z").arg("panic-abort-tests");
1446 }
1447 } else if test {
1448 cmd.arg("--cfg").arg("test");
1449 }
1450
1451 cmd.args(&features_args(unit));
1452 cmd.args(&check_cfg_args(unit));
1453
1454 let meta = build_runner.files().metadata(unit);
1455 cmd.arg("-C")
1456 .arg(&format!("metadata={}", meta.c_metadata()));
1457 if let Some(c_extra_filename) = meta.c_extra_filename() {
1458 cmd.arg("-C")
1459 .arg(&format!("extra-filename=-{c_extra_filename}"));
1460 }
1461
1462 if rpath {
1463 cmd.arg("-C").arg("rpath");
1464 }
1465
1466 cmd.arg("--out-dir")
1467 .arg(&build_runner.files().output_dir(unit));
1468
1469 unit.kind.add_target_arg(cmd);
1470
1471 add_codegen_linker(cmd, build_runner, unit, bcx.gctx.target_applies_to_host()?);
1472
1473 if incremental {
1474 add_codegen_incremental(cmd, build_runner, unit)
1475 }
1476
1477 let pkg_hint_mostly_unused = match hints.mostly_unused {
1478 None => None,
1479 Some(toml::Value::Boolean(b)) => Some(b),
1480 Some(v) => {
1481 unit_capped_warn(&format!(
1482 "ignoring unsupported value type ({}) for 'hints.mostly-unused', which expects a boolean",
1483 v.type_str()
1484 ))?;
1485 None
1486 }
1487 };
1488 if profile_hint_mostly_unused
1489 .or(pkg_hint_mostly_unused)
1490 .unwrap_or(false)
1491 {
1492 if bcx.gctx.cli_unstable().profile_hint_mostly_unused {
1493 cmd.arg("-Zhint-mostly-unused");
1494 } else {
1495 if profile_hint_mostly_unused.is_some() {
1496 warn(
1498 "ignoring 'hint-mostly-unused' profile option, pass `-Zprofile-hint-mostly-unused` to enable it",
1499 )?;
1500 } else if pkg_hint_mostly_unused.is_some() {
1501 unit_capped_warn(
1502 "ignoring 'hints.mostly-unused', pass `-Zprofile-hint-mostly-unused` to enable it",
1503 )?;
1504 }
1505 }
1506 }
1507
1508 let strip = strip.into_inner();
1509 if strip != StripInner::None {
1510 cmd.arg("-C").arg(format!("strip={}", strip));
1511 }
1512
1513 if unit.is_std {
1514 cmd.arg("-Z")
1520 .arg("force-unstable-if-unmarked")
1521 .env("RUSTC_BOOTSTRAP", "1");
1522 }
1523
1524 if let Some(version) = unit.pkg.manifest().rust_version()
1525 && bcx.gctx.cli_unstable().hint_msrv
1526 {
1527 cmd.arg("-Z").arg(format!("hint-msrv={version}"));
1528 }
1529
1530 Ok(())
1531}
1532
1533fn features_args(unit: &Unit) -> Vec<OsString> {
1535 let mut args = Vec::with_capacity(unit.features.len() * 2);
1536
1537 for feat in &unit.features {
1538 args.push(OsString::from("--cfg"));
1539 args.push(OsString::from(format!("feature=\"{}\"", feat)));
1540 }
1541
1542 args
1543}
1544
1545fn check_cfg_args(unit: &Unit) -> Vec<OsString> {
1547 let gross_cap_estimation = unit.pkg.summary().features().len() * 7 + 25;
1565 let mut arg_feature = OsString::with_capacity(gross_cap_estimation);
1566
1567 arg_feature.push("cfg(feature, values(");
1568 for (i, feature) in unit.pkg.summary().features().keys().enumerate() {
1569 if i != 0 {
1570 arg_feature.push(", ");
1571 }
1572 arg_feature.push("\"");
1573 arg_feature.push(feature);
1574 arg_feature.push("\"");
1575 }
1576 arg_feature.push("))");
1577
1578 vec![
1587 OsString::from("--check-cfg"),
1588 OsString::from("cfg(docsrs,test)"),
1589 OsString::from("--check-cfg"),
1590 arg_feature,
1591 ]
1592}
1593
1594fn lto_args(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> Vec<OsString> {
1596 let mut result = Vec::new();
1597 let mut push = |arg: &str| {
1598 result.push(OsString::from("-C"));
1599 result.push(OsString::from(arg));
1600 };
1601 match build_runner.lto[unit] {
1602 lto::Lto::Run(None) => push("lto"),
1603 lto::Lto::Run(Some(s)) => push(&format!("lto={}", s)),
1604 lto::Lto::Off => {
1605 push("lto=off");
1606 push("embed-bitcode=no");
1607 }
1608 lto::Lto::ObjectAndBitcode => {} lto::Lto::OnlyBitcode => push("linker-plugin-lto"),
1610 lto::Lto::OnlyObject => push("embed-bitcode=no"),
1611 }
1612 result
1613}
1614
1615fn build_deps_args(
1621 cmd: &mut ProcessBuilder,
1622 build_runner: &BuildRunner<'_, '_>,
1623 unit: &Unit,
1624) -> CargoResult<()> {
1625 let bcx = build_runner.bcx;
1626
1627 for arg in lib_search_paths(build_runner, unit)? {
1628 cmd.arg(arg);
1629 }
1630
1631 let deps = build_runner.unit_deps(unit);
1632
1633 if !deps
1637 .iter()
1638 .any(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_linkable())
1639 {
1640 if let Some(dep) = deps.iter().find(|dep| {
1641 !dep.unit.mode.is_doc() && dep.unit.target.is_lib() && !dep.unit.artifact.is_true()
1642 }) {
1643 let dep_name = dep.unit.target.crate_name();
1644 let name = unit.target.crate_name();
1645 bcx.gctx.shell().print_report(&[
1646 Level::WARNING.secondary_title(format!("the package `{dep_name}` provides no linkable target"))
1647 .elements([
1648 Level::NOTE.message(format!("this might cause `{name}` to fail compilation")),
1649 Level::NOTE.message("this warning might turn into a hard error in the future"),
1650 Level::HELP.message(format!("consider adding 'dylib' or 'rlib' to key 'crate-type' in `{dep_name}`'s Cargo.toml"))
1651 ])
1652 ], false)?;
1653 }
1654 }
1655
1656 let mut unstable_opts = false;
1657
1658 let first_custom_build_dep = deps.iter().find(|dep| dep.unit.mode.is_run_custom_build());
1660 if let Some(dep) = first_custom_build_dep {
1661 let out_dir = if bcx.gctx.cli_unstable().build_dir_new_layout {
1662 build_runner.files().out_dir_new_layout(&dep.unit)
1663 } else {
1664 build_runner.files().build_script_out_dir(&dep.unit)
1665 };
1666 cmd.env("OUT_DIR", &out_dir);
1667 }
1668
1669 let is_multiple_build_scripts_enabled = unit
1671 .pkg
1672 .manifest()
1673 .unstable_features()
1674 .require(Feature::multiple_build_scripts())
1675 .is_ok();
1676
1677 if is_multiple_build_scripts_enabled {
1678 for dep in deps {
1679 if dep.unit.mode.is_run_custom_build() {
1680 let out_dir = if bcx.gctx.cli_unstable().build_dir_new_layout {
1681 build_runner.files().out_dir_new_layout(&dep.unit)
1682 } else {
1683 build_runner.files().build_script_out_dir(&dep.unit)
1684 };
1685 let target_name = dep.unit.target.name();
1686 let out_dir_prefix = target_name
1687 .strip_prefix("build-script-")
1688 .unwrap_or(target_name);
1689 let out_dir_name = format!("{out_dir_prefix}_OUT_DIR");
1690 cmd.env(&out_dir_name, &out_dir);
1691 }
1692 }
1693 }
1694 for arg in extern_args(build_runner, unit, &mut unstable_opts)? {
1695 cmd.arg(arg);
1696 }
1697
1698 for (var, env) in artifact::get_env(build_runner, unit, deps)? {
1699 cmd.env(&var, env);
1700 }
1701
1702 if unstable_opts {
1705 cmd.arg("-Z").arg("unstable-options");
1706 }
1707
1708 Ok(())
1709}
1710
1711fn add_dep_arg<'a, 'b: 'a>(
1712 map: &mut BTreeMap<&'a Unit, PathBuf>,
1713 build_runner: &'b BuildRunner<'b, '_>,
1714 unit: &'a Unit,
1715) {
1716 for dep in build_runner.unit_deps(unit) {
1717 if dep.unit.target.is_custom_build() {
1719 continue;
1720 }
1721 if map.contains_key(&dep.unit) {
1722 continue;
1723 }
1724 map.insert(&dep.unit, build_runner.files().deps_dir(&dep.unit));
1725
1726 if dep.unit.target.proc_macro() {
1730 continue;
1731 }
1732 add_dep_arg(map, build_runner, &dep.unit);
1733 }
1734}
1735
1736fn add_custom_flags(
1740 cmd: &mut ProcessBuilder,
1741 build_script_outputs: &BuildScriptOutputs,
1742 metadata_vec: Option<Vec<UnitHash>>,
1743) -> CargoResult<()> {
1744 if let Some(metadata_vec) = metadata_vec {
1745 for metadata in metadata_vec {
1746 if let Some(output) = build_script_outputs.get(metadata) {
1747 for cfg in output.cfgs.iter() {
1748 cmd.arg("--cfg").arg(cfg);
1749 }
1750 for check_cfg in &output.check_cfgs {
1751 cmd.arg("--check-cfg").arg(check_cfg);
1752 }
1753 for (name, value) in output.env.iter() {
1754 cmd.env(name, value);
1755 }
1756 }
1757 }
1758 }
1759
1760 Ok(())
1761}
1762
1763pub fn lib_search_paths(
1765 build_runner: &BuildRunner<'_, '_>,
1766 unit: &Unit,
1767) -> CargoResult<Vec<OsString>> {
1768 let mut lib_search_paths = Vec::new();
1769 if build_runner.bcx.gctx.cli_unstable().build_dir_new_layout {
1770 let mut map = BTreeMap::new();
1771
1772 add_dep_arg(&mut map, build_runner, unit);
1774
1775 let paths = map.into_iter().map(|(_, path)| path).sorted_unstable();
1776
1777 for path in paths {
1778 let mut deps = OsString::from("dependency=");
1779 deps.push(path);
1780 lib_search_paths.extend(["-L".into(), deps]);
1781 }
1782 } else {
1783 let mut deps = OsString::from("dependency=");
1784 deps.push(build_runner.files().deps_dir(unit));
1785 lib_search_paths.extend(["-L".into(), deps]);
1786 }
1787
1788 if !unit.kind.is_host() {
1791 let mut deps = OsString::from("dependency=");
1792 deps.push(build_runner.files().host_deps(unit));
1793 lib_search_paths.extend(["-L".into(), deps]);
1794 }
1795
1796 Ok(lib_search_paths)
1797}
1798
1799fn is_public_dependency_enabled(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> bool {
1800 unit.pkg
1801 .manifest()
1802 .unstable_features()
1803 .require(Feature::public_dependency())
1804 .is_ok()
1805 || build_runner.bcx.gctx.cli_unstable().public_dependency
1806}
1807
1808pub fn extern_args(
1810 build_runner: &BuildRunner<'_, '_>,
1811 unit: &Unit,
1812 unstable_opts: &mut bool,
1813) -> CargoResult<Vec<OsString>> {
1814 let mut result = Vec::new();
1815 let deps = build_runner.unit_deps(unit);
1816
1817 let no_embed_metadata = !build_runner.bcx.gctx.should_embed_metadata();
1818 let public_dependency_enabled = is_public_dependency_enabled(build_runner, unit);
1819
1820 let mut link_to = |dep: &UnitDep,
1822 extern_crate_name: InternedString,
1823 noprelude: bool,
1824 nounused: bool|
1825 -> CargoResult<()> {
1826 let mut value = OsString::new();
1827 let mut opts = Vec::new();
1828 if !dep.public && unit.target.is_lib() && public_dependency_enabled {
1829 opts.push("priv");
1830 *unstable_opts = true;
1831 }
1832 if noprelude {
1833 opts.push("noprelude");
1834 *unstable_opts = true;
1835 }
1836 if nounused {
1837 opts.push("nounused");
1838 *unstable_opts = true;
1839 }
1840 if !opts.is_empty() {
1841 value.push(opts.join(","));
1842 value.push(":");
1843 }
1844 value.push(extern_crate_name.as_str());
1845 value.push("=");
1846
1847 let mut pass = |file| {
1848 let mut value = value.clone();
1849 value.push(file);
1850 result.push(OsString::from("--extern"));
1851 result.push(value);
1852 };
1853
1854 let outputs = build_runner.outputs(&dep.unit)?;
1855
1856 if build_runner.only_requires_rmeta(unit, &dep.unit) || dep.unit.mode.is_check() {
1857 let output = outputs
1859 .iter()
1860 .find(|output| output.flavor == FileFlavor::Rmeta)
1861 .expect("failed to find rmeta dep for pipelined dep");
1862 pass(&output.path);
1863 } else {
1864 for output in outputs.iter() {
1866 if output.flavor == FileFlavor::Linkable {
1867 pass(&output.path);
1868 }
1869 else if no_embed_metadata && output.flavor == FileFlavor::Rmeta {
1873 pass(&output.path);
1874 }
1875 }
1876 }
1877 Ok(())
1878 };
1879
1880 for dep in deps {
1881 if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
1882 link_to(dep, dep.extern_crate_name, dep.noprelude, dep.nounused)?;
1883 }
1884 }
1885 if unit.target.proc_macro() {
1886 result.push(OsString::from("--extern"));
1888 result.push(OsString::from("proc_macro"));
1889 }
1890
1891 Ok(result)
1892}
1893
1894fn add_codegen_linker(
1896 cmd: &mut ProcessBuilder,
1897 build_runner: &BuildRunner<'_, '_>,
1898 unit: &Unit,
1899 target_applies_to_host: bool,
1900) {
1901 let linker = if unit.target.for_host() && !target_applies_to_host {
1902 build_runner
1903 .compilation
1904 .host_linker()
1905 .map(|s| s.as_os_str())
1906 } else {
1907 build_runner
1908 .compilation
1909 .target_linker(unit.kind)
1910 .map(|s| s.as_os_str())
1911 };
1912
1913 if let Some(linker) = linker {
1914 let mut arg = OsString::from("linker=");
1915 arg.push(linker);
1916 cmd.arg("-C").arg(arg);
1917 }
1918}
1919
1920fn add_codegen_incremental(
1922 cmd: &mut ProcessBuilder,
1923 build_runner: &BuildRunner<'_, '_>,
1924 unit: &Unit,
1925) {
1926 let dir = build_runner.files().incremental_dir(&unit);
1927 let mut arg = OsString::from("incremental=");
1928 arg.push(dir.as_os_str());
1929 cmd.arg("-C").arg(arg);
1930}
1931
1932fn envify(s: &str) -> String {
1933 s.chars()
1934 .flat_map(|c| c.to_uppercase())
1935 .map(|c| if c == '-' { '_' } else { c })
1936 .collect()
1937}
1938
1939struct OutputOptions {
1942 format: MessageFormat,
1944 cache_cell: Option<(PathBuf, OnceCell<File>)>,
1949 show_diagnostics: bool,
1957 warnings_seen: usize,
1959 errors_seen: usize,
1961}
1962
1963impl OutputOptions {
1964 fn for_dirty(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions {
1965 let path = build_runner.files().message_cache_path(unit);
1966 drop(fs::remove_file(&path));
1968 let cache_cell = Some((path, OnceCell::new()));
1969
1970 let show_diagnostics = true;
1971
1972 let format = build_runner.bcx.build_config.message_format;
1973
1974 OutputOptions {
1975 format,
1976 cache_cell,
1977 show_diagnostics,
1978 warnings_seen: 0,
1979 errors_seen: 0,
1980 }
1981 }
1982
1983 fn for_fresh(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions {
1984 let cache_cell = None;
1985
1986 let show_diagnostics = unit.show_warnings(build_runner.bcx.gctx);
1989
1990 let format = build_runner.bcx.build_config.message_format;
1991
1992 OutputOptions {
1993 format,
1994 cache_cell,
1995 show_diagnostics,
1996 warnings_seen: 0,
1997 errors_seen: 0,
1998 }
1999 }
2000}
2001
2002struct ManifestErrorContext {
2008 path: PathBuf,
2010 spans: Option<Arc<toml::Spanned<toml::de::DeTable<'static>>>>,
2012 contents: Option<String>,
2014 rename_table: HashMap<InternedString, InternedString>,
2017 requested_kinds: Vec<CompileKind>,
2020 cfgs: Vec<Vec<Cfg>>,
2023 host_name: InternedString,
2024 cwd: PathBuf,
2026 term_width: usize,
2028}
2029
2030fn on_stdout_line(
2031 state: &JobState<'_, '_>,
2032 line: &str,
2033 _package_id: PackageId,
2034 _target: &Target,
2035) -> CargoResult<()> {
2036 state.stdout(line.to_string())?;
2037 Ok(())
2038}
2039
2040fn on_stderr_line(
2041 state: &JobState<'_, '_>,
2042 line: &str,
2043 package_id: PackageId,
2044 manifest: &ManifestErrorContext,
2045 target: &Target,
2046 options: &mut OutputOptions,
2047) -> CargoResult<()> {
2048 if on_stderr_line_inner(state, line, package_id, manifest, target, options)? {
2049 if let Some((path, cell)) = &mut options.cache_cell {
2051 let f = cell.try_borrow_mut_with(|| paths::create(path))?;
2053 debug_assert!(!line.contains('\n'));
2054 f.write_all(line.as_bytes())?;
2055 f.write_all(&[b'\n'])?;
2056 }
2057 }
2058 Ok(())
2059}
2060
2061fn on_stderr_line_inner(
2063 state: &JobState<'_, '_>,
2064 line: &str,
2065 package_id: PackageId,
2066 manifest: &ManifestErrorContext,
2067 target: &Target,
2068 options: &mut OutputOptions,
2069) -> CargoResult<bool> {
2070 if !line.starts_with('{') {
2076 state.stderr(line.to_string())?;
2077 return Ok(true);
2078 }
2079
2080 let mut compiler_message: Box<serde_json::value::RawValue> = match serde_json::from_str(line) {
2081 Ok(msg) => msg,
2082
2083 Err(e) => {
2087 debug!("failed to parse json: {:?}", e);
2088 state.stderr(line.to_string())?;
2089 return Ok(true);
2090 }
2091 };
2092
2093 let count_diagnostic = |level, options: &mut OutputOptions| {
2094 if level == "warning" {
2095 options.warnings_seen += 1;
2096 } else if level == "error" {
2097 options.errors_seen += 1;
2098 }
2099 };
2100
2101 if let Ok(report) = serde_json::from_str::<FutureIncompatReport>(compiler_message.get()) {
2102 for item in &report.future_incompat_report {
2103 count_diagnostic(&*item.diagnostic.level, options);
2104 }
2105 state.future_incompat_report(report.future_incompat_report);
2106 return Ok(true);
2107 }
2108
2109 let res = serde_json::from_str::<SectionTiming>(compiler_message.get());
2110 if let Ok(timing_record) = res {
2111 state.on_section_timing_emitted(timing_record);
2112 return Ok(false);
2113 }
2114
2115 let add_pub_in_priv_diagnostic = |diag: &mut String| -> bool {
2117 static PRIV_DEP_REGEX: LazyLock<Regex> =
2126 LazyLock::new(|| Regex::new("from private dependency '([A-Za-z0-9-_]+)'").unwrap());
2127 if let Some(crate_name) = PRIV_DEP_REGEX.captures(diag).and_then(|m| m.get(1))
2128 && let Some(ref contents) = manifest.contents
2129 && let Some(span) = manifest.find_crate_span(crate_name.as_str())
2130 {
2131 let rel_path = pathdiff::diff_paths(&manifest.path, &manifest.cwd)
2132 .unwrap_or_else(|| manifest.path.clone())
2133 .display()
2134 .to_string();
2135 let report = [Group::with_title(Level::NOTE.secondary_title(format!(
2136 "dependency `{}` declared here",
2137 crate_name.as_str()
2138 )))
2139 .element(
2140 Snippet::source(contents)
2141 .path(rel_path)
2142 .annotation(AnnotationKind::Context.span(span)),
2143 )];
2144
2145 let rendered = Renderer::styled()
2146 .term_width(manifest.term_width)
2147 .render(&report);
2148 diag.push_str(&rendered);
2149 diag.push('\n');
2150 return true;
2151 }
2152 false
2153 };
2154
2155 match options.format {
2158 MessageFormat::Human
2163 | MessageFormat::Short
2164 | MessageFormat::Json {
2165 render_diagnostics: true,
2166 ..
2167 } => {
2168 #[derive(serde::Deserialize)]
2169 struct CompilerMessage<'a> {
2170 rendered: String,
2174 #[serde(borrow)]
2175 message: Cow<'a, str>,
2176 #[serde(borrow)]
2177 level: Cow<'a, str>,
2178 children: Vec<PartialDiagnostic>,
2179 code: Option<DiagnosticCode>,
2180 }
2181
2182 #[derive(serde::Deserialize)]
2191 struct PartialDiagnostic {
2192 spans: Vec<PartialDiagnosticSpan>,
2193 }
2194
2195 #[derive(serde::Deserialize)]
2197 struct PartialDiagnosticSpan {
2198 suggestion_applicability: Option<Applicability>,
2199 }
2200
2201 #[derive(serde::Deserialize)]
2202 struct DiagnosticCode {
2203 code: String,
2204 }
2205
2206 if let Ok(mut msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2207 {
2208 if msg.message.starts_with("aborting due to")
2209 || msg.message.ends_with("warning emitted")
2210 || msg.message.ends_with("warnings emitted")
2211 {
2212 return Ok(true);
2214 }
2215 if msg.rendered.ends_with('\n') {
2217 msg.rendered.pop();
2218 }
2219 let mut rendered = msg.rendered;
2220 if options.show_diagnostics {
2221 let machine_applicable: bool = msg
2222 .children
2223 .iter()
2224 .map(|child| {
2225 child
2226 .spans
2227 .iter()
2228 .filter_map(|span| span.suggestion_applicability)
2229 .any(|app| app == Applicability::MachineApplicable)
2230 })
2231 .any(|b| b);
2232 count_diagnostic(&msg.level, options);
2233 if msg
2234 .code
2235 .as_ref()
2236 .is_some_and(|c| c.code == "exported_private_dependencies")
2237 && options.format != MessageFormat::Short
2238 {
2239 add_pub_in_priv_diagnostic(&mut rendered);
2240 }
2241 let lint = msg.code.is_some();
2242 state.emit_diag(&msg.level, rendered, lint, machine_applicable)?;
2243 }
2244 return Ok(true);
2245 }
2246 }
2247
2248 MessageFormat::Json { ansi, .. } => {
2249 #[derive(serde::Deserialize, serde::Serialize)]
2250 struct CompilerMessage<'a> {
2251 rendered: String,
2252 #[serde(flatten, borrow)]
2253 other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2254 code: Option<DiagnosticCode<'a>>,
2255 }
2256
2257 #[derive(serde::Deserialize, serde::Serialize)]
2258 struct DiagnosticCode<'a> {
2259 code: String,
2260 #[serde(flatten, borrow)]
2261 other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2262 }
2263
2264 if let Ok(mut error) =
2265 serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2266 {
2267 let modified_diag = if error
2268 .code
2269 .as_ref()
2270 .is_some_and(|c| c.code == "exported_private_dependencies")
2271 {
2272 add_pub_in_priv_diagnostic(&mut error.rendered)
2273 } else {
2274 false
2275 };
2276
2277 if !ansi {
2281 error.rendered = anstream::adapter::strip_str(&error.rendered).to_string();
2282 }
2283 if !ansi || modified_diag {
2284 let new_line = serde_json::to_string(&error)?;
2285 compiler_message = serde_json::value::RawValue::from_string(new_line)?;
2286 }
2287 }
2288 }
2289 }
2290
2291 #[derive(serde::Deserialize)]
2298 struct ArtifactNotification<'a> {
2299 #[serde(borrow)]
2300 artifact: Cow<'a, str>,
2301 }
2302
2303 if let Ok(artifact) = serde_json::from_str::<ArtifactNotification<'_>>(compiler_message.get()) {
2304 trace!("found directive from rustc: `{}`", artifact.artifact);
2305 if artifact.artifact.ends_with(".rmeta") {
2306 debug!("looks like metadata finished early!");
2307 state.rmeta_produced();
2308 }
2309 return Ok(false);
2310 }
2311
2312 #[derive(serde::Deserialize)]
2313 struct UnusedExterns {
2314 unused_extern_names: std::collections::BTreeSet<InternedString>,
2315 }
2316 if let Ok(uext) = serde_json::from_str::<UnusedExterns>(compiler_message.get()) {
2317 trace!(
2318 "obtained unused externs list from rustc: `{:?}`",
2319 uext.unused_extern_names
2320 );
2321 state.unused_externs(uext.unused_extern_names);
2322 return Ok(true);
2323 }
2324
2325 if !options.show_diagnostics {
2330 return Ok(true);
2331 }
2332
2333 #[derive(serde::Deserialize)]
2334 struct CompilerMessage<'a> {
2335 #[serde(borrow)]
2336 message: Cow<'a, str>,
2337 #[serde(borrow)]
2338 level: Cow<'a, str>,
2339 }
2340
2341 if let Ok(msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get()) {
2342 if msg.message.starts_with("aborting due to")
2343 || msg.message.ends_with("warning emitted")
2344 || msg.message.ends_with("warnings emitted")
2345 {
2346 return Ok(true);
2348 }
2349 count_diagnostic(&msg.level, options);
2350 }
2351
2352 let msg = machine_message::FromCompiler {
2353 package_id: package_id.to_spec(),
2354 manifest_path: &manifest.path,
2355 target,
2356 message: compiler_message,
2357 }
2358 .to_json_string();
2359
2360 state.stdout(msg)?;
2364 Ok(true)
2365}
2366
2367impl ManifestErrorContext {
2368 fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> ManifestErrorContext {
2369 let mut duplicates = HashSet::default();
2370 let mut rename_table = HashMap::default();
2371
2372 for dep in build_runner.unit_deps(unit) {
2373 let unrenamed_id = dep.unit.pkg.package_id().name();
2374 if duplicates.contains(&unrenamed_id) {
2375 continue;
2376 }
2377 match rename_table.entry(unrenamed_id) {
2378 std::collections::hash_map::Entry::Occupied(occ) => {
2379 occ.remove_entry();
2380 duplicates.insert(unrenamed_id);
2381 }
2382 std::collections::hash_map::Entry::Vacant(vac) => {
2383 vac.insert(dep.extern_crate_name);
2384 }
2385 }
2386 }
2387
2388 let bcx = build_runner.bcx;
2389 ManifestErrorContext {
2390 path: unit.pkg.manifest_path().to_owned(),
2391 spans: unit.pkg.manifest().document_rc(),
2392 contents: unit.pkg.manifest().contents().map(String::from),
2393 requested_kinds: bcx.target_data.requested_kinds().to_owned(),
2394 host_name: bcx.rustc().host,
2395 rename_table,
2396 cwd: path_args(build_runner.bcx.ws, unit).1,
2397 cfgs: bcx
2398 .target_data
2399 .requested_kinds()
2400 .iter()
2401 .map(|k| bcx.target_data.cfg(*k).to_owned())
2402 .collect(),
2403 term_width: bcx
2404 .gctx
2405 .shell()
2406 .err_width()
2407 .diagnostic_terminal_width()
2408 .unwrap_or(cargo_util_terminal::report::renderer::DEFAULT_TERM_WIDTH),
2409 }
2410 }
2411
2412 fn requested_target_names(&self) -> impl Iterator<Item = &str> {
2413 self.requested_kinds.iter().map(|kind| match kind {
2414 CompileKind::Host => &self.host_name,
2415 CompileKind::Target(target) => target.short_name(),
2416 })
2417 }
2418
2419 fn find_crate_span(&self, unrenamed: &str) -> Option<Range<usize>> {
2433 let Some(ref spans) = self.spans else {
2434 return None;
2435 };
2436
2437 let orig_name = self.rename_table.get(unrenamed)?.as_str();
2438
2439 if let Some((k, v)) = get_key_value(&spans, &["dependencies", orig_name]) {
2440 if let Some(package) = v.get_ref().as_table().and_then(|t| t.get("package")) {
2449 return Some(package.span());
2450 } else {
2451 return Some(k.span());
2452 }
2453 }
2454
2455 if let Some(target) = spans
2460 .deref()
2461 .as_ref()
2462 .get("target")
2463 .and_then(|t| t.as_ref().as_table())
2464 {
2465 for (platform, platform_table) in target.iter() {
2466 match platform.as_ref().parse::<Platform>() {
2467 Ok(Platform::Name(name)) => {
2468 if !self.requested_target_names().any(|n| n == name) {
2469 continue;
2470 }
2471 }
2472 Ok(Platform::Cfg(cfg_expr)) => {
2473 if !self.cfgs.iter().any(|cfgs| cfg_expr.matches(cfgs)) {
2474 continue;
2475 }
2476 }
2477 Err(_) => continue,
2478 }
2479
2480 let Some(platform_table) = platform_table.as_ref().as_table() else {
2481 continue;
2482 };
2483
2484 if let Some(deps) = platform_table
2485 .get("dependencies")
2486 .and_then(|d| d.as_ref().as_table())
2487 {
2488 if let Some((k, v)) = deps.get_key_value(orig_name) {
2489 if let Some(package) = v.get_ref().as_table().and_then(|t| t.get("package"))
2490 {
2491 return Some(package.span());
2492 } else {
2493 return Some(k.span());
2494 }
2495 }
2496 }
2497 }
2498 }
2499 None
2500 }
2501}
2502
2503fn replay_output_cache(
2507 package_id: PackageId,
2508 manifest: ManifestErrorContext,
2509 target: &Target,
2510 path: PathBuf,
2511 mut output_options: OutputOptions,
2512) -> Work {
2513 let target = target.clone();
2514 Work::new(move |state| {
2515 if !path.exists() {
2516 return Ok(());
2518 }
2519 let file = paths::open(&path)?;
2523 let mut reader = std::io::BufReader::new(file);
2524 let mut line = String::new();
2525 loop {
2526 let length = reader.read_line(&mut line)?;
2527 if length == 0 {
2528 break;
2529 }
2530 let trimmed = line.trim_end_matches(&['\n', '\r'][..]);
2531 on_stderr_line(
2532 state,
2533 trimmed,
2534 package_id,
2535 &manifest,
2536 &target,
2537 &mut output_options,
2538 )?;
2539 line.clear();
2540 }
2541 Ok(())
2542 })
2543}
2544
2545fn descriptive_pkg_name(name: &str, target: &Target, mode: &CompileMode) -> String {
2548 let desc_name = target.description_named();
2549 let mode = if mode.is_rustc_test() && !(target.is_test() || target.is_bench()) {
2550 " test"
2551 } else if mode.is_doc_test() {
2552 " doctest"
2553 } else if mode.is_doc() {
2554 " doc"
2555 } else {
2556 ""
2557 };
2558 format!("`{name}` ({desc_name}{mode})")
2559}
2560
2561pub(crate) fn apply_env_config(
2563 gctx: &crate::GlobalContext,
2564 cmd: &mut ProcessBuilder,
2565) -> CargoResult<()> {
2566 for (key, value) in gctx.env_config()?.iter() {
2567 if cmd.get_envs().contains_key(key) {
2569 continue;
2570 }
2571 cmd.env(key, value);
2572 }
2573 Ok(())
2574}
2575
2576fn should_include_scrape_units(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
2578 unit.mode.is_doc() && bcx.scrape_units.len() > 0 && bcx.ws.unit_needs_doc_scrape(unit)
2579}
2580
2581fn scrape_output_path(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<PathBuf> {
2583 assert!(unit.mode.is_doc() || unit.mode.is_doc_scrape());
2584 build_runner
2585 .outputs(unit)
2586 .map(|outputs| outputs[0].path.clone())
2587}
2588
2589fn rustdoc_dep_info_loc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> PathBuf {
2591 let mut loc = build_runner.files().fingerprint_file_path(unit, "");
2592 loc.set_extension("d");
2593 loc
2594}