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::{ProcessBuilder, ProcessError, paths};
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_metadatas = build_runner.find_build_script_metadatas(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_metadatas)?;
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_metadatas = build_runner.find_build_script_metadatas(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_metadatas,
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: profile_hint_mostly_unused,
1139 ..
1140 } = unit.profile.clone();
1141 let hints = unit.pkg.hints().cloned().unwrap_or_default();
1142 let test = unit.mode.is_any_test();
1143
1144 let warn = |msg: &str| {
1145 bcx.gctx.shell().warn(format!(
1146 "{}@{}: {msg}",
1147 unit.pkg.package_id().name(),
1148 unit.pkg.package_id().version()
1149 ))
1150 };
1151 let unit_capped_warn = |msg: &str| {
1152 if unit.show_warnings(bcx.gctx) {
1153 warn(msg)
1154 } else {
1155 Ok(())
1156 }
1157 };
1158
1159 cmd.arg("--crate-name").arg(&unit.target.crate_name());
1160
1161 let edition = unit.target.edition();
1162 edition.cmd_edition_arg(cmd);
1163
1164 add_path_args(bcx.ws, unit, cmd);
1165 add_error_format_and_color(build_runner, cmd);
1166 add_allow_features(build_runner, cmd);
1167
1168 let mut contains_dy_lib = false;
1169 if !test {
1170 for crate_type in &unit.target.rustc_crate_types() {
1171 cmd.arg("--crate-type").arg(crate_type.as_str());
1172 contains_dy_lib |= crate_type == &CrateType::Dylib;
1173 }
1174 }
1175
1176 if unit.mode.is_check() {
1177 cmd.arg("--emit=dep-info,metadata");
1178 } else if build_runner.bcx.gctx.cli_unstable().no_embed_metadata {
1179 if unit.benefits_from_no_embed_metadata() {
1189 cmd.arg("--emit=dep-info,metadata,link");
1190 cmd.args(&["-Z", "embed-metadata=no"]);
1191 } else {
1192 cmd.arg("--emit=dep-info,link");
1193 }
1194 } else {
1195 if !unit.requires_upstream_objects() {
1199 cmd.arg("--emit=dep-info,metadata,link");
1200 } else {
1201 cmd.arg("--emit=dep-info,link");
1202 }
1203 }
1204
1205 let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
1206 || (contains_dy_lib && !build_runner.is_primary_package(unit));
1207 if prefer_dynamic {
1208 cmd.arg("-C").arg("prefer-dynamic");
1209 }
1210
1211 if opt_level.as_str() != "0" {
1212 cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
1213 }
1214
1215 if *panic != PanicStrategy::Unwind {
1216 cmd.arg("-C").arg(format!("panic={}", panic));
1217 }
1218
1219 cmd.args(<o_args(build_runner, unit));
1220
1221 if let Some(backend) = codegen_backend {
1222 cmd.arg("-Z").arg(&format!("codegen-backend={}", backend));
1223 }
1224
1225 if let Some(n) = codegen_units {
1226 cmd.arg("-C").arg(&format!("codegen-units={}", n));
1227 }
1228
1229 let debuginfo = debuginfo.into_inner();
1230 if debuginfo != TomlDebugInfo::None {
1232 cmd.arg("-C").arg(format!("debuginfo={debuginfo}"));
1233 if let Some(split) = split_debuginfo {
1240 if build_runner
1241 .bcx
1242 .target_data
1243 .info(unit.kind)
1244 .supports_debuginfo_split(split)
1245 {
1246 cmd.arg("-C").arg(format!("split-debuginfo={split}"));
1247 }
1248 }
1249 }
1250
1251 if let Some(trim_paths) = trim_paths {
1252 trim_paths_args(cmd, build_runner, unit, &trim_paths)?;
1253 }
1254
1255 cmd.args(unit.pkg.manifest().lint_rustflags());
1256 cmd.args(&profile_rustflags);
1257
1258 if opt_level.as_str() != "0" {
1262 if debug_assertions {
1263 cmd.args(&["-C", "debug-assertions=on"]);
1264 if !overflow_checks {
1265 cmd.args(&["-C", "overflow-checks=off"]);
1266 }
1267 } else if overflow_checks {
1268 cmd.args(&["-C", "overflow-checks=on"]);
1269 }
1270 } else if !debug_assertions {
1271 cmd.args(&["-C", "debug-assertions=off"]);
1272 if overflow_checks {
1273 cmd.args(&["-C", "overflow-checks=on"]);
1274 }
1275 } else if !overflow_checks {
1276 cmd.args(&["-C", "overflow-checks=off"]);
1277 }
1278
1279 if test && unit.target.harness() {
1280 cmd.arg("--test");
1281
1282 if *panic == PanicStrategy::Abort {
1290 cmd.arg("-Z").arg("panic-abort-tests");
1291 }
1292 } else if test {
1293 cmd.arg("--cfg").arg("test");
1294 }
1295
1296 cmd.args(&features_args(unit));
1297 cmd.args(&check_cfg_args(unit));
1298
1299 let meta = build_runner.files().metadata(unit);
1300 cmd.arg("-C")
1301 .arg(&format!("metadata={}", meta.c_metadata()));
1302 if let Some(c_extra_filename) = meta.c_extra_filename() {
1303 cmd.arg("-C")
1304 .arg(&format!("extra-filename=-{c_extra_filename}"));
1305 }
1306
1307 if rpath {
1308 cmd.arg("-C").arg("rpath");
1309 }
1310
1311 cmd.arg("--out-dir")
1312 .arg(&build_runner.files().out_dir(unit));
1313
1314 fn opt(cmd: &mut ProcessBuilder, key: &str, prefix: &str, val: Option<&OsStr>) {
1315 if let Some(val) = val {
1316 let mut joined = OsString::from(prefix);
1317 joined.push(val);
1318 cmd.arg(key).arg(joined);
1319 }
1320 }
1321
1322 if let CompileKind::Target(n) = unit.kind {
1323 cmd.arg("--target").arg(n.rustc_target());
1324 }
1325
1326 opt(
1327 cmd,
1328 "-C",
1329 "linker=",
1330 build_runner
1331 .compilation
1332 .target_linker(unit.kind)
1333 .as_ref()
1334 .map(|s| s.as_ref()),
1335 );
1336 if incremental {
1337 let dir = build_runner
1338 .files()
1339 .layout(unit.kind)
1340 .incremental()
1341 .as_os_str();
1342 opt(cmd, "-C", "incremental=", Some(dir));
1343 }
1344
1345 let pkg_hint_mostly_unused = match hints.mostly_unused {
1346 None => None,
1347 Some(toml::Value::Boolean(b)) => Some(b),
1348 Some(v) => {
1349 unit_capped_warn(&format!(
1350 "ignoring unsupported value type ({}) for 'hints.mostly-unused', which expects a boolean",
1351 v.type_str()
1352 ))?;
1353 None
1354 }
1355 };
1356 if profile_hint_mostly_unused
1357 .or(pkg_hint_mostly_unused)
1358 .unwrap_or(false)
1359 {
1360 if bcx.gctx.cli_unstable().profile_hint_mostly_unused {
1361 cmd.arg("-Zhint-mostly-unused");
1362 } else {
1363 if profile_hint_mostly_unused.is_some() {
1364 warn(
1366 "ignoring 'hint-mostly-unused' profile option, pass `-Zprofile-hint-mostly-unused` to enable it",
1367 )?;
1368 } else if pkg_hint_mostly_unused.is_some() {
1369 unit_capped_warn(
1370 "ignoring 'hints.mostly-unused', pass `-Zprofile-hint-mostly-unused` to enable it",
1371 )?;
1372 }
1373 }
1374 }
1375
1376 let strip = strip.into_inner();
1377 if strip != StripInner::None {
1378 cmd.arg("-C").arg(format!("strip={}", strip));
1379 }
1380
1381 if unit.is_std {
1382 cmd.arg("-Z")
1388 .arg("force-unstable-if-unmarked")
1389 .env("RUSTC_BOOTSTRAP", "1");
1390 }
1391
1392 if unit.target.is_test() || unit.target.is_bench() {
1394 for bin_target in unit
1395 .pkg
1396 .manifest()
1397 .targets()
1398 .iter()
1399 .filter(|target| target.is_bin())
1400 {
1401 let exe_path = build_runner.files().bin_link_for_target(
1402 bin_target,
1403 unit.kind,
1404 build_runner.bcx,
1405 )?;
1406 let name = bin_target
1407 .binary_filename()
1408 .unwrap_or(bin_target.name().to_string());
1409 let key = format!("CARGO_BIN_EXE_{}", name);
1410 cmd.env(&key, exe_path);
1411 }
1412 }
1413 Ok(())
1414}
1415
1416fn features_args(unit: &Unit) -> Vec<OsString> {
1418 let mut args = Vec::with_capacity(unit.features.len() * 2);
1419
1420 for feat in &unit.features {
1421 args.push(OsString::from("--cfg"));
1422 args.push(OsString::from(format!("feature=\"{}\"", feat)));
1423 }
1424
1425 args
1426}
1427
1428fn trim_paths_args_rustdoc(
1430 cmd: &mut ProcessBuilder,
1431 build_runner: &BuildRunner<'_, '_>,
1432 unit: &Unit,
1433 trim_paths: &TomlTrimPaths,
1434) -> CargoResult<()> {
1435 match trim_paths {
1436 TomlTrimPaths::Values(values) if !values.contains(&TomlTrimPathsValue::Diagnostics) => {
1438 return Ok(());
1439 }
1440 _ => {}
1441 }
1442
1443 cmd.arg("-Zunstable-options");
1445
1446 cmd.arg(package_remap(build_runner, unit));
1449 cmd.arg(build_dir_remap(build_runner));
1450 cmd.arg(sysroot_remap(build_runner, unit));
1451
1452 Ok(())
1453}
1454
1455fn trim_paths_args(
1461 cmd: &mut ProcessBuilder,
1462 build_runner: &BuildRunner<'_, '_>,
1463 unit: &Unit,
1464 trim_paths: &TomlTrimPaths,
1465) -> CargoResult<()> {
1466 if trim_paths.is_none() {
1467 return Ok(());
1468 }
1469
1470 cmd.arg("-Zunstable-options");
1472 cmd.arg(format!("-Zremap-path-scope={trim_paths}"));
1473
1474 cmd.arg(package_remap(build_runner, unit));
1477 cmd.arg(build_dir_remap(build_runner));
1478 cmd.arg(sysroot_remap(build_runner, unit));
1479
1480 Ok(())
1481}
1482
1483fn sysroot_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1488 let mut remap = OsString::from("--remap-path-prefix=");
1489 remap.push({
1490 let mut sysroot = build_runner.bcx.target_data.info(unit.kind).sysroot.clone();
1492 sysroot.push("lib");
1493 sysroot.push("rustlib");
1494 sysroot.push("src");
1495 sysroot.push("rust");
1496 sysroot
1497 });
1498 remap.push("=");
1499 remap.push("/rustc/");
1500 if let Some(commit_hash) = build_runner.bcx.rustc().commit_hash.as_ref() {
1501 remap.push(commit_hash);
1502 } else {
1503 remap.push(build_runner.bcx.rustc().version.to_string());
1504 }
1505 remap
1506}
1507
1508fn package_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString {
1516 let pkg_root = unit.pkg.root();
1517 let ws_root = build_runner.bcx.ws.root();
1518 let mut remap = OsString::from("--remap-path-prefix=");
1519 let source_id = unit.pkg.package_id().source_id();
1520 if source_id.is_git() {
1521 remap.push(
1522 build_runner
1523 .bcx
1524 .gctx
1525 .git_checkouts_path()
1526 .as_path_unlocked(),
1527 );
1528 remap.push("=");
1529 } else if source_id.is_registry() {
1530 remap.push(
1531 build_runner
1532 .bcx
1533 .gctx
1534 .registry_source_path()
1535 .as_path_unlocked(),
1536 );
1537 remap.push("=");
1538 } else if pkg_root.strip_prefix(ws_root).is_ok() {
1539 remap.push(ws_root);
1540 remap.push("=."); } else {
1542 remap.push(pkg_root);
1543 remap.push("=");
1544 remap.push(unit.pkg.name());
1545 remap.push("-");
1546 remap.push(unit.pkg.version().to_string());
1547 }
1548 remap
1549}
1550
1551fn build_dir_remap(build_runner: &BuildRunner<'_, '_>) -> OsString {
1564 let build_dir = build_runner.bcx.ws.build_dir();
1565 let mut remap = OsString::from("--remap-path-prefix=");
1566 remap.push(build_dir.as_path_unlocked());
1567 remap.push("=/cargo/build-dir");
1568 remap
1569}
1570
1571fn check_cfg_args(unit: &Unit) -> Vec<OsString> {
1573 let gross_cap_estimation = unit.pkg.summary().features().len() * 7 + 25;
1591 let mut arg_feature = OsString::with_capacity(gross_cap_estimation);
1592
1593 arg_feature.push("cfg(feature, values(");
1594 for (i, feature) in unit.pkg.summary().features().keys().enumerate() {
1595 if i != 0 {
1596 arg_feature.push(", ");
1597 }
1598 arg_feature.push("\"");
1599 arg_feature.push(feature);
1600 arg_feature.push("\"");
1601 }
1602 arg_feature.push("))");
1603
1604 vec![
1613 OsString::from("--check-cfg"),
1614 OsString::from("cfg(docsrs,test)"),
1615 OsString::from("--check-cfg"),
1616 arg_feature,
1617 ]
1618}
1619
1620fn lto_args(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> Vec<OsString> {
1622 let mut result = Vec::new();
1623 let mut push = |arg: &str| {
1624 result.push(OsString::from("-C"));
1625 result.push(OsString::from(arg));
1626 };
1627 match build_runner.lto[unit] {
1628 lto::Lto::Run(None) => push("lto"),
1629 lto::Lto::Run(Some(s)) => push(&format!("lto={}", s)),
1630 lto::Lto::Off => {
1631 push("lto=off");
1632 push("embed-bitcode=no");
1633 }
1634 lto::Lto::ObjectAndBitcode => {} lto::Lto::OnlyBitcode => push("linker-plugin-lto"),
1636 lto::Lto::OnlyObject => push("embed-bitcode=no"),
1637 }
1638 result
1639}
1640
1641fn build_deps_args(
1647 cmd: &mut ProcessBuilder,
1648 build_runner: &BuildRunner<'_, '_>,
1649 unit: &Unit,
1650) -> CargoResult<()> {
1651 let bcx = build_runner.bcx;
1652 cmd.arg("-L").arg(&{
1653 let mut deps = OsString::from("dependency=");
1654 deps.push(build_runner.files().deps_dir(unit));
1655 deps
1656 });
1657
1658 if !unit.kind.is_host() {
1661 cmd.arg("-L").arg(&{
1662 let mut deps = OsString::from("dependency=");
1663 deps.push(build_runner.files().host_deps());
1664 deps
1665 });
1666 }
1667
1668 let deps = build_runner.unit_deps(unit);
1669
1670 if !deps
1674 .iter()
1675 .any(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_linkable())
1676 {
1677 if let Some(dep) = deps.iter().find(|dep| {
1678 !dep.unit.mode.is_doc() && dep.unit.target.is_lib() && !dep.unit.artifact.is_true()
1679 }) {
1680 bcx.gctx.shell().warn(format!(
1681 "The package `{}` \
1682 provides no linkable target. The compiler might raise an error while compiling \
1683 `{}`. Consider adding 'dylib' or 'rlib' to key `crate-type` in `{}`'s \
1684 Cargo.toml. This warning might turn into a hard error in the future.",
1685 dep.unit.target.crate_name(),
1686 unit.target.crate_name(),
1687 dep.unit.target.crate_name()
1688 ))?;
1689 }
1690 }
1691
1692 let mut unstable_opts = false;
1693
1694 for dep in deps {
1695 if dep.unit.mode.is_run_custom_build() {
1696 cmd.env(
1697 "OUT_DIR",
1698 &build_runner.files().build_script_out_dir(&dep.unit),
1699 );
1700 }
1701 }
1702
1703 for arg in extern_args(build_runner, unit, &mut unstable_opts)? {
1704 cmd.arg(arg);
1705 }
1706
1707 for (var, env) in artifact::get_env(build_runner, deps)? {
1708 cmd.env(&var, env);
1709 }
1710
1711 if unstable_opts {
1714 cmd.arg("-Z").arg("unstable-options");
1715 }
1716
1717 Ok(())
1718}
1719
1720fn add_custom_flags(
1724 cmd: &mut ProcessBuilder,
1725 build_script_outputs: &BuildScriptOutputs,
1726 metadata_vec: Option<Vec<UnitHash>>,
1727) -> CargoResult<()> {
1728 if let Some(metadata_vec) = metadata_vec {
1729 for metadata in metadata_vec {
1730 if let Some(output) = build_script_outputs.get(metadata) {
1731 for cfg in output.cfgs.iter() {
1732 cmd.arg("--cfg").arg(cfg);
1733 }
1734 for check_cfg in &output.check_cfgs {
1735 cmd.arg("--check-cfg").arg(check_cfg);
1736 }
1737 for (name, value) in output.env.iter() {
1738 cmd.env(name, value);
1739 }
1740 }
1741 }
1742 }
1743
1744 Ok(())
1745}
1746
1747pub fn extern_args(
1749 build_runner: &BuildRunner<'_, '_>,
1750 unit: &Unit,
1751 unstable_opts: &mut bool,
1752) -> CargoResult<Vec<OsString>> {
1753 let mut result = Vec::new();
1754 let deps = build_runner.unit_deps(unit);
1755
1756 let no_embed_metadata = build_runner.bcx.gctx.cli_unstable().no_embed_metadata;
1757
1758 let mut link_to =
1760 |dep: &UnitDep, extern_crate_name: InternedString, noprelude: bool| -> CargoResult<()> {
1761 let mut value = OsString::new();
1762 let mut opts = Vec::new();
1763 let is_public_dependency_enabled = unit
1764 .pkg
1765 .manifest()
1766 .unstable_features()
1767 .require(Feature::public_dependency())
1768 .is_ok()
1769 || build_runner.bcx.gctx.cli_unstable().public_dependency;
1770 if !dep.public && unit.target.is_lib() && is_public_dependency_enabled {
1771 opts.push("priv");
1772 *unstable_opts = true;
1773 }
1774 if noprelude {
1775 opts.push("noprelude");
1776 *unstable_opts = true;
1777 }
1778 if !opts.is_empty() {
1779 value.push(opts.join(","));
1780 value.push(":");
1781 }
1782 value.push(extern_crate_name.as_str());
1783 value.push("=");
1784
1785 let mut pass = |file| {
1786 let mut value = value.clone();
1787 value.push(file);
1788 result.push(OsString::from("--extern"));
1789 result.push(value);
1790 };
1791
1792 let outputs = build_runner.outputs(&dep.unit)?;
1793
1794 if build_runner.only_requires_rmeta(unit, &dep.unit) || dep.unit.mode.is_check() {
1795 let output = outputs
1797 .iter()
1798 .find(|output| output.flavor == FileFlavor::Rmeta)
1799 .expect("failed to find rmeta dep for pipelined dep");
1800 pass(&output.path);
1801 } else {
1802 for output in outputs.iter() {
1804 if output.flavor == FileFlavor::Linkable {
1805 pass(&output.path);
1806 }
1807 else if no_embed_metadata && output.flavor == FileFlavor::Rmeta {
1811 pass(&output.path);
1812 }
1813 }
1814 }
1815 Ok(())
1816 };
1817
1818 for dep in deps {
1819 if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
1820 link_to(dep, dep.extern_crate_name, dep.noprelude)?;
1821 }
1822 }
1823 if unit.target.proc_macro() {
1824 result.push(OsString::from("--extern"));
1826 result.push(OsString::from("proc_macro"));
1827 }
1828
1829 Ok(result)
1830}
1831
1832fn envify(s: &str) -> String {
1833 s.chars()
1834 .flat_map(|c| c.to_uppercase())
1835 .map(|c| if c == '-' { '_' } else { c })
1836 .collect()
1837}
1838
1839struct OutputOptions {
1842 format: MessageFormat,
1844 cache_cell: Option<(PathBuf, LazyCell<File>)>,
1849 show_diagnostics: bool,
1857 warnings_seen: usize,
1859 errors_seen: usize,
1861}
1862
1863impl OutputOptions {
1864 fn new(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OutputOptions {
1865 let path = build_runner.files().message_cache_path(unit);
1866 drop(fs::remove_file(&path));
1868 let cache_cell = Some((path, LazyCell::new()));
1869 let show_diagnostics =
1870 build_runner.bcx.gctx.warning_handling().unwrap_or_default() != WarningHandling::Allow;
1871 OutputOptions {
1872 format: build_runner.bcx.build_config.message_format,
1873 cache_cell,
1874 show_diagnostics,
1875 warnings_seen: 0,
1876 errors_seen: 0,
1877 }
1878 }
1879}
1880
1881fn on_stdout_line(
1882 state: &JobState<'_, '_>,
1883 line: &str,
1884 _package_id: PackageId,
1885 _target: &Target,
1886) -> CargoResult<()> {
1887 state.stdout(line.to_string())?;
1888 Ok(())
1889}
1890
1891fn on_stderr_line(
1892 state: &JobState<'_, '_>,
1893 line: &str,
1894 package_id: PackageId,
1895 manifest_path: &std::path::Path,
1896 target: &Target,
1897 options: &mut OutputOptions,
1898) -> CargoResult<()> {
1899 if on_stderr_line_inner(state, line, package_id, manifest_path, target, options)? {
1900 if let Some((path, cell)) = &mut options.cache_cell {
1902 let f = cell.try_borrow_mut_with(|| paths::create(path))?;
1904 debug_assert!(!line.contains('\n'));
1905 f.write_all(line.as_bytes())?;
1906 f.write_all(&[b'\n'])?;
1907 }
1908 }
1909 Ok(())
1910}
1911
1912fn on_stderr_line_inner(
1914 state: &JobState<'_, '_>,
1915 line: &str,
1916 package_id: PackageId,
1917 manifest_path: &std::path::Path,
1918 target: &Target,
1919 options: &mut OutputOptions,
1920) -> CargoResult<bool> {
1921 if !line.starts_with('{') {
1927 state.stderr(line.to_string())?;
1928 return Ok(true);
1929 }
1930
1931 let mut compiler_message: Box<serde_json::value::RawValue> = match serde_json::from_str(line) {
1932 Ok(msg) => msg,
1933
1934 Err(e) => {
1938 debug!("failed to parse json: {:?}", e);
1939 state.stderr(line.to_string())?;
1940 return Ok(true);
1941 }
1942 };
1943
1944 let count_diagnostic = |level, options: &mut OutputOptions| {
1945 if level == "warning" {
1946 options.warnings_seen += 1;
1947 } else if level == "error" {
1948 options.errors_seen += 1;
1949 }
1950 };
1951
1952 if let Ok(report) = serde_json::from_str::<FutureIncompatReport>(compiler_message.get()) {
1953 for item in &report.future_incompat_report {
1954 count_diagnostic(&*item.diagnostic.level, options);
1955 }
1956 state.future_incompat_report(report.future_incompat_report);
1957 return Ok(true);
1958 }
1959
1960 match options.format {
1963 MessageFormat::Human
1968 | MessageFormat::Short
1969 | MessageFormat::Json {
1970 render_diagnostics: true,
1971 ..
1972 } => {
1973 #[derive(serde::Deserialize)]
1974 struct CompilerMessage<'a> {
1975 rendered: String,
1979 #[serde(borrow)]
1980 message: Cow<'a, str>,
1981 #[serde(borrow)]
1982 level: Cow<'a, str>,
1983 children: Vec<PartialDiagnostic>,
1984 }
1985
1986 #[derive(serde::Deserialize)]
1995 struct PartialDiagnostic {
1996 spans: Vec<PartialDiagnosticSpan>,
1997 }
1998
1999 #[derive(serde::Deserialize)]
2001 struct PartialDiagnosticSpan {
2002 suggestion_applicability: Option<Applicability>,
2003 }
2004
2005 if let Ok(mut msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2006 {
2007 if msg.message.starts_with("aborting due to")
2008 || msg.message.ends_with("warning emitted")
2009 || msg.message.ends_with("warnings emitted")
2010 {
2011 return Ok(true);
2013 }
2014 if msg.rendered.ends_with('\n') {
2016 msg.rendered.pop();
2017 }
2018 let rendered = msg.rendered;
2019 if options.show_diagnostics {
2020 let machine_applicable: bool = msg
2021 .children
2022 .iter()
2023 .map(|child| {
2024 child
2025 .spans
2026 .iter()
2027 .filter_map(|span| span.suggestion_applicability)
2028 .any(|app| app == Applicability::MachineApplicable)
2029 })
2030 .any(|b| b);
2031 count_diagnostic(&msg.level, options);
2032 state.emit_diag(&msg.level, rendered, machine_applicable)?;
2033 }
2034 return Ok(true);
2035 }
2036 }
2037
2038 MessageFormat::Json { ansi: false, .. } => {
2042 #[derive(serde::Deserialize, serde::Serialize)]
2043 struct CompilerMessage<'a> {
2044 rendered: String,
2045 #[serde(flatten, borrow)]
2046 other: std::collections::BTreeMap<Cow<'a, str>, serde_json::Value>,
2047 }
2048 if let Ok(mut error) =
2049 serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get())
2050 {
2051 error.rendered = anstream::adapter::strip_str(&error.rendered).to_string();
2052 let new_line = serde_json::to_string(&error)?;
2053 compiler_message = serde_json::value::RawValue::from_string(new_line)?;
2054 }
2055 }
2056
2057 MessageFormat::Json { ansi: true, .. } => {}
2060 }
2061
2062 #[derive(serde::Deserialize)]
2069 struct ArtifactNotification<'a> {
2070 #[serde(borrow)]
2071 artifact: Cow<'a, str>,
2072 }
2073
2074 if let Ok(artifact) = serde_json::from_str::<ArtifactNotification<'_>>(compiler_message.get()) {
2075 trace!("found directive from rustc: `{}`", artifact.artifact);
2076 if artifact.artifact.ends_with(".rmeta") {
2077 debug!("looks like metadata finished early!");
2078 state.rmeta_produced();
2079 }
2080 return Ok(false);
2081 }
2082
2083 if !options.show_diagnostics {
2088 return Ok(true);
2089 }
2090
2091 #[derive(serde::Deserialize)]
2092 struct CompilerMessage<'a> {
2093 #[serde(borrow)]
2094 message: Cow<'a, str>,
2095 #[serde(borrow)]
2096 level: Cow<'a, str>,
2097 }
2098
2099 if let Ok(msg) = serde_json::from_str::<CompilerMessage<'_>>(compiler_message.get()) {
2100 if msg.message.starts_with("aborting due to")
2101 || msg.message.ends_with("warning emitted")
2102 || msg.message.ends_with("warnings emitted")
2103 {
2104 return Ok(true);
2106 }
2107 count_diagnostic(&msg.level, options);
2108 }
2109
2110 let msg = machine_message::FromCompiler {
2111 package_id: package_id.to_spec(),
2112 manifest_path,
2113 target,
2114 message: compiler_message,
2115 }
2116 .to_json_string();
2117
2118 state.stdout(msg)?;
2122 Ok(true)
2123}
2124
2125fn replay_output_cache(
2129 package_id: PackageId,
2130 manifest_path: PathBuf,
2131 target: &Target,
2132 path: PathBuf,
2133 format: MessageFormat,
2134 show_diagnostics: bool,
2135) -> Work {
2136 let target = target.clone();
2137 let mut options = OutputOptions {
2138 format,
2139 cache_cell: None,
2140 show_diagnostics,
2141 warnings_seen: 0,
2142 errors_seen: 0,
2143 };
2144 Work::new(move |state| {
2145 if !path.exists() {
2146 return Ok(());
2148 }
2149 let file = paths::open(&path)?;
2153 let mut reader = std::io::BufReader::new(file);
2154 let mut line = String::new();
2155 loop {
2156 let length = reader.read_line(&mut line)?;
2157 if length == 0 {
2158 break;
2159 }
2160 let trimmed = line.trim_end_matches(&['\n', '\r'][..]);
2161 on_stderr_line(
2162 state,
2163 trimmed,
2164 package_id,
2165 &manifest_path,
2166 &target,
2167 &mut options,
2168 )?;
2169 line.clear();
2170 }
2171 Ok(())
2172 })
2173}
2174
2175fn descriptive_pkg_name(name: &str, target: &Target, mode: &CompileMode) -> String {
2178 let desc_name = target.description_named();
2179 let mode = if mode.is_rustc_test() && !(target.is_test() || target.is_bench()) {
2180 " test"
2181 } else if mode.is_doc_test() {
2182 " doctest"
2183 } else if mode.is_doc() {
2184 " doc"
2185 } else {
2186 ""
2187 };
2188 format!("`{name}` ({desc_name}{mode})")
2189}
2190
2191pub(crate) fn apply_env_config(
2193 gctx: &crate::GlobalContext,
2194 cmd: &mut ProcessBuilder,
2195) -> CargoResult<()> {
2196 for (key, value) in gctx.env_config()?.iter() {
2197 if cmd.get_envs().contains_key(key) {
2199 continue;
2200 }
2201 cmd.env(key, value);
2202 }
2203 Ok(())
2204}
2205
2206fn should_include_scrape_units(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
2208 unit.mode.is_doc() && bcx.scrape_units.len() > 0 && bcx.ws.unit_needs_doc_scrape(unit)
2209}
2210
2211fn scrape_output_path(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<PathBuf> {
2213 assert!(unit.mode.is_doc() || unit.mode.is_doc_scrape());
2214 build_runner
2215 .outputs(unit)
2216 .map(|outputs| outputs[0].path.clone())
2217}
2218
2219fn rustdoc_dep_info_loc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> PathBuf {
2221 let mut loc = build_runner.files().fingerprint_file_path(unit, "");
2222 loc.set_extension("d");
2223 loc
2224}