Skip to content

Commit c20197f

Browse files
committed
Always write both .h/.rs headers; check header diff on single linux job; disable version r/w
1 parent 120a0dd commit c20197f

File tree

7 files changed

+126
-76
lines changed

7 files changed

+126
-76
lines changed

.github/composite/godot-itest/action.yml

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ inputs:
1919
default: ''
2020
description: "Command-line arguments passed to Godot"
2121

22+
godot-check-header:
23+
required: false
24+
default: 'false'
25+
description: "Should the job check against latest gdextension_interface.h, and warn on difference"
26+
2227
rust-toolchain:
2328
required: false
2429
default: 'stable'
@@ -75,24 +80,26 @@ runs:
7580

7681
# Note: no longer fails, as we expect header to be forward-compatible; instead issues a warning
7782
- name: "Copy and compare GDExtension header"
78-
if: inputs.artifact-name == 'godot-linux'
79-
working-directory: godot-ffi/src/gen
83+
if: inputs.godot-check-header == 'true'
8084
run: |
81-
mv $RUNNER_DIR/godot_bin/gdextension_interface.h gdextension_interface.h.bak
82-
git apply ../../godot-bindings/res/tweaks.patch
83-
git diff --no-index --exit-code --quiet gdextension_interface.h gdextension_interface.h.bak || {
85+
mv godot-ffi/src/gen/gdextension_interface.h godot-ffi/src/gen/gdextension_interface_prebuilt.h
86+
mv $RUNNER_DIR/godot_bin/gdextension_interface.h godot-ffi/src/gen/gdextension_interface.h
87+
git apply godot-bindings/res/tweak.patch
88+
cd godot-ffi/src/gen
89+
git diff --no-index --exit-code --quiet gdextension_interface_prebuilt.h gdextension_interface.h || {
8490
echo "OUTCOME=header-diff" >> $GITHUB_ENV
8591
echo "::warning::gdextension_interface.h is not up-to-date."
8692
echo ""
8793
8894
echo "### :warning: Outdated GDExtension API header" >> $GITHUB_STEP_SUMMARY
8995
echo "gdextension_interface.h contains the following differences:" >> $GITHUB_STEP_SUMMARY
9096
echo "\`\`\`diff" >> $GITHUB_STEP_SUMMARY
91-
git diff --no-index gdextension_interface.h gdextension_interface.h.bak >> $GITHUB_STEP_SUMMARY
97+
git diff --no-index gdextension_interface_prebuilt.h gdextension_interface.h >> $GITHUB_STEP_SUMMARY || true
9298
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
9399
echo "After manually updating file, run: \`git diff -R > tweak.patch\`." >> $GITHUB_STEP_SUMMARY
94100
95-
mv gdextension_interface.h.bak gdextension_interface.h
101+
# Undo modifications
102+
mv gdextension_interface_prebuilt.h gdextension_interface.h
96103
#exit 1
97104
}
98105
shell: bash
@@ -121,19 +128,20 @@ runs:
121128
run: |
122129
if grep -q "ObjectDB instances leaked at exit" "${{ runner.temp }}/log.txt"; then
123130
echo "OUTCOME=godot-leak" >> $GITHUB_ENV
124-
exit 2
131+
exit 3
125132
fi
126133
shell: bash
127134

128135
- name: "Conclusion"
129136
if: always()
130137
run: |
131-
echo "Evaluate conclusion ($OUTCOME)"
138+
echo "Evaluate conclusion: $OUTCOME"
132139
133140
case $OUTCOME in
134141
"success")
135-
echo "### :heavy_check_mark: Godot integration tests passed" > $GITHUB_STEP_SUMMARY
136-
echo "$GODOT_BUILT_FROM" >> $GITHUB_STEP_SUMMARY
142+
# Do not output success for now, to keep summary focused on warnings/errors
143+
#echo "### :heavy_check_mark: Godot integration tests passed" > $GITHUB_STEP_SUMMARY
144+
#echo "$GODOT_BUILT_FROM" >> $GITHUB_STEP_SUMMARY
137145
;;
138146
139147
"godot-runtime")

