Skip to content

Commit fa58910

Browse files
committed
Patch gdextension_interface.h with Rust regex instead of git patch
1 parent 0a2085b commit fa58910

File tree

5 files changed

+44
-47
lines changed

5 files changed

+44
-47
lines changed

godot-bindings/src/godot_exe.rs

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
use crate::godot_version::parse_godot_version;
1010
use crate::header_gen::generate_rust_binding;
1111
use crate::watch::StopWatch;
12+
13+
use regex::Regex;
14+
use std::fs;
1215
use std::path::{Path, PathBuf};
1316
use std::process::{Command, Output};
1417

@@ -26,7 +29,6 @@ const HEADER_PATH: &str = concat!(
2629
env!("CARGO_MANIFEST_DIR"),
2730
"/../target/godot-gen/gdextension_interface.h"
2831
);
29-
const RES_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/res");
3032

3133
pub fn load_gdextension_json(watch: &mut StopWatch) -> String {
3234
let json_path = Path::new(JSON_PATH);
@@ -45,7 +47,7 @@ pub fn load_gdextension_json(watch: &mut StopWatch) -> String {
4547
watch.record("dump_json");
4648
// }
4749

48-
let result = std::fs::read_to_string(json_path)
50+
let result = fs::read_to_string(json_path)
4951
.unwrap_or_else(|_| panic!("failed to open file {}", json_path.display()));
5052

5153
watch.record("read_json_file");
@@ -81,8 +83,7 @@ pub fn load_gdextension_header_rs(
8183

8284
watch.record("dump_header_c");
8385

84-
let tweak_path = Path::new(RES_PATH).join("tweak.patch");
85-
patch_c_header(c_header_path, &tweak_path);
86+
patch_c_header(c_header_path);
8687
generate_rust_binding(c_header_path, rust_out_path);
8788

8889
watch.record("generate_header_rs");
@@ -92,7 +93,7 @@ pub fn load_gdextension_header_rs(
9293
fn has_version_changed(current_version: &str) -> bool {
9394
let version_path = Path::new(GODOT_VERSION_PATH);
9495

95-
match std::fs::read_to_string(version_path) {
96+
match fs::read_to_string(version_path) {
9697
Ok(last_version) => current_version != last_version,
9798
Err(_) => true,
9899
}
@@ -168,26 +169,30 @@ fn dump_header_file(godot_bin: &Path, out_file: &Path) {
168169
println!("Generated {}/extension_api.json.", cwd.display());
169170
}
170171

171-
fn patch_c_header(c_header_path: &Path, tweak_path: &Path) {
172+
fn patch_c_header(c_header_path: &Path) {
172173
// The C header path *must* be passed in by the invoking crate, as the path cannot be relative to this crate.
173174
// Otherwise, it can be something like `/home/runner/.cargo/git/checkouts/gdext-76630c89719e160c/efd3b94/godot-bindings`.
174-
let cwd = c_header_path.parent().unwrap().parent().unwrap();
175-
println!("cwd: {}", cwd.display());
176175

177-
// Note: patch must have paths relative to Git root (aka top-level dir), so cwd is root
178-
rerun_on_changed(tweak_path);
176+
// Read the contents of the file into a string
177+
let c = fs::read_to_string(c_header_path)
178+
.unwrap_or_else(|_| panic!("failed to read C header file {}", c_header_path.display()));
179179

180-
let git = locate_git_binary();
181-
let mut cmd = Command::new(&git);
182-
cmd.current_dir(cwd).arg("apply").arg("-v").arg(tweak_path);
180+
// Use single regex with independent "const"/"Const", as there are definitions like this:
181+
// typedef const void *GDExtensionMethodBindPtr;
182+
let c = Regex::new(r"typedef (const )?void \*GDExtension(Const)?([a-zA-Z0-9]+?)Ptr;") //
183+
.expect("regex for mut typedef")
184+
.replace_all(&c, "typedef ${1}struct __Gdext$3 *GDExtension${2}${3}Ptr;");
183185

184-
let output = execute(cmd, "apply Git patch");
185-
let stderr = String::from_utf8(output.stderr).expect("convert Git patch output to UTF-8");
186+
println!("Patched contents:\n\n{}\n\n", c.as_ref());
186187

187-
// `git patch` returns 0 even if it skips a patch because it's not applicable -- treat this as error
188-
assert!(!stderr.contains("Skipped"), "Git patch was skipped");
188+
// Write the modified contents back to the file
189+
fs::write(c_header_path, c.as_ref()).unwrap_or_else(|_| {
190+
panic!(
191+
"failed to write patched C header file {}",
192+
c_header_path.display()
193+
)
194+
});
189195
}
190-
191196
fn locate_godot_binary() -> PathBuf {
192197
if let Ok(string) = std::env::var("GODOT4_BIN") {
193198
println!("Found GODOT4_BIN with path to executable: '{string}'");
@@ -204,21 +209,6 @@ fn locate_godot_binary() -> PathBuf {
204209
}
205210
}
206211

207-
fn locate_git_binary() -> PathBuf {
208-
if let Ok(string) = std::env::var("GIT_BIN") {
209-
println!("Found GIT_BIN with path to executable: '{string}'");
210-
PathBuf::from(string)
211-
} else if let Ok(path) = which::which("git") {
212-
println!("Found 'git' executable in PATH: {}", path.display());
213-
path
214-
} else {
215-
panic!(
216-
"gdext with `custom-godot` feature requires `git` executable or a GIT_BIN \
217-
environment variable (with the path to the executable)."
218-
)
219-
}
220-
}
221-
222212
fn execute(mut cmd: Command, error_message: &str) -> Output {
223213
let output = cmd
224214
.output()

godot-bindings/src/lib.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,18 @@ use std::path::Path;
1010

1111
pub use watch::StopWatch;
1212

13-
// Features `custom-godot` and `godot4-artifacts` are mutually exclusive.
13+
#[cfg(all(feature = "custom-godot", feature = "prebuilt-godot"))]
14+
compile_error!("Exactly one of `custom-godot` or `prebuilt-godot` must be specified (both given).");
15+
16+
#[cfg(not(any(feature = "custom-godot", feature = "prebuilt-godot")))]
17+
compile_error!("Exactly one of `custom-godot` or `prebuilt-godot` must be specified (none given).");
1418

1519
// ----------------------------------------------------------------------------------------------------------------------------------------------
1620
// Regenerate all files
1721

1822
#[cfg(feature = "custom-godot")]
1923
#[path = ""]
20-
mod rebuilt {
24+
mod custom {
2125
use super::*;
2226

2327
pub(crate) mod godot_exe;
@@ -41,30 +45,30 @@ mod rebuilt {
4145
}
4246

4347
#[cfg(feature = "custom-godot")]
44-
pub use rebuilt::*;
48+
pub use custom::*;
4549

4650
// ----------------------------------------------------------------------------------------------------------------------------------------------
4751
// Reuse existing files
4852

49-
#[cfg(not(feature = "custom-godot"))]
53+
#[cfg(feature = "prebuilt-godot")]
5054
#[path = ""]
51-
mod existing {
55+
mod prebuilt {
5256
use super::*;
5357

5458
pub fn load_gdextension_json(_watch: &mut StopWatch) -> &'static str {
55-
godot4_artifacts::load_gdextension_json()
59+
godot4_prebuilt::load_gdextension_json()
5660
}
5761

5862
pub fn write_gdextension_header_rs(to: &Path, _watch: &mut StopWatch) {
5963
// Note: prebuilt artifacts just return a static str.
60-
let header_rs = godot4_artifacts::load_gdextension_header_rs();
64+
let header_rs = godot4_prebuilt::load_gdextension_header_rs();
6165
std::fs::write(to, header_rs)
6266
.unwrap_or_else(|e| panic!("failed to write gdextension_interface.rs: {e}"));
6367
}
6468
}
6569

66-
#[cfg(not(feature = "custom-godot"))]
67-
pub use existing::*;
70+
#[cfg(feature = "prebuilt-godot")]
71+
pub use prebuilt::*;
6872

6973
// ----------------------------------------------------------------------------------------------------------------------------------------------
7074
// Common

godot-codegen/src/api_parser.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,10 @@ pub fn load_extension_api(watch: &mut godot_bindings::StopWatch) -> (ExtensionAp
226226
let build_config = "float_64"; // TODO infer this
227227

228228
// Use type inference, so we can accept both String (dynamically resolved) and &str (prebuilt).
229+
// #[allow]: as_ref() acts as impl AsRef<str>, but with conditional compilation
230+
229231
let json = godot_bindings::load_gdextension_json(watch);
232+
#[allow(clippy::useless_asref)]
230233
let json_str: &str = json.as_ref();
231234

232235
let model: ExtensionApi =

godot-core/src/obj/gd.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ where
162162
let callbacks = crate::storage::nop_instance_callbacks();
163163

164164
unsafe {
165-
let token = sys::get_library();
165+
let token = sys::get_library() as *mut std::ffi::c_void;
166166
let binding =
167167
interface_fn!(object_get_instance_binding)(self.obj_sys(), token, &callbacks);
168168

@@ -171,7 +171,7 @@ where
171171
"Class {} -- null instance; does the class have a Godot creator function?",
172172
std::any::type_name::<T>()
173173
);
174-
crate::private::as_storage::<T>(binding)
174+
crate::private::as_storage::<T>(binding as sys::GDExtensionClassInstancePtr)
175175
}
176176
}
177177
}

godot-core/src/registry.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,15 +299,15 @@ pub mod callbacks {
299299
let user_instance = make_user_instance(base);
300300
let instance = InstanceStorage::<T>::construct(user_instance);
301301
let instance_ptr = instance.into_raw();
302-
let instance_ptr = instance_ptr as *mut std::ffi::c_void; // TODO GDExtensionClassInstancePtr
302+
let instance_ptr = instance_ptr as sys::GDExtensionClassInstancePtr;
303303

304304
let binding_data_callbacks = crate::storage::nop_instance_callbacks();
305305
unsafe {
306306
interface_fn!(object_set_instance)(base_ptr, class_name.string_sys(), instance_ptr);
307307
interface_fn!(object_set_instance_binding)(
308308
base_ptr,
309-
sys::get_library(),
310-
instance_ptr,
309+
sys::get_library() as *mut std::ffi::c_void,
310+
instance_ptr as *mut std::ffi::c_void,
311311
&binding_data_callbacks,
312312
);
313313
}

0 commit comments

Comments
 (0)