generate.rs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. use std::{
  2. fs::{self, File},
  3. io::{self, Write},
  4. path::{Path, PathBuf},
  5. process::Command,
  6. str,
  7. };
  8. use tempfile::tempdir;
  9. use thiserror::Error;
  10. use crate::bindgen;
  11. #[derive(Error, Debug)]
  12. pub enum Error {
  13. #[error("error executing bpftool")]
  14. BpfTool(#[source] io::Error),
  15. #[error("{stderr}\nbpftool failed with exit code {code}")]
  16. BpfToolExit { code: i32, stderr: String },
  17. #[error("bindgen failed")]
  18. Bindgen(#[source] io::Error),
  19. #[error("{stderr}\nbindgen failed with exit code {code}")]
  20. BindgenExit { code: i32, stderr: String },
  21. #[error("rustfmt failed")]
  22. Rustfmt(#[source] io::Error),
  23. #[error("error reading header file")]
  24. ReadHeaderFile(#[source] io::Error),
  25. }
  26. pub enum InputFile {
  27. Btf(PathBuf),
  28. Header(PathBuf),
  29. }
  30. pub fn generate<T: AsRef<str>>(
  31. input_file: InputFile,
  32. types: &[T],
  33. additional_flags: &[T],
  34. ) -> Result<String, Error> {
  35. let additional_flags = additional_flags
  36. .iter()
  37. .map(|s| s.as_ref().into())
  38. .collect::<Vec<_>>();
  39. let mut bindgen = bindgen::bpf_builder();
  40. let (additional_flags, ctypes_prefix) = extract_ctypes_prefix(&additional_flags);
  41. if let Some(prefix) = ctypes_prefix {
  42. bindgen = bindgen.ctypes_prefix(prefix)
  43. }
  44. for ty in types {
  45. bindgen = bindgen.allowlist_type(ty);
  46. }
  47. let (c_header, name) = match &input_file {
  48. InputFile::Btf(path) => (c_header_from_btf(path)?, "kernel_types.h"),
  49. InputFile::Header(header) => (
  50. fs::read_to_string(header).map_err(Error::ReadHeaderFile)?,
  51. header.file_name().unwrap().to_str().unwrap(),
  52. ),
  53. };
  54. let dir = tempdir().unwrap();
  55. let file_path = dir.path().join(name);
  56. let mut file = File::create(&file_path).unwrap();
  57. let _ = file.write(c_header.as_bytes()).unwrap();
  58. let flags = combine_flags(&bindgen.command_line_flags(), &additional_flags);
  59. let output = Command::new("bindgen")
  60. .arg(file_path)
  61. .args(flags)
  62. .output()
  63. .map_err(Error::Bindgen)?;
  64. if !output.status.success() {
  65. return Err(Error::BindgenExit {
  66. code: output.status.code().unwrap(),
  67. stderr: str::from_utf8(&output.stderr).unwrap().to_owned(),
  68. });
  69. }
  70. Ok(str::from_utf8(&output.stdout).unwrap().to_owned())
  71. }
  72. fn c_header_from_btf(path: &Path) -> Result<String, Error> {
  73. let output = Command::new("bpftool")
  74. .args(["btf", "dump", "file"])
  75. .arg(path)
  76. .args(["format", "c"])
  77. .output()
  78. .map_err(Error::BpfTool)?;
  79. if !output.status.success() {
  80. return Err(Error::BpfToolExit {
  81. code: output.status.code().unwrap(),
  82. stderr: str::from_utf8(&output.stderr).unwrap().to_owned(),
  83. });
  84. }
  85. Ok(str::from_utf8(&output.stdout).unwrap().to_owned())
  86. }
  87. fn extract_ctypes_prefix(s: &[String]) -> (Vec<String>, Option<String>) {
  88. if let Some(index) = s.iter().position(|el| el == "--ctypes-prefix") {
  89. if index < s.len() - 1 {
  90. let mut flags = Vec::new();
  91. flags.extend_from_slice(&s[0..index]);
  92. // skip ["--ctypes-prefix", "value"]
  93. flags.extend_from_slice(&s[index + 2..]);
  94. return (flags, s.get(index + 1).cloned());
  95. }
  96. }
  97. (s.to_vec(), None)
  98. }
  99. fn combine_flags(s1: &[String], s2: &[String]) -> Vec<String> {
  100. let mut flags = Vec::new();
  101. let mut extra = Vec::new();
  102. for s in [s1, s2] {
  103. let mut s = s.splitn(2, |el| el == "--");
  104. // append args
  105. flags.extend(s.next().unwrap().iter().cloned());
  106. if let Some(e) = s.next() {
  107. // append extra args
  108. extra.extend(e.iter().cloned());
  109. }
  110. }
  111. // append extra args
  112. if !extra.is_empty() {
  113. flags.push("--".to_string());
  114. flags.extend(extra);
  115. }
  116. flags
  117. }
  118. #[cfg(test)]
  119. mod test {
  120. use super::{combine_flags, extract_ctypes_prefix};
  121. fn to_vec(s: &str) -> Vec<String> {
  122. s.split(' ').map(|x| x.into()).collect()
  123. }
  124. #[test]
  125. fn test_extract_ctypes_prefix() {
  126. let (flags, prefix) = extract_ctypes_prefix(&to_vec("foo --ctypes-prefix bar baz"));
  127. assert_eq!(flags, to_vec("foo baz"));
  128. assert_eq!(prefix.as_deref(), Some("bar"));
  129. let (flags, prefix) = extract_ctypes_prefix(&to_vec("foo --ctypes-prefi bar baz"));
  130. assert_eq!(flags, to_vec("foo --ctypes-prefi bar baz"));
  131. assert_eq!(prefix, None);
  132. let (flags, prefix) = extract_ctypes_prefix(&to_vec("--foo bar --ctypes-prefix"));
  133. assert_eq!(flags, to_vec("--foo bar --ctypes-prefix"));
  134. assert_eq!(prefix, None);
  135. let (flags, prefix) = extract_ctypes_prefix(&to_vec("--ctypes-prefix foo"));
  136. let empty: Vec<String> = Vec::new();
  137. assert_eq!(flags, empty);
  138. assert_eq!(prefix.as_deref(), Some("foo"));
  139. let (flags, prefix) = extract_ctypes_prefix(&to_vec("--ctypes-prefix"));
  140. assert_eq!(flags, to_vec("--ctypes-prefix"));
  141. assert_eq!(prefix, None);
  142. }
  143. #[test]
  144. fn test_combine_flags() {
  145. assert_eq!(
  146. combine_flags(&to_vec("a b"), &to_vec("c d"),).join(" "),
  147. "a b c d",
  148. );
  149. assert_eq!(
  150. combine_flags(&to_vec("a -- b"), &to_vec("a b"),).join(" "),
  151. "a a b -- b",
  152. );
  153. assert_eq!(
  154. combine_flags(&to_vec("a -- b"), &to_vec("c d"),).join(" "),
  155. "a c d -- b",
  156. );
  157. assert_eq!(
  158. combine_flags(&to_vec("a b"), &to_vec("c -- d"),).join(" "),
  159. "a b c -- d",
  160. );
  161. assert_eq!(
  162. combine_flags(&to_vec("a -- b"), &to_vec("c -- d"),).join(" "),
  163. "a c -- b d",
  164. );
  165. assert_eq!(
  166. combine_flags(&to_vec("a -- b"), &to_vec("-- c d"),).join(" "),
  167. "a -- b c d",
  168. );
  169. }
  170. }