.github/workflows/full-ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ jobs:
260260
rust-toolchain: ${{ matrix.rust-toolchain }}
261261
rust-env-rustflags: ${{ matrix.rust-env-rustflags }}
262262
with-llvm: ${{ matrix.with-llvm }}
263+
godot-check-header: ${{ matrix.name == 'linux' }}
263264

264265

265266
license-guard:

godot-bindings/res/tweak.patch

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,57 @@ index 0b7615f..6db266e 100644
2727
+typedef const struct __GdextObject *GDExtensionConstObjectPtr;
2828
+typedef struct __GdextType *GDExtensionTypePtr;
2929
+typedef const struct __GdextType *GDExtensionConstTypePtr;
30-
+typedef struct __GdextMethodBind *GDExtensionMethodBindPtr;
30+
+typedef const struct __GdextMethodBind *GDExtensionMethodBindPtr;
3131
typedef int64_t GDExtensionInt;
3232
typedef uint8_t GDExtensionBool;
3333
typedef uint64_t GDObjectInstanceID;
3434
-typedef void *GDExtensionRefPtr;
3535
-typedef const void *GDExtensionConstRefPtr;
36-
+typedef struct __GdextExtensionRef *GDExtensionRefPtr;
37-
+typedef const struct __GdextExtensionRef *GDExtensionConstRefPtr;
36+
+typedef struct __GdextRef *GDExtensionRefPtr;
37+
+typedef const struct __GdextRef *GDExtensionConstRefPtr;
3838

3939
/* VARIANT DATA I/O */
4040

