mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-11 21:48:36 +00:00
146 lines
4.7 KiB
Rust
146 lines
4.7 KiB
Rust
use crate::flags::Bindgen;
|
||
use anyhow::{anyhow, Context, Result};
|
||
use std::path::{Path, PathBuf};
|
||
|
||
impl Bindgen {
|
||
pub fn run(self) -> Result<()> {
|
||
let root = crate::project_root();
|
||
let bindings = self
|
||
.cimgui_path
|
||
.map(PathBuf::from)
|
||
.unwrap_or_else(|| root.join("imgui-sys/third-party"));
|
||
|
||
let output = self
|
||
.output_path
|
||
.map(PathBuf::from)
|
||
.unwrap_or_else(|| root.join("imgui-sys/src"));
|
||
|
||
let wasm_name = self
|
||
.wasm_import_name
|
||
.or_else(|| std::env::var("IMGUI_RS_WASM_IMPORT_NAME").ok())
|
||
.unwrap_or_else(|| "imgui-sys-v0".to_string());
|
||
|
||
let types = get_types(&bindings.join("structs_and_enums.json"))?;
|
||
let funcs = get_definitions(&bindings.join("definitions.json"))?;
|
||
let header = bindings.join("cimgui.h");
|
||
|
||
generate_binding_file(&header, &output.join("bindings.rs"), &types, &funcs, None)?;
|
||
generate_binding_file(
|
||
&header,
|
||
&output.join("wasm_bindings.rs"),
|
||
&types,
|
||
&funcs,
|
||
Some(&wasm_name),
|
||
)?;
|
||
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
fn get_types(structs_and_enums: &Path) -> Result<Vec<String>> {
|
||
let types_txt = std::fs::read_to_string(structs_and_enums)?;
|
||
let types_val = types_txt
|
||
.parse::<smoljson::ValOwn>()
|
||
.map_err(|e| anyhow!("Failed to parse {}: {:?}", structs_and_enums.display(), e))?;
|
||
let mut types: Vec<String> = types_val["enums"]
|
||
.as_object()
|
||
.ok_or_else(|| anyhow!("No `enums` in bindings file"))?
|
||
.keys()
|
||
.map(|k| format!("^{}", k))
|
||
.collect();
|
||
types.extend(
|
||
types_val["structs"]
|
||
.as_object()
|
||
.ok_or_else(|| anyhow!("No `structs` in bindings file"))?
|
||
.keys()
|
||
.map(|k| format!("^{}", k)),
|
||
);
|
||
Ok(types)
|
||
}
|
||
|
||
fn get_definitions(definitions: &Path) -> Result<Vec<String>> {
|
||
fn bad_arg_type(s: &str) -> bool {
|
||
s == "va_list" || s.starts_with("__")
|
||
}
|
||
let defs_txt = std::fs::read_to_string(definitions)?;
|
||
let defs_val = defs_txt
|
||
.parse::<smoljson::ValOwn>()
|
||
.map_err(|e| anyhow!("Failed to parse {}: {:?}", definitions.display(), e))?;
|
||
let definitions = defs_val
|
||
.into_object()
|
||
.ok_or_else(|| anyhow!("bad json data in defs file"))?;
|
||
let mut keep_defs = vec![];
|
||
for (name, def) in definitions {
|
||
let defs = def
|
||
.into_array()
|
||
.ok_or_else(|| anyhow!("def {} not an array", &name))?;
|
||
keep_defs.reserve(defs.len());
|
||
for func in defs {
|
||
let args = func["argsT"].as_array().unwrap();
|
||
if !args
|
||
.iter()
|
||
.any(|a| a["type"].as_str().map_or(false, bad_arg_type))
|
||
{
|
||
let name = func["ov_cimguiname"]
|
||
.as_str()
|
||
.ok_or_else(|| anyhow!("ov_cimguiname wasnt string..."))?;
|
||
keep_defs.push(format!("^{}", name));
|
||
}
|
||
}
|
||
}
|
||
Ok(keep_defs)
|
||
}
|
||
|
||
fn generate_binding_file(
|
||
header: &Path,
|
||
output: &Path,
|
||
types: &[String],
|
||
funcs: &[String],
|
||
wasm_import_mod: Option<&str>,
|
||
) -> Result<()> {
|
||
let mut cmd = std::process::Command::new("bindgen");
|
||
let a = &[
|
||
"--size_t-is-usize",
|
||
"--no-prepend-enum-name",
|
||
"--no-doc-comments",
|
||
// Layout tests aren't portable (they hardcode type sizes), and for
|
||
// our case they just serve to sanity check rustc's implementation of
|
||
// `#[repr(C)]`. If we bind directly to C++ ever, we should reconsider this.
|
||
"--no-layout-tests",
|
||
"--with-derive-default",
|
||
"--with-derive-partialeq",
|
||
"--with-derive-eq",
|
||
"--with-derive-hash",
|
||
"--impl-debug",
|
||
"--use-core",
|
||
];
|
||
cmd.args(a);
|
||
cmd.args(&["--blacklist-type", "__darwin_size_t"]);
|
||
cmd.args(&["--raw-line", "#![allow(nonstandard_style, clippy::all)]"]);
|
||
cmd.arg("--output").arg(output);
|
||
cmd.args(&["--ctypes-prefix", "cty"]);
|
||
|
||
if let Some(name) = wasm_import_mod {
|
||
cmd.args(&["--wasm-import-module-name", &name]);
|
||
}
|
||
for t in types {
|
||
cmd.args(&["--whitelist-type", t]);
|
||
}
|
||
for f in funcs {
|
||
cmd.args(&["--whitelist-function", f]);
|
||
}
|
||
cmd.arg(header);
|
||
cmd.args(&["--", "-DCIMGUI_DEFINE_ENUMS_AND_STRUCTS=1"]);
|
||
eprintln!("Executing bindgen [output = {}]", output.display());
|
||
let status = cmd.status().context("Failed to exacute bindgen")?;
|
||
if !status.success() {
|
||
anyhow!(
|
||
"Failed to execute bindgen: {}, see output for details",
|
||
status
|
||
);
|
||
}
|
||
eprintln!("Success [output = {}]", output.display());
|
||
|
||
Ok(())
|
||
}
|