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::BuildContext;
76pub use self::build_context::FileFlavor;
77pub use self::build_context::FileType;
78pub use self::build_context::RustcTargetData;
79pub use self::build_context::TargetInfo;
80pub use self::build_runner::{BuildRunner, Metadata, UnitHash};
81pub use self::compilation::{Compilation, Doctest, UnitOutput};
82pub use self::compile_kind::{CompileKind, CompileKindFallback, CompileTarget};
83pub use self::crate_type::CrateType;
84pub use self::custom_build::LinkArgTarget;
85pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts, LibraryPath};
86pub(crate) use self::fingerprint::DirtyReason;
87pub use self::fingerprint::RustdocFingerprint;
88pub use self::job_queue::Freshness;
89use self::job_queue::{Job, JobQueue, JobState, Work};
90pub(crate) use self::layout::Layout;
91pub use self::lto::Lto;
92use self::output_depinfo::output_depinfo;
93use self::output_sbom::build_sbom;
94use self::unit_graph::UnitDep;
95use crate::core::compiler::future_incompat::FutureIncompatReport;
96use crate::core::compiler::timings::SectionTiming;
97pub use crate::core::compiler::unit::{Unit, UnitInterner};
98use crate::core::manifest::TargetSourcePath;
99use crate::core::profiles::{PanicStrategy, Profile, StripInner};
100use crate::core::{Feature, PackageId, Target, Verbosity};
101use crate::util::OnceExt;
102use crate::util::context::WarningHandling;
103use crate::util::errors::{CargoResult, VerboseError};
104use crate::util::interning::InternedString;
105use crate::util::lints::get_key_value;
106use crate::util::machine_message::{self, Message};
107use crate::util::{add_path_args, internal, path_args};
108use cargo_util::{ProcessBuilder, ProcessError, paths};
109use cargo_util_schemas::manifest::TomlDebugInfo;
110use cargo_util_schemas::manifest::TomlTrimPaths;
111use cargo_util_schemas::manifest::TomlTrimPathsValue;
112use rustfix::diagnostics::Applicability;
113pub(crate) use timings::CompilationSection;
114
115const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version";
116
117pub trait Executor: Send + Sync + 'static {
121 fn init(&self, _build_runner: &BuildRunner<'_, '_>, _unit: &Unit) {}
125
126 fn exec(
129 &self,
130 cmd: &ProcessBuilder,
131 id: PackageId,
132 target: &Target,
133 mode: CompileMode,
134 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
135 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
136 ) -> CargoResult<()>;
137
138 fn force_rebuild(&self, _unit: &Unit) -> bool {
141 false
142 }
143}
144
145#[derive(Copy, Clone)]
148pub struct DefaultExecutor;
149
150impl Executor for DefaultExecutor {
151 #[instrument(name = "rustc", skip_all, fields(package = id.name().as_str(), process = cmd.to_string()))]
152 fn exec(
153 &self,
154 cmd: &ProcessBuilder,
155 id: PackageId,
156 _target: &Target,
157 _mode: CompileMode,
158 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
159 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
160 ) -> CargoResult<()> {
161 cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
162 .map(drop)
163 }
164}
165
166#[tracing::instrument(skip(build_runner, jobs, exec))]
176fn compile<'gctx>(
177 build_runner: &mut BuildRunner<'_, 'gctx>,
178 jobs: &mut JobQueue<'gctx>,
179 unit: &Unit,
180 exec: &Arc<dyn Executor>,
181 force_rebuild: bool,
182) -> CargoResult<()> {
183 let bcx = build_runner.bcx;
184 if !build_runner.compiled.insert(unit.clone()) {
185 return Ok(());
186 }
187
188 if !unit.skip_non_compile_time_dep {
192 fingerprint::prepare_init(build_runner, unit)?;
195
196 let job = if unit.mode.is_run_custom_build() {
197 custom_build::prepare(build_runner, unit)?
198 } else if unit.mode.is_doc_test() {
199 Job::new_fresh()
201 } else {
202 let force = exec.force_rebuild(unit) || force_rebuild;
203 let mut job = fingerprint::prepare_target(build_runner, unit, force)?;
204 job.before(if job.freshness().is_dirty() {
205 let work = if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
206 rustdoc(build_runner, unit)?
207 } else {
208 rustc(build_runner, unit, exec)?
209 };
210 work.then(link_targets(build_runner, unit, false)?)
211 } else {
212 let show_diagnostics = unit.show_warnings(bcx.gctx)
215 && build_runner.bcx.gctx.warning_handling()? != WarningHandling::Allow;
216 let manifest = ManifestErrorContext::new(build_runner, unit);
217 let work = replay_output_cache(
218 unit.pkg.package_id(),
219 manifest,
220 &unit.target,
221 build_runner.files().message_cache_path(unit),
222 build_runner.bcx.build_config.message_format,
223 show_diagnostics,
224 );
225 work.then(link_targets(build_runner, unit, true)?)
227 });
228
229 job
230 };
231 jobs.enqueue(build_runner, unit, job)?;
232 }
233
234 let deps = Vec::from(build_runner.unit_deps(unit)); for dep in deps {
237 compile(build_runner, jobs, &dep.unit, exec, false)?;
238 }
239
240 Ok(())
241}
242
243fn make_failed_scrape_diagnostic(
246 build_runner: &BuildRunner<'_, '_>,
247 unit: &Unit,
248 top_line: impl Display,
249) -> String {
250 let manifest_path = unit.pkg.manifest_path();
251 let relative_manifest_path = manifest_path
252 .strip_prefix(build_runner.bcx.ws.root())
253 .unwrap_or(&manifest_path);
254
255 format!(
256 "\
257{top_line}
258 Try running with `--verbose` to see the error message.
259 If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in {}",
260 relative_manifest_path.display()
261 )
262}
263
264fn rustc(
266 build_runner: &mut BuildRunner<'_, '_>,
267 unit: &Unit,
268 exec: &Arc<dyn Executor>,
269) -> CargoResult<Work> {
270 let mut rustc = prepare_rustc(build_runner, unit)?;
271
272 let name = unit.pkg.name();
273
274 let outputs = build_runner.outputs(unit)?;
275 let root = build_runner.files().out_dir(unit);
276
277 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
279 let current_id = unit.pkg.package_id();
280 let manifest = ManifestErrorContext::new(build_runner, unit);
281 let build_scripts = build_runner.build_scripts.get(unit).cloned();
282
283 let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib());
286
287 let dep_info_name =
288 if let Some(c_extra_filename) = build_runner.files().metadata(unit).c_extra_filename() {
289 format!("{}-{}.d", unit.target.crate_name(), c_extra_filename)
290 } else {
291 format!("{}.d", unit.target.crate_name())
292 };
293 let rustc_dep_info_loc = root.join(dep_info_name);
294 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
295
296 let mut output_options = OutputOptions::new(build_runner, unit);
297 let package_id = unit.pkg.package_id();
298 let target = Target::clone(&unit.target);
299 let mode = unit.mode;
300
301 exec.init(build_runner, unit);
302 let exec = exec.clone();
303
304 let root_output = build_runner.files().host_dest().map(|v| v.to_path_buf());
305 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
306 let pkg_root = unit.pkg.root().to_path_buf();
307 let cwd = rustc
308 .get_cwd()
309 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
310 .to_path_buf();
311 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
312 let script_metadatas = build_runner.find_build_script_metadatas(unit);
313 let is_local = unit.is_local();
314 let artifact = unit.artifact;
315 let sbom_files = build_runner.sbom_output_files(unit)?;
316 let sbom = build_sbom(build_runner, unit)?;
317
318 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
319 && !matches!(
320 build_runner.bcx.gctx.shell().verbosity(),
321 Verbosity::Verbose
322 );
323 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
324 let target_desc = unit.target.description_named();
327 let mut for_scrape_units = build_runner
328 .bcx
329 .scrape_units_have_dep_on(unit)
330 .into_iter()
331 .map(|unit| unit.target.description_named())
332 .collect::<Vec<_>>();
333 for_scrape_units.sort();
334 let for_scrape_units = for_scrape_units.join(", ");
335 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}"))
336 });
337 if hide_diagnostics_for_scrape_unit {
338 output_options.show_diagnostics = false;
339 }
340 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
341 return Ok(Work::new(move |state| {
342 if artifact.is_true() {
346 paths::create_dir_all(&root)?;
347 }
348
349 if let Some(build_scripts) = build_scripts {
357 let script_outputs = build_script_outputs.lock().unwrap();
358 add_native_deps(
359 &mut rustc,
360 &script_outputs,
361 &build_scripts,
362 pass_l_flag,
363 &target,
364 current_id,
365 mode,
366 )?;
367 if let Some(ref root_output) = root_output {
368 add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, root_output)?;
369 }
370 add_custom_flags(&mut rustc, &script_outputs, script_metadatas)?;
371 }
372
373 for output in outputs.iter() {
374 if output.path.extension() == Some(OsStr::new("rmeta")) {
378 let dst = root.join(&output.path).with_extension("rlib");
379 if dst.exists() {
380 paths::remove_file(&dst)?;
381 }
382 }
383
384 if output.hardlink.is_some() && output.path.exists() {
389 _ = paths::remove_file(&output.path).map_err(|e| {
390 tracing::debug!(
391 "failed to delete previous output file `{:?}`: {e:?}",
392 output.path
393 );
394 });
395 }
396 }
397
398 state.running(&rustc);
399 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
400 for file in sbom_files {
401 tracing::debug!("writing sbom to {}", file.display());
402 let outfile = BufWriter::new(paths::create(&file)?);
403 serde_json::to_writer(outfile, &sbom)?;
404 }
405
406 let result = exec
407 .exec(
408 &rustc,
409 package_id,
410 &target,
411 mode,
412 &mut |line| on_stdout_line(state, line, package_id, &target),
413 &mut |line| {
414 on_stderr_line(
415 state,
416 line,
417 package_id,
418 &manifest,
419 &target,
420 &mut output_options,
421 )
422 },
423 )
424 .map_err(|e| {
425 if output_options.errors_seen == 0 {
426 e
431 } else {
432 verbose_if_simple_exit_code(e)
433 }
434 })
435 .with_context(|| {
436 let warnings = match output_options.warnings_seen {
438 0 => String::new(),
439 1 => "; 1 warning emitted".to_string(),
440 count => format!("; {} warnings emitted", count),
441 };
442 let errors = match output_options.errors_seen {
443 0 => String::new(),
444 1 => " due to 1 previous error".to_string(),
445 count => format!(" due to {} previous errors", count),
446 };
447 let name = descriptive_pkg_name(&name, &target, &mode);
448 format!("could not compile {name}{errors}{warnings}")
449 });
450
451 if let Err(e) = result {
452 if let Some(diagnostic) = failed_scrape_diagnostic {
453 state.warning(diagnostic);
454 }
455
456 return Err(e);
457 }
458
459 debug_assert_eq!(output_options.errors_seen, 0);
461
462 if rustc_dep_info_loc.exists() {
463 fingerprint::translate_dep_info(
464 &rustc_dep_info_loc,
465 &dep_info_loc,
466 &cwd,
467 &pkg_root,
468 &build_dir,
469 &rustc,
470 is_local,
472 &env_config,
473 )
474 .with_context(|| {
475 internal(format!(
476 "could not parse/generate dep info at: {}",
477 rustc_dep_info_loc.display()
478 ))
479 })?;
480 paths::set_file_time_no_err(dep_info_loc, timestamp);
483 }
484
485 if mode.is_check() {
499 for output in outputs.iter() {
500 paths::set_file_time_no_err(&output.path, timestamp);
501 }
502 }
503
504 Ok(())
505 }));
506
507 fn add_native_deps(
510 rustc: &mut ProcessBuilder,
511 build_script_outputs: &BuildScriptOutputs,
512 build_scripts: &BuildScripts,
513 pass_l_flag: bool,
514 target: &Target,
515 current_id: PackageId,
516 mode: CompileMode,
517 ) -> CargoResult<()> {
518 let mut library_paths = vec![];
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 library_paths.extend(output.library_paths.iter());
528 }
529
530 library_paths.sort_by_key(|p| match p {
536 LibraryPath::CargoArtifact(_) => 0,
537 LibraryPath::External(_) => 1,
538 });
539
540 for path in library_paths.iter() {
541 rustc.arg("-L").arg(path.as_ref());
542 }
543
544 for key in build_scripts.to_link.iter() {
545 let output = build_script_outputs.get(key.1).ok_or_else(|| {
546 internal(format!(
547 "couldn't find build script output for {}/{}",
548 key.0, key.1
549 ))
550 })?;
551
552 if key.0 == current_id {
553 if pass_l_flag {
554 for name in output.library_links.iter() {
555 rustc.arg("-l").arg(name);
556 }
557 }
558 }
559
560 for (lt, arg) in &output.linker_args {
561 if lt.applies_to(target, mode)
567 && (key.0 == current_id || *lt == LinkArgTarget::Cdylib)
568 {
569 rustc.arg("-C").arg(format!("link-arg={}", arg));
570 }
571 }
572 }
573 Ok(())
574 }
575}
576
577fn verbose_if_simple_exit_code(err: Error) -> Error {
578 match err
581 .downcast_ref::<ProcessError>()
582 .as_ref()
583 .and_then(|perr| perr.code)
584 {
585 Some(n) if cargo_util::is_simple_exit_code(n) => VerboseError::new(err).into(),
586 _ => err,
587 }
588}
589
590fn link_targets(
593 build_runner: &mut BuildRunner<'_, '_>,
594 unit: &Unit,
595 fresh: bool,
596) -> CargoResult<Work> {
597 let bcx = build_runner.bcx;
598 let outputs = build_runner.outputs(unit)?;
599 let export_dir = build_runner.files().export_dir();
600 let package_id = unit.pkg.package_id();
601 let manifest_path = PathBuf::from(unit.pkg.manifest_path());
602 let profile = unit.profile.clone();
603 let unit_mode = unit.mode;
604 let features = unit.features.iter().map(|s| s.to_string()).collect();
605 let json_messages = bcx.build_config.emit_json();
606 let executable = build_runner.get_executable(unit)?;
607 let mut target = Target::clone(&unit.target);
608 if let TargetSourcePath::Metabuild = target.src_path() {
609 let path = unit
611 .pkg
612 .manifest()
613 .metabuild_path(build_runner.bcx.ws.build_dir());
614 target.set_src_path(TargetSourcePath::Path(path));
615 }
616
617 Ok(Work::new(move |state| {
618 let mut destinations = vec![];
623 for output in outputs.iter() {
624 let src = &output.path;
625 if !src.exists() {
628 continue;
629 }
630 let Some(dst) = output.hardlink.as_ref() else {
631 destinations.push(src.clone());
632 continue;
633 };
634 destinations.push(dst.clone());
635 paths::link_or_copy(src, dst)?;
636 if let Some(ref path) = output.export_path {
637 let export_dir = export_dir.as_ref().unwrap();
638 paths::create_dir_all(export_dir)?;
639
640 paths::link_or_copy(src, path)?;
641 }
642 }
643
644 if json_messages {
645 let debuginfo = match profile.debuginfo.into_inner() {
646 TomlDebugInfo::None => machine_message::ArtifactDebuginfo::Int(0),
647 TomlDebugInfo::Limited => machine_message::ArtifactDebuginfo::Int(1),
648 TomlDebugInfo::Full => machine_message::ArtifactDebuginfo::Int(2),
649 TomlDebugInfo::LineDirectivesOnly => {
650 machine_message::ArtifactDebuginfo::Named("line-directives-only")
651 }
652 TomlDebugInfo::LineTablesOnly => {
653 machine_message::ArtifactDebuginfo::Named("line-tables-only")
654 }
655 };
656 let art_profile = machine_message::ArtifactProfile {
657 opt_level: profile.opt_level.as_str(),
658 debuginfo: Some(debuginfo),
659 debug_assertions: profile.debug_assertions,
660 overflow_checks: profile.overflow_checks,
661 test: unit_mode.is_any_test(),
662 };
663
664 let msg = machine_message::Artifact {
665 package_id: package_id.to_spec(),
666 manifest_path,
667 target: &target,
668 profile: art_profile,
669 features,
670 filenames: destinations,
671 executable,
672 fresh,
673 }
674 .to_json_string();
675 state.stdout(msg)?;
676 }
677 Ok(())
678 }))
679}
680
681fn add_plugin_deps(
685 rustc: &mut ProcessBuilder,
686 build_script_outputs: &BuildScriptOutputs,
687 build_scripts: &BuildScripts,
688 root_output: &Path,
689) -> CargoResult<()> {
690 let var = paths::dylib_path_envvar();
691 let search_path = rustc.get_env(var).unwrap_or_default();
692 let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>();
693 for (pkg_id, metadata) in &build_scripts.plugins {
694 let output = build_script_outputs
695 .get(*metadata)
696 .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?;
697 search_path.append(&mut filter_dynamic_search_path(
698 output.library_paths.iter().map(AsRef::as_ref),
699 root_output,
700 ));
701 }
702 let search_path = paths::join_paths(&search_path, var)?;
703 rustc.env(var, &search_path);
704 Ok(())
705}
706
707fn get_dynamic_search_path(path: &Path) -> &Path {
708 match path.to_str().and_then(|s| s.split_once("=")) {
709 Some(("native" | "crate" | "dependency" | "framework" | "all", path)) => Path::new(path),
710 _ => path,
711 }
712}
713
714fn filter_dynamic_search_path<'a, I>(paths: I, root_output: &Path) -> Vec<PathBuf>
720where
721 I: Iterator<Item = &'a PathBuf>,
722{
723 let mut search_path = vec![];
724 for dir in paths {
725 let dir = get_dynamic_search_path(dir);
726 if dir.starts_with(&root_output) {
727 search_path.push(dir.to_path_buf());
728 } else {
729 debug!(
730 "Not including path {} in runtime library search path because it is \
731 outside target root {}",
732 dir.display(),
733 root_output.display()
734 );
735 }
736 }
737 search_path
738}
739
740fn prepare_rustc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
747 let gctx = build_runner.bcx.gctx;
748 let is_primary = build_runner.is_primary_package(unit);
749 let is_workspace = build_runner.bcx.ws.is_member(&unit.pkg);
750
751 let mut base = build_runner
752 .compilation
753 .rustc_process(unit, is_primary, is_workspace)?;
754 build_base_args(build_runner, &mut base, unit)?;
755 if unit.pkg.manifest().is_embedded() {
756 if !gctx.cli_unstable().script {
757 anyhow::bail!(
758 "parsing `{}` requires `-Zscript`",
759 unit.pkg.manifest_path().display()
760 );
761 }
762 base.arg("-Z").arg("crate-attr=feature(frontmatter)");
763 }
764
765 base.inherit_jobserver(&build_runner.jobserver);
766 build_deps_args(&mut base, build_runner, unit)?;
767 add_cap_lints(build_runner.bcx, unit, &mut base);
768 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
769 base.args(args);
770 }
771 base.args(&unit.rustflags);
772 if gctx.cli_unstable().binary_dep_depinfo {
773 base.arg("-Z").arg("binary-dep-depinfo");
774 }
775 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
776 base.arg("-Z").arg("checksum-hash-algorithm=blake3");
777 }
778
779 if is_primary {
780 base.env("CARGO_PRIMARY_PACKAGE", "1");
781 let file_list = std::env::join_paths(build_runner.sbom_output_files(unit)?)?;
782 base.env("CARGO_SBOM_PATH", file_list);
783 }
784
785 if unit.target.is_test() || unit.target.is_bench() {
786 let tmp = build_runner
787 .files()
788 .layout(unit.kind)
789 .build_dir()
790 .prepare_tmp()?;
791 base.env("CARGO_TARGET_TMPDIR", tmp.display().to_string());
792 }
793
794 Ok(base)
795}
796
797fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
804 let bcx = build_runner.bcx;
805 let mut rustdoc = build_runner.compilation.rustdoc_process(unit, None)?;
807 if unit.pkg.manifest().is_embedded() {
808 if !bcx.gctx.cli_unstable().script {
809 anyhow::bail!(
810 "parsing `{}` requires `-Zscript`",
811 unit.pkg.manifest_path().display()
812 );
813 }
814 rustdoc.arg("-Z").arg("crate-attr=feature(frontmatter)");
815 }
816 rustdoc.inherit_jobserver(&build_runner.jobserver);
817 let crate_name = unit.target.crate_name();
818 rustdoc.arg("--crate-name").arg(&crate_name);
819 add_path_args(bcx.ws, unit, &mut rustdoc);
820 add_cap_lints(bcx, unit, &mut rustdoc);
821
822 if let CompileKind::Target(target) = unit.kind {
823 rustdoc.arg("--target").arg(target.rustc_target());
824 }
825 let doc_dir = build_runner.files().out_dir(unit);
826 rustdoc.arg("-o").arg(&doc_dir);
827 rustdoc.args(&features_args(unit));
828 rustdoc.args(&check_cfg_args(unit));
829
830 add_error_format_and_color(build_runner, &mut rustdoc);
831 add_allow_features(build_runner, &mut rustdoc);
832
833 if build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo {
834 let mut arg = if build_runner.bcx.gctx.cli_unstable().rustdoc_mergeable_info {
837 OsString::from("--emit=invocation-specific,dep-info=")
839 } else {
840 OsString::from("--emit=toolchain-shared-resources,invocation-specific,dep-info=")
842 };
843 arg.push(rustdoc_dep_info_loc(build_runner, unit));
844 rustdoc.arg(arg);
845
846 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
847 rustdoc.arg("-Z").arg("checksum-hash-algorithm=blake3");
848 }
849
850 rustdoc.arg("-Zunstable-options");
851 } else if build_runner.bcx.gctx.cli_unstable().rustdoc_mergeable_info {
852 rustdoc.arg("--emit=invocation-specific");
854 rustdoc.arg("-Zunstable-options");
855 }
856
857 if build_runner.bcx.gctx.cli_unstable().rustdoc_mergeable_info {
858 rustdoc.arg("--merge=none");
860 let mut arg = OsString::from("--parts-out-dir=");
861 arg.push(build_runner.files().deps_dir_new_layout(unit));
863 rustdoc.arg(arg);
864 }
865
866 if let Some(trim_paths) = unit.profile.trim_paths.as_ref() {
867 trim_paths_args_rustdoc(&mut rustdoc, build_runner, unit, trim_paths)?;
868 }
869
870 rustdoc.args(unit.pkg.manifest().lint_rustflags());
871
872 let metadata = build_runner.metadata_for_doc_units[unit];
873 rustdoc
874 .arg("-C")
875 .arg(format!("metadata={}", metadata.c_metadata()));
876
877 if unit.mode.is_doc_scrape() {
878 debug_assert!(build_runner.bcx.scrape_units.contains(unit));
879
880 if unit.target.is_test() {
881 rustdoc.arg("--scrape-tests");
882 }
883
884 rustdoc.arg("-Zunstable-options");
885
886 rustdoc
887 .arg("--scrape-examples-output-path")
888 .arg(scrape_output_path(build_runner, unit)?);
889
890 for pkg in build_runner.bcx.packages.packages() {
892 let names = pkg
893 .targets()
894 .iter()
895 .map(|target| target.crate_name())
896 .collect::<HashSet<_>>();
897 for name in names {
898 rustdoc.arg("--scrape-examples-target-crate").arg(name);
899 }
900 }
901 }
902
903 if should_include_scrape_units(build_runner.bcx, unit) {
904 rustdoc.arg("-Zunstable-options");
905 }
906
907 build_deps_args(&mut rustdoc, build_runner, unit)?;
908 rustdoc::add_root_urls(build_runner, unit, &mut rustdoc)?;
909
910 rustdoc::add_output_format(build_runner, &mut rustdoc)?;
911
912 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
913 rustdoc.args(args);
914 }
915 rustdoc.args(&unit.rustdocflags);
916
917 if !crate_version_flag_already_present(&rustdoc) {
918 append_crate_version_flag(unit, &mut rustdoc);
919 }
920
921 Ok(rustdoc)
922}
923
924fn rustdoc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Work> {
926 let mut rustdoc = prepare_rustdoc(build_runner, unit)?;
927
928 let crate_name = unit.target.crate_name();
929 let doc_dir = build_runner.files().out_dir(unit);
930 paths::create_dir_all(&doc_dir)?;
934
935 let target_desc = unit.target.description_named();
936 let name = unit.pkg.name();
937 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
938 let package_id = unit.pkg.package_id();
939 let target = Target::clone(&unit.target);
940 let manifest = ManifestErrorContext::new(build_runner, unit);
941
942 let rustdoc_dep_info_loc = rustdoc_dep_info_loc(build_runner, unit);
943 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
944 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
945 let pkg_root = unit.pkg.root().to_path_buf();
946 let cwd = rustdoc
947 .get_cwd()
948 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
949 .to_path_buf();
950 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
951 let is_local = unit.is_local();
952 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
953 let rustdoc_depinfo_enabled = build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo;
954
955 let mut output_options = OutputOptions::new(build_runner, unit);
956 let script_metadatas = build_runner.find_build_script_metadatas(unit);
957 let scrape_outputs = if should_include_scrape_units(build_runner.bcx, unit) {
958 Some(
959 build_runner
960 .bcx
961 .scrape_units
962 .iter()
963 .map(|unit| {
964 Ok((
965 build_runner.files().metadata(unit).unit_id(),
966 scrape_output_path(build_runner, unit)?,
967 ))
968 })
969 .collect::<CargoResult<HashMap<_, _>>>()?,
970 )
971 } else {
972 None
973 };
974
975 let failed_scrape_units = Arc::clone(&build_runner.failed_scrape_units);
976 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
977 && !matches!(
978 build_runner.bcx.gctx.shell().verbosity(),
979 Verbosity::Verbose
980 );
981 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
982 make_failed_scrape_diagnostic(
983 build_runner,
984 unit,
985 format_args!("failed to scan {target_desc} in package `{name}` for example code usage"),
986 )
987 });
988 if hide_diagnostics_for_scrape_unit {
989 output_options.show_diagnostics = false;
990 }
991
992 Ok(Work::new(move |state| {
993 add_custom_flags(
994 &mut rustdoc,
995 &build_script_outputs.lock().unwrap(),
996 script_metadatas,
997 )?;
998
999 if let Some(scrape_outputs) = scrape_outputs {
1004 let failed_scrape_units = failed_scrape_units.lock().unwrap();
1005 for (metadata, output_path) in &scrape_outputs {
1006 if !failed_scrape_units.contains(metadata) {
1007 rustdoc.arg("--with-examples").arg(output_path);
1008 }
1009 }
1010 }
1011
1012 let crate_dir = doc_dir.join(&crate_name);
1013 if crate_dir.exists() {
1014 debug!("removing pre-existing doc directory {:?}", crate_dir);
1017 paths::remove_dir_all(crate_dir)?;
1018 }
1019 state.running(&rustdoc);
1020 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
1021
1022 let result = rustdoc
1023 .exec_with_streaming(
1024 &mut |line| on_stdout_line(state, line, package_id, &target),
1025 &mut |line| {
1026 on_stderr_line(
1027 state,
1028 line,
1029 package_id,
1030 &manifest,
1031 &target,
1032 &mut output_options,
1033 )
1034 },
1035 false,
1036 )
1037 .map_err(verbose_if_simple_exit_code)
1038 .with_context(|| format!("could not document `{}`", name));
1039
1040 if let Err(e) = result {
1041 if let Some(diagnostic) = failed_scrape_diagnostic {
1042 state.warning(diagnostic);
1043 }
1044
1045 return Err(e);
1046 }
1047
1048 if rustdoc_depinfo_enabled && rustdoc_dep_info_loc.exists() {
1049 fingerprint::translate_dep_info(
1050 &rustdoc_dep_info_loc,
1051 &dep_info_loc,
1052 &cwd,
1053 &pkg_root,
1054 &build_dir,
1055 &rustdoc,
1056 is_local,
1058 &env_config,
1059 )
1060 .with_context(|| {
1061 internal(format_args!(
1062 "could not parse/generate dep info at: {}",
1063 rustdoc_dep_info_loc.display()
1064 ))
1065 })?;
1066 paths::set_file_time_no_err(dep_info_loc, timestamp);
1069 }
1070
1071 Ok(())
1072 }))
1073}
1074
1075fn crate_version_flag_already_present(rustdoc: &ProcessBuilder) -> bool {
1078 rustdoc.get_args().any(|flag| {
1079 flag.to_str()
1080 .map_or(false, |flag| flag.starts_with(RUSTDOC_CRATE_VERSION_FLAG))
1081 })
1082}
1083
1084fn append_crate_version_flag(unit: &Unit, rustdoc: &mut ProcessBuilder) {
1085 rustdoc
1086 .arg(RUSTDOC_CRATE_VERSION_FLAG)
1087 .arg(unit.pkg.version().to_string());
1088}
1089
1090fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuilder) {
1094 if !unit.show_warnings(bcx.gctx) {
1097 cmd.arg("--cap-lints").arg("allow");
1098
1099 } else if !unit.is_local() {
1102 cmd.arg("--cap-lints").arg("warn");
1103 }
1104}
1105
1106fn add_allow_features(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1110 if let Some(allow) = &build_runner.bcx.gctx.cli_unstable().allow_features {
1111 use std::fmt::Write;
1112 let mut arg = String::from("-Zallow-features=");
1113 for f in allow {
1114 let _ = write!(&mut arg, "{f},");
1115 }
1116 cmd.arg(arg.trim_end_matches(','));
1117 }
1118}
1119
1120fn add_error_format_and_color(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1131 let enable_timings = build_runner.bcx.gctx.cli_unstable().section_timings
1132 && (!build_runner.bcx.build_config.timing_outputs.is_empty()
1133 || build_runner.bcx.logger.is_some());
1134 if enable_timings {
1135 cmd.arg("-Zunstable-options");
1136 }
1137
1138 cmd.arg("--error-format=json");
1139 let mut json = String::from("--json=diagnostic-rendered-ansi,artifacts,future-incompat");
1140
1141 if let MessageFormat::Short | MessageFormat::Json { short: true, .. } =
1142 build_runner.bcx.build_config.message_format
1143 {
1144 json.push_str(",diagnostic-short");
1145 } else if build_runner.bcx.gctx.shell().err_unicode()
1146 && build_runner.bcx.gctx.cli_unstable().rustc_unicode
1147 {
1148 json.push_str(",diagnostic-unicode");
1149 }
1150
1151 if enable_timings {
1152 json.push_str(",timings");
1153 }
1154
1155 cmd.arg(json);
1156
1157 let gctx = build_runner.bcx.gctx;
1158 if let Some(width) = gctx.shell().err_width().diagnostic_terminal_width() {
1159 cmd.arg(format!("--diagnostic-width={width}"));
1160 }
1161}
1162
1163fn build_base_args(
1165 build_runner: &BuildRunner<'_, '_>,
1166 cmd: &mut ProcessBuilder,
1167 unit: &Unit,
1168) -> CargoResult<()> {
1169 assert!(!unit.mode.is_run_custom_build());
1170
1171 let bcx = build_runner.bcx;
1172 let Profile {
1173 ref opt_level,
1174 codegen_backend,
1175 codegen_units,
1176 debuginfo,
1177 debug_assertions,
1178 split_debuginfo,
1179 overflow_checks,
1180 rpath,
1181 ref panic,
1182 incremental,
1183 strip,
1184 rustflags: profile_rustflags,
1185 trim_paths,
1186 hint_mostly_unused: profile_hint_mostly_unused,
1187 ..
1188 } = unit.profile.clone();
1189 let hints = unit.pkg.hints().cloned().unwrap_or_default();
1190 let test = unit.mode.is_any_test();
1191
1192 let warn = |msg: &str| {
1193 bcx.gctx.shell().warn(format!(
1194 "{}@{}: {msg}",
1195 unit.pkg.package_id().name(),
1196 unit.pkg.package_id().version()
1197 ))
1198 };
1199 let unit_capped_warn = |msg: &str| {
1200 if unit.show_warnings(bcx.gctx) {
1201 warn(msg)
1202 } else {
1203 Ok(())
1204 }
1205 };
1206
1207 cmd.arg("--crate-name").arg(&unit.target.crate_name());
1208
1209 let edition = unit.target.edition();
1210 edition.cmd_edition_arg(cmd);
1211
1212 add_path_args(bcx.ws, unit, cmd);
1213 add_error_format_and_color(build_runner, cmd);
1214 add_allow_features(build_runner, cmd);
1215
1216 let mut contains_dy_lib = false;
1217 if !test {
1218 for crate_type in &unit.target.rustc_crate_types() {
1219 cmd.arg("--crate-type").arg(crate_type.as_str());
1220 contains_dy_lib |= crate_type == &CrateType::Dylib;
1221 }
1222 }
1223
1224 if unit.mode.is_check() {
1225 cmd.arg("--emit=dep-info,metadata");
1226 } else if build_runner.bcx.gctx.cli_unstable().no_embed_metadata {
1227 if unit.benefits_from_no_embed_metadata() {
1237 cmd.arg("--emit=dep-info,metadata,link");
1238 cmd.args(&["-Z", "embed-metadata=no"]);
1239 } else {
1240 cmd.arg("--emit=dep-info,link");
1241 }
1242 } else {
1243 if !unit.requires_upstream_objects() {
1247 cmd.arg("--emit=dep-info,metadata,link");
1248 } else {
1249 cmd.arg("--emit=dep-info,link");
1250 }
1251 }
1252
1253 let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
1254 || (contains_dy_lib && !build_runner.is_primary_package(unit));
1255 if prefer_dynamic {
1256 cmd.arg("-C").arg("prefer-dynamic");
1257 }
1258
1259 if opt_level.as_str() != "0" {
1260 cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
1261 }
1262
1263 if *panic != PanicStrategy::Unwind {
1264 cmd.arg("-C").arg(format!("panic={}", panic));
1265 }
1266 if *panic == PanicStrategy::ImmediateAbort {
1267 cmd.arg("-Z").arg("unstable-options");
1268 }
1269
1270 cmd.args(<o_args(build_runner, unit));
1271
1272 if let Some(backend) = codegen_backend {
1273 cmd.arg("-Z").arg(&format!("codegen-backend={}", backend));
1274 }
1275
1276 if let Some(n) = codegen_units {
1277 cmd.arg("-C").arg(&format!("codegen-units={}", n));
1278 }
1279
1280 let debuginfo = debuginfo.into_inner();
1281 if debuginfo != TomlDebugInfo::None {
1283 cmd.arg("-C").arg(format!("debuginfo={debuginfo}"));
1284 if let Some(split) = split_debuginfo {
1291 if build_runner
1292 .bcx
1293 .target_data
1294 .info(unit.kind)
1295 .supports_debuginfo_split(split)
1296 {
1297 cmd.arg("-C").arg(format!("split-debuginfo={split}"));
1298 }
1299 }
1300 }
1301
1302 if let Some(trim_paths) = trim_paths {
1303 trim_paths_args(cmd, build_runner, unit, &trim_paths)?;
1304 }
1305
1306 cmd.args(unit.pkg.manifest().lint_rustflags());
1307 cmd.args(&profile_rustflags);
1308
1309 if opt_level.as_str() != "0" {
1313 if debug_assertions {
1314 cmd.args(&["-C", "debug-assertions=on"]);
1315 if !overflow_checks {
1316 cmd.args(&["-C", "overflow-checks=off"]);
1317 }
1318 } else if overflow_checks {
1319 cmd.args(&["-C", "overflow-checks=on"]);
1320 }
1321 } else if !debug_assertions {
1322 cmd.args(&["-C", "debug-assertions=off"]);
1323 if overflow_checks {
1324 cmd.args(&["-C", "overflow-checks=on"]);
1325 }
1326 } else if !overflow_checks {
1327 cmd.args(&["-C", "overflow-checks=off"]);
1328 }
1329
1330 if test && unit.target.harness() {
1331 cmd.arg("--test");
1332
1333 if *panic == PanicStrategy::Abort || *panic == PanicStrategy::ImmediateAbort {
1341 cmd.arg("-Z").arg("panic-abort-tests");
1342 }
1343 } else if test {
1344 cmd.arg("--cfg").arg("test");
1345 }
1346
1347 cmd.args(&features_args(unit));
1348 cmd.args(&check_cfg_args(unit));
1349
1350 let meta = build_runner.files().metadata(unit);
1351 cmd.arg("-C")
1352 .arg(&format!("metadata={}", meta.c_metadata()));
1353 if let Some(c_extra_filename) = meta.c_extra_filename() {
1354 cmd.arg("-C")
1355 .arg(&format!("extra-filename=-{c_extra_filename}"));
1356 }
1357
1358 if rpath {
1359 cmd.arg("-C").arg("rpath");
1360 }
1361
1362 cmd.arg("--out-dir")
1363 .arg(&build_runner.files().out_dir(unit));
1364
1365 fn opt(cmd: &mut ProcessBuilder, key: &str, prefix: &str, val: Option<&OsStr>) {
1366 if let Some(val) = val {
1367 let mut joined = OsString::from(prefix);
1368 joined.push(val);
1369 cmd.arg(key).arg(joined);
1370 }
1371 }
1372
1373 if let CompileKind::Target(n) = unit.kind {
1374 cmd.arg("--target").arg(n.rustc_target());
1375 }
1376
1377 opt(
1378 cmd,
1379 "-C",
1380 "linker=",
1381 build_runner
1382 .compilation
1383 .target_linker(unit.kind)
1384 .as_ref()
1385 .map(|s| s.as_ref()),
1386 );
1387 if incremental {
1388 let dir = build_runner.files().incremental_dir(&unit);
1389 opt(cmd, "-C", "incremental=", Some(dir.as_os_str()));
1390 }
1391
1392 let pkg_hint_mostly_unused = match hints.mostly_unused {
1393 None => None,
1394 Some(toml::Value::Boolean(b)) => Some(b),
1395 Some(v) => {
1396 unit_capped_warn(&format!(
1397 "ignoring unsupported value type ({}) for 'hints.mostly-unused', which expects a boolean",
1398 v.type_str()
1399 ))?;
1400 None
1401 }
1402 };
1403 if profile_hint_mostly_unused
1404 .or(pkg_hint_mostly_unused)
1405 .unwrap_or(false)
1406 {
1407 if bcx.gctx.cli_unstable().profile_hint_mostly_unused {
1408 cmd.arg("-Zhint-mostly-unused");
1409 } else {
1410 if profile_hint_mostly_unused.is_some() {
1411 warn(
1413 "ignoring 'hint-mostly-unused' profile option, pass `-Zprofile-hint-mostly-unused` to enable it",
1414 )?;
1415 } else if pkg_hint_mostly_unused.is_some() {
1416 unit_capped_warn(
1417 "ignoring 'hints.mostly-unused', pass `-Zprofile-hint-mostly-unused` to enable it",
1418 )?;
1419 }
1420 }
1421 }
1422
1423 let strip = strip.into_inner();
1424 if strip != StripInner::None {
1425 cmd.arg("-C").arg(format!("strip={}", strip));
1426 }
1427
1428 if unit.is_std {
1429 cmd.arg("-Z")
1435 .arg("force-unstable-if-unmarked")
1436 .env("RUSTC_BOOTSTRAP", "1");
1437 }
1438
1439 if unit.target.is_test() || unit.target.is_bench() {
1441 for bin_target in unit
1442 .pkg
1443 .manifest()
1444 .targets()
1445 .iter()
1446 .filter(|target| target.is_bin())
1447 {
1448 let exe_path = build_runner
1452 .files()
1453 .bin_link_for_target(bin_target, unit.kind, build_runner.bcx)?
1454 .map(|path| path.as_os_str().to_os_string())
1455 .unwrap_or_else(|| OsString::from(format!("placeholder:{}", bin_target.name())));
1456
1457 let name = bin_target
1458 .binary_filename()
1459 .unwrap_or(bin_target.name().to_string());
1460 let key = format!("CARGO_BIN_EXE_{}", name);
1461 cmd.env(&key, exe_path);
1462 }
1463 }
1464 Ok(())
1465}
1466
1467fn features_args(unit: &Unit) -> Vec<OsString> {
1469 let mut args = Vec::with_capacity(unit.features.len() * 2);
1470
1471 for feat in &unit.features {
1472 args.push(OsString::from("--cfg"));
1473 args.push(OsString::from(format!("feature=\"{}\"", feat)));
1474 }
1475
1476 args
1477}
1478
1479fn trim_paths_args_rustdoc(
1481 cmd: &mut ProcessBuilder,
1482 build_runner: &BuildRunner<'_, '_>,
1483 unit: &Unit,
1484 trim_paths: &TomlTrimPaths,
1485) -> CargoResult<()> {
1486 match trim_paths {
1487 TomlTrimPaths::Values(values) if !values.contains(&TomlTrimPathsValue::Diagnostics) => {
1489 return Ok(());
1490 }
1491 _ => {}
1492 }
1493
1494 cmd.arg("-Zunstable-options");
1496
1497 cmd.arg(package_remap(build_runner, unit));
1500 cmd.arg(build_dir_remap(build_runner));
1501 cmd.arg(sysroot_remap(build_runner, unit));
1502
1503 Ok(())
1504}
1505
1506fn trim_paths_args(
1512 cmd: &mut ProcessBuilder,
1513 build_runner: &BuildRunner<'_, '_>,
1514 unit: &Unit,
1515 trim_paths: &TomlTrimPaths,
1516) -> CargoResult<()> {
1517 if trim_paths.is_none() {
1518 return Ok(());
1519 }
1520
1521 cmd.arg("-Zunstable-options");
1523 cmd.arg(format!("-Zremap-path-scope={trim_paths}"));
1524
1525 cmd.arg(package_remap(build_runner, unit));
1528 cmd.arg(build_dir_remap(build_runner));
1529 cmd.arg(sysroot_remap(build_runner, unit));
1530
1531 Ok(())
1532}
1533
1534fn sysroot_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1539 let mut remap = OsString::from("--remap-path-prefix=");
1540 remap.push({
1541 let mut sysroot = build_runner.bcx.target_data.info(unit.kind).sysroot.clone();
1543 sysroot.push("lib");
1544 sysroot.push("rustlib");
1545 sysroot.push("src");
1546 sysroot.push("rust");
1547 sysroot
1548 });
1549 remap.push("=");
1550 remap.push("/rustc/");
1551 if let Some(commit_hash) = build_runner.bcx.rustc().commit_hash.as_ref() {
1552 remap.push(commit_hash);
1553 } else {
1554 remap.push(build_runner.bcx.rustc().version.to_string());
1555 }
1556 remap
1557}
1558
1559fn package_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1567 let pkg_root = unit.pkg.root();
1568 let ws_root = build_runner.bcx.ws.root();
1569 let mut remap = OsString::from("--remap-path-prefix=");
1570 let source_id = unit.pkg.package_id().source_id();
1571 if source_id.is_git() {
1572 remap.push(
1573 build_runner
1574 .bcx
1575 .gctx
1576 .git_checkouts_path()
1577 .as_path_unlocked(),
1578 );
1579 remap.push("=");
1580 } else if source_id.is_registry() {
1581 remap.push(
1582 build_runner
1583 .bcx
1584 .gctx
1585 .registry_source_path()
1586 .as_path_unlocked(),
1587 );
1588 remap.push("=");
1589 } else if pkg_root.strip_prefix(ws_root).is_ok() {
1590 remap.push(ws_root);
1591 remap.push("=."); } else {
1593 remap.push(pkg_root);
1594 remap.push("=");
1595 remap.push(unit.pkg.name());
1596 remap.push("-");
1597 remap.push(unit.pkg.version().to_string());
1598 }
1599 remap
1600}
1601
1602fn build_dir_remap(build_runner: &BuildRunner<'_, '_>) -> OsString {
1615 let build_dir = build_runner.bcx.ws.build_dir();
1616 let mut remap = OsString::from("--remap-path-prefix=");
1617 remap.push(build_dir.as_path_unlocked());
1618 remap.push("=/cargo/build-dir");
1619 remap
1620}
1621
1622fn check_cfg_args(unit: &Unit) -> Vec<OsString> {
1624 let gross_cap_estimation = unit.pkg.summary().features().len() * 7 + 25;
1642 let mut arg_feature = OsString::with_capacity(gross_cap_estimation);
1643
1644 arg_feature.push("cfg(feature, values(");
1645 for (i, feature) in unit.pkg.summary().features().keys().enumerate() {
1646 if i != 0 {
1647 arg_feature.push(", ");
1648 }
1649 arg_feature.push("\"");
1650 arg_feature.push(feature);
1651 arg_feature.push("\"");
1652 }
1653 arg_feature.push("))");
1654
1655 vec![
1664 OsString::from("--check-cfg"),
1665 OsString::from("cfg(docsrs,test)"),
1666 OsString::from("--check-cfg"),
1667 arg_feature,
1668 ]
1669}
1670
1671fn lto_args(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> Vec<OsString> {
1673 let mut result = Vec::new();
1674 let mut push = |arg: &str| {
1675 result.push(OsString::from("-C"));
1676 result.push(OsString::from(arg));
1677 };
1678 match build_runner.lto[unit] {
1679 lto::Lto::Run(None) => push("lto"),
1680 lto::Lto::Run(Some(s)) => push(&format!("lto={}", s)),
1681 lto::Lto::Off => {
1682 push("lto=off");
1683 push("embed-bitcode=no");
1684 }
1685 lto::Lto::ObjectAndBitcode => {} lto::Lto::OnlyBitcode => push("linker-plugin-lto"),
1687 lto::Lto::OnlyObject => push("embed-bitcode=no"),
1688 }
1689 result
1690}
1691
1692fn build_deps_args(
1698 cmd: &mut ProcessBuilder,
1699 build_runner: &BuildRunner<'_, '_>,
1700 unit: &Unit,
1701) -> CargoResult<()> {
1702 let bcx = build_runner.bcx;
1703 if build_runner.bcx.gctx.cli_unstable().build_dir_new_layout {
1704 let mut map = BTreeMap::new();
1705
1706 add_dep_arg(&mut map, build_runner, unit);
1708
1709 let paths = map.into_iter().map(|(_, path)| path).sorted_unstable();
1710
1711 for path in paths {
1712 cmd.arg("-L").arg(&{
1713 let mut deps = OsString::from("dependency=");
1714 deps.push(path);
1715 deps
1716 });
1717 }
1718 } else {
1719 cmd.arg("-L").arg(&{
1720 let mut deps = OsString::from("dependency=");
1721 deps.push(build_runner.files().deps_dir(unit));
1722 deps
1723 });
1724 }
1725
1726 if !unit.kind.is_host() {
1729 cmd.arg("-L").arg(&{
1730 let mut deps = OsString::from("dependency=");
1731 deps.push(build_runner.files().host_deps(unit));
1732 deps
1733 });
1734 }
1735
1736 let deps = build_runner.unit_deps(unit);
1737
1738 if !deps
1742 .iter()
1743 .any(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_linkable())
1744 {
1745 if let Some(dep) = deps.iter().find(|dep| {
1746 !dep.unit.mode.is_doc() && dep.unit.target.is_lib() && !dep.unit.artifact.is_true()
1747 }) {
1748 let dep_name = dep.unit.target.crate_name();
1749 let name = unit.target.crate_name();
1750 bcx.gctx.shell().print_report(&[
1751 Level::WARNING.secondary_title(format!("the package `{dep_name}` provides no linkable target"))
1752 .elements([
1753 Level::NOTE.message(format!("this might cause `{name}` to fail compilation")),
1754 Level::NOTE.message("this warning might turn into a hard error in the future"),
1755 Level::HELP.message(format!("consider adding 'dylib' or 'rlib' to key 'crate-type' in `{dep_name}`'s Cargo.toml"))
1756 ])
1757 ], false)?;
1758 }
1759 }
1760
1761 let mut unstable_opts = false;
1762
1763 let first_custom_build_dep = deps.iter().find(|dep| dep.unit.mode.is_run_custom_build());
1765 if let Some(dep) = first_custom_build_dep {
1766 let out_dir = &build_runner.files().build_script_out_dir(&dep.unit);
1767 cmd.env("OUT_DIR", &out_dir);
1768 }
1769
1770 let is_multiple_build_scripts_enabled = unit
1772 .pkg
1773 .manifest()
1774 .unstable_features()
1775 .require(Feature::multiple_build_scripts())
1776 .is_ok();
1777
1778 if is_multiple_build_scripts_enabled {
1779 for dep in deps {
1780 if dep.unit.mode.is_run_custom_build() {
1781 let out_dir = &build_runner.files().build_script_out_dir(&dep.unit);
1782 let target_name = dep.unit.target.name();
1783 let out_dir_prefix = target_name
1784 .strip_prefix("build-script-")
1785 .unwrap_or(target_name);
1786 let out_dir_name = format!("{out_dir_prefix}_OUT_DIR");
1787 cmd.env(&out_dir_name, &out_dir);
1788 }
1789 }
1790 }
1791 for arg in extern_args(build_runner, unit, &mut unstable_opts)? {
1792 cmd.arg(arg);
1793 }
1794
1795 for (var, env) in artifact::get_env(build_runner, deps)? {
1796 cmd.env(&var, env);
1797 }
1798
1799 if unstable_opts {
1802 cmd.arg("-Z").arg("unstable-options");
1803 }
1804
1805 Ok(())
1806}
1807
1808fn add_dep_arg<'a, 'b: 'a>(
1809 map: &mut BTreeMap<&'a Unit, PathBuf>,
1810 build_runner: &'b BuildRunner<'b, '_>,
1811 unit: &'a Unit,
1812) {
1813 if map.contains_key(&unit) {
1814 return;
1815 }
1816 map.insert(&unit, build_runner.files().deps_dir(&unit));
1817
1818 for dep in build_runner.unit_deps(unit) {
1819 add_dep_arg(map, build_runner, &dep.unit);
1820 }
1821}
1822
1823fn add_custom_flags(
1827 cmd: &mut ProcessBuilder,
1828 build_script_outputs: &BuildScriptOutputs,
1829 metadata_vec: Option<Vec<UnitHash>>,
1830) -> CargoResult<()> {
1831 if let Some(metadata_vec) = metadata_vec {
1832 for metadata in metadata_vec {
1833 if let Some(output) = build_script_outputs.get(metadata) {
1834 for cfg in output.cfgs.iter() {
1835 cmd.arg("--cfg").arg(cfg);
1836 }
1837 for check_cfg in &output.check_cfgs {
1838 cmd.arg("--check-cfg").arg(check_cfg);
1839 }
1840 for (name, value) in output.env.iter() {
1841 cmd.env(name, value);
1842 }
1843 }
1844 }
1845 }
1846
1847 Ok(())
1848}
1849
1850pub fn extern_args(
1852 build_runner: &BuildRunner<'_, '_>,
1853 unit: &Unit,
1854 unstable_opts: &mut bool,
1855) -> CargoResult<Vec<OsString>> {
1856 let mut result = Vec::new();
1857 let deps = build_runner.unit_deps(unit);
1858
1859 let no_embed_metadata = build_runner.bcx.gctx.cli_unstable().no_embed_metadata;
1860
1861 let mut link_to =
1863 |dep: &UnitDep, extern_crate_name: InternedString, noprelude: bool| -> CargoResult<()> {
1864 let mut value = OsString::new();
1865 let mut opts = Vec::new();
1866 let is_public_dependency_enabled = unit
1867 .pkg
1868 .manifest()
1869 .unstable_features()
1870 .require(Feature::public_dependency())
1871 .is_ok()
1872 || build_runner.bcx.gctx.cli_unstable().public_dependency;
1873 if !dep.public && unit.target.is_lib() && is_public_dependency_enabled {
1874 opts.push("priv");
1875 *unstable_opts = true;
1876 }
1877 if noprelude {
1878 opts.push("noprelude");
1879 *unstable_opts = true;
1880 }
1881 if !opts.is_empty() {
1882 value.push(opts.join(","));
1883 value.push(":");
1884 }
1885 value.push(extern_crate_name.as_str());
1886 value.push("=");
1887
1888 let mut pass = |file| {
1889 let mut value = value.clone();
1890 value.push(file);
1891 result.push(OsString::from("--extern"));
1892 result.push(value);
1893 };
1894
1895 let outputs = build_runner.outputs(&dep.unit)?;
1896
1897 if build_runner.only_requires_rmeta(unit, &dep.unit) || dep.unit.mode.is_check() {
1898 let output = outputs
1900 .iter()
1901 .find(|output| output.flavor == FileFlavor::Rmeta)
1902 .expect("failed to find rmeta dep for pipelined dep");
1903 pass(&output.path);
1904 } else {
1905 for output in outputs.iter() {
1907 if output.flavor == FileFlavor::Linkable {
1908 pass(&output.path);
1909 }
1910 else if no_embed_metadata && output.flavor == FileFlavor::Rmeta {
1914 pass(&output.path);
1915 }
1916 }
1917 }
1918 Ok(())
1919 };
1920
1921 for dep in deps {
1922 if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
1923 link_to(dep, dep.extern_crate_name, dep.noprelude)?;
1924 }
1925 }
1926 if unit.target.proc_macro() {
1927 result.push(OsString::from("--extern"));
1929 result.push(OsString::from("proc_macro"));
1930 }
1931
1932 Ok(result)
1933}
1934
1935fn envify(s: &str) -> String {
1936 s.chars()
1937 .flat_map(|c| c.to_uppercase())
1938 .map(|c| if c == '-' { '_' } else { c })
1939 .collect()
1940}
1941
1942struct OutputOptions {
1945 format: MessageFormat,
1947 cache_cell: Option<(PathBuf, OnceCell<File>)>,
1952 show_diagnostics: bool,
1960 warnings_seen: usize,
1962 errors_seen: usize,
1964}
1965
1966impl OutputOptions {
1967 fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions {
1968 let path = build_runner.files().message_cache_path(unit);
1969 drop(fs::remove_file(&path));
1971 let cache_cell = Some((path, OnceCell::new()));
1972 let show_diagnostics =
1973 build_runner.bcx.gctx.warning_handling().unwrap_or_default() != WarningHandling::Allow;
1974 OutputOptions {
1975 format: build_runner.bcx.build_config.message_format,
1976 cache_cell,
1977 show_diagnostics,
1978 warnings_seen: 0,
1979 errors_seen: 0,
1980 }
1981 }
1982}
1983
1984struct ManifestErrorContext {
1990 path: PathBuf,
1992 spans: toml::Spanned<toml::de::DeTable<'static>>,
1994 contents: String,
1996 rename_table: HashMap<InternedString, InternedString>,
1999 requested_kinds: Vec<CompileKind>,
2002 cfgs: Vec<Vec<Cfg>>,
2005 host_name: InternedString,
2006 cwd: PathBuf,
2008 term_width: usize,
2010}
2011
2012fn on_stdout_line(
2013 state: &JobState<'_, '_>,
2014 line: &str,
2015 _package_id: PackageId,
2016 _target: &Target,
2017) -> CargoResult<()> {
2018 state.stdout(line.to_string())?;
2019 Ok(())
2020}
2021
2022fn on_stderr_line(
2023 state: &JobState<'_, '_>,
2024 line: &str,
2025 package_id: PackageId,
2026 manifest: &ManifestErrorContext,
2027 target: &Target,
2028 options: &mut OutputOptions,
2029) -> CargoResult<()> {
2030 if on_stderr_line_inner(state, line, package_id, manifest, target, options)? {
2031 if let Some((path, cell)) = &mut options.cache_cell {
2033 let f = cell.try_borrow_mut_with(|| paths::create(path))?;
2035 debug_assert!(!line.contains('\n'));
2036 f.write_all(line.as_bytes())?;
2037 f.write_all(&[b'\n'])?;
2038 }
2039 }
2040 Ok(())
2041}
2042
2043fn on_stderr_line_inner(
2045 state: &JobState<'_, '_>,
2046 line: &str,
2047 package_id: PackageId,
2048 manifest: &ManifestErrorContext,
2049 target: &Target,
2050 options: &mut OutputOptions,
2051) -> CargoResult<bool> {
2052 if !line.starts_with('{') {
2058 state.stderr(line.to_string())?;
2059 return Ok(true);
2060 }
2061
2062 let mut compiler_message: Box<serde_json::value::RawValue> = match serde_json::from_str(line) {
2063 Ok(msg) => msg,
2064
2065 Err(e) => {
2069 debug!("failed to parse json: {:?}", e);
2070 state.stderr(line.to_string())?;
2071 return Ok(true);
2072 }
2073 };
2074
2075 let count_diagnostic = |level, options: &mut OutputOptions| {
2076 if level == "warning" {
2077 options.warnings_seen += 1;
2078 } else if level == "error" {
2079 options.errors_seen += 1;
2080 }
2081 };
2082
2083 if let Ok(report) = serde_json::from_str::<FutureIncompatReport>(compiler_message.get()) {
2084 for item in &report.future_incompat_report {
2085 count_diagnostic(&*item.diagnostic.level, options);
2086 }
2087 state.future_incompat_report(report.future_incompat_report);
2088 return Ok(true);
2089 }
2090
2091 let res = serde_json::from_str::<SectionTiming>(compiler_message.get());
2092 if let Ok(timing_record) = res {
2093 state.on_section_timing_emitted(timing_record);
2094 return Ok(false);
2095 }
2096
2097 let add_pub_in_priv_diagnostic = |diag: &mut String| -> bool {
2099 static PRIV_DEP_REGEX: LazyLock<Regex> =
2108 LazyLock::new(|| Regex::new("from private dependency '([A-Za-z0-9-_]+)'").unwrap());
2109 if let Some(crate_name) = PRIV_DEP_REGEX.captures(diag).and_then(|m| m.get(1))
2110 && let Some(span) = manifest.find_crate_span(crate_name.as_str())
2111 {
2112 let rel_path = pathdiff::diff_paths(&manifest.path, &manifest.cwd)
2113 .unwrap_or_else(|| manifest.path.clone())
2114 .display()
2115 .to_string();
2116 let report = [Group::with_title(Level::NOTE.secondary_title(format!(
2117 "dependency `{}` declared here",
2118 crate_name.as_str()
2119 )))
2120 .element(
2121 Snippet::source(&manifest.contents)
2122 .path(rel_path)
2123 .annotation(AnnotationKind::Context.span(span)),
2124 )];
2125
2126 let rendered = Renderer::styled()
2127 .term_width(manifest.term_width)
2128 .render(&report);
2129 diag.push_str(&rendered);
2130 diag.push('\n');
2131 return true;
2132 }
2133 false
2134 };
2135
2136 match options.format {
2139 MessageFormat::Human
2144 | MessageFormat::Short
2145 | MessageFormat::Json {
2146 render_diagnostics: true,
2147 ..
2148 } => {
2149 #[derive(serde::Deserialize)]
2150 struct CompilerMessage<'a> {
2151 rendered: String,
2155 #[serde(borrow)]
2156 message: Cow<'a, str>,
2157 #[serde(borrow)]
2158 level: Cow<'a, str>,
2159 children: Vec<PartialDiagnostic>,
2160 code: Option<DiagnosticCode>,
2161 }
2162
2163 #[derive(serde::Deserialize)]
2172 struct PartialDiagnostic {
2173 spans: Vec<PartialDiagnosticSpan>,
2174 }
2175
2176 #[derive(serde::Deserialize)]
2178 struct PartialDiagnosticSpan {
2179 suggestion_applicability: Option<Applicability>,
2180 }
2181
2182 #[derive(serde::Deserialize)]
2183 struct DiagnosticCode {
2184 code: String,
2185 }
2186
2187 if let Ok(mut msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2188 {
2189 if msg.message.starts_with("aborting due to")
2190 || msg.message.ends_with("warning emitted")
2191 || msg.message.ends_with("warnings emitted")
2192 {
2193 return Ok(true);
2195 }
2196 if msg.rendered.ends_with('\n') {
2198 msg.rendered.pop();
2199 }
2200 let mut rendered = msg.rendered;
2201 if options.show_diagnostics {
2202 let machine_applicable: bool = msg
2203 .children
2204 .iter()
2205 .map(|child| {
2206 child
2207 .spans
2208 .iter()
2209 .filter_map(|span| span.suggestion_applicability)
2210 .any(|app| app == Applicability::MachineApplicable)
2211 })
2212 .any(|b| b);
2213 count_diagnostic(&msg.level, options);
2214 if msg
2215 .code
2216 .as_ref()
2217 .is_some_and(|c| c.code == "exported_private_dependencies")
2218 && options.format != MessageFormat::Short
2219 {
2220 add_pub_in_priv_diagnostic(&mut rendered);
2221 }
2222 let lint = msg.code.is_some();
2223 state.emit_diag(&msg.level, rendered, lint, machine_applicable)?;
2224 }
2225 return Ok(true);
2226 }
2227 }
2228
2229 MessageFormat::Json { ansi, .. } => {
2230 #[derive(serde::Deserialize, serde::Serialize)]
2231 struct CompilerMessage<'a> {
2232 rendered: String,
2233 #[serde(flatten, borrow)]
2234 other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2235 code: Option<DiagnosticCode<'a>>,
2236 }
2237
2238 #[derive(serde::Deserialize, serde::Serialize)]
2239 struct DiagnosticCode<'a> {
2240 code: String,
2241 #[serde(flatten, borrow)]
2242 other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2243 }
2244
2245 if let Ok(mut error) =
2246 serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2247 {
2248 let modified_diag = if error
2249 .code
2250 .as_ref()
2251 .is_some_and(|c| c.code == "exported_private_dependencies")
2252 {
2253 add_pub_in_priv_diagnostic(&mut error.rendered)
2254 } else {
2255 false
2256 };
2257
2258 if !ansi {
2262 error.rendered = anstream::adapter::strip_str(&error.rendered).to_string();
2263 }
2264 if !ansi || modified_diag {
2265 let new_line = serde_json::to_string(&error)?;
2266 compiler_message = serde_json::value::RawValue::from_string(new_line)?;
2267 }
2268 }
2269 }
2270 }
2271
2272 #[derive(serde::Deserialize)]
2279 struct ArtifactNotification<'a> {
2280 #[serde(borrow)]
2281 artifact: Cow<'a, str>,
2282 }
2283
2284 if let Ok(artifact) = serde_json::from_str::<ArtifactNotification<'_>>(compiler_message.get()) {
2285 trace!("found directive from rustc: `{}`", artifact.artifact);
2286 if artifact.artifact.ends_with(".rmeta") {
2287 debug!("looks like metadata finished early!");
2288 state.rmeta_produced();
2289 }
2290 return Ok(false);
2291 }
2292
2293 if !options.show_diagnostics {
2298 return Ok(true);
2299 }
2300
2301 #[derive(serde::Deserialize)]
2302 struct CompilerMessage<'a> {
2303 #[serde(borrow)]
2304 message: Cow<'a, str>,
2305 #[serde(borrow)]
2306 level: Cow<'a, str>,
2307 }
2308
2309 if let Ok(msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get()) {
2310 if msg.message.starts_with("aborting due to")
2311 || msg.message.ends_with("warning emitted")
2312 || msg.message.ends_with("warnings emitted")
2313 {
2314 return Ok(true);
2316 }
2317 count_diagnostic(&msg.level, options);
2318 }
2319
2320 let msg = machine_message::FromCompiler {
2321 package_id: package_id.to_spec(),
2322 manifest_path: &manifest.path,
2323 target,
2324 message: compiler_message,
2325 }
2326 .to_json_string();
2327
2328 state.stdout(msg)?;
2332 Ok(true)
2333}
2334
2335impl ManifestErrorContext {
2336 fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> ManifestErrorContext {
2337 let mut duplicates = HashSet::new();
2338 let mut rename_table = HashMap::new();
2339
2340 for dep in build_runner.unit_deps(unit) {
2341 let unrenamed_id = dep.unit.pkg.package_id().name();
2342 if duplicates.contains(&unrenamed_id) {
2343 continue;
2344 }
2345 match rename_table.entry(unrenamed_id) {
2346 std::collections::hash_map::Entry::Occupied(occ) => {
2347 occ.remove_entry();
2348 duplicates.insert(unrenamed_id);
2349 }
2350 std::collections::hash_map::Entry::Vacant(vac) => {
2351 vac.insert(dep.extern_crate_name);
2352 }
2353 }
2354 }
2355
2356 let bcx = build_runner.bcx;
2357 ManifestErrorContext {
2358 path: unit.pkg.manifest_path().to_owned(),
2359 spans: unit.pkg.manifest().document().clone(),
2360 contents: unit.pkg.manifest().contents().to_owned(),
2361 requested_kinds: bcx.target_data.requested_kinds().to_owned(),
2362 host_name: bcx.rustc().host,
2363 rename_table,
2364 cwd: path_args(build_runner.bcx.ws, unit).1,
2365 cfgs: bcx
2366 .target_data
2367 .requested_kinds()
2368 .iter()
2369 .map(|k| bcx.target_data.cfg(*k).to_owned())
2370 .collect(),
2371 term_width: bcx
2372 .gctx
2373 .shell()
2374 .err_width()
2375 .diagnostic_terminal_width()
2376 .unwrap_or(annotate_snippets::renderer::DEFAULT_TERM_WIDTH),
2377 }
2378 }
2379
2380 fn requested_target_names(&self) -> impl Iterator<Item = &str> {
2381 self.requested_kinds.iter().map(|kind| match kind {
2382 CompileKind::Host => &self.host_name,
2383 CompileKind::Target(target) => target.short_name(),
2384 })
2385 }
2386
2387 fn find_crate_span(&self, unrenamed: &str) -> Option<Range<usize>> {
2401 let orig_name = self.rename_table.get(unrenamed)?.as_str();
2402
2403 if let Some((k, v)) = get_key_value(&self.spans, &["dependencies", orig_name]) {
2404 if let Some(package) = v.get_ref().as_table().and_then(|t| t.get("package")) {
2413 return Some(package.span());
2414 } else {
2415 return Some(k.span());
2416 }
2417 }
2418
2419 if let Some(target) = self
2424 .spans
2425 .as_ref()
2426 .get("target")
2427 .and_then(|t| t.as_ref().as_table())
2428 {
2429 for (platform, platform_table) in target.iter() {
2430 match platform.as_ref().parse::<Platform>() {
2431 Ok(Platform::Name(name)) => {
2432 if !self.requested_target_names().any(|n| n == name) {
2433 continue;
2434 }
2435 }
2436 Ok(Platform::Cfg(cfg_expr)) => {
2437 if !self.cfgs.iter().any(|cfgs| cfg_expr.matches(cfgs)) {
2438 continue;
2439 }
2440 }
2441 Err(_) => continue,
2442 }
2443
2444 let Some(platform_table) = platform_table.as_ref().as_table() else {
2445 continue;
2446 };
2447
2448 if let Some(deps) = platform_table
2449 .get("dependencies")
2450 .and_then(|d| d.as_ref().as_table())
2451 {
2452 if let Some((k, v)) = deps.get_key_value(orig_name) {
2453 if let Some(package) = v.get_ref().as_table().and_then(|t| t.get("package"))
2454 {
2455 return Some(package.span());
2456 } else {
2457 return Some(k.span());
2458 }
2459 }
2460 }
2461 }
2462 }
2463 None
2464 }
2465}
2466
2467fn replay_output_cache(
2471 package_id: PackageId,
2472 manifest: ManifestErrorContext,
2473 target: &Target,
2474 path: PathBuf,
2475 format: MessageFormat,
2476 show_diagnostics: bool,
2477) -> Work {
2478 let target = target.clone();
2479 let mut options = OutputOptions {
2480 format,
2481 cache_cell: None,
2482 show_diagnostics,
2483 warnings_seen: 0,
2484 errors_seen: 0,
2485 };
2486 Work::new(move |state| {
2487 if !path.exists() {
2488 return Ok(());
2490 }
2491 let file = paths::open(&path)?;
2495 let mut reader = std::io::BufReader::new(file);
2496 let mut line = String::new();
2497 loop {
2498 let length = reader.read_line(&mut line)?;
2499 if length == 0 {
2500 break;
2501 }
2502 let trimmed = line.trim_end_matches(&['\n', '\r'][..]);
2503 on_stderr_line(state, trimmed, package_id, &manifest, &target, &mut options)?;
2504 line.clear();
2505 }
2506 Ok(())
2507 })
2508}
2509
2510fn descriptive_pkg_name(name: &str, target: &Target, mode: &CompileMode) -> String {
2513 let desc_name = target.description_named();
2514 let mode = if mode.is_rustc_test() && !(target.is_test() || target.is_bench()) {
2515 " test"
2516 } else if mode.is_doc_test() {
2517 " doctest"
2518 } else if mode.is_doc() {
2519 " doc"
2520 } else {
2521 ""
2522 };
2523 format!("`{name}` ({desc_name}{mode})")
2524}
2525
2526pub(crate) fn apply_env_config(
2528 gctx: &crate::GlobalContext,
2529 cmd: &mut ProcessBuilder,
2530) -> CargoResult<()> {
2531 for (key, value) in gctx.env_config()?.iter() {
2532 if cmd.get_envs().contains_key(key) {
2534 continue;
2535 }
2536 cmd.env(key, value);
2537 }
2538 Ok(())
2539}
2540
2541fn should_include_scrape_units(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
2543 unit.mode.is_doc() && bcx.scrape_units.len() > 0 && bcx.ws.unit_needs_doc_scrape(unit)
2544}
2545
2546fn scrape_output_path(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<PathBuf> {
2548 assert!(unit.mode.is_doc() || unit.mode.is_doc_scrape());
2549 build_runner
2550 .outputs(unit)
2551 .map(|outputs| outputs[0].path.clone())
2552}
2553
2554fn rustdoc_dep_info_loc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> PathBuf {
2556 let mut loc = build_runner.files().fingerprint_file_path(unit, "");
2557 loc.set_extension("d");
2558 loc
2559}