|
|
@@ -0,0 +1,537 @@ |
|
|
|
#![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::Core, 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() |
|
|
|
} |