extern crate bindgen; use std::env; use std::fs::File; use std::io::{BufReader, BufRead}; use std::path::{PathBuf, Path}; use std::process::Command; const CRATE_DIR: &str = env!("CARGO_MANIFEST_DIR"); const CIMGUI_ORIGIN_URL: &str = "https://github.com/cimgui/cimgui.git"; /// Generates raw bindings to the (always) latest cimgui. /// /// It will first update cimgui, or clone it if it's not found. /// It will then bindgen `header.h`, which contains an include of `cimgui.h` /// along with the necessary declaration. fn main() { let cimgui_source_path_str = format!("{}/cimgui", CRATE_DIR); if Path::new(&cimgui_source_path_str).is_dir() { println!("updating cimgui..."); Command::new("git") .current_dir(CRATE_DIR) .args(["submodule", "update", "--init", "--recursive"]) .status() .expect("Failed to update cimgui."); } else { println!("cimgui not found; cloning..."); Command::new("git") .current_dir(CRATE_DIR) .args(["clone", "--recursive", CIMGUI_ORIGIN_URL]) .status() .expect("Failed to clone cimgui."); } println!("cargo:rerun-if-changed=wrapper.h"); let bindings = bindgen::Builder::default() .header("wrapper.h") // Tell cargo to invalidate the built crate whenever any of the // included header files changed. .parse_callbacks(Box::new(bindgen::CargoCallbacks)) .generate() .expect("Unable to generate bindings"); let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); bindings .write_to_file(out_path.join("bindings.rs")) .expect("Couldn't write bindings!"); // TODO Change crate version programmatically with this. // let cimgui_version = get_cimgui_version(); } const IMGUI_VERSION_DEFINITION: &str = "define IMGUI_VERSION_NUM"; /// Gets the updated/cloned major, minor and patch components of cimgui. /// /// It will look through the *imgui*.h header file for its version definition. /// /// # Panics /// If it reaches EOF without finding /// TODO Change to Result fn get_cimgui_version() -> Vec { let header = File::open("cimgui/imgui/imgui.h") .unwrap(); let mut header_bufreader = BufReader::new(header); let mut read: Vec = Vec::new(); loop { header_bufreader.read_until(b'#', &mut read) .expect("Couldn't read header until a `#` declaration!"); read.clear(); let mut line = String::new(); let read_line_result = header_bufreader.read_line(&mut line); // Has read the line without the `#`. if line.starts_with(IMGUI_VERSION_DEFINITION) { let raw_version = line.replace(IMGUI_VERSION_DEFINITION, ""); let trimmed_version = raw_version.trim(); // ~"XYYzz" let (major, minor_and_patch) = trimmed_version.split_at(1); // ~"YYzz" let (minor, patch) = minor_and_patch.split_at(2); return Vec::from([ major.to_string(), minor.to_string(), patch.to_string() ]); } else { // Replacement for `if read_line_result.contains(0)` // Why is it still "unstable"? read_line_result.map_or((), |ok_value| // read_line() returns Ok(0) when it reaches EOF. if ok_value == 0 { panic!("Reached EOL without finding imgui version!"); } ); } } }