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 if !unit.skip_non_compile_time_dep {
187 fingerprint::prepare_init(build_runner, unit)?;
190
191 let job = if unit.mode.is_run_custom_build() {
192 custom_build::prepare(build_runner, unit)?
193 } else if unit.mode.is_doc_test() {
194 Job::new_fresh()
196 } else if build_plan {
197 Job::new_dirty(
198 rustc(build_runner, unit, &exec.clone())?,
199 DirtyReason::FreshBuild,
200 )
201 } else {
202 let force = exec.force_rebuild(unit) || force_rebuild;
203 let mut job = fingerprint::prepare_target(build_runner, unit, force)?;
204 job.before(if job.freshness().is_dirty() {
205 let work = if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
206 rustdoc(build_runner, unit)?
207 } else {
208 rustc(build_runner, unit, exec)?
209 };
210 work.then(link_targets(build_runner, unit, false)?)
211 } else {
212 let show_diagnostics = unit.show_warnings(bcx.gctx)
215 && build_runner.bcx.gctx.warning_handling()? != WarningHandling::Allow;
216 let work = replay_output_cache(
217 unit.pkg.package_id(),
218 PathBuf::from(unit.pkg.manifest_path()),
219 &unit.target,
220 build_runner.files().message_cache_path(unit),
221 build_runner.bcx.build_config.message_format,
222 show_diagnostics,
223 );
224 work.then(link_targets(build_runner, unit, true)?)
226 });
227
228 job
229 };
230 jobs.enqueue(build_runner, unit, job)?;
231 }
232
233 let deps = Vec::from(build_runner.unit_deps(unit)); for dep in deps {
236 compile(build_runner, jobs, plan, &dep.unit, exec, false)?;
237 }
238 if build_plan {
239 plan.add(build_runner, unit)?;
240 }
241
242 Ok(())
243}
244
245fn make_failed_scrape_diagnostic(
248 build_runner: &BuildRunner<'_, '_>,
249 unit: &Unit,
250 top_line: impl Display,
251) -> String {
252 let manifest_path = unit.pkg.manifest_path();
253 let relative_manifest_path = manifest_path
254 .strip_prefix(build_runner.bcx.ws.root())
255 .unwrap_or(&manifest_path);
256
257 format!(
258 "\
259{top_line}
260 Try running with `--verbose` to see the error message.
261 If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in {}",
262 relative_manifest_path.display()
263 )
264}
265
266fn rustc(
268 build_runner: &mut BuildRunner<'_, '_>,
269 unit: &Unit,
270 exec: &Arc<dyn Executor>,
271) -> CargoResult<Work> {
272 let mut rustc = prepare_rustc(build_runner, unit)?;
273 let build_plan = build_runner.bcx.build_config.build_plan;
274
275 let name = unit.pkg.name();
276 let buildkey = unit.buildkey();
277
278 let outputs = build_runner.outputs(unit)?;
279 let root = build_runner.files().out_dir(unit);
280
281 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
283 let current_id = unit.pkg.package_id();
284 let manifest_path = PathBuf::from(unit.pkg.manifest_path());
285 let build_scripts = build_runner.build_scripts.get(unit).cloned();
286
287 let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib());
290
291 let dep_info_name =
292 if let Some(c_extra_filename) = build_runner.files().metadata(unit).c_extra_filename() {
293 format!("{}-{}.d", unit.target.crate_name(), c_extra_filename)
294 } else {
295 format!("{}.d", unit.target.crate_name())
296 };
297 let rustc_dep_info_loc = root.join(dep_info_name);
298 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
299
300 let mut output_options = OutputOptions::new(build_runner, unit);
301 let package_id = unit.pkg.package_id();
302 let target = Target::clone(&unit.target);
303 let mode = unit.mode;
304
305 exec.init(build_runner, unit);
306 let exec = exec.clone();
307
308 let root_output = build_runner.files().host_dest().to_path_buf();
309 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
310 let pkg_root = unit.pkg.root().to_path_buf();
311 let cwd = rustc
312 .get_cwd()
313 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
314 .to_path_buf();
315 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
316 let script_metadata = build_runner.find_build_script_metadata(unit);
317 let is_local = unit.is_local();
318 let artifact = unit.artifact;
319 let sbom_files = build_runner.sbom_output_files(unit)?;
320 let sbom = build_sbom(build_runner, unit)?;
321
322 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
323 && !matches!(
324 build_runner.bcx.gctx.shell().verbosity(),
325 Verbosity::Verbose
326 );
327 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
328 let target_desc = unit.target.description_named();
331 let mut for_scrape_units = build_runner
332 .bcx
333 .scrape_units_have_dep_on(unit)
334 .into_iter()
335 .map(|unit| unit.target.description_named())
336 .collect::<Vec<_>>();
337 for_scrape_units.sort();
338 let for_scrape_units = for_scrape_units.join(", ");
339 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}"))
340 });
341 if hide_diagnostics_for_scrape_unit {
342 output_options.show_diagnostics = false;
343 }
344 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
345 return Ok(Work::new(move |state| {
346 if artifact.is_true() {
350 paths::create_dir_all(&root)?;
351 }
352
353 if let Some(build_scripts) = build_scripts {
361 let script_outputs = build_script_outputs.lock().unwrap();
362 if !build_plan {
363 add_native_deps(
364 &mut rustc,
365 &script_outputs,
366 &build_scripts,
367 pass_l_flag,
368 &target,
369 current_id,
370 mode,
371 )?;
372 add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?;
373 }
374 add_custom_flags(&mut rustc, &script_outputs, script_metadata)?;
375 }
376
377 for output in outputs.iter() {
378 if output.path.extension() == Some(OsStr::new("rmeta")) {
382 let dst = root.join(&output.path).with_extension("rlib");
383 if dst.exists() {
384 paths::remove_file(&dst)?;
385 }
386 }
387
388 if output.hardlink.is_some() && output.path.exists() {
393 _ = paths::remove_file(&output.path).map_err(|e| {
394 tracing::debug!(
395 "failed to delete previous output file `{:?}`: {e:?}",
396 output.path
397 );
398 });
399 }
400 }
401
402 state.running(&rustc);
403 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
404 if build_plan {
405 state.build_plan(buildkey, rustc.clone(), outputs.clone());
406 } else {
407 for file in sbom_files {
408 tracing::debug!("writing sbom to {}", file.display());
409 let outfile = BufWriter::new(paths::create(&file)?);
410 serde_json::to_writer(outfile, &sbom)?;
411 }
412
413 let result = exec
414 .exec(
415 &rustc,
416 package_id,
417 &target,
418 mode,
419 &mut |line| on_stdout_line(state, line, package_id, &target),
420 &mut |line| {
421 on_stderr_line(
422 state,
423 line,
424 package_id,
425 &manifest_path,
426 &target,
427 &mut output_options,
428 )
429 },
430 )
431 .map_err(|e| {
432 if output_options.errors_seen == 0 {
433 e
438 } else {
439 verbose_if_simple_exit_code(e)
440 }
441 })
442 .with_context(|| {
443 let warnings = match output_options.warnings_seen {
445 0 => String::new(),
446 1 => "; 1 warning emitted".to_string(),
447 count => format!("; {} warnings emitted", count),
448 };
449 let errors = match output_options.errors_seen {
450 0 => String::new(),
451 1 => " due to 1 previous error".to_string(),
452 count => format!(" due to {} previous errors", count),
453 };
454 let name = descriptive_pkg_name(&name, &target, &mode);
455 format!("could not compile {name}{errors}{warnings}")
456 });
457
458 if let Err(e) = result {
459 if let Some(diagnostic) = failed_scrape_diagnostic {
460 state.warning(diagnostic);
461 }
462
463 return Err(e);
464 }
465
466 debug_assert_eq!(output_options.errors_seen, 0);
468 }
469
470 if rustc_dep_info_loc.exists() {
471 fingerprint::translate_dep_info(
472 &rustc_dep_info_loc,
473 &dep_info_loc,
474 &cwd,
475 &pkg_root,
476 &build_dir,
477 &rustc,
478 is_local,
480 &env_config,
481 )
482 .with_context(|| {
483 internal(format!(
484 "could not parse/generate dep info at: {}",
485 rustc_dep_info_loc.display()
486 ))
487 })?;
488 paths::set_file_time_no_err(dep_info_loc, timestamp);
491 }
492
493 Ok(())
494 }));
495
496 fn add_native_deps(
499 rustc: &mut ProcessBuilder,
500 build_script_outputs: &BuildScriptOutputs,
501 build_scripts: &BuildScripts,
502 pass_l_flag: bool,
503 target: &Target,
504 current_id: PackageId,
505 mode: CompileMode,
506 ) -> CargoResult<()> {
507 let mut library_paths = vec![];
508
509 for key in build_scripts.to_link.iter() {
510 let output = build_script_outputs.get(key.1).ok_or_else(|| {
511 internal(format!(
512 "couldn't find build script output for {}/{}",
513 key.0, key.1
514 ))
515 })?;
516 library_paths.extend(output.library_paths.iter());
517 }
518
519 library_paths.sort_by_key(|p| match p {
525 LibraryPath::CargoArtifact(_) => 0,
526 LibraryPath::External(_) => 1,
527 });
528
529 for path in library_paths.iter() {
530 rustc.arg("-L").arg(path.as_ref());
531 }
532
533 for key in build_scripts.to_link.iter() {
534 let output = build_script_outputs.get(key.1).ok_or_else(|| {
535 internal(format!(
536 "couldn't find build script output for {}/{}",
537 key.0, key.1
538 ))
539 })?;
540
541 if key.0 == current_id {
542 if pass_l_flag {
543 for name in output.library_links.iter() {
544 rustc.arg("-l").arg(name);
545 }
546 }
547 }
548
549 for (lt, arg) in &output.linker_args {
550 if lt.applies_to(target, mode)
556 && (key.0 == current_id || *lt == LinkArgTarget::Cdylib)
557 {
558 rustc.arg("-C").arg(format!("link-arg={}", arg));
559 }
560 }
561 }
562 Ok(())
563 }
564}
565
566fn verbose_if_simple_exit_code(err: Error) -> Error {
567 match err
570 .downcast_ref::<ProcessError>()
571 .as_ref()
572 .and_then(|perr| perr.code)
573 {
574 Some(n) if cargo_util::is_simple_exit_code(n) => VerboseError::new(err).into(),
575 _ => err,
576 }
577}
578
579fn link_targets(
582 build_runner: &mut BuildRunner<'_, '_>,
583 unit: &Unit,
584 fresh: bool,
585) -> CargoResult<Work> {
586 let bcx = build_runner.bcx;
587 let outputs = build_runner.outputs(unit)?;
588 let export_dir = build_runner.files().export_dir();
589 let package_id = unit.pkg.package_id();
590 let manifest_path = PathBuf::from(unit.pkg.manifest_path());
591 let profile = unit.profile.clone();
592 let unit_mode = unit.mode;
593 let features = unit.features.iter().map(|s| s.to_string()).collect();
594 let json_messages = bcx.build_config.emit_json();
595 let executable = build_runner.get_executable(unit)?;
596 let mut target = Target::clone(&unit.target);
597 if let TargetSourcePath::Metabuild = target.src_path() {
598 let path = unit
600 .pkg
601 .manifest()
602 .metabuild_path(build_runner.bcx.ws.build_dir());
603 target.set_src_path(TargetSourcePath::Path(path));
604 }
605
606 Ok(Work::new(move |state| {
607 let mut destinations = vec![];
612 for output in outputs.iter() {
613 let src = &output.path;
614 if !src.exists() {
617 continue;
618 }
619 let Some(dst) = output.hardlink.as_ref() else {
620 destinations.push(src.clone());
621 continue;
622 };
623 destinations.push(dst.clone());
624 paths::link_or_copy(src, dst)?;
625 if let Some(ref path) = output.export_path {
626 let export_dir = export_dir.as_ref().unwrap();
627 paths::create_dir_all(export_dir)?;
628
629 paths::link_or_copy(src, path)?;
630 }
631 }
632
633 if json_messages {
634 let debuginfo = match profile.debuginfo.into_inner() {
635 TomlDebugInfo::None => machine_message::ArtifactDebuginfo::Int(0),
636 TomlDebugInfo::Limited => machine_message::ArtifactDebuginfo::Int(1),
637 TomlDebugInfo::Full => machine_message::ArtifactDebuginfo::Int(2),
638 TomlDebugInfo::LineDirectivesOnly => {
639 machine_message::ArtifactDebuginfo::Named("line-directives-only")
640 }
641 TomlDebugInfo::LineTablesOnly => {
642 machine_message::ArtifactDebuginfo::Named("line-tables-only")
643 }
644 };
645 let art_profile = machine_message::ArtifactProfile {
646 opt_level: profile.opt_level.as_str(),
647 debuginfo: Some(debuginfo),
648 debug_assertions: profile.debug_assertions,
649 overflow_checks: profile.overflow_checks,
650 test: unit_mode.is_any_test(),
651 };
652
653 let msg = machine_message::Artifact {
654 package_id: package_id.to_spec(),
655 manifest_path,
656 target: &target,
657 profile: art_profile,
658 features,
659 filenames: destinations,
660 executable,
661 fresh,
662 }
663 .to_json_string();
664 state.stdout(msg)?;
665 }
666 Ok(())
667 }))
668}
669
670fn add_plugin_deps(
674 rustc: &mut ProcessBuilder,
675 build_script_outputs: &BuildScriptOutputs,
676 build_scripts: &BuildScripts,
677 root_output: &Path,
678) -> CargoResult<()> {
679 let var = paths::dylib_path_envvar();
680 let search_path = rustc.get_env(var).unwrap_or_default();
681 let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>();
682 for (pkg_id, metadata) in &build_scripts.plugins {
683 let output = build_script_outputs
684 .get(*metadata)
685 .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?;
686 search_path.append(&mut filter_dynamic_search_path(
687 output.library_paths.iter().map(AsRef::as_ref),
688 root_output,
689 ));
690 }
691 let search_path = paths::join_paths(&search_path, var)?;
692 rustc.env(var, &search_path);
693 Ok(())
694}
695
696fn get_dynamic_search_path(path: &Path) -> &Path {
697 match path.to_str().and_then(|s| s.split_once("=")) {
698 Some(("native" | "crate" | "dependency" | "framework" | "all", path)) => Path::new(path),
699 _ => path,
700 }
701}
702
703fn filter_dynamic_search_path<'a, I>(paths: I, root_output: &Path) -> Vec<PathBuf>
709where
710 I: Iterator<Item = &'a PathBuf>,
711{
712 let mut search_path = vec![];
713 for dir in paths {
714 let dir = get_dynamic_search_path(dir);
715 if dir.starts_with(&root_output) {
716 search_path.push(dir.to_path_buf());
717 } else {
718 debug!(
719 "Not including path {} in runtime library search path because it is \
720 outside target root {}",
721 dir.display(),
722 root_output.display()
723 );
724 }
725 }
726 search_path
727}
728
729fn prepare_rustc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
736 let gctx = build_runner.bcx.gctx;
737 let is_primary = build_runner.is_primary_package(unit);
738 let is_workspace = build_runner.bcx.ws.is_member(&unit.pkg);
739
740 let mut base = build_runner
741 .compilation
742 .rustc_process(unit, is_primary, is_workspace)?;
743 build_base_args(build_runner, &mut base, unit)?;
744 if unit.pkg.manifest().is_embedded() {
745 if !gctx.cli_unstable().script {
746 anyhow::bail!(
747 "parsing `{}` requires `-Zscript`",
748 unit.pkg.manifest_path().display()
749 );
750 }
751 base.arg("-Z").arg("crate-attr=feature(frontmatter)");
752 }
753
754 base.inherit_jobserver(&build_runner.jobserver);
755 build_deps_args(&mut base, build_runner, unit)?;
756 add_cap_lints(build_runner.bcx, unit, &mut base);
757 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
758 base.args(args);
759 }
760 base.args(&unit.rustflags);
761 if gctx.cli_unstable().binary_dep_depinfo {
762 base.arg("-Z").arg("binary-dep-depinfo");
763 }
764 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
765 base.arg("-Z").arg("checksum-hash-algorithm=blake3");
766 }
767
768 if is_primary {
769 base.env("CARGO_PRIMARY_PACKAGE", "1");
770 let file_list = std::env::join_paths(build_runner.sbom_output_files(unit)?)?;
771 base.env("CARGO_SBOM_PATH", file_list);
772 }
773
774 if unit.target.is_test() || unit.target.is_bench() {
775 let tmp = build_runner.files().layout(unit.kind).prepare_tmp()?;
776 base.env("CARGO_TARGET_TMPDIR", tmp.display().to_string());
777 }
778
779 Ok(base)
780}
781
782fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> {
789 let bcx = build_runner.bcx;
790 let mut rustdoc = build_runner.compilation.rustdoc_process(unit, None)?;
792 if unit.pkg.manifest().is_embedded() {
793 if !bcx.gctx.cli_unstable().script {
794 anyhow::bail!(
795 "parsing `{}` requires `-Zscript`",
796 unit.pkg.manifest_path().display()
797 );
798 }
799 rustdoc.arg("-Z").arg("crate-attr=feature(frontmatter)");
800 }
801 rustdoc.inherit_jobserver(&build_runner.jobserver);
802 let crate_name = unit.target.crate_name();
803 rustdoc.arg("--crate-name").arg(&crate_name);
804 add_path_args(bcx.ws, unit, &mut rustdoc);
805 add_cap_lints(bcx, unit, &mut rustdoc);
806
807 if let CompileKind::Target(target) = unit.kind {
808 rustdoc.arg("--target").arg(target.rustc_target());
809 }
810 let doc_dir = build_runner.files().out_dir(unit);
811 rustdoc.arg("-o").arg(&doc_dir);
812 rustdoc.args(&features_args(unit));
813 rustdoc.args(&check_cfg_args(unit));
814
815 add_error_format_and_color(build_runner, &mut rustdoc);
816 add_allow_features(build_runner, &mut rustdoc);
817
818 if build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo {
819 let mut arg =
822 OsString::from("--emit=toolchain-shared-resources,invocation-specific,dep-info=");
823 arg.push(rustdoc_dep_info_loc(build_runner, unit));
824 rustdoc.arg(arg);
825
826 if build_runner.bcx.gctx.cli_unstable().checksum_freshness {
827 rustdoc.arg("-Z").arg("checksum-hash-algorithm=blake3");
828 }
829
830 rustdoc.arg("-Zunstable-options");
831 }
832
833 if let Some(trim_paths) = unit.profile.trim_paths.as_ref() {
834 trim_paths_args_rustdoc(&mut rustdoc, build_runner, unit, trim_paths)?;
835 }
836
837 rustdoc.args(unit.pkg.manifest().lint_rustflags());
838
839 let metadata = build_runner.metadata_for_doc_units[unit];
840 rustdoc
841 .arg("-C")
842 .arg(format!("metadata={}", metadata.c_metadata()));
843
844 if unit.mode.is_doc_scrape() {
845 debug_assert!(build_runner.bcx.scrape_units.contains(unit));
846
847 if unit.target.is_test() {
848 rustdoc.arg("--scrape-tests");
849 }
850
851 rustdoc.arg("-Zunstable-options");
852
853 rustdoc
854 .arg("--scrape-examples-output-path")
855 .arg(scrape_output_path(build_runner, unit)?);
856
857 for pkg in build_runner.bcx.packages.packages() {
859 let names = pkg
860 .targets()
861 .iter()
862 .map(|target| target.crate_name())
863 .collect::<HashSet<_>>();
864 for name in names {
865 rustdoc.arg("--scrape-examples-target-crate").arg(name);
866 }
867 }
868 }
869
870 if should_include_scrape_units(build_runner.bcx, unit) {
871 rustdoc.arg("-Zunstable-options");
872 }
873
874 build_deps_args(&mut rustdoc, build_runner, unit)?;
875 rustdoc::add_root_urls(build_runner, unit, &mut rustdoc)?;
876
877 rustdoc::add_output_format(build_runner, &mut rustdoc)?;
878
879 if let Some(args) = build_runner.bcx.extra_args_for(unit) {
880 rustdoc.args(args);
881 }
882 rustdoc.args(&unit.rustdocflags);
883
884 if !crate_version_flag_already_present(&rustdoc) {
885 append_crate_version_flag(unit, &mut rustdoc);
886 }
887
888 Ok(rustdoc)
889}
890
891fn rustdoc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Work> {
893 let mut rustdoc = prepare_rustdoc(build_runner, unit)?;
894
895 let crate_name = unit.target.crate_name();
896 let doc_dir = build_runner.files().out_dir(unit);
897 paths::create_dir_all(&doc_dir)?;
901
902 let target_desc = unit.target.description_named();
903 let name = unit.pkg.name();
904 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
905 let package_id = unit.pkg.package_id();
906 let manifest_path = PathBuf::from(unit.pkg.manifest_path());
907 let target = Target::clone(&unit.target);
908
909 let rustdoc_dep_info_loc = rustdoc_dep_info_loc(build_runner, unit);
910 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
911 let build_dir = build_runner.bcx.ws.build_dir().into_path_unlocked();
912 let pkg_root = unit.pkg.root().to_path_buf();
913 let cwd = rustdoc
914 .get_cwd()
915 .unwrap_or_else(|| build_runner.bcx.gctx.cwd())
916 .to_path_buf();
917 let fingerprint_dir = build_runner.files().fingerprint_dir(unit);
918 let is_local = unit.is_local();
919 let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
920 let rustdoc_depinfo_enabled = build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo;
921
922 let mut output_options = OutputOptions::new(build_runner, unit);
923 let script_metadata = build_runner.find_build_script_metadata(unit);
924 let scrape_outputs = if should_include_scrape_units(build_runner.bcx, unit) {
925 Some(
926 build_runner
927 .bcx
928 .scrape_units
929 .iter()
930 .map(|unit| {
931 Ok((
932 build_runner.files().metadata(unit).unit_id(),
933 scrape_output_path(build_runner, unit)?,
934 ))
935 })
936 .collect::<CargoResult<HashMap<_, _>>>()?,
937 )
938 } else {
939 None
940 };
941
942 let failed_scrape_units = Arc::clone(&build_runner.failed_scrape_units);
943 let hide_diagnostics_for_scrape_unit = build_runner.bcx.unit_can_fail_for_docscraping(unit)
944 && !matches!(
945 build_runner.bcx.gctx.shell().verbosity(),
946 Verbosity::Verbose
947 );
948 let failed_scrape_diagnostic = hide_diagnostics_for_scrape_unit.then(|| {
949 make_failed_scrape_diagnostic(
950 build_runner,
951 unit,
952 format_args!("failed to scan {target_desc} in package `{name}` for example code usage"),
953 )
954 });
955 if hide_diagnostics_for_scrape_unit {
956 output_options.show_diagnostics = false;
957 }
958
959 Ok(Work::new(move |state| {
960 add_custom_flags(
961 &mut rustdoc,
962 &build_script_outputs.lock().unwrap(),
963 script_metadata,
964 )?;
965
966 if let Some(scrape_outputs) = scrape_outputs {
971 let failed_scrape_units = failed_scrape_units.lock().unwrap();
972 for (metadata, output_path) in &scrape_outputs {
973 if !failed_scrape_units.contains(metadata) {
974 rustdoc.arg("--with-examples").arg(output_path);
975 }
976 }
977 }
978
979 let crate_dir = doc_dir.join(&crate_name);
980 if crate_dir.exists() {
981 debug!("removing pre-existing doc directory {:?}", crate_dir);
984 paths::remove_dir_all(crate_dir)?;
985 }
986 state.running(&rustdoc);
987 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
988
989 let result = rustdoc
990 .exec_with_streaming(
991 &mut |line| on_stdout_line(state, line, package_id, &target),
992 &mut |line| {
993 on_stderr_line(
994 state,
995 line,
996 package_id,
997 &manifest_path,
998 &target,
999 &mut output_options,
1000 )
1001 },
1002 false,
1003 )
1004 .map_err(verbose_if_simple_exit_code)
1005 .with_context(|| format!("could not document `{}`", name));
1006
1007 if let Err(e) = result {
1008 if let Some(diagnostic) = failed_scrape_diagnostic {
1009 state.warning(diagnostic);
1010 }
1011
1012 return Err(e);
1013 }
1014
1015 if rustdoc_depinfo_enabled && rustdoc_dep_info_loc.exists() {
1016 fingerprint::translate_dep_info(
1017 &rustdoc_dep_info_loc,
1018 &dep_info_loc,
1019 &cwd,
1020 &pkg_root,
1021 &build_dir,
1022 &rustdoc,
1023 is_local,
1025 &env_config,
1026 )
1027 .with_context(|| {
1028 internal(format_args!(
1029 "could not parse/generate dep info at: {}",
1030 rustdoc_dep_info_loc.display()
1031 ))
1032 })?;
1033 paths::set_file_time_no_err(dep_info_loc, timestamp);
1036 }
1037
1038 Ok(())
1039 }))
1040}
1041
1042fn crate_version_flag_already_present(rustdoc: &ProcessBuilder) -> bool {
1045 rustdoc.get_args().any(|flag| {
1046 flag.to_str()
1047 .map_or(false, |flag| flag.starts_with(RUSTDOC_CRATE_VERSION_FLAG))
1048 })
1049}
1050
1051fn append_crate_version_flag(unit: &Unit, rustdoc: &mut ProcessBuilder) {
1052 rustdoc
1053 .arg(RUSTDOC_CRATE_VERSION_FLAG)
1054 .arg(unit.pkg.version().to_string());
1055}
1056
1057fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuilder) {
1061 if !unit.show_warnings(bcx.gctx) {
1064 cmd.arg("--cap-lints").arg("allow");
1065
1066 } else if !unit.is_local() {
1069 cmd.arg("--cap-lints").arg("warn");
1070 }
1071}
1072
1073fn add_allow_features(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1077 if let Some(allow) = &build_runner.bcx.gctx.cli_unstable().allow_features {
1078 use std::fmt::Write;
1079 let mut arg = String::from("-Zallow-features=");
1080 for f in allow {
1081 let _ = write!(&mut arg, "{f},");
1082 }
1083 cmd.arg(arg.trim_end_matches(','));
1084 }
1085}
1086
1087fn add_error_format_and_color(build_runner: &BuildRunner<'_, '_>, cmd: &mut ProcessBuilder) {
1098 cmd.arg("--error-format=json");
1099 let mut json = String::from("--json=diagnostic-rendered-ansi,artifacts,future-incompat");
1100
1101 match build_runner.bcx.build_config.message_format {
1102 MessageFormat::Short | MessageFormat::Json { short: true, .. } => {
1103 json.push_str(",diagnostic-short");
1104 }
1105 _ => {}
1106 }
1107 cmd.arg(json);
1108
1109 let gctx = build_runner.bcx.gctx;
1110 if let Some(width) = gctx.shell().err_width().diagnostic_terminal_width() {
1111 cmd.arg(format!("--diagnostic-width={width}"));
1112 }
1113}
1114
1115fn build_base_args(
1117 build_runner: &BuildRunner<'_, '_>,
1118 cmd: &mut ProcessBuilder,
1119 unit: &Unit,
1120) -> CargoResult<()> {
1121 assert!(!unit.mode.is_run_custom_build());
1122
1123 let bcx = build_runner.bcx;
1124 let Profile {
1125 ref opt_level,
1126 codegen_backend,
1127 codegen_units,
1128 debuginfo,
1129 debug_assertions,
1130 split_debuginfo,
1131 overflow_checks,
1132 rpath,
1133 ref panic,
1134 incremental,
1135 strip,
1136 rustflags: profile_rustflags,
1137 trim_paths,
1138 hint_mostly_unused,
1139 ..
1140 } = unit.profile.clone();
1141 let test = unit.mode.is_any_test();
1142
1143 cmd.arg("--crate-name").arg(&unit.target.crate_name());
1144
1145 let edition = unit.target.edition();
1146 edition.cmd_edition_arg(cmd);
1147
1148 add_path_args(bcx.ws, unit, cmd);
1149 add_error_format_and_color(build_runner, cmd);
1150 add_allow_features(build_runner, cmd);
1151
1152 let mut contains_dy_lib = false;
1153 if !test {
1154 for crate_type in &unit.target.rustc_crate_types() {
1155 cmd.arg("--crate-type").arg(crate_type.as_str());
1156 contains_dy_lib |= crate_type == &CrateType::Dylib;
1157 }
1158 }
1159
1160 if unit.mode.is_check() {
1161 cmd.arg("--emit=dep-info,metadata");
1162 } else if build_runner.bcx.gctx.cli_unstable().no_embed_metadata {
1163 if unit.benefits_from_no_embed_metadata() {
1173 cmd.arg("--emit=dep-info,metadata,link");
1174 cmd.args(&["-Z", "embed-metadata=no"]);
1175 } else {
1176 cmd.arg("--emit=dep-info,link");
1177 }
1178 } else {
1179 if !unit.requires_upstream_objects() {
1183 cmd.arg("--emit=dep-info,metadata,link");
1184 } else {
1185 cmd.arg("--emit=dep-info,link");
1186 }
1187 }
1188
1189 let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
1190 || (contains_dy_lib && !build_runner.is_primary_package(unit));
1191 if prefer_dynamic {
1192 cmd.arg("-C").arg("prefer-dynamic");
1193 }
1194
1195 if opt_level.as_str() != "0" {
1196 cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
1197 }
1198
1199 if *panic != PanicStrategy::Unwind {
1200 cmd.arg("-C").arg(format!("panic={}", panic));
1201 }
1202
1203 cmd.args(<o_args(build_runner, unit));
1204
1205 if let Some(backend) = codegen_backend {
1206 cmd.arg("-Z").arg(&format!("codegen-backend={}", backend));
1207 }
1208
1209 if let Some(n) = codegen_units {
1210 cmd.arg("-C").arg(&format!("codegen-units={}", n));
1211 }
1212
1213 let debuginfo = debuginfo.into_inner();
1214 if debuginfo != TomlDebugInfo::None {
1216 cmd.arg("-C").arg(format!("debuginfo={debuginfo}"));
1217 if let Some(split) = split_debuginfo {
1224 if build_runner
1225 .bcx
1226 .target_data
1227 .info(unit.kind)
1228 .supports_debuginfo_split(split)
1229 {
1230 cmd.arg("-C").arg(format!("split-debuginfo={split}"));
1231 }
1232 }
1233 }
1234
1235 if let Some(trim_paths) = trim_paths {
1236 trim_paths_args(cmd, build_runner, unit, &trim_paths)?;
1237 }
1238
1239 cmd.args(unit.pkg.manifest().lint_rustflags());
1240 cmd.args(&profile_rustflags);
1241
1242 if opt_level.as_str() != "0" {
1246 if debug_assertions {
1247 cmd.args(&["-C", "debug-assertions=on"]);
1248 if !overflow_checks {
1249 cmd.args(&["-C", "overflow-checks=off"]);
1250 }
1251 } else if overflow_checks {
1252 cmd.args(&["-C", "overflow-checks=on"]);
1253 }
1254 } else if !debug_assertions {
1255 cmd.args(&["-C", "debug-assertions=off"]);
1256 if overflow_checks {
1257 cmd.args(&["-C", "overflow-checks=on"]);
1258 }
1259 } else if !overflow_checks {
1260 cmd.args(&["-C", "overflow-checks=off"]);
1261 }
1262
1263 if test && unit.target.harness() {
1264 cmd.arg("--test");
1265
1266 if *panic == PanicStrategy::Abort {
1274 cmd.arg("-Z").arg("panic-abort-tests");
1275 }
1276 } else if test {
1277 cmd.arg("--cfg").arg("test");
1278 }
1279
1280 cmd.args(&features_args(unit));
1281 cmd.args(&check_cfg_args(unit));
1282
1283 let meta = build_runner.files().metadata(unit);
1284 cmd.arg("-C")
1285 .arg(&format!("metadata={}", meta.c_metadata()));
1286 if let Some(c_extra_filename) = meta.c_extra_filename() {
1287 cmd.arg("-C")
1288 .arg(&format!("extra-filename=-{c_extra_filename}"));
1289 }
1290
1291 if rpath {
1292 cmd.arg("-C").arg("rpath");
1293 }
1294
1295 cmd.arg("--out-dir")
1296 .arg(&build_runner.files().out_dir(unit));
1297
1298 fn opt(cmd: &mut ProcessBuilder, key: &str, prefix: &str, val: Option<&OsStr>) {
1299 if let Some(val) = val {
1300 let mut joined = OsString::from(prefix);
1301 joined.push(val);
1302 cmd.arg(key).arg(joined);
1303 }
1304 }
1305
1306 if let CompileKind::Target(n) = unit.kind {
1307 cmd.arg("--target").arg(n.rustc_target());
1308 }
1309
1310 opt(
1311 cmd,
1312 "-C",
1313 "linker=",
1314 build_runner
1315 .compilation
1316 .target_linker(unit.kind)
1317 .as_ref()
1318 .map(|s| s.as_ref()),
1319 );
1320 if incremental {
1321 let dir = build_runner
1322 .files()
1323 .layout(unit.kind)
1324 .incremental()
1325 .as_os_str();
1326 opt(cmd, "-C", "incremental=", Some(dir));
1327 }
1328
1329 if hint_mostly_unused {
1330 if bcx.gctx.cli_unstable().profile_hint_mostly_unused {
1331 cmd.arg("-Zhint-mostly-unused");
1332 } else {
1333 bcx.gctx
1334 .shell()
1335 .warn("ignoring 'hint-mostly-unused' profile option, pass `-Zprofile-hint-mostly-unused` to enable it")?;
1336 }
1337 }
1338
1339 let strip = strip.into_inner();
1340 if strip != StripInner::None {
1341 cmd.arg("-C").arg(format!("strip={}", strip));
1342 }
1343
1344 if unit.is_std {
1345 cmd.arg("-Z")
1351 .arg("force-unstable-if-unmarked")
1352 .env("RUSTC_BOOTSTRAP", "1");
1353 }
1354
1355 if unit.target.is_test() || unit.target.is_bench() {
1357 for bin_target in unit
1358 .pkg
1359 .manifest()
1360 .targets()
1361 .iter()
1362 .filter(|target| target.is_bin())
1363 {
1364 let exe_path = build_runner.files().bin_link_for_target(
1365 bin_target,
1366 unit.kind,
1367 build_runner.bcx,
1368 )?;
1369 let name = bin_target
1370 .binary_filename()
1371 .unwrap_or(bin_target.name().to_string());
1372 let key = format!("CARGO_BIN_EXE_{}", name);
1373 cmd.env(&key, exe_path);
1374 }
1375 }
1376 Ok(())
1377}
1378
1379fn features_args(unit: &Unit) -> Vec<OsString> {
1381 let mut args = Vec::with_capacity(unit.features.len() * 2);
1382
1383 for feat in &unit.features {
1384 args.push(OsString::from("--cfg"));
1385 args.push(OsString::from(format!("feature=\"{}\"", feat)));
1386 }
1387
1388 args
1389}
1390
1391fn trim_paths_args_rustdoc(
1393 cmd: &mut ProcessBuilder,
1394 build_runner: &BuildRunner<'_, '_>,
1395 unit: &Unit,
1396 trim_paths: &TomlTrimPaths,
1397) -> CargoResult<()> {
1398 match trim_paths {
1399 TomlTrimPaths::Values(values) if !values.contains(&TomlTrimPathsValue::Diagnostics) => {
1401 return Ok(())
1402 }
1403 _ => {}
1404 }
1405
1406 cmd.arg("-Zunstable-options");
1408
1409 cmd.arg(package_remap(build_runner, unit));
1412 cmd.arg(build_dir_remap(build_runner));
1413 cmd.arg(sysroot_remap(build_runner, unit));
1414
1415 Ok(())
1416}
1417
1418fn trim_paths_args(
1424 cmd: &mut ProcessBuilder,
1425 build_runner: &BuildRunner<'_, '_>,
1426 unit: &Unit,
1427 trim_paths: &TomlTrimPaths,
1428) -> CargoResult<()> {
1429 if trim_paths.is_none() {
1430 return Ok(());
1431 }
1432
1433 cmd.arg("-Zunstable-options");
1435 cmd.arg(format!("-Zremap-path-scope={trim_paths}"));
1436
1437 cmd.arg(package_remap(build_runner, unit));
1440 cmd.arg(build_dir_remap(build_runner));
1441 cmd.arg(sysroot_remap(build_runner, unit));
1442
1443 Ok(())
1444}
1445
1446fn sysroot_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1451 let mut remap = OsString::from("--remap-path-prefix=");
1452 remap.push({
1453 let mut sysroot = build_runner.bcx.target_data.info(unit.kind).sysroot.clone();
1455 sysroot.push("lib");
1456 sysroot.push("rustlib");
1457 sysroot.push("src");
1458 sysroot.push("rust");
1459 sysroot
1460 });
1461 remap.push("=");
1462 remap.push("/rustc/");
1463 if let Some(commit_hash) = build_runner.bcx.rustc().commit_hash.as_ref() {
1464 remap.push(commit_hash);
1465 } else {
1466 remap.push(build_runner.bcx.rustc().version.to_string());
1467 }
1468 remap
1469}
1470
1471fn package_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1479 let pkg_root = unit.pkg.root();
1480 let ws_root = build_runner.bcx.ws.root();
1481 let mut remap = OsString::from("--remap-path-prefix=");
1482 let source_id = unit.pkg.package_id().source_id();
1483 if source_id.is_git() {
1484 remap.push(
1485 build_runner
1486 .bcx
1487 .gctx
1488 .git_checkouts_path()
1489 .as_path_unlocked(),
1490 );
1491 remap.push("=");
1492 } else if source_id.is_registry() {
1493 remap.push(
1494 build_runner
1495 .bcx
1496 .gctx
1497 .registry_source_path()
1498 .as_path_unlocked(),
1499 );
1500 remap.push("=");
1501 } else if pkg_root.strip_prefix(ws_root).is_ok() {
1502 remap.push(ws_root);
1503 remap.push("=."); } else {
1505 remap.push(pkg_root);
1506 remap.push("=");
1507 remap.push(unit.pkg.name());
1508 remap.push("-");
1509 remap.push(unit.pkg.version().to_string());
1510 }
1511 remap
1512}
1513
1514fn build_dir_remap(build_runner: &BuildRunner<'_, '_>) -> OsString {
1527 let build_dir = build_runner.bcx.ws.build_dir();
1528 let mut remap = OsString::from("--remap-path-prefix=");
1529 remap.push(build_dir.as_path_unlocked());
1530 remap.push("=/cargo/build-dir");
1531 remap
1532}
1533
1534fn check_cfg_args(unit: &Unit) -> Vec<OsString> {
1536 let gross_cap_estimation = unit.pkg.summary().features().len() * 7 + 25;
1554 let mut arg_feature = OsString::with_capacity(gross_cap_estimation);
1555
1556 arg_feature.push("cfg(feature, values(");
1557 for (i, feature) in unit.pkg.summary().features().keys().enumerate() {
1558 if i != 0 {
1559 arg_feature.push(", ");
1560 }
1561 arg_feature.push("\"");
1562 arg_feature.push(feature);
1563 arg_feature.push("\"");
1564 }
1565 arg_feature.push("))");
1566
1567 vec![
1576 OsString::from("--check-cfg"),
1577 OsString::from("cfg(docsrs,test)"),
1578 OsString::from("--check-cfg"),
1579 arg_feature,
1580 ]
1581}
1582
1583fn lto_args(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> Vec<OsString> {
1585 let mut result = Vec::new();
1586 let mut push = |arg: &str| {
1587 result.push(OsString::from("-C"));
1588 result.push(OsString::from(arg));
1589 };
1590 match build_runner.lto[unit] {
1591 lto::Lto::Run(None) => push("lto"),
1592 lto::Lto::Run(Some(s)) => push(&format!("lto={}", s)),
1593 lto::Lto::Off => {
1594 push("lto=off");
1595 push("embed-bitcode=no");
1596 }
1597 lto::Lto::ObjectAndBitcode => {} lto::Lto::OnlyBitcode => push("linker-plugin-lto"),
1599 lto::Lto::OnlyObject => push("embed-bitcode=no"),
1600 }
1601 result
1602}
1603
1604fn build_deps_args(
1610 cmd: &mut ProcessBuilder,
1611 build_runner: &BuildRunner<'_, '_>,
1612 unit: &Unit,
1613) -> CargoResult<()> {
1614 let bcx = build_runner.bcx;
1615 cmd.arg("-L").arg(&{
1616 let mut deps = OsString::from("dependency=");
1617 deps.push(build_runner.files().deps_dir(unit));
1618 deps
1619 });
1620
1621 if !unit.kind.is_host() {
1624 cmd.arg("-L").arg(&{
1625 let mut deps = OsString::from("dependency=");
1626 deps.push(build_runner.files().host_deps());
1627 deps
1628 });
1629 }
1630
1631 let deps = build_runner.unit_deps(unit);
1632
1633 if !deps
1637 .iter()
1638 .any(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_linkable())
1639 {
1640 if let Some(dep) = deps.iter().find(|dep| {
1641 !dep.unit.mode.is_doc() && dep.unit.target.is_lib() && !dep.unit.artifact.is_true()
1642 }) {
1643 bcx.gctx.shell().warn(format!(
1644 "The package `{}` \
1645 provides no linkable target. The compiler might raise an error while compiling \
1646 `{}`. Consider adding 'dylib' or 'rlib' to key `crate-type` in `{}`'s \
1647 Cargo.toml. This warning might turn into a hard error in the future.",
1648 dep.unit.target.crate_name(),
1649 unit.target.crate_name(),
1650 dep.unit.target.crate_name()
1651 ))?;
1652 }
1653 }
1654
1655 let mut unstable_opts = false;
1656
1657 for dep in deps {
1658 if dep.unit.mode.is_run_custom_build() {
1659 cmd.env(
1660 "OUT_DIR",
1661 &build_runner.files().build_script_out_dir(&dep.unit),
1662 );
1663 }
1664 }
1665
1666 for arg in extern_args(build_runner, unit, &mut unstable_opts)? {
1667 cmd.arg(arg);
1668 }
1669
1670 for (var, env) in artifact::get_env(build_runner, deps)? {
1671 cmd.env(&var, env);
1672 }
1673
1674 if unstable_opts {
1677 cmd.arg("-Z").arg("unstable-options");
1678 }
1679
1680 Ok(())
1681}
1682
1683fn add_custom_flags(
1687 cmd: &mut ProcessBuilder,
1688 build_script_outputs: &BuildScriptOutputs,
1689 metadata: Option<UnitHash>,
1690) -> CargoResult<()> {
1691 if let Some(metadata) = metadata {
1692 if let Some(output) = build_script_outputs.get(metadata) {
1693 for cfg in output.cfgs.iter() {
1694 cmd.arg("--cfg").arg(cfg);
1695 }
1696 for check_cfg in &output.check_cfgs {
1697 cmd.arg("--check-cfg").arg(check_cfg);
1698 }
1699 for (name, value) in output.env.iter() {
1700 cmd.env(name, value);
1701 }
1702 }
1703 }
1704
1705 Ok(())
1706}
1707
1708pub fn extern_args(
1710 build_runner: &BuildRunner<'_, '_>,
1711 unit: &Unit,
1712 unstable_opts: &mut bool,
1713) -> CargoResult<Vec<OsString>> {
1714 let mut result = Vec::new();
1715 let deps = build_runner.unit_deps(unit);
1716
1717 let no_embed_metadata = build_runner.bcx.gctx.cli_unstable().no_embed_metadata;
1718
1719 let mut link_to =
1721 |dep: &UnitDep, extern_crate_name: InternedString, noprelude: bool| -> CargoResult<()> {
1722 let mut value = OsString::new();
1723 let mut opts = Vec::new();
1724 let is_public_dependency_enabled = unit
1725 .pkg
1726 .manifest()
1727 .unstable_features()
1728 .require(Feature::public_dependency())
1729 .is_ok()
1730 || build_runner.bcx.gctx.cli_unstable().public_dependency;
1731 if !dep.public && unit.target.is_lib() && is_public_dependency_enabled {
1732 opts.push("priv");
1733 *unstable_opts = true;
1734 }
1735 if noprelude {
1736 opts.push("noprelude");
1737 *unstable_opts = true;
1738 }
1739 if !opts.is_empty() {
1740 value.push(opts.join(","));
1741 value.push(":");
1742 }
1743 value.push(extern_crate_name.as_str());
1744 value.push("=");
1745
1746 let mut pass = |file| {
1747 let mut value = value.clone();
1748 value.push(file);
1749 result.push(OsString::from("--extern"));
1750 result.push(value);
1751 };
1752
1753 let outputs = build_runner.outputs(&dep.unit)?;
1754
1755 if build_runner.only_requires_rmeta(unit, &dep.unit) || dep.unit.mode.is_check() {
1756 let output = outputs
1758 .iter()
1759 .find(|output| output.flavor == FileFlavor::Rmeta)
1760 .expect("failed to find rmeta dep for pipelined dep");
1761 pass(&output.path);
1762 } else {
1763 for output in outputs.iter() {
1765 if output.flavor == FileFlavor::Linkable {
1766 pass(&output.path);
1767 }
1768 else if no_embed_metadata && output.flavor == FileFlavor::Rmeta {
1772 pass(&output.path);
1773 }
1774 }
1775 }
1776 Ok(())
1777 };
1778
1779 for dep in deps {
1780 if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
1781 link_to(dep, dep.extern_crate_name, dep.noprelude)?;
1782 }
1783 }
1784 if unit.target.proc_macro() {
1785 result.push(OsString::from("--extern"));
1787 result.push(OsString::from("proc_macro"));
1788 }
1789
1790 Ok(result)
1791}
1792
1793fn envify(s: &str) -> String {
1794 s.chars()
1795 .flat_map(|c| c.to_uppercase())
1796 .map(|c| if c == '-' { '_' } else { c })
1797 .collect()
1798}
1799
1800struct OutputOptions {
1803 format: MessageFormat,
1805 cache_cell: Option<(PathBuf, LazyCell<File>)>,
1810 show_diagnostics: bool,
1818 warnings_seen: usize,
1820 errors_seen: usize,
1822}
1823
1824impl OutputOptions {
1825 fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions {
1826 let path = build_runner.files().message_cache_path(unit);
1827 drop(fs::remove_file(&path));
1829 let cache_cell = Some((path, LazyCell::new()));
1830 let show_diagnostics =
1831 build_runner.bcx.gctx.warning_handling().unwrap_or_default() != WarningHandling::Allow;
1832 OutputOptions {
1833 format: build_runner.bcx.build_config.message_format,
1834 cache_cell,
1835 show_diagnostics,
1836 warnings_seen: 0,
1837 errors_seen: 0,
1838 }
1839 }
1840}
1841
1842fn on_stdout_line(
1843 state: &JobState<'_, '_>,
1844 line: &str,
1845 _package_id: PackageId,
1846 _target: &Target,
1847) -> CargoResult<()> {
1848 state.stdout(line.to_string())?;
1849 Ok(())
1850}
1851
1852fn on_stderr_line(
1853 state: &JobState<'_, '_>,
1854 line: &str,
1855 package_id: PackageId,
1856 manifest_path: &std::path::Path,
1857 target: &Target,
1858 options: &mut OutputOptions,
1859) -> CargoResult<()> {
1860 if on_stderr_line_inner(state, line, package_id, manifest_path, target, options)? {
1861 if let Some((path, cell)) = &mut options.cache_cell {
1863 let f = cell.try_borrow_mut_with(|| paths::create(path))?;
1865 debug_assert!(!line.contains('\n'));
1866 f.write_all(line.as_bytes())?;
1867 f.write_all(&[b'\n'])?;
1868 }
1869 }
1870 Ok(())
1871}
1872
1873fn on_stderr_line_inner(
1875 state: &JobState<'_, '_>,
1876 line: &str,
1877 package_id: PackageId,
1878 manifest_path: &std::path::Path,
1879 target: &Target,
1880 options: &mut OutputOptions,
1881) -> CargoResult<bool> {
1882 if !line.starts_with('{') {
1888 state.stderr(line.to_string())?;
1889 return Ok(true);
1890 }
1891
1892 let mut compiler_message: Box<serde_json::value::RawValue> = match serde_json::from_str(line) {
1893 Ok(msg) => msg,
1894
1895 Err(e) => {
1899 debug!("failed to parse json: {:?}", e);
1900 state.stderr(line.to_string())?;
1901 return Ok(true);
1902 }
1903 };
1904
1905 let count_diagnostic = |level, options: &mut OutputOptions| {
1906 if level == "warning" {
1907 options.warnings_seen += 1;
1908 } else if level == "error" {
1909 options.errors_seen += 1;
1910 }
1911 };
1912
1913 if let Ok(report) = serde_json::from_str::<FutureIncompatReport>(compiler_message.get()) {
1914 for item in &report.future_incompat_report {
1915 count_diagnostic(&*item.diagnostic.level, options);
1916 }
1917 state.future_incompat_report(report.future_incompat_report);
1918 return Ok(true);
1919 }
1920
1921 match options.format {
1924 MessageFormat::Human
1929 | MessageFormat::Short
1930 | MessageFormat::Json {
1931 render_diagnostics: true,
1932 ..
1933 } => {
1934 #[derive(serde::Deserialize)]
1935 struct CompilerMessage<'a> {
1936 rendered: String,
1940 #[serde(borrow)]
1941 message: Cow<'a, str>,
1942 #[serde(borrow)]
1943 level: Cow<'a, str>,
1944 children: Vec<PartialDiagnostic>,
1945 }
1946
1947 #[derive(serde::Deserialize)]
1956 struct PartialDiagnostic {
1957 spans: Vec<PartialDiagnosticSpan>,
1958 }
1959
1960 #[derive(serde::Deserialize)]
1962 struct PartialDiagnosticSpan {
1963 suggestion_applicability: Option<Applicability>,
1964 }
1965
1966 if let Ok(mut msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
1967 {
1968 if msg.message.starts_with("aborting due to")
1969 || msg.message.ends_with("warning emitted")
1970 || msg.message.ends_with("warnings emitted")
1971 {
1972 return Ok(true);
1974 }
1975 if msg.rendered.ends_with('\n') {
1977 msg.rendered.pop();
1978 }
1979 let rendered = msg.rendered;
1980 if options.show_diagnostics {
1981 let machine_applicable: bool = msg
1982 .children
1983 .iter()
1984 .map(|child| {
1985 child
1986 .spans
1987 .iter()
1988 .filter_map(|span| span.suggestion_applicability)
1989 .any(|app| app == Applicability::MachineApplicable)
1990 })
1991 .any(|b| b);
1992 count_diagnostic(&msg.level, options);
1993 state.emit_diag(&msg.level, rendered, machine_applicable)?;
1994 }
1995 return Ok(true);
1996 }
1997 }
1998
1999 MessageFormat::Json { ansi: false, .. } => {
2003 #[derive(serde::Deserialize, serde::Serialize)]
2004 struct CompilerMessage<'a> {
2005 rendered: String,
2006 #[serde(flatten, borrow)]
2007 other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2008 }
2009 if let Ok(mut error) =
2010 serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2011 {
2012 error.rendered = anstream::adapter::strip_str(&error.rendered).to_string();
2013 let new_line = serde_json::to_string(&error)?;
2014 compiler_message = serde_json::value::RawValue::from_string(new_line)?;
2015 }
2016 }
2017
2018 MessageFormat::Json { ansi: true, .. } => {}
2021 }
2022
2023 #[derive(serde::Deserialize)]
2030 struct ArtifactNotification<'a> {
2031 #[serde(borrow)]
2032 artifact: Cow<'a, str>,
2033 }
2034
2035 if let Ok(artifact) = serde_json::from_str::<ArtifactNotification<'_>>(compiler_message.get()) {
2036 trace!("found directive from rustc: `{}`", artifact.artifact);
2037 if artifact.artifact.ends_with(".rmeta") {
2038 debug!("looks like metadata finished early!");
2039 state.rmeta_produced();
2040 }
2041 return Ok(false);
2042 }
2043
2044 if !options.show_diagnostics {
2049 return Ok(true);
2050 }
2051
2052 #[derive(serde::Deserialize)]
2053 struct CompilerMessage<'a> {
2054 #[serde(borrow)]
2055 message: Cow<'a, str>,
2056 #[serde(borrow)]
2057 level: Cow<'a, str>,
2058 }
2059
2060 if let Ok(msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get()) {
2061 if msg.message.starts_with("aborting due to")
2062 || msg.message.ends_with("warning emitted")
2063 || msg.message.ends_with("warnings emitted")
2064 {
2065 return Ok(true);
2067 }
2068 count_diagnostic(&msg.level, options);
2069 }
2070
2071 let msg = machine_message::FromCompiler {
2072 package_id: package_id.to_spec(),
2073 manifest_path,
2074 target,
2075 message: compiler_message,
2076 }
2077 .to_json_string();
2078
2079 state.stdout(msg)?;
2083 Ok(true)
2084}
2085
2086fn replay_output_cache(
2090 package_id: PackageId,
2091 manifest_path: PathBuf,
2092 target: &Target,
2093 path: PathBuf,
2094 format: MessageFormat,
2095 show_diagnostics: bool,
2096) -> Work {
2097 let target = target.clone();
2098 let mut options = OutputOptions {
2099 format,
2100 cache_cell: None,
2101 show_diagnostics,
2102 warnings_seen: 0,
2103 errors_seen: 0,
2104 };
2105 Work::new(move |state| {
2106 if !path.exists() {
2107 return Ok(());
2109 }
2110 let file = paths::open(&path)?;
2114 let mut reader = std::io::BufReader::new(file);
2115 let mut line = String::new();
2116 loop {
2117 let length = reader.read_line(&mut line)?;
2118 if length == 0 {
2119 break;
2120 }
2121 let trimmed = line.trim_end_matches(&['\n', '\r'][..]);
2122 on_stderr_line(
2123 state,
2124 trimmed,
2125 package_id,
2126 &manifest_path,
2127 &target,
2128 &mut options,
2129 )?;
2130 line.clear();
2131 }
2132 Ok(())
2133 })
2134}
2135
2136fn descriptive_pkg_name(name: &str, target: &Target, mode: &CompileMode) -> String {
2139 let desc_name = target.description_named();
2140 let mode = if mode.is_rustc_test() && !(target.is_test() || target.is_bench()) {
2141 " test"
2142 } else if mode.is_doc_test() {
2143 " doctest"
2144 } else if mode.is_doc() {
2145 " doc"
2146 } else {
2147 ""
2148 };
2149 format!("`{name}` ({desc_name}{mode})")
2150}
2151
2152pub(crate) fn apply_env_config(
2154 gctx: &crate::GlobalContext,
2155 cmd: &mut ProcessBuilder,
2156) -> CargoResult<()> {
2157 for (key, value) in gctx.env_config()?.iter() {
2158 if cmd.get_envs().contains_key(key) {
2160 continue;
2161 }
2162 cmd.env(key, value);
2163 }
2164 Ok(())
2165}
2166
2167fn should_include_scrape_units(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
2169 unit.mode.is_doc() && bcx.scrape_units.len() > 0 && bcx.ws.unit_needs_doc_scrape(unit)
2170}
2171
2172fn scrape_output_path(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<PathBuf> {
2174 assert!(unit.mode.is_doc() || unit.mode.is_doc_scrape());
2175 build_runner
2176 .outputs(unit)
2177 .map(|outputs| outputs[0].path.clone())
2178}
2179
2180fn rustdoc_dep_info_loc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> PathBuf {
2182 let mut loc = build_runner.files().fingerprint_file_path(unit, "");
2183 loc.set_extension("d");
2184 loc
2185}