use anyhow::{Context, Result};
use libloading::Library;
use std::path::PathBuf;
use super::ffi::*;
pub struct SlangLibrary {
_library: Library,
pub create_session: FnSpCreateSession,
pub destroy_session: FnSpDestroySession,
pub create_compile_request: FnSpCreateCompileRequest,
pub destroy_compile_request: FnSpDestroyCompileRequest,
pub add_code_gen_target: FnSpAddCodeGenTarget,
pub add_translation_unit: FnSpAddTranslationUnit,
pub add_translation_unit_source_string: FnSpAddTranslationUnitSourceString,
pub add_entry_point: FnSpAddEntryPoint,
pub add_search_path: FnSpAddSearchPath,
pub compile: FnSpCompile,
pub get_diagnostic_output: FnSpGetDiagnosticOutput,
pub get_entry_point_code_blob: FnSpGetEntryPointCodeBlob,
pub get_target_code_blob: FnSpGetTargetCodeBlob,
}
impl SlangLibrary {
pub fn load() -> Result<Self> {
let lib_path = Self::find_library()?;
tracing::info!("Loading Slang library from: {}", lib_path.display());
let library = unsafe { Library::new(&lib_path) }
.with_context(|| format!("Failed to load Slang library from {}", lib_path.display()))?;
unsafe {
let create_session: FnSpCreateSession = *library
.get(b"spCreateSession\0")
.context("Failed to load spCreateSession")?;
let destroy_session: FnSpDestroySession = *library
.get(b"spDestroySession\0")
.context("Failed to load spDestroySession")?;
let create_compile_request: FnSpCreateCompileRequest = *library
.get(b"spCreateCompileRequest\0")
.context("Failed to load spCreateCompileRequest")?;
let destroy_compile_request: FnSpDestroyCompileRequest = *library
.get(b"spDestroyCompileRequest\0")
.context("Failed to load spDestroyCompileRequest")?;
let add_code_gen_target: FnSpAddCodeGenTarget = *library
.get(b"spAddCodeGenTarget\0")
.context("Failed to load spAddCodeGenTarget")?;
let add_translation_unit: FnSpAddTranslationUnit = *library
.get(b"spAddTranslationUnit\0")
.context("Failed to load spAddTranslationUnit")?;
let add_translation_unit_source_string: FnSpAddTranslationUnitSourceString = *library
.get(b"spAddTranslationUnitSourceString\0")
.context("Failed to load spAddTranslationUnitSourceString")?;
let add_entry_point: FnSpAddEntryPoint = *library
.get(b"spAddEntryPoint\0")
.context("Failed to load spAddEntryPoint")?;
let add_search_path: FnSpAddSearchPath = *library
.get(b"spAddSearchPath\0")
.context("Failed to load spAddSearchPath")?;
let compile: FnSpCompile = *library
.get(b"spCompile\0")
.context("Failed to load spCompile")?;
let get_diagnostic_output: FnSpGetDiagnosticOutput = *library
.get(b"spGetDiagnosticOutput\0")
.context("Failed to load spGetDiagnosticOutput")?;
let get_entry_point_code_blob: FnSpGetEntryPointCodeBlob = *library
.get(b"spGetEntryPointCodeBlob\0")
.context("Failed to load spGetEntryPointCodeBlob")?;
let get_target_code_blob: FnSpGetTargetCodeBlob = *library
.get(b"spGetTargetCodeBlob\0")
.context("Failed to load spGetTargetCodeBlob")?;
Ok(Self {
_library: library,
create_session,
destroy_session,
create_compile_request,
destroy_compile_request,
add_code_gen_target,
add_translation_unit,
add_translation_unit_source_string,
add_entry_point,
add_search_path,
compile,
get_diagnostic_output,
get_entry_point_code_blob,
get_target_code_blob,
})
}
}
fn find_library() -> Result<PathBuf> {
for env_var in ["GOLDY_SLANG_PATH", "RAG_SLANG_PATH"] {
if let Ok(path) = std::env::var(env_var) {
let path = PathBuf::from(path);
if path.exists() {
return Ok(path);
}
tracing::warn!("{} set but file not found: {}", env_var, path.display());
}
}
if let Some(path) = Self::find_build_script_library() {
return Ok(path);
}
if let Some(path) = Self::find_vendored_library() {
return Ok(path);
}
#[cfg(target_os = "windows")]
if let Some(path) = Self::find_vulkan_sdk_library() {
return Ok(path);
}
anyhow::bail!(
"Could not find Slang library. Options:\n\
1. Set GOLDY_SLANG_PATH environment variable\n\
2. Install Vulkan SDK 1.3.296+ (Windows)\n\
3. For development: run slang/download.sh"
)
}
fn find_build_script_library() -> Option<PathBuf> {
let slang_dir = option_env!("GOLDY_SLANG_DIR")?;
let lib_name = Self::library_name();
let path = PathBuf::from(slang_dir).join(lib_name);
if path.exists() {
return Some(path);
}
None
}
fn find_vendored_library() -> Option<PathBuf> {
let lib_name = Self::library_name();
let platform_dir = Self::platform_dir();
if let Ok(exe_path) = std::env::current_exe() {
if let Some(exe_dir) = exe_path.parent() {
let path = exe_dir.join("slang").join("bin").join(&platform_dir).join(&lib_name);
if path.exists() {
return Some(path);
}
let path = exe_dir.join("..").join("..").join("slang").join("bin").join(&platform_dir).join(&lib_name);
if path.exists() {
return Some(path);
}
}
}
let path = PathBuf::from("slang").join("bin").join(&platform_dir).join(&lib_name);
if path.exists() {
return Some(path);
}
None
}
#[cfg(target_os = "windows")]
fn find_vulkan_sdk_library() -> Option<PathBuf> {
if let Ok(sdk_path) = std::env::var("VULKAN_SDK") {
let path = PathBuf::from(&sdk_path).join("Bin").join("slang.dll");
if path.exists() {
return Some(path);
}
let path = PathBuf::from(&sdk_path).join("Bin").join("slang-compiler.dll");
if path.exists() {
return Some(path);
}
}
for version in ["1.3.296.0", "1.3.290.0", "1.3.283.0"] {
let path = PathBuf::from(format!("C:\\VulkanSDK\\{}\\Bin\\slang.dll", version));
if path.exists() {
return Some(path);
}
}
None
}
fn library_name() -> &'static str {
#[cfg(target_os = "windows")]
{
"slang-compiler.dll"
}
#[cfg(target_os = "linux")]
{
"libslang-compiler.so"
}
#[cfg(target_os = "macos")]
{
"libslang-compiler.dylib"
}
#[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
{
compile_error!("Unsupported platform for Slang library")
}
}
fn platform_dir() -> &'static str {
#[cfg(all(target_os = "windows", target_arch = "x86_64"))]
{
"windows-x86_64"
}
#[cfg(all(target_os = "windows", target_arch = "aarch64"))]
{
"windows-aarch64"
}
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
{
"linux-x86_64"
}
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
{
"linux-aarch64"
}
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
{
"macos-x86_64"
}
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
{
"macos-aarch64"
}
#[cfg(not(any(
all(target_os = "windows", target_arch = "x86_64"),
all(target_os = "windows", target_arch = "aarch64"),
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "linux", target_arch = "aarch64"),
all(target_os = "macos", target_arch = "x86_64"),
all(target_os = "macos", target_arch = "aarch64"),
)))]
{
compile_error!("Unsupported platform/architecture combination")
}
}
}