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