build.rs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. use std::{
  2. env,
  3. ffi::OsString,
  4. fs,
  5. io::{BufRead as _, BufReader},
  6. path::PathBuf,
  7. process::{Child, Command, Stdio},
  8. };
  9. use cargo_metadata::{
  10. Artifact, CompilerMessage, Message, Metadata, MetadataCommand, Package, Target,
  11. };
  12. use xtask::{exec, AYA_BUILD_INTEGRATION_BPF, LIBBPF_DIR};
  13. fn main() {
  14. println!("cargo:rerun-if-env-changed={}", AYA_BUILD_INTEGRATION_BPF);
  15. let build_integration_bpf = env::var(AYA_BUILD_INTEGRATION_BPF)
  16. .as_deref()
  17. .map(str::parse)
  18. .map(Result::unwrap)
  19. .unwrap_or_default();
  20. let Metadata { packages, .. } = MetadataCommand::new().no_deps().exec().unwrap();
  21. let integration_ebpf_package = packages
  22. .into_iter()
  23. .find(|Package { name, .. }| name == "integration-ebpf")
  24. .unwrap();
  25. let manifest_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap();
  26. let manifest_dir = PathBuf::from(manifest_dir);
  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. const C_BPF_PROBES: &[(&str, &str)] = &[
  38. ("ext.bpf.c", "ext.bpf.o"),
  39. ("main.bpf.c", "main.bpf.o"),
  40. ("multimap-btf.bpf.c", "multimap-btf.bpf.o"),
  41. ("text_64_64_reloc.c", "text_64_64_reloc.o"),
  42. ];
  43. let c_bpf_probes = C_BPF_PROBES
  44. .iter()
  45. .map(|(src, dst)| (src, out_dir.join(dst)));
  46. if build_integration_bpf {
  47. let libbpf_dir = manifest_dir
  48. .parent()
  49. .unwrap()
  50. .parent()
  51. .unwrap()
  52. .join(LIBBPF_DIR);
  53. println!("cargo:rerun-if-changed={}", libbpf_dir.to_str().unwrap());
  54. let libbpf_headers_dir = out_dir.join("libbpf_headers");
  55. let mut includedir = OsString::new();
  56. includedir.push("INCLUDEDIR=");
  57. includedir.push(&libbpf_headers_dir);
  58. exec(
  59. Command::new("make")
  60. .arg("-C")
  61. .arg(libbpf_dir.join("src"))
  62. .arg(includedir)
  63. .arg("install_headers"),
  64. )
  65. .unwrap();
  66. let bpf_dir = manifest_dir.join("bpf");
  67. let mut target_arch = OsString::new();
  68. target_arch.push("-D__TARGET_ARCH_");
  69. let arch = env::var_os("CARGO_CFG_TARGET_ARCH").unwrap();
  70. if arch == "x86_64" {
  71. target_arch.push("x86");
  72. } else if arch == "aarch64" {
  73. target_arch.push("arm64");
  74. } else {
  75. target_arch.push(arch);
  76. };
  77. for (src, dst) in c_bpf_probes {
  78. let src = bpf_dir.join(src);
  79. println!("cargo:rerun-if-changed={}", src.to_str().unwrap());
  80. exec(
  81. Command::new("clang")
  82. .arg("-I")
  83. .arg(&libbpf_headers_dir)
  84. .args(["-g", "-O2", "-target", target, "-c"])
  85. .arg(&target_arch)
  86. .arg(src)
  87. .arg("-o")
  88. .arg(dst),
  89. )
  90. .unwrap();
  91. }
  92. let target = format!("{target}-unknown-none");
  93. let Package { manifest_path, .. } = integration_ebpf_package;
  94. let integration_ebpf_dir = manifest_path.parent().unwrap();
  95. let mut cmd = Command::new("cargo");
  96. cmd.args([
  97. "build",
  98. "-Z",
  99. "build-std=core",
  100. "--release",
  101. "--message-format=json",
  102. "--target",
  103. &target,
  104. ]);
  105. // Workaround to make sure that the rust-toolchain.toml is respected.
  106. cmd.env_remove("RUSTUP_TOOLCHAIN")
  107. .current_dir(integration_ebpf_dir);
  108. // Workaround for https://github.com/rust-lang/cargo/issues/6412 where cargo flocks itself.
  109. let ebpf_target_dir = out_dir.join("integration-ebpf");
  110. cmd.arg("--target-dir").arg(&ebpf_target_dir);
  111. let mut child = cmd
  112. .stdout(Stdio::piped())
  113. .stderr(Stdio::piped())
  114. .spawn()
  115. .unwrap_or_else(|err| panic!("failed to spawn {cmd:?}: {err}"));
  116. let Child { stdout, stderr, .. } = &mut child;
  117. // Trampoline stdout to cargo warnings.
  118. let stderr = stderr.take().unwrap();
  119. let stderr = BufReader::new(stderr);
  120. let stderr = std::thread::spawn(move || {
  121. for line in stderr.lines() {
  122. let line = line.unwrap();
  123. println!("cargo:warning={line}");
  124. }
  125. });
  126. let stdout = stdout.take().unwrap();
  127. let stdout = BufReader::new(stdout);
  128. let mut executables = Vec::new();
  129. for message in Message::parse_stream(stdout) {
  130. #[allow(clippy::collapsible_match)]
  131. match message.expect("valid JSON") {
  132. Message::CompilerArtifact(Artifact {
  133. executable,
  134. target: Target { name, .. },
  135. ..
  136. }) => {
  137. if let Some(executable) = executable {
  138. executables.push((name, executable.into_std_path_buf()));
  139. }
  140. }
  141. Message::CompilerMessage(CompilerMessage { message, .. }) => {
  142. println!("cargo:warning={message}");
  143. }
  144. Message::TextLine(line) => {
  145. println!("cargo:warning={line}");
  146. }
  147. _ => {}
  148. }
  149. }
  150. let status = child
  151. .wait()
  152. .unwrap_or_else(|err| panic!("failed to wait for {cmd:?}: {err}"));
  153. match status.code() {
  154. Some(code) => match code {
  155. 0 => {}
  156. code => panic!("{cmd:?} exited with status code {code}"),
  157. },
  158. None => panic!("{cmd:?} terminated by signal"),
  159. }
  160. stderr.join().map_err(std::panic::resume_unwind).unwrap();
  161. for (name, binary) in executables {
  162. let dst = out_dir.join(name);
  163. let _: u64 = fs::copy(&binary, &dst)
  164. .unwrap_or_else(|err| panic!("failed to copy {binary:?} to {dst:?}: {err}"));
  165. }
  166. } else {
  167. for (_src, dst) in c_bpf_probes {
  168. fs::write(&dst, []).unwrap_or_else(|err| panic!("failed to create {dst:?}: {err}"));
  169. }
  170. let Package { targets, .. } = integration_ebpf_package;
  171. for Target { name, kind, .. } in targets {
  172. if *kind != ["bin"] {
  173. continue;
  174. }
  175. let dst = out_dir.join(name);
  176. fs::write(&dst, []).unwrap_or_else(|err| panic!("failed to create {dst:?}: {err}"));
  177. }
  178. }
  179. }