build.rs 6.7 KB

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