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;
93pub use crate::core::compiler::unit::{Unit, UnitInterner};
94use crate::core::manifest::TargetSourcePath;
95use crate::core::profiles::{PanicStrategy, Profile, StripInner};
96use crate::core::{Feature, PackageId, Target, Verbosity};
97use crate::util::context::WarningHandling;
98use crate::util::errors::{CargoResult, VerboseError};
99use crate::util::interning::InternedString;
100use crate::util::machine_message::{self, Message};
101use crate::util::{add_path_args, internal};
102use cargo_util::{paths, ProcessBuilder, ProcessError};
103use cargo_util_schemas::manifest::TomlDebugInfo;
104use cargo_util_schemas::manifest::TomlTrimPaths;
105use cargo_util_schemas::manifest::TomlTrimPathsValue;
106use rustfix::diagnostics::Applicability;
107
108const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version";
109
110pub trait Executor: Send + Sync + 'static {
114 fn init(&self, _build_runner: &BuildRunner<'_, '_>, _unit: &Unit) {}
118
119 fn exec(
122 &self,
123 cmd: &ProcessBuilder,
124 id: PackageId,
125 target: &Target,
126 mode: CompileMode,
127 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
128 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
129 ) -> CargoResult<()>;
130
131 fn force_rebuild(&self, _unit: &Unit) -> bool {
134 false
135 }
136}
137
138#[derive(Copy, Clone)]
141pub struct DefaultExecutor;
142
143impl Executor for DefaultExecutor {
144 #[instrument(name = "rustc", skip_all, fields(package = id.name().as_str(), process = cmd.to_string()))]
145 fn exec(
146 &self,
147 cmd: &ProcessBuilder,
148 id: PackageId,
149 _target: &Target,
150 _mode: CompileMode,
151 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
152 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
153 ) -> CargoResult<()> {
154 cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
155 .map(drop)
156 }
157}
158
159#[tracing::instrument(skip(build_runner, jobs, plan, exec))]
169fn compile<'gctx>(
170 build_runner: &mut BuildRunner<'_, 'gctx>,
171 jobs: &mut JobQueue<'gctx>,
172 plan: &mut BuildPlan,
173 unit: &Unit,
174 exec: &Arc<dyn Executor>,
175 force_rebuild: bool,
176) -> CargoResult<()> {
177 let bcx = build_runner.bcx;
178 let build_plan = bcx.build_config.build_plan;
179 if !build_runner.compiled.insert(unit.clone()) {
180 return Ok(());
181 }
182
183 fingerprint::prepare_init(build_runner, unit)?;
186
187 let job = if unit.mode.is_run_custom_build() {
188 custom_build::prepare(build_runner, unit)?
189 } else if unit.mode.is_doc_test() {
190 Job::new_fresh()
192 } else if build_plan {
193 Job::new_dirty(
194 rustc(build_runner, unit, &exec.clone())?,
195 DirtyReason::FreshBuild,
196 )
197 } else {
198 let force = exec.force_rebuild(unit) || force_rebuild;
199 let mut job = fingerprint::prepare_target(build_runner, unit, force)?;
200 job.before(if job.freshness().is_dirty() {
201 let work = if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
202 rustdoc(build_runner, unit)?
203 } else {
204 rustc(build_runner, unit, exec)?
205 };
206 work.then(link_targets(build_runner, unit, false)?)
207 } else {
208 let show_diagnostics = unit.show_warnings(bcx.gctx)
211 && build_runner.bcx.gctx.warning_handling()? != WarningHandling::Allow;
212 let work = replay_output_cache(
213 unit.pkg.package_id(),
214 PathBuf::from(unit.pkg.manifest_path()),
215 &unit.target,
216 build_runner.files().message_cache_path(unit),
217 build_runner.bcx.build_config.message_format,
218 show_diagnostics,
219 );
220 work.then(link_targets(build_runner, unit, true)?)
222 });
223
224 job
225 };
226 jobs.enqueue(build_runner, unit, job)?;
227
228 let deps = Vec::from(build_runner.unit_deps(unit)); for dep in deps {
231 compile(build_runner, jobs, plan, &dep.unit, exec, false)?;
232 }
233 if build_plan {
234 plan.add(build_runner, unit)?;
235 }
236
237 Ok(())
238}
239
240fn make_failed_scrape_diagnostic(
243 build_runner: &BuildRunner<'_, '_>,
244 unit: &Unit,
245 top_line: impl Display,
246) -> String {
247 let manifest_path = unit.pkg.manifest_path();
248 let relative_manifest_path = manifest_path
249 .strip_prefix(build_runner.bcx.ws.root())
250 .unwrap_or(&manifest_path);
251
252 format!(
253 "\
254{top_line}
255 Try running with `--verbose` to see the error message.
256 If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in {}",
257 relative_manifest_path.display()
258 )
259}
260
261fn rustc(
263 build_runner: &mut BuildRunner<'_, '_>,
264 unit: &Unit,
265 exec: &Arc<dyn Executor>,
266) -> CargoResult<Work> {
267 let mut rustc = prepare_rustc(build_runner, unit)?;
268 let build_plan = build_runner.bcx.build_config.build_plan;
269
270 let name = unit.pkg.name();
271 let buildkey = unit.buildkey();
272
273 let outputs = build_runner.outputs(unit)?;
274 let root = build_runner.files().out_dir(unit);
275
276 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
278 let current_id = unit.pkg.package_id();
279 let manifest_path = PathBuf::from(unit.pkg.manifest_path());
280 let build_scripts = build_runner.build_scripts.get(unit).cloned();
281
282 let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib());
285
286 let dep_info_name =
287 if let Some(c_extra_filename) = build_runner.files().metadata(unit).c_extra_filename() {
288 format!("{}-{}.d", unit.target.crate_name(), c_extra_filename)
289 } else {
290 format!("{}.d", unit.target.crate_name())
291 };
292 let rustc_dep_info_loc = root.join(dep_info_name);
293 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
294
295 let mut output_options = OutputOptions::new(build_runner, unit);
296 let package_id = unit.pkg.package_id();
297 let target = Target::clone(&unit.target);
298 let mode = unit.mode;
299
300 exec.init(build_runner, unit);
301 let exec = exec.clone();
302
303 let root_output = build_runner.files().host_dest().to_path_buf();
304 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
305 let pkg_root = unit.pkg.root().to_path_buf();
306 let cwd = rustc
307 .get_cwd()
308 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
309 .to_path_buf();
310 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
311 let script_metadata = build_runner.find_build_script_metadata(unit);
312 let is_local = unit.is_local();
313 let artifact = unit.artifact;
314 let sbom_files = build_runner.sbom_output_files(unit)?;
315 let sbom = build_sbom(build_runner, unit)?;
316
317 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
318 && !matches!(
319 build_runner.bcx.gctx.shell().verbosity(),
320 Verbosity::Verbose
321 );
322 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
323 let target_desc = unit.target.description_named();
326 let mut for_scrape_units = build_runner
327 .bcx
328 .scrape_units_have_dep_on(unit)
329 .into_iter()
330 .map(|unit| unit.target.description_named())
331 .collect::<Vec<_>>();
332 for_scrape_units.sort();
333 let for_scrape_units = for_scrape_units.join(", ");
334 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}"))
335 });
336 if hide_diagnostics_for_scrape_unit {
337 output_options.show_diagnostics = false;
338 }
339 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
340 return Ok(Work::new(move |state| {
341 if artifact.is_true() {
345 paths::create_dir_all(&root)?;
346 }
347
348 if let Some(build_scripts) = build_scripts {
356 let script_outputs = build_script_outputs.lock().unwrap();
357 if !build_plan {
358 add_native_deps(
359 &mut rustc,
360 &script_outputs,
361 &build_scripts,
362 pass_l_flag,
363 &target,
364 current_id,
365 mode,
366 )?;
367 add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?;
368 }
369 add_custom_flags(&mut rustc, &script_outputs, script_metadata)?;
370 }
371
372 for output in outputs.iter() {
373 if output.path.extension() == Some(OsStr::new("rmeta")) {
377 let dst = root.join(&output.path).with_extension("rlib");
378 if dst.exists() {
379 paths::remove_file(&dst)?;
380 }
381 }
382
383 if output.hardlink.is_some() && output.path.exists() {
388 _ = paths::remove_file(&output.path).map_err(|e| {
389 tracing::debug!(
390 "failed to delete previous output file `{:?}`: {e:?}",
391 output.path
392 );
393 });
394 }
395 }
396
397 state.running(&rustc);
398 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
399 if build_plan {
400 state.build_plan(buildkey, rustc.clone(), outputs.clone());
401 } else {
402 for file in sbom_files {
403 tracing::debug!("writing sbom to {}", file.display());
404 let outfile = BufWriter::new(paths::create(&file)?);
405 serde_json::to_writer(outfile, &sbom)?;
406 }
407
408 let result = exec
409 .exec(
410 &rustc,
411 package_id,
412 &target,
413 mode,
414 &mut |line| on_stdout_line(state, line, package_id, &target),
415 &mut |line| {
416 on_stderr_line(
417 state,
418 line,
419 package_id,
420 &manifest_path,
421 &target,
422 &mut output_options,
423 )
424 },
425 )
426 .map_err(|e| {
427 if output_options.errors_seen == 0 {
428 e
433 } else {
434 verbose_if_simple_exit_code(e)
435 }
436 })
437 .with_context(|| {
438 let warnings = match output_options.warnings_seen {
440 0 => String::new(),
441 1 => "; 1 warning emitted".to_string(),
442 count => format!("; {} warnings emitted", count),
443 };
444 let errors = match output_options.errors_seen {
445 0 => String::new(),
446 1 => " due to 1 previous error".to_string(),
447 count => format!(" due to {} previous errors", count),
448 };
449 let name = descriptive_pkg_name(&name, &target, &mode);
450 format!("could not compile {name}{errors}{warnings}")
451 });
452
453 if let Err(e) = result {
454 if let Some(diagnostic) = failed_scrape_diagnostic {
455 state.warning(diagnostic);
456 }
457
458 return Err(e);
459 }
460
461 debug_assert_eq!(output_options.errors_seen, 0);
463 }
464
465 if rustc_dep_info_loc.exists() {
466 fingerprint::translate_dep_info(
467 &rustc_dep_info_loc,
468 &dep_info_loc,
469 &cwd,
470 &pkg_root,
471 &build_dir,
472 &rustc,
473 is_local,
475 &env_config,
476 )
477 .with_context(|| {
478 internal(format!(
479 "could not parse/generate dep info at: {}",
480 rustc_dep_info_loc.display()
481 ))
482 })?;
483 paths::set_file_time_no_err(dep_info_loc, timestamp);
486 }
487
488 Ok(())
489 }));
490
491 fn add_native_deps(
494 rustc: &mut ProcessBuilder,
495 build_script_outputs: &BuildScriptOutputs,
496 build_scripts: &BuildScripts,
497 pass_l_flag: bool,
498 target: &Target,
499 current_id: PackageId,
500 mode: CompileMode,
501 ) -> CargoResult<()> {
502 let mut library_paths = vec![];
503
504 for key in build_scripts.to_link.iter() {
505 let output = build_script_outputs.get(key.1).ok_or_else(|| {
506 internal(format!(
507 "couldn't find build script output for {}/{}",
508 key.0, key.1
509 ))
510 })?;
511 library_paths.extend(output.library_paths.iter());
512 }
513
514 library_paths.sort_by_key(|p| match p {
520 LibraryPath::CargoArtifact(_) => 0,
521 LibraryPath::External(_) => 1,
522 });
523
524 for path in library_paths.iter() {
525 rustc.arg("-L").arg(path.as_ref());
526 }
527
528 for key in build_scripts.to_link.iter() {
529 let output = build_script_outputs.get(key.1).ok_or_else(|| {
530 internal(format!(
531 "couldn't find build script output for {}/{}",
532 key.0, key.1
533 ))
534 })?;
535
536 if key.0 == current_id {
537 if pass_l_flag {
538 for name in output.library_links.iter() {
539 rustc.arg("-l").arg(name);
540 }
541 }
542 }
543
544 for (lt, arg) in &output.linker_args {
545 if lt.applies_to(target, mode)
551 && (key.0 == current_id || *lt == LinkArgTarget::Cdylib)
552 {
553 rustc.arg("-C").arg(format!("link-arg={}", arg));
554 }
555 }
556 }
557 Ok(())
558 }
559}
560
561fn verbose_if_simple_exit_code(err: Error) -> Error {
562 match err
565 .downcast_ref::<ProcessError>()
566 .as_ref()
567 .and_then(|perr| perr.code)
568 {
569 Some(n) if cargo_util::is_simple_exit_code(n) => VerboseError::new(err).into(),
570 _ => err,
571 }
572}
573
574fn link_targets(
577 build_runner: &mut BuildRunner<'_, '_>,
578 unit: &Unit,
579 fresh: bool,
580) -> CargoResult<Work> {
581 let bcx = build_runner.bcx;
582 let outputs = build_runner.outputs(unit)?;
583 let export_dir = build_runner.files().export_dir();
584 let package_id = unit.pkg.package_id();
585 let manifest_path = PathBuf::from(unit.pkg.manifest_path());
586 let profile = unit.profile.clone();
587 let unit_mode = unit.mode;
588 let features = unit.features.iter().map(|s| s.to_string()).collect();
589 let json_messages = bcx.build_config.emit_json();
590 let executable = build_runner.get_executable(unit)?;
591 let mut target = Target::clone(&unit.target);
592 if let TargetSourcePath::Metabuild = target.src_path() {
593 let path = unit
595 .pkg
596 .manifest()
597 .metabuild_path(build_runner.bcx.ws.build_dir());
598 target.set_src_path(TargetSourcePath::Path(path));
599 }
600
601 Ok(Work::new(move |state| {
602 let mut destinations = vec![];
607 for output in outputs.iter() {
608 let src = &output.path;
609 if !src.exists() {
612 continue;
613 }
614 let Some(dst) = output.hardlink.as_ref() else {
615 destinations.push(src.clone());
616 continue;
617 };
618 destinations.push(dst.clone());
619 paths::link_or_copy(src, dst)?;
620 if let Some(ref path) = output.export_path {
621 let export_dir = export_dir.as_ref().unwrap();
622 paths::create_dir_all(export_dir)?;
623
624 paths::link_or_copy(src, path)?;
625 }
626 }
627
628 if json_messages {
629 let debuginfo = match profile.debuginfo.into_inner() {
630 TomlDebugInfo::None => machine_message::ArtifactDebuginfo::Int(0),
631 TomlDebugInfo::Limited => machine_message::ArtifactDebuginfo::Int(1),
632 TomlDebugInfo::Full => machine_message::ArtifactDebuginfo::Int(2),
633 TomlDebugInfo::LineDirectivesOnly => {
634 machine_message::ArtifactDebuginfo::Named("line-directives-only")
635 }
636 TomlDebugInfo::LineTablesOnly => {
637 machine_message::ArtifactDebuginfo::Named("line-tables-only")
638 }
639 };
640 let art_profile = machine_message::ArtifactProfile {
641 opt_level: profile.opt_level.as_str(),
642 debuginfo: Some(debuginfo),
643 debug_assertions: profile.debug_assertions,
644 overflow_checks: profile.overflow_checks,
645 test: unit_mode.is_any_test(),
646 };
647
648 let msg = machine_message::Artifact {
649 package_id: package_id.to_spec(),
650 manifest_path,
651 target: &target,
652 profile: art_profile,
653 features,
654 filenames: destinations,
655 executable,
656 fresh,
657 }
658 .to_json_string();
659 state.stdout(msg)?;
660 }
661 Ok(())
662 }))
663}
664
665fn add_plugin_deps(
669 rustc: &mut ProcessBuilder,
670 build_script_outputs: &BuildScriptOutputs,
671 build_scripts: &BuildScripts,
672 root_output: &Path,
673) -> CargoResult<()> {
674 let var = paths::dylib_path_envvar();
675 let search_path = rustc.get_env(var).unwrap_or_default();
676 let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>();
677 for (pkg_id, metadata) in &build_scripts.plugins {
678 let output = build_script_outputs
679 .get(*metadata)
680 .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?;
681 search_path.append(&mut filter_dynamic_search_path(
682 output.library_paths.iter().map(AsRef::as_ref),
683 root_output,
684 ));
685 }
686 let search_path = paths::join_paths(&search_path, var)?;
687 rustc.env(var, &search_path);
688 Ok(())
689}
690
691fn get_dynamic_search_path(path: &Path) -> &Path {
692 match path.to_str().and_then(|s| s.split_once("=")) {
693 Some(("native" | "crate" | "dependency" | "framework" | "all", path)) => Path::new(path),
694 _ => path,
695 }
696}
697
698fn filter_dynamic_search_path<'a, I>(paths: I, root_output: &Path) -> Vec<PathBuf>
704where
705 I: Iterator<Item = &'a PathBuf>,
706{
707 let mut search_path = vec![];
708 for dir in paths {
709 let dir = get_dynamic_search_path(dir);
710 if dir.starts_with(&root_output) {
711 search_path.push(dir.to_path_buf());
712 } else {
713 debug!(
714 "Not including path {} in runtime library search path because it is \
715 outside target root {}",
716 dir.display(),
717 root_output.display()
718 );
719 }
720 }
721 search_path
722}
723
724fn prepare_rustc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
731 let gctx = build_runner.bcx.gctx;
732 let is_primary = build_runner.is_primary_package(unit);
733 let is_workspace = build_runner.bcx.ws.is_member(&unit.pkg);
734
735 let mut base = build_runner
736 .compilation
737 .rustc_process(unit, is_primary, is_workspace)?;
738 build_base_args(build_runner, &mut base, unit)?;
739 if unit.pkg.manifest().is_embedded() {
740 if !gctx.cli_unstable().script {
741 anyhow::bail!(
742 "parsing `{}` requires `-Zscript`",
743 unit.pkg.manifest_path().display()
744 );
745 }
746 base.arg("-Z").arg("crate-attr=feature(frontmatter)");
747 }
748
749 base.inherit_jobserver(&build_runner.jobserver);
750 build_deps_args(&mut base, build_runner, unit)?;
751 add_cap_lints(build_runner.bcx, unit, &mut base);
752 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
753 base.args(args);
754 }
755 base.args(&unit.rustflags);
756 if gctx.cli_unstable().binary_dep_depinfo {
757 base.arg("-Z").arg("binary-dep-depinfo");
758 }
759 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
760 base.arg("-Z").arg("checksum-hash-algorithm=blake3");
761 }
762
763 if is_primary {
764 base.env("CARGO_PRIMARY_PACKAGE", "1");
765 let file_list = std::env::join_paths(build_runner.sbom_output_files(unit)?)?;
766 base.env("CARGO_SBOM_PATH", file_list);
767 }
768
769 if unit.target.is_test() || unit.target.is_bench() {
770 let tmp = build_runner.files().layout(unit.kind).prepare_tmp()?;
771 base.env("CARGO_TARGET_TMPDIR", tmp.display().to_string());
772 }
773
774 Ok(base)
775}
776
777fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
784 let bcx = build_runner.bcx;
785 let mut rustdoc = build_runner.compilation.rustdoc_process(unit, None)?;
787 if unit.pkg.manifest().is_embedded() {
788 if !bcx.gctx.cli_unstable().script {
789 anyhow::bail!(
790 "parsing `{}` requires `-Zscript`",
791 unit.pkg.manifest_path().display()
792 );
793 }
794 rustdoc.arg("-Z").arg("crate-attr=feature(frontmatter)");
795 }
796 rustdoc.inherit_jobserver(&build_runner.jobserver);
797 let crate_name = unit.target.crate_name();
798 rustdoc.arg("--crate-name").arg(&crate_name);
799 add_path_args(bcx.ws, unit, &mut rustdoc);
800 add_cap_lints(bcx, unit, &mut rustdoc);
801
802 if let CompileKind::Target(target) = unit.kind {
803 rustdoc.arg("--target").arg(target.rustc_target());
804 }
805 let doc_dir = build_runner.files().out_dir(unit);
806 rustdoc.arg("-o").arg(&doc_dir);
807 rustdoc.args(&features_args(unit));
808 rustdoc.args(&check_cfg_args(unit));
809
810 add_error_format_and_color(build_runner, &mut rustdoc);
811 add_allow_features(build_runner, &mut rustdoc);
812
813 if build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo {
814 let mut arg =
817 OsString::from("--emit=toolchain-shared-resources,invocation-specific,dep-info=");
818 arg.push(rustdoc_dep_info_loc(build_runner, unit));
819 rustdoc.arg(arg);
820
821 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
822 rustdoc.arg("-Z").arg("checksum-hash-algorithm=blake3");
823 }
824
825 rustdoc.arg("-Zunstable-options");
826 }
827
828 if let Some(trim_paths) = unit.profile.trim_paths.as_ref() {
829 trim_paths_args_rustdoc(&mut rustdoc, build_runner, unit, trim_paths)?;
830 }
831
832 rustdoc.args(unit.pkg.manifest().lint_rustflags());
833
834 let metadata = build_runner.metadata_for_doc_units[unit];
835 rustdoc
836 .arg("-C")
837 .arg(format!("metadata={}", metadata.c_metadata()));
838
839 if unit.mode.is_doc_scrape() {
840 debug_assert!(build_runner.bcx.scrape_units.contains(unit));
841
842 if unit.target.is_test() {
843 rustdoc.arg("--scrape-tests");
844 }
845
846 rustdoc.arg("-Zunstable-options");
847
848 rustdoc
849 .arg("--scrape-examples-output-path")
850 .arg(scrape_output_path(build_runner, unit)?);
851
852 for pkg in build_runner.bcx.packages.packages() {
854 let names = pkg
855 .targets()
856 .iter()
857 .map(|target| target.crate_name())
858 .collect::<HashSet<_>>();
859 for name in names {
860 rustdoc.arg("--scrape-examples-target-crate").arg(name);
861 }
862 }
863 }
864
865 if should_include_scrape_units(build_runner.bcx, unit) {
866 rustdoc.arg("-Zunstable-options");
867 }
868
869 build_deps_args(&mut rustdoc, build_runner, unit)?;
870 rustdoc::add_root_urls(build_runner, unit, &mut rustdoc)?;
871
872 rustdoc::add_output_format(build_runner, &mut rustdoc)?;
873
874 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
875 rustdoc.args(args);
876 }
877 rustdoc.args(&unit.rustdocflags);
878
879 if !crate_version_flag_already_present(&rustdoc) {
880 append_crate_version_flag(unit, &mut rustdoc);
881 }
882
883 Ok(rustdoc)
884}
885
886fn rustdoc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Work> {
888 let mut rustdoc = prepare_rustdoc(build_runner, unit)?;
889
890 let crate_name = unit.target.crate_name();
891 let doc_dir = build_runner.files().out_dir(unit);
892 paths::create_dir_all(&doc_dir)?;
896
897 let target_desc = unit.target.description_named();
898 let name = unit.pkg.name();
899 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
900 let package_id = unit.pkg.package_id();
901 let manifest_path = PathBuf::from(unit.pkg.manifest_path());
902 let target = Target::clone(&unit.target);
903
904 let rustdoc_dep_info_loc = rustdoc_dep_info_loc(build_runner, unit);
905 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
906 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
907 let pkg_root = unit.pkg.root().to_path_buf();
908 let cwd = rustdoc
909 .get_cwd()
910 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
911 .to_path_buf();
912 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
913 let is_local = unit.is_local();
914 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
915 let rustdoc_depinfo_enabled = build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo;
916
917 let mut output_options = OutputOptions::new(build_runner, unit);
918 let script_metadata = build_runner.find_build_script_metadata(unit);
919 let scrape_outputs = if should_include_scrape_units(build_runner.bcx, unit) {
920 Some(
921 build_runner
922 .bcx
923 .scrape_units
924 .iter()
925 .map(|unit| {
926 Ok((
927 build_runner.files().metadata(unit).unit_id(),
928 scrape_output_path(build_runner, unit)?,
929 ))
930 })
931 .collect::<CargoResult<HashMap<_, _>>>()?,
932 )
933 } else {
934 None
935 };
936
937 let failed_scrape_units = Arc::clone(&build_runner.failed_scrape_units);
938 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
939 && !matches!(
940 build_runner.bcx.gctx.shell().verbosity(),
941 Verbosity::Verbose
942 );
943 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
944 make_failed_scrape_diagnostic(
945 build_runner,
946 unit,
947 format_args!("failed to scan {target_desc} in package `{name}` for example code usage"),
948 )
949 });
950 if hide_diagnostics_for_scrape_unit {
951 output_options.show_diagnostics = false;
952 }
953
954 Ok(Work::new(move |state| {
955 add_custom_flags(
956 &mut rustdoc,
957 &build_script_outputs.lock().unwrap(),
958 script_metadata,
959 )?;
960
961 if let Some(scrape_outputs) = scrape_outputs {
966 let failed_scrape_units = failed_scrape_units.lock().unwrap();
967 for (metadata, output_path) in &scrape_outputs {
968 if !failed_scrape_units.contains(metadata) {
969 rustdoc.arg("--with-examples").arg(output_path);
970 }
971 }
972 }
973
974 let crate_dir = doc_dir.join(&crate_name);
975 if crate_dir.exists() {
976 debug!("removing pre-existing doc directory {:?}", crate_dir);
979 paths::remove_dir_all(crate_dir)?;
980 }
981 state.running(&rustdoc);
982 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
983
984 let result = rustdoc
985 .exec_with_streaming(
986 &mut |line| on_stdout_line(state, line, package_id, &target),
987 &mut |line| {
988 on_stderr_line(
989 state,
990 line,
991 package_id,
992 &manifest_path,
993 &target,
994 &mut output_options,
995 )
996 },
997 false,
998 )
999 .map_err(verbose_if_simple_exit_code)
1000 .with_context(|| format!("could not document `{}`", name));
1001
1002 if let Err(e) = result {
1003 if let Some(diagnostic) = failed_scrape_diagnostic {
1004 state.warning(diagnostic);
1005 }
1006
1007 return Err(e);
1008 }
1009
1010 if rustdoc_depinfo_enabled && rustdoc_dep_info_loc.exists() {
1011 fingerprint::translate_dep_info(
1012 &rustdoc_dep_info_loc,
1013 &dep_info_loc,
1014 &cwd,
1015 &pkg_root,
1016 &build_dir,
1017 &rustdoc,
1018 is_local,
1020 &env_config,
1021 )
1022 .with_context(|| {
1023 internal(format_args!(
1024 "could not parse/generate dep info at: {}",
1025 rustdoc_dep_info_loc.display()
1026 ))
1027 })?;
1028 paths::set_file_time_no_err(dep_info_loc, timestamp);
1031 }
1032
1033 Ok(())
1034 }))
1035}
1036
1037fn crate_version_flag_already_present(rustdoc: &ProcessBuilder) -> bool {
1040 rustdoc.get_args().any(|flag| {
1041 flag.to_str()
1042 .map_or(false, |flag| flag.starts_with(RUSTDOC_CRATE_VERSION_FLAG))
1043 })
1044}
1045
1046fn append_crate_version_flag(unit: &Unit, rustdoc: &mut ProcessBuilder) {
1047 rustdoc
1048 .arg(RUSTDOC_CRATE_VERSION_FLAG)
1049 .arg(unit.pkg.version().to_string());
1050}
1051
1052fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuilder) {
1056 if !unit.show_warnings(bcx.gctx) {
1059 cmd.arg("--cap-lints").arg("allow");
1060
1061 } else if !unit.is_local() {
1064 cmd.arg("--cap-lints").arg("warn");
1065 }
1066}
1067
1068fn add_allow_features(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1072 if let Some(allow) = &build_runner.bcx.gctx.cli_unstable().allow_features {
1073 use std::fmt::Write;
1074 let mut arg = String::from("-Zallow-features=");
1075 for f in allow {
1076 let _ = write!(&mut arg, "{f},");
1077 }
1078 cmd.arg(arg.trim_end_matches(','));
1079 }
1080}
1081
1082fn add_error_format_and_color(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1093 cmd.arg("--error-format=json");
1094 let mut json = String::from("--json=diagnostic-rendered-ansi,artifacts,future-incompat");
1095
1096 match build_runner.bcx.build_config.message_format {
1097 MessageFormat::Short | MessageFormat::Json { short: true, .. } => {
1098 json.push_str(",diagnostic-short");
1099 }
1100 _ => {}
1101 }
1102 cmd.arg(json);
1103
1104 let gctx = build_runner.bcx.gctx;
1105 if let Some(width) = gctx.shell().err_width().diagnostic_terminal_width() {
1106 cmd.arg(format!("--diagnostic-width={width}"));
1107 }
1108}
1109
1110fn build_base_args(
1112 build_runner: &BuildRunner<'_, '_>,
1113 cmd: &mut ProcessBuilder,
1114 unit: &Unit,
1115) -> CargoResult<()> {
1116 assert!(!unit.mode.is_run_custom_build());
1117
1118 let bcx = build_runner.bcx;
1119 let Profile {
1120 ref opt_level,
1121 codegen_backend,
1122 codegen_units,
1123 debuginfo,
1124 debug_assertions,
1125 split_debuginfo,
1126 overflow_checks,
1127 rpath,
1128 ref panic,
1129 incremental,
1130 strip,
1131 rustflags: profile_rustflags,
1132 trim_paths,
1133 ..
1134 } = unit.profile.clone();
1135 let test = unit.mode.is_any_test();
1136
1137 cmd.arg("--crate-name").arg(&unit.target.crate_name());
1138
1139 let edition = unit.target.edition();
1140 edition.cmd_edition_arg(cmd);
1141
1142 add_path_args(bcx.ws, unit, cmd);
1143 add_error_format_and_color(build_runner, cmd);
1144 add_allow_features(build_runner, cmd);
1145
1146 let mut contains_dy_lib = false;
1147 if !test {
1148 for crate_type in &unit.target.rustc_crate_types() {
1149 cmd.arg("--crate-type").arg(crate_type.as_str());
1150 contains_dy_lib |= crate_type == &CrateType::Dylib;
1151 }
1152 }
1153
1154 if unit.mode.is_check() {
1155 cmd.arg("--emit=dep-info,metadata");
1156 } else if build_runner.bcx.gctx.cli_unstable().no_embed_metadata {
1157 if unit.benefits_from_no_embed_metadata() {
1167 cmd.arg("--emit=dep-info,metadata,link");
1168 cmd.args(&["-Z", "embed-metadata=no"]);
1169 } else {
1170 cmd.arg("--emit=dep-info,link");
1171 }
1172 } else {
1173 if !unit.requires_upstream_objects() {
1177 cmd.arg("--emit=dep-info,metadata,link");
1178 } else {
1179 cmd.arg("--emit=dep-info,link");
1180 }
1181 }
1182
1183 let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
1184 || (contains_dy_lib && !build_runner.is_primary_package(unit));
1185 if prefer_dynamic {
1186 cmd.arg("-C").arg("prefer-dynamic");
1187 }
1188
1189 if opt_level.as_str() != "0" {
1190 cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
1191 }
1192
1193 if *panic != PanicStrategy::Unwind {
1194 cmd.arg("-C").arg(format!("panic={}", panic));
1195 }
1196
1197 cmd.args(<o_args(build_runner, unit));
1198
1199 if let Some(backend) = codegen_backend {
1200 cmd.arg("-Z").arg(&format!("codegen-backend={}", backend));
1201 }
1202
1203 if let Some(n) = codegen_units {
1204 cmd.arg("-C").arg(&format!("codegen-units={}", n));
1205 }
1206
1207 let debuginfo = debuginfo.into_inner();
1208 if debuginfo != TomlDebugInfo::None {
1210 cmd.arg("-C").arg(format!("debuginfo={debuginfo}"));
1211 if let Some(split) = split_debuginfo {
1218 if build_runner
1219 .bcx
1220 .target_data
1221 .info(unit.kind)
1222 .supports_debuginfo_split(split)
1223 {
1224 cmd.arg("-C").arg(format!("split-debuginfo={split}"));
1225 }
1226 }
1227 }
1228
1229 if let Some(trim_paths) = trim_paths {
1230 trim_paths_args(cmd, build_runner, unit, &trim_paths)?;
1231 }
1232
1233 cmd.args(unit.pkg.manifest().lint_rustflags());
1234 cmd.args(&profile_rustflags);
1235
1236 if opt_level.as_str() != "0" {
1240 if debug_assertions {
1241 cmd.args(&["-C", "debug-assertions=on"]);
1242 if !overflow_checks {
1243 cmd.args(&["-C", "overflow-checks=off"]);
1244 }
1245 } else if overflow_checks {
1246 cmd.args(&["-C", "overflow-checks=on"]);
1247 }
1248 } else if !debug_assertions {
1249 cmd.args(&["-C", "debug-assertions=off"]);
1250 if overflow_checks {
1251 cmd.args(&["-C", "overflow-checks=on"]);
1252 }
1253 } else if !overflow_checks {
1254 cmd.args(&["-C", "overflow-checks=off"]);
1255 }
1256
1257 if test && unit.target.harness() {
1258 cmd.arg("--test");
1259
1260 if *panic == PanicStrategy::Abort {
1268 cmd.arg("-Z").arg("panic-abort-tests");
1269 }
1270 } else if test {
1271 cmd.arg("--cfg").arg("test");
1272 }
1273
1274 cmd.args(&features_args(unit));
1275 cmd.args(&check_cfg_args(unit));
1276
1277 let meta = build_runner.files().metadata(unit);
1278 cmd.arg("-C")
1279 .arg(&format!("metadata={}", meta.c_metadata()));
1280 if let Some(c_extra_filename) = meta.c_extra_filename() {
1281 cmd.arg("-C")
1282 .arg(&format!("extra-filename=-{c_extra_filename}"));
1283 }
1284
1285 if rpath {
1286 cmd.arg("-C").arg("rpath");
1287 }
1288
1289 cmd.arg("--out-dir")
1290 .arg(&build_runner.files().out_dir(unit));
1291
1292 fn opt(cmd: &mut ProcessBuilder, key: &str, prefix: &str, val: Option<&OsStr>) {
1293 if let Some(val) = val {
1294 let mut joined = OsString::from(prefix);
1295 joined.push(val);
1296 cmd.arg(key).arg(joined);
1297 }
1298 }
1299
1300 if let CompileKind::Target(n) = unit.kind {
1301 cmd.arg("--target").arg(n.rustc_target());
1302 }
1303
1304 opt(
1305 cmd,
1306 "-C",
1307 "linker=",
1308 build_runner
1309 .compilation
1310 .target_linker(unit.kind)
1311 .as_ref()
1312 .map(|s| s.as_ref()),
1313 );
1314 if incremental {
1315 let dir = build_runner
1316 .files()
1317 .layout(unit.kind)
1318 .incremental()
1319 .as_os_str();
1320 opt(cmd, "-C", "incremental=", Some(dir));
1321 }
1322
1323 let strip = strip.into_inner();
1324 if strip != StripInner::None {
1325 cmd.arg("-C").arg(format!("strip={}", strip));
1326 }
1327
1328 if unit.is_std {
1329 cmd.arg("-Z")
1335 .arg("force-unstable-if-unmarked")
1336 .env("RUSTC_BOOTSTRAP", "1");
1337 }
1338
1339 if unit.target.is_test() || unit.target.is_bench() {
1341 for bin_target in unit
1342 .pkg
1343 .manifest()
1344 .targets()
1345 .iter()
1346 .filter(|target| target.is_bin())
1347 {
1348 let exe_path = build_runner.files().bin_link_for_target(
1349 bin_target,
1350 unit.kind,
1351 build_runner.bcx,
1352 )?;
1353 let name = bin_target
1354 .binary_filename()
1355 .unwrap_or(bin_target.name().to_string());
1356 let key = format!("CARGO_BIN_EXE_{}", name);
1357 cmd.env(&key, exe_path);
1358 }
1359 }
1360 Ok(())
1361}
1362
1363fn features_args(unit: &Unit) -> Vec<OsString> {
1365 let mut args = Vec::with_capacity(unit.features.len() * 2);
1366
1367 for feat in &unit.features {
1368 args.push(OsString::from("--cfg"));
1369 args.push(OsString::from(format!("feature=\"{}\"", feat)));
1370 }
1371
1372 args
1373}
1374
1375fn trim_paths_args_rustdoc(
1377 cmd: &mut ProcessBuilder,
1378 build_runner: &BuildRunner<'_, '_>,
1379 unit: &Unit,
1380 trim_paths: &TomlTrimPaths,
1381) -> CargoResult<()> {
1382 match trim_paths {
1383 TomlTrimPaths::Values(values) if !values.contains(&TomlTrimPathsValue::Diagnostics) => {
1385 return Ok(())
1386 }
1387 _ => {}
1388 }
1389
1390 cmd.arg("-Zunstable-options");
1392
1393 cmd.arg(package_remap(build_runner, unit));
1396 cmd.arg(sysroot_remap(build_runner, unit));
1397
1398 Ok(())
1399}
1400
1401fn trim_paths_args(
1407 cmd: &mut ProcessBuilder,
1408 build_runner: &BuildRunner<'_, '_>,
1409 unit: &Unit,
1410 trim_paths: &TomlTrimPaths,
1411) -> CargoResult<()> {
1412 if trim_paths.is_none() {
1413 return Ok(());
1414 }
1415
1416 cmd.arg("-Zunstable-options");
1418 cmd.arg(format!("-Zremap-path-scope={trim_paths}"));
1419
1420 cmd.arg(package_remap(build_runner, unit));
1423 cmd.arg(sysroot_remap(build_runner, unit));
1424
1425 Ok(())
1426}
1427
1428fn sysroot_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1433 let mut remap = OsString::from("--remap-path-prefix=");
1434 remap.push({
1435 let mut sysroot = build_runner.bcx.target_data.info(unit.kind).sysroot.clone();
1437 sysroot.push("lib");
1438 sysroot.push("rustlib");
1439 sysroot.push("src");
1440 sysroot.push("rust");
1441 sysroot
1442 });
1443 remap.push("=");
1444 remap.push("/rustc/");
1445 if let Some(commit_hash) = build_runner.bcx.rustc().commit_hash.as_ref() {
1446 remap.push(commit_hash);
1447 } else {
1448 remap.push(build_runner.bcx.rustc().version.to_string());
1449 }
1450 remap
1451}
1452
1453fn package_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1461 let pkg_root = unit.pkg.root();
1462 let ws_root = build_runner.bcx.ws.root();
1463 let mut remap = OsString::from("--remap-path-prefix=");
1464 let source_id = unit.pkg.package_id().source_id();
1465 if source_id.is_git() {
1466 remap.push(
1467 build_runner
1468 .bcx
1469 .gctx
1470 .git_checkouts_path()
1471 .as_path_unlocked(),
1472 );
1473 remap.push("=");
1474 } else if source_id.is_registry() {
1475 remap.push(
1476 build_runner
1477 .bcx
1478 .gctx
1479 .registry_source_path()
1480 .as_path_unlocked(),
1481 );
1482 remap.push("=");
1483 } else if pkg_root.strip_prefix(ws_root).is_ok() {
1484 remap.push(ws_root);
1485 remap.push("=."); } else {
1487 remap.push(pkg_root);
1488 remap.push("=");
1489 remap.push(unit.pkg.name());
1490 remap.push("-");
1491 remap.push(unit.pkg.version().to_string());
1492 }
1493 remap
1494}
1495
1496fn check_cfg_args(unit: &Unit) -> Vec<OsString> {
1498 let gross_cap_estimation = unit.pkg.summary().features().len() * 7 + 25;
1516 let mut arg_feature = OsString::with_capacity(gross_cap_estimation);
1517
1518 arg_feature.push("cfg(feature, values(");
1519 for (i, feature) in unit.pkg.summary().features().keys().enumerate() {
1520 if i != 0 {
1521 arg_feature.push(", ");
1522 }
1523 arg_feature.push("\"");
1524 arg_feature.push(feature);
1525 arg_feature.push("\"");
1526 }
1527 arg_feature.push("))");
1528
1529 vec![
1538 OsString::from("--check-cfg"),
1539 OsString::from("cfg(docsrs,test)"),
1540 OsString::from("--check-cfg"),
1541 arg_feature,
1542 ]
1543}
1544
1545fn lto_args(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> Vec<OsString> {
1547 let mut result = Vec::new();
1548 let mut push = |arg: &str| {
1549 result.push(OsString::from("-C"));
1550 result.push(OsString::from(arg));
1551 };
1552 match build_runner.lto[unit] {
1553 lto::Lto::Run(None) => push("lto"),
1554 lto::Lto::Run(Some(s)) => push(&format!("lto={}", s)),
1555 lto::Lto::Off => {
1556 push("lto=off");
1557 push("embed-bitcode=no");
1558 }
1559 lto::Lto::ObjectAndBitcode => {} lto::Lto::OnlyBitcode => push("linker-plugin-lto"),
1561 lto::Lto::OnlyObject => push("embed-bitcode=no"),
1562 }
1563 result
1564}
1565
1566fn build_deps_args(
1572 cmd: &mut ProcessBuilder,
1573 build_runner: &BuildRunner<'_, '_>,
1574 unit: &Unit,
1575) -> CargoResult<()> {
1576 let bcx = build_runner.bcx;
1577 cmd.arg("-L").arg(&{
1578 let mut deps = OsString::from("dependency=");
1579 deps.push(build_runner.files().deps_dir(unit));
1580 deps
1581 });
1582
1583 if !unit.kind.is_host() {
1586 cmd.arg("-L").arg(&{
1587 let mut deps = OsString::from("dependency=");
1588 deps.push(build_runner.files().host_deps());
1589 deps
1590 });
1591 }
1592
1593 let deps = build_runner.unit_deps(unit);
1594
1595 if !deps
1599 .iter()
1600 .any(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_linkable())
1601 {
1602 if let Some(dep) = deps.iter().find(|dep| {
1603 !dep.unit.mode.is_doc() && dep.unit.target.is_lib() && !dep.unit.artifact.is_true()
1604 }) {
1605 bcx.gctx.shell().warn(format!(
1606 "The package `{}` \
1607 provides no linkable target. The compiler might raise an error while compiling \
1608 `{}`. Consider adding 'dylib' or 'rlib' to key `crate-type` in `{}`'s \
1609 Cargo.toml. This warning might turn into a hard error in the future.",
1610 dep.unit.target.crate_name(),
1611 unit.target.crate_name(),
1612 dep.unit.target.crate_name()
1613 ))?;
1614 }
1615 }
1616
1617 let mut unstable_opts = false;
1618
1619 for dep in deps {
1620 if dep.unit.mode.is_run_custom_build() {
1621 cmd.env(
1622 "OUT_DIR",
1623 &build_runner.files().build_script_out_dir(&dep.unit),
1624 );
1625 }
1626 }
1627
1628 for arg in extern_args(build_runner, unit, &mut unstable_opts)? {
1629 cmd.arg(arg);
1630 }
1631
1632 for (var, env) in artifact::get_env(build_runner, deps)? {
1633 cmd.env(&var, env);
1634 }
1635
1636 if unstable_opts {
1639 cmd.arg("-Z").arg("unstable-options");
1640 }
1641
1642 Ok(())
1643}
1644
1645fn add_custom_flags(
1649 cmd: &mut ProcessBuilder,
1650 build_script_outputs: &BuildScriptOutputs,
1651 metadata: Option<UnitHash>,
1652) -> CargoResult<()> {
1653 if let Some(metadata) = metadata {
1654 if let Some(output) = build_script_outputs.get(metadata) {
1655 for cfg in output.cfgs.iter() {
1656 cmd.arg("--cfg").arg(cfg);
1657 }
1658 for check_cfg in &output.check_cfgs {
1659 cmd.arg("--check-cfg").arg(check_cfg);
1660 }
1661 for (name, value) in output.env.iter() {
1662 cmd.env(name, value);
1663 }
1664 }
1665 }
1666
1667 Ok(())
1668}
1669
1670pub fn extern_args(
1672 build_runner: &BuildRunner<'_, '_>,
1673 unit: &Unit,
1674 unstable_opts: &mut bool,
1675) -> CargoResult<Vec<OsString>> {
1676 let mut result = Vec::new();
1677 let deps = build_runner.unit_deps(unit);
1678
1679 let no_embed_metadata = build_runner.bcx.gctx.cli_unstable().no_embed_metadata;
1680
1681 let mut link_to =
1683 |dep: &UnitDep, extern_crate_name: InternedString, noprelude: bool| -> CargoResult<()> {
1684 let mut value = OsString::new();
1685 let mut opts = Vec::new();
1686 let is_public_dependency_enabled = unit
1687 .pkg
1688 .manifest()
1689 .unstable_features()
1690 .require(Feature::public_dependency())
1691 .is_ok()
1692 || build_runner.bcx.gctx.cli_unstable().public_dependency;
1693 if !dep.public && unit.target.is_lib() && is_public_dependency_enabled {
1694 opts.push("priv");
1695 *unstable_opts = true;
1696 }
1697 if noprelude {
1698 opts.push("noprelude");
1699 *unstable_opts = true;
1700 }
1701 if !opts.is_empty() {
1702 value.push(opts.join(","));
1703 value.push(":");
1704 }
1705 value.push(extern_crate_name.as_str());
1706 value.push("=");
1707
1708 let mut pass = |file| {
1709 let mut value = value.clone();
1710 value.push(file);
1711 result.push(OsString::from("--extern"));
1712 result.push(value);
1713 };
1714
1715 let outputs = build_runner.outputs(&dep.unit)?;
1716
1717 if build_runner.only_requires_rmeta(unit, &dep.unit) || dep.unit.mode.is_check() {
1718 let output = outputs
1720 .iter()
1721 .find(|output| output.flavor == FileFlavor::Rmeta)
1722 .expect("failed to find rmeta dep for pipelined dep");
1723 pass(&output.path);
1724 } else {
1725 for output in outputs.iter() {
1727 if output.flavor == FileFlavor::Linkable {
1728 pass(&output.path);
1729 }
1730 else if no_embed_metadata && output.flavor == FileFlavor::Rmeta {
1734 pass(&output.path);
1735 }
1736 }
1737 }
1738 Ok(())
1739 };
1740
1741 for dep in deps {
1742 if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
1743 link_to(dep, dep.extern_crate_name, dep.noprelude)?;
1744 }
1745 }
1746 if unit.target.proc_macro() {
1747 result.push(OsString::from("--extern"));
1749 result.push(OsString::from("proc_macro"));
1750 }
1751
1752 Ok(result)
1753}
1754
1755fn envify(s: &str) -> String {
1756 s.chars()
1757 .flat_map(|c| c.to_uppercase())
1758 .map(|c| if c == '-' { '_' } else { c })
1759 .collect()
1760}
1761
1762struct OutputOptions {
1765 format: MessageFormat,
1767 cache_cell: Option<(PathBuf, LazyCell<File>)>,
1772 show_diagnostics: bool,
1780 warnings_seen: usize,
1782 errors_seen: usize,
1784}
1785
1786impl OutputOptions {
1787 fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions {
1788 let path = build_runner.files().message_cache_path(unit);
1789 drop(fs::remove_file(&path));
1791 let cache_cell = Some((path, LazyCell::new()));
1792 let show_diagnostics =
1793 build_runner.bcx.gctx.warning_handling().unwrap_or_default() != WarningHandling::Allow;
1794 OutputOptions {
1795 format: build_runner.bcx.build_config.message_format,
1796 cache_cell,
1797 show_diagnostics,
1798 warnings_seen: 0,
1799 errors_seen: 0,
1800 }
1801 }
1802}
1803
1804fn on_stdout_line(
1805 state: &JobState<'_, '_>,
1806 line: &str,
1807 _package_id: PackageId,
1808 _target: &Target,
1809) -> CargoResult<()> {
1810 state.stdout(line.to_string())?;
1811 Ok(())
1812}
1813
1814fn on_stderr_line(
1815 state: &JobState<'_, '_>,
1816 line: &str,
1817 package_id: PackageId,
1818 manifest_path: &std::path::Path,
1819 target: &Target,
1820 options: &mut OutputOptions,
1821) -> CargoResult<()> {
1822 if on_stderr_line_inner(state, line, package_id, manifest_path, target, options)? {
1823 if let Some((path, cell)) = &mut options.cache_cell {
1825 let f = cell.try_borrow_mut_with(|| paths::create(path))?;
1827 debug_assert!(!line.contains('\n'));
1828 f.write_all(line.as_bytes())?;
1829 f.write_all(&[b'\n'])?;
1830 }
1831 }
1832 Ok(())
1833}
1834
1835fn on_stderr_line_inner(
1837 state: &JobState<'_, '_>,
1838 line: &str,
1839 package_id: PackageId,
1840 manifest_path: &std::path::Path,
1841 target: &Target,
1842 options: &mut OutputOptions,
1843) -> CargoResult<bool> {
1844 if !line.starts_with('{') {
1850 state.stderr(line.to_string())?;
1851 return Ok(true);
1852 }
1853
1854 let mut compiler_message: Box<serde_json::value::RawValue> = match serde_json::from_str(line) {
1855 Ok(msg) => msg,
1856
1857 Err(e) => {
1861 debug!("failed to parse json: {:?}", e);
1862 state.stderr(line.to_string())?;
1863 return Ok(true);
1864 }
1865 };
1866
1867 let count_diagnostic = |level, options: &mut OutputOptions| {
1868 if level == "warning" {
1869 options.warnings_seen += 1;
1870 } else if level == "error" {
1871 options.errors_seen += 1;
1872 }
1873 };
1874
1875 if let Ok(report) = serde_json::from_str::<FutureIncompatReport>(compiler_message.get()) {
1876 for item in &report.future_incompat_report {
1877 count_diagnostic(&*item.diagnostic.level, options);
1878 }
1879 state.future_incompat_report(report.future_incompat_report);
1880 return Ok(true);
1881 }
1882
1883 match options.format {
1886 MessageFormat::Human
1891 | MessageFormat::Short
1892 | MessageFormat::Json {
1893 render_diagnostics: true,
1894 ..
1895 } => {
1896 #[derive(serde::Deserialize)]
1897 struct CompilerMessage<'a> {
1898 rendered: String,
1902 #[serde(borrow)]
1903 message: Cow<'a, str>,
1904 #[serde(borrow)]
1905 level: Cow<'a, str>,
1906 children: Vec<PartialDiagnostic>,
1907 }
1908
1909 #[derive(serde::Deserialize)]
1918 struct PartialDiagnostic {
1919 spans: Vec<PartialDiagnosticSpan>,
1920 }
1921
1922 #[derive(serde::Deserialize)]
1924 struct PartialDiagnosticSpan {
1925 suggestion_applicability: Option<Applicability>,
1926 }
1927
1928 if let Ok(mut msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
1929 {
1930 if msg.message.starts_with("aborting due to")
1931 || msg.message.ends_with("warning emitted")
1932 || msg.message.ends_with("warnings emitted")
1933 {
1934 return Ok(true);
1936 }
1937 if msg.rendered.ends_with('\n') {
1939 msg.rendered.pop();
1940 }
1941 let rendered = msg.rendered;
1942 if options.show_diagnostics {
1943 let machine_applicable: bool = msg
1944 .children
1945 .iter()
1946 .map(|child| {
1947 child
1948 .spans
1949 .iter()
1950 .filter_map(|span| span.suggestion_applicability)
1951 .any(|app| app == Applicability::MachineApplicable)
1952 })
1953 .any(|b| b);
1954 count_diagnostic(&msg.level, options);
1955 state.emit_diag(&msg.level, rendered, machine_applicable)?;
1956 }
1957 return Ok(true);
1958 }
1959 }
1960
1961 MessageFormat::Json { ansi: false, .. } => {
1965 #[derive(serde::Deserialize, serde::Serialize)]
1966 struct CompilerMessage<'a> {
1967 rendered: String,
1968 #[serde(flatten, borrow)]
1969 other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
1970 }
1971 if let Ok(mut error) =
1972 serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
1973 {
1974 error.rendered = anstream::adapter::strip_str(&error.rendered).to_string();
1975 let new_line = serde_json::to_string(&error)?;
1976 compiler_message = serde_json::value::RawValue::from_string(new_line)?;
1977 }
1978 }
1979
1980 MessageFormat::Json { ansi: true, .. } => {}
1983 }
1984
1985 #[derive(serde::Deserialize)]
1992 struct ArtifactNotification<'a> {
1993 #[serde(borrow)]
1994 artifact: Cow<'a, str>,
1995 }
1996
1997 if let Ok(artifact) = serde_json::from_str::<ArtifactNotification<'_>>(compiler_message.get()) {
1998 trace!("found directive from rustc: `{}`", artifact.artifact);
1999 if artifact.artifact.ends_with(".rmeta") {
2000 debug!("looks like metadata finished early!");
2001 state.rmeta_produced();
2002 }
2003 return Ok(false);
2004 }
2005
2006 if !options.show_diagnostics {
2011 return Ok(true);
2012 }
2013
2014 #[derive(serde::Deserialize)]
2015 struct CompilerMessage<'a> {
2016 #[serde(borrow)]
2017 message: Cow<'a, str>,
2018 #[serde(borrow)]
2019 level: Cow<'a, str>,
2020 }
2021
2022 if let Ok(msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get()) {
2023 if msg.message.starts_with("aborting due to")
2024 || msg.message.ends_with("warning emitted")
2025 || msg.message.ends_with("warnings emitted")
2026 {
2027 return Ok(true);
2029 }
2030 count_diagnostic(&msg.level, options);
2031 }
2032
2033 let msg = machine_message::FromCompiler {
2034 package_id: package_id.to_spec(),
2035 manifest_path,
2036 target,
2037 message: compiler_message,
2038 }
2039 .to_json_string();
2040
2041 state.stdout(msg)?;
2045 Ok(true)
2046}
2047
2048fn replay_output_cache(
2052 package_id: PackageId,
2053 manifest_path: PathBuf,
2054 target: &Target,
2055 path: PathBuf,
2056 format: MessageFormat,
2057 show_diagnostics: bool,
2058) -> Work {
2059 let target = target.clone();
2060 let mut options = OutputOptions {
2061 format,
2062 cache_cell: None,
2063 show_diagnostics,
2064 warnings_seen: 0,
2065 errors_seen: 0,
2066 };
2067 Work::new(move |state| {
2068 if !path.exists() {
2069 return Ok(());
2071 }
2072 let file = paths::open(&path)?;
2076 let mut reader = std::io::BufReader::new(file);
2077 let mut line = String::new();
2078 loop {
2079 let length = reader.read_line(&mut line)?;
2080 if length == 0 {
2081 break;
2082 }
2083 let trimmed = line.trim_end_matches(&['\n', '\r'][..]);
2084 on_stderr_line(
2085 state,
2086 trimmed,
2087 package_id,
2088 &manifest_path,
2089 &target,
2090 &mut options,
2091 )?;
2092 line.clear();
2093 }
2094 Ok(())
2095 })
2096}
2097
2098fn descriptive_pkg_name(name: &str, target: &Target, mode: &CompileMode) -> String {
2101 let desc_name = target.description_named();
2102 let mode = if mode.is_rustc_test() && !(target.is_test() || target.is_bench()) {
2103 " test"
2104 } else if mode.is_doc_test() {
2105 " doctest"
2106 } else if mode.is_doc() {
2107 " doc"
2108 } else {
2109 ""
2110 };
2111 format!("`{name}` ({desc_name}{mode})")
2112}
2113
2114pub(crate) fn apply_env_config(
2116 gctx: &crate::GlobalContext,
2117 cmd: &mut ProcessBuilder,
2118) -> CargoResult<()> {
2119 for (key, value) in gctx.env_config()?.iter() {
2120 if cmd.get_envs().contains_key(key) {
2122 continue;
2123 }
2124 cmd.env(key, value);
2125 }
2126 Ok(())
2127}
2128
2129fn should_include_scrape_units(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
2131 unit.mode.is_doc() && bcx.scrape_units.len() > 0 && bcx.ws.unit_needs_doc_scrape(unit)
2132}
2133
2134fn scrape_output_path(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<PathBuf> {
2136 assert!(unit.mode.is_doc() || unit.mode.is_doc_scrape());
2137 build_runner
2138 .outputs(unit)
2139 .map(|outputs| outputs[0].path.clone())
2140}
2141
2142fn rustdoc_dep_info_loc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> PathBuf {
2144 let mut loc = build_runner.files().fingerprint_file_path(unit, "");
2145 loc.set_extension("d");
2146 loc
2147}