summaryrefslogtreecommitdiff
path: root/codegen
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2023-04-07 20:20:43 +0200
committerHampusM <hampus@hampusmat.com>2023-04-07 20:22:07 +0200
commit966ebb03abd8ae5ed4f47f4b53c00222269a56b4 (patch)
tree24e3325f1657bef89005583e9650b5c8e57459df /codegen
parentc7efbb55f075748d88e25735c6d7e85ba768fd15 (diff)
refactor: replace usage of opengl-registry-macros
Diffstat (limited to 'codegen')
-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
7 files changed, 257 insertions, 0 deletions
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(())
+}