mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-11 05:28:35 +00:00
Replace imgui-sys-bindgen with a newly-added xtask setup
This commit is contained in:
parent
60847630df
commit
4f5eba718f
2
.cargo/config
Normal file
2
.cargo/config
Normal file
@ -0,0 +1,2 @@
|
||||
[alias]
|
||||
xtask = "run --package xtask --"
|
||||
@ -7,5 +7,5 @@ members = [
|
||||
"imgui-winit-support",
|
||||
"imgui-examples",
|
||||
"imgui-gfx-examples",
|
||||
"imgui-sys-bindgen",
|
||||
"xtask",
|
||||
]
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
[package]
|
||||
name = "imgui-sys-bindgen"
|
||||
version = "0.0.0"
|
||||
authors = ["The imgui-rs Developers"]
|
||||
edition = "2018"
|
||||
description = "imgui-sys bindings updater"
|
||||
homepage = "https://github.com/imgui-rs/imgui-rs"
|
||||
repository = "https://github.com/imgui-rs/imgui-rs"
|
||||
license = "MIT/Apache-2.0"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
bindgen = "0.56"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
@ -1,123 +0,0 @@
|
||||
use bindgen::{Bindings, EnumVariation, RustTarget};
|
||||
use serde_derive::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::fs::{read_to_string, File};
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct BindgenError;
|
||||
|
||||
impl fmt::Display for BindgenError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Failed to generate bindings")
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for BindgenError {}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct StructsAndEnums {
|
||||
enums: HashMap<String, serde_json::Value>,
|
||||
structs: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct DefinitionArg {
|
||||
#[serde(rename = "type")]
|
||||
type_: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Definition {
|
||||
#[serde(rename = "argsT")]
|
||||
args_t: Vec<DefinitionArg>,
|
||||
ov_cimguiname: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Whitelist {
|
||||
enums: Vec<String>,
|
||||
structs: Vec<String>,
|
||||
definitions: Vec<String>,
|
||||
}
|
||||
|
||||
fn parse_whitelist<R: Read>(
|
||||
structs_and_enums: R,
|
||||
definitions: R,
|
||||
) -> Result<Whitelist, serde_json::Error> {
|
||||
let StructsAndEnums { enums, structs } = serde_json::from_reader(structs_and_enums)?;
|
||||
let enums = enums.keys().cloned().collect();
|
||||
let structs = structs.keys().cloned().collect();
|
||||
|
||||
let definitions: HashMap<String, Vec<Definition>> = serde_json::from_reader(definitions)?;
|
||||
let definitions = definitions
|
||||
.into_iter()
|
||||
.flat_map(|(_, defs)| defs.into_iter())
|
||||
.filter_map(|d| {
|
||||
let uses_va_list = d.args_t.iter().any(|a| a.type_ == "va_list");
|
||||
if uses_va_list {
|
||||
None
|
||||
} else {
|
||||
Some(d.ov_cimguiname)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Whitelist {
|
||||
enums,
|
||||
structs,
|
||||
definitions,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn generate_bindings<P: AsRef<Path>>(
|
||||
path: &P,
|
||||
wasm_import_name: Option<String>,
|
||||
) -> Result<Bindings, Box<dyn Error>> {
|
||||
let path = path.as_ref();
|
||||
let structs_and_enums = File::open(path.join("structs_and_enums.json"))?;
|
||||
let definitions = File::open(path.join("definitions.json"))?;
|
||||
let header = read_to_string(path.join("cimgui.h"))?;
|
||||
|
||||
let whitelist = parse_whitelist(structs_and_enums, definitions)?;
|
||||
let mut builder = bindgen::builder()
|
||||
.raw_line("#![allow(non_upper_case_globals)]")
|
||||
.raw_line("#![allow(non_camel_case_types)]")
|
||||
.raw_line("#![allow(non_snake_case)]")
|
||||
.raw_line("#![allow(clippy::all)]")
|
||||
.header_contents("cimgui.h", &header)
|
||||
.rust_target(RustTarget::Stable_1_40)
|
||||
.default_enum_style(EnumVariation::Consts)
|
||||
.size_t_is_usize(true)
|
||||
.prepend_enum_name(false)
|
||||
.generate_comments(false)
|
||||
.layout_tests(true)
|
||||
.derive_copy(true)
|
||||
.derive_debug(true)
|
||||
.derive_default(true)
|
||||
.derive_hash(true)
|
||||
.derive_partialeq(true)
|
||||
.derive_eq(true)
|
||||
.impl_debug(true)
|
||||
.rustfmt_bindings(true)
|
||||
.clang_arg("-DCIMGUI_DEFINE_ENUMS_AND_STRUCTS=1");
|
||||
|
||||
if let Some(name) = wasm_import_name {
|
||||
builder = builder.wasm_import_module_name(name);
|
||||
}
|
||||
|
||||
for e in whitelist.structs {
|
||||
builder = builder.whitelist_type(format!("^{}", e));
|
||||
}
|
||||
for e in whitelist.enums {
|
||||
builder = builder.whitelist_type(format!("^{}", e));
|
||||
}
|
||||
for e in whitelist.definitions {
|
||||
builder = builder.whitelist_function(format!("^{}", e));
|
||||
}
|
||||
let bindings = builder.generate().map_err(|_| BindgenError)?;
|
||||
Ok(bindings)
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
use imgui_sys_bindgen::generate_bindings;
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
let cwd = env::current_dir().expect("Failed to read current directory");
|
||||
let sys_path = cwd
|
||||
.join("..")
|
||||
.join("imgui-sys")
|
||||
.canonicalize()
|
||||
.expect("Failed to find imgui-sys directory");
|
||||
let bindings = generate_bindings(&sys_path.join("third-party"), None)
|
||||
.expect("Failed to generate bindings");
|
||||
let output_path = sys_path.join("src").join("bindings.rs");
|
||||
bindings
|
||||
.write_to_file(&output_path)
|
||||
.expect("Failed to write bindings");
|
||||
|
||||
let wasm_ffi_import_name = option_env!("IMGUI_RS_WASM_IMPORT_NAME")
|
||||
.map(|s| s.to_string())
|
||||
.or_else(|| Some("imgui-sys-v0".to_string()));
|
||||
|
||||
let wasm_bindings = generate_bindings(&sys_path.join("third-party"), wasm_ffi_import_name)
|
||||
.expect("Failed to generate bindings");
|
||||
let output_path = sys_path.join("src").join("wasm_bindings.rs");
|
||||
wasm_bindings
|
||||
.write_to_file(&output_path)
|
||||
.expect("Failed to write wasm bindings");
|
||||
}
|
||||
@ -1,9 +1,6 @@
|
||||
/* automatically generated by rust-bindgen 0.55.1 */
|
||||
/* automatically generated by rust-bindgen 0.56.0 */
|
||||
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(clippy::all)]
|
||||
#![allow(nonstandard_style, clippy::all)]
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
/* automatically generated by rust-bindgen 0.55.1 */
|
||||
/* automatically generated by rust-bindgen 0.56.0 */
|
||||
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(clippy::all)]
|
||||
#![allow(nonstandard_style, clippy::all)]
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
|
||||
12
xtask/Cargo.toml
Normal file
12
xtask/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
# See https://github.com/matklad/cargo-xtask for more on this pattern.
|
||||
[package]
|
||||
name = "xtask"
|
||||
version = "0.1.0"
|
||||
authors = ["Thom Chiovoloni <chiovolonit@gmail.com>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
pico-args = "0.3"
|
||||
smoljson = "0.1"
|
||||
155
xtask/src/bindgen.rs
Normal file
155
xtask/src/bindgen.rs
Normal file
@ -0,0 +1,155 @@
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub struct GenBindings {
|
||||
pub bindings_path: Option<String>,
|
||||
pub output_path: Option<String>,
|
||||
pub wasm_import_name: Option<String>,
|
||||
}
|
||||
|
||||
impl GenBindings {
|
||||
pub fn run(self) -> Result<()> {
|
||||
let root = crate::project_root();
|
||||
let bindings = self
|
||||
.bindings_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",
|
||||
"--with-derive-default",
|
||||
"--with-derive-partialeq",
|
||||
"--with-derive-eq",
|
||||
"--with-derive-hash",
|
||||
"--impl-debug",
|
||||
];
|
||||
cmd.args(a);
|
||||
cmd.args(&["--blacklist-type", "__darwin_size_t"]);
|
||||
cmd.args(&["--raw-line", "#![allow(nonstandard_style, clippy::all)]"]);
|
||||
cmd.arg("--output").arg(output);
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
// impl Deref for CountingCommand {
|
||||
// type Target = std::process::Command;
|
||||
// fn deref(&self) -> &Self::Target {
|
||||
// &self.c
|
||||
// }
|
||||
// }
|
||||
// impl std::ops::DerefMut for CountingCommand {
|
||||
// fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
// &mut self.c
|
||||
// }
|
||||
// }
|
||||
30
xtask/src/lib.rs
Normal file
30
xtask/src/lib.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use std::{
|
||||
env,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
pub mod bindgen;
|
||||
|
||||
pub fn project_root() -> PathBuf {
|
||||
Path::new(
|
||||
&env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()),
|
||||
)
|
||||
.ancestors()
|
||||
.nth(1)
|
||||
.unwrap()
|
||||
.to_path_buf()
|
||||
}
|
||||
|
||||
pub fn pushd(p: impl AsRef<Path>) -> std::io::Result<Pushd> {
|
||||
let cwd = std::env::current_dir()?;
|
||||
std::env::set_current_dir(p.as_ref())?;
|
||||
Ok(Pushd(cwd))
|
||||
}
|
||||
pub struct Pushd(PathBuf);
|
||||
impl Drop for Pushd {
|
||||
fn drop(&mut self) {
|
||||
if let Err(e) = std::env::set_current_dir(&self.0) {
|
||||
eprintln!("warning: popd failed: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
59
xtask/src/main.rs
Normal file
59
xtask/src/main.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use anyhow::Result;
|
||||
use pico_args::Arguments;
|
||||
use xtask::bindgen::GenBindings;
|
||||
use xtask::{project_root, pushd};
|
||||
|
||||
fn main() {
|
||||
if let Err(e) = try_main() {
|
||||
eprintln!("{}", e);
|
||||
for cause in e.chain().skip(1) {
|
||||
eprintln!("Caused By: {}", cause);
|
||||
}
|
||||
std::process::exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
const HELP: &str = "\
|
||||
cargo xtask
|
||||
Run custom build command.
|
||||
|
||||
USAGE:
|
||||
cargo xtask <SUBCOMMAND>
|
||||
|
||||
SUBCOMMANDS:
|
||||
bindgen - produce bindings using bindgen \
|
||||
must have bindgen installed, may require unix \
|
||||
as we pass `bindgen` very many CLI args
|
||||
TODO:
|
||||
run - run or list examples
|
||||
lint-all - run clippy as we would in CI
|
||||
test-all - run the tests we'd run in CI
|
||||
";
|
||||
|
||||
fn try_main() -> Result<()> {
|
||||
let _g = pushd(project_root())?;
|
||||
|
||||
let mut args = Arguments::from_env();
|
||||
let subcommand = args.subcommand()?.unwrap_or_default();
|
||||
|
||||
match subcommand.as_str() {
|
||||
"bindgen" => {
|
||||
// none of these are required.
|
||||
let cmd = GenBindings {
|
||||
// defaults to <project>/imgui-sys/third-party
|
||||
bindings_path: args.opt_value_from_str("--cimgui-dir")?,
|
||||
// defaults to <project>/imgui-sys/src
|
||||
output_path: args.opt_value_from_str("--output-dir")?,
|
||||
// defaults to "imgui-sys-v0", but can be set in
|
||||
// env("IMGUI_RS_WASM_IMPORT_NAME")
|
||||
wasm_import_name: args.opt_value_from_str("--wasm-import-name")?,
|
||||
};
|
||||
args.finish()?;
|
||||
cmd.run()?;
|
||||
}
|
||||
_ => {
|
||||
eprintln!("{}", HELP);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user