diff --git a/godot-bindings/src/godot_exe.rs b/godot-bindings/src/godot_exe.rs index 495989e0b..dfb5a9dd6 100644 --- a/godot-bindings/src/godot_exe.rs +++ b/godot-bindings/src/godot_exe.rs @@ -56,18 +56,16 @@ pub fn write_gdextension_headers( watch: &mut StopWatch, ) { // None=(unknown, no engine), Some=(version of Godot). Later verified by header itself. - let is_engine_4_0; - if is_h_provided { - is_engine_4_0 = None; - } else { + // Even though we don't support 4.0 anymore, we still detect it, for better error messages. + if !is_h_provided { // No external C header file: Godot binary is present, we use it to dump C header let godot_bin = locate_godot_binary(); rerun_on_changed(&godot_bin); watch.record("locate_godot"); - // Regenerate API JSON if first time or Godot version is different - let version = read_godot_version(&godot_bin); - is_engine_4_0 = Some(version.major == 4 && version.minor == 0); + // Regenerate API JSON if first time or Godot version is different. + // Note: read_godot_version() already panics if 4.0 is still in use; no need to check again. + let _version = read_godot_version(&godot_bin); // if !c_header_path.exists() || has_version_changed(&version) { dump_header_file(&godot_bin, inout_h_path); @@ -79,7 +77,7 @@ pub fn write_gdextension_headers( // Listening to changes on files that are generated by this build step cause an infinite loop with cargo watch of // build -> detect change -> rebuild -> detect change -> ... // rerun_on_changed(inout_h_path); - patch_c_header(inout_h_path, is_engine_4_0); + patch_c_header(inout_h_path); watch.record("patch_header_h"); generate_rust_binding(inout_h_path, out_rs_path); @@ -117,7 +115,13 @@ pub(crate) fn read_godot_version(godot_bin: &Path) -> GodotVersion { assert_eq!( parsed.major, 4, - "Only Godot versions >= 4.0 are supported; found version {}.", + "Only Godot versions with major version 4 are supported; found version {}.", + stdout.trim() + ); + + assert!( + parsed.minor > 0, + "Godot 4.0 is no longer supported by godot-rust; found version {}.", stdout.trim() ); @@ -158,35 +162,23 @@ fn dump_header_file(godot_bin: &Path, out_file: &Path) { println!("Generated {}/gdextension_interface.h.", cwd.display()); } -fn patch_c_header(inout_h_path: &Path, is_engine_4_0: Option) { +fn patch_c_header(inout_h_path: &Path) { // The C header path *must* be passed in by the invoking crate, as the path cannot be relative to this crate. // Otherwise, it can be something like `/home/runner/.cargo/git/checkouts/gdext-76630c89719e160c/efd3b94/godot-bindings`. - println!( - "Patch C header '{}' (is_engine_4_0={is_engine_4_0:?})...", - inout_h_path.display() - ); + println!("Patch C header '{}'...", inout_h_path.display()); let mut c = fs::read_to_string(inout_h_path) .unwrap_or_else(|_| panic!("failed to read C header file {}", inout_h_path.display())); - // Detect whether header is legacy (4.0) or modern (4.1+) format. - let is_header_4_0 = !c.contains("GDExtensionInterfaceGetProcAddress"); - println!("is_header_4_0={is_header_4_0}"); - - // Sanity check - if let Some(is_engine_4_0) = is_engine_4_0 { - assert_eq!( - is_header_4_0, is_engine_4_0, - "Mismatch between engine/header versions" - ); - } - - if is_header_4_0 { - polyfill_legacy_header(&mut c); - } + // Detect whether header is legacy (4.0) format. This should generally already be checked outside. + assert!( + c.contains("GDExtensionInterfaceGetProcAddress"), + "C header file '{}' seems to be GDExtension version 4.0, which is no longer support by godot-rust.", + inout_h_path.display() + ); - // Patch for variant converters and type constructors + // Patch for variant converters and type constructors. c = c.replace( "typedef void (*GDExtensionVariantFromTypeConstructorFunc)(GDExtensionVariantPtr, GDExtensionTypePtr);", "typedef void (*GDExtensionVariantFromTypeConstructorFunc)(GDExtensionUninitializedVariantPtr, GDExtensionTypePtr);" @@ -217,47 +209,6 @@ fn patch_c_header(inout_h_path: &Path, is_engine_4_0: Option) { }); } -/// Backport Godot 4.1+ changes to the old GDExtension API, so gdext can use both uniformly. -fn polyfill_legacy_header(c: &mut String) { - // Newer Uninitialized* types -- use same types as initialized ones, because old functions are not written with Uninitialized* in mind - let pos = c - .find("typedef int64_t GDExtensionInt;") - .expect("Unexpected gdextension_interface.h format (int)"); - - c.insert_str( - pos, - "\ - // gdext polyfill\n\ - typedef struct __GdextVariant *GDExtensionUninitializedVariantPtr;\n\ - typedef struct __GdextStringName *GDExtensionUninitializedStringNamePtr;\n\ - typedef struct __GdextString *GDExtensionUninitializedStringPtr;\n\ - typedef struct __GdextObject *GDExtensionUninitializedObjectPtr;\n\ - typedef struct __GdextType *GDExtensionUninitializedTypePtr;\n\ - \n", - ); - - // Typedef GDExtensionInterfaceGetProcAddress (simply resolving to GDExtensionInterface, as it's the same parameter) - let pos = c - .find("/* INITIALIZATION */") - .expect("Unexpected gdextension_interface.h format (struct)"); - - c.insert_str( - pos, - "\ - // gdext polyfill\n\ - typedef struct {\n\ - uint32_t major;\n\ - uint32_t minor;\n\ - uint32_t patch;\n\ - const char *string;\n\ - } GDExtensionGodotVersion;\n\ - typedef void (*GDExtensionInterfaceFunctionPtr)();\n\ - typedef void (*GDExtensionInterfaceGetGodotVersion)(GDExtensionGodotVersion *r_godot_version);\n\ - typedef GDExtensionInterfaceFunctionPtr (*GDExtensionInterfaceGetProcAddress)(const char *p_function_name);\n\ - \n", - ); -} - pub(crate) fn locate_godot_binary() -> PathBuf { if let Ok(string) = std::env::var("GODOT4_BIN") { println!("Found GODOT4_BIN with path to executable: '{string}'");