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