-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathbindings_generator.rs
101 lines (87 loc) · 3.1 KB
/
bindings_generator.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
use anyhow::{bail, Result};
use std::{env, fs, path, process::Command};
pub(crate) struct BindingsGenerator<'a> {
pub(crate) cargo: &'a str,
pub(crate) crate_dir: &'a str,
pub(crate) crate_name: &'a str,
pub(crate) expand_args: Vec<String>,
}
pub fn inject_rid_ffi_types(stdout: &[u8]) -> String {
format!(
"{stdout}\n{rid_ffi}",
stdout = std::str::from_utf8(stdout).unwrap(),
rid_ffi = rid_ffi::code_rid_vec()
)
}
impl<'a> BindingsGenerator<'a> {
pub(crate) fn generate(&self) -> Result<cbindgen::Bindings> {
let expanded_rust_path = self.expand_crate()?;
let bindings = self.cbindgen(&expanded_rust_path)?;
Ok(bindings)
}
// cargo rustc --lib -- -Zunpretty=expanded
fn expand_crate(&self) -> Result<String> {
let output = Command::new(&self.cargo)
.args(&self.expand_args)
.current_dir(&self.crate_dir)
.output()?;
let stderr = std::str::from_utf8(&output.stderr).unwrap();
// TODO: this needs to be less brittle, i.e. if any message contains 'error:' we'd bail
if stderr.contains("error:") {
bail!("\n'cargo expand' encountered error(s): \n\n{}\n\n", stderr);
}
let expanded_rust_path = self.expand_crate_path();
let code = inject_rid_ffi_types(&output.stdout);
fs::write(&expanded_rust_path, code)?;
Ok(format!(
"{}",
&expanded_rust_path.as_path().to_str().unwrap()
))
}
fn cbindgen(&self, expanded_rust_path: &str) -> Result<cbindgen::Bindings> {
let built = cbindgen::Builder::new()
.with_src(expanded_rust_path)
.with_language(cbindgen::Language::C)
.with_include_version(true)
.with_no_includes()
.with_include("stdint.h")
.with_parse_deps(false)
.generate()?;
Ok(built)
}
fn expand_crate_path(&self) -> path::PathBuf {
let mut root = env::temp_dir();
root.push(format!("rid_{}_expanded.rs", self.crate_name));
root
}
}
// TODO: disabled due to getting stuck
#[cfg(test_disabled)]
mod tests {
use super::*;
fn bindings_path(crate_name: &str) -> String {
let mut root = env::temp_dir();
root.push(format!("rid_test_{}_binding.h", crate_name));
root.to_str().unwrap().to_string()
}
#[test]
fn binding_generator() {
let crate_dir = "./fixtures/foo-bar-baz";
let crate_name = "foo-bar-baz";
let binding_h = bindings_path(&crate_name);
let generator = BindingsGenerator {
cargo: "cargo",
crate_dir,
crate_name,
};
let bindings = generator.generate().unwrap();
bindings.write_to_file(&binding_h);
eprintln!("binding.h written to '{}'", &binding_h);
let binding_h = fs::read_to_string(&binding_h).unwrap();
assert!(binding_h.len() > 0);
assert!(binding_h.contains("typedef struct Foo Foo;"));
assert!(
binding_h.contains("FFI access methods generated for struct 'Baz'")
);
}
}