4
4
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
5
*/
6
6
7
+ //! Commands related to Godot executable
8
+
7
9
use crate :: godot_version:: parse_godot_version;
8
10
use crate :: header_gen:: generate_rust_binding;
9
11
use crate :: watch:: StopWatch ;
10
12
use std:: path:: { Path , PathBuf } ;
11
- use std:: process:: Command ;
12
-
13
- /// Commands related to Godot executable
14
-
15
- const GODOT_VERSION_PATH : & str = concat ! ( env!( "CARGO_MANIFEST_DIR" ) , "/gen/godot_version.txt" ) ;
16
- const JSON_PATH : & str = concat ! ( env!( "CARGO_MANIFEST_DIR" ) , "/gen/extension_api.json" ) ;
17
- const HEADER_PATH : & str = concat ! ( env!( "CARGO_MANIFEST_DIR" ) , "/gen/gdextension_interface.h" ) ;
13
+ use std:: process:: { Command , Output } ;
14
+
15
+ // Note: CARGO_BUILD_TARGET_DIR and CARGO_TARGET_DIR are not set.
16
+ // OUT_DIR would be standing to reason, but it's an unspecified path that cannot be referenced by CI.
17
+ const GODOT_VERSION_PATH : & str = concat ! (
18
+ env!( "CARGO_MANIFEST_DIR" ) ,
19
+ "/../target/godot-gen/godot_version.txt"
20
+ ) ;
21
+ const JSON_PATH : & str = concat ! (
22
+ env!( "CARGO_MANIFEST_DIR" ) ,
23
+ "/../target/godot-gen/extension_api.json"
24
+ ) ;
25
+ const HEADER_PATH : & str = concat ! (
26
+ env!( "CARGO_MANIFEST_DIR" ) ,
27
+ "/../target/godot-gen/gdextension_interface.h"
28
+ ) ;
18
29
const RES_PATH : & str = concat ! ( env!( "CARGO_MANIFEST_DIR" ) , "/res" ) ;
19
30
20
31
pub fn load_gdextension_json ( watch : & mut StopWatch ) -> String {
@@ -31,7 +42,7 @@ pub fn load_gdextension_json(watch: &mut StopWatch) -> String {
31
42
dump_extension_api ( & godot_bin, json_path) ;
32
43
update_version_file ( & version) ;
33
44
34
- watch. record ( "dump_gdextension_json " ) ;
45
+ watch. record ( "dump_json " ) ;
35
46
// }
36
47
37
48
let result = std:: fs:: read_to_string ( json_path)
@@ -41,34 +52,40 @@ pub fn load_gdextension_json(watch: &mut StopWatch) -> String {
41
52
result
42
53
}
43
54
44
- pub fn load_gdextension_header_rs ( rust_out_path : & Path , watch : & mut StopWatch ) -> String {
45
- let c_header_path = Path :: new ( HEADER_PATH ) ;
46
- let resource_path = Path :: new ( RES_PATH ) ;
47
- rerun_on_changed ( c_header_path) ;
48
-
49
- let godot_bin = locate_godot_binary ( ) ;
50
- rerun_on_changed ( & godot_bin) ;
51
- watch. record ( "locate_godot" ) ;
55
+ pub fn load_gdextension_header_rs (
56
+ c_in_path : Option < & Path > ,
57
+ rust_out_path : & Path ,
58
+ watch : & mut StopWatch ,
59
+ ) {
60
+ let c_header_path;
52
61
53
- // Regenerate API JSON if first time or Godot version is different
54
- let version = read_godot_version ( & godot_bin) ;
55
- // if !c_header_path.exists() || has_version_changed(&version) {
56
- dump_header_file ( & godot_bin, c_header_path) ;
57
- update_version_file ( & version) ;
62
+ if let Some ( c_in_path) = c_in_path {
63
+ // External C header file provided; we don't invoke Godot
64
+ c_header_path = c_in_path;
65
+ } else {
66
+ // No external C header file: Godot binary is present, we use it to dump C header
67
+ let godot_bin = locate_godot_binary ( ) ;
68
+ rerun_on_changed ( & godot_bin) ;
69
+ watch. record ( "locate_godot" ) ;
70
+
71
+ // Regenerate API JSON if first time or Godot version is different
72
+ let version = read_godot_version ( & godot_bin) ;
73
+ c_header_path = Path :: new ( HEADER_PATH ) ;
74
+
75
+ // if !c_header_path.exists() || has_version_changed(&version) {
76
+ dump_header_file ( & godot_bin, c_header_path) ;
77
+ update_version_file ( & version) ;
78
+ // }
79
+ } ;
80
+ rerun_on_changed ( c_header_path) ;
58
81
59
- watch. record ( "dump_gdextension_header" ) ;
60
- // }
82
+ watch. record ( "dump_header_c" ) ;
61
83
62
- patch_c_header ( & resource_path. join ( "tweak.patch" ) ) ;
84
+ let tweak_path = Path :: new ( RES_PATH ) . join ( "tweak.patch" ) ;
85
+ patch_c_header ( c_header_path, & tweak_path) ;
63
86
generate_rust_binding ( c_header_path, rust_out_path) ;
64
87
65
- watch. record ( "read_header_file" ) ;
66
- std:: fs:: read_to_string ( rust_out_path) . unwrap_or_else ( |_| {
67
- panic ! (
68
- "failed to read generated Rust file {}" ,
69
- rust_out_path. display( )
70
- )
71
- } )
88
+ watch. record ( "generate_header_rs" ) ;
72
89
}
73
90
74
91
#[ allow( dead_code) ]
@@ -151,21 +168,30 @@ fn dump_header_file(godot_bin: &Path, out_file: &Path) {
151
168
println ! ( "Generated {}/extension_api.json." , cwd. display( ) ) ;
152
169
}
153
170
154
- fn patch_c_header ( tweak_path : & Path ) {
171
+ fn patch_c_header ( c_header_path : & Path , tweak_path : & Path ) {
172
+ // The C header path *must* be passed in by the invoking crate, as the path cannot be relative to this crate.
173
+ // 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( ) ) ;
176
+
155
177
// 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
178
rerun_on_changed ( tweak_path) ;
158
179
159
180
let git = locate_git_binary ( ) ;
160
181
let mut cmd = Command :: new ( & git) ;
161
182
cmd. current_dir ( cwd) . arg ( "apply" ) . arg ( "-v" ) . arg ( tweak_path) ;
162
183
163
- execute ( cmd, "apply Git patch" ) ;
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
+
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" ) ;
164
189
}
165
190
166
191
fn locate_godot_binary ( ) -> PathBuf {
167
192
if let Ok ( string) = std:: env:: var ( "GODOT4_BIN" ) {
168
193
println ! ( "Found GODOT4_BIN with path to executable: '{string}'" ) ;
194
+ println ! ( "cargo:rerun-if-env-changed=GODOT4_BIN" ) ;
169
195
PathBuf :: from ( string)
170
196
} else if let Ok ( path) = which:: which ( "godot4" ) {
171
197
println ! ( "Found 'godot4' executable in PATH: {}" , path. display( ) ) ;
@@ -193,12 +219,23 @@ fn locate_git_binary() -> PathBuf {
193
219
}
194
220
}
195
221
196
- fn execute ( mut cmd : Command , error_message : & str ) {
222
+ fn execute ( mut cmd : Command , error_message : & str ) -> Output {
197
223
let output = cmd
198
224
. output ( )
199
225
. unwrap_or_else ( |_| panic ! ( "failed to execute command: {error_message}" ) ) ;
200
226
201
- if !output. status . success ( ) {
227
+ if output. status . success ( ) {
228
+ println ! (
229
+ "[stdout] {}" ,
230
+ String :: from_utf8( output. stdout. clone( ) ) . unwrap( )
231
+ ) ;
232
+ println ! (
233
+ "[stderr] {}" ,
234
+ String :: from_utf8( output. stderr. clone( ) ) . unwrap( )
235
+ ) ;
236
+ println ! ( "[status] {}" , output. status) ;
237
+ output
238
+ } else {
202
239
println ! ( "[stdout] {}" , String :: from_utf8( output. stdout) . unwrap( ) ) ;
203
240
println ! ( "[stderr] {}" , String :: from_utf8( output. stderr) . unwrap( ) ) ;
204
241
println ! ( "[status] {}" , output. status) ;
@@ -208,5 +245,4 @@ fn execute(mut cmd: Command, error_message: &str) {
208
245
209
246
fn rerun_on_changed ( path : & Path ) {
210
247
println ! ( "cargo:rerun-if-changed={}" , path. display( ) ) ;
211
- println ! ( "cargo:rerun-if-env-changed=GODOT4_BIN" ) ;
212
248
}
0 commit comments