Skip to content

Make generated bindings no_std compatible and add CStr symbol names feature #551

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ The following crates are contained in this repository:

[![Version](https://img.shields.io/crates/v/gl.svg)](https://crates.io/crates/gl) [![License](https://img.shields.io/crates/l/gl.svg)](https://github.com/brendanzab/gl-rs/blob/master/LICENSE) [![Downloads](https://img.shields.io/crates/d/gl.svg)](https://crates.io/crates/gl)

[README](https://github.com/brendanzab/gl-rs/tree/master/gl)
[README](gl)

An OpenGL function pointer loader for the Rust Programming Language.
An no_std OpenGL function pointer loader for the Rust Programming Language.

```toml
[dependencies]
Expand All @@ -27,9 +27,9 @@ gl = "0.14.0"

[![Version](https://img.shields.io/crates/v/gl_generator.svg)](https://crates.io/crates/gl_generator) [![License](https://img.shields.io/crates/l/gl_generator.svg)](https://github.com/brendanzab/gl-rs/blob/master/LICENSE) [![Downloads](https://img.shields.io/crates/d/gl_generator.svg)](https://crates.io/crates/gl_generator)

[README](https://github.com/brendanzab/gl-rs/tree/master/gl_generator)
[README](gl_generator)

Code generators for creating bindings to the Khronos OpenGL APIs.
Code generators for creating no_std bindings to the Khronos OpenGL APIs.

```toml
[build-dependencies]
Expand All @@ -40,7 +40,7 @@ gl_generator = "0.14.0"

[![Version](https://img.shields.io/crates/v/khronos_api.svg)](https://crates.io/crates/khronos_api) [![License](https://img.shields.io/crates/l/khronos_api.svg)](https://github.com/brendanzab/gl-rs/blob/master/LICENSE) [![Downloads](https://img.shields.io/crates/d/khronos_api.svg)](https://crates.io/crates/khronos_api)

[README](https://github.com/brendanzab/gl-rs/tree/master/khronos_api)
[README](khronos_api)

The Khronos XML API Registry, exposed as byte string constants.

Expand All @@ -61,7 +61,7 @@ git submodule update --init

[![Version](https://img.shields.io/crates/v/webgl_generator.svg)](https://crates.io/crates/webgl_generator) [![License](https://img.shields.io/crates/l/webgl_generator.svg)](https://github.com/brendanzab/gl-rs/blob/master/LICENSE) [![Downloads](https://img.shields.io/crates/d/webgl_generator.svg)](https://crates.io/crates/webgl_generator)

[README](https://github.com/brendanzab/gl-rs/tree/master/webgl_generator)
[README](webgl_generator)

Code generators for creating bindings to the WebGL APIs.

Expand All @@ -74,7 +74,7 @@ webgl_generator = "0.2.0"

[![Version](https://img.shields.io/crates/v/webgl_stdweb.svg)](https://crates.io/crates/webgl_stdweb) [![License](https://img.shields.io/crates/l/webgl_stdweb.svg)](https://github.com/brendanzab/gl-rs/blob/master/LICENSE) [![Downloads](https://img.shields.io/crates/d/webgl_stdweb.svg)](https://crates.io/crates/webgl_stdweb)

[README](https://github.com/brendanzab/gl-rs/tree/master/webgl_stdweb)
[README](webgl_stdweb)

WebGL bindings using stdweb

Expand Down
4 changes: 4 additions & 0 deletions gl/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[package]
name = "gl"
version = "0.14.0"
edition = "2021" # c-string literals require Rust 2021 or later
authors = [
"Brendan Zabarauskas <[email protected]>",
"Corey Richardson",
Expand All @@ -19,5 +20,8 @@ keywords = ["gl", "egl", "opengl", "khronos"]
[build-dependencies]
gl_generator = { version = "0.14.0", path = "../gl_generator" }

[features]
cstr_symbols = ["gl_generator/cstr_symbols"]

[dev-dependencies]
glutin = "0.24"
27 changes: 26 additions & 1 deletion gl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![License](https://img.shields.io/crates/l/gl.svg)](https://github.com/brendanzab/gl-rs/blob/master/LICENSE)
[![Downloads](https://img.shields.io/crates/d/gl.svg)](https://crates.io/crates/gl)

An OpenGL function pointer loader for the Rust Programming Language.
An no_std OpenGL function pointer loader for the Rust Programming Language.

```toml
[dependencies]
Expand Down Expand Up @@ -60,3 +60,28 @@ if gl::Viewport::is_loaded() {
// do something...
}
```

## Symbol string types
In previous versions, if you needed a null-terminated c-string from the `gl::load_with` function, you had to convert the &str to a CString. This limited the use of this library in `no_std` environments.

In the latest release you can add the `"cstr_symbols"` feature to
your `Cargo.toml` and the symbol name will now be a null-terminated `&CStr`, so you won't have to convert between them anymore.

```rust
// Quick example with sdl3-sys

// Without the feature
gl::load_with(| procname: &str | {
match CString::new(procname) { // Allocates and requires std
Ok(procname) => unsafe {
SDL_GL_GetProcAddress(procname.as_ptr()) as *const _
},
Err(_) => ptr::null(),
}
});

// With the feature
gl::load_with(| procname: &CStr | {
unsafe { SDL_GL_GetProcAddress(procname.as_ptr()) as *const _ }
});
```
1 change: 1 addition & 0 deletions gl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
//! ~~~
//!

#![no_std]
#![crate_name = "gl"]
#![crate_type = "lib"]

Expand Down
1 change: 1 addition & 0 deletions gl_generator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ path = "lib.rs"

[features]
unstable_generator_utils = []
cstr_symbols = []

[dependencies]
khronos_api = { version = "3.2.0", path = "../khronos_api" }
Expand Down
30 changes: 27 additions & 3 deletions gl_generator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![License](https://img.shields.io/crates/l/gl_generator.svg)](https://github.com/brendanzab/gl-rs/blob/master/LICENSE)
[![Downloads](https://img.shields.io/crates/d/gl_generator.svg)](https://crates.io/crates/gl_generator)

Code generators for creating bindings to the Khronos OpenGL APIs.
Code generators for creating no_std bindings to the Khronos OpenGL APIs.

## Usage

Expand Down Expand Up @@ -64,8 +64,32 @@ fn main() {
}
```

The `build.rs` file will generate all the OpenGL functions in a file named,
`bindings.rs` plus all enumerations, and all types in the `types` submodule.
The `build.rs` file will generate all the OpenGL functions in a file named `bindings.rs` plus all enumerations and all types in the `types` submodule. Note that while the generated bindings are `no_std`, the generator itself is not.

## Symbol string types
In previous versions, if you needed a null-terminated c-string from the `gl::load_with` function, you had to convert the &str to a CString. This limited the use of this library in `no_std` environments.

In the latest release you can add the `"cstr_symbols"` feature to
your `Cargo.toml` and the symbol name will now be a null-terminated `&CStr`, so you won't have to convert between them anymore.

```rust
// Quick example with sdl3-sys

// Without the feature
gl::load_with(| procname: &str | {
match CString::new(procname) { // Allocates and requires std
Ok(procname) => unsafe {
SDL_GL_GetProcAddress(procname.as_ptr()) as *const _
},
Err(_) => ptr::null(),
}
});

// With the feature
gl::load_with(| procname: &CStr | {
unsafe { SDL_GL_GetProcAddress(procname.as_ptr()) as *const _ }
});
```

## Generator types

Expand Down
30 changes: 16 additions & 14 deletions gl_generator/generators/debug_struct_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ where
dest,
r#"
mod __gl_imports {{
pub use std::mem;
pub use std::marker::Send;
pub use std::os::raw;
pub use core::mem;
pub use core::marker::Send;
pub use core::ffi;
}}
"#
)
Expand Down Expand Up @@ -96,17 +96,17 @@ where
#[derive(Clone)]
pub struct FnPtr {{
/// The function pointer that will be used when calling the function.
f: *const __gl_imports::raw::c_void,
f: *const __gl_imports::ffi::c_void,
/// True if the pointer points to a real function, false if points to a `panic!` fn.
is_loaded: bool,
}}

impl FnPtr {{
/// Creates a `FnPtr` from a load attempt.
fn new(ptr: *const __gl_imports::raw::c_void) -> FnPtr {{
fn new(ptr: *const __gl_imports::ffi::c_void) -> FnPtr {{
if ptr.is_null() {{
FnPtr {{
f: missing_fn_panic as *const __gl_imports::raw::c_void,
f: missing_fn_panic as *const __gl_imports::ffi::c_void,
is_loaded: false
}}
}} else {{
Expand Down Expand Up @@ -185,12 +185,12 @@ where
/// let gl = Gl::load_with(|s| glfw.get_proc_address(s));
/// ~~~
#[allow(dead_code, unused_variables)]
pub fn load_with<F>(mut loadfn: F) -> {api} where F: FnMut(&'static str) -> *const __gl_imports::raw::c_void {{
pub fn load_with<F>(mut loadfn: F) -> {api} where F: FnMut(&'static {s_type}) -> *const __gl_imports::ffi::c_void {{
#[inline(never)]
fn do_metaloadfn(loadfn: &mut dyn FnMut(&'static str) -> *const __gl_imports::raw::c_void,
symbol: &'static str,
symbols: &[&'static str])
-> *const __gl_imports::raw::c_void {{
fn do_metaloadfn(loadfn: &mut dyn FnMut(&'static {s_type}) -> *const __gl_imports::ffi::c_void,
symbol: &'static {s_type},
symbols: &[&'static {s_type}])
-> *const __gl_imports::ffi::c_void {{
let mut ptr = loadfn(symbol);
if ptr.is_null() {{
for &sym in symbols {{
Expand All @@ -200,22 +200,24 @@ where
}}
ptr
}}
let mut metaloadfn = |symbol: &'static str, symbols: &[&'static str]| {{
let mut metaloadfn = |symbol: &'static {s_type}, symbols: &[&'static {s_type}]| {{
do_metaloadfn(&mut loadfn, symbol, symbols)
}};
{api} {{",
s_type = super::SYMBOL_NAME_TYPE,
api = super::gen_struct_name(registry.api))?;

for cmd in &registry.cmds {
writeln!(
dest,
"{name}: FnPtr::new(metaloadfn(\"{symbol}\", &[{fallbacks}])),",
"{name}: FnPtr::new(metaloadfn({s_prefix}\"{symbol}\", &[{fallbacks}])),",
name = cmd.proto.ident,
s_prefix = super::SYMBOL_NAME_PREFIX,
symbol = super::gen_symbol_name(registry.api, &cmd.proto.ident),
fallbacks = match registry.aliases.get(&cmd.proto.ident) {
Some(fbs) => fbs
.iter()
.map(|name| format!("\"{}\"", super::gen_symbol_name(registry.api, &name)))
.map(|name| format!("{}\"{}\"", super::SYMBOL_NAME_PREFIX, super::gen_symbol_name(registry.api, &name)))
.collect::<Vec<_>>()
.join(", "),
None => format!(""),
Expand Down
40 changes: 21 additions & 19 deletions gl_generator/generators/global_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ where
dest,
r#"
mod __gl_imports {{
pub use std::mem;
pub use std::os::raw;
pub use core::mem;
pub use core::ffi;
}}
"#
)
Expand All @@ -63,9 +63,9 @@ where
dest,
r#"
#[inline(never)]
fn metaloadfn(loadfn: &mut dyn FnMut(&'static str) -> *const __gl_imports::raw::c_void,
symbol: &'static str,
fallbacks: &[&'static str]) -> *const __gl_imports::raw::c_void {{
fn metaloadfn(loadfn: &mut dyn FnMut(&'static {s_type}) -> *const __gl_imports::ffi::c_void,
symbol: &'static {s_type},
fallbacks: &[&'static {s_type}]) -> *const __gl_imports::ffi::c_void {{
let mut ptr = loadfn(symbol);
if ptr.is_null() {{
for &sym in fallbacks {{
Expand All @@ -75,8 +75,7 @@ where
}}
ptr
}}
"#
)
"#, s_type = super::SYMBOL_NAME_TYPE)
}

/// Creates a `types` module which contains all the type aliases.
Expand Down Expand Up @@ -156,16 +155,16 @@ where
#[allow(missing_copy_implementations)]
pub struct FnPtr {{
/// The function pointer that will be used when calling the function.
f: *const __gl_imports::raw::c_void,
f: *const __gl_imports::ffi::c_void,
/// True if the pointer points to a real function, false if points to a `panic!` fn.
is_loaded: bool,
}}

impl FnPtr {{
/// Creates a `FnPtr` from a load attempt.
pub fn new(ptr: *const __gl_imports::raw::c_void) -> FnPtr {{
pub fn new(ptr: *const __gl_imports::ffi::c_void) -> FnPtr {{
if ptr.is_null() {{
FnPtr {{ f: missing_fn_panic as *const __gl_imports::raw::c_void, is_loaded: false }}
FnPtr {{ f: missing_fn_panic as *const __gl_imports::ffi::c_void, is_loaded: false }}
}} else {{
FnPtr {{ f: ptr, is_loaded: true }}
}}
Expand All @@ -184,15 +183,15 @@ where
"mod storage {{
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
use super::__gl_imports::raw;
use super::__gl_imports::ffi;
use super::FnPtr;"
)?;

for c in &registry.cmds {
writeln!(
dest,
"pub static mut {name}: FnPtr = FnPtr {{
f: super::missing_fn_panic as *const raw::c_void,
f: super::missing_fn_panic as *const ffi::c_void,
is_loaded: false
}};",
name = c.proto.ident
Expand All @@ -215,7 +214,7 @@ where
Some(v) => {
let names = v
.iter()
.map(|name| format!("\"{}\"", super::gen_symbol_name(registry.api, &name[..])))
.map(|name| format!("{}\"{}\"", super::SYMBOL_NAME_PREFIX, super::gen_symbol_name(registry.api, &name[..])))
.collect::<Vec<_>>();
format!("&[{}]", names.join(", "))
},
Expand All @@ -231,7 +230,7 @@ where
#[allow(non_snake_case)]
pub mod {fnname} {{
use super::{{storage, metaloadfn}};
use super::__gl_imports::raw;
use super::__gl_imports::ffi;
use super::FnPtr;

#[inline]
Expand All @@ -241,14 +240,16 @@ where
}}

#[allow(dead_code)]
pub fn load_with<F>(mut loadfn: F) where F: FnMut(&'static str) -> *const raw::c_void {{
pub fn load_with<F>(mut loadfn: F) where F: FnMut(&'static {s_type}) -> *const ffi::c_void {{
unsafe {{
storage::{fnname} = FnPtr::new(metaloadfn(&mut loadfn, "{symbol}", {fallbacks}))
storage::{fnname} = FnPtr::new(metaloadfn(&mut loadfn, {s_prefix}"{symbol}", {fallbacks}))
}}
}}
}}
"##,
fnname = fnname,
s_type = super::SYMBOL_NAME_TYPE,
s_prefix = super::SYMBOL_NAME_PREFIX,
fallbacks = fallbacks,
symbol = symbol
)?;
Expand Down Expand Up @@ -290,10 +291,11 @@ where
/// gl::load_with(|s| glfw.get_proc_address(s));
/// ~~~
#[allow(dead_code)]
pub fn load_with<F>(mut loadfn: F) where F: FnMut(&'static str) -> *const __gl_imports::raw::c_void {{
pub fn load_with<F>(mut loadfn: F) where F: FnMut(&'static {s_type}) -> *const __gl_imports::ffi::c_void {{
#[inline(never)]
fn inner(loadfn: &mut dyn FnMut(&'static str) -> *const __gl_imports::raw::c_void) {{
")?;
fn inner(loadfn: &mut dyn FnMut(&'static {s_type}) -> *const __gl_imports::ffi::c_void) {{
",
s_type = super::SYMBOL_NAME_TYPE)?;

for c in &registry.cmds {
writeln!(
Expand Down
16 changes: 16 additions & 0 deletions gl_generator/generators/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,22 @@ use registry::{Cmd, Enum, Registry};
use std::io;
use Api;

pub const SYMBOL_NAME_TYPE: &'static str = {
#[cfg(not(feature = "cstr_symbols"))]
{ "str" }
#[cfg(feature = "cstr_symbols")]
{ "core::ffi::CStr" } // NOTE: Im using core::ffi instead of __gl_imports::ffi here on purpose
// there are some files that use this module that don't have the __gl_imports module imported
// and I don't want two variations of the same thing (so we are using the complete core::ffi path)
};

pub const SYMBOL_NAME_PREFIX: &'static str = {
#[cfg(not(feature = "cstr_symbols"))]
{ "" }
#[cfg(feature = "cstr_symbols")]
{ "c" } // https://doc.rust-lang.org/edition-guide/rust-2021/c-string-literals.html
};

pub mod debug_struct_gen;
pub mod global_gen;
pub mod static_gen;
Expand Down
Loading