|
- #![allow(clippy::trivial_regex)]
- #![allow(clippy::useless_format)]
-
- extern crate gl_generator;
-
- use std::env::var;
- use std::fs::File;
- use std::io::{Error as IoError, Write};
- use std::path::Path;
-
- use gl_generator::{
- generators::{gen_struct_name, gen_symbol_name, gen_types},
- Api, Cmd, Fallbacks, Generator as GlGenerator, Profile, Registry,
- };
- use lazy_static::lazy_static;
- use regex::{Captures, Regex};
-
- fn main() {
- let out_dir = var("OUT_DIR").unwrap();
- let mut file = File::create(&Path::new(&out_dir).join("bindings.rs")).unwrap();
-
- let generator = Generator {
- generate_debug: var("CARGO_FEATURE_GENERATE_DEBUG").is_ok(),
- generate_global: var("CARGO_FEATURE_GENERATE_GLOBAL").is_ok(),
- generate_struct: var("CARGO_FEATURE_GENERATE_STRUCT").is_ok(),
- log_fn: if var("CARGO_FEATURE_USE_LOG_CRATE").is_ok() {
- "log::trace!"
- } else {
- "println!"
- },
- };
-
- Registry::new(Api::Gl, (4, 5), Profile::Compatibility, Fallbacks::All, [])
- .write_bindings(generator, &mut file)
- .unwrap();
- }
-
- pub struct Generator {
- generate_debug: bool,
- generate_global: bool,
- generate_struct: bool,
- log_fn: &'static str,
- }
-
- impl GlGenerator for Generator {
- fn write<W>(&self, registry: &Registry, dest: &mut W) -> Result<(), IoError>
- where
- W: Write,
- {
- self.write_header(dest)?;
- self.write_type_aliases(registry, dest)?;
- self.write_enums(registry, dest)?;
- self.write_fnptr_struct_def(registry, dest)?;
-
- if self.generate_global {
- self.write_load_fn(registry, dest)?;
- self.write_fns(registry, dest)?;
- self.write_ptrs(registry, dest)?;
- self.write_fn_mods(registry, dest)?;
- }
-
- if self.generate_struct {
- self.write_struct_def(registry, dest)?;
- self.write_struct_impl(registry, dest)?;
- }
-
- Ok(())
- }
- }
-
- impl Generator {
- fn write_header<W>(&self, dest: &mut W) -> Result<(), IoError>
- where
- W: Write,
- {
- writeln!(
- dest,
- r#"
- mod __gl_imports {{
- pub use std::os::raw;
- pub use std::mem::transmute;
- }}"#
- )?;
-
- Ok(())
- }
-
- fn write_type_aliases<W>(&self, registry: &Registry, dest: &mut W) -> Result<(), IoError>
- where
- W: Write,
- {
- writeln!(
- dest,
- r#"
- pub mod types {{"#
- )?;
-
- gen_types(registry.api, dest)?;
-
- writeln!(
- dest,
- r#"
- }}"#
- )?;
-
- Ok(())
- }
-
- fn write_enums<W>(&self, registry: &Registry, dest: &mut W) -> Result<(), IoError>
- where
- W: Write,
- {
- for enm in ®istry.enums {
- writeln!(
- dest,
- r#"pub const {ident}: {types_prefix}{ty} = {value}{cast_suffix};"#,
- ident = enm.ident,
- types_prefix = if enm.ty == "&'static str" {
- ""
- } else {
- "types::"
- },
- ty = enm.ty,
- value = enm.value,
- cast_suffix = match enm.cast {
- true => format!(" as types::{}", enm.ty),
- false => String::new(),
- },
- )?;
- }
-
- Ok(())
- }
-
- fn write_fnptr_struct_def<W>(&self, registry: &Registry, dest: &mut W) -> Result<(), IoError>
- where
- W: Write,
- {
- writeln!(
- dest,
- r#"
- #[derive(Clone)]
- pub struct FnPtr {{
- f: *const std::os::raw::c_void,
- is_loaded: bool,
- }}
-
- impl FnPtr {{
- fn new(ptr: *const std::os::raw::c_void) -> FnPtr {{
- if ptr.is_null() {{
- FnPtr {{
- f: missing_fn_panic as *const std::os::raw::c_void,
- is_loaded: false
- }}
- }} else {{
- FnPtr {{ f: ptr, is_loaded: true }}
- }}
- }}
-
- #[inline]
- pub fn is_loaded(&self) -> bool {{
- self.is_loaded
- }}
- }}
-
- #[inline(never)]
- fn missing_fn_panic() -> ! {{
- panic!("{api} function was not loaded")
- }}
-
- #[inline(never)]
- fn metaloadfn(
- loadfn: &mut dyn FnMut(&'static str) -> *const std::os::raw::c_void,
- symbol: &'static str,
- fallbacks: &[&'static str]) -> *const std::os::raw::c_void
- {{
- let mut ptr = loadfn(symbol);
-
- if ptr.is_null() {{
- for &sym in fallbacks {{
- ptr = loadfn(sym);
- if !ptr.is_null() {{ break; }}
- }}
- }}
-
- ptr
- }}"#,
- api = registry.api,
- )
- }
-
- fn write_load_fn<W>(&self, registry: &Registry, dest: &mut W) -> Result<(), IoError>
- where
- W: Write,
- {
- write!(
- dest,
- r#"
- pub fn load_with<F>(mut loadfn: F) where F: FnMut(&'static str) -> *const __gl_imports::raw::c_void {{
- #[inline(never)]
- fn inner(loadfn: &mut dyn FnMut(&'static str) -> *const __gl_imports::raw::c_void) {{"#
- )?;
-
- for c in ®istry.cmds {
- write!(
- dest,
- r#"
- {cmd_name}::load_with(&mut *loadfn);"#,
- cmd_name = to_snake_case(&c.proto.ident),
- )?;
- }
-
- writeln!(
- dest,
- r#"
- }}
-
- inner(&mut loadfn)
- }}"#
- )?;
-
- Ok(())
- }
-
- fn write_fns<W>(&self, registry: &Registry, dest: &mut W) -> Result<(), IoError>
- where
- W: Write,
- {
- let has_get_error = registry
- .cmds
- .iter()
- .any(|cmd| cmd.proto.ident == "GetError");
-
- for cmd in ®istry.cmds {
- self.gen_func(dest, cmd, has_get_error, true)?;
- }
-
- Ok(())
- }
-
- fn write_ptrs<W>(&self, registry: &Registry, dest: &mut W) -> Result<(), IoError>
- where
- W: Write,
- {
- writeln!(
- dest,
- r#"
- mod storage {{
- use super::__gl_imports::raw;
- use super::FnPtr;"#
- )?;
-
- for c in ®istry.cmds {
- writeln!(
- dest,
- r#"
- pub static mut {name}: FnPtr = FnPtr {{
- f: super::missing_fn_panic as *const raw::c_void,
- is_loaded: false
- }};"#,
- name = to_snake_case(&c.proto.ident)
- )?;
- }
-
- writeln!(
- dest,
- r#"
- }}"#
- )?;
-
- Ok(())
- }
-
- fn write_fn_mods<W>(&self, registry: &Registry, dest: &mut W) -> Result<(), IoError>
- where
- W: Write,
- {
- for c in ®istry.cmds {
- let fallbacks = match registry.aliases.get(&c.proto.ident) {
- Some(v) => {
- let names = v
- .iter()
- .map(|name| format!("\"{}\"", gen_symbol_name(registry.api, &name)))
- .collect::<Vec<_>>();
- format!("&[{}]", names.join(", "))
- }
- None => "&[]".to_string(),
- };
- let fnname = to_snake_case(&c.proto.ident);
- let symbol = gen_symbol_name(registry.api, &c.proto.ident);
- let symbol = &symbol;
-
- writeln!(
- dest,
- r##"
- pub mod {fnname} {{
- use super::{{storage, metaloadfn}};
- use super::__gl_imports::raw;
- use super::FnPtr;
-
- #[inline]
- pub fn is_loaded() -> bool {{
- unsafe {{ storage::{fnname}.is_loaded }}
- }}
-
- pub fn load_with<F>(mut loadfn: F) where F: FnMut(&'static str) -> *const raw::c_void {{
- unsafe {{
- storage::{fnname} = FnPtr::new(metaloadfn(&mut loadfn, "{symbol}", {fallbacks}))
- }}
- }}
- }}"##,
- fnname = fnname,
- fallbacks = fallbacks,
- symbol = symbol
- )?;
- }
-
- Ok(())
- }
-
- fn write_struct_def<W>(&self, registry: &Registry, dest: &mut W) -> Result<(), IoError>
- where
- W: Write,
- {
- writeln!(
- dest,
- r#"
- #[derive(Clone)]
- pub struct {api} {{"#,
- api = gen_struct_name(registry.api)
- )?;
-
- for cmd in ®istry.cmds {
- writeln!(
- dest,
- r#" pub {name}: FnPtr,"#,
- name = to_snake_case(&cmd.proto.ident)
- )?;
- }
-
- writeln!(dest, r#" }}"#)?;
-
- Ok(())
- }
-
- fn write_struct_impl<W>(&self, registry: &Registry, dest: &mut W) -> Result<(), IoError>
- where
- W: Write,
- {
- write!(
- dest,
- r#"
- impl {api} {{
- pub fn load_with<F>(mut loadfn: F) -> {api} where F: FnMut(&'static str) -> *const std::os::raw::c_void {{
- let mut do_loadfn = |symbol: &'static str, symbols: &[&'static str]| {{
- metaloadfn(&mut loadfn, symbol, symbols)
- }};
-
- {api} {{"#,
- api = gen_struct_name(registry.api)
- )?;
-
- for cmd in ®istry.cmds {
- write!(
- dest,
- r#"
- {name}: FnPtr::new(do_loadfn("{symbol}", &[{fallbacks}])),"#,
- name = to_snake_case(&cmd.proto.ident),
- symbol = gen_symbol_name(registry.api, &cmd.proto.ident),
- fallbacks = match registry.aliases.get(&cmd.proto.ident) {
- Some(fbs) => fbs
- .iter()
- .map(|name| format!(r#""{}""#, gen_symbol_name(registry.api, &name)))
- .collect::<Vec<_>>()
- .join(", "),
- None => format!(""),
- },
- )?
- }
-
- writeln!(
- dest,
- r#"
- }}
- }}"#
- )?;
-
- let has_get_error = registry
- .cmds
- .iter()
- .any(|cmd| cmd.proto.ident == "GetError");
-
- for cmd in ®istry.cmds {
- self.gen_func(dest, cmd, has_get_error, false)?;
- }
-
- writeln!(
- dest,
- r#"
- }}
-
- unsafe impl std::marker::Send for {api} {{}}"#,
- api = gen_struct_name(registry.api)
- )
- }
-
- fn gen_func<W>(
- &self,
- dest: &mut W,
- cmd: &Cmd,
- has_get_error: bool,
- is_global: bool,
- ) -> Result<(), IoError>
- where
- W: Write,
- {
- let ident = to_snake_case(&cmd.proto.ident);
- let idents = gen_parameters(cmd, true, false);
- let typed_params = gen_parameters(cmd, false, true);
- let mut print_ln = String::new();
- let mut print_err = String::new();
-
- if self.generate_debug {
- print_ln = format!(
- r#"
- {log_fn}("[OpenGL] {}({})" {});
- "#,
- ident,
- (0..idents.len())
- .map(|_| "{:?}".to_string())
- .collect::<Vec<_>>()
- .join(", "),
- idents
- .iter()
- .zip(typed_params.iter())
- .map(|(name, ty)| if ty.contains("GLDEBUGPROC") {
- format!(r#", "<callback>""#)
- } else {
- format!(r#", {}"#, name)
- })
- .collect::<Vec<_>>()
- .concat(),
- log_fn = self.log_fn,
- );
-
- if cmd.proto.ident != "GetError" && has_get_error {
- print_err = format!(
- r#"
- let __get_err = __gl_imports::transmute::<_, extern "system" fn() -> u32>({this}get_error.f);
- match __get_err() {{
- 0 => (),
- r => {log_fn}("[OpenGL] ^ GL error triggered: {{}}", r),
- }}
- "#,
- this = if is_global { "storage::" } else { "self." },
- log_fn = self.log_fn,
- );
- }
- }
-
- write!(
- dest,
- r#"
- #[inline]
- pub fn {name}({this_ty}{params}) -> {return_suffix} {{
- unsafe {{{print_ln}
- let __f = __gl_imports::transmute::<_, extern "system" fn({typed_params}) -> {return_suffix}>({this}{name}.f);
- let __r = __f({idents});
- {print_err}
- __r
- }}
- }}"#,
- this_ty = if is_global { "" } else { "&self, " },
- this = if is_global { "storage::" } else { "self." },
- name = ident,
- params = gen_parameters(cmd, true, true).join(", "),
- typed_params = typed_params.join(", "),
- return_suffix = cmd.proto.ty,
- idents = idents.join(", "),
- print_ln = print_ln,
- print_err = print_err,
- )
- }
- }
-
- pub fn gen_parameters(cmd: &Cmd, with_idents: bool, with_types: bool) -> Vec<String> {
- cmd.params
- .iter()
- .map(|binding| {
- if with_idents && with_types {
- format!("{}: {}", to_snake_case(&binding.ident), binding.ty)
- } else if with_types {
- format!("{}", binding.ty)
- } else if with_idents {
- format!("{}", to_snake_case(&binding.ident))
- } else {
- panic!()
- }
- })
- .collect()
- }
-
- fn to_snake_case(s: &str) -> String {
- lazy_static! {
- static ref RX01: Regex = Regex::new(r"([a-z])([A-Z])").unwrap();
- static ref RX02: Regex = Regex::new(r"([a-z])([0-9][0-9A-Za-z]+)$").unwrap();
- static ref RX03: Regex = Regex::new(r"([A-Z])([A-Z][a-z]+)").unwrap();
- static ref RX04: Regex = Regex::new(r"([A-Z])([0-9])").unwrap();
- static ref RX05: Regex = Regex::new(r"([A-Z][a-z]*)(uiv|usv)$").unwrap();
- static ref RX06: Regex = Regex::new(r"([A-Z][a-z]*)(dv|fv|iv)$").unwrap();
- static ref RX07: Regex = Regex::new(r"([A-Z][a-z]*)(v|f|i)$").unwrap();
- static ref RX08: Regex = Regex::new(r"i_64").unwrap();
- static ref RX09: Regex = Regex::new(r"u_i64").unwrap();
- static ref RX10: Regex = Regex::new(r"f_i").unwrap();
- static ref RX11: Regex = Regex::new(r"([a-z]{3,})i$").unwrap();
- static ref RX12: Regex = Regex::new(r"([a-z])i_v").unwrap();
- static ref RX13: Regex = Regex::new(r"([a-z])i64_v").unwrap();
- static ref RX14: Regex = Regex::new(r"[Ii]nteger_64").unwrap();
- }
-
- let s = RX01.replace_all(s, |c: &Captures| format!("{}_{}", &c[1], &c[2]));
- let s = RX02.replace_all(&s, |c: &Captures| format!("{}_{}", &c[1], &c[2]));
- let s = RX03.replace_all(&s, |c: &Captures| format!("{}_{}", &c[1], &c[2]));
- let s = RX04.replace_all(&s, |c: &Captures| format!("{}_{}", &c[1], &c[2]));
- let s = RX05.replace_all(&s, |c: &Captures| format!("{}_{}", &c[1], &c[2]));
- let s = RX06.replace_all(&s, |c: &Captures| format!("{}_{}", &c[1], &c[2]));
- let s = RX07.replace_all(&s, |c: &Captures| format!("{}_{}", &c[1], &c[2]));
- let s = RX08.replace_all(&s, "_i64");
- let s = RX09.replace_all(&s, "_ui64");
- let s = RX10.replace_all(&s, "_fi");
- let s = RX11.replace_all(&s, |c: &Captures| format!("{}_i", &c[1]));
- let s = RX12.replace_all(&s, |c: &Captures| format!("{}_i_v", &c[1]));
- let s = RX13.replace_all(&s, |c: &Captures| format!("{}_i64_v", &c[1]));
- let s = RX14.replace_all(&s, "Integer64");
-
- s.into_owned().to_lowercase()
- }
|