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::path::{Path, PathBuf};
66use std::sync::Arc;
67
68use anyhow::{Context as _, Error};
69use lazycell::LazyCell;
70use tracing::{debug, instrument, trace};
71
72pub use self::build_config::UserIntent;
73pub use self::build_config::{BuildConfig, CompileMode, MessageFormat, TimingOutput};
74pub use self::build_context::{
75 BuildContext, FileFlavor, FileType, RustDocFingerprint, RustcTargetData, TargetInfo,
76};
77use self::build_plan::BuildPlan;
78pub use self::build_runner::{BuildRunner, Metadata, UnitHash};
79pub use self::compilation::{Compilation, Doctest, UnitOutput};
80pub use self::compile_kind::{CompileKind, CompileKindFallback, CompileTarget};
81pub use self::crate_type::CrateType;
82pub use self::custom_build::LinkArgTarget;
83pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts, LibraryPath};
84pub(crate) use self::fingerprint::DirtyReason;
85pub use self::job_queue::Freshness;
86use self::job_queue::{Job, JobQueue, JobState, Work};
87pub(crate) use self::layout::Layout;
88pub use self::lto::Lto;
89use self::output_depinfo::output_depinfo;
90use self::output_sbom::build_sbom;
91use self::unit_graph::UnitDep;
92use crate::core::compiler::future_incompat::FutureIncompatReport;
93use crate::core::compiler::timings::SectionTiming;
94pub use crate::core::compiler::unit::{Unit, UnitInterner};
95use crate::core::manifest::TargetSourcePath;
96use crate::core::profiles::{PanicStrategy, Profile, StripInner};
97use crate::core::{Feature, PackageId, Target, Verbosity};
98use crate::util::context::WarningHandling;
99use crate::util::errors::{CargoResult, VerboseError};
100use crate::util::interning::InternedString;
101use crate::util::machine_message::{self, Message};
102use crate::util::{add_path_args, internal};
103use cargo_util::{ProcessBuilder, ProcessError, paths};
104use cargo_util_schemas::manifest::TomlDebugInfo;
105use cargo_util_schemas::manifest::TomlTrimPaths;
106use cargo_util_schemas::manifest::TomlTrimPathsValue;
107use rustfix::diagnostics::Applicability;
108pub(crate) use timings::CompilationSection;
109
110const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version";
111
112pub trait Executor: Send + Sync + 'static {
116 fn init(&self, _build_runner: &BuildRunner<'_, '_>, _unit: &Unit) {}
120
121 fn exec(
124 &self,
125 cmd: &ProcessBuilder,
126 id: PackageId,
127 target: &Target,
128 mode: CompileMode,
129 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
130 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
131 ) -> CargoResult<()>;
132
133 fn force_rebuild(&self, _unit: &Unit) -> bool {
136 false
137 }
138}
139
140#[derive(Copy, Clone)]
143pub struct DefaultExecutor;
144
145impl Executor for DefaultExecutor {
146 #[instrument(name = "rustc", skip_all, fields(package = id.name().as_str(), process = cmd.to_string()))]
147 fn exec(
148 &self,
149 cmd: &ProcessBuilder,
150 id: PackageId,
151 _target: &Target,
152 _mode: CompileMode,
153 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
154 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
155 ) -> CargoResult<()> {
156 cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
157 .map(drop)
158 }
159}
160
161#[tracing::instrument(skip(build_runner, jobs, plan, exec))]
171fn compile<'gctx>(
172 build_runner: &mut BuildRunner<'_, 'gctx>,
173 jobs: &mut JobQueue<'gctx>,
174 plan: &mut BuildPlan,
175 unit: &Unit,
176 exec: &Arc<dyn Executor>,
177 force_rebuild: bool,
178) -> CargoResult<()> {
179 let bcx = build_runner.bcx;
180 let build_plan = bcx.build_config.build_plan;
181 if !build_runner.compiled.insert(unit.clone()) {
182 return Ok(());
183 }
184
185 if !unit.skip_non_compile_time_dep {
189 fingerprint::prepare_init(build_runner, unit)?;
192
193 let job = if unit.mode.is_run_custom_build() {
194 custom_build::prepare(build_runner, unit)?
195 } else if unit.mode.is_doc_test() {
196 Job::new_fresh()
198 } else if build_plan {
199 Job::new_dirty(
200 rustc(build_runner, unit, &exec.clone())?,
201 DirtyReason::FreshBuild,
202 )
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 work = replay_output_cache(
219 unit.pkg.package_id(),
220 PathBuf::from(unit.pkg.manifest_path()),
221 &unit.target,
222 build_runner.files().message_cache_path(unit),
223 build_runner.bcx.build_config.message_format,
224 show_diagnostics,
225 );
226 work.then(link_targets(build_runner, unit, true)?)
228 });
229
230 job
231 };
232 jobs.enqueue(build_runner, unit, job)?;
233 }
234
235 let deps = Vec::from(build_runner.unit_deps(unit)); for dep in deps {
238 compile(build_runner, jobs, plan, &dep.unit, exec, false)?;
239 }
240 if build_plan {
241 plan.add(build_runner, unit)?;
242 }
243
244 Ok(())
245}
246
247fn make_failed_scrape_diagnostic(
250 build_runner: &BuildRunner<'_, '_>,
251 unit: &Unit,
252 top_line: impl Display,
253) -> String {
254 let manifest_path = unit.pkg.manifest_path();
255 let relative_manifest_path = manifest_path
256 .strip_prefix(build_runner.bcx.ws.root())
257 .unwrap_or(&manifest_path);
258
259 format!(
260 "\
261{top_line}
262 Try running with `--verbose` to see the error message.
263 If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in {}",
264 relative_manifest_path.display()
265 )
266}
267
268fn rustc(
270 build_runner: &mut BuildRunner<'_, '_>,
271 unit: &Unit,
272 exec: &Arc<dyn Executor>,
273) -> CargoResult<Work> {
274 let mut rustc = prepare_rustc(build_runner, unit)?;
275 let build_plan = build_runner.bcx.build_config.build_plan;
276
277 let name = unit.pkg.name();
278 let buildkey = unit.buildkey();
279
280 let outputs = build_runner.outputs(unit)?;
281 let root = build_runner.files().out_dir(unit);
282
283 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
285 let current_id = unit.pkg.package_id();
286 let manifest_path = PathBuf::from(unit.pkg.manifest_path());
287 let build_scripts = build_runner.build_scripts.get(unit).cloned();
288
289 let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib());
292
293 let dep_info_name =
294 if let Some(c_extra_filename) = build_runner.files().metadata(unit).c_extra_filename() {
295 format!("{}-{}.d", unit.target.crate_name(), c_extra_filename)
296 } else {
297 format!("{}.d", unit.target.crate_name())
298 };
299 let rustc_dep_info_loc = root.join(dep_info_name);
300 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
301
302 let mut output_options = OutputOptions::new(build_runner, unit);
303 let package_id = unit.pkg.package_id();
304 let target = Target::clone(&unit.target);
305 let mode = unit.mode;
306
307 exec.init(build_runner, unit);
308 let exec = exec.clone();
309
310 let root_output = build_runner.files().host_dest().to_path_buf();
311 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
312 let pkg_root = unit.pkg.root().to_path_buf();
313 let cwd = rustc
314 .get_cwd()
315 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
316 .to_path_buf();
317 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
318 let script_metadatas = build_runner.find_build_script_metadatas(unit);
319 let is_local = unit.is_local();
320 let artifact = unit.artifact;
321 let sbom_files = build_runner.sbom_output_files(unit)?;
322 let sbom = build_sbom(build_runner, unit)?;
323
324 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
325 && !matches!(
326 build_runner.bcx.gctx.shell().verbosity(),
327 Verbosity::Verbose
328 );
329 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
330 let target_desc = unit.target.description_named();
333 let mut for_scrape_units = build_runner
334 .bcx
335 .scrape_units_have_dep_on(unit)
336 .into_iter()
337 .map(|unit| unit.target.description_named())
338 .collect::<Vec<_>>();
339 for_scrape_units.sort();
340 let for_scrape_units = for_scrape_units.join(", ");
341 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}"))
342 });
343 if hide_diagnostics_for_scrape_unit {
344 output_options.show_diagnostics = false;
345 }
346 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
347 return Ok(Work::new(move |state| {
348 if artifact.is_true() {
352 paths::create_dir_all(&root)?;
353 }
354
355 if let Some(build_scripts) = build_scripts {
363 let script_outputs = build_script_outputs.lock().unwrap();
364 if !build_plan {
365 add_native_deps(
366 &mut rustc,
367 &script_outputs,
368 &build_scripts,
369 pass_l_flag,
370 &target,
371 current_id,
372 mode,
373 )?;
374 add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?;
375 }
376 add_custom_flags(&mut rustc, &script_outputs, script_metadatas)?;
377 }
378
379 for output in outputs.iter() {
380 if output.path.extension() == Some(OsStr::new("rmeta")) {
384 let dst = root.join(&output.path).with_extension("rlib");
385 if dst.exists() {
386 paths::remove_file(&dst)?;
387 }
388 }
389
390 if output.hardlink.is_some() && output.path.exists() {
395 _ = paths::remove_file(&output.path).map_err(|e| {
396 tracing::debug!(
397 "failed to delete previous output file `{:?}`: {e:?}",
398 output.path
399 );
400 });
401 }
402 }
403
404 state.running(&rustc);
405 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
406 if build_plan {
407 state.build_plan(buildkey, rustc.clone(), outputs.clone());
408 } else {
409 for file in sbom_files {
410 tracing::debug!("writing sbom to {}", file.display());
411 let outfile = BufWriter::new(paths::create(&file)?);
412 serde_json::to_writer(outfile, &sbom)?;
413 }
414
415 let result = exec
416 .exec(
417 &rustc,
418 package_id,
419 &target,
420 mode,
421 &mut |line| on_stdout_line(state, line, package_id, &target),
422 &mut |line| {
423 on_stderr_line(
424 state,
425 line,
426 package_id,
427 &manifest_path,
428 &target,
429 &mut output_options,
430 )
431 },
432 )
433 .map_err(|e| {
434 if output_options.errors_seen == 0 {
435 e
440 } else {
441 verbose_if_simple_exit_code(e)
442 }
443 })
444 .with_context(|| {
445 let warnings = match output_options.warnings_seen {
447 0 => String::new(),
448 1 => "; 1 warning emitted".to_string(),
449 count => format!("; {} warnings emitted", count),
450 };
451 let errors = match output_options.errors_seen {
452 0 => String::new(),
453 1 => " due to 1 previous error".to_string(),
454 count => format!(" due to {} previous errors", count),
455 };
456 let name = descriptive_pkg_name(&name, &target, &mode);
457 format!("could not compile {name}{errors}{warnings}")
458 });
459
460 if let Err(e) = result {
461 if let Some(diagnostic) = failed_scrape_diagnostic {
462 state.warning(diagnostic);
463 }
464
465 return Err(e);
466 }
467
468 debug_assert_eq!(output_options.errors_seen, 0);
470 }
471
472 if rustc_dep_info_loc.exists() {
473 fingerprint::translate_dep_info(
474 &rustc_dep_info_loc,
475 &dep_info_loc,
476 &cwd,
477 &pkg_root,
478 &build_dir,
479 &rustc,
480 is_local,
482 &env_config,
483 )
484 .with_context(|| {
485 internal(format!(
486 "could not parse/generate dep info at: {}",
487 rustc_dep_info_loc.display()
488 ))
489 })?;
490 paths::set_file_time_no_err(dep_info_loc, timestamp);
493 }
494
495 Ok(())
496 }));
497
498 fn add_native_deps(
501 rustc: &mut ProcessBuilder,
502 build_script_outputs: &BuildScriptOutputs,
503 build_scripts: &BuildScripts,
504 pass_l_flag: bool,
505 target: &Target,
506 current_id: PackageId,
507 mode: CompileMode,
508 ) -> CargoResult<()> {
509 let mut library_paths = vec![];
510
511 for key in build_scripts.to_link.iter() {
512 let output = build_script_outputs.get(key.1).ok_or_else(|| {
513 internal(format!(
514 "couldn't find build script output for {}/{}",
515 key.0, key.1
516 ))
517 })?;
518 library_paths.extend(output.library_paths.iter());
519 }
520
521 library_paths.sort_by_key(|p| match p {
527 LibraryPath::CargoArtifact(_) => 0,
528 LibraryPath::External(_) => 1,
529 });
530
531 for path in library_paths.iter() {
532 rustc.arg("-L").arg(path.as_ref());
533 }
534
535 for key in build_scripts.to_link.iter() {
536 let output = build_script_outputs.get(key.1).ok_or_else(|| {
537 internal(format!(
538 "couldn't find build script output for {}/{}",
539 key.0, key.1
540 ))
541 })?;
542
543 if key.0 == current_id {
544 if pass_l_flag {
545 for name in output.library_links.iter() {
546 rustc.arg("-l").arg(name);
547 }
548 }
549 }
550
551 for (lt, arg) in &output.linker_args {
552 if lt.applies_to(target, mode)
558 && (key.0 == current_id || *lt == LinkArgTarget::Cdylib)
559 {
560 rustc.arg("-C").arg(format!("link-arg={}", arg));
561 }
562 }
563 }
564 Ok(())
565 }
566}
567
568fn verbose_if_simple_exit_code(err: Error) -> Error {
569 match err
572 .downcast_ref::<ProcessError>()
573 .as_ref()
574 .and_then(|perr| perr.code)
575 {
576 Some(n) if cargo_util::is_simple_exit_code(n) => VerboseError::new(err).into(),
577 _ => err,
578 }
579}
580
581fn link_targets(
584 build_runner: &mut BuildRunner<'_, '_>,
585 unit: &Unit,
586 fresh: bool,
587) -> CargoResult<Work> {
588 let bcx = build_runner.bcx;
589 let outputs = build_runner.outputs(unit)?;
590 let export_dir = build_runner.files().export_dir();
591 let package_id = unit.pkg.package_id();
592 let manifest_path = PathBuf::from(unit.pkg.manifest_path());
593 let profile = unit.profile.clone();
594 let unit_mode = unit.mode;
595 let features = unit.features.iter().map(|s| s.to_string()).collect();
596 let json_messages = bcx.build_config.emit_json();
597 let executable = build_runner.get_executable(unit)?;
598 let mut target = Target::clone(&unit.target);
599 if let TargetSourcePath::Metabuild = target.src_path() {
600 let path = unit
602 .pkg
603 .manifest()
604 .metabuild_path(build_runner.bcx.ws.build_dir());
605 target.set_src_path(TargetSourcePath::Path(path));
606 }
607
608 Ok(Work::new(move |state| {
609 let mut destinations = vec![];
614 for output in outputs.iter() {
615 let src = &output.path;
616 if !src.exists() {
619 continue;
620 }
621 let Some(dst) = output.hardlink.as_ref() else {
622 destinations.push(src.clone());
623 continue;
624 };
625 destinations.push(dst.clone());
626 paths::link_or_copy(src, dst)?;
627 if let Some(ref path) = output.export_path {
628 let export_dir = export_dir.as_ref().unwrap();
629 paths::create_dir_all(export_dir)?;
630
631 paths::link_or_copy(src, path)?;
632 }
633 }
634
635 if json_messages {
636 let debuginfo = match profile.debuginfo.into_inner() {
637 TomlDebugInfo::None => machine_message::ArtifactDebuginfo::Int(0),
638 TomlDebugInfo::Limited => machine_message::ArtifactDebuginfo::Int(1),
639 TomlDebugInfo::Full => machine_message::ArtifactDebuginfo::Int(2),
640 TomlDebugInfo::LineDirectivesOnly => {
641 machine_message::ArtifactDebuginfo::Named("line-directives-only")
642 }
643 TomlDebugInfo::LineTablesOnly => {
644 machine_message::ArtifactDebuginfo::Named("line-tables-only")
645 }
646 };
647 let art_profile = machine_message::ArtifactProfile {
648 opt_level: profile.opt_level.as_str(),
649 debuginfo: Some(debuginfo),
650 debug_assertions: profile.debug_assertions,
651 overflow_checks: profile.overflow_checks,
652 test: unit_mode.is_any_test(),
653 };
654
655 let msg = machine_message::Artifact {
656 package_id: package_id.to_spec(),
657 manifest_path,
658 target: &target,
659 profile: art_profile,
660 features,
661 filenames: destinations,
662 executable,
663 fresh,
664 }
665 .to_json_string();
666 state.stdout(msg)?;
667 }
668 Ok(())
669 }))
670}
671
672fn add_plugin_deps(
676 rustc: &mut ProcessBuilder,
677 build_script_outputs: &BuildScriptOutputs,
678 build_scripts: &BuildScripts,
679 root_output: &Path,
680) -> CargoResult<()> {
681 let var = paths::dylib_path_envvar();
682 let search_path = rustc.get_env(var).unwrap_or_default();
683 let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>();
684 for (pkg_id, metadata) in &build_scripts.plugins {
685 let output = build_script_outputs
686 .get(*metadata)
687 .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?;
688 search_path.append(&mut filter_dynamic_search_path(
689 output.library_paths.iter().map(AsRef::as_ref),
690 root_output,
691 ));
692 }
693 let search_path = paths::join_paths(&search_path, var)?;
694 rustc.env(var, &search_path);
695 Ok(())
696}
697
698fn get_dynamic_search_path(path: &Path) -> &Path {
699 match path.to_str().and_then(|s| s.split_once("=")) {
700 Some(("native" | "crate" | "dependency" | "framework" | "all", path)) => Path::new(path),
701 _ => path,
702 }
703}
704
705fn filter_dynamic_search_path<'a, I>(paths: I, root_output: &Path) -> Vec<PathBuf>
711where
712 I: Iterator<Item = &'a PathBuf>,
713{
714 let mut search_path = vec![];
715 for dir in paths {
716 let dir = get_dynamic_search_path(dir);
717 if dir.starts_with(&root_output) {
718 search_path.push(dir.to_path_buf());
719 } else {
720 debug!(
721 "Not including path {} in runtime library search path because it is \
722 outside target root {}",
723 dir.display(),
724 root_output.display()
725 );
726 }
727 }
728 search_path
729}
730
731fn prepare_rustc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
738 let gctx = build_runner.bcx.gctx;
739 let is_primary = build_runner.is_primary_package(unit);
740 let is_workspace = build_runner.bcx.ws.is_member(&unit.pkg);
741
742 let mut base = build_runner
743 .compilation
744 .rustc_process(unit, is_primary, is_workspace)?;
745 build_base_args(build_runner, &mut base, unit)?;
746 if unit.pkg.manifest().is_embedded() {
747 if !gctx.cli_unstable().script {
748 anyhow::bail!(
749 "parsing `{}` requires `-Zscript`",
750 unit.pkg.manifest_path().display()
751 );
752 }
753 base.arg("-Z").arg("crate-attr=feature(frontmatter)");
754 }
755
756 base.inherit_jobserver(&build_runner.jobserver);
757 build_deps_args(&mut base, build_runner, unit)?;
758 add_cap_lints(build_runner.bcx, unit, &mut base);
759 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
760 base.args(args);
761 }
762 base.args(&unit.rustflags);
763 if gctx.cli_unstable().binary_dep_depinfo {
764 base.arg("-Z").arg("binary-dep-depinfo");
765 }
766 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
767 base.arg("-Z").arg("checksum-hash-algorithm=blake3");
768 }
769
770 if is_primary {
771 base.env("CARGO_PRIMARY_PACKAGE", "1");
772 let file_list = std::env::join_paths(build_runner.sbom_output_files(unit)?)?;
773 base.env("CARGO_SBOM_PATH", file_list);
774 }
775
776 if unit.target.is_test() || unit.target.is_bench() {
777 let tmp = build_runner.files().layout(unit.kind).prepare_tmp()?;
778 base.env("CARGO_TARGET_TMPDIR", tmp.display().to_string());
779 }
780
781 Ok(base)
782}
783
784fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
791 let bcx = build_runner.bcx;
792 let mut rustdoc = build_runner.compilation.rustdoc_process(unit, None)?;
794 if unit.pkg.manifest().is_embedded() {
795 if !bcx.gctx.cli_unstable().script {
796 anyhow::bail!(
797 "parsing `{}` requires `-Zscript`",
798 unit.pkg.manifest_path().display()
799 );
800 }
801 rustdoc.arg("-Z").arg("crate-attr=feature(frontmatter)");
802 }
803 rustdoc.inherit_jobserver(&build_runner.jobserver);
804 let crate_name = unit.target.crate_name();
805 rustdoc.arg("--crate-name").arg(&crate_name);
806 add_path_args(bcx.ws, unit, &mut rustdoc);
807 add_cap_lints(bcx, unit, &mut rustdoc);
808
809 if let CompileKind::Target(target) = unit.kind {
810 rustdoc.arg("--target").arg(target.rustc_target());
811 }
812 let doc_dir = build_runner.files().out_dir(unit);
813 rustdoc.arg("-o").arg(&doc_dir);
814 rustdoc.args(&features_args(unit));
815 rustdoc.args(&check_cfg_args(unit));
816
817 add_error_format_and_color(build_runner, &mut rustdoc);
818 add_allow_features(build_runner, &mut rustdoc);
819
820 if build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo {
821 let mut arg =
824 OsString::from("--emit=toolchain-shared-resources,invocation-specific,dep-info=");
825 arg.push(rustdoc_dep_info_loc(build_runner, unit));
826 rustdoc.arg(arg);
827
828 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
829 rustdoc.arg("-Z").arg("checksum-hash-algorithm=blake3");
830 }
831
832 rustdoc.arg("-Zunstable-options");
833 }
834
835 if let Some(trim_paths) = unit.profile.trim_paths.as_ref() {
836 trim_paths_args_rustdoc(&mut rustdoc, build_runner, unit, trim_paths)?;
837 }
838
839 rustdoc.args(unit.pkg.manifest().lint_rustflags());
840
841 let metadata = build_runner.metadata_for_doc_units[unit];
842 rustdoc
843 .arg("-C")
844 .arg(format!("metadata={}", metadata.c_metadata()));
845
846 if unit.mode.is_doc_scrape() {
847 debug_assert!(build_runner.bcx.scrape_units.contains(unit));
848
849 if unit.target.is_test() {
850 rustdoc.arg("--scrape-tests");
851 }
852
853 rustdoc.arg("-Zunstable-options");
854
855 rustdoc
856 .arg("--scrape-examples-output-path")
857 .arg(scrape_output_path(build_runner, unit)?);
858
859 for pkg in build_runner.bcx.packages.packages() {
861 let names = pkg
862 .targets()
863 .iter()
864 .map(|target| target.crate_name())
865 .collect::<HashSet<_>>();
866 for name in names {
867 rustdoc.arg("--scrape-examples-target-crate").arg(name);
868 }
869 }
870 }
871
872 if should_include_scrape_units(build_runner.bcx, unit) {
873 rustdoc.arg("-Zunstable-options");
874 }
875
876 build_deps_args(&mut rustdoc, build_runner, unit)?;
877 rustdoc::add_root_urls(build_runner, unit, &mut rustdoc)?;
878
879 rustdoc::add_output_format(build_runner, &mut rustdoc)?;
880
881 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
882 rustdoc.args(args);
883 }
884 rustdoc.args(&unit.rustdocflags);
885
886 if !crate_version_flag_already_present(&rustdoc) {
887 append_crate_version_flag(unit, &mut rustdoc);
888 }
889
890 Ok(rustdoc)
891}
892
893fn rustdoc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Work> {
895 let mut rustdoc = prepare_rustdoc(build_runner, unit)?;
896
897 let crate_name = unit.target.crate_name();
898 let doc_dir = build_runner.files().out_dir(unit);
899 paths::create_dir_all(&doc_dir)?;
903
904 let target_desc = unit.target.description_named();
905 let name = unit.pkg.name();
906 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
907 let package_id = unit.pkg.package_id();
908 let manifest_path = PathBuf::from(unit.pkg.manifest_path());
909 let target = Target::clone(&unit.target);
910
911 let rustdoc_dep_info_loc = rustdoc_dep_info_loc(build_runner, unit);
912 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
913 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
914 let pkg_root = unit.pkg.root().to_path_buf();
915 let cwd = rustdoc
916 .get_cwd()
917 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
918 .to_path_buf();
919 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
920 let is_local = unit.is_local();
921 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
922 let rustdoc_depinfo_enabled = build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo;
923
924 let mut output_options = OutputOptions::new(build_runner, unit);
925 let script_metadatas = build_runner.find_build_script_metadatas(unit);
926 let scrape_outputs = if should_include_scrape_units(build_runner.bcx, unit) {
927 Some(
928 build_runner
929 .bcx
930 .scrape_units
931 .iter()
932 .map(|unit| {
933 Ok((
934 build_runner.files().metadata(unit).unit_id(),
935 scrape_output_path(build_runner, unit)?,
936 ))
937 })
938 .collect::<CargoResult<HashMap<_, _>>>()?,
939 )
940 } else {
941 None
942 };
943
944 let failed_scrape_units = Arc::clone(&build_runner.failed_scrape_units);
945 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
946 && !matches!(
947 build_runner.bcx.gctx.shell().verbosity(),
948 Verbosity::Verbose
949 );
950 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
951 make_failed_scrape_diagnostic(
952 build_runner,
953 unit,
954 format_args!("failed to scan {target_desc} in package `{name}` for example code usage"),
955 )
956 });
957 if hide_diagnostics_for_scrape_unit {
958 output_options.show_diagnostics = false;
959 }
960
961 Ok(Work::new(move |state| {
962 add_custom_flags(
963 &mut rustdoc,
964 &build_script_outputs.lock().unwrap(),
965 script_metadatas,
966 )?;
967
968 if let Some(scrape_outputs) = scrape_outputs {
973 let failed_scrape_units = failed_scrape_units.lock().unwrap();
974 for (metadata, output_path) in &scrape_outputs {
975 if !failed_scrape_units.contains(metadata) {
976 rustdoc.arg("--with-examples").arg(output_path);
977 }
978 }
979 }
980
981 let crate_dir = doc_dir.join(&crate_name);
982 if crate_dir.exists() {
983 debug!("removing pre-existing doc directory {:?}", crate_dir);
986 paths::remove_dir_all(crate_dir)?;
987 }
988 state.running(&rustdoc);
989 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
990
991 let result = rustdoc
992 .exec_with_streaming(
993 &mut |line| on_stdout_line(state, line, package_id, &target),
994 &mut |line| {
995 on_stderr_line(
996 state,
997 line,
998 package_id,
999 &manifest_path,
1000 &target,
1001 &mut output_options,
1002 )
1003 },
1004 false,
1005 )
1006 .map_err(verbose_if_simple_exit_code)
1007 .with_context(|| format!("could not document `{}`", name));
1008
1009 if let Err(e) = result {
1010 if let Some(diagnostic) = failed_scrape_diagnostic {
1011 state.warning(diagnostic);
1012 }
1013
1014 return Err(e);
1015 }
1016
1017 if rustdoc_depinfo_enabled && rustdoc_dep_info_loc.exists() {
1018 fingerprint::translate_dep_info(
1019 &rustdoc_dep_info_loc,
1020 &dep_info_loc,
1021 &cwd,
1022 &pkg_root,
1023 &build_dir,
1024 &rustdoc,
1025 is_local,
1027 &env_config,
1028 )
1029 .with_context(|| {
1030 internal(format_args!(
1031 "could not parse/generate dep info at: {}",
1032 rustdoc_dep_info_loc.display()
1033 ))
1034 })?;
1035 paths::set_file_time_no_err(dep_info_loc, timestamp);
1038 }
1039
1040 Ok(())
1041 }))
1042}
1043
1044fn crate_version_flag_already_present(rustdoc: &ProcessBuilder) -> bool {
1047 rustdoc.get_args().any(|flag| {
1048 flag.to_str()
1049 .map_or(false, |flag| flag.starts_with(RUSTDOC_CRATE_VERSION_FLAG))
1050 })
1051}
1052
1053fn append_crate_version_flag(unit: &Unit, rustdoc: &mut ProcessBuilder) {
1054 rustdoc
1055 .arg(RUSTDOC_CRATE_VERSION_FLAG)
1056 .arg(unit.pkg.version().to_string());
1057}
1058
1059fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuilder) {
1063 if !unit.show_warnings(bcx.gctx) {
1066 cmd.arg("--cap-lints").arg("allow");
1067
1068 } else if !unit.is_local() {
1071 cmd.arg("--cap-lints").arg("warn");
1072 }
1073}
1074
1075fn add_allow_features(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1079 if let Some(allow) = &build_runner.bcx.gctx.cli_unstable().allow_features {
1080 use std::fmt::Write;
1081 let mut arg = String::from("-Zallow-features=");
1082 for f in allow {
1083 let _ = write!(&mut arg, "{f},");
1084 }
1085 cmd.arg(arg.trim_end_matches(','));
1086 }
1087}
1088
1089fn add_error_format_and_color(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1100 let enable_timings = build_runner.bcx.gctx.cli_unstable().section_timings
1101 && !build_runner.bcx.build_config.timing_outputs.is_empty();
1102 if enable_timings {
1103 cmd.arg("-Zunstable-options");
1104 }
1105
1106 cmd.arg("--error-format=json");
1107 let mut json = String::from("--json=diagnostic-rendered-ansi,artifacts,future-incompat");
1108
1109 match build_runner.bcx.build_config.message_format {
1110 MessageFormat::Short | MessageFormat::Json { short: true, .. } => {
1111 json.push_str(",diagnostic-short");
1112 }
1113 _ => {}
1114 }
1115
1116 if enable_timings {
1117 json.push_str(",timings");
1118 }
1119
1120 cmd.arg(json);
1121
1122 let gctx = build_runner.bcx.gctx;
1123 if let Some(width) = gctx.shell().err_width().diagnostic_terminal_width() {
1124 cmd.arg(format!("--diagnostic-width={width}"));
1125 }
1126}
1127
1128fn build_base_args(
1130 build_runner: &BuildRunner<'_, '_>,
1131 cmd: &mut ProcessBuilder,
1132 unit: &Unit,
1133) -> CargoResult<()> {
1134 assert!(!unit.mode.is_run_custom_build());
1135
1136 let bcx = build_runner.bcx;
1137 let Profile {
1138 ref opt_level,
1139 codegen_backend,
1140 codegen_units,
1141 debuginfo,
1142 debug_assertions,
1143 split_debuginfo,
1144 overflow_checks,
1145 rpath,
1146 ref panic,
1147 incremental,
1148 strip,
1149 rustflags: profile_rustflags,
1150 trim_paths,
1151 hint_mostly_unused: profile_hint_mostly_unused,
1152 ..
1153 } = unit.profile.clone();
1154 let hints = unit.pkg.hints().cloned().unwrap_or_default();
1155 let test = unit.mode.is_any_test();
1156
1157 let warn = |msg: &str| {
1158 bcx.gctx.shell().warn(format!(
1159 "{}@{}: {msg}",
1160 unit.pkg.package_id().name(),
1161 unit.pkg.package_id().version()
1162 ))
1163 };
1164 let unit_capped_warn = |msg: &str| {
1165 if unit.show_warnings(bcx.gctx) {
1166 warn(msg)
1167 } else {
1168 Ok(())
1169 }
1170 };
1171
1172 cmd.arg("--crate-name").arg(&unit.target.crate_name());
1173
1174 let edition = unit.target.edition();
1175 edition.cmd_edition_arg(cmd);
1176
1177 add_path_args(bcx.ws, unit, cmd);
1178 add_error_format_and_color(build_runner, cmd);
1179 add_allow_features(build_runner, cmd);
1180
1181 let mut contains_dy_lib = false;
1182 if !test {
1183 for crate_type in &unit.target.rustc_crate_types() {
1184 cmd.arg("--crate-type").arg(crate_type.as_str());
1185 contains_dy_lib |= crate_type == &CrateType::Dylib;
1186 }
1187 }
1188
1189 if unit.mode.is_check() {
1190 cmd.arg("--emit=dep-info,metadata");
1191 } else if build_runner.bcx.gctx.cli_unstable().no_embed_metadata {
1192 if unit.benefits_from_no_embed_metadata() {
1202 cmd.arg("--emit=dep-info,metadata,link");
1203 cmd.args(&["-Z", "embed-metadata=no"]);
1204 } else {
1205 cmd.arg("--emit=dep-info,link");
1206 }
1207 } else {
1208 if !unit.requires_upstream_objects() {
1212 cmd.arg("--emit=dep-info,metadata,link");
1213 } else {
1214 cmd.arg("--emit=dep-info,link");
1215 }
1216 }
1217
1218 let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
1219 || (contains_dy_lib && !build_runner.is_primary_package(unit));
1220 if prefer_dynamic {
1221 cmd.arg("-C").arg("prefer-dynamic");
1222 }
1223
1224 if opt_level.as_str() != "0" {
1225 cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
1226 }
1227
1228 if *panic != PanicStrategy::Unwind {
1229 cmd.arg("-C").arg(format!("panic={}", panic));
1230 }
1231
1232 cmd.args(<o_args(build_runner, unit));
1233
1234 if let Some(backend) = codegen_backend {
1235 cmd.arg("-Z").arg(&format!("codegen-backend={}", backend));
1236 }
1237
1238 if let Some(n) = codegen_units {
1239 cmd.arg("-C").arg(&format!("codegen-units={}", n));
1240 }
1241
1242 let debuginfo = debuginfo.into_inner();
1243 if debuginfo != TomlDebugInfo::None {
1245 cmd.arg("-C").arg(format!("debuginfo={debuginfo}"));
1246 if let Some(split) = split_debuginfo {
1253 if build_runner
1254 .bcx
1255 .target_data
1256 .info(unit.kind)
1257 .supports_debuginfo_split(split)
1258 {
1259 cmd.arg("-C").arg(format!("split-debuginfo={split}"));
1260 }
1261 }
1262 }
1263
1264 if let Some(trim_paths) = trim_paths {
1265 trim_paths_args(cmd, build_runner, unit, &trim_paths)?;
1266 }
1267
1268 cmd.args(unit.pkg.manifest().lint_rustflags());
1269 cmd.args(&profile_rustflags);
1270
1271 if opt_level.as_str() != "0" {
1275 if debug_assertions {
1276 cmd.args(&["-C", "debug-assertions=on"]);
1277 if !overflow_checks {
1278 cmd.args(&["-C", "overflow-checks=off"]);
1279 }
1280 } else if overflow_checks {
1281 cmd.args(&["-C", "overflow-checks=on"]);
1282 }
1283 } else if !debug_assertions {
1284 cmd.args(&["-C", "debug-assertions=off"]);
1285 if overflow_checks {
1286 cmd.args(&["-C", "overflow-checks=on"]);
1287 }
1288 } else if !overflow_checks {
1289 cmd.args(&["-C", "overflow-checks=off"]);
1290 }
1291
1292 if test && unit.target.harness() {
1293 cmd.arg("--test");
1294
1295 if *panic == PanicStrategy::Abort {
1303 cmd.arg("-Z").arg("panic-abort-tests");
1304 }
1305 } else if test {
1306 cmd.arg("--cfg").arg("test");
1307 }
1308
1309 cmd.args(&features_args(unit));
1310 cmd.args(&check_cfg_args(unit));
1311
1312 let meta = build_runner.files().metadata(unit);
1313 cmd.arg("-C")
1314 .arg(&format!("metadata={}", meta.c_metadata()));
1315 if let Some(c_extra_filename) = meta.c_extra_filename() {
1316 cmd.arg("-C")
1317 .arg(&format!("extra-filename=-{c_extra_filename}"));
1318 }
1319
1320 if rpath {
1321 cmd.arg("-C").arg("rpath");
1322 }
1323
1324 cmd.arg("--out-dir")
1325 .arg(&build_runner.files().out_dir(unit));
1326
1327 fn opt(cmd: &mut ProcessBuilder, key: &str, prefix: &str, val: Option<&OsStr>) {
1328 if let Some(val) = val {
1329 let mut joined = OsString::from(prefix);
1330 joined.push(val);
1331 cmd.arg(key).arg(joined);
1332 }
1333 }
1334
1335 if let CompileKind::Target(n) = unit.kind {
1336 cmd.arg("--target").arg(n.rustc_target());
1337 }
1338
1339 opt(
1340 cmd,
1341 "-C",
1342 "linker=",
1343 build_runner
1344 .compilation
1345 .target_linker(unit.kind)
1346 .as_ref()
1347 .map(|s| s.as_ref()),
1348 );
1349 if incremental {
1350 let dir = build_runner
1351 .files()
1352 .layout(unit.kind)
1353 .incremental()
1354 .as_os_str();
1355 opt(cmd, "-C", "incremental=", Some(dir));
1356 }
1357
1358 let pkg_hint_mostly_unused = match hints.mostly_unused {
1359 None => None,
1360 Some(toml::Value::Boolean(b)) => Some(b),
1361 Some(v) => {
1362 unit_capped_warn(&format!(
1363 "ignoring unsupported value type ({}) for 'hints.mostly-unused', which expects a boolean",
1364 v.type_str()
1365 ))?;
1366 None
1367 }
1368 };
1369 if profile_hint_mostly_unused
1370 .or(pkg_hint_mostly_unused)
1371 .unwrap_or(false)
1372 {
1373 if bcx.gctx.cli_unstable().profile_hint_mostly_unused {
1374 cmd.arg("-Zhint-mostly-unused");
1375 } else {
1376 if profile_hint_mostly_unused.is_some() {
1377 warn(
1379 "ignoring 'hint-mostly-unused' profile option, pass `-Zprofile-hint-mostly-unused` to enable it",
1380 )?;
1381 } else if pkg_hint_mostly_unused.is_some() {
1382 unit_capped_warn(
1383 "ignoring 'hints.mostly-unused', pass `-Zprofile-hint-mostly-unused` to enable it",
1384 )?;
1385 }
1386 }
1387 }
1388
1389 let strip = strip.into_inner();
1390 if strip != StripInner::None {
1391 cmd.arg("-C").arg(format!("strip={}", strip));
1392 }
1393
1394 if unit.is_std {
1395 cmd.arg("-Z")
1401 .arg("force-unstable-if-unmarked")
1402 .env("RUSTC_BOOTSTRAP", "1");
1403 }
1404
1405 if unit.target.is_test() || unit.target.is_bench() {
1407 for bin_target in unit
1408 .pkg
1409 .manifest()
1410 .targets()
1411 .iter()
1412 .filter(|target| target.is_bin())
1413 {
1414 let exe_path = build_runner.files().bin_link_for_target(
1415 bin_target,
1416 unit.kind,
1417 build_runner.bcx,
1418 )?;
1419 let name = bin_target
1420 .binary_filename()
1421 .unwrap_or(bin_target.name().to_string());
1422 let key = format!("CARGO_BIN_EXE_{}", name);
1423 cmd.env(&key, exe_path);
1424 }
1425 }
1426 Ok(())
1427}
1428
1429fn features_args(unit: &Unit) -> Vec<OsString> {
1431 let mut args = Vec::with_capacity(unit.features.len() * 2);
1432
1433 for feat in &unit.features {
1434 args.push(OsString::from("--cfg"));
1435 args.push(OsString::from(format!("feature=\"{}\"", feat)));
1436 }
1437
1438 args
1439}
1440
1441fn trim_paths_args_rustdoc(
1443 cmd: &mut ProcessBuilder,
1444 build_runner: &BuildRunner<'_, '_>,
1445 unit: &Unit,
1446 trim_paths: &TomlTrimPaths,
1447) -> CargoResult<()> {
1448 match trim_paths {
1449 TomlTrimPaths::Values(values) if !values.contains(&TomlTrimPathsValue::Diagnostics) => {
1451 return Ok(());
1452 }
1453 _ => {}
1454 }
1455
1456 cmd.arg("-Zunstable-options");
1458
1459 cmd.arg(package_remap(build_runner, unit));
1462 cmd.arg(build_dir_remap(build_runner));
1463 cmd.arg(sysroot_remap(build_runner, unit));
1464
1465 Ok(())
1466}
1467
1468fn trim_paths_args(
1474 cmd: &mut ProcessBuilder,
1475 build_runner: &BuildRunner<'_, '_>,
1476 unit: &Unit,
1477 trim_paths: &TomlTrimPaths,
1478) -> CargoResult<()> {
1479 if trim_paths.is_none() {
1480 return Ok(());
1481 }
1482
1483 cmd.arg("-Zunstable-options");
1485 cmd.arg(format!("-Zremap-path-scope={trim_paths}"));
1486
1487 cmd.arg(package_remap(build_runner, unit));
1490 cmd.arg(build_dir_remap(build_runner));
1491 cmd.arg(sysroot_remap(build_runner, unit));
1492
1493 Ok(())
1494}
1495
1496fn sysroot_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1501 let mut remap = OsString::from("--remap-path-prefix=");
1502 remap.push({
1503 let mut sysroot = build_runner.bcx.target_data.info(unit.kind).sysroot.clone();
1505 sysroot.push("lib");
1506 sysroot.push("rustlib");
1507 sysroot.push("src");
1508 sysroot.push("rust");
1509 sysroot
1510 });
1511 remap.push("=");
1512 remap.push("/rustc/");
1513 if let Some(commit_hash) = build_runner.bcx.rustc().commit_hash.as_ref() {
1514 remap.push(commit_hash);
1515 } else {
1516 remap.push(build_runner.bcx.rustc().version.to_string());
1517 }
1518 remap
1519}
1520
1521fn package_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1529 let pkg_root = unit.pkg.root();
1530 let ws_root = build_runner.bcx.ws.root();
1531 let mut remap = OsString::from("--remap-path-prefix=");
1532 let source_id = unit.pkg.package_id().source_id();
1533 if source_id.is_git() {
1534 remap.push(
1535 build_runner
1536 .bcx
1537 .gctx
1538 .git_checkouts_path()
1539 .as_path_unlocked(),
1540 );
1541 remap.push("=");
1542 } else if source_id.is_registry() {
1543 remap.push(
1544 build_runner
1545 .bcx
1546 .gctx
1547 .registry_source_path()
1548 .as_path_unlocked(),
1549 );
1550 remap.push("=");
1551 } else if pkg_root.strip_prefix(ws_root).is_ok() {
1552 remap.push(ws_root);
1553 remap.push("=."); } else {
1555 remap.push(pkg_root);
1556 remap.push("=");
1557 remap.push(unit.pkg.name());
1558 remap.push("-");
1559 remap.push(unit.pkg.version().to_string());
1560 }
1561 remap
1562}
1563
1564fn build_dir_remap(build_runner: &BuildRunner<'_, '_>) -> OsString {
1577 let build_dir = build_runner.bcx.ws.build_dir();
1578 let mut remap = OsString::from("--remap-path-prefix=");
1579 remap.push(build_dir.as_path_unlocked());
1580 remap.push("=/cargo/build-dir");
1581 remap
1582}
1583
1584fn check_cfg_args(unit: &Unit) -> Vec<OsString> {
1586 let gross_cap_estimation = unit.pkg.summary().features().len() * 7 + 25;
1604 let mut arg_feature = OsString::with_capacity(gross_cap_estimation);
1605
1606 arg_feature.push("cfg(feature, values(");
1607 for (i, feature) in unit.pkg.summary().features().keys().enumerate() {
1608 if i != 0 {
1609 arg_feature.push(", ");
1610 }
1611 arg_feature.push("\"");
1612 arg_feature.push(feature);
1613 arg_feature.push("\"");
1614 }
1615 arg_feature.push("))");
1616
1617 vec![
1626 OsString::from("--check-cfg"),
1627 OsString::from("cfg(docsrs,test)"),
1628 OsString::from("--check-cfg"),
1629 arg_feature,
1630 ]
1631}
1632
1633fn lto_args(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> Vec<OsString> {
1635 let mut result = Vec::new();
1636 let mut push = |arg: &str| {
1637 result.push(OsString::from("-C"));
1638 result.push(OsString::from(arg));
1639 };
1640 match build_runner.lto[unit] {
1641 lto::Lto::Run(None) => push("lto"),
1642 lto::Lto::Run(Some(s)) => push(&format!("lto={}", s)),
1643 lto::Lto::Off => {
1644 push("lto=off");
1645 push("embed-bitcode=no");
1646 }
1647 lto::Lto::ObjectAndBitcode => {} lto::Lto::OnlyBitcode => push("linker-plugin-lto"),
1649 lto::Lto::OnlyObject => push("embed-bitcode=no"),
1650 }
1651 result
1652}
1653
1654fn build_deps_args(
1660 cmd: &mut ProcessBuilder,
1661 build_runner: &BuildRunner<'_, '_>,
1662 unit: &Unit,
1663) -> CargoResult<()> {
1664 let bcx = build_runner.bcx;
1665 cmd.arg("-L").arg(&{
1666 let mut deps = OsString::from("dependency=");
1667 deps.push(build_runner.files().deps_dir(unit));
1668 deps
1669 });
1670
1671 if !unit.kind.is_host() {
1674 cmd.arg("-L").arg(&{
1675 let mut deps = OsString::from("dependency=");
1676 deps.push(build_runner.files().host_deps());
1677 deps
1678 });
1679 }
1680
1681 let deps = build_runner.unit_deps(unit);
1682
1683 if !deps
1687 .iter()
1688 .any(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_linkable())
1689 {
1690 if let Some(dep) = deps.iter().find(|dep| {
1691 !dep.unit.mode.is_doc() && dep.unit.target.is_lib() && !dep.unit.artifact.is_true()
1692 }) {
1693 bcx.gctx.shell().warn(format!(
1694 "The package `{}` \
1695 provides no linkable target. The compiler might raise an error while compiling \
1696 `{}`. Consider adding 'dylib' or 'rlib' to key `crate-type` in `{}`'s \
1697 Cargo.toml. This warning might turn into a hard error in the future.",
1698 dep.unit.target.crate_name(),
1699 unit.target.crate_name(),
1700 dep.unit.target.crate_name()
1701 ))?;
1702 }
1703 }
1704
1705 let mut unstable_opts = false;
1706
1707 for dep in deps {
1708 if dep.unit.mode.is_run_custom_build() {
1709 cmd.env(
1710 "OUT_DIR",
1711 &build_runner.files().build_script_out_dir(&dep.unit),
1712 );
1713 }
1714 }
1715
1716 for arg in extern_args(build_runner, unit, &mut unstable_opts)? {
1717 cmd.arg(arg);
1718 }
1719
1720 for (var, env) in artifact::get_env(build_runner, deps)? {
1721 cmd.env(&var, env);
1722 }
1723
1724 if unstable_opts {
1727 cmd.arg("-Z").arg("unstable-options");
1728 }
1729
1730 Ok(())
1731}
1732
1733fn add_custom_flags(
1737 cmd: &mut ProcessBuilder,
1738 build_script_outputs: &BuildScriptOutputs,
1739 metadata_vec: Option<Vec<UnitHash>>,
1740) -> CargoResult<()> {
1741 if let Some(metadata_vec) = metadata_vec {
1742 for metadata in metadata_vec {
1743 if let Some(output) = build_script_outputs.get(metadata) {
1744 for cfg in output.cfgs.iter() {
1745 cmd.arg("--cfg").arg(cfg);
1746 }
1747 for check_cfg in &output.check_cfgs {
1748 cmd.arg("--check-cfg").arg(check_cfg);
1749 }
1750 for (name, value) in output.env.iter() {
1751 cmd.env(name, value);
1752 }
1753 }
1754 }
1755 }
1756
1757 Ok(())
1758}
1759
1760pub fn extern_args(
1762 build_runner: &BuildRunner<'_, '_>,
1763 unit: &Unit,
1764 unstable_opts: &mut bool,
1765) -> CargoResult<Vec<OsString>> {
1766 let mut result = Vec::new();
1767 let deps = build_runner.unit_deps(unit);
1768
1769 let no_embed_metadata = build_runner.bcx.gctx.cli_unstable().no_embed_metadata;
1770
1771 let mut link_to =
1773 |dep: &UnitDep, extern_crate_name: InternedString, noprelude: bool| -> CargoResult<()> {
1774 let mut value = OsString::new();
1775 let mut opts = Vec::new();
1776 let is_public_dependency_enabled = unit
1777 .pkg
1778 .manifest()
1779 .unstable_features()
1780 .require(Feature::public_dependency())
1781 .is_ok()
1782 || build_runner.bcx.gctx.cli_unstable().public_dependency;
1783 if !dep.public && unit.target.is_lib() && is_public_dependency_enabled {
1784 opts.push("priv");
1785 *unstable_opts = true;
1786 }
1787 if noprelude {
1788 opts.push("noprelude");
1789 *unstable_opts = true;
1790 }
1791 if !opts.is_empty() {
1792 value.push(opts.join(","));
1793 value.push(":");
1794 }
1795 value.push(extern_crate_name.as_str());
1796 value.push("=");
1797
1798 let mut pass = |file| {
1799 let mut value = value.clone();
1800 value.push(file);
1801 result.push(OsString::from("--extern"));
1802 result.push(value);
1803 };
1804
1805 let outputs = build_runner.outputs(&dep.unit)?;
1806
1807 if build_runner.only_requires_rmeta(unit, &dep.unit) || dep.unit.mode.is_check() {
1808 let output = outputs
1810 .iter()
1811 .find(|output| output.flavor == FileFlavor::Rmeta)
1812 .expect("failed to find rmeta dep for pipelined dep");
1813 pass(&output.path);
1814 } else {
1815 for output in outputs.iter() {
1817 if output.flavor == FileFlavor::Linkable {
1818 pass(&output.path);
1819 }
1820 else if no_embed_metadata && output.flavor == FileFlavor::Rmeta {
1824 pass(&output.path);
1825 }
1826 }
1827 }
1828 Ok(())
1829 };
1830
1831 for dep in deps {
1832 if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
1833 link_to(dep, dep.extern_crate_name, dep.noprelude)?;
1834 }
1835 }
1836 if unit.target.proc_macro() {
1837 result.push(OsString::from("--extern"));
1839 result.push(OsString::from("proc_macro"));
1840 }
1841
1842 Ok(result)
1843}
1844
1845fn envify(s: &str) -> String {
1846 s.chars()
1847 .flat_map(|c| c.to_uppercase())
1848 .map(|c| if c == '-' { '_' } else { c })
1849 .collect()
1850}
1851
1852struct OutputOptions {
1855 format: MessageFormat,
1857 cache_cell: Option<(PathBuf, LazyCell<File>)>,
1862 show_diagnostics: bool,
1870 warnings_seen: usize,
1872 errors_seen: usize,
1874}
1875
1876impl OutputOptions {
1877 fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions {
1878 let path = build_runner.files().message_cache_path(unit);
1879 drop(fs::remove_file(&path));
1881 let cache_cell = Some((path, LazyCell::new()));
1882 let show_diagnostics =
1883 build_runner.bcx.gctx.warning_handling().unwrap_or_default() != WarningHandling::Allow;
1884 OutputOptions {
1885 format: build_runner.bcx.build_config.message_format,
1886 cache_cell,
1887 show_diagnostics,
1888 warnings_seen: 0,
1889 errors_seen: 0,
1890 }
1891 }
1892}
1893
1894fn on_stdout_line(
1895 state: &JobState<'_, '_>,
1896 line: &str,
1897 _package_id: PackageId,
1898 _target: &Target,
1899) -> CargoResult<()> {
1900 state.stdout(line.to_string())?;
1901 Ok(())
1902}
1903
1904fn on_stderr_line(
1905 state: &JobState<'_, '_>,
1906 line: &str,
1907 package_id: PackageId,
1908 manifest_path: &std::path::Path,
1909 target: &Target,
1910 options: &mut OutputOptions,
1911) -> CargoResult<()> {
1912 if on_stderr_line_inner(state, line, package_id, manifest_path, target, options)? {
1913 if let Some((path, cell)) = &mut options.cache_cell {
1915 let f = cell.try_borrow_mut_with(|| paths::create(path))?;
1917 debug_assert!(!line.contains('\n'));
1918 f.write_all(line.as_bytes())?;
1919 f.write_all(&[b'\n'])?;
1920 }
1921 }
1922 Ok(())
1923}
1924
1925fn on_stderr_line_inner(
1927 state: &JobState<'_, '_>,
1928 line: &str,
1929 package_id: PackageId,
1930 manifest_path: &std::path::Path,
1931 target: &Target,
1932 options: &mut OutputOptions,
1933) -> CargoResult<bool> {
1934 if !line.starts_with('{') {
1940 state.stderr(line.to_string())?;
1941 return Ok(true);
1942 }
1943
1944 let mut compiler_message: Box<serde_json::value::RawValue> = match serde_json::from_str(line) {
1945 Ok(msg) => msg,
1946
1947 Err(e) => {
1951 debug!("failed to parse json: {:?}", e);
1952 state.stderr(line.to_string())?;
1953 return Ok(true);
1954 }
1955 };
1956
1957 let count_diagnostic = |level, options: &mut OutputOptions| {
1958 if level == "warning" {
1959 options.warnings_seen += 1;
1960 } else if level == "error" {
1961 options.errors_seen += 1;
1962 }
1963 };
1964
1965 if let Ok(report) = serde_json::from_str::<FutureIncompatReport>(compiler_message.get()) {
1966 for item in &report.future_incompat_report {
1967 count_diagnostic(&*item.diagnostic.level, options);
1968 }
1969 state.future_incompat_report(report.future_incompat_report);
1970 return Ok(true);
1971 }
1972
1973 let res = serde_json::from_str::<SectionTiming>(compiler_message.get());
1974 if let Ok(timing_record) = res {
1975 state.on_section_timing_emitted(timing_record);
1976 return Ok(false);
1977 }
1978
1979 match options.format {
1982 MessageFormat::Human
1987 | MessageFormat::Short
1988 | MessageFormat::Json {
1989 render_diagnostics: true,
1990 ..
1991 } => {
1992 #[derive(serde::Deserialize)]
1993 struct CompilerMessage<'a> {
1994 rendered: String,
1998 #[serde(borrow)]
1999 message: Cow<'a, str>,
2000 #[serde(borrow)]
2001 level: Cow<'a, str>,
2002 children: Vec<PartialDiagnostic>,
2003 }
2004
2005 #[derive(serde::Deserialize)]
2014 struct PartialDiagnostic {
2015 spans: Vec<PartialDiagnosticSpan>,
2016 }
2017
2018 #[derive(serde::Deserialize)]
2020 struct PartialDiagnosticSpan {
2021 suggestion_applicability: Option<Applicability>,
2022 }
2023
2024 if let Ok(mut msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2025 {
2026 if msg.message.starts_with("aborting due to")
2027 || msg.message.ends_with("warning emitted")
2028 || msg.message.ends_with("warnings emitted")
2029 {
2030 return Ok(true);
2032 }
2033 if msg.rendered.ends_with('\n') {
2035 msg.rendered.pop();
2036 }
2037 let rendered = msg.rendered;
2038 if options.show_diagnostics {
2039 let machine_applicable: bool = msg
2040 .children
2041 .iter()
2042 .map(|child| {
2043 child
2044 .spans
2045 .iter()
2046 .filter_map(|span| span.suggestion_applicability)
2047 .any(|app| app == Applicability::MachineApplicable)
2048 })
2049 .any(|b| b);
2050 count_diagnostic(&msg.level, options);
2051 state.emit_diag(&msg.level, rendered, machine_applicable)?;
2052 }
2053 return Ok(true);
2054 }
2055 }
2056
2057 MessageFormat::Json { ansi: false, .. } => {
2061 #[derive(serde::Deserialize, serde::Serialize)]
2062 struct CompilerMessage<'a> {
2063 rendered: String,
2064 #[serde(flatten, borrow)]
2065 other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2066 }
2067 if let Ok(mut error) =
2068 serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2069 {
2070 error.rendered = anstream::adapter::strip_str(&error.rendered).to_string();
2071 let new_line = serde_json::to_string(&error)?;
2072 compiler_message = serde_json::value::RawValue::from_string(new_line)?;
2073 }
2074 }
2075
2076 MessageFormat::Json { ansi: true, .. } => {}
2079 }
2080
2081 #[derive(serde::Deserialize)]
2088 struct ArtifactNotification<'a> {
2089 #[serde(borrow)]
2090 artifact: Cow<'a, str>,
2091 }
2092
2093 if let Ok(artifact) = serde_json::from_str::<ArtifactNotification<'_>>(compiler_message.get()) {
2094 trace!("found directive from rustc: `{}`", artifact.artifact);
2095 if artifact.artifact.ends_with(".rmeta") {
2096 debug!("looks like metadata finished early!");
2097 state.rmeta_produced();
2098 }
2099 return Ok(false);
2100 }
2101
2102 if !options.show_diagnostics {
2107 return Ok(true);
2108 }
2109
2110 #[derive(serde::Deserialize)]
2111 struct CompilerMessage<'a> {
2112 #[serde(borrow)]
2113 message: Cow<'a, str>,
2114 #[serde(borrow)]
2115 level: Cow<'a, str>,
2116 }
2117
2118 if let Ok(msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get()) {
2119 if msg.message.starts_with("aborting due to")
2120 || msg.message.ends_with("warning emitted")
2121 || msg.message.ends_with("warnings emitted")
2122 {
2123 return Ok(true);
2125 }
2126 count_diagnostic(&msg.level, options);
2127 }
2128
2129 let msg = machine_message::FromCompiler {
2130 package_id: package_id.to_spec(),
2131 manifest_path,
2132 target,
2133 message: compiler_message,
2134 }
2135 .to_json_string();
2136
2137 state.stdout(msg)?;
2141 Ok(true)
2142}
2143
2144fn replay_output_cache(
2148 package_id: PackageId,
2149 manifest_path: PathBuf,
2150 target: &Target,
2151 path: PathBuf,
2152 format: MessageFormat,
2153 show_diagnostics: bool,
2154) -> Work {
2155 let target = target.clone();
2156 let mut options = OutputOptions {
2157 format,
2158 cache_cell: None,
2159 show_diagnostics,
2160 warnings_seen: 0,
2161 errors_seen: 0,
2162 };
2163 Work::new(move |state| {
2164 if !path.exists() {
2165 return Ok(());
2167 }
2168 let file = paths::open(&path)?;
2172 let mut reader = std::io::BufReader::new(file);
2173 let mut line = String::new();
2174 loop {
2175 let length = reader.read_line(&mut line)?;
2176 if length == 0 {
2177 break;
2178 }
2179 let trimmed = line.trim_end_matches(&['\n', '\r'][..]);
2180 on_stderr_line(
2181 state,
2182 trimmed,
2183 package_id,
2184 &manifest_path,
2185 &target,
2186 &mut options,
2187 )?;
2188 line.clear();
2189 }
2190 Ok(())
2191 })
2192}
2193
2194fn descriptive_pkg_name(name: &str, target: &Target, mode: &CompileMode) -> String {
2197 let desc_name = target.description_named();
2198 let mode = if mode.is_rustc_test() && !(target.is_test() || target.is_bench()) {
2199 " test"
2200 } else if mode.is_doc_test() {
2201 " doctest"
2202 } else if mode.is_doc() {
2203 " doc"
2204 } else {
2205 ""
2206 };
2207 format!("`{name}` ({desc_name}{mode})")
2208}
2209
2210pub(crate) fn apply_env_config(
2212 gctx: &crate::GlobalContext,
2213 cmd: &mut ProcessBuilder,
2214) -> CargoResult<()> {
2215 for (key, value) in gctx.env_config()?.iter() {
2216 if cmd.get_envs().contains_key(key) {
2218 continue;
2219 }
2220 cmd.env(key, value);
2221 }
2222 Ok(())
2223}
2224
2225fn should_include_scrape_units(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
2227 unit.mode.is_doc() && bcx.scrape_units.len() > 0 && bcx.ws.unit_needs_doc_scrape(unit)
2228}
2229
2230fn scrape_output_path(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<PathBuf> {
2232 assert!(unit.mode.is_doc() || unit.mode.is_doc_scrape());
2233 build_runner
2234 .outputs(unit)
2235 .map(|outputs| outputs[0].path.clone())
2236}
2237
2238fn rustdoc_dep_info_loc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> PathBuf {
2240 let mut loc = build_runner.files().fingerprint_file_path(unit, "");
2241 loc.set_extension("d");
2242 loc
2243}