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 cargo_util::collections::{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_schemas::core::CrateType;
72use cargo_util_terminal::report::{AnnotationKind, Group, Level, Renderer, Snippet};
73use regex::Regex;
74use tracing::{debug, instrument, trace};
75
76pub use self::build_config::UserIntent;
77pub use self::build_config::{BuildConfig, CompileMode, MessageFormat};
78pub use self::build_context::BuildContext;
79pub use self::build_context::DepKindSet;
80pub use self::build_context::FileFlavor;
81pub use self::build_context::FileType;
82pub use self::build_context::RustcTargetData;
83pub use self::build_context::TargetInfo;
84pub use self::build_runner::{BuildRunner, Metadata, UnitHash};
85pub use self::compilation::{Compilation, Doctest, UnitOutput};
86pub use self::compile_kind::{CompileKind, CompileKindFallback, CompileTarget};
87pub use self::crate_type::CrateTypeExt;
88pub use self::custom_build::LinkArgTarget;
89pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts, LibraryPath};
90pub(crate) use self::fingerprint::DirtyReason;
91pub use self::fingerprint::RustdocFingerprint;
92pub use self::job_queue::Freshness;
93use self::job_queue::{Job, JobQueue, JobState, Work};
94pub(crate) use self::layout::Layout;
95pub use self::lto::Lto;
96use self::output_depinfo::output_depinfo;
97use self::output_sbom::build_sbom;
98use self::trim_paths::trim_paths_args;
99use self::trim_paths::trim_paths_args_rustdoc;
100use self::unit_graph::UnitDep;
101
102use crate::compiler::future_incompat::FutureIncompatReport;
103use crate::compiler::locking::LockKey;
104use crate::compiler::timings::SectionTiming;
105pub use crate::compiler::unit::Unit;
106pub use crate::compiler::unit::UnitIndex;
107pub use crate::compiler::unit::UnitInterner;
108use crate::context::FingerprintMethod;
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::Feature;
116use crate::workspace::PackageId;
117use crate::workspace::Target;
118use crate::workspace::TargetKind;
119use crate::workspace::manifest::TargetSourcePath;
120use crate::workspace::profiles::{PanicStrategy, Profile, StripInner};
121
122use cargo_util::{ExecStats, ProcessBuilder, ProcessError, paths};
123use cargo_util_schemas::manifest::TomlDebugInfo;
124use cargo_util_terminal::Verbosity;
125use rustfix::diagnostics::Applicability;
126
127const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version";
128
129pub trait Executor: Send + Sync + 'static {
133 fn init(&self, _build_runner: &BuildRunner<'_, '_>, _unit: &Unit) {}
137
138 fn exec(
144 &self,
145 cmd: &ProcessBuilder,
146 id: PackageId,
147 target: &Target,
148 mode: CompileMode,
149 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
150 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
151 capture_rusage: bool,
152 ) -> (ExecStats, CargoResult<()>);
153
154 fn force_rebuild(&self, _unit: &Unit) -> bool {
157 false
158 }
159}
160
161#[derive(Copy, Clone)]
164pub struct DefaultExecutor;
165
166impl Executor for DefaultExecutor {
167 #[instrument(name = "rustc", skip_all, fields(package = id.name().as_str(), process = cmd.to_string()))]
168 fn exec(
169 &self,
170 cmd: &ProcessBuilder,
171 id: PackageId,
172 _target: &Target,
173 _mode: CompileMode,
174 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
175 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
176 capture_rusage: bool,
177 ) -> (ExecStats, CargoResult<()>) {
178 let (stats, result) =
179 cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false, capture_rusage);
180 (stats, result.map(drop))
181 }
182}
183
184#[tracing::instrument(skip(build_runner, jobs, exec))]
194fn compile<'gctx>(
195 build_runner: &mut BuildRunner<'_, 'gctx>,
196 jobs: &mut JobQueue<'gctx>,
197 unit: &Unit,
198 exec: &Arc<dyn Executor>,
199 force_rebuild: bool,
200) -> CargoResult<()> {
201 if !build_runner.compiled.insert(unit.clone()) {
202 return Ok(());
203 }
204
205 let lock = if build_runner.bcx.gctx.cli_unstable().fine_grain_locking {
206 Some(build_runner.lock_manager.lock_shared(build_runner, unit)?)
207 } else {
208 None
209 };
210
211 match build_runner
212 .bcx
213 .gctx
214 .build_config()?
215 .fingerprint
216 .unwrap_or_default()
217 {
218 FingerprintMethod::Content => {
219 if !build_runner.bcx.gctx.cli_unstable().checksum_freshness {
220 build_runner.bcx.gctx.shell().warn(
221 r#"ignoring `build.fingerprint = "content"` without `-Zchecksum-freshness`"#,
222 )?;
223 }
224 }
225 FingerprintMethod::Mtime => {}
226 }
227
228 if !unit.skip_non_compile_time_dep {
232 fingerprint::prepare_init(build_runner, unit)?;
235
236 let job = if unit.mode.is_run_custom_build() {
237 custom_build::prepare(build_runner, unit)?
238 } else if unit.mode.is_doc_test() {
239 Job::new_fresh()
241 } else {
242 let force = exec.force_rebuild(unit) || force_rebuild;
243 let mut job = fingerprint::prepare_target(build_runner, unit, force)?;
244 job.before(if job.freshness().is_dirty() {
245 let work = if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
246 rustdoc(build_runner, unit)?
247 } else {
248 rustc(build_runner, unit, exec)?
249 };
250 work.then(link_targets(build_runner, unit, false)?)
251 } else {
252 let output_options = OutputOptions::for_fresh(build_runner, unit);
253 let manifest = ManifestErrorContext::new(build_runner, unit);
254 let work = replay_output_cache(
255 unit.pkg.package_id(),
256 manifest,
257 &unit.target,
258 build_runner.files().message_cache_path(unit),
259 output_options,
260 );
261 work.then(link_targets(build_runner, unit, true)?)
263 });
264
265 if build_runner.bcx.gctx.cli_unstable().fine_grain_locking && job.freshness().is_dirty()
268 {
269 if let Some(lock) = lock {
270 build_runner.lock_manager.unlock(&lock)?;
277 job.before(prebuild_lock_exclusive(lock.clone()));
278 job.after(downgrade_lock_to_shared(lock));
279 }
280 }
281
282 job
283 };
284 jobs.enqueue(build_runner, unit, job)?;
285 }
286
287 let deps = Vec::from(build_runner.unit_deps(unit)); for dep in deps {
290 compile(build_runner, jobs, &dep.unit, exec, false)?;
291 }
292
293 Ok(())
294}
295
296fn make_failed_scrape_diagnostic(
299 build_runner: &BuildRunner<'_, '_>,
300 unit: &Unit,
301 top_line: impl Display,
302) -> String {
303 let manifest_path = unit.pkg.manifest_path();
304 let relative_manifest_path = manifest_path
305 .strip_prefix(build_runner.bcx.ws.root())
306 .unwrap_or(&manifest_path);
307
308 format!(
309 "\
310{top_line}
311 Try running with `--verbose` to see the error message.
312 If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in {}",
313 relative_manifest_path.display()
314 )
315}
316
317#[tracing::instrument(skip_all)]
319fn rustc(
320 build_runner: &mut BuildRunner<'_, '_>,
321 unit: &Unit,
322 exec: &Arc<dyn Executor>,
323) -> CargoResult<Work> {
324 let mut rustc = prepare_rustc(build_runner, unit)?;
325
326 let name = unit.pkg.name();
327
328 let outputs = build_runner.outputs(unit)?;
329 let root = build_runner.files().output_dir(unit);
330
331 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
333 let current_id = unit.pkg.package_id();
334 let manifest = ManifestErrorContext::new(build_runner, unit);
335 let build_scripts = build_runner.build_scripts.get(unit).cloned();
336
337 let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib());
340
341 let dep_info_name =
342 if let Some(c_extra_filename) = build_runner.files().metadata(unit).c_extra_filename() {
343 format!("{}-{}.d", unit.target.crate_name(), c_extra_filename)
344 } else {
345 format!("{}.d", unit.target.crate_name())
346 };
347 let rustc_dep_info_loc = root.join(dep_info_name);
348 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
349
350 let mut output_options = OutputOptions::for_dirty(build_runner, unit);
351 let package_id = unit.pkg.package_id();
352 let target = Target::clone(&unit.target);
353 let mode = unit.mode;
354 let capture_rusage = build_runner.bcx.capture_rusage();
355
356 exec.init(build_runner, unit);
357 let exec = exec.clone();
358
359 let root_output = build_runner.files().host_dest().map(|v| v.to_path_buf());
360 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
361 let pkg_root = unit.pkg.root().to_path_buf();
362 let ws_root = build_runner.bcx.ws.root().to_path_buf();
363 let cwd = rustc
364 .get_cwd()
365 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
366 .to_path_buf();
367 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
368 let script_metadatas = build_runner.find_build_script_metadatas(unit);
369 let is_local = unit.is_local();
370 let artifact = unit.artifact;
371 let build_dir_new_layout = build_runner.bcx.gctx.cli_unstable().build_dir_new_layout;
372 let sbom_files = build_runner.sbom_output_files(unit)?;
373 let sbom = if !sbom_files.is_empty() {
374 Some(build_sbom(build_runner, unit)?)
375 } else {
376 None
377 };
378
379 let unremap_files = build_runner.unremap_output_files(unit)?;
380 let unremap_content = if unremap_files.is_empty() {
381 None
382 } else {
383 let mut buf = Vec::new();
384 trim_paths::write_unremap_file(&mut buf, build_runner, unit)?;
385 Some(buf)
386 };
387
388 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
389 && !matches!(
390 build_runner.bcx.gctx.shell().verbosity(),
391 Verbosity::Verbose
392 );
393 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
394 let target_desc = unit.target.description_named();
397 let mut for_scrape_units = build_runner
398 .bcx
399 .scrape_units_have_dep_on(unit)
400 .into_iter()
401 .map(|unit| unit.target.description_named())
402 .collect::<Vec<_>>();
403 for_scrape_units.sort();
404 let for_scrape_units = for_scrape_units.join(", ");
405 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}"))
406 });
407 if hide_diagnostics_for_scrape_unit {
408 output_options.show_diagnostics = false;
409 }
410 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
411 return Ok(Work::new(move |state| {
412 if build_dir_new_layout {
413 if root.exists() {
416 paths::remove_dir_all(&root)?;
417 }
418 paths::create_dir_all(&root)?;
419 } else if artifact.is_true() {
420 paths::create_dir_all(&root)?;
424 }
425
426 if let Some(build_scripts) = build_scripts {
434 let script_outputs = build_script_outputs.lock().unwrap();
435 add_native_deps(
436 &mut rustc,
437 &script_outputs,
438 &build_scripts,
439 pass_l_flag,
440 &target,
441 current_id,
442 mode,
443 )?;
444 if let Some(ref root_output) = root_output {
445 add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, root_output)?;
446 }
447 add_custom_flags(&mut rustc, &script_outputs, script_metadatas)?;
448 }
449
450 if !build_dir_new_layout {
451 for output in outputs.iter() {
452 if output.path.extension() == Some(OsStr::new("rmeta")) {
456 let dst = root.join(&output.path).with_extension("rlib");
457 if dst.exists() {
458 paths::remove_file(&dst)?;
459 }
460 }
461
462 if output.hardlink.is_some() && output.path.exists() {
467 _ = paths::remove_file(&output.path).map_err(|e| {
468 tracing::debug!(
469 "failed to delete previous output file `{:?}`: {e:?}",
470 output.path
471 );
472 });
473 }
474 }
475 }
476
477 state.running(&rustc);
478 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
479 if let Some(sbom) = sbom {
480 for file in sbom_files {
481 tracing::debug!("writing sbom to {}", file.display());
482 let outfile = BufWriter::new(paths::create(&file)?);
483 serde_json::to_writer(outfile, &sbom)?;
484 }
485 }
486
487 if let Some(content) = &unremap_content {
488 for file in &unremap_files {
489 tracing::debug!("writing unremap file to {}", file.display());
490 paths::write_atomic(file, content)?;
491 }
492 }
493
494 let (stats, result) = exec.exec(
495 &rustc,
496 package_id,
497 &target,
498 mode,
499 &mut |line| on_stdout_line(state, line, package_id, &target),
500 &mut |line| {
501 on_stderr_line(
502 state,
503 line,
504 package_id,
505 &manifest,
506 &target,
507 &mut output_options,
508 )
509 },
510 capture_rusage,
511 );
512 let result = result
513 .map_err(|e| {
514 if output_options.errors_seen == 0 {
515 e
520 } else {
521 verbose_if_simple_exit_code(e)
522 }
523 })
524 .with_context(|| {
525 let warnings = match output_options.warnings_seen {
527 0 => String::new(),
528 1 => "; 1 warning emitted".to_string(),
529 count => format!("; {} warnings emitted", count),
530 };
531 let errors = match output_options.errors_seen {
532 0 => String::new(),
533 1 => " due to 1 previous error".to_string(),
534 count => format!(" due to {} previous errors", count),
535 };
536 let name = descriptive_pkg_name(&name, &target, &mode);
537 format!("could not compile {name}{errors}{warnings}")
538 });
539
540 if let Some(peak_memory) = stats.peak_rss {
541 state.peak_memory(peak_memory);
542 }
543
544 if let Err(e) = result {
545 if let Some(diagnostic) = failed_scrape_diagnostic {
546 state.warning(diagnostic);
547 }
548
549 return Err(e);
550 }
551
552 debug_assert_eq!(output_options.errors_seen, 0);
554
555 if rustc_dep_info_loc.exists() {
556 fingerprint::translate_dep_info(
557 &rustc_dep_info_loc,
558 &dep_info_loc,
559 &cwd,
560 &pkg_root,
561 &build_dir,
562 &ws_root,
563 &rustc,
564 is_local,
566 &env_config,
567 )
568 .with_context(|| {
569 internal(format!(
570 "could not parse/generate dep info at: {}",
571 rustc_dep_info_loc.display()
572 ))
573 })?;
574 paths::set_file_time_no_err(dep_info_loc, timestamp);
577 }
578
579 if mode.is_check() {
593 for output in outputs.iter() {
594 paths::set_file_time_no_err(&output.path, timestamp);
595 }
596 }
597
598 Ok(())
599 }));
600
601 fn add_native_deps(
604 rustc: &mut ProcessBuilder,
605 build_script_outputs: &BuildScriptOutputs,
606 build_scripts: &BuildScripts,
607 pass_l_flag: bool,
608 target: &Target,
609 current_id: PackageId,
610 mode: CompileMode,
611 ) -> CargoResult<()> {
612 let mut library_paths = vec![];
613
614 for key in build_scripts.to_link.iter() {
615 let output = build_script_outputs.get(key.1).ok_or_else(|| {
616 internal(format!(
617 "couldn't find build script output for {}/{}",
618 key.0, key.1
619 ))
620 })?;
621 library_paths.extend(output.library_paths.iter());
622 }
623
624 library_paths.sort_by_key(|p| match p {
630 LibraryPath::CargoArtifact(_) => 0,
631 LibraryPath::External(_) => 1,
632 });
633
634 for path in library_paths.iter() {
635 rustc.arg("-L").arg(path.as_ref());
636 }
637
638 for key in build_scripts.to_link.iter() {
639 let output = build_script_outputs.get(key.1).ok_or_else(|| {
640 internal(format!(
641 "couldn't find build script output for {}/{}",
642 key.0, key.1
643 ))
644 })?;
645
646 if key.0 == current_id {
647 if pass_l_flag {
648 for name in output.library_links.iter() {
649 rustc.arg("-l").arg(name);
650 }
651 }
652 }
653
654 for (lt, arg) in &output.linker_args {
655 if lt.applies_to(target, mode)
661 && (key.0 == current_id || *lt == LinkArgTarget::Cdylib)
662 {
663 rustc.arg("-C").arg(format!("link-arg={}", arg));
664 }
665 }
666 }
667 Ok(())
668 }
669}
670
671fn verbose_if_simple_exit_code(err: Error) -> Error {
672 match err
675 .downcast_ref::<ProcessError>()
676 .as_ref()
677 .and_then(|perr| perr.code)
678 {
679 Some(n) if cargo_util::is_simple_exit_code(n) => VerboseError::new(err).into(),
680 _ => err,
681 }
682}
683
684fn prebuild_lock_exclusive(lock: LockKey) -> Work {
685 Work::new(move |state| {
686 state.lock_exclusive(&lock)?;
687 Ok(())
688 })
689}
690
691fn downgrade_lock_to_shared(lock: LockKey) -> Work {
692 Work::new(move |state| {
693 state.downgrade_to_shared(&lock)?;
694 Ok(())
695 })
696}
697
698fn link_targets(
701 build_runner: &mut BuildRunner<'_, '_>,
702 unit: &Unit,
703 fresh: bool,
704) -> CargoResult<Work> {
705 let bcx = build_runner.bcx;
706 let outputs = build_runner.outputs(unit)?;
707 let export_dir = build_runner.files().export_dir();
708 let package_id = unit.pkg.package_id();
709 let manifest_path = PathBuf::from(unit.pkg.manifest_path());
710 let profile = unit.profile.clone();
711 let unit_mode = unit.mode;
712 let features = unit.features.iter().map(|s| s.to_string()).collect();
713 let json_messages = bcx.build_config.emit_json();
714 let executable = build_runner.get_executable(unit)?;
715 let mut target = Target::clone(&unit.target);
716 if let TargetSourcePath::Metabuild = target.src_path() {
717 let path = unit
719 .pkg
720 .manifest()
721 .metabuild_path(build_runner.bcx.ws.build_dir());
722 target.set_src_path(TargetSourcePath::Path(path));
723 }
724
725 Ok(Work::new(move |state| {
726 let mut destinations = vec![];
731 for output in outputs.iter() {
732 let src = &output.path;
733 if !src.exists() {
736 continue;
737 }
738 let Some(dst) = output.hardlink.as_ref() else {
739 destinations.push(src.clone());
740 continue;
741 };
742 destinations.push(dst.clone());
743 paths::link_or_copy(src, dst)?;
744 if let Some(ref path) = output.export_path {
745 let export_dir = export_dir.as_ref().unwrap();
746 paths::create_dir_all(export_dir)?;
747
748 paths::link_or_copy(src, path)?;
749 }
750 }
751
752 if json_messages {
753 let debuginfo = match profile.debuginfo.into_inner() {
754 TomlDebugInfo::None => machine_message::ArtifactDebuginfo::Int(0),
755 TomlDebugInfo::Limited => machine_message::ArtifactDebuginfo::Int(1),
756 TomlDebugInfo::Full => machine_message::ArtifactDebuginfo::Int(2),
757 TomlDebugInfo::LineDirectivesOnly => {
758 machine_message::ArtifactDebuginfo::Named("line-directives-only")
759 }
760 TomlDebugInfo::LineTablesOnly => {
761 machine_message::ArtifactDebuginfo::Named("line-tables-only")
762 }
763 };
764 let art_profile = machine_message::ArtifactProfile {
765 opt_level: profile.opt_level.as_str(),
766 debuginfo: Some(debuginfo),
767 debug_assertions: profile.debug_assertions,
768 overflow_checks: profile.overflow_checks,
769 test: unit_mode.is_any_test(),
770 };
771
772 let msg = machine_message::Artifact {
773 package_id: package_id.to_spec(),
774 manifest_path,
775 target: &target,
776 profile: art_profile,
777 features,
778 filenames: destinations,
779 executable,
780 fresh,
781 }
782 .to_json_string();
783 state.stdout(msg)?;
784 }
785 Ok(())
786 }))
787}
788
789fn add_plugin_deps(
793 rustc: &mut ProcessBuilder,
794 build_script_outputs: &BuildScriptOutputs,
795 build_scripts: &BuildScripts,
796 root_output: &Path,
797) -> CargoResult<()> {
798 let var = paths::dylib_path_envvar();
799 let search_path = rustc.get_env(var).unwrap_or_default();
800 let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>();
801 for (pkg_id, metadata) in &build_scripts.plugins {
802 let output = build_script_outputs
803 .get(*metadata)
804 .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?;
805 search_path.append(&mut filter_dynamic_search_path(
806 output.library_paths.iter().map(AsRef::as_ref),
807 root_output,
808 ));
809 }
810 let search_path = paths::join_paths(&search_path, var)?;
811 rustc.env(var, &search_path);
812 Ok(())
813}
814
815fn get_dynamic_search_path(path: &Path) -> &Path {
816 match path.to_str().and_then(|s| s.split_once("=")) {
817 Some(("native" | "crate" | "dependency" | "framework" | "all", path)) => Path::new(path),
818 _ => path,
819 }
820}
821
822fn filter_dynamic_search_path<'a, I>(paths: I, root_output: &Path) -> Vec<PathBuf>
828where
829 I: Iterator<Item = &'a PathBuf>,
830{
831 let mut search_path = vec![];
832 for dir in paths {
833 let dir = get_dynamic_search_path(dir);
834 if dir.starts_with(&root_output) {
835 search_path.push(dir.to_path_buf());
836 } else {
837 debug!(
838 "Not including path {} in runtime library search path because it is \
839 outside target root {}",
840 dir.display(),
841 root_output.display()
842 );
843 }
844 }
845 search_path
846}
847
848#[tracing::instrument(skip_all)]
855fn prepare_rustc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
856 let gctx = build_runner.bcx.gctx;
857 let is_primary = build_runner.is_primary_package(unit);
858 let is_workspace = build_runner.bcx.ws.is_member(&unit.pkg);
859
860 let mut base = build_runner
861 .compilation
862 .rustc_process(unit, is_primary, is_workspace)?;
863 build_base_args(build_runner, &mut base, unit)?;
864 if unit.pkg.manifest().is_embedded() {
865 if !gctx.cli_unstable().script {
866 anyhow::bail!(
867 "parsing `{}` requires `-Zscript`",
868 unit.pkg.manifest_path().display()
869 );
870 }
871 base.arg("-Z").arg("crate-attr=feature(frontmatter)");
872 base.arg("-Z").arg("crate-attr=allow(unused_features)");
873 }
874
875 base.inherit_jobserver(&build_runner.jobserver);
876 build_deps_args(&mut base, build_runner, unit)?;
877 add_cap_lints(build_runner.bcx, unit, &mut base);
878 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
879 base.args(args);
880 }
881 base.args(&unit.rustflags);
882
883 if unit.target.is_lib() && unit.mode.is_rustc_test() {
886 base.arg("-Adead_code_pub_in_binary");
887 }
888 if gctx.cli_unstable().binary_dep_depinfo {
889 base.arg("-Z").arg("binary-dep-depinfo");
890 }
891 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
892 match build_runner
893 .bcx
894 .gctx
895 .build_config()?
896 .fingerprint
897 .unwrap_or_default()
898 {
899 FingerprintMethod::Content => {
900 base.arg("-Z").arg("checksum-hash-algorithm=blake3");
901 }
902 FingerprintMethod::Mtime => {}
903 }
904 }
905
906 if is_primary {
907 base.env("CARGO_PRIMARY_PACKAGE", "1");
908 let file_list = build_runner.sbom_output_files(unit)?;
909 if !file_list.is_empty() {
910 let file_list = std::env::join_paths(file_list)?;
911 base.env("CARGO_SBOM_PATH", file_list);
912 }
913 }
914
915 if unit.target.is_test() || unit.target.is_bench() {
916 let tmp = build_runner
917 .files()
918 .layout(unit.kind)
919 .build_dir()
920 .prepare_tmp()?;
921 base.env("CARGO_TARGET_TMPDIR", tmp.display().to_string());
922 }
923
924 base.arg("--force-warn=unused_crate_dependencies");
927
928 Ok(base)
929}
930
931#[tracing::instrument(skip_all)]
938fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
939 let bcx = build_runner.bcx;
940 let mut rustdoc = build_runner.compilation.rustdoc_process(unit, None)?;
942 let wants_json_output = build_runner.bcx.build_config.intent.wants_doc_json_output();
943 if unit.pkg.manifest().is_embedded() {
944 if !bcx.gctx.cli_unstable().script {
945 anyhow::bail!(
946 "parsing `{}` requires `-Zscript`",
947 unit.pkg.manifest_path().display()
948 );
949 }
950 rustdoc.arg("-Z").arg("crate-attr=feature(frontmatter)");
951 rustdoc.arg("-Z").arg("crate-attr=allow(unused_features)");
952 }
953 rustdoc.inherit_jobserver(&build_runner.jobserver);
954 let crate_name = unit.target.crate_name();
955 rustdoc.arg("--crate-name").arg(&crate_name);
956 add_path_args(bcx.ws, unit, &mut rustdoc);
957 add_cap_lints(bcx, unit, &mut rustdoc);
958
959 unit.kind.add_target_arg(&mut rustdoc);
960
961 let doc_dir = if wants_json_output {
962 build_runner.files().out_dir_new_layout(unit)
966 } else {
967 build_runner.files().output_dir(unit)
968 };
969
970 rustdoc.arg("-o").arg(&doc_dir);
971 rustdoc.args(&features_args(unit));
972 rustdoc.args(&check_cfg_args(unit));
973
974 add_error_format_and_color(build_runner, &mut rustdoc);
975 add_allow_features(build_runner, &mut rustdoc);
976
977 if build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo {
978 let mut arg = if wants_json_output {
981 OsString::from("--emit=dep-info=")
982 } else if build_runner.bcx.gctx.cli_unstable().rustdoc_mergeable_info {
983 OsString::from("--emit=html-non-static-files,dep-info=")
985 } else {
986 OsString::from("--emit=html-static-files,html-non-static-files,dep-info=")
988 };
989 arg.push(rustdoc_dep_info_loc(build_runner, unit));
990 rustdoc.arg(arg);
991
992 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
993 match build_runner
994 .bcx
995 .gctx
996 .build_config()?
997 .fingerprint
998 .unwrap_or_default()
999 {
1000 FingerprintMethod::Content => {
1001 rustdoc.arg("-Z").arg("checksum-hash-algorithm=blake3");
1002 }
1003 FingerprintMethod::Mtime => {}
1004 }
1005 }
1006 } else if build_runner.bcx.gctx.cli_unstable().rustdoc_mergeable_info && !wants_json_output {
1007 rustdoc.arg("--emit=html-non-static-files");
1009 }
1010
1011 if build_runner.bcx.gctx.cli_unstable().rustdoc_mergeable_info && !wants_json_output {
1012 rustdoc.arg("-Zunstable-options");
1014 let mut arg = OsString::from("--write-doc-meta-dir=");
1015 arg.push(build_runner.files().out_dir_new_layout(unit));
1017 rustdoc.arg(arg);
1018 }
1019
1020 trim_paths_args_rustdoc(&mut rustdoc, build_runner, unit, &unit.profile.trim_paths)?;
1021
1022 rustdoc.args(unit.pkg.manifest().lint_rustflags());
1023
1024 let metadata = build_runner.metadata_for_doc_units[unit];
1025 rustdoc
1026 .arg("-C")
1027 .arg(format!("metadata={}", metadata.c_metadata()));
1028
1029 if unit.mode.is_doc_scrape() {
1030 debug_assert!(build_runner.bcx.scrape_units.contains(unit));
1031
1032 if unit.target.is_test() {
1033 rustdoc.arg("--scrape-tests");
1034 }
1035
1036 rustdoc.arg("-Zunstable-options");
1037
1038 rustdoc
1039 .arg("--scrape-examples-output-path")
1040 .arg(scrape_output_path(build_runner, unit)?);
1041
1042 for pkg in build_runner.bcx.packages.packages() {
1044 let names = pkg
1045 .targets()
1046 .iter()
1047 .map(|target| target.crate_name())
1048 .collect::<HashSet<_>>();
1049 for name in names {
1050 rustdoc.arg("--scrape-examples-target-crate").arg(name);
1051 }
1052 }
1053 }
1054
1055 if should_include_scrape_units(build_runner.bcx, unit) {
1056 rustdoc.arg("-Zunstable-options");
1057 }
1058
1059 build_deps_args(&mut rustdoc, build_runner, unit)?;
1060 rustdoc::add_root_urls(build_runner, unit, &mut rustdoc)?;
1061
1062 rustdoc::add_output_format(build_runner, &mut rustdoc)?;
1063
1064 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
1065 rustdoc.args(args);
1066 }
1067 rustdoc.args(&unit.rustdocflags);
1068
1069 if !crate_version_flag_already_present(&rustdoc) {
1070 append_crate_version_flag(unit, &mut rustdoc);
1071 }
1072
1073 Ok(rustdoc)
1074}
1075
1076#[tracing::instrument(skip_all)]
1078fn rustdoc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Work> {
1079 let mut rustdoc = prepare_rustdoc(build_runner, unit)?;
1080
1081 let crate_name = unit.target.crate_name();
1082 let is_json_output = build_runner.bcx.build_config.intent.wants_doc_json_output();
1083 let doc_dir = build_runner.files().output_dir(unit);
1084 paths::create_dir_all(&doc_dir)?;
1088
1089 let target_desc = unit.target.description_named();
1090 let name = unit.pkg.name();
1091 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
1092 let package_id = unit.pkg.package_id();
1093 let target = Target::clone(&unit.target);
1094 let manifest = ManifestErrorContext::new(build_runner, unit);
1095
1096 let rustdoc_dep_info_loc = rustdoc_dep_info_loc(build_runner, unit);
1097 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
1098 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
1099 let pkg_root = unit.pkg.root().to_path_buf();
1100 let ws_root = build_runner.bcx.ws.root().to_path_buf();
1101 let cwd = rustdoc
1102 .get_cwd()
1103 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
1104 .to_path_buf();
1105 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
1106 let is_local = unit.is_local();
1107 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
1108 let capture_rusage = build_runner.bcx.capture_rusage();
1109 let rustdoc_depinfo_enabled = build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo;
1110
1111 let mut output_options = OutputOptions::for_dirty(build_runner, unit);
1112 let script_metadatas = build_runner.find_build_script_metadatas(unit);
1113 let scrape_outputs = if should_include_scrape_units(build_runner.bcx, unit) {
1114 Some(
1115 build_runner
1116 .bcx
1117 .scrape_units
1118 .iter()
1119 .map(|unit| {
1120 Ok((
1121 build_runner.files().metadata(unit).unit_id(),
1122 scrape_output_path(build_runner, unit)?,
1123 ))
1124 })
1125 .collect::<CargoResult<HashMap<_, _>>>()?,
1126 )
1127 } else {
1128 None
1129 };
1130
1131 let failed_scrape_units = Arc::clone(&build_runner.failed_scrape_units);
1132 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
1133 && !matches!(
1134 build_runner.bcx.gctx.shell().verbosity(),
1135 Verbosity::Verbose
1136 );
1137 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
1138 make_failed_scrape_diagnostic(
1139 build_runner,
1140 unit,
1141 format_args!("failed to scan {target_desc} in package `{name}` for example code usage"),
1142 )
1143 });
1144 if hide_diagnostics_for_scrape_unit {
1145 output_options.show_diagnostics = false;
1146 }
1147
1148 Ok(Work::new(move |state| {
1149 add_custom_flags(
1150 &mut rustdoc,
1151 &build_script_outputs.lock().unwrap(),
1152 script_metadatas,
1153 )?;
1154
1155 if let Some(scrape_outputs) = scrape_outputs {
1160 let failed_scrape_units = failed_scrape_units.lock().unwrap();
1161 for (metadata, output_path) in &scrape_outputs {
1162 if !failed_scrape_units.contains(metadata) {
1163 rustdoc.arg("--with-examples").arg(output_path);
1164 }
1165 }
1166 }
1167
1168 if !is_json_output {
1169 let crate_dir = doc_dir.join(&crate_name);
1170 if crate_dir.exists() {
1171 debug!("removing pre-existing doc directory {:?}", crate_dir);
1174 paths::remove_dir_all(&crate_dir)?;
1175 }
1176 };
1177 state.running(&rustdoc);
1178 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
1179
1180 let (stats, result) = rustdoc.exec_with_streaming(
1181 &mut |line| on_stdout_line(state, line, package_id, &target),
1182 &mut |line| {
1183 on_stderr_line(
1184 state,
1185 line,
1186 package_id,
1187 &manifest,
1188 &target,
1189 &mut output_options,
1190 )
1191 },
1192 false,
1193 capture_rusage,
1194 );
1195 let result = result
1196 .map_err(verbose_if_simple_exit_code)
1197 .with_context(|| format!("could not document `{}`", name));
1198
1199 if let Some(peak_memory) = stats.peak_rss {
1200 state.peak_memory(peak_memory);
1201 }
1202
1203 if let Err(e) = result {
1204 if let Some(diagnostic) = failed_scrape_diagnostic {
1205 state.warning(diagnostic);
1206 }
1207
1208 return Err(e);
1209 }
1210 if rustdoc_depinfo_enabled && rustdoc_dep_info_loc.exists() {
1211 fingerprint::translate_dep_info(
1212 &rustdoc_dep_info_loc,
1213 &dep_info_loc,
1214 &cwd,
1215 &pkg_root,
1216 &build_dir,
1217 &ws_root,
1218 &rustdoc,
1219 is_local,
1221 &env_config,
1222 )
1223 .with_context(|| {
1224 internal(format_args!(
1225 "could not parse/generate dep info at: {}",
1226 rustdoc_dep_info_loc.display()
1227 ))
1228 })?;
1229 paths::set_file_time_no_err(dep_info_loc, timestamp);
1232 }
1233
1234 Ok(())
1235 }))
1236}
1237
1238fn crate_version_flag_already_present(rustdoc: &ProcessBuilder) -> bool {
1241 rustdoc.get_args().any(|flag| {
1242 flag.to_str()
1243 .map_or(false, |flag| flag.starts_with(RUSTDOC_CRATE_VERSION_FLAG))
1244 })
1245}
1246
1247fn append_crate_version_flag(unit: &Unit, rustdoc: &mut ProcessBuilder) {
1248 rustdoc
1249 .arg(RUSTDOC_CRATE_VERSION_FLAG)
1250 .arg(unit.pkg.version().to_string());
1251}
1252
1253enum CapLints {
1254 Allow,
1255 Warn,
1256}
1257
1258fn compute_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit) -> Option<CapLints> {
1259 if !unit.show_warnings(bcx.gctx) {
1262 Some(CapLints::Allow)
1263 } else if !unit.is_local() {
1266 Some(CapLints::Warn)
1267 } else {
1268 None
1269 }
1270}
1271
1272fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuilder) {
1276 if let Some(cap_lints) = compute_cap_lints(bcx, unit) {
1277 match cap_lints {
1278 CapLints::Allow => {
1279 cmd.arg("--cap-lints").arg("allow");
1280 }
1281 CapLints::Warn => {
1282 cmd.arg("--cap-lints").arg("warn");
1283 }
1284 }
1285 }
1286}
1287
1288fn add_allow_features(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1292 if let Some(allow) = &build_runner.bcx.gctx.cli_unstable().allow_features {
1293 use std::fmt::Write;
1294 let mut arg = String::from("-Zallow-features=");
1295 for f in allow {
1296 let _ = write!(&mut arg, "{f},");
1297 }
1298 cmd.arg(arg.trim_end_matches(','));
1299 }
1300}
1301
1302fn add_error_format_and_color(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1313 let enable_timings =
1314 build_runner.bcx.gctx.cli_unstable().section_timings && build_runner.bcx.logger.is_some();
1315 if enable_timings {
1316 cmd.arg("-Zunstable-options");
1317 }
1318
1319 cmd.arg("--error-format=json");
1320
1321 let mut json = String::from(
1322 "--json=diagnostic-rendered-ansi,artifacts,future-incompat,unused-externs-silent",
1323 );
1324 if let MessageFormat::Short | MessageFormat::Json { short: true, .. } =
1325 build_runner.bcx.build_config.message_format
1326 {
1327 json.push_str(",diagnostic-short");
1328 } else if build_runner.bcx.gctx.shell().err_unicode()
1329 && build_runner.bcx.gctx.cli_unstable().rustc_unicode
1330 {
1331 json.push_str(",diagnostic-unicode");
1332 }
1333 if enable_timings {
1334 json.push_str(",timings");
1335 }
1336 cmd.arg(json);
1337
1338 let gctx = build_runner.bcx.gctx;
1339 if let Some(width) = gctx.shell().err_width().diagnostic_terminal_width() {
1340 cmd.arg(format!("--diagnostic-width={width}"));
1341 }
1342}
1343
1344fn build_base_args(
1346 build_runner: &BuildRunner<'_, '_>,
1347 cmd: &mut ProcessBuilder,
1348 unit: &Unit,
1349) -> CargoResult<()> {
1350 assert!(!unit.mode.is_run_custom_build());
1351
1352 let bcx = build_runner.bcx;
1353 let Profile {
1354 ref opt_level,
1355 codegen_backend,
1356 codegen_units,
1357 debuginfo,
1358 debug_assertions,
1359 split_debuginfo,
1360 overflow_checks,
1361 rpath,
1362 ref panic,
1363 incremental,
1364 strip,
1365 rustflags: profile_rustflags,
1366 trim_paths,
1367 hint_mostly_unused: profile_hint_mostly_unused,
1368 ..
1369 } = unit.profile.clone();
1370 let hints = unit.pkg.hints().cloned().unwrap_or_default();
1371 let test = unit.mode.is_any_test();
1372
1373 cmd.arg("--crate-name").arg(&unit.target.crate_name());
1374
1375 let edition = unit.target.edition();
1376 edition.cmd_edition_arg(cmd);
1377
1378 add_path_args(bcx.ws, unit, cmd);
1379 add_error_format_and_color(build_runner, cmd);
1380 add_allow_features(build_runner, cmd);
1381
1382 let mut contains_dy_lib = false;
1383 if !test {
1384 for crate_type in &unit.target.rustc_crate_types() {
1385 cmd.arg("--crate-type").arg(crate_type.as_str());
1386 contains_dy_lib |= crate_type == &CrateType::Dylib;
1387 }
1388 }
1389
1390 if unit.mode.is_check() {
1391 cmd.arg("--emit=dep-info,metadata");
1392 } else if !build_runner
1393 .bcx
1394 .target_data
1395 .info(unit.kind)
1396 .should_embed_metadata()
1397 {
1398 if unit.benefits_from_no_embed_metadata() {
1408 cmd.arg("--emit=dep-info,metadata,link");
1409 cmd.args(&["-Z", "embed-metadata=no"]);
1410 } else {
1411 cmd.arg("--emit=dep-info,link");
1412 }
1413 } else {
1414 if !unit.requires_upstream_objects() {
1418 cmd.arg("--emit=dep-info,metadata,link");
1419 } else {
1420 cmd.arg("--emit=dep-info,link");
1421 }
1422 }
1423
1424 let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
1425 || (contains_dy_lib && !build_runner.is_primary_package(unit));
1426 if prefer_dynamic {
1427 cmd.arg("-C").arg("prefer-dynamic");
1428 }
1429
1430 if opt_level.as_str() != "0" {
1431 cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
1432 }
1433
1434 if *panic != PanicStrategy::Unwind {
1435 cmd.arg("-C").arg(format!("panic={}", panic));
1436 }
1437 if *panic == PanicStrategy::ImmediateAbort {
1438 cmd.arg("-Z").arg("unstable-options");
1439 }
1440
1441 cmd.args(<o_args(build_runner, unit));
1442
1443 if let Some(backend) = codegen_backend {
1444 cmd.arg("-Z").arg(&format!("codegen-backend={}", backend));
1445 }
1446
1447 if let Some(n) = codegen_units {
1448 cmd.arg("-C").arg(&format!("codegen-units={}", n));
1449 }
1450
1451 let debuginfo = debuginfo.into_inner();
1452 if debuginfo != TomlDebugInfo::None {
1454 cmd.arg("-C").arg(format!("debuginfo={debuginfo}"));
1455 if let Some(split) = split_debuginfo {
1462 if build_runner
1463 .bcx
1464 .target_data
1465 .info(unit.kind)
1466 .supports_debuginfo_split(split)
1467 {
1468 cmd.arg("-C").arg(format!("split-debuginfo={split}"));
1469 }
1470 }
1471 }
1472
1473 trim_paths_args(cmd, build_runner, unit, &trim_paths)?;
1474
1475 match compute_cap_lints(bcx, unit) {
1476 None | Some(CapLints::Warn) => {
1477 cmd.args(unit.pkg.manifest().lint_rustflags());
1478 }
1479 Some(CapLints::Allow) => {}
1482 }
1483 cmd.args(&profile_rustflags);
1484
1485 if opt_level.as_str() != "0" {
1489 if debug_assertions {
1490 cmd.args(&["-C", "debug-assertions=on"]);
1491 if !overflow_checks {
1492 cmd.args(&["-C", "overflow-checks=off"]);
1493 }
1494 } else if overflow_checks {
1495 cmd.args(&["-C", "overflow-checks=on"]);
1496 }
1497 } else if !debug_assertions {
1498 cmd.args(&["-C", "debug-assertions=off"]);
1499 if overflow_checks {
1500 cmd.args(&["-C", "overflow-checks=on"]);
1501 }
1502 } else if !overflow_checks {
1503 cmd.args(&["-C", "overflow-checks=off"]);
1504 }
1505
1506 if test && unit.target.harness() {
1507 cmd.arg("--test");
1508
1509 if *panic == PanicStrategy::Abort || *panic == PanicStrategy::ImmediateAbort {
1517 cmd.arg("-Z").arg("panic-abort-tests");
1518 }
1519 } else if test {
1520 cmd.arg("--cfg").arg("test");
1521 }
1522
1523 cmd.args(&features_args(unit));
1524 cmd.args(&check_cfg_args(unit));
1525
1526 let meta = build_runner.files().metadata(unit);
1527 cmd.arg("-C")
1528 .arg(&format!("metadata={}", meta.c_metadata()));
1529 if let Some(c_extra_filename) = meta.c_extra_filename() {
1530 cmd.arg("-C")
1531 .arg(&format!("extra-filename=-{c_extra_filename}"));
1532 }
1533
1534 if rpath {
1535 cmd.arg("-C").arg("rpath");
1536 }
1537
1538 cmd.arg("--out-dir")
1539 .arg(&build_runner.files().output_dir(unit));
1540
1541 unit.kind.add_target_arg(cmd);
1542
1543 add_codegen_linker(cmd, build_runner, unit, bcx.gctx.target_applies_to_host()?);
1544
1545 if incremental {
1546 add_codegen_incremental(cmd, build_runner, unit)
1547 }
1548
1549 let pkg_hint_mostly_unused = match hints.mostly_unused {
1550 Some(toml::Value::Boolean(b)) => Some(b),
1551 _ => None,
1554 };
1555 if profile_hint_mostly_unused
1556 .or(pkg_hint_mostly_unused)
1557 .unwrap_or(false)
1558 && bcx.gctx.cli_unstable().profile_hint_mostly_unused
1559 {
1560 cmd.arg("-Zhint-mostly-unused");
1561 }
1562
1563 let strip = strip.into_inner();
1564 if strip != StripInner::None {
1565 cmd.arg("-C").arg(format!("strip={}", strip));
1566 }
1567
1568 if unit.is_std {
1569 cmd.arg("-Z")
1575 .arg("force-unstable-if-unmarked")
1576 .env("RUSTC_BOOTSTRAP", "1");
1577 }
1578
1579 if let Some(version) = unit.pkg.manifest().rust_version()
1580 && bcx.gctx.cli_unstable().hint_msrv
1581 {
1582 cmd.arg("-Z").arg(format!("hint-msrv={version}"));
1583 }
1584
1585 Ok(())
1586}
1587
1588fn features_args(unit: &Unit) -> Vec<OsString> {
1590 let mut args = Vec::with_capacity(unit.features.len() * 2);
1591
1592 for feat in &unit.features {
1593 args.push(OsString::from("--cfg"));
1594 args.push(OsString::from(format!("feature=\"{}\"", feat)));
1595 }
1596
1597 args
1598}
1599
1600fn check_cfg_args(unit: &Unit) -> Vec<OsString> {
1602 let gross_cap_estimation = unit.pkg.summary().features().len() * 7 + 25;
1620 let mut arg_feature = OsString::with_capacity(gross_cap_estimation);
1621
1622 arg_feature.push("cfg(feature, values(");
1623 for (i, feature) in unit.pkg.summary().features().keys().enumerate() {
1624 if i != 0 {
1625 arg_feature.push(", ");
1626 }
1627 arg_feature.push("\"");
1628 arg_feature.push(feature);
1629 arg_feature.push("\"");
1630 }
1631 arg_feature.push("))");
1632
1633 vec![
1642 OsString::from("--check-cfg"),
1643 OsString::from("cfg(docsrs,test)"),
1644 OsString::from("--check-cfg"),
1645 arg_feature,
1646 ]
1647}
1648
1649fn lto_args(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> Vec<OsString> {
1651 let mut result = Vec::new();
1652 let mut push = |arg: &str| {
1653 result.push(OsString::from("-C"));
1654 result.push(OsString::from(arg));
1655 };
1656 match build_runner.lto[unit] {
1657 lto::Lto::Run(None) => push("lto"),
1658 lto::Lto::Run(Some(s)) => push(&format!("lto={}", s)),
1659 lto::Lto::Off => {
1660 push("lto=off");
1661 push("embed-bitcode=no");
1662 }
1663 lto::Lto::ObjectAndBitcode => {} lto::Lto::OnlyBitcode => push("linker-plugin-lto"),
1665 lto::Lto::OnlyObject => push("embed-bitcode=no"),
1666 }
1667 result
1668}
1669
1670fn build_deps_args(
1676 cmd: &mut ProcessBuilder,
1677 build_runner: &BuildRunner<'_, '_>,
1678 unit: &Unit,
1679) -> CargoResult<()> {
1680 let bcx = build_runner.bcx;
1681
1682 for arg in lib_search_paths(build_runner, unit)? {
1683 cmd.arg(arg);
1684 }
1685
1686 let deps = build_runner.unit_deps(unit);
1687
1688 if !deps
1692 .iter()
1693 .any(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_linkable())
1694 {
1695 if let Some(dep) = deps.iter().find(|dep| {
1696 !dep.unit.mode.is_doc() && dep.unit.target.is_lib() && !dep.unit.artifact.is_true()
1697 }) {
1698 let dep_name = dep.unit.target.crate_name();
1699 let name = unit.target.crate_name();
1700 bcx.gctx.shell().print_report(&[
1701 Level::WARNING.secondary_title(format!("the package `{dep_name}` provides no linkable target"))
1702 .elements([
1703 Level::NOTE.message(format!("this might cause `{name}` to fail compilation")),
1704 Level::NOTE.message("this warning might turn into a hard error in the future"),
1705 Level::HELP.message(format!("consider adding 'dylib' or 'rlib' to key 'crate-type' in `{dep_name}`'s Cargo.toml"))
1706 ])
1707 ], false)?;
1708 }
1709 }
1710
1711 let mut unstable_opts = false;
1712
1713 let first_custom_build_dep = deps.iter().find(|dep| dep.unit.mode.is_run_custom_build());
1715 if let Some(dep) = first_custom_build_dep {
1716 let out_dir = if bcx.gctx.cli_unstable().build_dir_new_layout {
1717 build_runner.files().out_dir_new_layout(&dep.unit)
1718 } else {
1719 build_runner.files().build_script_out_dir(&dep.unit)
1720 };
1721 cmd.env("OUT_DIR", &out_dir);
1722 }
1723
1724 let is_multiple_build_scripts_enabled = unit
1726 .pkg
1727 .manifest()
1728 .unstable_features()
1729 .require(Feature::multiple_build_scripts())
1730 .is_ok();
1731
1732 if is_multiple_build_scripts_enabled {
1733 for dep in deps {
1734 if dep.unit.mode.is_run_custom_build() {
1735 let out_dir = if bcx.gctx.cli_unstable().build_dir_new_layout {
1736 build_runner.files().out_dir_new_layout(&dep.unit)
1737 } else {
1738 build_runner.files().build_script_out_dir(&dep.unit)
1739 };
1740 let target_name = dep.unit.target.name();
1741 let out_dir_prefix = target_name
1742 .strip_prefix("build-script-")
1743 .unwrap_or(target_name);
1744 let out_dir_name = format!("{out_dir_prefix}_OUT_DIR");
1745 cmd.env(&out_dir_name, &out_dir);
1746 }
1747 }
1748 }
1749 for arg in extern_args(build_runner, unit, &mut unstable_opts)? {
1750 cmd.arg(arg);
1751 }
1752
1753 for (var, env) in artifact::get_env(build_runner, unit, deps)? {
1754 cmd.env(&var, env);
1755 }
1756
1757 if unstable_opts {
1760 cmd.arg("-Z").arg("unstable-options");
1761 }
1762
1763 Ok(())
1764}
1765
1766fn add_custom_flags(
1770 cmd: &mut ProcessBuilder,
1771 build_script_outputs: &BuildScriptOutputs,
1772 metadata_vec: Option<Vec<UnitHash>>,
1773) -> CargoResult<()> {
1774 if let Some(metadata_vec) = metadata_vec {
1775 for metadata in metadata_vec {
1776 if let Some(output) = build_script_outputs.get(metadata) {
1777 for cfg in output.cfgs.iter() {
1778 cmd.arg("--cfg").arg(cfg);
1779 }
1780 for check_cfg in &output.check_cfgs {
1781 cmd.arg("--check-cfg").arg(check_cfg);
1782 }
1783 for (name, value) in output.env.iter() {
1784 cmd.env(name, value);
1785 }
1786 }
1787 }
1788 }
1789
1790 Ok(())
1791}
1792
1793#[tracing::instrument(skip_all)]
1795pub fn lib_search_paths(
1796 build_runner: &BuildRunner<'_, '_>,
1797 unit: &Unit,
1798) -> CargoResult<Vec<OsString>> {
1799 let mut lib_search_paths = Vec::new();
1800 if build_runner.bcx.gctx.cli_unstable().build_dir_new_layout {
1801 let paths = dep_paths_for_args(unit, build_runner);
1803
1804 for path in paths {
1805 let mut deps = OsString::from("dependency=");
1806 deps.push(path);
1807 lib_search_paths.extend(["-L".into(), deps]);
1808 }
1809 } else {
1810 let mut deps = OsString::from("dependency=");
1811 deps.push(build_runner.files().deps_dir(unit));
1812 lib_search_paths.extend(["-L".into(), deps]);
1813 }
1814
1815 if !unit.kind.is_host() {
1818 let mut deps = OsString::from("dependency=");
1819 deps.push(build_runner.files().host_deps(unit));
1820 lib_search_paths.extend(["-L".into(), deps]);
1821 }
1822
1823 Ok(lib_search_paths)
1824}
1825
1826fn dep_paths_for_args(unit: &Unit, build_runner: &BuildRunner<'_, '_>) -> Vec<PathBuf> {
1840 let direct = build_runner.unit_deps(unit);
1841 if direct.is_empty() {
1842 return Vec::new();
1843 }
1844
1845 let is_proc_macro = |u: &Unit| match u.target.kind() {
1849 TargetKind::Lib(kinds) => kinds.contains(&CrateType::ProcMacro),
1850 _ => false,
1851 };
1852
1853 let mut indirect = HashSet::default();
1854 let mut visited = HashSet::default();
1855 let mut stack: Vec<&Unit> = direct
1856 .iter()
1857 .filter(|d| !d.unit.target.is_custom_build())
1858 .map(|d| &d.unit)
1859 .collect();
1860 while let Some(u) = stack.pop() {
1861 if !visited.insert(u.clone()) {
1862 continue;
1863 }
1864 if is_proc_macro(u) {
1865 continue;
1866 }
1867 for dep in build_runner.unit_deps(u) {
1868 let v = &dep.unit;
1869 if v.target.is_custom_build() {
1870 continue;
1871 }
1872 indirect.insert(v.clone());
1873 if is_proc_macro(v) {
1874 continue;
1875 }
1876 if !visited.contains(v) {
1877 stack.push(v);
1878 }
1879 }
1880 }
1881
1882 let mut paths: Vec<PathBuf> = indirect
1883 .into_iter()
1884 .map(|u| build_runner.files().deps_dir(&u))
1885 .collect();
1886
1887 paths.sort_unstable_by(|a, b| match (a.to_str(), b.to_str()) {
1896 (Some(a), Some(b)) => a.cmp(b),
1897 (Some(_), None) => std::cmp::Ordering::Less,
1898 (None, Some(_)) => std::cmp::Ordering::Greater,
1899 (None, None) => std::cmp::Ordering::Less,
1900 });
1901 paths.dedup_by(|a, b| matches!((a.to_str(), b.to_str()), (Some(x), Some(y)) if x == y));
1902
1903 paths
1904}
1905
1906fn is_public_dependency_enabled(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> bool {
1907 unit.pkg
1908 .manifest()
1909 .unstable_features()
1910 .require(Feature::public_dependency())
1911 .is_ok()
1912 || build_runner.bcx.gctx.cli_unstable().public_dependency
1913}
1914
1915pub fn extern_args(
1917 build_runner: &BuildRunner<'_, '_>,
1918 unit: &Unit,
1919 unstable_opts: &mut bool,
1920) -> CargoResult<Vec<OsString>> {
1921 let mut result = Vec::new();
1922 let deps = build_runner.unit_deps(unit);
1923
1924 let no_embed_metadata = !build_runner
1925 .bcx
1926 .target_data
1927 .info(unit.kind)
1928 .should_embed_metadata();
1929 let public_dependency_enabled = is_public_dependency_enabled(build_runner, unit);
1930
1931 let mut link_to = |dep: &UnitDep,
1933 extern_crate_name: InternedString,
1934 noprelude: bool,
1935 nounused: bool|
1936 -> CargoResult<()> {
1937 let mut value = OsString::new();
1938 let mut opts = Vec::new();
1939 if !dep.public && unit.target.is_lib() && public_dependency_enabled {
1940 opts.push("priv");
1941 *unstable_opts = true;
1942 }
1943 if noprelude {
1944 opts.push("noprelude");
1945 *unstable_opts = true;
1946 }
1947 if nounused {
1948 opts.push("nounused");
1949 *unstable_opts = true;
1950 }
1951 if !opts.is_empty() {
1952 value.push(opts.join(","));
1953 value.push(":");
1954 }
1955 value.push(extern_crate_name.as_str());
1956 value.push("=");
1957
1958 let mut pass = |file| {
1959 let mut value = value.clone();
1960 value.push(file);
1961 result.push(OsString::from("--extern"));
1962 result.push(value);
1963 };
1964
1965 let outputs = build_runner.outputs(&dep.unit)?;
1966
1967 if build_runner.only_requires_rmeta(unit, &dep.unit) || dep.unit.mode.is_check() {
1968 let output = outputs
1970 .iter()
1971 .find(|output| output.flavor == FileFlavor::Rmeta)
1972 .expect("failed to find rmeta dep for pipelined dep");
1973 pass(&output.path);
1974 } else {
1975 for output in outputs.iter() {
1977 if output.flavor == FileFlavor::Linkable {
1978 pass(&output.path);
1979 }
1980 else if no_embed_metadata && output.flavor == FileFlavor::Rmeta {
1984 pass(&output.path);
1985 }
1986 }
1987 }
1988 Ok(())
1989 };
1990
1991 for dep in deps {
1992 if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
1993 link_to(dep, dep.extern_crate_name, dep.noprelude, dep.nounused)?;
1994 }
1995 }
1996 if unit.target.proc_macro() {
1997 result.push(OsString::from("--extern"));
1999 result.push(OsString::from("proc_macro"));
2000 }
2001
2002 Ok(result)
2003}
2004
2005fn add_codegen_linker(
2007 cmd: &mut ProcessBuilder,
2008 build_runner: &BuildRunner<'_, '_>,
2009 unit: &Unit,
2010 target_applies_to_host: bool,
2011) {
2012 let linker = if unit.target.for_host() && !target_applies_to_host {
2013 build_runner
2014 .compilation
2015 .host_linker()
2016 .map(|s| s.as_os_str())
2017 } else {
2018 build_runner
2019 .compilation
2020 .target_linker(unit.kind)
2021 .map(|s| s.as_os_str())
2022 };
2023
2024 if let Some(linker) = linker {
2025 let mut arg = OsString::from("linker=");
2026 arg.push(linker);
2027 cmd.arg("-C").arg(arg);
2028 }
2029}
2030
2031fn add_codegen_incremental(
2033 cmd: &mut ProcessBuilder,
2034 build_runner: &BuildRunner<'_, '_>,
2035 unit: &Unit,
2036) {
2037 let dir = build_runner.files().incremental_dir(&unit);
2038 let mut arg = OsString::from("incremental=");
2039 arg.push(dir.as_os_str());
2040 cmd.arg("-C").arg(arg);
2041}
2042
2043fn envify(s: &str) -> String {
2044 s.chars()
2045 .flat_map(|c| c.to_uppercase())
2046 .map(|c| if c == '-' { '_' } else { c })
2047 .collect()
2048}
2049
2050struct OutputOptions {
2053 format: MessageFormat,
2055 cache_cell: Option<(PathBuf, OnceCell<File>)>,
2060 show_diagnostics: bool,
2068 warnings_seen: usize,
2070 errors_seen: usize,
2072}
2073
2074impl OutputOptions {
2075 fn for_dirty(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions {
2076 let path = build_runner.files().message_cache_path(unit);
2077 drop(fs::remove_file(&path));
2079 let cache_cell = Some((path, OnceCell::new()));
2080
2081 let show_diagnostics = true;
2082
2083 let format = build_runner.bcx.build_config.message_format;
2084
2085 OutputOptions {
2086 format,
2087 cache_cell,
2088 show_diagnostics,
2089 warnings_seen: 0,
2090 errors_seen: 0,
2091 }
2092 }
2093
2094 fn for_fresh(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions {
2095 let cache_cell = None;
2096
2097 let show_diagnostics = unit.show_warnings(build_runner.bcx.gctx);
2100
2101 let format = build_runner.bcx.build_config.message_format;
2102
2103 OutputOptions {
2104 format,
2105 cache_cell,
2106 show_diagnostics,
2107 warnings_seen: 0,
2108 errors_seen: 0,
2109 }
2110 }
2111}
2112
2113struct ManifestErrorContext {
2119 path: PathBuf,
2121 spans: Option<Arc<toml::Spanned<toml::de::DeTable<'static>>>>,
2123 contents: Option<String>,
2125 rename_table: HashMap<InternedString, InternedString>,
2128 requested_kinds: Vec<CompileKind>,
2131 cfgs: Vec<Vec<Cfg>>,
2134 host_name: InternedString,
2135 cwd: PathBuf,
2137 term_width: usize,
2139}
2140
2141fn on_stdout_line(
2142 state: &JobState<'_, '_>,
2143 line: &str,
2144 _package_id: PackageId,
2145 _target: &Target,
2146) -> CargoResult<()> {
2147 state.stdout(line.to_string())?;
2148 Ok(())
2149}
2150
2151fn on_stderr_line(
2152 state: &JobState<'_, '_>,
2153 line: &str,
2154 package_id: PackageId,
2155 manifest: &ManifestErrorContext,
2156 target: &Target,
2157 options: &mut OutputOptions,
2158) -> CargoResult<()> {
2159 if on_stderr_line_inner(state, line, package_id, manifest, target, options)? {
2160 if let Some((path, cell)) = &mut options.cache_cell {
2162 let f = cell.try_borrow_mut_with(|| paths::create(path))?;
2164 debug_assert!(!line.contains('\n'));
2165 f.write_all(line.as_bytes())?;
2166 f.write_all(&[b'\n'])?;
2167 }
2168 }
2169 Ok(())
2170}
2171
2172fn on_stderr_line_inner(
2174 state: &JobState<'_, '_>,
2175 line: &str,
2176 package_id: PackageId,
2177 manifest: &ManifestErrorContext,
2178 target: &Target,
2179 options: &mut OutputOptions,
2180) -> CargoResult<bool> {
2181 if !line.starts_with('{') {
2187 state.stderr(line.to_string())?;
2188 return Ok(true);
2189 }
2190
2191 let mut compiler_message: Box<serde_json::value::RawValue> = match serde_json::from_str(line) {
2192 Ok(msg) => msg,
2193
2194 Err(e) => {
2198 debug!("failed to parse json: {:?}", e);
2199 state.stderr(line.to_string())?;
2200 return Ok(true);
2201 }
2202 };
2203
2204 let count_diagnostic = |level, options: &mut OutputOptions| {
2205 if level == "warning" {
2206 options.warnings_seen += 1;
2207 } else if level == "error" {
2208 options.errors_seen += 1;
2209 }
2210 };
2211
2212 if let Ok(report) = serde_json::from_str::<FutureIncompatReport>(compiler_message.get()) {
2213 for item in &report.future_incompat_report {
2214 count_diagnostic(&*item.diagnostic.level, options);
2215 }
2216 state.future_incompat_report(report.future_incompat_report);
2217 return Ok(true);
2218 }
2219
2220 let res = serde_json::from_str::<SectionTiming>(compiler_message.get());
2221 if let Ok(timing_record) = res {
2222 state.on_section_timing_emitted(timing_record);
2223 return Ok(false);
2224 }
2225
2226 let add_pub_in_priv_diagnostic = |diag: &mut String| -> bool {
2228 static PRIV_DEP_REGEX: LazyLock<Regex> =
2237 LazyLock::new(|| Regex::new("from private dependency '([A-Za-z0-9-_]+)'").unwrap());
2238 if let Some(crate_name) = PRIV_DEP_REGEX.captures(diag).and_then(|m| m.get(1))
2239 && let Some(ref contents) = manifest.contents
2240 && let Some(span) = manifest.find_crate_span(crate_name.as_str())
2241 {
2242 let rel_path = pathdiff::diff_paths(&manifest.path, &manifest.cwd)
2243 .unwrap_or_else(|| manifest.path.clone())
2244 .display()
2245 .to_string();
2246 let report = [Group::with_title(Level::NOTE.secondary_title(format!(
2247 "dependency `{}` declared here",
2248 crate_name.as_str()
2249 )))
2250 .element(
2251 Snippet::source(contents)
2252 .path(rel_path)
2253 .annotation(AnnotationKind::Context.span(span)),
2254 )];
2255
2256 let rendered = Renderer::styled()
2257 .term_width(manifest.term_width)
2258 .render(&report);
2259 diag.push_str(&rendered);
2260 diag.push('\n');
2261 return true;
2262 }
2263 false
2264 };
2265
2266 match options.format {
2269 MessageFormat::Human
2274 | MessageFormat::Short
2275 | MessageFormat::Json {
2276 render_diagnostics: true,
2277 ..
2278 } => {
2279 #[derive(serde::Deserialize)]
2280 struct CompilerMessage<'a> {
2281 rendered: String,
2285 #[serde(borrow)]
2286 message: Cow<'a, str>,
2287 #[serde(borrow)]
2288 level: Cow<'a, str>,
2289 children: Vec<PartialDiagnostic>,
2290 code: Option<DiagnosticCode>,
2291 }
2292
2293 #[derive(serde::Deserialize)]
2302 struct PartialDiagnostic {
2303 spans: Vec<PartialDiagnosticSpan>,
2304 }
2305
2306 #[derive(serde::Deserialize)]
2308 struct PartialDiagnosticSpan {
2309 suggestion_applicability: Option<Applicability>,
2310 }
2311
2312 #[derive(serde::Deserialize)]
2313 struct DiagnosticCode {
2314 code: String,
2315 }
2316
2317 if let Ok(mut msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2318 {
2319 if msg.message.starts_with("aborting due to")
2320 || msg.message.ends_with("warning emitted")
2321 || msg.message.ends_with("warnings emitted")
2322 {
2323 return Ok(true);
2325 }
2326 if msg.rendered.ends_with('\n') {
2328 msg.rendered.pop();
2329 }
2330 let mut rendered = msg.rendered;
2331 if options.show_diagnostics {
2332 let machine_applicable: bool = msg
2333 .children
2334 .iter()
2335 .map(|child| {
2336 child
2337 .spans
2338 .iter()
2339 .filter_map(|span| span.suggestion_applicability)
2340 .any(|app| app == Applicability::MachineApplicable)
2341 })
2342 .any(|b| b);
2343 count_diagnostic(&msg.level, options);
2344 if msg
2345 .code
2346 .as_ref()
2347 .is_some_and(|c| c.code == "exported_private_dependencies")
2348 && options.format != MessageFormat::Short
2349 {
2350 add_pub_in_priv_diagnostic(&mut rendered);
2351 }
2352 let lint = msg.code.is_some();
2353 state.emit_diag(&msg.level, rendered, lint, machine_applicable)?;
2354 }
2355 return Ok(true);
2356 }
2357 }
2358
2359 MessageFormat::Json { ansi, .. } => {
2360 #[derive(serde::Deserialize, serde::Serialize)]
2361 struct CompilerMessage<'a> {
2362 rendered: String,
2363 #[serde(flatten, borrow)]
2364 other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2365 code: Option<DiagnosticCode<'a>>,
2366 }
2367
2368 #[derive(serde::Deserialize, serde::Serialize)]
2369 struct DiagnosticCode<'a> {
2370 code: String,
2371 #[serde(flatten, borrow)]
2372 other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2373 }
2374
2375 if let Ok(mut error) =
2376 serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2377 {
2378 let modified_diag = if error
2379 .code
2380 .as_ref()
2381 .is_some_and(|c| c.code == "exported_private_dependencies")
2382 {
2383 add_pub_in_priv_diagnostic(&mut error.rendered)
2384 } else {
2385 false
2386 };
2387
2388 if !ansi {
2392 error.rendered = anstream::adapter::strip_str(&error.rendered).to_string();
2393 }
2394 if !ansi || modified_diag {
2395 let new_line = serde_json::to_string(&error)?;
2396 compiler_message = serde_json::value::RawValue::from_string(new_line)?;
2397 }
2398 }
2399 }
2400 }
2401
2402 #[derive(serde::Deserialize)]
2409 struct ArtifactNotification<'a> {
2410 #[serde(borrow)]
2411 artifact: Cow<'a, str>,
2412 }
2413
2414 if let Ok(artifact) = serde_json::from_str::<ArtifactNotification<'_>>(compiler_message.get()) {
2415 trace!("found directive from rustc: `{}`", artifact.artifact);
2416 if artifact.artifact.ends_with(".rmeta") {
2417 debug!("looks like metadata finished early!");
2418 state.rmeta_produced();
2419 }
2420 return Ok(false);
2421 }
2422
2423 #[derive(serde::Deserialize)]
2424 struct UnusedExterns {
2425 unused_extern_names: std::collections::BTreeSet<InternedString>,
2426 }
2427 if let Ok(uext) = serde_json::from_str::<UnusedExterns>(compiler_message.get()) {
2428 trace!(
2429 "obtained unused externs list from rustc: `{:?}`",
2430 uext.unused_extern_names
2431 );
2432 state.unused_externs(uext.unused_extern_names);
2433 return Ok(true);
2434 }
2435
2436 if !options.show_diagnostics {
2441 return Ok(true);
2442 }
2443
2444 #[derive(serde::Deserialize)]
2445 struct CompilerMessage<'a> {
2446 #[serde(borrow)]
2447 message: Cow<'a, str>,
2448 #[serde(borrow)]
2449 level: Cow<'a, str>,
2450 }
2451
2452 if let Ok(msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get()) {
2453 if msg.message.starts_with("aborting due to")
2454 || msg.message.ends_with("warning emitted")
2455 || msg.message.ends_with("warnings emitted")
2456 {
2457 return Ok(true);
2459 }
2460 count_diagnostic(&msg.level, options);
2461 }
2462
2463 let msg = machine_message::FromCompiler {
2464 package_id: package_id.to_spec(),
2465 manifest_path: &manifest.path,
2466 target,
2467 message: compiler_message,
2468 }
2469 .to_json_string();
2470
2471 state.stdout(msg)?;
2475 Ok(true)
2476}
2477
2478impl ManifestErrorContext {
2479 fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> ManifestErrorContext {
2480 let mut duplicates = HashSet::default();
2481 let mut rename_table = HashMap::default();
2482
2483 for dep in build_runner.unit_deps(unit) {
2484 let unrenamed_id = dep.unit.pkg.package_id().name();
2485 if duplicates.contains(&unrenamed_id) {
2486 continue;
2487 }
2488 match rename_table.entry(unrenamed_id) {
2489 std::collections::hash_map::Entry::Occupied(occ) => {
2490 occ.remove_entry();
2491 duplicates.insert(unrenamed_id);
2492 }
2493 std::collections::hash_map::Entry::Vacant(vac) => {
2494 vac.insert(dep.extern_crate_name);
2495 }
2496 }
2497 }
2498
2499 let bcx = build_runner.bcx;
2500 ManifestErrorContext {
2501 path: unit.pkg.manifest_path().to_owned(),
2502 spans: unit.pkg.manifest().document_rc(),
2503 contents: unit.pkg.manifest().contents().map(String::from),
2504 requested_kinds: bcx.target_data.requested_kinds().to_owned(),
2505 host_name: bcx.rustc().host,
2506 rename_table,
2507 cwd: path_args(build_runner.bcx.ws, unit).1,
2508 cfgs: bcx
2509 .target_data
2510 .requested_kinds()
2511 .iter()
2512 .map(|k| bcx.target_data.cfg(*k).to_owned())
2513 .collect(),
2514 term_width: bcx
2515 .gctx
2516 .shell()
2517 .err_width()
2518 .diagnostic_terminal_width()
2519 .unwrap_or(cargo_util_terminal::report::renderer::DEFAULT_TERM_WIDTH),
2520 }
2521 }
2522
2523 fn requested_target_names(&self) -> impl Iterator<Item = &str> {
2524 self.requested_kinds.iter().map(|kind| match kind {
2525 CompileKind::Host => &self.host_name,
2526 CompileKind::Target(target) => target.short_name(),
2527 })
2528 }
2529
2530 fn find_crate_span(&self, unrenamed: &str) -> Option<Range<usize>> {
2544 let Some(ref spans) = self.spans else {
2545 return None;
2546 };
2547
2548 let orig_name = self.rename_table.get(unrenamed)?.as_str();
2549
2550 if let Some((k, v)) = get_key_value(&spans, &["dependencies", orig_name]) {
2551 if let Some(package) = v.get_ref().as_table().and_then(|t| t.get("package")) {
2560 return Some(package.span());
2561 } else {
2562 return Some(k.span());
2563 }
2564 }
2565
2566 if let Some(target) = spans
2571 .deref()
2572 .as_ref()
2573 .get("target")
2574 .and_then(|t| t.as_ref().as_table())
2575 {
2576 for (platform, platform_table) in target.iter() {
2577 match platform.as_ref().parse::<Platform>() {
2578 Ok(Platform::Name(name)) => {
2579 if !self.requested_target_names().any(|n| n == name) {
2580 continue;
2581 }
2582 }
2583 Ok(Platform::Cfg(cfg_expr)) => {
2584 if !self.cfgs.iter().any(|cfgs| cfg_expr.matches(cfgs)) {
2585 continue;
2586 }
2587 }
2588 Err(_) => continue,
2589 }
2590
2591 let Some(platform_table) = platform_table.as_ref().as_table() else {
2592 continue;
2593 };
2594
2595 if let Some(deps) = platform_table
2596 .get("dependencies")
2597 .and_then(|d| d.as_ref().as_table())
2598 {
2599 if let Some((k, v)) = deps.get_key_value(orig_name) {
2600 if let Some(package) = v.get_ref().as_table().and_then(|t| t.get("package"))
2601 {
2602 return Some(package.span());
2603 } else {
2604 return Some(k.span());
2605 }
2606 }
2607 }
2608 }
2609 }
2610 None
2611 }
2612}
2613
2614fn replay_output_cache(
2618 package_id: PackageId,
2619 manifest: ManifestErrorContext,
2620 target: &Target,
2621 path: PathBuf,
2622 mut output_options: OutputOptions,
2623) -> Work {
2624 let target = target.clone();
2625 Work::new(move |state| {
2626 if !path.exists() {
2627 return Ok(());
2629 }
2630 let file = paths::open(&path)?;
2634 let mut reader = std::io::BufReader::new(file);
2635 let mut line = String::new();
2636 loop {
2637 let length = reader.read_line(&mut line)?;
2638 if length == 0 {
2639 break;
2640 }
2641 let trimmed = line.trim_end_matches(&['\n', '\r'][..]);
2642 on_stderr_line(
2643 state,
2644 trimmed,
2645 package_id,
2646 &manifest,
2647 &target,
2648 &mut output_options,
2649 )?;
2650 line.clear();
2651 }
2652 Ok(())
2653 })
2654}
2655
2656fn descriptive_pkg_name(name: &str, target: &Target, mode: &CompileMode) -> String {
2659 let desc_name = target.description_named();
2660 let mode = if mode.is_rustc_test() && !(target.is_test() || target.is_bench()) {
2661 " test"
2662 } else if mode.is_doc_test() {
2663 " doctest"
2664 } else if mode.is_doc() {
2665 " doc"
2666 } else {
2667 ""
2668 };
2669 format!("`{name}` ({desc_name}{mode})")
2670}
2671
2672pub(crate) fn apply_env_config(
2674 gctx: &crate::GlobalContext,
2675 cmd: &mut ProcessBuilder,
2676) -> CargoResult<()> {
2677 for (key, value) in gctx.env_config()?.iter() {
2678 if cmd.get_envs().contains_key(key) {
2680 continue;
2681 }
2682 cmd.env(key, value);
2683 }
2684 Ok(())
2685}
2686
2687fn should_include_scrape_units(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
2689 unit.mode.is_doc() && bcx.scrape_units.len() > 0 && bcx.ws.unit_needs_doc_scrape(unit)
2690}
2691
2692fn scrape_output_path(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<PathBuf> {
2694 assert!(unit.mode.is_doc() || unit.mode.is_doc_scrape());
2695 build_runner
2696 .outputs(unit)
2697 .map(|outputs| outputs[0].path.clone())
2698}
2699
2700fn rustdoc_dep_info_loc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> PathBuf {
2702 let mut loc = build_runner.files().fingerprint_file_path(unit, "");
2703 loc.set_extension("d");
2704 loc
2705}