Skip to content
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
93 changes: 93 additions & 0 deletions godot-bindings/src/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,96 @@ pub use gdextension_api::version_4_5 as prebuilt;
// [line] pub use gdextension_api::version_$snakeVersion as prebuilt;
pub use gdextension_api::version_4_5 as prebuilt;
// ]]

// Platform-specific header loading for cross-compilation support.
// The prebuilt module is compiled for HOST (not TARGET) when used as a build-dependency,
// so we determine the platform at runtime based on CARGO_CFG_TARGET_* environment variables.
#[cfg(not(any(feature = "api-custom", feature = "api-custom-json")))]
pub(crate) mod prebuilt_platform {
use std::borrow::Cow;

/// Load platform-specific prebuilt bindings based on the TARGET platform (not HOST).
///
/// Since godot-bindings is a build-dependency, it's compiled for HOST, but we need bindings for TARGET.
/// We detect the target platform using CARGO_CFG_TARGET_* environment variables available during
/// build script execution, then read the corresponding file from the gdextension-api dependency.
pub fn load_gdextension_header_rs_for_target() -> Cow<'static, str> {
// Determine TARGET platform from environment variables
let target_family = std::env::var("CARGO_CFG_TARGET_FAMILY").ok();
let target_os = std::env::var("CARGO_CFG_TARGET_OS").ok();

// Select platform suffix matching gdextension-api's file naming
let platform = match (target_family.as_deref(), target_os.as_deref()) {
(Some("windows"), _) => "windows",
(_, Some("macos" | "ios")) => "macos",
_ => "linux", // Linux, Android, and other Unix-like systems
};

// Read the file from gdextension-api dependency in Cargo cache
load_platform_file_from_cache(platform)
}

/// Load platform-specific bindings file from the gdextension-api dependency.
fn load_platform_file_from_cache(platform: &str) -> Cow<'static, str> {
let home = std::env::var("HOME")
.or_else(|_| std::env::var("USERPROFILE"))
.expect("HOME or USERPROFILE environment variable");

// Search git checkouts (for git dependencies)
if let Some(contents) = try_load_from_git(&home, platform) {
return Cow::Owned(contents);
}

// Search registry (for crates.io dependencies)
if let Some(contents) = try_load_from_registry(&home, platform) {
return Cow::Owned(contents);
}

panic!(
"Failed to locate gdextension-api dependency for platform '{}'.\n\
The dependency should be in Cargo cache at:\n\
- {}/.cargo/git/checkouts/godot4-prebuilt-*\n\
- {}/.cargo/registry/src/*/gdextension-api-*",
platform, home, home
);
}

fn try_load_from_git(home: &str, platform: &str) -> Option<String> {
let git_dir = format!("{home}/.cargo/git/checkouts");
for entry in std::fs::read_dir(&git_dir).ok()?.flatten() {
let path = entry.path();
if path.file_name()?.to_str()?.starts_with("godot4-prebuilt-") {
// Found godot4-prebuilt checkout, look for the hash subdirectory
for subdir in std::fs::read_dir(&path).ok()?.flatten() {
let file_path = subdir.path().join(format!(
"versions/4.5/res/gdextension_interface_{}.rs",
platform
));
if let Ok(contents) = std::fs::read_to_string(&file_path) {
return Some(contents);
}
}
}
}
None
}

fn try_load_from_registry(home: &str, platform: &str) -> Option<String> {
let registry_dir = format!("{home}/.cargo/registry/src");
for host_entry in std::fs::read_dir(&registry_dir).ok()?.flatten() {
for crate_entry in std::fs::read_dir(host_entry.path()).ok()?.flatten() {
let path = crate_entry.path();
if path.file_name()?.to_str()?.starts_with("gdextension-api-") {
let file_path = path.join(format!(
"versions/4.5/res/gdextension_interface_{}.rs",
platform
));
if let Ok(contents) = std::fs::read_to_string(&file_path) {
return Some(contents);
}
}
}
}
None
}
}
6 changes: 5 additions & 1 deletion godot-bindings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ mod depend_on_prebuilt {
.unwrap_or_else(|e| panic!("failed to write gdextension_interface.h: {e}"));
watch.record("write_header_h");

let rs_contents = prebuilt::load_gdextension_header_rs();
// CROSS-COMPILATION FIX:
// Since godot-bindings is a build-dependency, it and gdextension-api are compiled for the HOST platform.
// The #[cfg] attributes in gdextension-api::load_gdextension_header_rs() evaluate for HOST, not TARGET.
// We read CARGO_CFG_TARGET_* environment variables to select the correct bindings at runtime.
let rs_contents = crate::import::prebuilt_platform::load_gdextension_header_rs_for_target();
std::fs::write(rs_path, rs_contents.as_ref())
.unwrap_or_else(|e| panic!("failed to write gdextension_interface.rs: {e}"));
watch.record("write_header_rs");
Expand Down