summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml7
-rw-r--r--build.rs23
-rw-r--r--codegen/bindings.rs24
-rw-r--r--codegen/command/functions.rs62
-rw-r--r--codegen/command/globals.rs15
-rw-r--r--codegen/command/mod.rs2
-rw-r--r--codegen/formatting.rs54
-rw-r--r--codegen/load_with.rs28
-rw-r--r--codegen/mod.rs72
-rw-r--r--src/lib.rs45
10 files changed, 272 insertions, 60 deletions
diff --git a/Cargo.toml b/Cargo.toml
index cafa690..282fa10 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,7 +5,12 @@ edition = "2021"
description = "Low-level OpenGL bindings"
[dependencies]
-opengl-registry-macros = "0.1.0"
[build-dependencies]
bindgen = "0.63.0"
+opengl-registry = "0.2.0"
+quote = "1.0.23"
+proc-macro2 = "1.0.56"
+convert_case = "0.6.0"
+regex = "1.7.1"
+once_cell = "1.17.0"
diff --git a/build.rs b/build.rs
index bdc2a55..513a65b 100644
--- a/build.rs
+++ b/build.rs
@@ -2,26 +2,17 @@ use std::env;
use std::error::Error;
use std::path::PathBuf;
-use bindgen::{CodegenConfig, MacroTypeVariation};
+use crate::codegen::bindings::generate_bindings;
+use crate::codegen::generate_using_registry;
+
+mod codegen;
pub fn main() -> Result<(), Box<dyn Error>>
{
- println!("cargo:rerun-if-changed=gl.h");
-
- let bindings = bindgen::Builder::default()
- .header("gl.h")
- .default_macro_constant_type(MacroTypeVariation::Signed)
- .with_codegen_config(CodegenConfig::all() & !CodegenConfig::FUNCTIONS)
- .allowlist_type("GL.*")
- .allowlist_type("_cl_.*")
- .allowlist_var("GL_.*")
- .blocklist_item("GL_Z4.*")
- .blocklist_item("GL_Z6.*")
- .generate()?;
-
- let out_path = PathBuf::from(env::var("OUT_DIR")?);
+ let out_dir = PathBuf::from(env::var("OUT_DIR")?);
- bindings.write_to_file(out_path.join("bindings.rs"))?;
+ generate_bindings(&out_dir.join("bindings.rs"))?;
+ generate_using_registry(&out_dir.join("generated.rs"))?;
Ok(())
}
diff --git a/codegen/bindings.rs b/codegen/bindings.rs
new file mode 100644
index 0000000..d6b7e0d
--- /dev/null
+++ b/codegen/bindings.rs
@@ -0,0 +1,24 @@
+use std::error::Error;
+use std::path::Path;
+
+use bindgen::{CodegenConfig, MacroTypeVariation};
+
+pub fn generate_bindings(dest_file: &Path) -> Result<(), Box<dyn Error>>
+{
+ println!("cargo:rerun-if-changed=gl.h");
+
+ let bindings = bindgen::Builder::default()
+ .header("gl.h")
+ .default_macro_constant_type(MacroTypeVariation::Signed)
+ .with_codegen_config(CodegenConfig::all() & !CodegenConfig::FUNCTIONS)
+ .allowlist_type("GL.*")
+ .allowlist_type("_cl_.*")
+ .allowlist_var("GL_.*")
+ .blocklist_item("GL_Z4.*")
+ .blocklist_item("GL_Z6.*")
+ .generate()?;
+
+ bindings.write_to_file(dest_file)?;
+
+ Ok(())
+}
diff --git a/codegen/command/functions.rs b/codegen/command/functions.rs
new file mode 100644
index 0000000..6d1a3d3
--- /dev/null
+++ b/codegen/command/functions.rs
@@ -0,0 +1,62 @@
+use std::error::Error;
+use std::str::FromStr;
+
+use opengl_registry::command::Command;
+use proc_macro2::{LexError, TokenStream};
+use quote::{format_ident, quote};
+
+use crate::codegen::formatting::{clean_param_name, fix_type};
+
+pub fn create_command_functions(
+ commands: &[Command],
+) -> Result<Vec<TokenStream>, Box<dyn Error>>
+{
+ commands.iter().map(|command| {
+ let command_name_str = command.prototype().name();
+
+ let command_name = format_ident!("{}", command.prototype().name());
+
+ let command_name_prefixless = format_ident!("{}", command.prototype()
+ .name()
+ .strip_prefix("gl")
+ .unwrap_or_else(|| command.prototype().name()));
+
+ let command_args = command
+ .parameters()
+ .iter()
+ .map(|param| {
+ let param_name = format_ident!("{}", clean_param_name(param.name()));
+
+ let param_type = TokenStream::from_str(&fix_type(param.get_type()))?;
+
+ Ok::<_, LexError>(quote! { #param_name: #param_type })
+ }).collect::<Result<Vec<_>, _>>()?;
+
+ let command_return_type = TokenStream::from_str(&fix_type(command.prototype().return_type()))?;
+
+ let command_arg_types = command
+ .parameters()
+ .iter()
+ .map(|param| TokenStream::from_str(&fix_type(param.get_type())))
+ .collect::<Result<Vec<_>, _>>()?;
+
+ let command_arg_names = command
+ .parameters()
+ .iter()
+ .map(|param| format_ident!("{}", clean_param_name(param.name())))
+ .collect::<Vec<_>>();
+
+ Ok(quote! {
+ #[doc = #command_name_str]
+ #[allow(non_snake_case, clippy::too_many_arguments)]
+ pub unsafe fn #command_name_prefixless(#(#command_args),*) -> #command_return_type {
+ type GLFunc =
+ unsafe extern "C" fn(#(#command_arg_types),*) -> #command_return_type;
+
+ let gl_func = std::mem::transmute::<_, GLFunc>(functions::#command_name);
+
+ gl_func(#(#command_arg_names),*)
+ }
+ })
+ }).collect()
+}
diff --git a/codegen/command/globals.rs b/codegen/command/globals.rs
new file mode 100644
index 0000000..e522e68
--- /dev/null
+++ b/codegen/command/globals.rs
@@ -0,0 +1,15 @@
+use opengl_registry::command::Command;
+use proc_macro2::TokenStream;
+use quote::{format_ident, quote};
+
+pub fn create_command_globals(commands: &[Command]) -> Vec<TokenStream>
+{
+ commands.iter().map(|command| {
+ let command_name = format_ident!("{}", command.prototype().name());
+
+ quote! {
+ #[allow(non_upper_case_globals)]
+ pub static mut #command_name: unsafe extern "C" fn() = function_not_loaded;
+ }
+ }).collect()
+}
diff --git a/codegen/command/mod.rs b/codegen/command/mod.rs
new file mode 100644
index 0000000..7559bc1
--- /dev/null
+++ b/codegen/command/mod.rs
@@ -0,0 +1,2 @@
+pub mod functions;
+pub mod globals;
diff --git a/codegen/formatting.rs b/codegen/formatting.rs
new file mode 100644
index 0000000..c480be4
--- /dev/null
+++ b/codegen/formatting.rs
@@ -0,0 +1,54 @@
+use convert_case::{Case, Casing};
+use once_cell::sync::Lazy;
+use regex::{Captures, Regex};
+
+static C_PTR_RE: Lazy<Regex> =
+ Lazy::new(|| Regex::new(r"(const )?([a-zA-Z0-9_]+)\s?\*(.*)").unwrap());
+
+static REST_C_PTR_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(const)?\s?\*").unwrap());
+
+pub fn clean_param_name(param_name: &str) -> String
+{
+ match param_name {
+ "ref" => "reference",
+ "type" => "ty",
+ "in" => "inside",
+ "box" => "a_box",
+ name => name,
+ }
+ .to_case(Case::Snake)
+}
+
+pub fn fix_type(ty: &str) -> String
+{
+ let ty = ty.replace("struct", "");
+
+ let ty = c_ptr_to_rust_ptr(&ty);
+
+ ty.replace("void", "std::ffi::c_void")
+}
+
+pub fn c_ptr_to_rust_ptr(c_ptr: &str) -> String
+{
+ C_PTR_RE
+ .replace(c_ptr, |captures: &Captures| {
+ let const_or_mut = captures.get(1).map_or_else(|| "mut", |_| "const");
+
+ let type_name = captures.get(2).unwrap().as_str();
+
+ let rest = captures.get(3).unwrap();
+
+ let rest_ptr_matches = REST_C_PTR_RE.captures_iter(rest.as_str());
+
+ let rest_ptrs = rest_ptr_matches
+ .map(|capts| {
+ let const_or_mut = capts.get(1).map_or_else(|| "mut", |_| "const");
+
+ format!("*{const_or_mut} ")
+ })
+ .collect::<Vec<_>>();
+
+ format!("{}*{const_or_mut} {type_name}", rest_ptrs.join(" "))
+ })
+ .to_string()
+}
diff --git a/codegen/load_with.rs b/codegen/load_with.rs
new file mode 100644
index 0000000..0e8e672
--- /dev/null
+++ b/codegen/load_with.rs
@@ -0,0 +1,28 @@
+use opengl_registry::command::Command;
+use proc_macro2::TokenStream;
+use quote::{format_ident, quote};
+
+pub fn create_load_with_function(commands: &[Command]) -> TokenStream
+{
+ let command_assignments = commands.iter().map(|command| {
+ let command_name = format_ident!("{}", command.prototype().name());
+
+ let command_name_str = command.prototype().name();
+
+ quote! {
+ functions::#command_name = get_proc_addr(#command_name_str);
+ }
+ });
+
+ quote! {
+ /// Loads OpenGL function pointers using the given function address retriever function.
+ pub fn load_with<GetProcAddrFn>(get_proc_addr: GetProcAddrFn)
+ where
+ GetProcAddrFn: Fn(&str) -> unsafe extern "C" fn(),
+ {
+ unsafe {
+ #(#command_assignments)*
+ }
+ }
+ }
+}
diff --git a/codegen/mod.rs b/codegen/mod.rs
new file mode 100644
index 0000000..42dc9dc
--- /dev/null
+++ b/codegen/mod.rs
@@ -0,0 +1,72 @@
+use std::error::Error;
+use std::fs::File;
+use std::io::Write;
+use std::path::Path;
+
+use opengl_registry::api_interface_definition::FeatureKind;
+use opengl_registry::Registry;
+use quote::quote;
+
+use crate::codegen::command::functions::create_command_functions;
+use crate::codegen::command::globals::create_command_globals;
+use crate::codegen::load_with::create_load_with_function;
+
+pub mod bindings;
+
+mod command;
+mod formatting;
+mod load_with;
+
+pub fn generate_using_registry(dest_file: &Path) -> Result<(), Box<dyn Error>>
+{
+ let registry = Registry::retrieve()?;
+
+ let removed_commands = registry
+ .api_interface_definitions()
+ .iter()
+ .flat_map(|api_interface_def| {
+ api_interface_def.removals().iter().flat_map(|removal| {
+ removal.features().iter().filter_map(|feature| {
+ if feature.kind() != FeatureKind::Command {
+ return None;
+ }
+
+ Some(feature.name())
+ })
+ })
+ })
+ .collect::<Vec<_>>();
+
+ let filtered_commands = registry
+ .commands()
+ .iter()
+ .filter(|command| !removed_commands.contains(&command.prototype().name()))
+ .cloned()
+ .collect::<Vec<_>>();
+
+ let command_globals = create_command_globals(&filtered_commands);
+ let command_functions = create_command_functions(&filtered_commands)?;
+ let load_with_function = create_load_with_function(&filtered_commands);
+
+ let tokens = quote! {
+ #(#command_functions)*
+
+ #load_with_function
+
+ mod functions
+ {
+ #(#command_globals)*
+
+ extern "C" fn function_not_loaded()
+ {
+ panic!("OpenGL function not loaded");
+ }
+ }
+ };
+
+ let mut file = File::create(dest_file)?;
+
+ file.write_all(tokens.to_string().as_bytes())?;
+
+ Ok(())
+}
diff --git a/src/lib.rs b/src/lib.rs
index cd2e4d0..88eb0a3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,20 +1,9 @@
//! OpenGL bindings.
#![deny(clippy::all, clippy::pedantic, missing_docs)]
-use opengl_registry_macros::for_each_opengl_command;
-
-for_each_opengl_command! {
- #[doc = stringify!(#gl_command_name)]
- #[allow(non_snake_case, clippy::too_many_arguments)]
- pub unsafe fn #gl_command_name_noprefix(#gl_command_args) -> #gl_command_ret_type {
- type GLFunc =
- unsafe extern "C" fn(#gl_command_arg_types) -> #gl_command_ret_type;
-
- let gl_func = std::mem::transmute::<_, GLFunc>(functions::#gl_command_name);
+pub use ffi::*;
- gl_func(#gl_command_arg_names)
- }
-}
+include!(concat!(env!("OUT_DIR"), "/generated.rs"));
mod ffi
{
@@ -29,33 +18,3 @@ mod ffi
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
-
-pub use ffi::*;
-
-/// Loads OpenGL function pointers using the given function address retriever function.
-pub fn load_with<GetProcAddrFn>(get_proc_addr: GetProcAddrFn)
-where
- GetProcAddrFn: Fn(&str) -> unsafe extern "C" fn(),
-{
- for_each_opengl_command! {
- unsafe {
- functions::#gl_command_name = get_proc_addr(stringify!(#gl_command_name));
- }
- };
-}
-
-mod functions
-{
- use opengl_registry_macros::for_each_opengl_command;
-
- for_each_opengl_command!(
- #[doc = stringify!(#gl_command_name)]
- #[allow(non_upper_case_globals)]
- pub static mut #gl_command_name: unsafe extern "C" fn() = function_not_loaded;
- );
-
- extern "C" fn function_not_loaded()
- {
- panic!("OpenGL function not loaded");
- }
-}