build.rs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. use std::{
  2. env, fs,
  3. io::{BufRead as _, BufReader},
  4. path::PathBuf,
  5. process::{Child, Command, Stdio},
  6. };
  7. use cargo_metadata::{
  8. Artifact, CompilerMessage, Message, Metadata, MetadataCommand, Package, Target,
  9. };
  10. /// This crate has a runtime dependency on artifacts produced by the `syscall_ebpf-ebpf` crate.
  11. /// This would be better expressed as one or more [artifact-dependencies][bindeps] but issues such
  12. /// as:
  13. ///
  14. /// * https://github.com/rust-lang/cargo/issues/12374
  15. /// * https://github.com/rust-lang/cargo/issues/12375
  16. /// * https://github.com/rust-lang/cargo/issues/12385
  17. ///
  18. /// prevent their use for the time being.
  19. ///
  20. /// [bindeps]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html?highlight=feature#artifact-dependencies
  21. fn main() {
  22. let Metadata { packages, .. } = MetadataCommand::new().no_deps().exec().unwrap();
  23. let ebpf_package = packages
  24. .into_iter()
  25. .find(|Package { name, .. }| name == "syscall_ebpf-ebpf")
  26. .unwrap();
  27. let out_dir = env::var_os("OUT_DIR").unwrap();
  28. let out_dir = PathBuf::from(out_dir);
  29. let endian = env::var_os("CARGO_CFG_TARGET_ENDIAN").unwrap();
  30. let target = if endian == "big" {
  31. "bpfeb"
  32. } else if endian == "little" {
  33. "bpfel"
  34. } else {
  35. panic!("unsupported endian={:?}", endian)
  36. };
  37. // TODO(https://github.com/rust-lang/cargo/issues/4001): Make this `false` if we can determine
  38. // we're in a check build.
  39. let build_ebpf = true;
  40. if build_ebpf {
  41. let arch = env::var_os("CARGO_CFG_TARGET_ARCH").unwrap();
  42. let target = format!("{target}-unknown-none");
  43. let Package { manifest_path, .. } = ebpf_package;
  44. let ebpf_dir = manifest_path.parent().unwrap();
  45. // We have a build-dependency on `syscall_ebpf-ebpf`, so cargo will automatically rebuild us
  46. // if `syscall_ebpf-ebpf`'s *library* target or any of its dependencies change. Since we
  47. // depend on `syscall_ebpf-ebpf`'s *binary* targets, that only gets us half of the way. This
  48. // stanza ensures cargo will rebuild us on changes to the binaries too, which gets us the
  49. // rest of the way.
  50. println!("cargo:rerun-if-changed={}", ebpf_dir.as_str());
  51. let mut cmd = Command::new("cargo");
  52. cmd.args([
  53. "build",
  54. "-Z",
  55. "build-std=core",
  56. "--bins",
  57. "--message-format=json",
  58. "--release",
  59. "--target",
  60. &target,
  61. ]);
  62. cmd.env("CARGO_CFG_BPF_TARGET_ARCH", arch);
  63. // Workaround to make sure that the rust-toolchain.toml is respected.
  64. for key in ["RUSTUP_TOOLCHAIN", "RUSTC", "RUSTC_WORKSPACE_WRAPPER"] {
  65. cmd.env_remove(key);
  66. }
  67. cmd.current_dir(ebpf_dir);
  68. // Workaround for https://github.com/rust-lang/cargo/issues/6412 where cargo flocks itself.
  69. let ebpf_target_dir = out_dir.join("../syscall_ebpf-ebpf");
  70. cmd.arg("--target-dir").arg(&ebpf_target_dir);
  71. let mut child = cmd
  72. .stdout(Stdio::piped())
  73. .stderr(Stdio::piped())
  74. .spawn()
  75. .unwrap_or_else(|err| panic!("failed to spawn {cmd:?}: {err}"));
  76. let Child { stdout, stderr, .. } = &mut child;
  77. // Trampoline stdout to cargo warnings.
  78. let stderr = stderr.take().unwrap();
  79. let stderr = BufReader::new(stderr);
  80. let stderr = std::thread::spawn(move || {
  81. for line in stderr.lines() {
  82. let line = line.unwrap();
  83. println!("cargo:warning={line}");
  84. }
  85. });
  86. let stdout = stdout.take().unwrap();
  87. let stdout = BufReader::new(stdout);
  88. let mut executables = Vec::new();
  89. for message in Message::parse_stream(stdout) {
  90. #[allow(clippy::collapsible_match)]
  91. match message.expect("valid JSON") {
  92. Message::CompilerArtifact(Artifact {
  93. executable,
  94. target: Target { name, .. },
  95. ..
  96. }) => {
  97. if let Some(executable) = executable {
  98. executables.push((name, executable.into_std_path_buf()));
  99. }
  100. }
  101. Message::CompilerMessage(CompilerMessage { message, .. }) => {
  102. for line in message.rendered.unwrap_or_default().split('\n') {
  103. println!("cargo:warning={line}");
  104. }
  105. }
  106. Message::TextLine(line) => {
  107. println!("cargo:warning={line}");
  108. }
  109. _ => {}
  110. }
  111. }
  112. let status = child
  113. .wait()
  114. .unwrap_or_else(|err| panic!("failed to wait for {cmd:?}: {err}"));
  115. assert_eq!(status.code(), Some(0), "{cmd:?} failed: {status:?}");
  116. stderr.join().map_err(std::panic::resume_unwind).unwrap();
  117. for (name, binary) in executables {
  118. let dst = out_dir.join(name);
  119. let _: u64 = fs::copy(&binary, &dst)
  120. .unwrap_or_else(|err| panic!("failed to copy {binary:?} to {dst:?}: {err}"));
  121. }
  122. } else {
  123. let Package { targets, .. } = ebpf_package;
  124. for Target { name, kind, .. } in targets {
  125. if *kind != ["bin"] {
  126. continue;
  127. }
  128. let dst = out_dir.join(name);
  129. fs::write(&dst, []).unwrap_or_else(|err| panic!("failed to create {dst:?}: {err}"));
  130. }
  131. }
  132. }