1use super::{fingerprint, BuildRunner, Job, Unit, Work};
35use crate::core::compiler::artifact;
36use crate::core::compiler::build_runner::UnitHash;
37use crate::core::compiler::fingerprint::DirtyReason;
38use crate::core::compiler::job_queue::JobState;
39use crate::core::{profiles::ProfileRoot, PackageId, Target};
40use crate::util::errors::CargoResult;
41use crate::util::internal;
42use crate::util::machine_message::{self, Message};
43use anyhow::{bail, Context as _};
44use cargo_platform::Cfg;
45use cargo_util::paths;
46use cargo_util_schemas::manifest::RustVersion;
47use std::collections::hash_map::{Entry, HashMap};
48use std::collections::{BTreeSet, HashSet};
49use std::path::{Path, PathBuf};
50use std::str::{self, FromStr};
51use std::sync::{Arc, Mutex};
52
53const CARGO_ERROR_SYNTAX: &str = "cargo::error=";
58const OLD_CARGO_WARNING_SYNTAX: &str = "cargo:warning=";
63const NEW_CARGO_WARNING_SYNTAX: &str = "cargo::warning=";
68
69#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
70pub enum Severity {
71 Error,
72 Warning,
73}
74
75pub type LogMessage = (Severity, String);
76
77#[derive(Clone, Debug, Hash, Default, PartialEq, Eq, PartialOrd, Ord)]
79pub struct BuildOutput {
80 pub library_paths: Vec<PathBuf>,
82 pub library_links: Vec<String>,
84 pub linker_args: Vec<(LinkArgTarget, String)>,
86 pub cfgs: Vec<String>,
88 pub check_cfgs: Vec<String>,
90 pub env: Vec<(String, String)>,
92 pub metadata: Vec<(String, String)>,
94 pub rerun_if_changed: Vec<PathBuf>,
97 pub rerun_if_env_changed: Vec<String>,
99 pub log_messages: Vec<LogMessage>,
106}
107
108#[derive(Default)]
119pub struct BuildScriptOutputs {
120 outputs: HashMap<UnitHash, BuildOutput>,
121}
122
123#[derive(Default)]
127pub struct BuildScripts {
128 pub to_link: Vec<(PackageId, UnitHash)>,
145 seen_to_link: HashSet<(PackageId, UnitHash)>,
147 pub plugins: BTreeSet<(PackageId, UnitHash)>,
156}
157
158#[derive(Debug)]
161pub struct BuildDeps {
162 pub build_script_output: PathBuf,
165 pub rerun_if_changed: Vec<PathBuf>,
167 pub rerun_if_env_changed: Vec<String>,
169}
170
171#[derive(Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord)]
180pub enum LinkArgTarget {
181 All,
183 Cdylib,
185 Bin,
187 SingleBin(String),
189 Test,
191 Bench,
193 Example,
195}
196
197impl LinkArgTarget {
198 pub fn applies_to(&self, target: &Target) -> bool {
200 match self {
201 LinkArgTarget::All => true,
202 LinkArgTarget::Cdylib => target.is_cdylib(),
203 LinkArgTarget::Bin => target.is_bin(),
204 LinkArgTarget::SingleBin(name) => target.is_bin() && target.name() == name,
205 LinkArgTarget::Test => target.is_test(),
206 LinkArgTarget::Bench => target.is_bench(),
207 LinkArgTarget::Example => target.is_exe_example(),
208 }
209 }
210}
211
212#[tracing::instrument(skip_all)]
214pub fn prepare(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Job> {
215 let metadata = build_runner.get_run_build_script_metadata(unit);
216 if build_runner
217 .build_script_outputs
218 .lock()
219 .unwrap()
220 .contains_key(metadata)
221 {
222 fingerprint::prepare_target(build_runner, unit, false)
224 } else {
225 build_work(build_runner, unit)
226 }
227}
228
229fn emit_build_output(
232 state: &JobState<'_, '_>,
233 output: &BuildOutput,
234 out_dir: &Path,
235 package_id: PackageId,
236) -> CargoResult<()> {
237 let library_paths = output
238 .library_paths
239 .iter()
240 .map(|l| l.display().to_string())
241 .collect::<Vec<_>>();
242
243 let msg = machine_message::BuildScript {
244 package_id: package_id.to_spec(),
245 linked_libs: &output.library_links,
246 linked_paths: &library_paths,
247 cfgs: &output.cfgs,
248 env: &output.env,
249 out_dir,
250 }
251 .to_json_string();
252 state.stdout(msg)?;
253 Ok(())
254}
255
256fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Job> {
265 assert!(unit.mode.is_run_custom_build());
266 let bcx = &build_runner.bcx;
267 let dependencies = build_runner.unit_deps(unit);
268 let build_script_unit = dependencies
269 .iter()
270 .find(|d| !d.unit.mode.is_run_custom_build() && d.unit.target.is_custom_build())
271 .map(|d| &d.unit)
272 .expect("running a script not depending on an actual script");
273 let script_dir = build_runner.files().build_script_dir(build_script_unit);
274 let script_out_dir = build_runner.files().build_script_out_dir(unit);
275 let script_run_dir = build_runner.files().build_script_run_dir(unit);
276 let build_plan = bcx.build_config.build_plan;
277 let invocation_name = unit.buildkey();
278
279 if let Some(deps) = unit.pkg.manifest().metabuild() {
280 prepare_metabuild(build_runner, build_script_unit, deps)?;
281 }
282
283 let to_exec = script_dir.join(unit.target.name());
285
286 let to_exec = to_exec.into_os_string();
294 let mut cmd = build_runner.compilation.host_process(to_exec, &unit.pkg)?;
295 let debug = unit.profile.debuginfo.is_turned_on();
296 cmd.env("OUT_DIR", &script_out_dir)
297 .env("CARGO_MANIFEST_DIR", unit.pkg.root())
298 .env("CARGO_MANIFEST_PATH", unit.pkg.manifest_path())
299 .env("NUM_JOBS", &bcx.jobs().to_string())
300 .env("TARGET", bcx.target_data.short_name(&unit.kind))
301 .env("DEBUG", debug.to_string())
302 .env("OPT_LEVEL", &unit.profile.opt_level)
303 .env(
304 "PROFILE",
305 match unit.profile.root {
306 ProfileRoot::Release => "release",
307 ProfileRoot::Debug => "debug",
308 },
309 )
310 .env("HOST", &bcx.host_triple())
311 .env("RUSTC", &bcx.rustc().path)
312 .env("RUSTDOC", &*bcx.gctx.rustdoc()?)
313 .inherit_jobserver(&build_runner.jobserver);
314
315 for (var, value) in artifact::get_env(build_runner, dependencies)? {
317 cmd.env(&var, value);
318 }
319
320 if let Some(linker) = &build_runner.compilation.target_linker(unit.kind) {
321 cmd.env("RUSTC_LINKER", linker);
322 }
323
324 if let Some(links) = unit.pkg.manifest().links() {
325 cmd.env("CARGO_MANIFEST_LINKS", links);
326 }
327
328 if let Some(trim_paths) = unit.profile.trim_paths.as_ref() {
329 cmd.env("CARGO_TRIM_PATHS", trim_paths.to_string());
330 }
331
332 for feat in &unit.features {
335 cmd.env(&format!("CARGO_FEATURE_{}", super::envify(feat)), "1");
336 }
337
338 let mut cfg_map = HashMap::new();
339 cfg_map.insert(
340 "feature",
341 unit.features.iter().map(|s| s.as_str()).collect::<Vec<_>>(),
342 );
343 for cfg in bcx.target_data.cfg(unit.kind) {
344 match *cfg {
345 Cfg::Name(ref n) => {
346 cfg_map.insert(n.as_str(), Vec::new());
347 }
348 Cfg::KeyPair(ref k, ref v) => {
349 let values = cfg_map.entry(k.as_str()).or_default();
350 values.push(v.as_str());
351 }
352 }
353 }
354 for (k, v) in cfg_map {
355 if k == "debug_assertions" {
356 continue;
359 }
360 let k = format!("CARGO_CFG_{}", super::envify(k));
363 cmd.env(&k, v.join(","));
364 }
365
366 if let Some(wrapper) = bcx.rustc().wrapper.as_ref() {
368 cmd.env("RUSTC_WRAPPER", wrapper);
369 } else {
370 cmd.env_remove("RUSTC_WRAPPER");
371 }
372 cmd.env_remove("RUSTC_WORKSPACE_WRAPPER");
373 if build_runner.bcx.ws.is_member(&unit.pkg) {
374 if let Some(wrapper) = bcx.rustc().workspace_wrapper.as_ref() {
375 cmd.env("RUSTC_WORKSPACE_WRAPPER", wrapper);
376 }
377 }
378 cmd.env("CARGO_ENCODED_RUSTFLAGS", unit.rustflags.join("\x1f"));
379 cmd.env_remove("RUSTFLAGS");
380
381 if build_runner.bcx.ws.gctx().extra_verbose() {
382 cmd.display_env_vars();
383 }
384
385 let lib_deps = dependencies
391 .iter()
392 .filter_map(|dep| {
393 if dep.unit.mode.is_run_custom_build() {
394 let dep_metadata = build_runner.get_run_build_script_metadata(&dep.unit);
395 Some((
396 dep.unit.pkg.manifest().links().unwrap().to_string(),
397 dep.unit.pkg.package_id(),
398 dep_metadata,
399 ))
400 } else {
401 None
402 }
403 })
404 .collect::<Vec<_>>();
405 let library_name = unit.pkg.library().map(|t| t.crate_name());
406 let pkg_descr = unit.pkg.to_string();
407 let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
408 let id = unit.pkg.package_id();
409 let output_file = script_run_dir.join("output");
410 let err_file = script_run_dir.join("stderr");
411 let root_output_file = script_run_dir.join("root-output");
412 let host_target_root = build_runner.files().host_dest().to_path_buf();
413 let all = (
414 id,
415 library_name.clone(),
416 pkg_descr.clone(),
417 Arc::clone(&build_script_outputs),
418 output_file.clone(),
419 script_out_dir.clone(),
420 );
421 let build_scripts = build_runner.build_scripts.get(unit).cloned();
422 let json_messages = bcx.build_config.emit_json();
423 let extra_verbose = bcx.gctx.extra_verbose();
424 let (prev_output, prev_script_out_dir) = prev_build_output(build_runner, unit);
425 let metadata_hash = build_runner.get_run_build_script_metadata(unit);
426
427 paths::create_dir_all(&script_dir)?;
428 paths::create_dir_all(&script_out_dir)?;
429
430 let nightly_features_allowed = build_runner.bcx.gctx.nightly_features_allowed;
431 let targets: Vec<Target> = unit.pkg.targets().to_vec();
432 let msrv = unit.pkg.rust_version().cloned();
433 let targets_fresh = targets.clone();
435 let msrv_fresh = msrv.clone();
436
437 let env_profile_name = unit.profile.name.to_uppercase();
438 let built_with_debuginfo = build_runner
439 .bcx
440 .unit_graph
441 .get(unit)
442 .and_then(|deps| deps.iter().find(|dep| dep.unit.target == unit.target))
443 .map(|dep| dep.unit.profile.debuginfo.is_turned_on())
444 .unwrap_or(false);
445
446 let dirty = Work::new(move |state| {
452 paths::create_dir_all(&script_out_dir)
457 .context("failed to create script output directory for build command")?;
458
459 if !build_plan {
464 let build_script_outputs = build_script_outputs.lock().unwrap();
465 for (name, dep_id, dep_metadata) in lib_deps {
466 let script_output = build_script_outputs.get(dep_metadata).ok_or_else(|| {
467 internal(format!(
468 "failed to locate build state for env vars: {}/{}",
469 dep_id, dep_metadata
470 ))
471 })?;
472 let data = &script_output.metadata;
473 for (key, value) in data.iter() {
474 cmd.env(
475 &format!("DEP_{}_{}", super::envify(&name), super::envify(key)),
476 value,
477 );
478 }
479 }
480 if let Some(build_scripts) = build_scripts {
481 super::add_plugin_deps(
482 &mut cmd,
483 &build_script_outputs,
484 &build_scripts,
485 &host_target_root,
486 )?;
487 }
488 }
489
490 if build_plan {
491 state.build_plan(invocation_name, cmd.clone(), Arc::new(Vec::new()));
492 return Ok(());
493 }
494
495 state.running(&cmd);
497 let timestamp = paths::set_invocation_time(&script_run_dir)?;
498 let prefix = format!("[{} {}] ", id.name(), id.version());
499 let mut log_messages_in_case_of_panic = Vec::new();
500 let output = cmd
501 .exec_with_streaming(
502 &mut |stdout| {
503 if let Some(error) = stdout.strip_prefix(CARGO_ERROR_SYNTAX) {
504 log_messages_in_case_of_panic.push((Severity::Error, error.to_owned()));
505 }
506 if let Some(warning) = stdout
507 .strip_prefix(OLD_CARGO_WARNING_SYNTAX)
508 .or(stdout.strip_prefix(NEW_CARGO_WARNING_SYNTAX))
509 {
510 log_messages_in_case_of_panic.push((Severity::Warning, warning.to_owned()));
511 }
512 if extra_verbose {
513 state.stdout(format!("{}{}", prefix, stdout))?;
514 }
515 Ok(())
516 },
517 &mut |stderr| {
518 if extra_verbose {
519 state.stderr(format!("{}{}", prefix, stderr))?;
520 }
521 Ok(())
522 },
523 true,
524 )
525 .with_context(|| {
526 let mut build_error_context =
527 format!("failed to run custom build command for `{}`", pkg_descr);
528
529 #[allow(clippy::disallowed_methods)]
536 if let Ok(show_backtraces) = std::env::var("RUST_BACKTRACE") {
537 if !built_with_debuginfo && show_backtraces != "0" {
538 build_error_context.push_str(&format!(
539 "\n\
540 note: To improve backtraces for build dependencies, set the \
541 CARGO_PROFILE_{env_profile_name}_BUILD_OVERRIDE_DEBUG=true environment \
542 variable to enable debug information generation.",
543 ));
544 }
545 }
546
547 build_error_context
548 });
549
550 if let Err(error) = output {
552 insert_log_messages_in_build_outputs(
553 build_script_outputs,
554 id,
555 metadata_hash,
556 log_messages_in_case_of_panic,
557 );
558 return Err(error);
559 }
560 else if log_messages_in_case_of_panic
562 .iter()
563 .any(|(severity, _)| *severity == Severity::Error)
564 {
565 insert_log_messages_in_build_outputs(
566 build_script_outputs,
567 id,
568 metadata_hash,
569 log_messages_in_case_of_panic,
570 );
571 anyhow::bail!("build script logged errors");
572 }
573
574 let output = output.unwrap();
575
576 paths::write(&output_file, &output.stdout)?;
584 paths::set_file_time_no_err(output_file, timestamp);
587 paths::write(&err_file, &output.stderr)?;
588 paths::write(&root_output_file, paths::path2bytes(&script_out_dir)?)?;
589 let parsed_output = BuildOutput::parse(
590 &output.stdout,
591 library_name,
592 &pkg_descr,
593 &script_out_dir,
594 &script_out_dir,
595 nightly_features_allowed,
596 &targets,
597 &msrv,
598 )?;
599
600 if json_messages {
601 emit_build_output(state, &parsed_output, script_out_dir.as_path(), id)?;
602 }
603 build_script_outputs
604 .lock()
605 .unwrap()
606 .insert(id, metadata_hash, parsed_output);
607 Ok(())
608 });
609
610 let fresh = Work::new(move |state| {
614 let (id, library_name, pkg_descr, build_script_outputs, output_file, script_out_dir) = all;
615 let output = match prev_output {
616 Some(output) => output,
617 None => BuildOutput::parse_file(
618 &output_file,
619 library_name,
620 &pkg_descr,
621 &prev_script_out_dir,
622 &script_out_dir,
623 nightly_features_allowed,
624 &targets_fresh,
625 &msrv_fresh,
626 )?,
627 };
628
629 if json_messages {
630 emit_build_output(state, &output, script_out_dir.as_path(), id)?;
631 }
632
633 build_script_outputs
634 .lock()
635 .unwrap()
636 .insert(id, metadata_hash, output);
637 Ok(())
638 });
639
640 let mut job = if build_runner.bcx.build_config.build_plan {
641 Job::new_dirty(Work::noop(), DirtyReason::FreshBuild)
642 } else {
643 fingerprint::prepare_target(build_runner, unit, false)?
644 };
645 if job.freshness().is_dirty() {
646 job.before(dirty);
647 } else {
648 job.before(fresh);
649 }
650 Ok(job)
651}
652
653fn insert_log_messages_in_build_outputs(
656 build_script_outputs: Arc<Mutex<BuildScriptOutputs>>,
657 id: PackageId,
658 metadata_hash: UnitHash,
659 log_messages: Vec<LogMessage>,
660) {
661 let build_output_with_only_log_messages = BuildOutput {
662 log_messages,
663 ..BuildOutput::default()
664 };
665 build_script_outputs.lock().unwrap().insert(
666 id,
667 metadata_hash,
668 build_output_with_only_log_messages,
669 );
670}
671
672impl BuildOutput {
673 pub fn parse_file(
675 path: &Path,
676 library_name: Option<String>,
677 pkg_descr: &str,
678 script_out_dir_when_generated: &Path,
679 script_out_dir: &Path,
680 nightly_features_allowed: bool,
681 targets: &[Target],
682 msrv: &Option<RustVersion>,
683 ) -> CargoResult<BuildOutput> {
684 let contents = paths::read_bytes(path)?;
685 BuildOutput::parse(
686 &contents,
687 library_name,
688 pkg_descr,
689 script_out_dir_when_generated,
690 script_out_dir,
691 nightly_features_allowed,
692 targets,
693 msrv,
694 )
695 }
696
697 pub fn parse(
702 input: &[u8],
703 library_name: Option<String>,
705 pkg_descr: &str,
706 script_out_dir_when_generated: &Path,
707 script_out_dir: &Path,
708 nightly_features_allowed: bool,
709 targets: &[Target],
710 msrv: &Option<RustVersion>,
711 ) -> CargoResult<BuildOutput> {
712 let mut library_paths = Vec::new();
713 let mut library_links = Vec::new();
714 let mut linker_args = Vec::new();
715 let mut cfgs = Vec::new();
716 let mut check_cfgs = Vec::new();
717 let mut env = Vec::new();
718 let mut metadata = Vec::new();
719 let mut rerun_if_changed = Vec::new();
720 let mut rerun_if_env_changed = Vec::new();
721 let mut log_messages = Vec::new();
722 let whence = format!("build script of `{}`", pkg_descr);
723 const RESERVED_PREFIXES: &[&str] = &[
731 "rustc-flags=",
732 "rustc-link-lib=",
733 "rustc-link-search=",
734 "rustc-link-arg-cdylib=",
735 "rustc-cdylib-link-arg=",
736 "rustc-link-arg-bins=",
737 "rustc-link-arg-bin=",
738 "rustc-link-arg-tests=",
739 "rustc-link-arg-benches=",
740 "rustc-link-arg-examples=",
741 "rustc-link-arg=",
742 "rustc-cfg=",
743 "rustc-check-cfg=",
744 "rustc-env=",
745 "warning=",
746 "rerun-if-changed=",
747 "rerun-if-env-changed=",
748 ];
749 const DOCS_LINK_SUGGESTION: &str = "See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script \
750 for more information about build script outputs.";
751
752 fn has_reserved_prefix(flag: &str) -> bool {
753 RESERVED_PREFIXES
754 .iter()
755 .any(|reserved_prefix| flag.starts_with(reserved_prefix))
756 }
757
758 fn check_minimum_supported_rust_version_for_new_syntax(
759 pkg_descr: &str,
760 msrv: &Option<RustVersion>,
761 flag: &str,
762 ) -> CargoResult<()> {
763 if let Some(msrv) = msrv {
764 let new_syntax_added_in = RustVersion::from_str("1.77.0")?;
765 if !new_syntax_added_in.is_compatible_with(msrv.as_partial()) {
766 let old_syntax_suggestion = if has_reserved_prefix(flag) {
767 format!(
768 "Switch to the old `cargo:{flag}` syntax (note the single colon).\n"
769 )
770 } else if flag.starts_with("metadata=") {
771 let old_format_flag = flag.strip_prefix("metadata=").unwrap();
772 format!("Switch to the old `cargo:{old_format_flag}` syntax instead of `cargo::{flag}` (note the single colon).\n")
773 } else {
774 String::new()
775 };
776
777 bail!(
778 "the `cargo::` syntax for build script output instructions was added in \
779 Rust 1.77.0, but the minimum supported Rust version of `{pkg_descr}` is {msrv}.\n\
780 {old_syntax_suggestion}\
781 {DOCS_LINK_SUGGESTION}"
782 );
783 }
784 }
785
786 Ok(())
787 }
788
789 fn parse_directive<'a>(
790 whence: &str,
791 line: &str,
792 data: &'a str,
793 old_syntax: bool,
794 ) -> CargoResult<(&'a str, &'a str)> {
795 let mut iter = data.splitn(2, "=");
796 let key = iter.next();
797 let value = iter.next();
798 match (key, value) {
799 (Some(a), Some(b)) => Ok((a, b.trim_end())),
800 _ => bail!(
801 "invalid output in {whence}: `{line}`\n\
802 Expected a line with `{syntax}KEY=VALUE` with an `=` character, \
803 but none was found.\n\
804 {DOCS_LINK_SUGGESTION}",
805 syntax = if old_syntax { "cargo:" } else { "cargo::" },
806 ),
807 }
808 }
809
810 fn parse_metadata<'a>(
811 whence: &str,
812 line: &str,
813 data: &'a str,
814 old_syntax: bool,
815 ) -> CargoResult<(&'a str, &'a str)> {
816 let mut iter = data.splitn(2, "=");
817 let key = iter.next();
818 let value = iter.next();
819 match (key, value) {
820 (Some(a), Some(b)) => Ok((a, b.trim_end())),
821 _ => bail!(
822 "invalid output in {whence}: `{line}`\n\
823 Expected a line with `{syntax}KEY=VALUE` with an `=` character, \
824 but none was found.\n\
825 {DOCS_LINK_SUGGESTION}",
826 syntax = if old_syntax {
827 "cargo:"
828 } else {
829 "cargo::metadata="
830 },
831 ),
832 }
833 }
834
835 for line in input.split(|b| *b == b'\n') {
836 let line = match str::from_utf8(line) {
837 Ok(line) => line.trim(),
838 Err(..) => continue,
839 };
840 let mut old_syntax = false;
841 let (key, value) = if let Some(data) = line.strip_prefix("cargo::") {
842 check_minimum_supported_rust_version_for_new_syntax(pkg_descr, msrv, data)?;
843 parse_directive(whence.as_str(), line, data, old_syntax)?
845 } else if let Some(data) = line.strip_prefix("cargo:") {
846 old_syntax = true;
847 if has_reserved_prefix(data) {
849 parse_directive(whence.as_str(), line, data, old_syntax)?
850 } else {
851 ("metadata", data)
853 }
854 } else {
855 continue;
857 };
858 let value = value.replace(
860 script_out_dir_when_generated.to_str().unwrap(),
861 script_out_dir.to_str().unwrap(),
862 );
863
864 let syntax_prefix = if old_syntax { "cargo:" } else { "cargo::" };
865 macro_rules! check_and_add_target {
866 ($target_kind: expr, $is_target_kind: expr, $link_type: expr) => {
867 if !targets.iter().any(|target| $is_target_kind(target)) {
868 bail!(
869 "invalid instruction `{}{}` from {}\n\
870 The package {} does not have a {} target.",
871 syntax_prefix,
872 key,
873 whence,
874 pkg_descr,
875 $target_kind
876 );
877 }
878 linker_args.push(($link_type, value));
879 };
880 }
881
882 match key {
884 "rustc-flags" => {
885 let (paths, links) = BuildOutput::parse_rustc_flags(&value, &whence)?;
886 library_links.extend(links.into_iter());
887 library_paths.extend(paths.into_iter());
888 }
889 "rustc-link-lib" => library_links.push(value.to_string()),
890 "rustc-link-search" => library_paths.push(PathBuf::from(value)),
891 "rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
892 if !targets.iter().any(|target| target.is_cdylib()) {
893 log_messages.push((
894 Severity::Warning,
895 format!(
896 "{}{} was specified in the build script of {}, \
897 but that package does not contain a cdylib target\n\
898 \n\
899 Allowing this was an unintended change in the 1.50 \
900 release, and may become an error in the future. \
901 For more information, see \
902 <https://github.com/rust-lang/cargo/issues/9562>.",
903 syntax_prefix, key, pkg_descr
904 ),
905 ));
906 }
907 linker_args.push((LinkArgTarget::Cdylib, value))
908 }
909 "rustc-link-arg-bins" => {
910 check_and_add_target!("bin", Target::is_bin, LinkArgTarget::Bin);
911 }
912 "rustc-link-arg-bin" => {
913 let (bin_name, arg) = value.split_once('=').ok_or_else(|| {
914 anyhow::format_err!(
915 "invalid instruction `{}{}={}` from {}\n\
916 The instruction should have the form {}{}=BIN=ARG",
917 syntax_prefix,
918 key,
919 value,
920 whence,
921 syntax_prefix,
922 key
923 )
924 })?;
925 if !targets
926 .iter()
927 .any(|target| target.is_bin() && target.name() == bin_name)
928 {
929 bail!(
930 "invalid instruction `{}{}` from {}\n\
931 The package {} does not have a bin target with the name `{}`.",
932 syntax_prefix,
933 key,
934 whence,
935 pkg_descr,
936 bin_name
937 );
938 }
939 linker_args.push((
940 LinkArgTarget::SingleBin(bin_name.to_owned()),
941 arg.to_string(),
942 ));
943 }
944 "rustc-link-arg-tests" => {
945 check_and_add_target!("test", Target::is_test, LinkArgTarget::Test);
946 }
947 "rustc-link-arg-benches" => {
948 check_and_add_target!("benchmark", Target::is_bench, LinkArgTarget::Bench);
949 }
950 "rustc-link-arg-examples" => {
951 check_and_add_target!("example", Target::is_example, LinkArgTarget::Example);
952 }
953 "rustc-link-arg" => {
954 linker_args.push((LinkArgTarget::All, value));
955 }
956 "rustc-cfg" => cfgs.push(value.to_string()),
957 "rustc-check-cfg" => check_cfgs.push(value.to_string()),
958 "rustc-env" => {
959 let (key, val) = BuildOutput::parse_rustc_env(&value, &whence)?;
960 if key == "RUSTC_BOOTSTRAP" {
963 let rustc_bootstrap_allows = |name: Option<&str>| {
973 let name = match name {
974 None => return false,
978 Some(n) => n,
979 };
980 #[allow(clippy::disallowed_methods)]
984 std::env::var("RUSTC_BOOTSTRAP")
985 .map_or(false, |var| var.split(',').any(|s| s == name))
986 };
987 if nightly_features_allowed
988 || rustc_bootstrap_allows(library_name.as_deref())
989 {
990 log_messages.push((Severity::Warning, format!("Cannot set `RUSTC_BOOTSTRAP={}` from {}.\n\
991 note: Crates cannot set `RUSTC_BOOTSTRAP` themselves, as doing so would subvert the stability guarantees of Rust for your project.",
992 val, whence
993 )));
994 } else {
995 bail!("Cannot set `RUSTC_BOOTSTRAP={}` from {}.\n\
998 note: Crates cannot set `RUSTC_BOOTSTRAP` themselves, as doing so would subvert the stability guarantees of Rust for your project.\n\
999 help: If you're sure you want to do this in your project, set the environment variable `RUSTC_BOOTSTRAP={}` before running cargo instead.",
1000 val,
1001 whence,
1002 library_name.as_deref().unwrap_or("1"),
1003 );
1004 }
1005 } else {
1006 env.push((key, val));
1007 }
1008 }
1009 "error" => log_messages.push((Severity::Error, value.to_string())),
1010 "warning" => log_messages.push((Severity::Warning, value.to_string())),
1011 "rerun-if-changed" => rerun_if_changed.push(PathBuf::from(value)),
1012 "rerun-if-env-changed" => rerun_if_env_changed.push(value.to_string()),
1013 "metadata" => {
1014 let (key, value) = parse_metadata(whence.as_str(), line, &value, old_syntax)?;
1015 metadata.push((key.to_owned(), value.to_owned()));
1016 }
1017 _ => bail!(
1018 "invalid output in {whence}: `{line}`\n\
1019 Unknown key: `{key}`.\n\
1020 {DOCS_LINK_SUGGESTION}",
1021 ),
1022 }
1023 }
1024
1025 Ok(BuildOutput {
1026 library_paths,
1027 library_links,
1028 linker_args,
1029 cfgs,
1030 check_cfgs,
1031 env,
1032 metadata,
1033 rerun_if_changed,
1034 rerun_if_env_changed,
1035 log_messages,
1036 })
1037 }
1038
1039 pub fn parse_rustc_flags(
1043 value: &str,
1044 whence: &str,
1045 ) -> CargoResult<(Vec<PathBuf>, Vec<String>)> {
1046 let value = value.trim();
1047 let mut flags_iter = value
1048 .split(|c: char| c.is_whitespace())
1049 .filter(|w| w.chars().any(|c| !c.is_whitespace()));
1050 let (mut library_paths, mut library_links) = (Vec::new(), Vec::new());
1051
1052 while let Some(flag) = flags_iter.next() {
1053 if flag.starts_with("-l") || flag.starts_with("-L") {
1054 let (flag, mut value) = flag.split_at(2);
1058 if value.is_empty() {
1059 value = match flags_iter.next() {
1060 Some(v) => v,
1061 None => bail! {
1062 "Flag in rustc-flags has no value in {}: {}",
1063 whence,
1064 value
1065 },
1066 }
1067 }
1068
1069 match flag {
1070 "-l" => library_links.push(value.to_string()),
1071 "-L" => library_paths.push(PathBuf::from(value)),
1072
1073 _ => unreachable!(),
1075 };
1076 } else {
1077 bail!(
1078 "Only `-l` and `-L` flags are allowed in {}: `{}`",
1079 whence,
1080 value
1081 )
1082 }
1083 }
1084 Ok((library_paths, library_links))
1085 }
1086
1087 pub fn parse_rustc_env(value: &str, whence: &str) -> CargoResult<(String, String)> {
1091 match value.split_once('=') {
1092 Some((n, v)) => Ok((n.to_owned(), v.to_owned())),
1093 _ => bail!("Variable rustc-env has no value in {whence}: {value}"),
1094 }
1095 }
1096}
1097
1098fn prepare_metabuild(
1102 build_runner: &BuildRunner<'_, '_>,
1103 unit: &Unit,
1104 deps: &[String],
1105) -> CargoResult<()> {
1106 let mut output = Vec::new();
1107 let available_deps = build_runner.unit_deps(unit);
1108 let meta_deps: Vec<_> = deps
1110 .iter()
1111 .filter_map(|name| {
1112 available_deps
1113 .iter()
1114 .find(|d| d.unit.pkg.name().as_str() == name.as_str())
1115 .map(|d| d.unit.target.crate_name())
1116 })
1117 .collect();
1118 output.push("fn main() {\n".to_string());
1119 for dep in &meta_deps {
1120 output.push(format!(" {}::metabuild();\n", dep));
1121 }
1122 output.push("}\n".to_string());
1123 let output = output.join("");
1124 let path = unit
1125 .pkg
1126 .manifest()
1127 .metabuild_path(build_runner.bcx.ws.target_dir());
1128 paths::create_dir_all(path.parent().unwrap())?;
1129 paths::write_if_changed(path, &output)?;
1130 Ok(())
1131}
1132
1133impl BuildDeps {
1134 pub fn new(output_file: &Path, output: Option<&BuildOutput>) -> BuildDeps {
1137 BuildDeps {
1138 build_script_output: output_file.to_path_buf(),
1139 rerun_if_changed: output
1140 .map(|p| &p.rerun_if_changed)
1141 .cloned()
1142 .unwrap_or_default(),
1143 rerun_if_env_changed: output
1144 .map(|p| &p.rerun_if_env_changed)
1145 .cloned()
1146 .unwrap_or_default(),
1147 }
1148 }
1149}
1150
1151pub fn build_map(build_runner: &mut BuildRunner<'_, '_>) -> CargoResult<()> {
1173 let mut ret = HashMap::new();
1174 for unit in &build_runner.bcx.roots {
1175 build(&mut ret, build_runner, unit)?;
1176 }
1177 build_runner
1178 .build_scripts
1179 .extend(ret.into_iter().map(|(k, v)| (k, Arc::new(v))));
1180 return Ok(());
1181
1182 fn build<'a>(
1185 out: &'a mut HashMap<Unit, BuildScripts>,
1186 build_runner: &mut BuildRunner<'_, '_>,
1187 unit: &Unit,
1188 ) -> CargoResult<&'a BuildScripts> {
1189 if out.contains_key(unit) {
1192 return Ok(&out[unit]);
1193 }
1194
1195 if unit.mode.is_run_custom_build() {
1197 if let Some(links) = unit.pkg.manifest().links() {
1198 if let Some(output) = unit.links_overrides.get(links) {
1199 let metadata = build_runner.get_run_build_script_metadata(unit);
1200 build_runner.build_script_outputs.lock().unwrap().insert(
1201 unit.pkg.package_id(),
1202 metadata,
1203 output.clone(),
1204 );
1205 }
1206 }
1207 }
1208
1209 let mut ret = BuildScripts::default();
1210
1211 if !unit.target.is_custom_build() && unit.pkg.has_custom_build() {
1213 let script_meta = build_runner
1214 .find_build_script_metadata(unit)
1215 .expect("has_custom_build should have RunCustomBuild");
1216 add_to_link(&mut ret, unit.pkg.package_id(), script_meta);
1217 }
1218
1219 if unit.mode.is_run_custom_build() {
1220 parse_previous_explicit_deps(build_runner, unit);
1221 }
1222
1223 let mut dependencies: Vec<Unit> = build_runner
1228 .unit_deps(unit)
1229 .iter()
1230 .map(|d| d.unit.clone())
1231 .collect();
1232 dependencies.sort_by_key(|u| u.pkg.package_id());
1233
1234 for dep_unit in dependencies.iter() {
1235 let dep_scripts = build(out, build_runner, dep_unit)?;
1236
1237 if dep_unit.target.for_host() {
1238 ret.plugins.extend(dep_scripts.to_link.iter().cloned());
1239 } else if dep_unit.target.is_linkable() {
1240 for &(pkg, metadata) in dep_scripts.to_link.iter() {
1241 add_to_link(&mut ret, pkg, metadata);
1242 }
1243 }
1244 }
1245
1246 match out.entry(unit.clone()) {
1247 Entry::Vacant(entry) => Ok(entry.insert(ret)),
1248 Entry::Occupied(_) => panic!("cyclic dependencies in `build_map`"),
1249 }
1250 }
1251
1252 fn add_to_link(scripts: &mut BuildScripts, pkg: PackageId, metadata: UnitHash) {
1255 if scripts.seen_to_link.insert((pkg, metadata)) {
1256 scripts.to_link.push((pkg, metadata));
1257 }
1258 }
1259
1260 fn parse_previous_explicit_deps(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) {
1262 let script_run_dir = build_runner.files().build_script_run_dir(unit);
1263 let output_file = script_run_dir.join("output");
1264 let (prev_output, _) = prev_build_output(build_runner, unit);
1265 let deps = BuildDeps::new(&output_file, prev_output.as_ref());
1266 build_runner.build_explicit_deps.insert(unit.clone(), deps);
1267 }
1268}
1269
1270fn prev_build_output(
1276 build_runner: &mut BuildRunner<'_, '_>,
1277 unit: &Unit,
1278) -> (Option<BuildOutput>, PathBuf) {
1279 let script_out_dir = build_runner.files().build_script_out_dir(unit);
1280 let script_run_dir = build_runner.files().build_script_run_dir(unit);
1281 let root_output_file = script_run_dir.join("root-output");
1282 let output_file = script_run_dir.join("output");
1283
1284 let prev_script_out_dir = paths::read_bytes(&root_output_file)
1285 .and_then(|bytes| paths::bytes2path(&bytes))
1286 .unwrap_or_else(|_| script_out_dir.clone());
1287
1288 (
1289 BuildOutput::parse_file(
1290 &output_file,
1291 unit.pkg.library().map(|t| t.crate_name()),
1292 &unit.pkg.to_string(),
1293 &prev_script_out_dir,
1294 &script_out_dir,
1295 build_runner.bcx.gctx.nightly_features_allowed,
1296 unit.pkg.targets(),
1297 &unit.pkg.rust_version().cloned(),
1298 )
1299 .ok(),
1300 prev_script_out_dir,
1301 )
1302}
1303
1304impl BuildScriptOutputs {
1305 fn insert(&mut self, pkg_id: PackageId, metadata: UnitHash, parsed_output: BuildOutput) {
1307 match self.outputs.entry(metadata) {
1308 Entry::Vacant(entry) => {
1309 entry.insert(parsed_output);
1310 }
1311 Entry::Occupied(entry) => panic!(
1312 "build script output collision for {}/{}\n\
1313 old={:?}\nnew={:?}",
1314 pkg_id,
1315 metadata,
1316 entry.get(),
1317 parsed_output
1318 ),
1319 }
1320 }
1321
1322 fn contains_key(&self, metadata: UnitHash) -> bool {
1324 self.outputs.contains_key(&metadata)
1325 }
1326
1327 pub fn get(&self, meta: UnitHash) -> Option<&BuildOutput> {
1329 self.outputs.get(&meta)
1330 }
1331
1332 pub fn iter(&self) -> impl Iterator<Item = (&UnitHash, &BuildOutput)> {
1334 self.outputs.iter()
1335 }
1336}