5
5
*/
6
6
7
7
use crate :: godot_version:: parse_godot_version;
8
- use crate :: header_gen:: run_bindgen ;
8
+ use crate :: header_gen:: generate_rust_binding ;
9
9
use crate :: watch:: StopWatch ;
10
10
use std:: path:: { Path , PathBuf } ;
11
11
use std:: process:: Command ;
@@ -15,6 +15,7 @@ use std::process::Command;
15
15
const GODOT_VERSION_PATH : & str = concat ! ( env!( "CARGO_MANIFEST_DIR" ) , "/gen/godot_version.txt" ) ;
16
16
const JSON_PATH : & str = concat ! ( env!( "CARGO_MANIFEST_DIR" ) , "/gen/extension_api.json" ) ;
17
17
const HEADER_PATH : & str = concat ! ( env!( "CARGO_MANIFEST_DIR" ) , "/gen/gdextension_interface.h" ) ;
18
+ const RES_PATH : & str = concat ! ( env!( "CARGO_MANIFEST_DIR" ) , "/res" ) ;
18
19
19
20
pub fn load_gdextension_json ( watch : & mut StopWatch ) -> String {
20
21
let json_path = Path :: new ( JSON_PATH ) ;
@@ -26,12 +27,12 @@ pub fn load_gdextension_json(watch: &mut StopWatch) -> String {
26
27
27
28
// Regenerate API JSON if first time or Godot version is different
28
29
let version = read_godot_version ( & godot_bin) ;
29
- if !json_path. exists ( ) || has_version_changed ( & version) {
30
- dump_extension_api ( & godot_bin, json_path) ;
31
- update_version_file ( & version) ;
30
+ // if !json_path.exists() || has_version_changed(&version) {
31
+ dump_extension_api ( & godot_bin, json_path) ;
32
+ update_version_file ( & version) ;
32
33
33
- watch. record ( "dump_gdextension_json" ) ;
34
- }
34
+ watch. record ( "dump_gdextension_json" ) ;
35
+ // }
35
36
36
37
let result = std:: fs:: read_to_string ( json_path)
37
38
. unwrap_or_else ( |_| panic ! ( "failed to open file {}" , json_path. display( ) ) ) ;
@@ -40,8 +41,9 @@ pub fn load_gdextension_json(watch: &mut StopWatch) -> String {
40
41
result
41
42
}
42
43
43
- pub fn load_gdextension_rust_header ( rust_out_path : & Path , watch : & mut StopWatch ) -> String {
44
+ pub fn load_gdextension_header_rs ( rust_out_path : & Path , watch : & mut StopWatch ) -> String {
44
45
let c_header_path = Path :: new ( HEADER_PATH ) ;
46
+ let resource_path = Path :: new ( RES_PATH ) ;
45
47
rerun_on_changed ( c_header_path) ;
46
48
47
49
let godot_bin = locate_godot_binary ( ) ;
@@ -50,14 +52,16 @@ pub fn load_gdextension_rust_header(rust_out_path: &Path, watch: &mut StopWatch)
50
52
51
53
// Regenerate API JSON if first time or Godot version is different
52
54
let version = read_godot_version ( & godot_bin) ;
53
- if !c_header_path. exists ( ) || has_version_changed ( & version) {
54
- dump_header_file ( & godot_bin, c_header_path) ;
55
- update_version_file ( & version) ;
55
+ // if !c_header_path.exists() || has_version_changed(&version) {
56
+ dump_header_file ( & godot_bin, c_header_path) ;
57
+ update_version_file ( & version) ;
56
58
57
- watch. record ( "dump_gdextension_header" ) ;
58
- }
59
+ watch. record ( "dump_gdextension_header" ) ;
60
+ // }
61
+
62
+ patch_c_header ( & resource_path. join ( "tweak.patch" ) ) ;
63
+ generate_rust_binding ( c_header_path, rust_out_path) ;
59
64
60
- run_bindgen ( c_header_path, rust_out_path) ;
61
65
watch. record ( "read_header_file" ) ;
62
66
std:: fs:: read_to_string ( rust_out_path) . unwrap_or_else ( |_| {
63
67
panic ! (
@@ -67,6 +71,7 @@ pub fn load_gdextension_rust_header(rust_out_path: &Path, watch: &mut StopWatch)
67
71
} )
68
72
}
69
73
74
+ #[ allow( dead_code) ]
70
75
fn has_version_changed ( current_version : & str ) -> bool {
71
76
let version_path = Path :: new ( GODOT_VERSION_PATH ) ;
72
77
@@ -121,18 +126,12 @@ fn dump_extension_api(godot_bin: &Path, out_file: &Path) {
121
126
std:: fs:: create_dir_all ( cwd) . unwrap_or_else ( |_| panic ! ( "create directory '{}'" , cwd. display( ) ) ) ;
122
127
println ! ( "Dump GDExtension API JSON to dir '{}'..." , cwd. display( ) ) ;
123
128
124
- Command :: new ( godot_bin)
125
- . current_dir ( cwd)
129
+ let mut cmd = Command :: new ( godot_bin) ;
130
+ cmd . current_dir ( cwd)
126
131
. arg ( "--headless" )
127
- . arg ( "--dump-extension-api" )
128
- . arg ( cwd)
129
- . output ( )
130
- . unwrap_or_else ( |_| {
131
- panic ! (
132
- "failed to invoke Godot executable '{}'" ,
133
- godot_bin. display( )
134
- )
135
- } ) ;
132
+ . arg ( "--dump-extension-api" ) ;
133
+
134
+ execute ( cmd, "dump Godot header file" ) ;
136
135
137
136
println ! ( "Generated {}/gdextension_interface.h." , cwd. display( ) ) ;
138
137
}
@@ -142,22 +141,28 @@ fn dump_header_file(godot_bin: &Path, out_file: &Path) {
142
141
std:: fs:: create_dir_all ( cwd) . unwrap_or_else ( |_| panic ! ( "create directory '{}'" , cwd. display( ) ) ) ;
143
142
println ! ( "Dump GDExtension header file to dir '{}'..." , cwd. display( ) ) ;
144
143
145
- Command :: new ( godot_bin)
146
- . current_dir ( cwd)
144
+ let mut cmd = Command :: new ( godot_bin) ;
145
+ cmd . current_dir ( cwd)
147
146
. arg ( "--headless" )
148
- . arg ( "--dump-gdextension-interface" )
149
- . arg ( cwd)
150
- . output ( )
151
- . unwrap_or_else ( |_| {
152
- panic ! (
153
- "failed to invoke Godot executable '{}'" ,
154
- godot_bin. display( )
155
- )
156
- } ) ;
147
+ . arg ( "--dump-gdextension-interface" ) ;
148
+
149
+ execute ( cmd, "dump Godot JSON file" ) ;
157
150
158
151
println ! ( "Generated {}/extension_api.json." , cwd. display( ) ) ;
159
152
}
160
153
154
+ fn patch_c_header ( tweak_path : & Path ) {
155
+ // Note: patch must have paths relative to Git root (aka top-level dir), so cwd is root
156
+ let cwd = tweak_path. parent ( ) . unwrap ( ) . parent ( ) . unwrap ( ) ;
157
+ rerun_on_changed ( tweak_path) ;
158
+
159
+ let git = locate_git_binary ( ) ;
160
+ let mut cmd = Command :: new ( & git) ;
161
+ cmd. current_dir ( cwd) . arg ( "apply" ) . arg ( "-v" ) . arg ( tweak_path) ;
162
+
163
+ execute ( cmd, "apply Git patch" ) ;
164
+ }
165
+
161
166
fn locate_godot_binary ( ) -> PathBuf {
162
167
if let Ok ( string) = std:: env:: var ( "GODOT4_BIN" ) {
163
168
println ! ( "Found GODOT4_BIN with path to executable: '{string}'" ) ;
@@ -167,12 +172,40 @@ fn locate_godot_binary() -> PathBuf {
167
172
path
168
173
} else {
169
174
panic ! (
170
- "Bindings generation requires 'godot4' executable or a GODOT4_BIN \
175
+ "gdext with `custom-godot` feature requires 'godot4' executable or a GODOT4_BIN \
176
+ environment variable (with the path to the executable)."
177
+ )
178
+ }
179
+ }
180
+
181
+ fn locate_git_binary ( ) -> PathBuf {
182
+ if let Ok ( string) = std:: env:: var ( "GIT_BIN" ) {
183
+ println ! ( "Found GIT_BIN with path to executable: '{string}'" ) ;
184
+ PathBuf :: from ( string)
185
+ } else if let Ok ( path) = which:: which ( "git" ) {
186
+ println ! ( "Found 'git' executable in PATH: {}" , path. display( ) ) ;
187
+ path
188
+ } else {
189
+ panic ! (
190
+ "gdext with `custom-godot` feature requires `git` executable or a GIT_BIN \
171
191
environment variable (with the path to the executable)."
172
192
)
173
193
}
174
194
}
175
195
196
+ fn execute ( mut cmd : Command , error_message : & str ) {
197
+ let output = cmd
198
+ . output ( )
199
+ . unwrap_or_else ( |_| panic ! ( "failed to execute command: {error_message}" ) ) ;
200
+
201
+ if !output. status . success ( ) {
202
+ println ! ( "[stdout] {}" , String :: from_utf8( output. stdout) . unwrap( ) ) ;
203
+ println ! ( "[stderr] {}" , String :: from_utf8( output. stderr) . unwrap( ) ) ;
204
+ println ! ( "[status] {}" , output. status) ;
205
+ panic ! ( "command returned error: {error_message}" ) ;
206
+ }
207
+ }
208
+
176
209
fn rerun_on_changed ( path : & Path ) {
177
210
println ! ( "cargo:rerun-if-changed={}" , path. display( ) ) ;
178
211
println ! ( "cargo:rerun-if-env-changed=GODOT4_BIN" ) ;
0 commit comments