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 lto;
45mod output_depinfo;
46mod output_sbom;
47pub mod rustdoc;
48pub mod standard_lib;
49mod timings;
50mod unit;
51pub mod unit_dependencies;
52pub mod unit_graph;
53
54use std::borrow::Cow;
55use std::cell::OnceCell;
56use std::collections::{BTreeMap, HashMap, HashSet};
57use std::env;
58use std::ffi::{OsStr, OsString};
59use std::fmt::Display;
60use std::fs::{self, File};
61use std::io::{BufRead, BufWriter, Write};
62use std::ops::Range;
63use std::path::{Path, PathBuf};
64use std::sync::{Arc, LazyLock};
65
66use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
67use anyhow::{Context as _, Error};
68use cargo_platform::{Cfg, Platform};
69use itertools::Itertools;
70use regex::Regex;
71use tracing::{debug, instrument, trace};
72
73pub use self::build_config::UserIntent;
74pub use self::build_config::{BuildConfig, CompileMode, MessageFormat, TimingOutput};
75pub use self::build_context::{
76 BuildContext, FileFlavor, FileType, RustDocFingerprint, RustcTargetData, TargetInfo,
77};
78pub use self::build_runner::{BuildRunner, Metadata, UnitHash};
79pub use self::compilation::{Compilation, Doctest, UnitOutput};
80pub use self::compile_kind::{CompileKind, CompileKindFallback, CompileTarget};
81pub use self::crate_type::CrateType;
82pub use self::custom_build::LinkArgTarget;
83pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts, LibraryPath};
84pub(crate) use self::fingerprint::DirtyReason;
85pub use self::job_queue::Freshness;
86use self::job_queue::{Job, JobQueue, JobState, Work};
87pub(crate) use self::layout::Layout;
88pub use self::lto::Lto;
89use self::output_depinfo::output_depinfo;
90use self::output_sbom::build_sbom;
91use self::unit_graph::UnitDep;
92use crate::core::compiler::future_incompat::FutureIncompatReport;
93use crate::core::compiler::timings::SectionTiming;
94pub use crate::core::compiler::unit::{Unit, UnitInterner};
95use crate::core::manifest::TargetSourcePath;
96use crate::core::profiles::{PanicStrategy, Profile, StripInner};
97use crate::core::{Feature, PackageId, Target, Verbosity};
98use crate::util::OnceExt;
99use crate::util::context::WarningHandling;
100use crate::util::errors::{CargoResult, VerboseError};
101use crate::util::interning::InternedString;
102use crate::util::lints::get_key_value;
103use crate::util::machine_message::{self, Message};
104use crate::util::{add_path_args, internal, path_args};
105use cargo_util::{ProcessBuilder, ProcessError, paths};
106use cargo_util_schemas::manifest::TomlDebugInfo;
107use cargo_util_schemas::manifest::TomlTrimPaths;
108use cargo_util_schemas::manifest::TomlTrimPathsValue;
109use rustfix::diagnostics::Applicability;
110pub(crate) use timings::CompilationSection;
111
112const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version";
113
114pub trait Executor: Send + Sync + 'static {
118 fn init(&self, _build_runner: &BuildRunner<'_, '_>, _unit: &Unit) {}
122
123 fn exec(
126 &self,
127 cmd: &ProcessBuilder,
128 id: PackageId,
129 target: &Target,
130 mode: CompileMode,
131 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
132 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
133 ) -> CargoResult<()>;
134
135 fn force_rebuild(&self, _unit: &Unit) -> bool {
138 false
139 }
140}
141
142#[derive(Copy, Clone)]
145pub struct DefaultExecutor;
146
147impl Executor for DefaultExecutor {
148 #[instrument(name = "rustc", skip_all, fields(package = id.name().as_str(), process = cmd.to_string()))]
149 fn exec(
150 &self,
151 cmd: &ProcessBuilder,
152 id: PackageId,
153 _target: &Target,
154 _mode: CompileMode,
155 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
156 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
157 ) -> CargoResult<()> {
158 cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
159 .map(drop)
160 }
161}
162
163#[tracing::instrument(skip(build_runner, jobs, exec))]
173fn compile<'gctx>(
174 build_runner: &mut BuildRunner<'_, 'gctx>,
175 jobs: &mut JobQueue<'gctx>,
176 unit: &Unit,
177 exec: &Arc<dyn Executor>,
178 force_rebuild: bool,
179) -> CargoResult<()> {
180 let bcx = build_runner.bcx;
181 if !build_runner.compiled.insert(unit.clone()) {
182 return Ok(());
183 }
184
185 if !unit.skip_non_compile_time_dep {
189 fingerprint::prepare_init(build_runner, unit)?;
192
193 let job = if unit.mode.is_run_custom_build() {
194 custom_build::prepare(build_runner, unit)?
195 } else if unit.mode.is_doc_test() {
196 Job::new_fresh()
198 } else {
199 let force = exec.force_rebuild(unit) || force_rebuild;
200 let mut job = fingerprint::prepare_target(build_runner, unit, force)?;
201 job.before(if job.freshness().is_dirty() {
202 let work = if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
203 rustdoc(build_runner, unit)?
204 } else {
205 rustc(build_runner, unit, exec)?
206 };
207 work.then(link_targets(build_runner, unit, false)?)
208 } else {
209 let show_diagnostics = unit.show_warnings(bcx.gctx)
212 && build_runner.bcx.gctx.warning_handling()? != WarningHandling::Allow;
213 let manifest = ManifestErrorContext::new(build_runner, unit);
214 let work = replay_output_cache(
215 unit.pkg.package_id(),
216 manifest,
217 &unit.target,
218 build_runner.files().message_cache_path(unit),
219 build_runner.bcx.build_config.message_format,
220 show_diagnostics,
221 );
222 work.then(link_targets(build_runner, unit, true)?)
224 });
225
226 job
227 };
228 jobs.enqueue(build_runner, unit, job)?;
229 }
230
231 let deps = Vec::from(build_runner.unit_deps(unit)); for dep in deps {
234 compile(build_runner, jobs, &dep.unit, exec, false)?;
235 }
236
237 Ok(())
238}
239
240fn make_failed_scrape_diagnostic(
243 build_runner: &BuildRunner<'_, '_>,
244 unit: &Unit,
245 top_line: impl Display,
246) -> String {
247 let manifest_path = unit.pkg.manifest_path();
248 let relative_manifest_path = manifest_path
249 .strip_prefix(build_runner.bcx.ws.root())
250 .unwrap_or(&manifest_path);
251
252 format!(
253 "\
254{top_line}
255 Try running with `--verbose` to see the error message.
256 If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in {}",
257 relative_manifest_path.display()
258 )
259}
260
261fn rustc(
263 build_runner: &mut BuildRunner<'_, '_>,
264 unit: &Unit,
265 exec: &Arc<dyn Executor>,
266) -> CargoResult<Work> {
267 let mut rustc = prepare_rustc(build_runner, unit)?;
268
269 let name = unit.pkg.name();
270
271 let outputs = build_runner.outputs(unit)?;
272 let root = build_runner.files().out_dir(unit);
273
274 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
276 let current_id = unit.pkg.package_id();
277 let manifest = ManifestErrorContext::new(build_runner, unit);
278 let build_scripts = build_runner.build_scripts.get(unit).cloned();
279
280 let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib());
283
284 let dep_info_name =
285 if let Some(c_extra_filename) = build_runner.files().metadata(unit).c_extra_filename() {
286 format!("{}-{}.d", unit.target.crate_name(), c_extra_filename)
287 } else {
288 format!("{}.d", unit.target.crate_name())
289 };
290 let rustc_dep_info_loc = root.join(dep_info_name);
291 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
292
293 let mut output_options = OutputOptions::new(build_runner, unit);
294 let package_id = unit.pkg.package_id();
295 let target = Target::clone(&unit.target);
296 let mode = unit.mode;
297
298 exec.init(build_runner, unit);
299 let exec = exec.clone();
300
301 let root_output = build_runner.files().host_dest().to_path_buf();
302 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
303 let pkg_root = unit.pkg.root().to_path_buf();
304 let cwd = rustc
305 .get_cwd()
306 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
307 .to_path_buf();
308 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
309 let script_metadatas = build_runner.find_build_script_metadatas(unit);
310 let is_local = unit.is_local();
311 let artifact = unit.artifact;
312 let sbom_files = build_runner.sbom_output_files(unit)?;
313 let sbom = build_sbom(build_runner, unit)?;
314
315 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
316 && !matches!(
317 build_runner.bcx.gctx.shell().verbosity(),
318 Verbosity::Verbose
319 );
320 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
321 let target_desc = unit.target.description_named();
324 let mut for_scrape_units = build_runner
325 .bcx
326 .scrape_units_have_dep_on(unit)
327 .into_iter()
328 .map(|unit| unit.target.description_named())
329 .collect::<Vec<_>>();
330 for_scrape_units.sort();
331 let for_scrape_units = for_scrape_units.join(", ");
332 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}"))
333 });
334 if hide_diagnostics_for_scrape_unit {
335 output_options.show_diagnostics = false;
336 }
337 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
338 return Ok(Work::new(move |state| {
339 if artifact.is_true() {
343 paths::create_dir_all(&root)?;
344 }
345
346 if let Some(build_scripts) = build_scripts {
354 let script_outputs = build_script_outputs.lock().unwrap();
355 add_native_deps(
356 &mut rustc,
357 &script_outputs,
358 &build_scripts,
359 pass_l_flag,
360 &target,
361 current_id,
362 mode,
363 )?;
364 add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?;
365 add_custom_flags(&mut rustc, &script_outputs, script_metadatas)?;
366 }
367
368 for output in outputs.iter() {
369 if output.path.extension() == Some(OsStr::new("rmeta")) {
373 let dst = root.join(&output.path).with_extension("rlib");
374 if dst.exists() {
375 paths::remove_file(&dst)?;
376 }
377 }
378
379 if output.hardlink.is_some() && output.path.exists() {
384 _ = paths::remove_file(&output.path).map_err(|e| {
385 tracing::debug!(
386 "failed to delete previous output file `{:?}`: {e:?}",
387 output.path
388 );
389 });
390 }
391 }
392
393 state.running(&rustc);
394 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
395 for file in sbom_files {
396 tracing::debug!("writing sbom to {}", file.display());
397 let outfile = BufWriter::new(paths::create(&file)?);
398 serde_json::to_writer(outfile, &sbom)?;
399 }
400
401 let result = exec
402 .exec(
403 &rustc,
404 package_id,
405 &target,
406 mode,
407 &mut |line| on_stdout_line(state, line, package_id, &target),
408 &mut |line| {
409 on_stderr_line(
410 state,
411 line,
412 package_id,
413 &manifest,
414 &target,
415 &mut output_options,
416 )
417 },
418 )
419 .map_err(|e| {
420 if output_options.errors_seen == 0 {
421 e
426 } else {
427 verbose_if_simple_exit_code(e)
428 }
429 })
430 .with_context(|| {
431 let warnings = match output_options.warnings_seen {
433 0 => String::new(),
434 1 => "; 1 warning emitted".to_string(),
435 count => format!("; {} warnings emitted", count),
436 };
437 let errors = match output_options.errors_seen {
438 0 => String::new(),
439 1 => " due to 1 previous error".to_string(),
440 count => format!(" due to {} previous errors", count),
441 };
442 let name = descriptive_pkg_name(&name, &target, &mode);
443 format!("could not compile {name}{errors}{warnings}")
444 });
445
446 if let Err(e) = result {
447 if let Some(diagnostic) = failed_scrape_diagnostic {
448 state.warning(diagnostic);
449 }
450
451 return Err(e);
452 }
453
454 debug_assert_eq!(output_options.errors_seen, 0);
456
457 if rustc_dep_info_loc.exists() {
458 fingerprint::translate_dep_info(
459 &rustc_dep_info_loc,
460 &dep_info_loc,
461 &cwd,
462 &pkg_root,
463 &build_dir,
464 &rustc,
465 is_local,
467 &env_config,
468 )
469 .with_context(|| {
470 internal(format!(
471 "could not parse/generate dep info at: {}",
472 rustc_dep_info_loc.display()
473 ))
474 })?;
475 paths::set_file_time_no_err(dep_info_loc, timestamp);
478 }
479
480 Ok(())
481 }));
482
483 fn add_native_deps(
486 rustc: &mut ProcessBuilder,
487 build_script_outputs: &BuildScriptOutputs,
488 build_scripts: &BuildScripts,
489 pass_l_flag: bool,
490 target: &Target,
491 current_id: PackageId,
492 mode: CompileMode,
493 ) -> CargoResult<()> {
494 let mut library_paths = vec![];
495
496 for key in build_scripts.to_link.iter() {
497 let output = build_script_outputs.get(key.1).ok_or_else(|| {
498 internal(format!(
499 "couldn't find build script output for {}/{}",
500 key.0, key.1
501 ))
502 })?;
503 library_paths.extend(output.library_paths.iter());
504 }
505
506 library_paths.sort_by_key(|p| match p {
512 LibraryPath::CargoArtifact(_) => 0,
513 LibraryPath::External(_) => 1,
514 });
515
516 for path in library_paths.iter() {
517 rustc.arg("-L").arg(path.as_ref());
518 }
519
520 for key in build_scripts.to_link.iter() {
521 let output = build_script_outputs.get(key.1).ok_or_else(|| {
522 internal(format!(
523 "couldn't find build script output for {}/{}",
524 key.0, key.1
525 ))
526 })?;
527
528 if key.0 == current_id {
529 if pass_l_flag {
530 for name in output.library_links.iter() {
531 rustc.arg("-l").arg(name);
532 }
533 }
534 }
535
536 for (lt, arg) in &output.linker_args {
537 if lt.applies_to(target, mode)
543 && (key.0 == current_id || *lt == LinkArgTarget::Cdylib)
544 {
545 rustc.arg("-C").arg(format!("link-arg={}", arg));
546 }
547 }
548 }
549 Ok(())
550 }
551}
552
553fn verbose_if_simple_exit_code(err: Error) -> Error {
554 match err
557 .downcast_ref::<ProcessError>()
558 .as_ref()
559 .and_then(|perr| perr.code)
560 {
561 Some(n) if cargo_util::is_simple_exit_code(n) => VerboseError::new(err).into(),
562 _ => err,
563 }
564}
565
566fn link_targets(
569 build_runner: &mut BuildRunner<'_, '_>,
570 unit: &Unit,
571 fresh: bool,
572) -> CargoResult<Work> {
573 let bcx = build_runner.bcx;
574 let outputs = build_runner.outputs(unit)?;
575 let export_dir = build_runner.files().export_dir();
576 let package_id = unit.pkg.package_id();
577 let manifest_path = PathBuf::from(unit.pkg.manifest_path());
578 let profile = unit.profile.clone();
579 let unit_mode = unit.mode;
580 let features = unit.features.iter().map(|s| s.to_string()).collect();
581 let json_messages = bcx.build_config.emit_json();
582 let executable = build_runner.get_executable(unit)?;
583 let mut target = Target::clone(&unit.target);
584 if let TargetSourcePath::Metabuild = target.src_path() {
585 let path = unit
587 .pkg
588 .manifest()
589 .metabuild_path(build_runner.bcx.ws.build_dir());
590 target.set_src_path(TargetSourcePath::Path(path));
591 }
592
593 Ok(Work::new(move |state| {
594 let mut destinations = vec![];
599 for output in outputs.iter() {
600 let src = &output.path;
601 if !src.exists() {
604 continue;
605 }
606 let Some(dst) = output.hardlink.as_ref() else {
607 destinations.push(src.clone());
608 continue;
609 };
610 destinations.push(dst.clone());
611 paths::link_or_copy(src, dst)?;
612 if let Some(ref path) = output.export_path {
613 let export_dir = export_dir.as_ref().unwrap();
614 paths::create_dir_all(export_dir)?;
615
616 paths::link_or_copy(src, path)?;
617 }
618 }
619
620 if json_messages {
621 let debuginfo = match profile.debuginfo.into_inner() {
622 TomlDebugInfo::None => machine_message::ArtifactDebuginfo::Int(0),
623 TomlDebugInfo::Limited => machine_message::ArtifactDebuginfo::Int(1),
624 TomlDebugInfo::Full => machine_message::ArtifactDebuginfo::Int(2),
625 TomlDebugInfo::LineDirectivesOnly => {
626 machine_message::ArtifactDebuginfo::Named("line-directives-only")
627 }
628 TomlDebugInfo::LineTablesOnly => {
629 machine_message::ArtifactDebuginfo::Named("line-tables-only")
630 }
631 };
632 let art_profile = machine_message::ArtifactProfile {
633 opt_level: profile.opt_level.as_str(),
634 debuginfo: Some(debuginfo),
635 debug_assertions: profile.debug_assertions,
636 overflow_checks: profile.overflow_checks,
637 test: unit_mode.is_any_test(),
638 };
639
640 let msg = machine_message::Artifact {
641 package_id: package_id.to_spec(),
642 manifest_path,
643 target: &target,
644 profile: art_profile,
645 features,
646 filenames: destinations,
647 executable,
648 fresh,
649 }
650 .to_json_string();
651 state.stdout(msg)?;
652 }
653 Ok(())
654 }))
655}
656
657fn add_plugin_deps(
661 rustc: &mut ProcessBuilder,
662 build_script_outputs: &BuildScriptOutputs,
663 build_scripts: &BuildScripts,
664 root_output: &Path,
665) -> CargoResult<()> {
666 let var = paths::dylib_path_envvar();
667 let search_path = rustc.get_env(var).unwrap_or_default();
668 let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>();
669 for (pkg_id, metadata) in &build_scripts.plugins {
670 let output = build_script_outputs
671 .get(*metadata)
672 .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?;
673 search_path.append(&mut filter_dynamic_search_path(
674 output.library_paths.iter().map(AsRef::as_ref),
675 root_output,
676 ));
677 }
678 let search_path = paths::join_paths(&search_path, var)?;
679 rustc.env(var, &search_path);
680 Ok(())
681}
682
683fn get_dynamic_search_path(path: &Path) -> &Path {
684 match path.to_str().and_then(|s| s.split_once("=")) {
685 Some(("native" | "crate" | "dependency" | "framework" | "all", path)) => Path::new(path),
686 _ => path,
687 }
688}
689
690fn filter_dynamic_search_path<'a, I>(paths: I, root_output: &Path) -> Vec<PathBuf>
696where
697 I: Iterator<Item = &'a PathBuf>,
698{
699 let mut search_path = vec![];
700 for dir in paths {
701 let dir = get_dynamic_search_path(dir);
702 if dir.starts_with(&root_output) {
703 search_path.push(dir.to_path_buf());
704 } else {
705 debug!(
706 "Not including path {} in runtime library search path because it is \
707 outside target root {}",
708 dir.display(),
709 root_output.display()
710 );
711 }
712 }
713 search_path
714}
715
716fn prepare_rustc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
723 let gctx = build_runner.bcx.gctx;
724 let is_primary = build_runner.is_primary_package(unit);
725 let is_workspace = build_runner.bcx.ws.is_member(&unit.pkg);
726
727 let mut base = build_runner
728 .compilation
729 .rustc_process(unit, is_primary, is_workspace)?;
730 build_base_args(build_runner, &mut base, unit)?;
731 if unit.pkg.manifest().is_embedded() {
732 if !gctx.cli_unstable().script {
733 anyhow::bail!(
734 "parsing `{}` requires `-Zscript`",
735 unit.pkg.manifest_path().display()
736 );
737 }
738 base.arg("-Z").arg("crate-attr=feature(frontmatter)");
739 }
740
741 base.inherit_jobserver(&build_runner.jobserver);
742 build_deps_args(&mut base, build_runner, unit)?;
743 add_cap_lints(build_runner.bcx, unit, &mut base);
744 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
745 base.args(args);
746 }
747 base.args(&unit.rustflags);
748 if gctx.cli_unstable().binary_dep_depinfo {
749 base.arg("-Z").arg("binary-dep-depinfo");
750 }
751 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
752 base.arg("-Z").arg("checksum-hash-algorithm=blake3");
753 }
754
755 if is_primary {
756 base.env("CARGO_PRIMARY_PACKAGE", "1");
757 let file_list = std::env::join_paths(build_runner.sbom_output_files(unit)?)?;
758 base.env("CARGO_SBOM_PATH", file_list);
759 }
760
761 if unit.target.is_test() || unit.target.is_bench() {
762 let tmp = build_runner
763 .files()
764 .layout(unit.kind)
765 .build_dir()
766 .prepare_tmp()?;
767 base.env("CARGO_TARGET_TMPDIR", tmp.display().to_string());
768 }
769
770 Ok(base)
771}
772
773fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
780 let bcx = build_runner.bcx;
781 let mut rustdoc = build_runner.compilation.rustdoc_process(unit, None)?;
783 if unit.pkg.manifest().is_embedded() {
784 if !bcx.gctx.cli_unstable().script {
785 anyhow::bail!(
786 "parsing `{}` requires `-Zscript`",
787 unit.pkg.manifest_path().display()
788 );
789 }
790 rustdoc.arg("-Z").arg("crate-attr=feature(frontmatter)");
791 }
792 rustdoc.inherit_jobserver(&build_runner.jobserver);
793 let crate_name = unit.target.crate_name();
794 rustdoc.arg("--crate-name").arg(&crate_name);
795 add_path_args(bcx.ws, unit, &mut rustdoc);
796 add_cap_lints(bcx, unit, &mut rustdoc);
797
798 if let CompileKind::Target(target) = unit.kind {
799 rustdoc.arg("--target").arg(target.rustc_target());
800 }
801 let doc_dir = build_runner.files().out_dir(unit);
802 rustdoc.arg("-o").arg(&doc_dir);
803 rustdoc.args(&features_args(unit));
804 rustdoc.args(&check_cfg_args(unit));
805
806 add_error_format_and_color(build_runner, &mut rustdoc);
807 add_allow_features(build_runner, &mut rustdoc);
808
809 if build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo {
810 let mut arg =
813 OsString::from("--emit=toolchain-shared-resources,invocation-specific,dep-info=");
814 arg.push(rustdoc_dep_info_loc(build_runner, unit));
815 rustdoc.arg(arg);
816
817 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
818 rustdoc.arg("-Z").arg("checksum-hash-algorithm=blake3");
819 }
820
821 rustdoc.arg("-Zunstable-options");
822 }
823
824 if let Some(trim_paths) = unit.profile.trim_paths.as_ref() {
825 trim_paths_args_rustdoc(&mut rustdoc, build_runner, unit, trim_paths)?;
826 }
827
828 rustdoc.args(unit.pkg.manifest().lint_rustflags());
829
830 let metadata = build_runner.metadata_for_doc_units[unit];
831 rustdoc
832 .arg("-C")
833 .arg(format!("metadata={}", metadata.c_metadata()));
834
835 if unit.mode.is_doc_scrape() {
836 debug_assert!(build_runner.bcx.scrape_units.contains(unit));
837
838 if unit.target.is_test() {
839 rustdoc.arg("--scrape-tests");
840 }
841
842 rustdoc.arg("-Zunstable-options");
843
844 rustdoc
845 .arg("--scrape-examples-output-path")
846 .arg(scrape_output_path(build_runner, unit)?);
847
848 for pkg in build_runner.bcx.packages.packages() {
850 let names = pkg
851 .targets()
852 .iter()
853 .map(|target| target.crate_name())
854 .collect::<HashSet<_>>();
855 for name in names {
856 rustdoc.arg("--scrape-examples-target-crate").arg(name);
857 }
858 }
859 }
860
861 if should_include_scrape_units(build_runner.bcx, unit) {
862 rustdoc.arg("-Zunstable-options");
863 }
864
865 build_deps_args(&mut rustdoc, build_runner, unit)?;
866 rustdoc::add_root_urls(build_runner, unit, &mut rustdoc)?;
867
868 rustdoc::add_output_format(build_runner, &mut rustdoc)?;
869
870 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
871 rustdoc.args(args);
872 }
873 rustdoc.args(&unit.rustdocflags);
874
875 if !crate_version_flag_already_present(&rustdoc) {
876 append_crate_version_flag(unit, &mut rustdoc);
877 }
878
879 Ok(rustdoc)
880}
881
882fn rustdoc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Work> {
884 let mut rustdoc = prepare_rustdoc(build_runner, unit)?;
885
886 let crate_name = unit.target.crate_name();
887 let doc_dir = build_runner.files().out_dir(unit);
888 paths::create_dir_all(&doc_dir)?;
892
893 let target_desc = unit.target.description_named();
894 let name = unit.pkg.name();
895 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
896 let package_id = unit.pkg.package_id();
897 let target = Target::clone(&unit.target);
898 let manifest = ManifestErrorContext::new(build_runner, unit);
899
900 let rustdoc_dep_info_loc = rustdoc_dep_info_loc(build_runner, unit);
901 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
902 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
903 let pkg_root = unit.pkg.root().to_path_buf();
904 let cwd = rustdoc
905 .get_cwd()
906 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
907 .to_path_buf();
908 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
909 let is_local = unit.is_local();
910 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
911 let rustdoc_depinfo_enabled = build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo;
912
913 let mut output_options = OutputOptions::new(build_runner, unit);
914 let script_metadatas = build_runner.find_build_script_metadatas(unit);
915 let scrape_outputs = if should_include_scrape_units(build_runner.bcx, unit) {
916 Some(
917 build_runner
918 .bcx
919 .scrape_units
920 .iter()
921 .map(|unit| {
922 Ok((
923 build_runner.files().metadata(unit).unit_id(),
924 scrape_output_path(build_runner, unit)?,
925 ))
926 })
927 .collect::<CargoResult<HashMap<_, _>>>()?,
928 )
929 } else {
930 None
931 };
932
933 let failed_scrape_units = Arc::clone(&build_runner.failed_scrape_units);
934 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
935 && !matches!(
936 build_runner.bcx.gctx.shell().verbosity(),
937 Verbosity::Verbose
938 );
939 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
940 make_failed_scrape_diagnostic(
941 build_runner,
942 unit,
943 format_args!("failed to scan {target_desc} in package `{name}` for example code usage"),
944 )
945 });
946 if hide_diagnostics_for_scrape_unit {
947 output_options.show_diagnostics = false;
948 }
949
950 Ok(Work::new(move |state| {
951 add_custom_flags(
952 &mut rustdoc,
953 &build_script_outputs.lock().unwrap(),
954 script_metadatas,
955 )?;
956
957 if let Some(scrape_outputs) = scrape_outputs {
962 let failed_scrape_units = failed_scrape_units.lock().unwrap();
963 for (metadata, output_path) in &scrape_outputs {
964 if !failed_scrape_units.contains(metadata) {
965 rustdoc.arg("--with-examples").arg(output_path);
966 }
967 }
968 }
969
970 let crate_dir = doc_dir.join(&crate_name);
971 if crate_dir.exists() {
972 debug!("removing pre-existing doc directory {:?}", crate_dir);
975 paths::remove_dir_all(crate_dir)?;
976 }
977 state.running(&rustdoc);
978 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
979
980 let result = rustdoc
981 .exec_with_streaming(
982 &mut |line| on_stdout_line(state, line, package_id, &target),
983 &mut |line| {
984 on_stderr_line(
985 state,
986 line,
987 package_id,
988 &manifest,
989 &target,
990 &mut output_options,
991 )
992 },
993 false,
994 )
995 .map_err(verbose_if_simple_exit_code)
996 .with_context(|| format!("could not document `{}`", name));
997
998 if let Err(e) = result {
999 if let Some(diagnostic) = failed_scrape_diagnostic {
1000 state.warning(diagnostic);
1001 }
1002
1003 return Err(e);
1004 }
1005
1006 if rustdoc_depinfo_enabled && rustdoc_dep_info_loc.exists() {
1007 fingerprint::translate_dep_info(
1008 &rustdoc_dep_info_loc,
1009 &dep_info_loc,
1010 &cwd,
1011 &pkg_root,
1012 &build_dir,
1013 &rustdoc,
1014 is_local,
1016 &env_config,
1017 )
1018 .with_context(|| {
1019 internal(format_args!(
1020 "could not parse/generate dep info at: {}",
1021 rustdoc_dep_info_loc.display()
1022 ))
1023 })?;
1024 paths::set_file_time_no_err(dep_info_loc, timestamp);
1027 }
1028
1029 Ok(())
1030 }))
1031}
1032
1033fn crate_version_flag_already_present(rustdoc: &ProcessBuilder) -> bool {
1036 rustdoc.get_args().any(|flag| {
1037 flag.to_str()
1038 .map_or(false, |flag| flag.starts_with(RUSTDOC_CRATE_VERSION_FLAG))
1039 })
1040}
1041
1042fn append_crate_version_flag(unit: &Unit, rustdoc: &mut ProcessBuilder) {
1043 rustdoc
1044 .arg(RUSTDOC_CRATE_VERSION_FLAG)
1045 .arg(unit.pkg.version().to_string());
1046}
1047
1048fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuilder) {
1052 if !unit.show_warnings(bcx.gctx) {
1055 cmd.arg("--cap-lints").arg("allow");
1056
1057 } else if !unit.is_local() {
1060 cmd.arg("--cap-lints").arg("warn");
1061 }
1062}
1063
1064fn add_allow_features(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1068 if let Some(allow) = &build_runner.bcx.gctx.cli_unstable().allow_features {
1069 use std::fmt::Write;
1070 let mut arg = String::from("-Zallow-features=");
1071 for f in allow {
1072 let _ = write!(&mut arg, "{f},");
1073 }
1074 cmd.arg(arg.trim_end_matches(','));
1075 }
1076}
1077
1078fn add_error_format_and_color(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1089 let enable_timings = build_runner.bcx.gctx.cli_unstable().section_timings
1090 && !build_runner.bcx.build_config.timing_outputs.is_empty();
1091 if enable_timings {
1092 cmd.arg("-Zunstable-options");
1093 }
1094
1095 cmd.arg("--error-format=json");
1096 let mut json = String::from("--json=diagnostic-rendered-ansi,artifacts,future-incompat");
1097
1098 if let MessageFormat::Short | MessageFormat::Json { short: true, .. } =
1099 build_runner.bcx.build_config.message_format
1100 {
1101 json.push_str(",diagnostic-short");
1102 } else if build_runner.bcx.gctx.shell().err_unicode()
1103 && build_runner.bcx.gctx.cli_unstable().rustc_unicode
1104 {
1105 json.push_str(",diagnostic-unicode");
1106 }
1107
1108 if enable_timings {
1109 json.push_str(",timings");
1110 }
1111
1112 cmd.arg(json);
1113
1114 let gctx = build_runner.bcx.gctx;
1115 if let Some(width) = gctx.shell().err_width().diagnostic_terminal_width() {
1116 cmd.arg(format!("--diagnostic-width={width}"));
1117 }
1118}
1119
1120fn build_base_args(
1122 build_runner: &BuildRunner<'_, '_>,
1123 cmd: &mut ProcessBuilder,
1124 unit: &Unit,
1125) -> CargoResult<()> {
1126 assert!(!unit.mode.is_run_custom_build());
1127
1128 let bcx = build_runner.bcx;
1129 let Profile {
1130 ref opt_level,
1131 codegen_backend,
1132 codegen_units,
1133 debuginfo,
1134 debug_assertions,
1135 split_debuginfo,
1136 overflow_checks,
1137 rpath,
1138 ref panic,
1139 incremental,
1140 strip,
1141 rustflags: profile_rustflags,
1142 trim_paths,
1143 hint_mostly_unused: profile_hint_mostly_unused,
1144 ..
1145 } = unit.profile.clone();
1146 let hints = unit.pkg.hints().cloned().unwrap_or_default();
1147 let test = unit.mode.is_any_test();
1148
1149 let warn = |msg: &str| {
1150 bcx.gctx.shell().warn(format!(
1151 "{}@{}: {msg}",
1152 unit.pkg.package_id().name(),
1153 unit.pkg.package_id().version()
1154 ))
1155 };
1156 let unit_capped_warn = |msg: &str| {
1157 if unit.show_warnings(bcx.gctx) {
1158 warn(msg)
1159 } else {
1160 Ok(())
1161 }
1162 };
1163
1164 cmd.arg("--crate-name").arg(&unit.target.crate_name());
1165
1166 let edition = unit.target.edition();
1167 edition.cmd_edition_arg(cmd);
1168
1169 add_path_args(bcx.ws, unit, cmd);
1170 add_error_format_and_color(build_runner, cmd);
1171 add_allow_features(build_runner, cmd);
1172
1173 let mut contains_dy_lib = false;
1174 if !test {
1175 for crate_type in &unit.target.rustc_crate_types() {
1176 cmd.arg("--crate-type").arg(crate_type.as_str());
1177 contains_dy_lib |= crate_type == &CrateType::Dylib;
1178 }
1179 }
1180
1181 if unit.mode.is_check() {
1182 cmd.arg("--emit=dep-info,metadata");
1183 } else if build_runner.bcx.gctx.cli_unstable().no_embed_metadata {
1184 if unit.benefits_from_no_embed_metadata() {
1194 cmd.arg("--emit=dep-info,metadata,link");
1195 cmd.args(&["-Z", "embed-metadata=no"]);
1196 } else {
1197 cmd.arg("--emit=dep-info,link");
1198 }
1199 } else {
1200 if !unit.requires_upstream_objects() {
1204 cmd.arg("--emit=dep-info,metadata,link");
1205 } else {
1206 cmd.arg("--emit=dep-info,link");
1207 }
1208 }
1209
1210 let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
1211 || (contains_dy_lib && !build_runner.is_primary_package(unit));
1212 if prefer_dynamic {
1213 cmd.arg("-C").arg("prefer-dynamic");
1214 }
1215
1216 if opt_level.as_str() != "0" {
1217 cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
1218 }
1219
1220 if *panic != PanicStrategy::Unwind {
1221 cmd.arg("-C").arg(format!("panic={}", panic));
1222 }
1223 if *panic == PanicStrategy::ImmediateAbort {
1224 cmd.arg("-Z").arg("unstable-options");
1225 }
1226
1227 cmd.args(<o_args(build_runner, unit));
1228
1229 if let Some(backend) = codegen_backend {
1230 cmd.arg("-Z").arg(&format!("codegen-backend={}", backend));
1231 }
1232
1233 if let Some(n) = codegen_units {
1234 cmd.arg("-C").arg(&format!("codegen-units={}", n));
1235 }
1236
1237 let debuginfo = debuginfo.into_inner();
1238 if debuginfo != TomlDebugInfo::None {
1240 cmd.arg("-C").arg(format!("debuginfo={debuginfo}"));
1241 if let Some(split) = split_debuginfo {
1248 if build_runner
1249 .bcx
1250 .target_data
1251 .info(unit.kind)
1252 .supports_debuginfo_split(split)
1253 {
1254 cmd.arg("-C").arg(format!("split-debuginfo={split}"));
1255 }
1256 }
1257 }
1258
1259 if let Some(trim_paths) = trim_paths {
1260 trim_paths_args(cmd, build_runner, unit, &trim_paths)?;
1261 }
1262
1263 cmd.args(unit.pkg.manifest().lint_rustflags());
1264 cmd.args(&profile_rustflags);
1265
1266 if opt_level.as_str() != "0" {
1270 if debug_assertions {
1271 cmd.args(&["-C", "debug-assertions=on"]);
1272 if !overflow_checks {
1273 cmd.args(&["-C", "overflow-checks=off"]);
1274 }
1275 } else if overflow_checks {
1276 cmd.args(&["-C", "overflow-checks=on"]);
1277 }
1278 } else if !debug_assertions {
1279 cmd.args(&["-C", "debug-assertions=off"]);
1280 if overflow_checks {
1281 cmd.args(&["-C", "overflow-checks=on"]);
1282 }
1283 } else if !overflow_checks {
1284 cmd.args(&["-C", "overflow-checks=off"]);
1285 }
1286
1287 if test && unit.target.harness() {
1288 cmd.arg("--test");
1289
1290 if *panic == PanicStrategy::Abort || *panic == PanicStrategy::ImmediateAbort {
1298 cmd.arg("-Z").arg("panic-abort-tests");
1299 }
1300 } else if test {
1301 cmd.arg("--cfg").arg("test");
1302 }
1303
1304 cmd.args(&features_args(unit));
1305 cmd.args(&check_cfg_args(unit));
1306
1307 let meta = build_runner.files().metadata(unit);
1308 cmd.arg("-C")
1309 .arg(&format!("metadata={}", meta.c_metadata()));
1310 if let Some(c_extra_filename) = meta.c_extra_filename() {
1311 cmd.arg("-C")
1312 .arg(&format!("extra-filename=-{c_extra_filename}"));
1313 }
1314
1315 if rpath {
1316 cmd.arg("-C").arg("rpath");
1317 }
1318
1319 cmd.arg("--out-dir")
1320 .arg(&build_runner.files().out_dir(unit));
1321
1322 fn opt(cmd: &mut ProcessBuilder, key: &str, prefix: &str, val: Option<&OsStr>) {
1323 if let Some(val) = val {
1324 let mut joined = OsString::from(prefix);
1325 joined.push(val);
1326 cmd.arg(key).arg(joined);
1327 }
1328 }
1329
1330 if let CompileKind::Target(n) = unit.kind {
1331 cmd.arg("--target").arg(n.rustc_target());
1332 }
1333
1334 opt(
1335 cmd,
1336 "-C",
1337 "linker=",
1338 build_runner
1339 .compilation
1340 .target_linker(unit.kind)
1341 .as_ref()
1342 .map(|s| s.as_ref()),
1343 );
1344 if incremental {
1345 let dir = build_runner.files().incremental_dir(&unit);
1346 opt(cmd, "-C", "incremental=", Some(dir.as_os_str()));
1347 }
1348
1349 let pkg_hint_mostly_unused = match hints.mostly_unused {
1350 None => None,
1351 Some(toml::Value::Boolean(b)) => Some(b),
1352 Some(v) => {
1353 unit_capped_warn(&format!(
1354 "ignoring unsupported value type ({}) for 'hints.mostly-unused', which expects a boolean",
1355 v.type_str()
1356 ))?;
1357 None
1358 }
1359 };
1360 if profile_hint_mostly_unused
1361 .or(pkg_hint_mostly_unused)
1362 .unwrap_or(false)
1363 {
1364 if bcx.gctx.cli_unstable().profile_hint_mostly_unused {
1365 cmd.arg("-Zhint-mostly-unused");
1366 } else {
1367 if profile_hint_mostly_unused.is_some() {
1368 warn(
1370 "ignoring 'hint-mostly-unused' profile option, pass `-Zprofile-hint-mostly-unused` to enable it",
1371 )?;
1372 } else if pkg_hint_mostly_unused.is_some() {
1373 unit_capped_warn(
1374 "ignoring 'hints.mostly-unused', pass `-Zprofile-hint-mostly-unused` to enable it",
1375 )?;
1376 }
1377 }
1378 }
1379
1380 let strip = strip.into_inner();
1381 if strip != StripInner::None {
1382 cmd.arg("-C").arg(format!("strip={}", strip));
1383 }
1384
1385 if unit.is_std {
1386 cmd.arg("-Z")
1392 .arg("force-unstable-if-unmarked")
1393 .env("RUSTC_BOOTSTRAP", "1");
1394 }
1395
1396 if unit.target.is_test() || unit.target.is_bench() {
1398 for bin_target in unit
1399 .pkg
1400 .manifest()
1401 .targets()
1402 .iter()
1403 .filter(|target| target.is_bin())
1404 {
1405 let exe_path = build_runner.files().bin_link_for_target(
1406 bin_target,
1407 unit.kind,
1408 build_runner.bcx,
1409 )?;
1410 let name = bin_target
1411 .binary_filename()
1412 .unwrap_or(bin_target.name().to_string());
1413 let key = format!("CARGO_BIN_EXE_{}", name);
1414 cmd.env(&key, exe_path);
1415 }
1416 }
1417 Ok(())
1418}
1419
1420fn features_args(unit: &Unit) -> Vec<OsString> {
1422 let mut args = Vec::with_capacity(unit.features.len() * 2);
1423
1424 for feat in &unit.features {
1425 args.push(OsString::from("--cfg"));
1426 args.push(OsString::from(format!("feature=\"{}\"", feat)));
1427 }
1428
1429 args
1430}
1431
1432fn trim_paths_args_rustdoc(
1434 cmd: &mut ProcessBuilder,
1435 build_runner: &BuildRunner<'_, '_>,
1436 unit: &Unit,
1437 trim_paths: &TomlTrimPaths,
1438) -> CargoResult<()> {
1439 match trim_paths {
1440 TomlTrimPaths::Values(values) if !values.contains(&TomlTrimPathsValue::Diagnostics) => {
1442 return Ok(());
1443 }
1444 _ => {}
1445 }
1446
1447 cmd.arg("-Zunstable-options");
1449
1450 cmd.arg(package_remap(build_runner, unit));
1453 cmd.arg(build_dir_remap(build_runner));
1454 cmd.arg(sysroot_remap(build_runner, unit));
1455
1456 Ok(())
1457}
1458
1459fn trim_paths_args(
1465 cmd: &mut ProcessBuilder,
1466 build_runner: &BuildRunner<'_, '_>,
1467 unit: &Unit,
1468 trim_paths: &TomlTrimPaths,
1469) -> CargoResult<()> {
1470 if trim_paths.is_none() {
1471 return Ok(());
1472 }
1473
1474 cmd.arg("-Zunstable-options");
1476 cmd.arg(format!("-Zremap-path-scope={trim_paths}"));
1477
1478 cmd.arg(package_remap(build_runner, unit));
1481 cmd.arg(build_dir_remap(build_runner));
1482 cmd.arg(sysroot_remap(build_runner, unit));
1483
1484 Ok(())
1485}
1486
1487fn sysroot_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1492 let mut remap = OsString::from("--remap-path-prefix=");
1493 remap.push({
1494 let mut sysroot = build_runner.bcx.target_data.info(unit.kind).sysroot.clone();
1496 sysroot.push("lib");
1497 sysroot.push("rustlib");
1498 sysroot.push("src");
1499 sysroot.push("rust");
1500 sysroot
1501 });
1502 remap.push("=");
1503 remap.push("/rustc/");
1504 if let Some(commit_hash) = build_runner.bcx.rustc().commit_hash.as_ref() {
1505 remap.push(commit_hash);
1506 } else {
1507 remap.push(build_runner.bcx.rustc().version.to_string());
1508 }
1509 remap
1510}
1511
1512fn package_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1520 let pkg_root = unit.pkg.root();
1521 let ws_root = build_runner.bcx.ws.root();
1522 let mut remap = OsString::from("--remap-path-prefix=");
1523 let source_id = unit.pkg.package_id().source_id();
1524 if source_id.is_git() {
1525 remap.push(
1526 build_runner
1527 .bcx
1528 .gctx
1529 .git_checkouts_path()
1530 .as_path_unlocked(),
1531 );
1532 remap.push("=");
1533 } else if source_id.is_registry() {
1534 remap.push(
1535 build_runner
1536 .bcx
1537 .gctx
1538 .registry_source_path()
1539 .as_path_unlocked(),
1540 );
1541 remap.push("=");
1542 } else if pkg_root.strip_prefix(ws_root).is_ok() {
1543 remap.push(ws_root);
1544 remap.push("=."); } else {
1546 remap.push(pkg_root);
1547 remap.push("=");
1548 remap.push(unit.pkg.name());
1549 remap.push("-");
1550 remap.push(unit.pkg.version().to_string());
1551 }
1552 remap
1553}
1554
1555fn build_dir_remap(build_runner: &BuildRunner<'_, '_>) -> OsString {
1568 let build_dir = build_runner.bcx.ws.build_dir();
1569 let mut remap = OsString::from("--remap-path-prefix=");
1570 remap.push(build_dir.as_path_unlocked());
1571 remap.push("=/cargo/build-dir");
1572 remap
1573}
1574
1575fn check_cfg_args(unit: &Unit) -> Vec<OsString> {
1577 let gross_cap_estimation = unit.pkg.summary().features().len() * 7 + 25;
1595 let mut arg_feature = OsString::with_capacity(gross_cap_estimation);
1596
1597 arg_feature.push("cfg(feature, values(");
1598 for (i, feature) in unit.pkg.summary().features().keys().enumerate() {
1599 if i != 0 {
1600 arg_feature.push(", ");
1601 }
1602 arg_feature.push("\"");
1603 arg_feature.push(feature);
1604 arg_feature.push("\"");
1605 }
1606 arg_feature.push("))");
1607
1608 vec![
1617 OsString::from("--check-cfg"),
1618 OsString::from("cfg(docsrs,test)"),
1619 OsString::from("--check-cfg"),
1620 arg_feature,
1621 ]
1622}
1623
1624fn lto_args(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> Vec<OsString> {
1626 let mut result = Vec::new();
1627 let mut push = |arg: &str| {
1628 result.push(OsString::from("-C"));
1629 result.push(OsString::from(arg));
1630 };
1631 match build_runner.lto[unit] {
1632 lto::Lto::Run(None) => push("lto"),
1633 lto::Lto::Run(Some(s)) => push(&format!("lto={}", s)),
1634 lto::Lto::Off => {
1635 push("lto=off");
1636 push("embed-bitcode=no");
1637 }
1638 lto::Lto::ObjectAndBitcode => {} lto::Lto::OnlyBitcode => push("linker-plugin-lto"),
1640 lto::Lto::OnlyObject => push("embed-bitcode=no"),
1641 }
1642 result
1643}
1644
1645fn build_deps_args(
1651 cmd: &mut ProcessBuilder,
1652 build_runner: &BuildRunner<'_, '_>,
1653 unit: &Unit,
1654) -> CargoResult<()> {
1655 let bcx = build_runner.bcx;
1656 if build_runner.bcx.gctx.cli_unstable().build_dir_new_layout {
1657 let mut map = BTreeMap::new();
1658
1659 add_dep_arg(&mut map, build_runner, unit);
1661
1662 let paths = map.into_iter().map(|(_, path)| path).sorted_unstable();
1663
1664 for path in paths {
1665 cmd.arg("-L").arg(&{
1666 let mut deps = OsString::from("dependency=");
1667 deps.push(path);
1668 deps
1669 });
1670 }
1671 } else {
1672 cmd.arg("-L").arg(&{
1673 let mut deps = OsString::from("dependency=");
1674 deps.push(build_runner.files().deps_dir(unit));
1675 deps
1676 });
1677 }
1678
1679 if !unit.kind.is_host() {
1682 cmd.arg("-L").arg(&{
1683 let mut deps = OsString::from("dependency=");
1684 deps.push(build_runner.files().host_deps(unit));
1685 deps
1686 });
1687 }
1688
1689 let deps = build_runner.unit_deps(unit);
1690
1691 if !deps
1695 .iter()
1696 .any(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_linkable())
1697 {
1698 if let Some(dep) = deps.iter().find(|dep| {
1699 !dep.unit.mode.is_doc() && dep.unit.target.is_lib() && !dep.unit.artifact.is_true()
1700 }) {
1701 let dep_name = dep.unit.target.crate_name();
1702 let name = unit.target.crate_name();
1703 bcx.gctx.shell().print_report(&[
1704 Level::WARNING.secondary_title(format!("the package `{dep_name}` provides no linkable target"))
1705 .elements([
1706 Level::NOTE.message(format!("this might cause `{name}` to fail compilation")),
1707 Level::NOTE.message("this warning might turn into a hard error in the future"),
1708 Level::HELP.message(format!("consider adding 'dylib' or 'rlib' to key 'crate-type' in `{dep_name}`'s Cargo.toml"))
1709 ])
1710 ], false)?;
1711 }
1712 }
1713
1714 let mut unstable_opts = false;
1715
1716 let first_custom_build_dep = deps.iter().find(|dep| dep.unit.mode.is_run_custom_build());
1718 if let Some(dep) = first_custom_build_dep {
1719 let out_dir = &build_runner.files().build_script_out_dir(&dep.unit);
1720 cmd.env("OUT_DIR", &out_dir);
1721 }
1722
1723 let is_multiple_build_scripts_enabled = unit
1725 .pkg
1726 .manifest()
1727 .unstable_features()
1728 .require(Feature::multiple_build_scripts())
1729 .is_ok();
1730
1731 if is_multiple_build_scripts_enabled {
1732 for dep in deps {
1733 if dep.unit.mode.is_run_custom_build() {
1734 let out_dir = &build_runner.files().build_script_out_dir(&dep.unit);
1735 let target_name = dep.unit.target.name();
1736 let out_dir_prefix = target_name
1737 .strip_prefix("build-script-")
1738 .unwrap_or(target_name);
1739 let out_dir_name = format!("{out_dir_prefix}_OUT_DIR");
1740 cmd.env(&out_dir_name, &out_dir);
1741 }
1742 }
1743 }
1744 for arg in extern_args(build_runner, unit, &mut unstable_opts)? {
1745 cmd.arg(arg);
1746 }
1747
1748 for (var, env) in artifact::get_env(build_runner, deps)? {
1749 cmd.env(&var, env);
1750 }
1751
1752 if unstable_opts {
1755 cmd.arg("-Z").arg("unstable-options");
1756 }
1757
1758 Ok(())
1759}
1760
1761fn add_dep_arg<'a, 'b: 'a>(
1762 map: &mut BTreeMap<&'a Unit, PathBuf>,
1763 build_runner: &'b BuildRunner<'b, '_>,
1764 unit: &'a Unit,
1765) {
1766 if map.contains_key(&unit) {
1767 return;
1768 }
1769 map.insert(&unit, build_runner.files().deps_dir(&unit));
1770
1771 for dep in build_runner.unit_deps(unit) {
1772 add_dep_arg(map, build_runner, &dep.unit);
1773 }
1774}
1775
1776fn add_custom_flags(
1780 cmd: &mut ProcessBuilder,
1781 build_script_outputs: &BuildScriptOutputs,
1782 metadata_vec: Option<Vec<UnitHash>>,
1783) -> CargoResult<()> {
1784 if let Some(metadata_vec) = metadata_vec {
1785 for metadata in metadata_vec {
1786 if let Some(output) = build_script_outputs.get(metadata) {
1787 for cfg in output.cfgs.iter() {
1788 cmd.arg("--cfg").arg(cfg);
1789 }
1790 for check_cfg in &output.check_cfgs {
1791 cmd.arg("--check-cfg").arg(check_cfg);
1792 }
1793 for (name, value) in output.env.iter() {
1794 cmd.env(name, value);
1795 }
1796 }
1797 }
1798 }
1799
1800 Ok(())
1801}
1802
1803pub fn extern_args(
1805 build_runner: &BuildRunner<'_, '_>,
1806 unit: &Unit,
1807 unstable_opts: &mut bool,
1808) -> CargoResult<Vec<OsString>> {
1809 let mut result = Vec::new();
1810 let deps = build_runner.unit_deps(unit);
1811
1812 let no_embed_metadata = build_runner.bcx.gctx.cli_unstable().no_embed_metadata;
1813
1814 let mut link_to =
1816 |dep: &UnitDep, extern_crate_name: InternedString, noprelude: bool| -> CargoResult<()> {
1817 let mut value = OsString::new();
1818 let mut opts = Vec::new();
1819 let is_public_dependency_enabled = unit
1820 .pkg
1821 .manifest()
1822 .unstable_features()
1823 .require(Feature::public_dependency())
1824 .is_ok()
1825 || build_runner.bcx.gctx.cli_unstable().public_dependency;
1826 if !dep.public && unit.target.is_lib() && is_public_dependency_enabled {
1827 opts.push("priv");
1828 *unstable_opts = true;
1829 }
1830 if noprelude {
1831 opts.push("noprelude");
1832 *unstable_opts = true;
1833 }
1834 if !opts.is_empty() {
1835 value.push(opts.join(","));
1836 value.push(":");
1837 }
1838 value.push(extern_crate_name.as_str());
1839 value.push("=");
1840
1841 let mut pass = |file| {
1842 let mut value = value.clone();
1843 value.push(file);
1844 result.push(OsString::from("--extern"));
1845 result.push(value);
1846 };
1847
1848 let outputs = build_runner.outputs(&dep.unit)?;
1849
1850 if build_runner.only_requires_rmeta(unit, &dep.unit) || dep.unit.mode.is_check() {
1851 let output = outputs
1853 .iter()
1854 .find(|output| output.flavor == FileFlavor::Rmeta)
1855 .expect("failed to find rmeta dep for pipelined dep");
1856 pass(&output.path);
1857 } else {
1858 for output in outputs.iter() {
1860 if output.flavor == FileFlavor::Linkable {
1861 pass(&output.path);
1862 }
1863 else if no_embed_metadata && output.flavor == FileFlavor::Rmeta {
1867 pass(&output.path);
1868 }
1869 }
1870 }
1871 Ok(())
1872 };
1873
1874 for dep in deps {
1875 if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
1876 link_to(dep, dep.extern_crate_name, dep.noprelude)?;
1877 }
1878 }
1879 if unit.target.proc_macro() {
1880 result.push(OsString::from("--extern"));
1882 result.push(OsString::from("proc_macro"));
1883 }
1884
1885 Ok(result)
1886}
1887
1888fn envify(s: &str) -> String {
1889 s.chars()
1890 .flat_map(|c| c.to_uppercase())
1891 .map(|c| if c == '-' { '_' } else { c })
1892 .collect()
1893}
1894
1895struct OutputOptions {
1898 format: MessageFormat,
1900 cache_cell: Option<(PathBuf, OnceCell<File>)>,
1905 show_diagnostics: bool,
1913 warnings_seen: usize,
1915 errors_seen: usize,
1917}
1918
1919impl OutputOptions {
1920 fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions {
1921 let path = build_runner.files().message_cache_path(unit);
1922 drop(fs::remove_file(&path));
1924 let cache_cell = Some((path, OnceCell::new()));
1925 let show_diagnostics =
1926 build_runner.bcx.gctx.warning_handling().unwrap_or_default() != WarningHandling::Allow;
1927 OutputOptions {
1928 format: build_runner.bcx.build_config.message_format,
1929 cache_cell,
1930 show_diagnostics,
1931 warnings_seen: 0,
1932 errors_seen: 0,
1933 }
1934 }
1935}
1936
1937struct ManifestErrorContext {
1943 path: PathBuf,
1945 spans: toml::Spanned<toml::de::DeTable<'static>>,
1947 contents: String,
1949 rename_table: HashMap<InternedString, InternedString>,
1952 requested_kinds: Vec<CompileKind>,
1955 cfgs: Vec<Vec<Cfg>>,
1958 host_name: InternedString,
1959 cwd: PathBuf,
1961 term_width: usize,
1963}
1964
1965fn on_stdout_line(
1966 state: &JobState<'_, '_>,
1967 line: &str,
1968 _package_id: PackageId,
1969 _target: &Target,
1970) -> CargoResult<()> {
1971 state.stdout(line.to_string())?;
1972 Ok(())
1973}
1974
1975fn on_stderr_line(
1976 state: &JobState<'_, '_>,
1977 line: &str,
1978 package_id: PackageId,
1979 manifest: &ManifestErrorContext,
1980 target: &Target,
1981 options: &mut OutputOptions,
1982) -> CargoResult<()> {
1983 if on_stderr_line_inner(state, line, package_id, manifest, target, options)? {
1984 if let Some((path, cell)) = &mut options.cache_cell {
1986 let f = cell.try_borrow_mut_with(|| paths::create(path))?;
1988 debug_assert!(!line.contains('\n'));
1989 f.write_all(line.as_bytes())?;
1990 f.write_all(&[b'\n'])?;
1991 }
1992 }
1993 Ok(())
1994}
1995
1996fn on_stderr_line_inner(
1998 state: &JobState<'_, '_>,
1999 line: &str,
2000 package_id: PackageId,
2001 manifest: &ManifestErrorContext,
2002 target: &Target,
2003 options: &mut OutputOptions,
2004) -> CargoResult<bool> {
2005 if !line.starts_with('{') {
2011 state.stderr(line.to_string())?;
2012 return Ok(true);
2013 }
2014
2015 let mut compiler_message: Box<serde_json::value::RawValue> = match serde_json::from_str(line) {
2016 Ok(msg) => msg,
2017
2018 Err(e) => {
2022 debug!("failed to parse json: {:?}", e);
2023 state.stderr(line.to_string())?;
2024 return Ok(true);
2025 }
2026 };
2027
2028 let count_diagnostic = |level, options: &mut OutputOptions| {
2029 if level == "warning" {
2030 options.warnings_seen += 1;
2031 } else if level == "error" {
2032 options.errors_seen += 1;
2033 }
2034 };
2035
2036 if let Ok(report) = serde_json::from_str::<FutureIncompatReport>(compiler_message.get()) {
2037 for item in &report.future_incompat_report {
2038 count_diagnostic(&*item.diagnostic.level, options);
2039 }
2040 state.future_incompat_report(report.future_incompat_report);
2041 return Ok(true);
2042 }
2043
2044 let res = serde_json::from_str::<SectionTiming>(compiler_message.get());
2045 if let Ok(timing_record) = res {
2046 state.on_section_timing_emitted(timing_record);
2047 return Ok(false);
2048 }
2049
2050 let add_pub_in_priv_diagnostic = |diag: &mut String| -> bool {
2052 static PRIV_DEP_REGEX: LazyLock<Regex> =
2061 LazyLock::new(|| Regex::new("from private dependency '([A-Za-z0-9-_]+)'").unwrap());
2062 if let Some(crate_name) = PRIV_DEP_REGEX.captures(diag).and_then(|m| m.get(1))
2063 && let Some(span) = manifest.find_crate_span(crate_name.as_str())
2064 {
2065 let rel_path = pathdiff::diff_paths(&manifest.path, &manifest.cwd)
2066 .unwrap_or_else(|| manifest.path.clone())
2067 .display()
2068 .to_string();
2069 let report = [Group::with_title(Level::NOTE.secondary_title(format!(
2070 "dependency `{}` declared here",
2071 crate_name.as_str()
2072 )))
2073 .element(
2074 Snippet::source(&manifest.contents)
2075 .path(rel_path)
2076 .annotation(AnnotationKind::Context.span(span)),
2077 )];
2078
2079 let rendered = Renderer::styled()
2080 .term_width(manifest.term_width)
2081 .render(&report);
2082 diag.push_str(&rendered);
2083 diag.push('\n');
2084 return true;
2085 }
2086 false
2087 };
2088
2089 match options.format {
2092 MessageFormat::Human
2097 | MessageFormat::Short
2098 | MessageFormat::Json {
2099 render_diagnostics: true,
2100 ..
2101 } => {
2102 #[derive(serde::Deserialize)]
2103 struct CompilerMessage<'a> {
2104 rendered: String,
2108 #[serde(borrow)]
2109 message: Cow<'a, str>,
2110 #[serde(borrow)]
2111 level: Cow<'a, str>,
2112 children: Vec<PartialDiagnostic>,
2113 code: Option<DiagnosticCode>,
2114 }
2115
2116 #[derive(serde::Deserialize)]
2125 struct PartialDiagnostic {
2126 spans: Vec<PartialDiagnosticSpan>,
2127 }
2128
2129 #[derive(serde::Deserialize)]
2131 struct PartialDiagnosticSpan {
2132 suggestion_applicability: Option<Applicability>,
2133 }
2134
2135 #[derive(serde::Deserialize)]
2136 struct DiagnosticCode {
2137 code: String,
2138 }
2139
2140 if let Ok(mut msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2141 {
2142 if msg.message.starts_with("aborting due to")
2143 || msg.message.ends_with("warning emitted")
2144 || msg.message.ends_with("warnings emitted")
2145 {
2146 return Ok(true);
2148 }
2149 if msg.rendered.ends_with('\n') {
2151 msg.rendered.pop();
2152 }
2153 let mut rendered = msg.rendered;
2154 if options.show_diagnostics {
2155 let machine_applicable: bool = msg
2156 .children
2157 .iter()
2158 .map(|child| {
2159 child
2160 .spans
2161 .iter()
2162 .filter_map(|span| span.suggestion_applicability)
2163 .any(|app| app == Applicability::MachineApplicable)
2164 })
2165 .any(|b| b);
2166 count_diagnostic(&msg.level, options);
2167 if msg
2168 .code
2169 .as_ref()
2170 .is_some_and(|c| c.code == "exported_private_dependencies")
2171 && options.format != MessageFormat::Short
2172 {
2173 add_pub_in_priv_diagnostic(&mut rendered);
2174 }
2175 let lint = msg.code.is_some();
2176 state.emit_diag(&msg.level, rendered, lint, machine_applicable)?;
2177 }
2178 return Ok(true);
2179 }
2180 }
2181
2182 MessageFormat::Json { ansi, .. } => {
2183 #[derive(serde::Deserialize, serde::Serialize)]
2184 struct CompilerMessage<'a> {
2185 rendered: String,
2186 #[serde(flatten, borrow)]
2187 other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2188 code: Option<DiagnosticCode<'a>>,
2189 }
2190
2191 #[derive(serde::Deserialize, serde::Serialize)]
2192 struct DiagnosticCode<'a> {
2193 code: String,
2194 #[serde(flatten, borrow)]
2195 other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2196 }
2197
2198 if let Ok(mut error) =
2199 serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2200 {
2201 let modified_diag = if error
2202 .code
2203 .as_ref()
2204 .is_some_and(|c| c.code == "exported_private_dependencies")
2205 {
2206 add_pub_in_priv_diagnostic(&mut error.rendered)
2207 } else {
2208 false
2209 };
2210
2211 if !ansi {
2215 error.rendered = anstream::adapter::strip_str(&error.rendered).to_string();
2216 }
2217 if !ansi || modified_diag {
2218 let new_line = serde_json::to_string(&error)?;
2219 compiler_message = serde_json::value::RawValue::from_string(new_line)?;
2220 }
2221 }
2222 }
2223 }
2224
2225 #[derive(serde::Deserialize)]
2232 struct ArtifactNotification<'a> {
2233 #[serde(borrow)]
2234 artifact: Cow<'a, str>,
2235 }
2236
2237 if let Ok(artifact) = serde_json::from_str::<ArtifactNotification<'_>>(compiler_message.get()) {
2238 trace!("found directive from rustc: `{}`", artifact.artifact);
2239 if artifact.artifact.ends_with(".rmeta") {
2240 debug!("looks like metadata finished early!");
2241 state.rmeta_produced();
2242 }
2243 return Ok(false);
2244 }
2245
2246 if !options.show_diagnostics {
2251 return Ok(true);
2252 }
2253
2254 #[derive(serde::Deserialize)]
2255 struct CompilerMessage<'a> {
2256 #[serde(borrow)]
2257 message: Cow<'a, str>,
2258 #[serde(borrow)]
2259 level: Cow<'a, str>,
2260 }
2261
2262 if let Ok(msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get()) {
2263 if msg.message.starts_with("aborting due to")
2264 || msg.message.ends_with("warning emitted")
2265 || msg.message.ends_with("warnings emitted")
2266 {
2267 return Ok(true);
2269 }
2270 count_diagnostic(&msg.level, options);
2271 }
2272
2273 let msg = machine_message::FromCompiler {
2274 package_id: package_id.to_spec(),
2275 manifest_path: &manifest.path,
2276 target,
2277 message: compiler_message,
2278 }
2279 .to_json_string();
2280
2281 state.stdout(msg)?;
2285 Ok(true)
2286}
2287
2288impl ManifestErrorContext {
2289 fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> ManifestErrorContext {
2290 let mut duplicates = HashSet::new();
2291 let mut rename_table = HashMap::new();
2292
2293 for dep in build_runner.unit_deps(unit) {
2294 let unrenamed_id = dep.unit.pkg.package_id().name();
2295 if duplicates.contains(&unrenamed_id) {
2296 continue;
2297 }
2298 match rename_table.entry(unrenamed_id) {
2299 std::collections::hash_map::Entry::Occupied(occ) => {
2300 occ.remove_entry();
2301 duplicates.insert(unrenamed_id);
2302 }
2303 std::collections::hash_map::Entry::Vacant(vac) => {
2304 vac.insert(dep.extern_crate_name);
2305 }
2306 }
2307 }
2308
2309 let bcx = build_runner.bcx;
2310 ManifestErrorContext {
2311 path: unit.pkg.manifest_path().to_owned(),
2312 spans: unit.pkg.manifest().document().clone(),
2313 contents: unit.pkg.manifest().contents().to_owned(),
2314 requested_kinds: bcx.target_data.requested_kinds().to_owned(),
2315 host_name: bcx.rustc().host,
2316 rename_table,
2317 cwd: path_args(build_runner.bcx.ws, unit).1,
2318 cfgs: bcx
2319 .target_data
2320 .requested_kinds()
2321 .iter()
2322 .map(|k| bcx.target_data.cfg(*k).to_owned())
2323 .collect(),
2324 term_width: bcx
2325 .gctx
2326 .shell()
2327 .err_width()
2328 .diagnostic_terminal_width()
2329 .unwrap_or(annotate_snippets::renderer::DEFAULT_TERM_WIDTH),
2330 }
2331 }
2332
2333 fn requested_target_names(&self) -> impl Iterator<Item = &str> {
2334 self.requested_kinds.iter().map(|kind| match kind {
2335 CompileKind::Host => &self.host_name,
2336 CompileKind::Target(target) => target.short_name(),
2337 })
2338 }
2339
2340 fn find_crate_span(&self, unrenamed: &str) -> Option<Range<usize>> {
2354 let orig_name = self.rename_table.get(unrenamed)?.as_str();
2355
2356 if let Some((k, v)) = get_key_value(&self.spans, &["dependencies", orig_name]) {
2357 if let Some(package) = v.get_ref().as_table().and_then(|t| t.get("package")) {
2366 return Some(package.span());
2367 } else {
2368 return Some(k.span());
2369 }
2370 }
2371
2372 if let Some(target) = self
2377 .spans
2378 .as_ref()
2379 .get("target")
2380 .and_then(|t| t.as_ref().as_table())
2381 {
2382 for (platform, platform_table) in target.iter() {
2383 match platform.as_ref().parse::<Platform>() {
2384 Ok(Platform::Name(name)) => {
2385 if !self.requested_target_names().any(|n| n == name) {
2386 continue;
2387 }
2388 }
2389 Ok(Platform::Cfg(cfg_expr)) => {
2390 if !self.cfgs.iter().any(|cfgs| cfg_expr.matches(cfgs)) {
2391 continue;
2392 }
2393 }
2394 Err(_) => continue,
2395 }
2396
2397 let Some(platform_table) = platform_table.as_ref().as_table() else {
2398 continue;
2399 };
2400
2401 if let Some(deps) = platform_table
2402 .get("dependencies")
2403 .and_then(|d| d.as_ref().as_table())
2404 {
2405 if let Some((k, v)) = deps.get_key_value(orig_name) {
2406 if let Some(package) = v.get_ref().as_table().and_then(|t| t.get("package"))
2407 {
2408 return Some(package.span());
2409 } else {
2410 return Some(k.span());
2411 }
2412 }
2413 }
2414 }
2415 }
2416 None
2417 }
2418}
2419
2420fn replay_output_cache(
2424 package_id: PackageId,
2425 manifest: ManifestErrorContext,
2426 target: &Target,
2427 path: PathBuf,
2428 format: MessageFormat,
2429 show_diagnostics: bool,
2430) -> Work {
2431 let target = target.clone();
2432 let mut options = OutputOptions {
2433 format,
2434 cache_cell: None,
2435 show_diagnostics,
2436 warnings_seen: 0,
2437 errors_seen: 0,
2438 };
2439 Work::new(move |state| {
2440 if !path.exists() {
2441 return Ok(());
2443 }
2444 let file = paths::open(&path)?;
2448 let mut reader = std::io::BufReader::new(file);
2449 let mut line = String::new();
2450 loop {
2451 let length = reader.read_line(&mut line)?;
2452 if length == 0 {
2453 break;
2454 }
2455 let trimmed = line.trim_end_matches(&['\n', '\r'][..]);
2456 on_stderr_line(state, trimmed, package_id, &manifest, &target, &mut options)?;
2457 line.clear();
2458 }
2459 Ok(())
2460 })
2461}
2462
2463fn descriptive_pkg_name(name: &str, target: &Target, mode: &CompileMode) -> String {
2466 let desc_name = target.description_named();
2467 let mode = if mode.is_rustc_test() && !(target.is_test() || target.is_bench()) {
2468 " test"
2469 } else if mode.is_doc_test() {
2470 " doctest"
2471 } else if mode.is_doc() {
2472 " doc"
2473 } else {
2474 ""
2475 };
2476 format!("`{name}` ({desc_name}{mode})")
2477}
2478
2479pub(crate) fn apply_env_config(
2481 gctx: &crate::GlobalContext,
2482 cmd: &mut ProcessBuilder,
2483) -> CargoResult<()> {
2484 for (key, value) in gctx.env_config()?.iter() {
2485 if cmd.get_envs().contains_key(key) {
2487 continue;
2488 }
2489 cmd.env(key, value);
2490 }
2491 Ok(())
2492}
2493
2494fn should_include_scrape_units(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
2496 unit.mode.is_doc() && bcx.scrape_units.len() > 0 && bcx.ws.unit_needs_doc_scrape(unit)
2497}
2498
2499fn scrape_output_path(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<PathBuf> {
2501 assert!(unit.mode.is_doc() || unit.mode.is_doc_scrape());
2502 build_runner
2503 .outputs(unit)
2504 .map(|outputs| outputs[0].path.clone())
2505}
2506
2507fn rustdoc_dep_info_loc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> PathBuf {
2509 let mut loc = build_runner.files().fingerprint_file_path(unit, "");
2510 loc.set_extension("d");
2511 loc
2512}