slang-rs/slang-sys/build.rs
Lauro Oyen 3419fb03d9 Fix crash when enabling optimizations
Due to generating rust style enums for slang enums that can contain
more variants than explicitly defined, the generated optimized code
probably trunkated the integer values of these additional variants.
Fix by using constified enums for these cases.
2024-07-13 22:32:03 +02:00

127 lines
3.7 KiB
Rust

extern crate bindgen;
use std::env;
use std::path::{Path, PathBuf};
fn main() {
let slang_dir = env::var("SLANG_DIR").map(PathBuf::from).expect(
"Environment variable `SLANG_DIR` should be set to the directory of a Slang installation. \
This directory should contain `slang.h` and a `bin` subdirectory.",
);
let out_dir = env::var("OUT_DIR")
.map(PathBuf::from)
.expect("Couldn't determine output directory.");
link_libraries(&slang_dir);
bindgen::builder()
.header(slang_dir.join("slang.h").to_str().unwrap())
.clang_arg("-v")
.clang_arg("-xc++")
.clang_arg("-std=c++14")
.allowlist_function("slang_.*")
.allowlist_type("slang.*")
.allowlist_var("SLANG_.*")
.with_codegen_config(
bindgen::CodegenConfig::FUNCTIONS
| bindgen::CodegenConfig::TYPES
| bindgen::CodegenConfig::VARS,
)
.parse_callbacks(Box::new(ParseCallback {}))
.default_enum_style(bindgen::EnumVariation::Rust {
non_exhaustive: false,
})
.constified_enum("SlangProfileID")
.constified_enum("SlangCapabilityID")
.vtable_generation(true)
.layout_tests(false)
.derive_copy(true)
.generate()
.expect("Couldn't generate bindings.")
.write_to_file(out_dir.join("bindings.rs"))
.expect("Couldn't write bindings.");
}
fn link_libraries(slang_dir: &Path) {
let target_os = env::var("CARGO_CFG_TARGET_OS").expect("Couldn't determine target OS.");
let target_arch =
env::var("CARGO_CFG_TARGET_ARCH").expect("Couldn't determine target architecture.");
let target = match (&*target_os, &*target_arch) {
("windows", "x86") => "windows-x86",
("windows", "x86_64") => "windows-x64",
("windows", "aarch64") => "windows-aarch64",
("linux", "x86_64") => "linux-x64",
("linux", "aarch64") => "linux-aarch64",
("macos", "x86_64") => "macosx-x64",
(os, arch) => panic!("Unsupported OS or architecture: {os} {arch}"),
};
let bin_dir = slang_dir.join(format!("bin/{target}/release"));
if !bin_dir.is_dir() {
panic!(
"
Could not find the target-specific `bin` subdirectory (bin/{target}/release) in the Slang installation directory. \
The Slang installation may not match the target this crate is being compiled for.
"
)
}
println!("cargo:rustc-link-search=native={}", bin_dir.display());
println!("cargo:rustc-link-lib=dylib=slang");
}
#[derive(Debug)]
struct ParseCallback {}
impl bindgen::callbacks::ParseCallbacks for ParseCallback {
fn enum_variant_name(
&self,
enum_name: Option<&str>,
original_variant_name: &str,
_variant_value: bindgen::callbacks::EnumVariantValue,
) -> Option<String> {
let enum_name = enum_name?;
// Map enum names to the part of their variant names that needs to be trimmed.
// When an enum name is not in this map the code below will try to trim the enum name itself.
let mut map = std::collections::HashMap::new();
map.insert("SlangMatrixLayoutMode", "SlangMatrixLayout");
map.insert("SlangCompileTarget", "Slang");
let trim = map.get(enum_name).unwrap_or(&enum_name);
let new_variant_name = pascal_case_from_snake_case(original_variant_name);
let new_variant_name = new_variant_name.trim_start_matches(trim);
Some(new_variant_name.to_string())
}
}
/// Converts `snake_case` or `SNAKE_CASE` to `PascalCase`.
/// If the input is already in `PascalCase` it will be returned as is.
fn pascal_case_from_snake_case(snake_case: &str) -> String {
let mut result = String::new();
let should_lower = snake_case
.chars()
.filter(|c| c.is_alphabetic())
.all(|c| c.is_uppercase());
for part in snake_case.split('_') {
for (i, c) in part.chars().enumerate() {
if i == 0 {
result.push(c.to_ascii_uppercase());
} else if should_lower {
result.push(c.to_ascii_lowercase());
} else {
result.push(c);
}
}
}
result
}