1pub mod artifact;
35mod build_config;
36pub(crate) mod build_context;
37mod build_plan;
38pub(crate) mod build_runner;
39mod compilation;
40mod compile_kind;
41mod crate_type;
42mod custom_build;
43pub(crate) mod fingerprint;
44pub mod future_incompat;
45pub(crate) mod job_queue;
46pub(crate) mod layout;
47mod links;
48mod lto;
49mod output_depinfo;
50mod output_sbom;
51pub mod rustdoc;
52pub mod standard_lib;
53mod timings;
54mod unit;
55pub mod unit_dependencies;
56pub mod unit_graph;
57
58use std::borrow::Cow;
59use std::collections::{HashMap, HashSet};
60use std::env;
61use std::ffi::{OsStr, OsString};
62use std::fmt::Display;
63use std::fs::{self, File};
64use std::io::{BufRead, BufWriter, Write};
65use std::ops::Range;
66use std::path::{Path, PathBuf};
67use std::sync::{Arc, LazyLock};
68
69use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
70use anyhow::{Context as _, Error};
71use cargo_platform::{Cfg, Platform};
72use lazycell::LazyCell;
73use regex::Regex;
74use tracing::{debug, instrument, trace};
75
76pub use self::build_config::UserIntent;
77pub use self::build_config::{BuildConfig, CompileMode, MessageFormat, TimingOutput};
78pub use self::build_context::{
79 BuildContext, FileFlavor, FileType, RustDocFingerprint, RustcTargetData, TargetInfo,
80};
81use self::build_plan::BuildPlan;
82pub use self::build_runner::{BuildRunner, Metadata, UnitHash};
83pub use self::compilation::{Compilation, Doctest, UnitOutput};
84pub use self::compile_kind::{CompileKind, CompileKindFallback, CompileTarget};
85pub use self::crate_type::CrateType;
86pub use self::custom_build::LinkArgTarget;
87pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts, LibraryPath};
88pub(crate) use self::fingerprint::DirtyReason;
89pub use self::job_queue::Freshness;
90use self::job_queue::{Job, JobQueue, JobState, Work};
91pub(crate) use self::layout::Layout;
92pub use self::lto::Lto;
93use self::output_depinfo::output_depinfo;
94use self::output_sbom::build_sbom;
95use self::unit_graph::UnitDep;
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::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, plan, exec))]
176fn compile<'gctx>(
177 build_runner: &mut BuildRunner<'_, 'gctx>,
178 jobs: &mut JobQueue<'gctx>,
179 plan: &mut BuildPlan,
180 unit: &Unit,
181 exec: &Arc<dyn Executor>,
182 force_rebuild: bool,
183) -> CargoResult<()> {
184 let bcx = build_runner.bcx;
185 let build_plan = bcx.build_config.build_plan;
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 if build_plan {
204 Job::new_dirty(
205 rustc(build_runner, unit, &exec.clone())?,
206 DirtyReason::FreshBuild,
207 )
208 } else {
209 let force = exec.force_rebuild(unit) || force_rebuild;
210 let mut job = fingerprint::prepare_target(build_runner, unit, force)?;
211 job.before(if job.freshness().is_dirty() {
212 let work = if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
213 rustdoc(build_runner, unit)?
214 } else {
215 rustc(build_runner, unit, exec)?
216 };
217 work.then(link_targets(build_runner, unit, false)?)
218 } else {
219 let show_diagnostics = unit.show_warnings(bcx.gctx)
222 && build_runner.bcx.gctx.warning_handling()? != WarningHandling::Allow;
223 let manifest = ManifestErrorContext::new(build_runner, unit);
224 let work = replay_output_cache(
225 unit.pkg.package_id(),
226 manifest,
227 &unit.target,
228 build_runner.files().message_cache_path(unit),
229 build_runner.bcx.build_config.message_format,
230 show_diagnostics,
231 );
232 work.then(link_targets(build_runner, unit, true)?)
234 });
235
236 job
237 };
238 jobs.enqueue(build_runner, unit, job)?;
239 }
240
241 let deps = Vec::from(build_runner.unit_deps(unit)); for dep in deps {
244 compile(build_runner, jobs, plan, &dep.unit, exec, false)?;
245 }
246 if build_plan {
247 plan.add(build_runner, unit)?;
248 }
249
250 Ok(())
251}
252
253fn make_failed_scrape_diagnostic(
256 build_runner: &BuildRunner<'_, '_>,
257 unit: &Unit,
258 top_line: impl Display,
259) -> String {
260 let manifest_path = unit.pkg.manifest_path();
261 let relative_manifest_path = manifest_path
262 .strip_prefix(build_runner.bcx.ws.root())
263 .unwrap_or(&manifest_path);
264
265 format!(
266 "\
267{top_line}
268 Try running with `--verbose` to see the error message.
269 If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in {}",
270 relative_manifest_path.display()
271 )
272}
273
274fn rustc(
276 build_runner: &mut BuildRunner<'_, '_>,
277 unit: &Unit,
278 exec: &Arc<dyn Executor>,
279) -> CargoResult<Work> {
280 let mut rustc = prepare_rustc(build_runner, unit)?;
281 let build_plan = build_runner.bcx.build_config.build_plan;
282
283 let name = unit.pkg.name();
284 let buildkey = unit.buildkey();
285
286 let outputs = build_runner.outputs(unit)?;
287 let root = build_runner.files().out_dir(unit);
288
289 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
291 let current_id = unit.pkg.package_id();
292 let manifest = ManifestErrorContext::new(build_runner, unit);
293 let build_scripts = build_runner.build_scripts.get(unit).cloned();
294
295 let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib());
298
299 let dep_info_name =
300 if let Some(c_extra_filename) = build_runner.files().metadata(unit).c_extra_filename() {
301 format!("{}-{}.d", unit.target.crate_name(), c_extra_filename)
302 } else {
303 format!("{}.d", unit.target.crate_name())
304 };
305 let rustc_dep_info_loc = root.join(dep_info_name);
306 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
307
308 let mut output_options = OutputOptions::new(build_runner, unit);
309 let package_id = unit.pkg.package_id();
310 let target = Target::clone(&unit.target);
311 let mode = unit.mode;
312
313 exec.init(build_runner, unit);
314 let exec = exec.clone();
315
316 let root_output = build_runner.files().host_dest().to_path_buf();
317 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
318 let pkg_root = unit.pkg.root().to_path_buf();
319 let cwd = rustc
320 .get_cwd()
321 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
322 .to_path_buf();
323 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
324 let script_metadatas = build_runner.find_build_script_metadatas(unit);
325 let is_local = unit.is_local();
326 let artifact = unit.artifact;
327 let sbom_files = build_runner.sbom_output_files(unit)?;
328 let sbom = build_sbom(build_runner, unit)?;
329
330 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
331 && !matches!(
332 build_runner.bcx.gctx.shell().verbosity(),
333 Verbosity::Verbose
334 );
335 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
336 let target_desc = unit.target.description_named();
339 let mut for_scrape_units = build_runner
340 .bcx
341 .scrape_units_have_dep_on(unit)
342 .into_iter()
343 .map(|unit| unit.target.description_named())
344 .collect::<Vec<_>>();
345 for_scrape_units.sort();
346 let for_scrape_units = for_scrape_units.join(", ");
347 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}"))
348 });
349 if hide_diagnostics_for_scrape_unit {
350 output_options.show_diagnostics = false;
351 }
352 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
353 return Ok(Work::new(move |state| {
354 if artifact.is_true() {
358 paths::create_dir_all(&root)?;
359 }
360
361 if let Some(build_scripts) = build_scripts {
369 let script_outputs = build_script_outputs.lock().unwrap();
370 if !build_plan {
371 add_native_deps(
372 &mut rustc,
373 &script_outputs,
374 &build_scripts,
375 pass_l_flag,
376 &target,
377 current_id,
378 mode,
379 )?;
380 add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?;
381 }
382 add_custom_flags(&mut rustc, &script_outputs, script_metadatas)?;
383 }
384
385 for output in outputs.iter() {
386 if output.path.extension() == Some(OsStr::new("rmeta")) {
390 let dst = root.join(&output.path).with_extension("rlib");
391 if dst.exists() {
392 paths::remove_file(&dst)?;
393 }
394 }
395
396 if output.hardlink.is_some() && output.path.exists() {
401 _ = paths::remove_file(&output.path).map_err(|e| {
402 tracing::debug!(
403 "failed to delete previous output file `{:?}`: {e:?}",
404 output.path
405 );
406 });
407 }
408 }
409
410 state.running(&rustc);
411 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
412 if build_plan {
413 state.build_plan(buildkey, rustc.clone(), outputs.clone());
414 } else {
415 for file in sbom_files {
416 tracing::debug!("writing sbom to {}", file.display());
417 let outfile = BufWriter::new(paths::create(&file)?);
418 serde_json::to_writer(outfile, &sbom)?;
419 }
420
421 let result = exec
422 .exec(
423 &rustc,
424 package_id,
425 &target,
426 mode,
427 &mut |line| on_stdout_line(state, line, package_id, &target),
428 &mut |line| {
429 on_stderr_line(
430 state,
431 line,
432 package_id,
433 &manifest,
434 &target,
435 &mut output_options,
436 )
437 },
438 )
439 .map_err(|e| {
440 if output_options.errors_seen == 0 {
441 e
446 } else {
447 verbose_if_simple_exit_code(e)
448 }
449 })
450 .with_context(|| {
451 let warnings = match output_options.warnings_seen {
453 0 => String::new(),
454 1 => "; 1 warning emitted".to_string(),
455 count => format!("; {} warnings emitted", count),
456 };
457 let errors = match output_options.errors_seen {
458 0 => String::new(),
459 1 => " due to 1 previous error".to_string(),
460 count => format!(" due to {} previous errors", count),
461 };
462 let name = descriptive_pkg_name(&name, &target, &mode);
463 format!("could not compile {name}{errors}{warnings}")
464 });
465
466 if let Err(e) = result {
467 if let Some(diagnostic) = failed_scrape_diagnostic {
468 state.warning(diagnostic);
469 }
470
471 return Err(e);
472 }
473
474 debug_assert_eq!(output_options.errors_seen, 0);
476 }
477
478 if rustc_dep_info_loc.exists() {
479 fingerprint::translate_dep_info(
480 &rustc_dep_info_loc,
481 &dep_info_loc,
482 &cwd,
483 &pkg_root,
484 &build_dir,
485 &rustc,
486 is_local,
488 &env_config,
489 )
490 .with_context(|| {
491 internal(format!(
492 "could not parse/generate dep info at: {}",
493 rustc_dep_info_loc.display()
494 ))
495 })?;
496 paths::set_file_time_no_err(dep_info_loc, timestamp);
499 }
500
501 Ok(())
502 }));
503
504 fn add_native_deps(
507 rustc: &mut ProcessBuilder,
508 build_script_outputs: &BuildScriptOutputs,
509 build_scripts: &BuildScripts,
510 pass_l_flag: bool,
511 target: &Target,
512 current_id: PackageId,
513 mode: CompileMode,
514 ) -> CargoResult<()> {
515 let mut library_paths = vec![];
516
517 for key in build_scripts.to_link.iter() {
518 let output = build_script_outputs.get(key.1).ok_or_else(|| {
519 internal(format!(
520 "couldn't find build script output for {}/{}",
521 key.0, key.1
522 ))
523 })?;
524 library_paths.extend(output.library_paths.iter());
525 }
526
527 library_paths.sort_by_key(|p| match p {
533 LibraryPath::CargoArtifact(_) => 0,
534 LibraryPath::External(_) => 1,
535 });
536
537 for path in library_paths.iter() {
538 rustc.arg("-L").arg(path.as_ref());
539 }
540
541 for key in build_scripts.to_link.iter() {
542 let output = build_script_outputs.get(key.1).ok_or_else(|| {
543 internal(format!(
544 "couldn't find build script output for {}/{}",
545 key.0, key.1
546 ))
547 })?;
548
549 if key.0 == current_id {
550 if pass_l_flag {
551 for name in output.library_links.iter() {
552 rustc.arg("-l").arg(name);
553 }
554 }
555 }
556
557 for (lt, arg) in &output.linker_args {
558 if lt.applies_to(target, mode)
564 && (key.0 == current_id || *lt == LinkArgTarget::Cdylib)
565 {
566 rustc.arg("-C").arg(format!("link-arg={}", arg));
567 }
568 }
569 }
570 Ok(())
571 }
572}
573
574fn verbose_if_simple_exit_code(err: Error) -> Error {
575 match err
578 .downcast_ref::<ProcessError>()
579 .as_ref()
580 .and_then(|perr| perr.code)
581 {
582 Some(n) if cargo_util::is_simple_exit_code(n) => VerboseError::new(err).into(),
583 _ => err,
584 }
585}
586
587fn link_targets(
590 build_runner: &mut BuildRunner<'_, '_>,
591 unit: &Unit,
592 fresh: bool,
593) -> CargoResult<Work> {
594 let bcx = build_runner.bcx;
595 let outputs = build_runner.outputs(unit)?;
596 let export_dir = build_runner.files().export_dir();
597 let package_id = unit.pkg.package_id();
598 let manifest_path = PathBuf::from(unit.pkg.manifest_path());
599 let profile = unit.profile.clone();
600 let unit_mode = unit.mode;
601 let features = unit.features.iter().map(|s| s.to_string()).collect();
602 let json_messages = bcx.build_config.emit_json();
603 let executable = build_runner.get_executable(unit)?;
604 let mut target = Target::clone(&unit.target);
605 if let TargetSourcePath::Metabuild = target.src_path() {
606 let path = unit
608 .pkg
609 .manifest()
610 .metabuild_path(build_runner.bcx.ws.build_dir());
611 target.set_src_path(TargetSourcePath::Path(path));
612 }
613
614 Ok(Work::new(move |state| {
615 let mut destinations = vec![];
620 for output in outputs.iter() {
621 let src = &output.path;
622 if !src.exists() {
625 continue;
626 }
627 let Some(dst) = output.hardlink.as_ref() else {
628 destinations.push(src.clone());
629 continue;
630 };
631 destinations.push(dst.clone());
632 paths::link_or_copy(src, dst)?;
633 if let Some(ref path) = output.export_path {
634 let export_dir = export_dir.as_ref().unwrap();
635 paths::create_dir_all(export_dir)?;
636
637 paths::link_or_copy(src, path)?;
638 }
639 }
640
641 if json_messages {
642 let debuginfo = match profile.debuginfo.into_inner() {
643 TomlDebugInfo::None => machine_message::ArtifactDebuginfo::Int(0),
644 TomlDebugInfo::Limited => machine_message::ArtifactDebuginfo::Int(1),
645 TomlDebugInfo::Full => machine_message::ArtifactDebuginfo::Int(2),
646 TomlDebugInfo::LineDirectivesOnly => {
647 machine_message::ArtifactDebuginfo::Named("line-directives-only")
648 }
649 TomlDebugInfo::LineTablesOnly => {
650 machine_message::ArtifactDebuginfo::Named("line-tables-only")
651 }
652 };
653 let art_profile = machine_message::ArtifactProfile {
654 opt_level: profile.opt_level.as_str(),
655 debuginfo: Some(debuginfo),
656 debug_assertions: profile.debug_assertions,
657 overflow_checks: profile.overflow_checks,
658 test: unit_mode.is_any_test(),
659 };
660
661 let msg = machine_message::Artifact {
662 package_id: package_id.to_spec(),
663 manifest_path,
664 target: &target,
665 profile: art_profile,
666 features,
667 filenames: destinations,
668 executable,
669 fresh,
670 }
671 .to_json_string();
672 state.stdout(msg)?;
673 }
674 Ok(())
675 }))
676}
677
678fn add_plugin_deps(
682 rustc: &mut ProcessBuilder,
683 build_script_outputs: &BuildScriptOutputs,
684 build_scripts: &BuildScripts,
685 root_output: &Path,
686) -> CargoResult<()> {
687 let var = paths::dylib_path_envvar();
688 let search_path = rustc.get_env(var).unwrap_or_default();
689 let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>();
690 for (pkg_id, metadata) in &build_scripts.plugins {
691 let output = build_script_outputs
692 .get(*metadata)
693 .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?;
694 search_path.append(&mut filter_dynamic_search_path(
695 output.library_paths.iter().map(AsRef::as_ref),
696 root_output,
697 ));
698 }
699 let search_path = paths::join_paths(&search_path, var)?;
700 rustc.env(var, &search_path);
701 Ok(())
702}
703
704fn get_dynamic_search_path(path: &Path) -> &Path {
705 match path.to_str().and_then(|s| s.split_once("=")) {
706 Some(("native" | "crate" | "dependency" | "framework" | "all", path)) => Path::new(path),
707 _ => path,
708 }
709}
710
711fn filter_dynamic_search_path<'a, I>(paths: I, root_output: &Path) -> Vec<PathBuf>
717where
718 I: Iterator<Item = &'a PathBuf>,
719{
720 let mut search_path = vec![];
721 for dir in paths {
722 let dir = get_dynamic_search_path(dir);
723 if dir.starts_with(&root_output) {
724 search_path.push(dir.to_path_buf());
725 } else {
726 debug!(
727 "Not including path {} in runtime library search path because it is \
728 outside target root {}",
729 dir.display(),
730 root_output.display()
731 );
732 }
733 }
734 search_path
735}
736
737fn prepare_rustc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
744 let gctx = build_runner.bcx.gctx;
745 let is_primary = build_runner.is_primary_package(unit);
746 let is_workspace = build_runner.bcx.ws.is_member(&unit.pkg);
747
748 let mut base = build_runner
749 .compilation
750 .rustc_process(unit, is_primary, is_workspace)?;
751 build_base_args(build_runner, &mut base, unit)?;
752 if unit.pkg.manifest().is_embedded() {
753 if !gctx.cli_unstable().script {
754 anyhow::bail!(
755 "parsing `{}` requires `-Zscript`",
756 unit.pkg.manifest_path().display()
757 );
758 }
759 base.arg("-Z").arg("crate-attr=feature(frontmatter)");
760 }
761
762 base.inherit_jobserver(&build_runner.jobserver);
763 build_deps_args(&mut base, build_runner, unit)?;
764 add_cap_lints(build_runner.bcx, unit, &mut base);
765 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
766 base.args(args);
767 }
768 base.args(&unit.rustflags);
769 if gctx.cli_unstable().binary_dep_depinfo {
770 base.arg("-Z").arg("binary-dep-depinfo");
771 }
772 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
773 base.arg("-Z").arg("checksum-hash-algorithm=blake3");
774 }
775
776 if is_primary {
777 base.env("CARGO_PRIMARY_PACKAGE", "1");
778 let file_list = std::env::join_paths(build_runner.sbom_output_files(unit)?)?;
779 base.env("CARGO_SBOM_PATH", file_list);
780 }
781
782 if unit.target.is_test() || unit.target.is_bench() {
783 let tmp = build_runner.files().layout(unit.kind).prepare_tmp()?;
784 base.env("CARGO_TARGET_TMPDIR", tmp.display().to_string());
785 }
786
787 Ok(base)
788}
789
790fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
797 let bcx = build_runner.bcx;
798 let mut rustdoc = build_runner.compilation.rustdoc_process(unit, None)?;
800 if unit.pkg.manifest().is_embedded() {
801 if !bcx.gctx.cli_unstable().script {
802 anyhow::bail!(
803 "parsing `{}` requires `-Zscript`",
804 unit.pkg.manifest_path().display()
805 );
806 }
807 rustdoc.arg("-Z").arg("crate-attr=feature(frontmatter)");
808 }
809 rustdoc.inherit_jobserver(&build_runner.jobserver);
810 let crate_name = unit.target.crate_name();
811 rustdoc.arg("--crate-name").arg(&crate_name);
812 add_path_args(bcx.ws, unit, &mut rustdoc);
813 add_cap_lints(bcx, unit, &mut rustdoc);
814
815 if let CompileKind::Target(target) = unit.kind {
816 rustdoc.arg("--target").arg(target.rustc_target());
817 }
818 let doc_dir = build_runner.files().out_dir(unit);
819 rustdoc.arg("-o").arg(&doc_dir);
820 rustdoc.args(&features_args(unit));
821 rustdoc.args(&check_cfg_args(unit));
822
823 add_error_format_and_color(build_runner, &mut rustdoc);
824 add_allow_features(build_runner, &mut rustdoc);
825
826 if build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo {
827 let mut arg =
830 OsString::from("--emit=toolchain-shared-resources,invocation-specific,dep-info=");
831 arg.push(rustdoc_dep_info_loc(build_runner, unit));
832 rustdoc.arg(arg);
833
834 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
835 rustdoc.arg("-Z").arg("checksum-hash-algorithm=blake3");
836 }
837
838 rustdoc.arg("-Zunstable-options");
839 }
840
841 if let Some(trim_paths) = unit.profile.trim_paths.as_ref() {
842 trim_paths_args_rustdoc(&mut rustdoc, build_runner, unit, trim_paths)?;
843 }
844
845 rustdoc.args(unit.pkg.manifest().lint_rustflags());
846
847 let metadata = build_runner.metadata_for_doc_units[unit];
848 rustdoc
849 .arg("-C")
850 .arg(format!("metadata={}", metadata.c_metadata()));
851
852 if unit.mode.is_doc_scrape() {
853 debug_assert!(build_runner.bcx.scrape_units.contains(unit));
854
855 if unit.target.is_test() {
856 rustdoc.arg("--scrape-tests");
857 }
858
859 rustdoc.arg("-Zunstable-options");
860
861 rustdoc
862 .arg("--scrape-examples-output-path")
863 .arg(scrape_output_path(build_runner, unit)?);
864
865 for pkg in build_runner.bcx.packages.packages() {
867 let names = pkg
868 .targets()
869 .iter()
870 .map(|target| target.crate_name())
871 .collect::<HashSet<_>>();
872 for name in names {
873 rustdoc.arg("--scrape-examples-target-crate").arg(name);
874 }
875 }
876 }
877
878 if should_include_scrape_units(build_runner.bcx, unit) {
879 rustdoc.arg("-Zunstable-options");
880 }
881
882 build_deps_args(&mut rustdoc, build_runner, unit)?;
883 rustdoc::add_root_urls(build_runner, unit, &mut rustdoc)?;
884
885 rustdoc::add_output_format(build_runner, &mut rustdoc)?;
886
887 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
888 rustdoc.args(args);
889 }
890 rustdoc.args(&unit.rustdocflags);
891
892 if !crate_version_flag_already_present(&rustdoc) {
893 append_crate_version_flag(unit, &mut rustdoc);
894 }
895
896 Ok(rustdoc)
897}
898
899fn rustdoc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Work> {
901 let mut rustdoc = prepare_rustdoc(build_runner, unit)?;
902
903 let crate_name = unit.target.crate_name();
904 let doc_dir = build_runner.files().out_dir(unit);
905 paths::create_dir_all(&doc_dir)?;
909
910 let target_desc = unit.target.description_named();
911 let name = unit.pkg.name();
912 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
913 let package_id = unit.pkg.package_id();
914 let target = Target::clone(&unit.target);
915 let manifest = ManifestErrorContext::new(build_runner, unit);
916
917 let rustdoc_dep_info_loc = rustdoc_dep_info_loc(build_runner, unit);
918 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
919 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
920 let pkg_root = unit.pkg.root().to_path_buf();
921 let cwd = rustdoc
922 .get_cwd()
923 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
924 .to_path_buf();
925 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
926 let is_local = unit.is_local();
927 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
928 let rustdoc_depinfo_enabled = build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo;
929
930 let mut output_options = OutputOptions::new(build_runner, unit);
931 let script_metadatas = build_runner.find_build_script_metadatas(unit);
932 let scrape_outputs = if should_include_scrape_units(build_runner.bcx, unit) {
933 Some(
934 build_runner
935 .bcx
936 .scrape_units
937 .iter()
938 .map(|unit| {
939 Ok((
940 build_runner.files().metadata(unit).unit_id(),
941 scrape_output_path(build_runner, unit)?,
942 ))
943 })
944 .collect::<CargoResult<HashMap<_, _>>>()?,
945 )
946 } else {
947 None
948 };
949
950 let failed_scrape_units = Arc::clone(&build_runner.failed_scrape_units);
951 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
952 && !matches!(
953 build_runner.bcx.gctx.shell().verbosity(),
954 Verbosity::Verbose
955 );
956 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
957 make_failed_scrape_diagnostic(
958 build_runner,
959 unit,
960 format_args!("failed to scan {target_desc} in package `{name}` for example code usage"),
961 )
962 });
963 if hide_diagnostics_for_scrape_unit {
964 output_options.show_diagnostics = false;
965 }
966
967 Ok(Work::new(move |state| {
968 add_custom_flags(
969 &mut rustdoc,
970 &build_script_outputs.lock().unwrap(),
971 script_metadatas,
972 )?;
973
974 if let Some(scrape_outputs) = scrape_outputs {
979 let failed_scrape_units = failed_scrape_units.lock().unwrap();
980 for (metadata, output_path) in &scrape_outputs {
981 if !failed_scrape_units.contains(metadata) {
982 rustdoc.arg("--with-examples").arg(output_path);
983 }
984 }
985 }
986
987 let crate_dir = doc_dir.join(&crate_name);
988 if crate_dir.exists() {
989 debug!("removing pre-existing doc directory {:?}", crate_dir);
992 paths::remove_dir_all(crate_dir)?;
993 }
994 state.running(&rustdoc);
995 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
996
997 let result = rustdoc
998 .exec_with_streaming(
999 &mut |line| on_stdout_line(state, line, package_id, &target),
1000 &mut |line| {
1001 on_stderr_line(
1002 state,
1003 line,
1004 package_id,
1005 &manifest,
1006 &target,
1007 &mut output_options,
1008 )
1009 },
1010 false,
1011 )
1012 .map_err(verbose_if_simple_exit_code)
1013 .with_context(|| format!("could not document `{}`", name));
1014
1015 if let Err(e) = result {
1016 if let Some(diagnostic) = failed_scrape_diagnostic {
1017 state.warning(diagnostic);
1018 }
1019
1020 return Err(e);
1021 }
1022
1023 if rustdoc_depinfo_enabled && rustdoc_dep_info_loc.exists() {
1024 fingerprint::translate_dep_info(
1025 &rustdoc_dep_info_loc,
1026 &dep_info_loc,
1027 &cwd,
1028 &pkg_root,
1029 &build_dir,
1030 &rustdoc,
1031 is_local,
1033 &env_config,
1034 )
1035 .with_context(|| {
1036 internal(format_args!(
1037 "could not parse/generate dep info at: {}",
1038 rustdoc_dep_info_loc.display()
1039 ))
1040 })?;
1041 paths::set_file_time_no_err(dep_info_loc, timestamp);
1044 }
1045
1046 Ok(())
1047 }))
1048}
1049
1050fn crate_version_flag_already_present(rustdoc: &ProcessBuilder) -> bool {
1053 rustdoc.get_args().any(|flag| {
1054 flag.to_str()
1055 .map_or(false, |flag| flag.starts_with(RUSTDOC_CRATE_VERSION_FLAG))
1056 })
1057}
1058
1059fn append_crate_version_flag(unit: &Unit, rustdoc: &mut ProcessBuilder) {
1060 rustdoc
1061 .arg(RUSTDOC_CRATE_VERSION_FLAG)
1062 .arg(unit.pkg.version().to_string());
1063}
1064
1065fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuilder) {
1069 if !unit.show_warnings(bcx.gctx) {
1072 cmd.arg("--cap-lints").arg("allow");
1073
1074 } else if !unit.is_local() {
1077 cmd.arg("--cap-lints").arg("warn");
1078 }
1079}
1080
1081fn add_allow_features(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1085 if let Some(allow) = &build_runner.bcx.gctx.cli_unstable().allow_features {
1086 use std::fmt::Write;
1087 let mut arg = String::from("-Zallow-features=");
1088 for f in allow {
1089 let _ = write!(&mut arg, "{f},");
1090 }
1091 cmd.arg(arg.trim_end_matches(','));
1092 }
1093}
1094
1095fn add_error_format_and_color(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1106 let enable_timings = build_runner.bcx.gctx.cli_unstable().section_timings
1107 && !build_runner.bcx.build_config.timing_outputs.is_empty();
1108 if enable_timings {
1109 cmd.arg("-Zunstable-options");
1110 }
1111
1112 cmd.arg("--error-format=json");
1113 let mut json = String::from("--json=diagnostic-rendered-ansi,artifacts,future-incompat");
1114
1115 match build_runner.bcx.build_config.message_format {
1116 MessageFormat::Short | MessageFormat::Json { short: true, .. } => {
1117 json.push_str(",diagnostic-short");
1118 }
1119 _ => {}
1120 }
1121
1122 if enable_timings {
1123 json.push_str(",timings");
1124 }
1125
1126 cmd.arg(json);
1127
1128 let gctx = build_runner.bcx.gctx;
1129 if let Some(width) = gctx.shell().err_width().diagnostic_terminal_width() {
1130 cmd.arg(format!("--diagnostic-width={width}"));
1131 }
1132}
1133
1134fn build_base_args(
1136 build_runner: &BuildRunner<'_, '_>,
1137 cmd: &mut ProcessBuilder,
1138 unit: &Unit,
1139) -> CargoResult<()> {
1140 assert!(!unit.mode.is_run_custom_build());
1141
1142 let bcx = build_runner.bcx;
1143 let Profile {
1144 ref opt_level,
1145 codegen_backend,
1146 codegen_units,
1147 debuginfo,
1148 debug_assertions,
1149 split_debuginfo,
1150 overflow_checks,
1151 rpath,
1152 ref panic,
1153 incremental,
1154 strip,
1155 rustflags: profile_rustflags,
1156 trim_paths,
1157 hint_mostly_unused: profile_hint_mostly_unused,
1158 ..
1159 } = unit.profile.clone();
1160 let hints = unit.pkg.hints().cloned().unwrap_or_default();
1161 let test = unit.mode.is_any_test();
1162
1163 let warn = |msg: &str| {
1164 bcx.gctx.shell().warn(format!(
1165 "{}@{}: {msg}",
1166 unit.pkg.package_id().name(),
1167 unit.pkg.package_id().version()
1168 ))
1169 };
1170 let unit_capped_warn = |msg: &str| {
1171 if unit.show_warnings(bcx.gctx) {
1172 warn(msg)
1173 } else {
1174 Ok(())
1175 }
1176 };
1177
1178 cmd.arg("--crate-name").arg(&unit.target.crate_name());
1179
1180 let edition = unit.target.edition();
1181 edition.cmd_edition_arg(cmd);
1182
1183 add_path_args(bcx.ws, unit, cmd);
1184 add_error_format_and_color(build_runner, cmd);
1185 add_allow_features(build_runner, cmd);
1186
1187 let mut contains_dy_lib = false;
1188 if !test {
1189 for crate_type in &unit.target.rustc_crate_types() {
1190 cmd.arg("--crate-type").arg(crate_type.as_str());
1191 contains_dy_lib |= crate_type == &CrateType::Dylib;
1192 }
1193 }
1194
1195 if unit.mode.is_check() {
1196 cmd.arg("--emit=dep-info,metadata");
1197 } else if build_runner.bcx.gctx.cli_unstable().no_embed_metadata {
1198 if unit.benefits_from_no_embed_metadata() {
1208 cmd.arg("--emit=dep-info,metadata,link");
1209 cmd.args(&["-Z", "embed-metadata=no"]);
1210 } else {
1211 cmd.arg("--emit=dep-info,link");
1212 }
1213 } else {
1214 if !unit.requires_upstream_objects() {
1218 cmd.arg("--emit=dep-info,metadata,link");
1219 } else {
1220 cmd.arg("--emit=dep-info,link");
1221 }
1222 }
1223
1224 let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
1225 || (contains_dy_lib && !build_runner.is_primary_package(unit));
1226 if prefer_dynamic {
1227 cmd.arg("-C").arg("prefer-dynamic");
1228 }
1229
1230 if opt_level.as_str() != "0" {
1231 cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
1232 }
1233
1234 if *panic != PanicStrategy::Unwind {
1235 cmd.arg("-C").arg(format!("panic={}", panic));
1236 }
1237 if *panic == PanicStrategy::ImmediateAbort {
1238 cmd.arg("-Z").arg("unstable-options");
1239 }
1240
1241 cmd.args(<o_args(build_runner, unit));
1242
1243 if let Some(backend) = codegen_backend {
1244 cmd.arg("-Z").arg(&format!("codegen-backend={}", backend));
1245 }
1246
1247 if let Some(n) = codegen_units {
1248 cmd.arg("-C").arg(&format!("codegen-units={}", n));
1249 }
1250
1251 let debuginfo = debuginfo.into_inner();
1252 if debuginfo != TomlDebugInfo::None {
1254 cmd.arg("-C").arg(format!("debuginfo={debuginfo}"));
1255 if let Some(split) = split_debuginfo {
1262 if build_runner
1263 .bcx
1264 .target_data
1265 .info(unit.kind)
1266 .supports_debuginfo_split(split)
1267 {
1268 cmd.arg("-C").arg(format!("split-debuginfo={split}"));
1269 }
1270 }
1271 }
1272
1273 if let Some(trim_paths) = trim_paths {
1274 trim_paths_args(cmd, build_runner, unit, &trim_paths)?;
1275 }
1276
1277 cmd.args(unit.pkg.manifest().lint_rustflags());
1278 cmd.args(&profile_rustflags);
1279
1280 if opt_level.as_str() != "0" {
1284 if debug_assertions {
1285 cmd.args(&["-C", "debug-assertions=on"]);
1286 if !overflow_checks {
1287 cmd.args(&["-C", "overflow-checks=off"]);
1288 }
1289 } else if overflow_checks {
1290 cmd.args(&["-C", "overflow-checks=on"]);
1291 }
1292 } else if !debug_assertions {
1293 cmd.args(&["-C", "debug-assertions=off"]);
1294 if overflow_checks {
1295 cmd.args(&["-C", "overflow-checks=on"]);
1296 }
1297 } else if !overflow_checks {
1298 cmd.args(&["-C", "overflow-checks=off"]);
1299 }
1300
1301 if test && unit.target.harness() {
1302 cmd.arg("--test");
1303
1304 if *panic == PanicStrategy::Abort || *panic == PanicStrategy::ImmediateAbort {
1312 cmd.arg("-Z").arg("panic-abort-tests");
1313 }
1314 } else if test {
1315 cmd.arg("--cfg").arg("test");
1316 }
1317
1318 cmd.args(&features_args(unit));
1319 cmd.args(&check_cfg_args(unit));
1320
1321 let meta = build_runner.files().metadata(unit);
1322 cmd.arg("-C")
1323 .arg(&format!("metadata={}", meta.c_metadata()));
1324 if let Some(c_extra_filename) = meta.c_extra_filename() {
1325 cmd.arg("-C")
1326 .arg(&format!("extra-filename=-{c_extra_filename}"));
1327 }
1328
1329 if rpath {
1330 cmd.arg("-C").arg("rpath");
1331 }
1332
1333 cmd.arg("--out-dir")
1334 .arg(&build_runner.files().out_dir(unit));
1335
1336 fn opt(cmd: &mut ProcessBuilder, key: &str, prefix: &str, val: Option<&OsStr>) {
1337 if let Some(val) = val {
1338 let mut joined = OsString::from(prefix);
1339 joined.push(val);
1340 cmd.arg(key).arg(joined);
1341 }
1342 }
1343
1344 if let CompileKind::Target(n) = unit.kind {
1345 cmd.arg("--target").arg(n.rustc_target());
1346 }
1347
1348 opt(
1349 cmd,
1350 "-C",
1351 "linker=",
1352 build_runner
1353 .compilation
1354 .target_linker(unit.kind)
1355 .as_ref()
1356 .map(|s| s.as_ref()),
1357 );
1358 if incremental {
1359 let dir = build_runner
1360 .files()
1361 .layout(unit.kind)
1362 .incremental()
1363 .as_os_str();
1364 opt(cmd, "-C", "incremental=", Some(dir));
1365 }
1366
1367 let pkg_hint_mostly_unused = match hints.mostly_unused {
1368 None => None,
1369 Some(toml::Value::Boolean(b)) => Some(b),
1370 Some(v) => {
1371 unit_capped_warn(&format!(
1372 "ignoring unsupported value type ({}) for 'hints.mostly-unused', which expects a boolean",
1373 v.type_str()
1374 ))?;
1375 None
1376 }
1377 };
1378 if profile_hint_mostly_unused
1379 .or(pkg_hint_mostly_unused)
1380 .unwrap_or(false)
1381 {
1382 if bcx.gctx.cli_unstable().profile_hint_mostly_unused {
1383 cmd.arg("-Zhint-mostly-unused");
1384 } else {
1385 if profile_hint_mostly_unused.is_some() {
1386 warn(
1388 "ignoring 'hint-mostly-unused' profile option, pass `-Zprofile-hint-mostly-unused` to enable it",
1389 )?;
1390 } else if pkg_hint_mostly_unused.is_some() {
1391 unit_capped_warn(
1392 "ignoring 'hints.mostly-unused', pass `-Zprofile-hint-mostly-unused` to enable it",
1393 )?;
1394 }
1395 }
1396 }
1397
1398 let strip = strip.into_inner();
1399 if strip != StripInner::None {
1400 cmd.arg("-C").arg(format!("strip={}", strip));
1401 }
1402
1403 if unit.is_std {
1404 cmd.arg("-Z")
1410 .arg("force-unstable-if-unmarked")
1411 .env("RUSTC_BOOTSTRAP", "1");
1412 }
1413
1414 if unit.target.is_test() || unit.target.is_bench() {
1416 for bin_target in unit
1417 .pkg
1418 .manifest()
1419 .targets()
1420 .iter()
1421 .filter(|target| target.is_bin())
1422 {
1423 let exe_path = build_runner.files().bin_link_for_target(
1424 bin_target,
1425 unit.kind,
1426 build_runner.bcx,
1427 )?;
1428 let name = bin_target
1429 .binary_filename()
1430 .unwrap_or(bin_target.name().to_string());
1431 let key = format!("CARGO_BIN_EXE_{}", name);
1432 cmd.env(&key, exe_path);
1433 }
1434 }
1435 Ok(())
1436}
1437
1438fn features_args(unit: &Unit) -> Vec<OsString> {
1440 let mut args = Vec::with_capacity(unit.features.len() * 2);
1441
1442 for feat in &unit.features {
1443 args.push(OsString::from("--cfg"));
1444 args.push(OsString::from(format!("feature=\"{}\"", feat)));
1445 }
1446
1447 args
1448}
1449
1450fn trim_paths_args_rustdoc(
1452 cmd: &mut ProcessBuilder,
1453 build_runner: &BuildRunner<'_, '_>,
1454 unit: &Unit,
1455 trim_paths: &TomlTrimPaths,
1456) -> CargoResult<()> {
1457 match trim_paths {
1458 TomlTrimPaths::Values(values) if !values.contains(&TomlTrimPathsValue::Diagnostics) => {
1460 return Ok(());
1461 }
1462 _ => {}
1463 }
1464
1465 cmd.arg("-Zunstable-options");
1467
1468 cmd.arg(package_remap(build_runner, unit));
1471 cmd.arg(build_dir_remap(build_runner));
1472 cmd.arg(sysroot_remap(build_runner, unit));
1473
1474 Ok(())
1475}
1476
1477fn trim_paths_args(
1483 cmd: &mut ProcessBuilder,
1484 build_runner: &BuildRunner<'_, '_>,
1485 unit: &Unit,
1486 trim_paths: &TomlTrimPaths,
1487) -> CargoResult<()> {
1488 if trim_paths.is_none() {
1489 return Ok(());
1490 }
1491
1492 cmd.arg("-Zunstable-options");
1494 cmd.arg(format!("-Zremap-path-scope={trim_paths}"));
1495
1496 cmd.arg(package_remap(build_runner, unit));
1499 cmd.arg(build_dir_remap(build_runner));
1500 cmd.arg(sysroot_remap(build_runner, unit));
1501
1502 Ok(())
1503}
1504
1505fn sysroot_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1510 let mut remap = OsString::from("--remap-path-prefix=");
1511 remap.push({
1512 let mut sysroot = build_runner.bcx.target_data.info(unit.kind).sysroot.clone();
1514 sysroot.push("lib");
1515 sysroot.push("rustlib");
1516 sysroot.push("src");
1517 sysroot.push("rust");
1518 sysroot
1519 });
1520 remap.push("=");
1521 remap.push("/rustc/");
1522 if let Some(commit_hash) = build_runner.bcx.rustc().commit_hash.as_ref() {
1523 remap.push(commit_hash);
1524 } else {
1525 remap.push(build_runner.bcx.rustc().version.to_string());
1526 }
1527 remap
1528}
1529
1530fn package_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1538 let pkg_root = unit.pkg.root();
1539 let ws_root = build_runner.bcx.ws.root();
1540 let mut remap = OsString::from("--remap-path-prefix=");
1541 let source_id = unit.pkg.package_id().source_id();
1542 if source_id.is_git() {
1543 remap.push(
1544 build_runner
1545 .bcx
1546 .gctx
1547 .git_checkouts_path()
1548 .as_path_unlocked(),
1549 );
1550 remap.push("=");
1551 } else if source_id.is_registry() {
1552 remap.push(
1553 build_runner
1554 .bcx
1555 .gctx
1556 .registry_source_path()
1557 .as_path_unlocked(),
1558 );
1559 remap.push("=");
1560 } else if pkg_root.strip_prefix(ws_root).is_ok() {
1561 remap.push(ws_root);
1562 remap.push("=."); } else {
1564 remap.push(pkg_root);
1565 remap.push("=");
1566 remap.push(unit.pkg.name());
1567 remap.push("-");
1568 remap.push(unit.pkg.version().to_string());
1569 }
1570 remap
1571}
1572
1573fn build_dir_remap(build_runner: &BuildRunner<'_, '_>) -> OsString {
1586 let build_dir = build_runner.bcx.ws.build_dir();
1587 let mut remap = OsString::from("--remap-path-prefix=");
1588 remap.push(build_dir.as_path_unlocked());
1589 remap.push("=/cargo/build-dir");
1590 remap
1591}
1592
1593fn check_cfg_args(unit: &Unit) -> Vec<OsString> {
1595 let gross_cap_estimation = unit.pkg.summary().features().len() * 7 + 25;
1613 let mut arg_feature = OsString::with_capacity(gross_cap_estimation);
1614
1615 arg_feature.push("cfg(feature, values(");
1616 for (i, feature) in unit.pkg.summary().features().keys().enumerate() {
1617 if i != 0 {
1618 arg_feature.push(", ");
1619 }
1620 arg_feature.push("\"");
1621 arg_feature.push(feature);
1622 arg_feature.push("\"");
1623 }
1624 arg_feature.push("))");
1625
1626 vec![
1635 OsString::from("--check-cfg"),
1636 OsString::from("cfg(docsrs,test)"),
1637 OsString::from("--check-cfg"),
1638 arg_feature,
1639 ]
1640}
1641
1642fn lto_args(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> Vec<OsString> {
1644 let mut result = Vec::new();
1645 let mut push = |arg: &str| {
1646 result.push(OsString::from("-C"));
1647 result.push(OsString::from(arg));
1648 };
1649 match build_runner.lto[unit] {
1650 lto::Lto::Run(None) => push("lto"),
1651 lto::Lto::Run(Some(s)) => push(&format!("lto={}", s)),
1652 lto::Lto::Off => {
1653 push("lto=off");
1654 push("embed-bitcode=no");
1655 }
1656 lto::Lto::ObjectAndBitcode => {} lto::Lto::OnlyBitcode => push("linker-plugin-lto"),
1658 lto::Lto::OnlyObject => push("embed-bitcode=no"),
1659 }
1660 result
1661}
1662
1663fn build_deps_args(
1669 cmd: &mut ProcessBuilder,
1670 build_runner: &BuildRunner<'_, '_>,
1671 unit: &Unit,
1672) -> CargoResult<()> {
1673 let bcx = build_runner.bcx;
1674 cmd.arg("-L").arg(&{
1675 let mut deps = OsString::from("dependency=");
1676 deps.push(build_runner.files().deps_dir(unit));
1677 deps
1678 });
1679
1680 if !unit.kind.is_host() {
1683 cmd.arg("-L").arg(&{
1684 let mut deps = OsString::from("dependency=");
1685 deps.push(build_runner.files().host_deps());
1686 deps
1687 });
1688 }
1689
1690 let deps = build_runner.unit_deps(unit);
1691
1692 if !deps
1696 .iter()
1697 .any(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_linkable())
1698 {
1699 if let Some(dep) = deps.iter().find(|dep| {
1700 !dep.unit.mode.is_doc() && dep.unit.target.is_lib() && !dep.unit.artifact.is_true()
1701 }) {
1702 bcx.gctx.shell().warn(format!(
1703 "The package `{}` \
1704 provides no linkable target. The compiler might raise an error while compiling \
1705 `{}`. Consider adding 'dylib' or 'rlib' to key `crate-type` in `{}`'s \
1706 Cargo.toml. This warning might turn into a hard error in the future.",
1707 dep.unit.target.crate_name(),
1708 unit.target.crate_name(),
1709 dep.unit.target.crate_name()
1710 ))?;
1711 }
1712 }
1713
1714 let mut unstable_opts = false;
1715
1716 let first_custom_build_dep = deps.iter().find(|dep| dep.unit.mode.is_run_custom_build());
1718 if let Some(dep) = first_custom_build_dep {
1719 let out_dir = &build_runner.files().build_script_out_dir(&dep.unit);
1720 cmd.env("OUT_DIR", &out_dir);
1721 }
1722
1723 let is_multiple_build_scripts_enabled = unit
1725 .pkg
1726 .manifest()
1727 .unstable_features()
1728 .require(Feature::multiple_build_scripts())
1729 .is_ok();
1730
1731 if is_multiple_build_scripts_enabled {
1732 for dep in deps {
1733 if dep.unit.mode.is_run_custom_build() {
1734 let out_dir = &build_runner.files().build_script_out_dir(&dep.unit);
1735 let target_name = dep.unit.target.name();
1736 let out_dir_prefix = target_name
1737 .strip_prefix("build-script-")
1738 .unwrap_or(target_name);
1739 let out_dir_name = format!("{out_dir_prefix}_OUT_DIR");
1740 cmd.env(&out_dir_name, &out_dir);
1741 }
1742 }
1743 }
1744 for arg in extern_args(build_runner, unit, &mut unstable_opts)? {
1745 cmd.arg(arg);
1746 }
1747
1748 for (var, env) in artifact::get_env(build_runner, deps)? {
1749 cmd.env(&var, env);
1750 }
1751
1752 if unstable_opts {
1755 cmd.arg("-Z").arg("unstable-options");
1756 }
1757
1758 Ok(())
1759}
1760
1761fn add_custom_flags(
1765 cmd: &mut ProcessBuilder,
1766 build_script_outputs: &BuildScriptOutputs,
1767 metadata_vec: Option<Vec<UnitHash>>,
1768) -> CargoResult<()> {
1769 if let Some(metadata_vec) = metadata_vec {
1770 for metadata in metadata_vec {
1771 if let Some(output) = build_script_outputs.get(metadata) {
1772 for cfg in output.cfgs.iter() {
1773 cmd.arg("--cfg").arg(cfg);
1774 }
1775 for check_cfg in &output.check_cfgs {
1776 cmd.arg("--check-cfg").arg(check_cfg);
1777 }
1778 for (name, value) in output.env.iter() {
1779 cmd.env(name, value);
1780 }
1781 }
1782 }
1783 }
1784
1785 Ok(())
1786}
1787
1788pub fn extern_args(
1790 build_runner: &BuildRunner<'_, '_>,
1791 unit: &Unit,
1792 unstable_opts: &mut bool,
1793) -> CargoResult<Vec<OsString>> {
1794 let mut result = Vec::new();
1795 let deps = build_runner.unit_deps(unit);
1796
1797 let no_embed_metadata = build_runner.bcx.gctx.cli_unstable().no_embed_metadata;
1798
1799 let mut link_to =
1801 |dep: &UnitDep, extern_crate_name: InternedString, noprelude: bool| -> CargoResult<()> {
1802 let mut value = OsString::new();
1803 let mut opts = Vec::new();
1804 let is_public_dependency_enabled = unit
1805 .pkg
1806 .manifest()
1807 .unstable_features()
1808 .require(Feature::public_dependency())
1809 .is_ok()
1810 || build_runner.bcx.gctx.cli_unstable().public_dependency;
1811 if !dep.public && unit.target.is_lib() && is_public_dependency_enabled {
1812 opts.push("priv");
1813 *unstable_opts = true;
1814 }
1815 if noprelude {
1816 opts.push("noprelude");
1817 *unstable_opts = true;
1818 }
1819 if !opts.is_empty() {
1820 value.push(opts.join(","));
1821 value.push(":");
1822 }
1823 value.push(extern_crate_name.as_str());
1824 value.push("=");
1825
1826 let mut pass = |file| {
1827 let mut value = value.clone();
1828 value.push(file);
1829 result.push(OsString::from("--extern"));
1830 result.push(value);
1831 };
1832
1833 let outputs = build_runner.outputs(&dep.unit)?;
1834
1835 if build_runner.only_requires_rmeta(unit, &dep.unit) || dep.unit.mode.is_check() {
1836 let output = outputs
1838 .iter()
1839 .find(|output| output.flavor == FileFlavor::Rmeta)
1840 .expect("failed to find rmeta dep for pipelined dep");
1841 pass(&output.path);
1842 } else {
1843 for output in outputs.iter() {
1845 if output.flavor == FileFlavor::Linkable {
1846 pass(&output.path);
1847 }
1848 else if no_embed_metadata && output.flavor == FileFlavor::Rmeta {
1852 pass(&output.path);
1853 }
1854 }
1855 }
1856 Ok(())
1857 };
1858
1859 for dep in deps {
1860 if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
1861 link_to(dep, dep.extern_crate_name, dep.noprelude)?;
1862 }
1863 }
1864 if unit.target.proc_macro() {
1865 result.push(OsString::from("--extern"));
1867 result.push(OsString::from("proc_macro"));
1868 }
1869
1870 Ok(result)
1871}
1872
1873fn envify(s: &str) -> String {
1874 s.chars()
1875 .flat_map(|c| c.to_uppercase())
1876 .map(|c| if c == '-' { '_' } else { c })
1877 .collect()
1878}
1879
1880struct OutputOptions {
1883 format: MessageFormat,
1885 cache_cell: Option<(PathBuf, LazyCell<File>)>,
1890 show_diagnostics: bool,
1898 warnings_seen: usize,
1900 errors_seen: usize,
1902}
1903
1904impl OutputOptions {
1905 fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions {
1906 let path = build_runner.files().message_cache_path(unit);
1907 drop(fs::remove_file(&path));
1909 let cache_cell = Some((path, LazyCell::new()));
1910 let show_diagnostics =
1911 build_runner.bcx.gctx.warning_handling().unwrap_or_default() != WarningHandling::Allow;
1912 OutputOptions {
1913 format: build_runner.bcx.build_config.message_format,
1914 cache_cell,
1915 show_diagnostics,
1916 warnings_seen: 0,
1917 errors_seen: 0,
1918 }
1919 }
1920}
1921
1922struct ManifestErrorContext {
1928 path: PathBuf,
1930 spans: toml::Spanned<toml::de::DeTable<'static>>,
1932 contents: String,
1934 rename_table: HashMap<InternedString, InternedString>,
1937 requested_kinds: Vec<CompileKind>,
1940 cfgs: Vec<Vec<Cfg>>,
1943 host_name: InternedString,
1944 cwd: PathBuf,
1946 term_width: usize,
1948}
1949
1950fn on_stdout_line(
1951 state: &JobState<'_, '_>,
1952 line: &str,
1953 _package_id: PackageId,
1954 _target: &Target,
1955) -> CargoResult<()> {
1956 state.stdout(line.to_string())?;
1957 Ok(())
1958}
1959
1960fn on_stderr_line(
1961 state: &JobState<'_, '_>,
1962 line: &str,
1963 package_id: PackageId,
1964 manifest: &ManifestErrorContext,
1965 target: &Target,
1966 options: &mut OutputOptions,
1967) -> CargoResult<()> {
1968 if on_stderr_line_inner(state, line, package_id, manifest, target, options)? {
1969 if let Some((path, cell)) = &mut options.cache_cell {
1971 let f = cell.try_borrow_mut_with(|| paths::create(path))?;
1973 debug_assert!(!line.contains('\n'));
1974 f.write_all(line.as_bytes())?;
1975 f.write_all(&[b'\n'])?;
1976 }
1977 }
1978 Ok(())
1979}
1980
1981fn on_stderr_line_inner(
1983 state: &JobState<'_, '_>,
1984 line: &str,
1985 package_id: PackageId,
1986 manifest: &ManifestErrorContext,
1987 target: &Target,
1988 options: &mut OutputOptions,
1989) -> CargoResult<bool> {
1990 if !line.starts_with('{') {
1996 state.stderr(line.to_string())?;
1997 return Ok(true);
1998 }
1999
2000 let mut compiler_message: Box<serde_json::value::RawValue> = match serde_json::from_str(line) {
2001 Ok(msg) => msg,
2002
2003 Err(e) => {
2007 debug!("failed to parse json: {:?}", e);
2008 state.stderr(line.to_string())?;
2009 return Ok(true);
2010 }
2011 };
2012
2013 let count_diagnostic = |level, options: &mut OutputOptions| {
2014 if level == "warning" {
2015 options.warnings_seen += 1;
2016 } else if level == "error" {
2017 options.errors_seen += 1;
2018 }
2019 };
2020
2021 if let Ok(report) = serde_json::from_str::<FutureIncompatReport>(compiler_message.get()) {
2022 for item in &report.future_incompat_report {
2023 count_diagnostic(&*item.diagnostic.level, options);
2024 }
2025 state.future_incompat_report(report.future_incompat_report);
2026 return Ok(true);
2027 }
2028
2029 let res = serde_json::from_str::<SectionTiming>(compiler_message.get());
2030 if let Ok(timing_record) = res {
2031 state.on_section_timing_emitted(timing_record);
2032 return Ok(false);
2033 }
2034
2035 let add_pub_in_priv_diagnostic = |diag: &mut String| -> bool {
2037 static PRIV_DEP_REGEX: LazyLock<Regex> =
2046 LazyLock::new(|| Regex::new("from private dependency '([A-Za-z0-9-_]+)'").unwrap());
2047 if let Some(crate_name) = PRIV_DEP_REGEX.captures(diag).and_then(|m| m.get(1))
2048 && let Some(span) = manifest.find_crate_span(crate_name.as_str())
2049 {
2050 let rel_path = pathdiff::diff_paths(&manifest.path, &manifest.cwd)
2051 .unwrap_or_else(|| manifest.path.clone())
2052 .display()
2053 .to_string();
2054 let report = [Group::with_title(Level::NOTE.secondary_title(format!(
2055 "dependency `{}` declared here",
2056 crate_name.as_str()
2057 )))
2058 .element(
2059 Snippet::source(&manifest.contents)
2060 .path(rel_path)
2061 .annotation(AnnotationKind::Context.span(span)),
2062 )];
2063
2064 let rendered = Renderer::styled()
2065 .term_width(manifest.term_width)
2066 .render(&report);
2067 diag.push_str(&rendered);
2068 diag.push('\n');
2069 return true;
2070 }
2071 false
2072 };
2073
2074 match options.format {
2077 MessageFormat::Human
2082 | MessageFormat::Short
2083 | MessageFormat::Json {
2084 render_diagnostics: true,
2085 ..
2086 } => {
2087 #[derive(serde::Deserialize)]
2088 struct CompilerMessage<'a> {
2089 rendered: String,
2093 #[serde(borrow)]
2094 message: Cow<'a, str>,
2095 #[serde(borrow)]
2096 level: Cow<'a, str>,
2097 children: Vec<PartialDiagnostic>,
2098 code: Option<DiagnosticCode>,
2099 }
2100
2101 #[derive(serde::Deserialize)]
2110 struct PartialDiagnostic {
2111 spans: Vec<PartialDiagnosticSpan>,
2112 }
2113
2114 #[derive(serde::Deserialize)]
2116 struct PartialDiagnosticSpan {
2117 suggestion_applicability: Option<Applicability>,
2118 }
2119
2120 #[derive(serde::Deserialize)]
2121 struct DiagnosticCode {
2122 code: String,
2123 }
2124
2125 if let Ok(mut msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2126 {
2127 if msg.message.starts_with("aborting due to")
2128 || msg.message.ends_with("warning emitted")
2129 || msg.message.ends_with("warnings emitted")
2130 {
2131 return Ok(true);
2133 }
2134 if msg.rendered.ends_with('\n') {
2136 msg.rendered.pop();
2137 }
2138 let mut rendered = msg.rendered;
2139 if options.show_diagnostics {
2140 let machine_applicable: bool = msg
2141 .children
2142 .iter()
2143 .map(|child| {
2144 child
2145 .spans
2146 .iter()
2147 .filter_map(|span| span.suggestion_applicability)
2148 .any(|app| app == Applicability::MachineApplicable)
2149 })
2150 .any(|b| b);
2151 count_diagnostic(&msg.level, options);
2152 if msg
2153 .code
2154 .is_some_and(|c| c.code == "exported_private_dependencies")
2155 && options.format != MessageFormat::Short
2156 {
2157 add_pub_in_priv_diagnostic(&mut rendered);
2158 }
2159 state.emit_diag(&msg.level, rendered, machine_applicable)?;
2160 }
2161 return Ok(true);
2162 }
2163 }
2164
2165 MessageFormat::Json { ansi, .. } => {
2166 #[derive(serde::Deserialize, serde::Serialize)]
2167 struct CompilerMessage<'a> {
2168 rendered: String,
2169 #[serde(flatten, borrow)]
2170 other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2171 code: Option<DiagnosticCode>,
2172 }
2173
2174 #[derive(serde::Deserialize, serde::Serialize)]
2175 struct DiagnosticCode {
2176 code: String,
2177 }
2178
2179 if let Ok(mut error) =
2180 serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2181 {
2182 let modified_diag = if error
2183 .code
2184 .as_ref()
2185 .is_some_and(|c| c.code == "exported_private_dependencies")
2186 {
2187 add_pub_in_priv_diagnostic(&mut error.rendered)
2188 } else {
2189 false
2190 };
2191
2192 if !ansi {
2196 error.rendered = anstream::adapter::strip_str(&error.rendered).to_string();
2197 }
2198 if !ansi || modified_diag {
2199 let new_line = serde_json::to_string(&error)?;
2200 compiler_message = serde_json::value::RawValue::from_string(new_line)?;
2201 }
2202 }
2203 }
2204 }
2205
2206 #[derive(serde::Deserialize)]
2213 struct ArtifactNotification<'a> {
2214 #[serde(borrow)]
2215 artifact: Cow<'a, str>,
2216 }
2217
2218 if let Ok(artifact) = serde_json::from_str::<ArtifactNotification<'_>>(compiler_message.get()) {
2219 trace!("found directive from rustc: `{}`", artifact.artifact);
2220 if artifact.artifact.ends_with(".rmeta") {
2221 debug!("looks like metadata finished early!");
2222 state.rmeta_produced();
2223 }
2224 return Ok(false);
2225 }
2226
2227 if !options.show_diagnostics {
2232 return Ok(true);
2233 }
2234
2235 #[derive(serde::Deserialize)]
2236 struct CompilerMessage<'a> {
2237 #[serde(borrow)]
2238 message: Cow<'a, str>,
2239 #[serde(borrow)]
2240 level: Cow<'a, str>,
2241 }
2242
2243 if let Ok(msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get()) {
2244 if msg.message.starts_with("aborting due to")
2245 || msg.message.ends_with("warning emitted")
2246 || msg.message.ends_with("warnings emitted")
2247 {
2248 return Ok(true);
2250 }
2251 count_diagnostic(&msg.level, options);
2252 }
2253
2254 let msg = machine_message::FromCompiler {
2255 package_id: package_id.to_spec(),
2256 manifest_path: &manifest.path,
2257 target,
2258 message: compiler_message,
2259 }
2260 .to_json_string();
2261
2262 state.stdout(msg)?;
2266 Ok(true)
2267}
2268
2269impl ManifestErrorContext {
2270 fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> ManifestErrorContext {
2271 let mut duplicates = HashSet::new();
2272 let mut rename_table = HashMap::new();
2273
2274 for dep in build_runner.unit_deps(unit) {
2275 let unrenamed_id = dep.unit.pkg.package_id().name();
2276 if duplicates.contains(&unrenamed_id) {
2277 continue;
2278 }
2279 match rename_table.entry(unrenamed_id) {
2280 std::collections::hash_map::Entry::Occupied(occ) => {
2281 occ.remove_entry();
2282 duplicates.insert(unrenamed_id);
2283 }
2284 std::collections::hash_map::Entry::Vacant(vac) => {
2285 vac.insert(dep.extern_crate_name);
2286 }
2287 }
2288 }
2289
2290 let bcx = build_runner.bcx;
2291 ManifestErrorContext {
2292 path: unit.pkg.manifest_path().to_owned(),
2293 spans: unit.pkg.manifest().document().clone(),
2294 contents: unit.pkg.manifest().contents().to_owned(),
2295 requested_kinds: bcx.target_data.requested_kinds().to_owned(),
2296 host_name: bcx.rustc().host,
2297 rename_table,
2298 cwd: path_args(build_runner.bcx.ws, unit).1,
2299 cfgs: bcx
2300 .target_data
2301 .requested_kinds()
2302 .iter()
2303 .map(|k| bcx.target_data.cfg(*k).to_owned())
2304 .collect(),
2305 term_width: bcx
2306 .gctx
2307 .shell()
2308 .err_width()
2309 .diagnostic_terminal_width()
2310 .unwrap_or(annotate_snippets::renderer::DEFAULT_TERM_WIDTH),
2311 }
2312 }
2313
2314 fn requested_target_names(&self) -> impl Iterator<Item = &str> {
2315 self.requested_kinds.iter().map(|kind| match kind {
2316 CompileKind::Host => &self.host_name,
2317 CompileKind::Target(target) => target.short_name(),
2318 })
2319 }
2320
2321 fn find_crate_span(&self, unrenamed: &str) -> Option<Range<usize>> {
2335 let orig_name = self.rename_table.get(unrenamed)?.as_str();
2336
2337 if let Some((k, v)) = get_key_value(&self.spans, &["dependencies", orig_name]) {
2338 if let Some(package) = v.get_ref().as_table().and_then(|t| t.get("package")) {
2347 return Some(package.span());
2348 } else {
2349 return Some(k.span());
2350 }
2351 }
2352
2353 if let Some(target) = self
2358 .spans
2359 .as_ref()
2360 .get("target")
2361 .and_then(|t| t.as_ref().as_table())
2362 {
2363 for (platform, platform_table) in target.iter() {
2364 match platform.as_ref().parse::<Platform>() {
2365 Ok(Platform::Name(name)) => {
2366 if !self.requested_target_names().any(|n| n == name) {
2367 continue;
2368 }
2369 }
2370 Ok(Platform::Cfg(cfg_expr)) => {
2371 if !self.cfgs.iter().any(|cfgs| cfg_expr.matches(cfgs)) {
2372 continue;
2373 }
2374 }
2375 Err(_) => continue,
2376 }
2377
2378 let Some(platform_table) = platform_table.as_ref().as_table() else {
2379 continue;
2380 };
2381
2382 if let Some(deps) = platform_table
2383 .get("dependencies")
2384 .and_then(|d| d.as_ref().as_table())
2385 {
2386 if let Some((k, v)) = deps.get_key_value(orig_name) {
2387 if let Some(package) = v.get_ref().as_table().and_then(|t| t.get("package"))
2388 {
2389 return Some(package.span());
2390 } else {
2391 return Some(k.span());
2392 }
2393 }
2394 }
2395 }
2396 }
2397 None
2398 }
2399}
2400
2401fn replay_output_cache(
2405 package_id: PackageId,
2406 manifest: ManifestErrorContext,
2407 target: &Target,
2408 path: PathBuf,
2409 format: MessageFormat,
2410 show_diagnostics: bool,
2411) -> Work {
2412 let target = target.clone();
2413 let mut options = OutputOptions {
2414 format,
2415 cache_cell: None,
2416 show_diagnostics,
2417 warnings_seen: 0,
2418 errors_seen: 0,
2419 };
2420 Work::new(move |state| {
2421 if !path.exists() {
2422 return Ok(());
2424 }
2425 let file = paths::open(&path)?;
2429 let mut reader = std::io::BufReader::new(file);
2430 let mut line = String::new();
2431 loop {
2432 let length = reader.read_line(&mut line)?;
2433 if length == 0 {
2434 break;
2435 }
2436 let trimmed = line.trim_end_matches(&['\n', '\r'][..]);
2437 on_stderr_line(state, trimmed, package_id, &manifest, &target, &mut options)?;
2438 line.clear();
2439 }
2440 Ok(())
2441 })
2442}
2443
2444fn descriptive_pkg_name(name: &str, target: &Target, mode: &CompileMode) -> String {
2447 let desc_name = target.description_named();
2448 let mode = if mode.is_rustc_test() && !(target.is_test() || target.is_bench()) {
2449 " test"
2450 } else if mode.is_doc_test() {
2451 " doctest"
2452 } else if mode.is_doc() {
2453 " doc"
2454 } else {
2455 ""
2456 };
2457 format!("`{name}` ({desc_name}{mode})")
2458}
2459
2460pub(crate) fn apply_env_config(
2462 gctx: &crate::GlobalContext,
2463 cmd: &mut ProcessBuilder,
2464) -> CargoResult<()> {
2465 for (key, value) in gctx.env_config()?.iter() {
2466 if cmd.get_envs().contains_key(key) {
2468 continue;
2469 }
2470 cmd.env(key, value);
2471 }
2472 Ok(())
2473}
2474
2475fn should_include_scrape_units(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
2477 unit.mode.is_doc() && bcx.scrape_units.len() > 0 && bcx.ws.unit_needs_doc_scrape(unit)
2478}
2479
2480fn scrape_output_path(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<PathBuf> {
2482 assert!(unit.mode.is_doc() || unit.mode.is_doc_scrape());
2483 build_runner
2484 .outputs(unit)
2485 .map(|outputs| outputs[0].path.clone())
2486}
2487
2488fn rustdoc_dep_info_loc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> PathBuf {
2490 let mut loc = build_runner.files().fingerprint_file_path(unit, "");
2491 loc.set_extension("d");
2492 loc
2493}