build.rs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. use std::{
  2. env,
  3. ffi::OsString,
  4. fmt::Write as _,
  5. fs::copy,
  6. io::BufReader,
  7. path::PathBuf,
  8. process::{Child, Command, Stdio},
  9. };
  10. use cargo_metadata::{Artifact, CompilerMessage, Message, Target};
  11. fn main() {
  12. let manifest_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap();
  13. let manifest_dir = PathBuf::from(manifest_dir);
  14. let out_dir = env::var_os("OUT_DIR").unwrap();
  15. let out_dir = PathBuf::from(out_dir);
  16. let libbpf_dir = manifest_dir
  17. .parent()
  18. .unwrap()
  19. .parent()
  20. .unwrap()
  21. .join("libbpf");
  22. let libbpf_headers_dir = out_dir.join("libbpf_headers");
  23. let mut includedir = OsString::new();
  24. includedir.push("INCLUDEDIR=");
  25. includedir.push(&libbpf_headers_dir);
  26. let mut cmd = Command::new("make");
  27. cmd.arg("-C")
  28. .arg(libbpf_dir.join("src"))
  29. .arg(includedir)
  30. .arg("install_headers");
  31. let status = cmd
  32. .status()
  33. .unwrap_or_else(|err| panic!("failed to run {cmd:?}: {err}"));
  34. match status.code() {
  35. Some(code) => match code {
  36. 0 => {}
  37. code => panic!("{cmd:?} exited with code {code}"),
  38. },
  39. None => panic!("{cmd:?} terminated by signal"),
  40. }
  41. let bpf_dir = manifest_dir.join("bpf");
  42. let endian = env::var_os("CARGO_CFG_TARGET_ENDIAN").unwrap();
  43. let target = if endian == "big" {
  44. "bpfeb"
  45. } else if endian == "little" {
  46. "bpfel"
  47. } else {
  48. panic!("unsupported endian={:?}", endian)
  49. };
  50. let mut target_arch = OsString::new();
  51. target_arch.push("-D__TARGET_ARCH_");
  52. let arch = env::var_os("CARGO_CFG_TARGET_ARCH").unwrap();
  53. if arch == "x86_64" {
  54. target_arch.push("x86");
  55. } else if arch == "aarch64" {
  56. target_arch.push("arm64");
  57. } else {
  58. target_arch.push(arch);
  59. };
  60. for (src, dst) in [
  61. ("ext.bpf.c", "ext.bpf.o"),
  62. ("main.bpf.c", "main.bpf.o"),
  63. ("multimap-btf.bpf.c", "multimap-btf.bpf.o"),
  64. ("text_64_64_reloc.c", "text_64_64_reloc.o"),
  65. ] {
  66. let src = bpf_dir.join(src);
  67. let out = out_dir.join(dst);
  68. let mut cmd = Command::new("clang");
  69. cmd.arg("-I")
  70. .arg(&libbpf_headers_dir)
  71. .args(["-g", "-O2", "-target", target, "-c"])
  72. .arg(&target_arch)
  73. .arg(src)
  74. .arg("-o")
  75. .arg(out);
  76. let status = cmd
  77. .status()
  78. .unwrap_or_else(|err| panic!("failed to run {cmd:?}: {err}"));
  79. match status.code() {
  80. Some(code) => match code {
  81. 0 => {}
  82. code => panic!("{cmd:?} exited with code {code}"),
  83. },
  84. None => panic!("{cmd:?} terminated by signal"),
  85. }
  86. }
  87. let ebpf_dir = manifest_dir.parent().unwrap().join("integration-ebpf");
  88. let target = format!("{target}-unknown-none");
  89. let mut cmd = Command::new("cargo");
  90. cmd.current_dir(&ebpf_dir).args([
  91. "build",
  92. "-Z",
  93. "build-std=core",
  94. "--release",
  95. "--message-format=json",
  96. "--target",
  97. &target,
  98. ]);
  99. let mut child = cmd
  100. .stdout(Stdio::piped())
  101. .spawn()
  102. .unwrap_or_else(|err| panic!("failed to spawn {cmd:?}: {err}"));
  103. let Child { stdout, .. } = &mut child;
  104. let stdout = stdout.take().unwrap();
  105. let reader = BufReader::new(stdout);
  106. let mut executables = Vec::new();
  107. let mut compiler_messages = String::new();
  108. for message in Message::parse_stream(reader) {
  109. #[allow(clippy::collapsible_match)]
  110. match message.expect("valid JSON") {
  111. Message::CompilerArtifact(Artifact {
  112. executable,
  113. target: Target { name, .. },
  114. ..
  115. }) => {
  116. if let Some(executable) = executable {
  117. executables.push((name, executable.into_std_path_buf()));
  118. }
  119. }
  120. Message::CompilerMessage(CompilerMessage { message, .. }) => {
  121. writeln!(&mut compiler_messages, "{message}").unwrap()
  122. }
  123. _ => {}
  124. }
  125. }
  126. let status = child
  127. .wait()
  128. .unwrap_or_else(|err| panic!("failed to wait for {cmd:?}: {err}"));
  129. match status.code() {
  130. Some(code) => match code {
  131. 0 => {}
  132. code => panic!("{cmd:?} exited with status code {code}:\n{compiler_messages}"),
  133. },
  134. None => panic!("{cmd:?} terminated by signal"),
  135. }
  136. for (name, binary) in executables {
  137. let dst = out_dir.join(name);
  138. let _: u64 = copy(&binary, &dst)
  139. .unwrap_or_else(|err| panic!("failed to copy {binary:?} to {dst:?}: {err}"));
  140. }
  141. }