41+
@@ -203,7 +203,7 @@ typedef struct {
42+
43+
/* EXTENSION CLASSES */
44+
45+
-typedef void *GDExtensionClassInstancePtr;
46+
+typedef struct __GdextClassInstance *GDExtensionClassInstancePtr;
47+
48+
typedef GDExtensionBool (*GDExtensionClassSet)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionConstVariantPtr p_value);
49+
typedef GDExtensionBool (*GDExtensionClassGet)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret);
50+
@@ -266,7 +266,7 @@ typedef struct {
51+
void *class_userdata; // Per-class user data, later accessible in instance bindings.
52+
} GDExtensionClassCreationInfo;
53+
54+
-typedef void *GDExtensionClassLibraryPtr;
55+
+typedef struct __GdextClassLibrary *GDExtensionClassLibraryPtr;
56+
57+
/* Method */
58+
59+
@@ -323,7 +323,7 @@ typedef struct {
60+
61+
/* SCRIPT INSTANCE EXTENSION */
62+
63+
-typedef void *GDExtensionScriptInstanceDataPtr; // Pointer to custom ScriptInstance native implementation.
64+
+typedef struct __GdextScriptInstanceData *GDExtensionScriptInstanceDataPtr; // Pointer to custom ScriptInstance native implementation.
65+
66+
typedef GDExtensionBool (*GDExtensionScriptInstanceSet)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionConstVariantPtr p_value);
67+
typedef GDExtensionBool (*GDExtensionScriptInstanceGet)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret);
68+
@@ -353,13 +353,13 @@ typedef GDExtensionBool (*GDExtensionScriptInstanceRefCountDecremented)(GDExtens
69+
typedef GDExtensionObjectPtr (*GDExtensionScriptInstanceGetScript)(GDExtensionScriptInstanceDataPtr p_instance);
70+
typedef GDExtensionBool (*GDExtensionScriptInstanceIsPlaceholder)(GDExtensionScriptInstanceDataPtr p_instance);
71+
72+
-typedef void *GDExtensionScriptLanguagePtr;
73+
+typedef struct __GdextScriptLanguage *GDExtensionScriptLanguagePtr;
74+
75+
typedef GDExtensionScriptLanguagePtr (*GDExtensionScriptInstanceGetLanguage)(GDExtensionScriptInstanceDataPtr p_instance);
76+
77+
typedef void (*GDExtensionScriptInstanceFree)(GDExtensionScriptInstanceDataPtr p_instance);
78+
79+
-typedef void *GDExtensionScriptInstancePtr; // Pointer to ScriptInstance.
80+
+typedef struct __GdextScriptInstance *GDExtensionScriptInstancePtr; // Pointer to ScriptInstance.
81+
82+
typedef struct {
83+
GDExtensionScriptInstanceSet set_func;

godot-bindings/src/godot_exe.rs

Lines changed: 32 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,8 @@ use std::process::{Command, Output};
1717

1818
// Note: CARGO_BUILD_TARGET_DIR and CARGO_TARGET_DIR are not set.
1919
// OUT_DIR would be standing to reason, but it's an unspecified path that cannot be referenced by CI.
20-
const GODOT_VERSION_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/src/gen/godot_version.txt");
20+
// const GODOT_VERSION_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/src/gen/godot_version.txt");
2121
const JSON_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/src/gen/extension_api.json");
22-
const HEADER_PATH: &str = concat!(
23-
env!("CARGO_MANIFEST_DIR"),
24-
"/src/gen/gdextension_interface.h"
25-
);
2622

2723
pub fn load_gdextension_json(watch: &mut StopWatch) -> String {
2824
let json_path = Path::new(JSON_PATH);
@@ -33,57 +29,52 @@ pub fn load_gdextension_json(watch: &mut StopWatch) -> String {
3329
watch.record("locate_godot");
3430

3531
// Regenerate API JSON if first time or Godot version is different
36-
let version = read_godot_version(&godot_bin);
32+
let _version = read_godot_version(&godot_bin);
3733
// if !json_path.exists() || has_version_changed(&version) {
3834
dump_extension_api(&godot_bin, json_path);
39-
update_version_file(&version);
35+
// update_version_file(&version);
4036

41-
watch.record("dump_json");
37+
watch.record("dump_api_json");
4238
// }
4339

4440
let result = fs::read_to_string(json_path)
4541
.unwrap_or_else(|_| panic!("failed to open file {}", json_path.display()));
4642

47-
watch.record("read_json_file");
43+
watch.record("read_api_json");
4844
result
4945
}
5046

51-
pub fn load_gdextension_header_rs(
52-
c_in_path: Option<&Path>,
53-
rust_out_path: &Path,
47+
pub fn write_gdextension_headers(
48+
inout_h_path: &Path,
49+
out_rs_path: &Path,
50+
is_h_provided: bool,
5451
watch: &mut StopWatch,
5552
) {
56-
let c_header_path;
57-
58-
if let Some(c_in_path) = c_in_path {
59-
// External C header file provided; we don't invoke Godot
60-
c_header_path = c_in_path;
61-
} else {
53+
if !is_h_provided {
6254
// No external C header file: Godot binary is present, we use it to dump C header
6355
let godot_bin = locate_godot_binary();
6456
rerun_on_changed(&godot_bin);
6557
watch.record("locate_godot");
6658

6759
// Regenerate API JSON if first time or Godot version is different
68-
let version = read_godot_version(&godot_bin);
69-
c_header_path = Path::new(HEADER_PATH);
60+
let _version = read_godot_version(&godot_bin);
7061

7162
// if !c_header_path.exists() || has_version_changed(&version) {
72-
dump_header_file(&godot_bin, c_header_path);
73-
update_version_file(&version);
63+
dump_header_file(&godot_bin, inout_h_path);
64+
// update_version_file(&version);
65+
watch.record("dump_header_h");
7466
// }
7567
};
76-
rerun_on_changed(c_header_path);
7768

78-
watch.record("dump_header_c");
79-
80-
patch_c_header(c_header_path);
81-
generate_rust_binding(c_header_path, rust_out_path);
69+
rerun_on_changed(inout_h_path);
70+
patch_c_header(inout_h_path);
71+
watch.record("patch_header_h");
8272

73+
generate_rust_binding(inout_h_path, out_rs_path);
8374
watch.record("generate_header_rs");
8475
}
8576

86-
#[allow(dead_code)]
77+
/*
8778
fn has_version_changed(current_version: &str) -> bool {
8879
let version_path = Path::new(GODOT_VERSION_PATH);
8980
@@ -97,9 +88,10 @@ fn update_version_file(version: &str) {
9788
let version_path = Path::new(GODOT_VERSION_PATH);
9889
rerun_on_changed(version_path);
9990
100-
std::fs::write(version_path, version)
91+
fs::write(version_path, version)
10192
.unwrap_or_else(|_| panic!("write Godot version to file {}", version_path.display()));
10293
}
94+
*/
10395

10496
fn read_godot_version(godot_bin: &Path) -> String {
10597
let output = Command::new(godot_bin)
@@ -135,41 +127,39 @@ fn read_godot_version(godot_bin: &Path) -> String {
135127

136128
fn dump_extension_api(godot_bin: &Path, out_file: &Path) {
137129
let cwd = out_file.parent().unwrap();
138-
std::fs::create_dir_all(cwd).unwrap_or_else(|_| panic!("create directory '{}'", cwd.display()));
130+
fs::create_dir_all(cwd).unwrap_or_else(|_| panic!("create directory '{}'", cwd.display()));
139131
println!("Dump GDExtension API JSON to dir '{}'...", cwd.display());
140132

141133
let mut cmd = Command::new(godot_bin);
142134
cmd.current_dir(cwd)
143135
.arg("--headless")
144136
.arg("--dump-extension-api");
145137

146-
execute(cmd, "dump Godot header file");
147-
148-
println!("Generated {}/gdextension_interface.h.", cwd.display());
138+
execute(cmd, "dump Godot JSON file");
139+
println!("Generated {}/extension_api.json.", cwd.display());
149140
}
150141

151142
fn dump_header_file(godot_bin: &Path, out_file: &Path) {
152143
let cwd = out_file.parent().unwrap();
153-
std::fs::create_dir_all(cwd).unwrap_or_else(|_| panic!("create directory '{}'", cwd.display()));
144+
fs::create_dir_all(cwd).unwrap_or_else(|_| panic!("create directory '{}'", cwd.display()));
154145
println!("Dump GDExtension header file to dir '{}'...", cwd.display());
155146

156147
let mut cmd = Command::new(godot_bin);
157148
cmd.current_dir(cwd)
158149
.arg("--headless")
159150
.arg("--dump-gdextension-interface");
160151

161-
execute(cmd, "dump Godot JSON file");
162-
163-
println!("Generated {}/extension_api.json.", cwd.display());
152+
execute(cmd, "dump Godot header file");
153+
println!("Generated {}/gdextension_interface.h.", cwd.display());
164154
}
165155

166-
fn patch_c_header(c_header_path: &Path) {
156+
fn patch_c_header(inout_h_path: &Path) {
167157
// The C header path *must* be passed in by the invoking crate, as the path cannot be relative to this crate.
168158
// Otherwise, it can be something like `/home/runner/.cargo/git/checkouts/gdext-76630c89719e160c/efd3b94/godot-bindings`.
169159

170160
// Read the contents of the file into a string
171-
let c = fs::read_to_string(c_header_path)
172-
.unwrap_or_else(|_| panic!("failed to read C header file {}", c_header_path.display()));
161+
let c = fs::read_to_string(inout_h_path)
162+
.unwrap_or_else(|_| panic!("failed to read C header file {}", inout_h_path.display()));
173163

174164
// Use single regex with independent "const"/"Const", as there are definitions like this:
175165
// typedef const void *GDExtensionMethodBindPtr;
@@ -180,10 +170,10 @@ fn patch_c_header(c_header_path: &Path) {
180170
println!("Patched contents:\n\n{}\n\n", c.as_ref());
181171

182172
// Write the modified contents back to the file
183-
fs::write(c_header_path, c.as_ref()).unwrap_or_else(|_| {
173+
fs::write(inout_h_path, c.as_ref()).unwrap_or_else(|_| {
184174
panic!(
185175
"failed to write patched C header file {}",
186-
c_header_path.display()
176+
inout_h_path.display()
187177
)
188178
});
189179
}

godot-bindings/src/header_gen.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
use std::env;
88
use std::path::Path;
99

10-
pub(crate) fn generate_rust_binding(in_c_path: &Path, out_rust_path: &Path) {
11-
let c_header_path = in_c_path.display().to_string();
10+
pub(crate) fn generate_rust_binding(in_h_path: &Path, out_rs_path: &Path) {
11+
let c_header_path = in_h_path.display().to_string();
1212
println!("cargo:rerun-if-changed={}", c_header_path);
1313

1414
let builder = bindgen::Builder::default()
@@ -19,7 +19,7 @@ pub(crate) fn generate_rust_binding(in_c_path: &Path, out_rust_path: &Path) {
1919
.prepend_enum_name(false);
2020

2121
std::fs::create_dir_all(
22-
out_rust_path
22+
out_rs_path
2323
.parent()
2424
.expect("bindgen output file has parent dir"),
2525
)
@@ -30,18 +30,18 @@ pub(crate) fn generate_rust_binding(in_c_path: &Path, out_rust_path: &Path) {
3030
.unwrap_or_else(|err| {
3131
panic!(
3232
"bindgen generate failed\n c: {}\n rs: {}\n err: {}\n",
33-
in_c_path.display(),
34-
out_rust_path.display(),
33+
in_h_path.display(),
34+
out_rs_path.display(),
3535
err
3636
)
3737
});
3838

3939
// Write the bindings to the $OUT_DIR/bindings.rs file.
40-
bindings.write_to_file(out_rust_path).unwrap_or_else(|err| {
40+
bindings.write_to_file(out_rs_path).unwrap_or_else(|err| {
4141
panic!(
4242
"bindgen write failed\n c: {}\n rs: {}\n err: {}\n",
43-
in_c_path.display(),
44-
out_rust_path.display(),
43+
in_h_path.display(),
44+
out_rs_path.display(),
4545
err
4646
)
4747
});

godot-bindings/src/lib.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,17 @@ mod custom {
2929
pub(crate) mod godot_version;
3030
pub(crate) mod header_gen;
3131

32-
// #[cfg(not(feature = "custom-godot-extheader"))]
3332
pub fn load_gdextension_json(watch: &mut StopWatch) -> String {
3433
godot_exe::load_gdextension_json(watch)
3534
}
3635

37-
// #[cfg(not(feature = "custom-godot-extheader"))]
38-
pub fn write_gdextension_header_rs(to: &Path, watch: &mut StopWatch) {
39-
godot_exe::load_gdextension_header_rs(None, to, watch);
36+
pub fn write_gdextension_headers(h_path: &Path, rs_path: &Path, watch: &mut StopWatch) {
37+
godot_exe::write_gdextension_headers(h_path, rs_path, false, watch);
4038
}
4139

4240
#[cfg(feature = "custom-godot-extheader")]
43-
pub fn write_gdextension_header_rs_from_c(from: &Path, to: &Path, watch: &mut StopWatch) {
44-
godot_exe::load_gdextension_header_rs(Some(from), to, watch);
41+
pub fn write_gdextension_headers_from_c(h_path: &Path, rs_path: &Path, watch: &mut StopWatch) {
42+
godot_exe::write_gdextension_headers(h_path, rs_path, true, watch);
4543
}
4644
}
4745

@@ -60,11 +58,17 @@ mod prebuilt {
6058
godot4_prebuilt::load_gdextension_json()
6159
}
6260

63-
pub fn write_gdextension_header_rs(to: &Path, _watch: &mut StopWatch) {
61+
pub fn write_gdextension_headers(h_path: &Path, rs_path: &Path, watch: &mut StopWatch) {
6462
// Note: prebuilt artifacts just return a static str.
65-
let header_rs = godot4_prebuilt::load_gdextension_header_rs();
66-
std::fs::write(to, header_rs)
63+
let h_contents = godot4_prebuilt::load_gdextension_header_h();
64+
std::fs::write(h_path, h_contents)
65+
.unwrap_or_else(|e| panic!("failed to write gdextension_interface.h: {e}"));
66+
watch.record("write_header_h");
67+
68+
let rs_contents = godot4_prebuilt::load_gdextension_header_rs();
69+
std::fs::write(rs_path, rs_contents)
6770
.unwrap_or_else(|e| panic!("failed to write gdextension_interface.rs: {e}"));
71+
watch.record("write_header_rs");
6872
}
6973
}
7074

0 commit comments

Comments
 (0)