diff --git a/.github/composite/godot-itest/action.yml b/.github/composite/godot-itest/action.yml index d665fe300..0e999fdda 100644 --- a/.github/composite/godot-itest/action.yml +++ b/.github/composite/godot-itest/action.yml @@ -19,6 +19,11 @@ inputs: default: '' description: "Command-line arguments passed to Godot" + godot-check-header: + required: false + default: 'false' + description: "Should the job check against latest gdextension_interface.h, and warn on difference" + rust-toolchain: required: false default: 'stable' @@ -60,42 +65,45 @@ runs: echo "GODOT_BUILT_FROM=_Built from [\`$godotVer\`](https://github.com/godotengine/godot/commit/$gitSha)._" >> $GITHUB_ENV shell: bash - # Note: if this fails, run `git diff -R > tweaks.patch` after updating the file manually - - name: "Copy and compare GDExtension header" - if: inputs.artifact-name == 'godot-linux' - run: | - mkdir -p godot-codegen/input - cp $RUNNER_DIR/godot_bin/gdextension_interface.h godot-codegen/input/gdextension_interface.h - git apply godot-codegen/input/tweak.patch -v - git diff --exit-code --quiet || { - echo "OUTCOME=header-diff" >> $GITHUB_ENV - echo "gdextension_interface.h is not up-to-date; abort." - echo "" - - echo "### :x: Outdated GDExtension API header" >> $GITHUB_STEP_SUMMARY - echo "gdextension_interface.h contains the following differences:" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`diff" >> $GITHUB_STEP_SUMMARY - git diff >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "After manually updating file, run: \`git diff -R > tweak.patch\`." >> $GITHUB_STEP_SUMMARY - - exit 1 - } - shell: bash - - name: "Install Rust" uses: ./.github/composite/rust with: rust: ${{ inputs.rust-toolchain }} with-llvm: ${{ inputs.with-llvm }} - - name: "Build godot-rust" + - name: "Build gdext (itest)" run: | cargo build -p itest ${{ inputs.rust-extra-args }} shell: bash env: RUSTFLAGS: ${{ inputs.rust-env-rustflags }} + # Note: no longer fails, as we expect header to be forward-compatible; instead issues a warning + - name: "Copy and compare GDExtension header" + if: inputs.godot-check-header == 'true' + run: | + mv godot-ffi/src/gen/gdextension_interface.h godot-ffi/src/gen/gdextension_interface_prebuilt.h + mv $RUNNER_DIR/godot_bin/gdextension_interface.h godot-ffi/src/gen/gdextension_interface.h + git apply godot-bindings/res/tweak.patch + cd godot-ffi/src/gen + git diff --no-index --exit-code --quiet gdextension_interface_prebuilt.h gdextension_interface.h || { + echo "OUTCOME=header-diff" >> $GITHUB_ENV + echo "::warning::gdextension_interface.h is not up-to-date." + echo "" + + echo "### :warning: Outdated GDExtension API header" >> $GITHUB_STEP_SUMMARY + echo "gdextension_interface.h contains the following differences:" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`diff" >> $GITHUB_STEP_SUMMARY + git diff --no-index gdextension_interface_prebuilt.h gdextension_interface.h >> $GITHUB_STEP_SUMMARY || true + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "After manually updating file, run: \`git diff -R > tweak.patch\`." >> $GITHUB_STEP_SUMMARY + + # Undo modifications + mv gdextension_interface_prebuilt.h gdextension_interface.h + #exit 1 + } + shell: bash + - name: "Run Godot integration tests" # Aborts immediately if Godot outputs certain keywords (would otherwise stall until CI runner times out). # Explanation: @@ -120,19 +128,20 @@ runs: run: | if grep -q "ObjectDB instances leaked at exit" "${{ runner.temp }}/log.txt"; then echo "OUTCOME=godot-leak" >> $GITHUB_ENV - exit 2 + exit 3 fi shell: bash - name: "Conclusion" if: always() run: | - echo "Evaluate conclusion ($OUTCOME)" + echo "Evaluate conclusion: $OUTCOME" case $OUTCOME in "success") - echo "### :heavy_check_mark: Godot integration tests passed" > $GITHUB_STEP_SUMMARY - echo "$GODOT_BUILT_FROM" >> $GITHUB_STEP_SUMMARY + # Do not output success for now, to keep summary focused on warnings/errors + #echo "### :heavy_check_mark: Godot integration tests passed" > $GITHUB_STEP_SUMMARY + #echo "$GODOT_BUILT_FROM" >> $GITHUB_STEP_SUMMARY ;; "godot-runtime") diff --git a/.github/composite/llvm/action.yml b/.github/composite/llvm/action.yml index 49012b1d0..5da47e7d9 100644 --- a/.github/composite/llvm/action.yml +++ b/.github/composite/llvm/action.yml @@ -24,7 +24,7 @@ runs: - name: "Cache LLVM and clang" id: cache-llvm # Note: conditionals not yet supported; see https://github.com/actions/runner/issues/834 - # if: ${{ inputs.llvm == 'true' }} + # if: inputs.llvm == 'true' uses: actions/cache@v3 with: # path: | @@ -34,7 +34,7 @@ runs: key: llvm-10.0 - uses: KyleMayes/install-llvm-action@v1 - # if: ${{ inputs.llvm == 'true' }} + # if: inputs.llvm == 'true' with: version: "10.0" directory: ${{ env.LLVM_INSTALL_DIR }} diff --git a/.github/composite/rust/action.yml b/.github/composite/rust/action.yml index 816f9a644..1d3f2d5d8 100644 --- a/.github/composite/rust/action.yml +++ b/.github/composite/rust/action.yml @@ -61,8 +61,8 @@ runs: cache-on-failure: true - name: "Install LLVM" + if: inputs.with-llvm == 'true' uses: ./.github/composite/llvm - if: ${{ inputs.with-llvm == 'true' }} - name: "Set environment variables used by toolchain" run: | diff --git a/.github/workflows/full-ci.yml b/.github/workflows/full-ci.yml index 65bb9025c..3b57638a0 100644 --- a/.github/workflows/full-ci.yml +++ b/.github/workflows/full-ci.yml @@ -42,7 +42,6 @@ jobs: - name: "Install Rust" uses: ./.github/composite/rust with: - rust: stable components: rustfmt - name: "Check rustfmt" @@ -50,33 +49,14 @@ jobs: clippy: - name: clippy (${{ matrix.name }}) runs-on: ubuntu-20.04 - strategy: - fail-fast: false - matrix: - include: - - name: linux - rust-toolchain: stable - godot-binary: godot.linuxbsd.editor.dev.x86_64 - - - name: linux-double - rust-toolchain: stable - godot-binary: godot.linuxbsd.editor.dev.double.x86_64 - rust-extra-args: --features double-precision steps: - uses: actions/checkout@v3 - name: "Install Rust" uses: ./.github/composite/rust - - # TODO get rid of Godot binary, once the JSON is either versioned or fetched from somewhere - # Replaces also backspaces on Windows, since they cause problems in Bash - - name: "Install Godot" - uses: ./.github/composite/godot-install with: - artifact-name: godot-${{ matrix.name }} - godot-binary: ${{ matrix.godot-binary }} + components: clippy - name: "Check clippy" run: | @@ -84,7 +64,6 @@ jobs: -D clippy::suspicious -D clippy::style -D clippy::complexity -D clippy::perf \ -D clippy::dbg_macro -D clippy::todo -D clippy::unimplemented -D warnings - unit-test: name: unit-test (${{ matrix.name }}${{ matrix.rust-special }}) runs-on: ${{ matrix.os }} @@ -98,28 +77,19 @@ jobs: include: - name: macos os: macos-11 - rust-toolchain: stable - godot-binary: godot.macos.editor.dev.x86_64 - with-llvm: true - name: windows os: windows-latest - rust-toolchain: stable-x86_64-pc-windows-msvc - godot-binary: godot.windows.editor.dev.x86_64.exe # Don't use latest Ubuntu (22.04) as it breaks lots of ecosystem compatibility. # If ever moving to ubuntu-latest, need to manually install libtinfo5 for LLVM. - name: linux os: ubuntu-20.04 - rust-toolchain: stable - godot-binary: godot.linuxbsd.editor.dev.x86_64 - name: linux os: ubuntu-20.04 - rust-toolchain: stable rust-special: -minimal-deps - godot-binary: godot.linuxbsd.editor.dev.x86_64 - + steps: - uses: actions/checkout@v3 @@ -128,26 +98,17 @@ jobs: with: rust: stable cache-key: ${{ matrix.rust-special }} # '-minimal-deps' or empty/not defined - with-llvm: ${{ matrix.with-llvm }} - name: "Install Rust nightly (minimal deps)" + if: matrix.rust-special == '-minimal-deps' uses: ./.github/composite/rust with: rust: nightly cache-key: minimal-deps-nightly - if: ${{ matrix.rust-special == '-minimal-deps' }} - name: "Install minimal dependency versions from Cargo" + if: matrix.rust-special == '-minimal-deps' run: cargo +nightly update -Z minimal-versions - if: ${{ matrix.rust-special == '-minimal-deps' }} - - # TODO get rid of Godot binary, once the JSON is either versioned or fetched from somewhere - # Replaces also backspaces on Windows, since they cause problems in Bash - - name: "Install Godot" - uses: ./.github/composite/godot-install - with: - artifact-name: godot-${{ matrix.name }} - godot-binary: ${{ matrix.godot-binary }} - name: "Compile tests" run: cargo test $GDEXT_FEATURES --no-run ${{ matrix.rust-extra-args }} @@ -170,41 +131,52 @@ jobs: include: - name: macos os: macos-12 - rust-toolchain: stable godot-binary: godot.macos.editor.dev.x86_64 - with-llvm: true - + - name: macos-double os: macos-12 - rust-toolchain: stable godot-binary: godot.macos.editor.dev.double.x86_64 rust-extra-args: --features double-precision + + - name: macos-nightly + os: macos-12 + artifact-name: macos + godot-binary: godot.macos.editor.dev.x86_64 + rust-extra-args: --features godot/custom-godot with-llvm: true - name: windows os: windows-latest - rust-toolchain: stable-x86_64-pc-windows-msvc godot-binary: godot.windows.editor.dev.x86_64.exe - name: windows-double os: windows-latest - rust-toolchain: stable-x86_64-pc-windows-msvc godot-binary: godot.windows.editor.dev.double.x86_64.exe rust-extra-args: --features double-precision + - name: windows-nightly + os: windows-latest + artifact-name: windows + godot-binary: godot.windows.editor.dev.x86_64.exe + rust-extra-args: --features godot/custom-godot + # Don't use latest Ubuntu (22.04) as it breaks lots of ecosystem compatibility. # If ever moving to ubuntu-latest, need to manually install libtinfo5 for LLVM. - name: linux os: ubuntu-20.04 - rust-toolchain: stable godot-binary: godot.linuxbsd.editor.dev.x86_64 - name: linux-double os: ubuntu-20.04 - rust-toolchain: stable godot-binary: godot.linuxbsd.editor.dev.double.x86_64 rust-extra-args: --features double-precision + - name: linux-nightly + os: ubuntu-20.04 + artifact-name: linux + godot-binary: godot.linuxbsd.editor.dev.x86_64 + rust-extra-args: --features godot/custom-godot + # Special Godot binaries compiled with AddressSanitizer/LeakSanitizer to detect UB/leaks. # Additionally, the Godot source is patched to make dlclose() a no-op, as unloading dynamic libraries loses stacktrace and # cause false positives like println!. See https://github.com/google/sanitizers/issues/89. @@ -231,13 +203,14 @@ jobs: - name: "Run Godot integration test" uses: ./.github/composite/godot-itest with: - artifact-name: godot-${{ matrix.name }} + artifact-name: godot-${{ matrix.artifact-name || matrix.name }} godot-binary: ${{ matrix.godot-binary }} godot-args: ${{ matrix.godot-args }} rust-extra-args: ${{ matrix.rust-extra-args }} - rust-toolchain: ${{ matrix.rust-toolchain }} + rust-toolchain: ${{ matrix.rust-toolchain || 'stable' }} rust-env-rustflags: ${{ matrix.rust-env-rustflags }} with-llvm: ${{ matrix.with-llvm }} + godot-check-header: ${{ matrix.name == 'linux' }} license-guard: diff --git a/.github/workflows/minimal-ci.yml b/.github/workflows/minimal-ci.yml index 7741f295b..71af8b5ae 100644 --- a/.github/workflows/minimal-ci.yml +++ b/.github/workflows/minimal-ci.yml @@ -44,40 +44,21 @@ jobs: clippy: - name: clippy (${{ matrix.name }}) runs-on: ubuntu-20.04 - strategy: - fail-fast: false - matrix: - include: - - name: linux - rust-toolchain: stable - godot-binary: godot.linuxbsd.editor.dev.x86_64 - - - name: linux-double - rust-toolchain: stable - godot-binary: godot.linuxbsd.editor.dev.double.x86_64 - rust-extra-args: --features double-precision steps: - uses: actions/checkout@v3 - name: "Install Rust" uses: ./.github/composite/rust - - # TODO get rid of Godot binary, once the JSON is either versioned or fetched from somewhere - # Replaces also backspaces on Windows, since they cause problems in Bash - - name: "Install Godot" - uses: ./.github/composite/godot-install with: - artifact-name: godot-${{ matrix.name }} - godot-binary: ${{ matrix.godot-binary }} + components: clippy - name: "Check clippy" run: | cargo clippy --all-targets $GDEXT_FEATURES ${{ matrix.rust-extra-args }} -- \ -D clippy::suspicious -D clippy::style -D clippy::complexity -D clippy::perf \ -D clippy::dbg_macro -D clippy::todo -D clippy::unimplemented -D warnings - + unit-test: name: unit-test @@ -88,12 +69,6 @@ jobs: - name: "Install Rust" uses: ./.github/composite/rust - - name: "Install Godot" - uses: ./.github/composite/godot-install - with: - artifact-name: godot-linux - godot-binary: godot.linuxbsd.editor.dev.x86_64 - - name: "Compile tests" run: cargo test $GDEXT_FEATURES --no-run @@ -113,7 +88,6 @@ jobs: with: artifact-name: godot-linux godot-binary: godot.linuxbsd.editor.dev.x86_64 - #godot_ver: ${{ matrix.godot }} license-guard: diff --git a/Cargo.toml b/Cargo.toml index dfc53e532..873c62f94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,41 +1,13 @@ [workspace] members = [ + "godot-bindings", "godot-codegen", "godot-ffi", - "godot-macros", "godot-core", + "godot-macros", "godot", # Godot integration "itest/rust", "examples/dodge-the-creeps/rust" ] - -# These dependencies don't need to be debugged, make build scripts run faster -[profile.dev.package.bindgen] -debug = 0 -opt-level = 3 - -[profile.dev.package.nanoserde] -debug = 0 -opt-level = 3 - -[profile.dev.package.quote] -debug = 0 -opt-level = 3 - -[profile.dev.package.proc-macro2] -debug = 0 -opt-level = 3 - -[profile.dev.package.venial] -debug = 0 -opt-level = 3 - -[profile.dev.package.godot-codegen] -debug = 0 -opt-level = 3 - -#[profile.dev.package.regex] -#debug = 0 -#opt-level = 3 \ No newline at end of file diff --git a/godot-bindings/Cargo.toml b/godot-bindings/Cargo.toml new file mode 100644 index 000000000..d6262e447 --- /dev/null +++ b/godot-bindings/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "godot-bindings" +version = "0.1.0" +edition = "2021" +rust-version = "1.63" +license = "MPL-2.0" +keywords = ["gamedev", "godot", "engine", "ffi", "sys"] +categories = ["game-engines", "graphics"] + +# Since features are additive and we want the user to user prebuilt by default, we need to have `prebuilt-godot` as the +# default feature. However, it's not possible to _disable_ the prebuilt dependency when specifying `godot-custom` (without +# requiring no-default-features), so we unfortunately still need to depend on prebuilt and just ignore it. +# The artifact generator explicitly excludes that though (to avoid a quasi-circular dependency back to its repo). +[features] +default = ["prebuilt-godot"] +prebuilt-godot = ["dep:godot4-prebuilt"] +custom-godot = ["dep:bindgen", "dep:regex", "dep:which"] +custom-godot-extheader = [] + +[dependencies] +godot4-prebuilt = { optional = true, git = "https://github.com/godot-rust/godot4-prebuilt", branch = "4.0.1" } + +# Version >= 1.5.5 for security: https://blog.rust-lang.org/2022/03/08/cve-2022-24713.html +# 'unicode-gencat' needed for \d, see: https://docs.rs/regex/1.5.5/regex/#unicode-features +bindgen = { optional = true, version = "0.64", default-features = false, features = ["runtime"] } +regex = { optional = true, version = "1.5.5", default-features = false, features = ["std", "unicode-gencat"] } +which = { optional = true, version = "4" } diff --git a/godot-bindings/res/tweak.patch b/godot-bindings/res/tweak.patch new file mode 100644 index 000000000..9aad26029 --- /dev/null +++ b/godot-bindings/res/tweak.patch @@ -0,0 +1,83 @@ +diff --git b/godot-ffi/src/gen/gdextension_interface.h a/godot-ffi/src/gen/gdextension_interface.h +index 0b7615f..6db266e 100644 +--- b/godot-ffi/src/gen/gdextension_interface.h ++++ a/godot-ffi/src/gen/gdextension_interface.h +@@ -139,22 +139,22 @@ typedef enum { + + } GDExtensionVariantOperator; + +-typedef void *GDExtensionVariantPtr; +-typedef const void *GDExtensionConstVariantPtr; +-typedef void *GDExtensionStringNamePtr; +-typedef const void *GDExtensionConstStringNamePtr; +-typedef void *GDExtensionStringPtr; +-typedef const void *GDExtensionConstStringPtr; +-typedef void *GDExtensionObjectPtr; +-typedef const void *GDExtensionConstObjectPtr; +-typedef void *GDExtensionTypePtr; +-typedef const void *GDExtensionConstTypePtr; +-typedef const void *GDExtensionMethodBindPtr; ++typedef struct __GdextVariant *GDExtensionVariantPtr; ++typedef const struct __GdextVariant *GDExtensionConstVariantPtr; ++typedef struct __GdextStringName *GDExtensionStringNamePtr; ++typedef const struct __GdextStringName *GDExtensionConstStringNamePtr; ++typedef struct __GdextString *GDExtensionStringPtr; ++typedef const struct __GdextString *GDExtensionConstStringPtr; ++typedef struct __GdextObject *GDExtensionObjectPtr; ++typedef const struct __GdextObject *GDExtensionConstObjectPtr; ++typedef struct __GdextType *GDExtensionTypePtr; ++typedef const struct __GdextType *GDExtensionConstTypePtr; ++typedef const struct __GdextMethodBind *GDExtensionMethodBindPtr; + typedef int64_t GDExtensionInt; + typedef uint8_t GDExtensionBool; + typedef uint64_t GDObjectInstanceID; +-typedef void *GDExtensionRefPtr; +-typedef const void *GDExtensionConstRefPtr; ++typedef struct __GdextRef *GDExtensionRefPtr; ++typedef const struct __GdextRef *GDExtensionConstRefPtr; + + /* VARIANT DATA I/O */ + +@@ -203,7 +203,7 @@ typedef struct { + + /* EXTENSION CLASSES */ + +-typedef void *GDExtensionClassInstancePtr; ++typedef struct __GdextClassInstance *GDExtensionClassInstancePtr; + + typedef GDExtensionBool (*GDExtensionClassSet)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionConstVariantPtr p_value); + typedef GDExtensionBool (*GDExtensionClassGet)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret); +@@ -266,7 +266,7 @@ typedef struct { + void *class_userdata; // Per-class user data, later accessible in instance bindings. + } GDExtensionClassCreationInfo; + +-typedef void *GDExtensionClassLibraryPtr; ++typedef struct __GdextClassLibrary *GDExtensionClassLibraryPtr; + + /* Method */ + +@@ -323,7 +323,7 @@ typedef struct { + + /* SCRIPT INSTANCE EXTENSION */ + +-typedef void *GDExtensionScriptInstanceDataPtr; // Pointer to custom ScriptInstance native implementation. ++typedef struct __GdextScriptInstanceData *GDExtensionScriptInstanceDataPtr; // Pointer to custom ScriptInstance native implementation. + + typedef GDExtensionBool (*GDExtensionScriptInstanceSet)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionConstVariantPtr p_value); + typedef GDExtensionBool (*GDExtensionScriptInstanceGet)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret); +@@ -353,13 +353,13 @@ typedef GDExtensionBool (*GDExtensionScriptInstanceRefCountDecremented)(GDExtens + typedef GDExtensionObjectPtr (*GDExtensionScriptInstanceGetScript)(GDExtensionScriptInstanceDataPtr p_instance); + typedef GDExtensionBool (*GDExtensionScriptInstanceIsPlaceholder)(GDExtensionScriptInstanceDataPtr p_instance); + +-typedef void *GDExtensionScriptLanguagePtr; ++typedef struct __GdextScriptLanguage *GDExtensionScriptLanguagePtr; + + typedef GDExtensionScriptLanguagePtr (*GDExtensionScriptInstanceGetLanguage)(GDExtensionScriptInstanceDataPtr p_instance); + + typedef void (*GDExtensionScriptInstanceFree)(GDExtensionScriptInstanceDataPtr p_instance); + +-typedef void *GDExtensionScriptInstancePtr; // Pointer to ScriptInstance. ++typedef struct __GdextScriptInstance *GDExtensionScriptInstancePtr; // Pointer to ScriptInstance. + + typedef struct { + GDExtensionScriptInstanceSet set_func; diff --git a/godot-bindings/src/godot_exe.rs b/godot-bindings/src/godot_exe.rs new file mode 100644 index 000000000..916104b00 --- /dev/null +++ b/godot-bindings/src/godot_exe.rs @@ -0,0 +1,222 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +//! Commands related to Godot executable + +use crate::godot_version::parse_godot_version; +use crate::header_gen::generate_rust_binding; +use crate::watch::StopWatch; + +use regex::Regex; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::{Command, Output}; + +// Note: CARGO_BUILD_TARGET_DIR and CARGO_TARGET_DIR are not set. +// OUT_DIR would be standing to reason, but it's an unspecified path that cannot be referenced by CI. +// const GODOT_VERSION_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/src/gen/godot_version.txt"); +const JSON_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/src/gen/extension_api.json"); + +pub fn load_gdextension_json(watch: &mut StopWatch) -> String { + let json_path = Path::new(JSON_PATH); + rerun_on_changed(json_path); + + 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); + // if !json_path.exists() || has_version_changed(&version) { + dump_extension_api(&godot_bin, json_path); + // update_version_file(&version); + + watch.record("dump_api_json"); + // } + + let result = fs::read_to_string(json_path) + .unwrap_or_else(|_| panic!("failed to open file {}", json_path.display())); + + watch.record("read_api_json"); + result +} + +pub fn write_gdextension_headers( + inout_h_path: &Path, + out_rs_path: &Path, + is_h_provided: bool, + watch: &mut StopWatch, +) { + 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); + + // if !c_header_path.exists() || has_version_changed(&version) { + dump_header_file(&godot_bin, inout_h_path); + // update_version_file(&version); + watch.record("dump_header_h"); + // } + }; + + rerun_on_changed(inout_h_path); + patch_c_header(inout_h_path); + watch.record("patch_header_h"); + + generate_rust_binding(inout_h_path, out_rs_path); + watch.record("generate_header_rs"); +} + +/* +fn has_version_changed(current_version: &str) -> bool { + let version_path = Path::new(GODOT_VERSION_PATH); + + match fs::read_to_string(version_path) { + Ok(last_version) => current_version != last_version, + Err(_) => true, + } +} + +fn update_version_file(version: &str) { + let version_path = Path::new(GODOT_VERSION_PATH); + rerun_on_changed(version_path); + + fs::write(version_path, version) + .unwrap_or_else(|_| panic!("write Godot version to file {}", version_path.display())); +} +*/ + +fn read_godot_version(godot_bin: &Path) -> String { + let output = Command::new(godot_bin) + .arg("--version") + .output() + .unwrap_or_else(|_| { + panic!( + "failed to invoke Godot executable '{}'", + godot_bin.display() + ) + }); + + let output = String::from_utf8(output.stdout).expect("convert Godot version to UTF-8"); + println!("Godot version: {output}"); + + match parse_godot_version(&output) { + Ok(parsed) => { + assert_eq!( + parsed.major, + 4, + "Only Godot versions >= 4.0 are supported; found version {}.", + output.trim() + ); + + parsed.full_string + } + Err(e) => { + // Don't treat this as fatal error + panic!("failed to parse Godot version '{output}': {e}") + } + } +} + +fn dump_extension_api(godot_bin: &Path, out_file: &Path) { + let cwd = out_file.parent().unwrap(); + fs::create_dir_all(cwd).unwrap_or_else(|_| panic!("create directory '{}'", cwd.display())); + println!("Dump GDExtension API JSON to dir '{}'...", cwd.display()); + + let mut cmd = Command::new(godot_bin); + cmd.current_dir(cwd) + .arg("--headless") + .arg("--dump-extension-api"); + + execute(cmd, "dump Godot JSON file"); + println!("Generated {}/extension_api.json.", cwd.display()); +} + +fn dump_header_file(godot_bin: &Path, out_file: &Path) { + let cwd = out_file.parent().unwrap(); + fs::create_dir_all(cwd).unwrap_or_else(|_| panic!("create directory '{}'", cwd.display())); + println!("Dump GDExtension header file to dir '{}'...", cwd.display()); + + let mut cmd = Command::new(godot_bin); + cmd.current_dir(cwd) + .arg("--headless") + .arg("--dump-gdextension-interface"); + + execute(cmd, "dump Godot header file"); + println!("Generated {}/gdextension_interface.h.", cwd.display()); +} + +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`. + + // Read the contents of the file into a string + let c = fs::read_to_string(inout_h_path) + .unwrap_or_else(|_| panic!("failed to read C header file {}", inout_h_path.display())); + + // Use single regex with independent "const"/"Const", as there are definitions like this: + // typedef const void *GDExtensionMethodBindPtr; + let c = Regex::new(r"typedef (const )?void \*GDExtension(Const)?([a-zA-Z0-9]+?)Ptr;") // + .expect("regex for mut typedef") + .replace_all(&c, "typedef ${1}struct __Gdext$3 *GDExtension${2}${3}Ptr;"); + + println!("Patched contents:\n\n{}\n\n", c.as_ref()); + + // Write the modified contents back to the file + fs::write(inout_h_path, c.as_ref()).unwrap_or_else(|_| { + panic!( + "failed to write patched C header file {}", + inout_h_path.display() + ) + }); +} +fn locate_godot_binary() -> PathBuf { + if let Ok(string) = std::env::var("GODOT4_BIN") { + println!("Found GODOT4_BIN with path to executable: '{string}'"); + println!("cargo:rerun-if-env-changed=GODOT4_BIN"); + PathBuf::from(string) + } else if let Ok(path) = which::which("godot4") { + println!("Found 'godot4' executable in PATH: {}", path.display()); + path + } else { + panic!( + "gdext with `custom-godot` feature requires 'godot4' executable or a GODOT4_BIN \ + environment variable (with the path to the executable)." + ) + } +} + +fn execute(mut cmd: Command, error_message: &str) -> Output { + let output = cmd + .output() + .unwrap_or_else(|_| panic!("failed to execute command: {error_message}")); + + if output.status.success() { + println!( + "[stdout] {}", + String::from_utf8(output.stdout.clone()).unwrap() + ); + println!( + "[stderr] {}", + String::from_utf8(output.stderr.clone()).unwrap() + ); + println!("[status] {}", output.status); + output + } else { + println!("[stdout] {}", String::from_utf8(output.stdout).unwrap()); + println!("[stderr] {}", String::from_utf8(output.stderr).unwrap()); + println!("[status] {}", output.status); + panic!("command returned error: {error_message}"); + } +} + +fn rerun_on_changed(path: &Path) { + println!("cargo:rerun-if-changed={}", path.display()); +} diff --git a/godot-codegen/src/godot_version.rs b/godot-bindings/src/godot_version.rs similarity index 100% rename from godot-codegen/src/godot_version.rs rename to godot-bindings/src/godot_version.rs diff --git a/godot-bindings/src/header_gen.rs b/godot-bindings/src/header_gen.rs new file mode 100644 index 000000000..c9a30b8bc --- /dev/null +++ b/godot-bindings/src/header_gen.rs @@ -0,0 +1,108 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use std::env; +use std::path::Path; + +pub(crate) fn generate_rust_binding(in_h_path: &Path, out_rs_path: &Path) { + let c_header_path = in_h_path.display().to_string(); + println!("cargo:rerun-if-changed={}", c_header_path); + + let builder = bindgen::Builder::default() + .header(c_header_path) + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + .prepend_enum_name(false); + + std::fs::create_dir_all( + out_rs_path + .parent() + .expect("bindgen output file has parent dir"), + ) + .expect("create bindgen output dir"); + + let bindings = configure_platform_specific(builder) + .generate() + .unwrap_or_else(|err| { + panic!( + "bindgen generate failed\n c: {}\n rs: {}\n err: {}\n", + in_h_path.display(), + out_rs_path.display(), + err + ) + }); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + bindings.write_to_file(out_rs_path).unwrap_or_else(|err| { + panic!( + "bindgen write failed\n c: {}\n rs: {}\n err: {}\n", + in_h_path.display(), + out_rs_path.display(), + err + ) + }); +} + +//#[cfg(target_os = "macos")] +fn configure_platform_specific(builder: bindgen::Builder) -> bindgen::Builder { + let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap(); + if target_vendor == "apple" { + eprintln!("Build selected for macOS."); + let path = env::var("LLVM_PATH").expect("env var 'LLVM_PATH' not set"); + + builder + .clang_arg("-I") + // .clang_arg(format!("{path}/include")) + .clang_arg(apple_include_path().expect("apple include path")) + .clang_arg("-L") + .clang_arg(format!("{path}/lib")) + } else { + eprintln!("Build selected for Linux/Windows."); + builder + } +} + +fn apple_include_path() -> Result { + use std::process::Command; + + let target = std::env::var("TARGET").unwrap(); + let platform = if target.contains("apple-darwin") { + "macosx" + } else if target == "x86_64-apple-ios" || target == "aarch64-apple-ios-sim" { + "iphonesimulator" + } else if target == "aarch64-apple-ios" { + "iphoneos" + } else { + panic!("not building for macOS or iOS"); + }; + + // run `xcrun --sdk iphoneos --show-sdk-path` + let output = Command::new("xcrun") + .args(["--sdk", platform, "--show-sdk-path"]) + .output()? + .stdout; + let prefix = std::str::from_utf8(&output) + .expect("invalid output from `xcrun`") + .trim_end(); + + let suffix = "usr/include"; + let directory = format!("{prefix}/{suffix}"); + + Ok(directory) +} + +// #[cfg(not(target_os = "macos"))] +// fn configure_platform_specific(builder: Builder) -> Builder { +// println!("Build selected for Linux/Windows."); +// builder +// } + +/*fn rerun_if_any_changed(paths: &Vec){ + for path in paths { + println!("cargo:rerun-if-changed={}", path.display()); + } +}*/ diff --git a/godot-bindings/src/lib.rs b/godot-bindings/src/lib.rs new file mode 100644 index 000000000..8cbf4a334 --- /dev/null +++ b/godot-bindings/src/lib.rs @@ -0,0 +1,87 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +pub(crate) mod watch; + +use std::path::Path; + +pub use watch::StopWatch; + +// Note: we cannot prevent both `custom-godot` and `prebuilt-godot` from being specified; see Cargo.toml for more information. + +#[cfg(not(any(feature = "custom-godot", feature = "prebuilt-godot")))] +compile_error!( + "At least one of `custom-godot` or `prebuilt-godot` must be specified (none given)." +); + +// ---------------------------------------------------------------------------------------------------------------------------------------------- +// Regenerate all files + +#[cfg(feature = "custom-godot")] +#[path = ""] +mod custom { + use super::*; + + pub(crate) mod godot_exe; + pub(crate) mod godot_version; + pub(crate) mod header_gen; + + pub fn load_gdextension_json(watch: &mut StopWatch) -> String { + godot_exe::load_gdextension_json(watch) + } + + pub fn write_gdextension_headers(h_path: &Path, rs_path: &Path, watch: &mut StopWatch) { + godot_exe::write_gdextension_headers(h_path, rs_path, false, watch); + } + + #[cfg(feature = "custom-godot-extheader")] + pub fn write_gdextension_headers_from_c(h_path: &Path, rs_path: &Path, watch: &mut StopWatch) { + godot_exe::write_gdextension_headers(h_path, rs_path, true, watch); + } +} + +#[cfg(feature = "custom-godot")] +pub use custom::*; + +// ---------------------------------------------------------------------------------------------------------------------------------------------- +// Reuse existing files + +#[cfg(not(feature = "custom-godot"))] +#[path = ""] +mod prebuilt { + use super::*; + + pub fn load_gdextension_json(_watch: &mut StopWatch) -> &'static str { + godot4_prebuilt::load_gdextension_json() + } + + pub fn write_gdextension_headers(h_path: &Path, rs_path: &Path, watch: &mut StopWatch) { + // Note: prebuilt artifacts just return a static str. + let h_contents = godot4_prebuilt::load_gdextension_header_h(); + std::fs::write(h_path, h_contents) + .unwrap_or_else(|e| panic!("failed to write gdextension_interface.h: {e}")); + watch.record("write_header_h"); + + let rs_contents = godot4_prebuilt::load_gdextension_header_rs(); + std::fs::write(rs_path, rs_contents) + .unwrap_or_else(|e| panic!("failed to write gdextension_interface.rs: {e}")); + watch.record("write_header_rs"); + } +} + +#[cfg(not(feature = "custom-godot"))] +pub use prebuilt::*; + +// ---------------------------------------------------------------------------------------------------------------------------------------------- +// Common + +pub fn clear_dir(dir: &Path, watch: &mut StopWatch) { + if dir.exists() { + std::fs::remove_dir_all(dir).unwrap_or_else(|e| panic!("failed to delete dir: {e}")); + watch.record("delete_gen_dir"); + } + std::fs::create_dir_all(dir).unwrap_or_else(|e| panic!("failed to create dir: {e}")); +} diff --git a/godot-codegen/src/watch.rs b/godot-bindings/src/watch.rs similarity index 100% rename from godot-codegen/src/watch.rs rename to godot-bindings/src/watch.rs diff --git a/godot-codegen/Cargo.toml b/godot-codegen/Cargo.toml index 1c4179efb..a07d3d4af 100644 --- a/godot-codegen/Cargo.toml +++ b/godot-codegen/Cargo.toml @@ -8,17 +8,15 @@ keywords = ["gamedev", "godot", "engine", "codegen"] categories = ["game-engines", "graphics"] [features] +default = [] codegen-fmt = [] codegen-full = [] double-precision = [] +custom-godot = ["godot-bindings/custom-godot"] [dependencies] -quote = "1" -proc-macro2 = "1" -which = "4" +godot-bindings = { path = "../godot-bindings" } heck = "0.4" - -# Version >= 1.5.5 for security: https://blog.rust-lang.org/2022/03/08/cve-2022-24713.html -# 'unicode-gencat' needed for \d, see: https://docs.rs/regex/1.5.5/regex/#unicode-features -regex = { version = "1.5.5", default-features = false, features = ["std", "unicode-gencat"] } nanoserde = "0.1.29" +proc-macro2 = "1" +quote = "1" diff --git a/godot-codegen/input/gdextension_interface.h b/godot-codegen/input/gdextension_interface.h deleted file mode 100644 index 02597f480..000000000 --- a/godot-codegen/input/gdextension_interface.h +++ /dev/null @@ -1,643 +0,0 @@ -/**************************************************************************/ -/* gdextension_interface.h */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#ifndef GDEXTENSION_INTERFACE_H -#define GDEXTENSION_INTERFACE_H - -/* This is a C class header, you can copy it and use it directly in your own binders. - * Together with the JSON file, you should be able to generate any binder. - */ - -#include -#include - -#ifndef __cplusplus -typedef uint32_t char32_t; -typedef uint16_t char16_t; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* VARIANT TYPES */ - -typedef enum { - GDEXTENSION_VARIANT_TYPE_NIL, - - /* atomic types */ - GDEXTENSION_VARIANT_TYPE_BOOL, - GDEXTENSION_VARIANT_TYPE_INT, - GDEXTENSION_VARIANT_TYPE_FLOAT, - GDEXTENSION_VARIANT_TYPE_STRING, - - /* math types */ - GDEXTENSION_VARIANT_TYPE_VECTOR2, - GDEXTENSION_VARIANT_TYPE_VECTOR2I, - GDEXTENSION_VARIANT_TYPE_RECT2, - GDEXTENSION_VARIANT_TYPE_RECT2I, - GDEXTENSION_VARIANT_TYPE_VECTOR3, - GDEXTENSION_VARIANT_TYPE_VECTOR3I, - GDEXTENSION_VARIANT_TYPE_TRANSFORM2D, - GDEXTENSION_VARIANT_TYPE_VECTOR4, - GDEXTENSION_VARIANT_TYPE_VECTOR4I, - GDEXTENSION_VARIANT_TYPE_PLANE, - GDEXTENSION_VARIANT_TYPE_QUATERNION, - GDEXTENSION_VARIANT_TYPE_AABB, - GDEXTENSION_VARIANT_TYPE_BASIS, - GDEXTENSION_VARIANT_TYPE_TRANSFORM3D, - GDEXTENSION_VARIANT_TYPE_PROJECTION, - - /* misc types */ - GDEXTENSION_VARIANT_TYPE_COLOR, - GDEXTENSION_VARIANT_TYPE_STRING_NAME, - GDEXTENSION_VARIANT_TYPE_NODE_PATH, - GDEXTENSION_VARIANT_TYPE_RID, - GDEXTENSION_VARIANT_TYPE_OBJECT, - GDEXTENSION_VARIANT_TYPE_CALLABLE, - GDEXTENSION_VARIANT_TYPE_SIGNAL, - GDEXTENSION_VARIANT_TYPE_DICTIONARY, - GDEXTENSION_VARIANT_TYPE_ARRAY, - - /* typed arrays */ - GDEXTENSION_VARIANT_TYPE_PACKED_BYTE_ARRAY, - GDEXTENSION_VARIANT_TYPE_PACKED_INT32_ARRAY, - GDEXTENSION_VARIANT_TYPE_PACKED_INT64_ARRAY, - GDEXTENSION_VARIANT_TYPE_PACKED_FLOAT32_ARRAY, - GDEXTENSION_VARIANT_TYPE_PACKED_FLOAT64_ARRAY, - GDEXTENSION_VARIANT_TYPE_PACKED_STRING_ARRAY, - GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR2_ARRAY, - GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR3_ARRAY, - GDEXTENSION_VARIANT_TYPE_PACKED_COLOR_ARRAY, - - GDEXTENSION_VARIANT_TYPE_VARIANT_MAX -} GDExtensionVariantType; - -typedef enum { - /* comparison */ - GDEXTENSION_VARIANT_OP_EQUAL, - GDEXTENSION_VARIANT_OP_NOT_EQUAL, - GDEXTENSION_VARIANT_OP_LESS, - GDEXTENSION_VARIANT_OP_LESS_EQUAL, - GDEXTENSION_VARIANT_OP_GREATER, - GDEXTENSION_VARIANT_OP_GREATER_EQUAL, - - /* mathematic */ - GDEXTENSION_VARIANT_OP_ADD, - GDEXTENSION_VARIANT_OP_SUBTRACT, - GDEXTENSION_VARIANT_OP_MULTIPLY, - GDEXTENSION_VARIANT_OP_DIVIDE, - GDEXTENSION_VARIANT_OP_NEGATE, - GDEXTENSION_VARIANT_OP_POSITIVE, - GDEXTENSION_VARIANT_OP_MODULE, - GDEXTENSION_VARIANT_OP_POWER, - - /* bitwise */ - GDEXTENSION_VARIANT_OP_SHIFT_LEFT, - GDEXTENSION_VARIANT_OP_SHIFT_RIGHT, - GDEXTENSION_VARIANT_OP_BIT_AND, - GDEXTENSION_VARIANT_OP_BIT_OR, - GDEXTENSION_VARIANT_OP_BIT_XOR, - GDEXTENSION_VARIANT_OP_BIT_NEGATE, - - /* logic */ - GDEXTENSION_VARIANT_OP_AND, - GDEXTENSION_VARIANT_OP_OR, - GDEXTENSION_VARIANT_OP_XOR, - GDEXTENSION_VARIANT_OP_NOT, - - /* containment */ - GDEXTENSION_VARIANT_OP_IN, - GDEXTENSION_VARIANT_OP_MAX - -} GDExtensionVariantOperator; - -typedef struct TagVariant *GDExtensionVariantPtr; -typedef const struct TagVariant *GDExtensionConstVariantPtr; -typedef struct TagStringName *GDExtensionStringNamePtr; -typedef const struct TagStringName *GDExtensionConstStringNamePtr; -typedef struct TagString *GDExtensionStringPtr; -typedef const struct TagString *GDExtensionConstStringPtr; -typedef struct TagObject *GDExtensionObjectPtr; -typedef const struct TagObject *GDExtensionConstObjectPtr; -typedef struct TagType *GDExtensionTypePtr; -typedef const struct TagType *GDExtensionConstTypePtr; -typedef struct TagMethodBind *GDExtensionMethodBindPtr; -typedef int64_t GDExtensionInt; -typedef uint8_t GDExtensionBool; -typedef uint64_t GDObjectInstanceID; -typedef struct TagExtensionRef *GDExtensionRefPtr; -typedef const struct TagExtensionRef *GDExtensionConstRefPtr; - -/* VARIANT DATA I/O */ - -typedef enum { - GDEXTENSION_CALL_OK, - GDEXTENSION_CALL_ERROR_INVALID_METHOD, - GDEXTENSION_CALL_ERROR_INVALID_ARGUMENT, // Expected a different variant type. - GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS, // Expected lower number of arguments. - GDEXTENSION_CALL_ERROR_TOO_FEW_ARGUMENTS, // Expected higher number of arguments. - GDEXTENSION_CALL_ERROR_INSTANCE_IS_NULL, - GDEXTENSION_CALL_ERROR_METHOD_NOT_CONST, // Used for const call. -} GDExtensionCallErrorType; - -typedef struct { - GDExtensionCallErrorType error; - int32_t argument; - int32_t expected; -} GDExtensionCallError; - -typedef void (*GDExtensionVariantFromTypeConstructorFunc)(GDExtensionVariantPtr, GDExtensionTypePtr); -typedef void (*GDExtensionTypeFromVariantConstructorFunc)(GDExtensionTypePtr, GDExtensionVariantPtr); -typedef void (*GDExtensionPtrOperatorEvaluator)(GDExtensionConstTypePtr p_left, GDExtensionConstTypePtr p_right, GDExtensionTypePtr r_result); -typedef void (*GDExtensionPtrBuiltInMethod)(GDExtensionTypePtr p_base, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_return, int p_argument_count); -typedef void (*GDExtensionPtrConstructor)(GDExtensionTypePtr p_base, const GDExtensionConstTypePtr *p_args); -typedef void (*GDExtensionPtrDestructor)(GDExtensionTypePtr p_base); -typedef void (*GDExtensionPtrSetter)(GDExtensionTypePtr p_base, GDExtensionConstTypePtr p_value); -typedef void (*GDExtensionPtrGetter)(GDExtensionConstTypePtr p_base, GDExtensionTypePtr r_value); -typedef void (*GDExtensionPtrIndexedSetter)(GDExtensionTypePtr p_base, GDExtensionInt p_index, GDExtensionConstTypePtr p_value); -typedef void (*GDExtensionPtrIndexedGetter)(GDExtensionConstTypePtr p_base, GDExtensionInt p_index, GDExtensionTypePtr r_value); -typedef void (*GDExtensionPtrKeyedSetter)(GDExtensionTypePtr p_base, GDExtensionConstTypePtr p_key, GDExtensionConstTypePtr p_value); -typedef void (*GDExtensionPtrKeyedGetter)(GDExtensionConstTypePtr p_base, GDExtensionConstTypePtr p_key, GDExtensionTypePtr r_value); -typedef uint32_t (*GDExtensionPtrKeyedChecker)(GDExtensionConstVariantPtr p_base, GDExtensionConstVariantPtr p_key); -typedef void (*GDExtensionPtrUtilityFunction)(GDExtensionTypePtr r_return, const GDExtensionConstTypePtr *p_args, int p_argument_count); - -typedef GDExtensionObjectPtr (*GDExtensionClassConstructor)(); - -typedef void *(*GDExtensionInstanceBindingCreateCallback)(void *p_token, void *p_instance); -typedef void (*GDExtensionInstanceBindingFreeCallback)(void *p_token, void *p_instance, void *p_binding); -typedef GDExtensionBool (*GDExtensionInstanceBindingReferenceCallback)(void *p_token, void *p_binding, GDExtensionBool p_reference); - -typedef struct { - GDExtensionInstanceBindingCreateCallback create_callback; - GDExtensionInstanceBindingFreeCallback free_callback; - GDExtensionInstanceBindingReferenceCallback reference_callback; -} GDExtensionInstanceBindingCallbacks; - -/* EXTENSION CLASSES */ - -typedef void *GDExtensionClassInstancePtr; - -typedef GDExtensionBool (*GDExtensionClassSet)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionConstVariantPtr p_value); -typedef GDExtensionBool (*GDExtensionClassGet)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret); -typedef uint64_t (*GDExtensionClassGetRID)(GDExtensionClassInstancePtr p_instance); - -typedef struct { - GDExtensionVariantType type; - GDExtensionStringNamePtr name; - GDExtensionStringNamePtr class_name; - uint32_t hint; // Bitfield of `PropertyHint` (defined in `extension_api.json`). - GDExtensionStringPtr hint_string; - uint32_t usage; // Bitfield of `PropertyUsageFlags` (defined in `extension_api.json`). -} GDExtensionPropertyInfo; - -typedef struct { - GDExtensionStringNamePtr name; - GDExtensionPropertyInfo return_value; - uint32_t flags; // Bitfield of `GDExtensionClassMethodFlags`. - int32_t id; - - /* Arguments: `default_arguments` is an array of size `argument_count`. */ - uint32_t argument_count; - GDExtensionPropertyInfo *arguments; - - /* Default arguments: `default_arguments` is an array of size `default_argument_count`. */ - uint32_t default_argument_count; - GDExtensionVariantPtr *default_arguments; -} GDExtensionMethodInfo; - -typedef const GDExtensionPropertyInfo *(*GDExtensionClassGetPropertyList)(GDExtensionClassInstancePtr p_instance, uint32_t *r_count); -typedef void (*GDExtensionClassFreePropertyList)(GDExtensionClassInstancePtr p_instance, const GDExtensionPropertyInfo *p_list); -typedef GDExtensionBool (*GDExtensionClassPropertyCanRevert)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name); -typedef GDExtensionBool (*GDExtensionClassPropertyGetRevert)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret); -typedef void (*GDExtensionClassNotification)(GDExtensionClassInstancePtr p_instance, int32_t p_what); -typedef void (*GDExtensionClassToString)(GDExtensionClassInstancePtr p_instance, GDExtensionBool *r_is_valid, GDExtensionStringPtr p_out); -typedef void (*GDExtensionClassReference)(GDExtensionClassInstancePtr p_instance); -typedef void (*GDExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance); -typedef void (*GDExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret); -typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance)(void *p_userdata); -typedef void (*GDExtensionClassFreeInstance)(void *p_userdata, GDExtensionClassInstancePtr p_instance); -typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_userdata, GDExtensionConstStringNamePtr p_name); - -typedef struct { - GDExtensionBool is_virtual; - GDExtensionBool is_abstract; - GDExtensionClassSet set_func; - GDExtensionClassGet get_func; - GDExtensionClassGetPropertyList get_property_list_func; - GDExtensionClassFreePropertyList free_property_list_func; - GDExtensionClassPropertyCanRevert property_can_revert_func; - GDExtensionClassPropertyGetRevert property_get_revert_func; - GDExtensionClassNotification notification_func; - GDExtensionClassToString to_string_func; - GDExtensionClassReference reference_func; - GDExtensionClassUnreference unreference_func; - GDExtensionClassCreateInstance create_instance_func; // (Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract. - GDExtensionClassFreeInstance free_instance_func; // Destructor; mandatory. - GDExtensionClassGetVirtual get_virtual_func; // Queries a virtual function by name and returns a callback to invoke the requested virtual function. - GDExtensionClassGetRID get_rid_func; - void *class_userdata; // Per-class user data, later accessible in instance bindings. -} GDExtensionClassCreationInfo; - -typedef void *GDExtensionClassLibraryPtr; - -/* Method */ - -typedef enum { - GDEXTENSION_METHOD_FLAG_NORMAL = 1, - GDEXTENSION_METHOD_FLAG_EDITOR = 2, - GDEXTENSION_METHOD_FLAG_CONST = 4, - GDEXTENSION_METHOD_FLAG_VIRTUAL = 8, - GDEXTENSION_METHOD_FLAG_VARARG = 16, - GDEXTENSION_METHOD_FLAG_STATIC = 32, - GDEXTENSION_METHOD_FLAGS_DEFAULT = GDEXTENSION_METHOD_FLAG_NORMAL, -} GDExtensionClassMethodFlags; - -typedef enum { - GDEXTENSION_METHOD_ARGUMENT_METADATA_NONE, - GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT8, - GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT16, - GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT32, - GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT64, - GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT8, - GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT16, - GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT32, - GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT64, - GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_FLOAT, - GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE -} GDExtensionClassMethodArgumentMetadata; - -typedef void (*GDExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); -typedef void (*GDExtensionClassMethodPtrCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret); - -typedef struct { - GDExtensionStringNamePtr name; - void *method_userdata; - GDExtensionClassMethodCall call_func; - GDExtensionClassMethodPtrCall ptrcall_func; - uint32_t method_flags; // Bitfield of `GDExtensionClassMethodFlags`. - - /* If `has_return_value` is false, `return_value_info` and `return_value_metadata` are ignored. */ - GDExtensionBool has_return_value; - GDExtensionPropertyInfo *return_value_info; - GDExtensionClassMethodArgumentMetadata return_value_metadata; - - /* Arguments: `arguments_info` and `arguments_metadata` are array of size `argument_count`. - * Name and hint information for the argument can be omitted in release builds. Class name should always be present if it applies. - */ - uint32_t argument_count; - GDExtensionPropertyInfo *arguments_info; - GDExtensionClassMethodArgumentMetadata *arguments_metadata; - - /* Default arguments: `default_arguments` is an array of size `default_argument_count`. */ - uint32_t default_argument_count; - GDExtensionVariantPtr *default_arguments; -} GDExtensionClassMethodInfo; - -/* SCRIPT INSTANCE EXTENSION */ - -typedef void *GDExtensionScriptInstanceDataPtr; // Pointer to custom ScriptInstance native implementation. - -typedef GDExtensionBool (*GDExtensionScriptInstanceSet)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionConstVariantPtr p_value); -typedef GDExtensionBool (*GDExtensionScriptInstanceGet)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret); -typedef const GDExtensionPropertyInfo *(*GDExtensionScriptInstanceGetPropertyList)(GDExtensionScriptInstanceDataPtr p_instance, uint32_t *r_count); -typedef void (*GDExtensionScriptInstanceFreePropertyList)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionPropertyInfo *p_list); -typedef GDExtensionVariantType (*GDExtensionScriptInstanceGetPropertyType)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionBool *r_is_valid); - -typedef GDExtensionBool (*GDExtensionScriptInstancePropertyCanRevert)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name); -typedef GDExtensionBool (*GDExtensionScriptInstancePropertyGetRevert)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret); - -typedef GDExtensionObjectPtr (*GDExtensionScriptInstanceGetOwner)(GDExtensionScriptInstanceDataPtr p_instance); -typedef void (*GDExtensionScriptInstancePropertyStateAdd)(GDExtensionConstStringNamePtr p_name, GDExtensionConstVariantPtr p_value, void *p_userdata); -typedef void (*GDExtensionScriptInstanceGetPropertyState)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionScriptInstancePropertyStateAdd p_add_func, void *p_userdata); - -typedef const GDExtensionMethodInfo *(*GDExtensionScriptInstanceGetMethodList)(GDExtensionScriptInstanceDataPtr p_instance, uint32_t *r_count); -typedef void (*GDExtensionScriptInstanceFreeMethodList)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionMethodInfo *p_list); - -typedef GDExtensionBool (*GDExtensionScriptInstanceHasMethod)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name); - -typedef void (*GDExtensionScriptInstanceCall)(GDExtensionScriptInstanceDataPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); -typedef void (*GDExtensionScriptInstanceNotification)(GDExtensionScriptInstanceDataPtr p_instance, int32_t p_what); -typedef void (*GDExtensionScriptInstanceToString)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out); - -typedef void (*GDExtensionScriptInstanceRefCountIncremented)(GDExtensionScriptInstanceDataPtr p_instance); -typedef GDExtensionBool (*GDExtensionScriptInstanceRefCountDecremented)(GDExtensionScriptInstanceDataPtr p_instance); - -typedef GDExtensionObjectPtr (*GDExtensionScriptInstanceGetScript)(GDExtensionScriptInstanceDataPtr p_instance); -typedef GDExtensionBool (*GDExtensionScriptInstanceIsPlaceholder)(GDExtensionScriptInstanceDataPtr p_instance); - -typedef void *GDExtensionScriptLanguagePtr; - -typedef GDExtensionScriptLanguagePtr (*GDExtensionScriptInstanceGetLanguage)(GDExtensionScriptInstanceDataPtr p_instance); - -typedef void (*GDExtensionScriptInstanceFree)(GDExtensionScriptInstanceDataPtr p_instance); - -typedef void *GDExtensionScriptInstancePtr; // Pointer to ScriptInstance. - -typedef struct { - GDExtensionScriptInstanceSet set_func; - GDExtensionScriptInstanceGet get_func; - GDExtensionScriptInstanceGetPropertyList get_property_list_func; - GDExtensionScriptInstanceFreePropertyList free_property_list_func; - - GDExtensionScriptInstancePropertyCanRevert property_can_revert_func; - GDExtensionScriptInstancePropertyGetRevert property_get_revert_func; - - GDExtensionScriptInstanceGetOwner get_owner_func; - GDExtensionScriptInstanceGetPropertyState get_property_state_func; - - GDExtensionScriptInstanceGetMethodList get_method_list_func; - GDExtensionScriptInstanceFreeMethodList free_method_list_func; - GDExtensionScriptInstanceGetPropertyType get_property_type_func; - - GDExtensionScriptInstanceHasMethod has_method_func; - - GDExtensionScriptInstanceCall call_func; - GDExtensionScriptInstanceNotification notification_func; - - GDExtensionScriptInstanceToString to_string_func; - - GDExtensionScriptInstanceRefCountIncremented refcount_incremented_func; - GDExtensionScriptInstanceRefCountDecremented refcount_decremented_func; - - GDExtensionScriptInstanceGetScript get_script_func; - - GDExtensionScriptInstanceIsPlaceholder is_placeholder_func; - - GDExtensionScriptInstanceSet set_fallback_func; - GDExtensionScriptInstanceGet get_fallback_func; - - GDExtensionScriptInstanceGetLanguage get_language_func; - - GDExtensionScriptInstanceFree free_func; - -} GDExtensionScriptInstanceInfo; - -/* INTERFACE */ - -typedef struct { - uint32_t version_major; - uint32_t version_minor; - uint32_t version_patch; - const char *version_string; - - /* GODOT CORE */ - - void *(*mem_alloc)(size_t p_bytes); - void *(*mem_realloc)(void *p_ptr, size_t p_bytes); - void (*mem_free)(void *p_ptr); - - void (*print_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify); - void (*print_error_with_message)(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify); - void (*print_warning)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify); - void (*print_warning_with_message)(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify); - void (*print_script_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify); - void (*print_script_error_with_message)(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, GDExtensionBool p_editor_notify); - - uint64_t (*get_native_struct_size)(GDExtensionConstStringNamePtr p_name); - - /* GODOT VARIANT */ - - /* variant general */ - void (*variant_new_copy)(GDExtensionVariantPtr r_dest, GDExtensionConstVariantPtr p_src); - void (*variant_new_nil)(GDExtensionVariantPtr r_dest); - void (*variant_destroy)(GDExtensionVariantPtr p_self); - - /* variant type */ - void (*variant_call)(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); - void (*variant_call_static)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); - void (*variant_evaluate)(GDExtensionVariantOperator p_op, GDExtensionConstVariantPtr p_a, GDExtensionConstVariantPtr p_b, GDExtensionVariantPtr r_return, GDExtensionBool *r_valid); - void (*variant_set)(GDExtensionVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid); - void (*variant_set_named)(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid); - void (*variant_set_keyed)(GDExtensionVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid); - void (*variant_set_indexed)(GDExtensionVariantPtr p_self, GDExtensionInt p_index, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid, GDExtensionBool *r_oob); - void (*variant_get)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid); - void (*variant_get_named)(GDExtensionConstVariantPtr p_self, GDExtensionConstStringNamePtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid); - void (*variant_get_keyed)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid); - void (*variant_get_indexed)(GDExtensionConstVariantPtr p_self, GDExtensionInt p_index, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid, GDExtensionBool *r_oob); - GDExtensionBool (*variant_iter_init)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionBool *r_valid); - GDExtensionBool (*variant_iter_next)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionBool *r_valid); - void (*variant_iter_get)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_iter, GDExtensionVariantPtr r_ret, GDExtensionBool *r_valid); - GDExtensionInt (*variant_hash)(GDExtensionConstVariantPtr p_self); - GDExtensionInt (*variant_recursive_hash)(GDExtensionConstVariantPtr p_self, GDExtensionInt p_recursion_count); - GDExtensionBool (*variant_hash_compare)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_other); - GDExtensionBool (*variant_booleanize)(GDExtensionConstVariantPtr p_self); - void (*variant_duplicate)(GDExtensionConstVariantPtr p_self, GDExtensionVariantPtr r_ret, GDExtensionBool p_deep); - void (*variant_stringify)(GDExtensionConstVariantPtr p_self, GDExtensionStringPtr r_ret); - - GDExtensionVariantType (*variant_get_type)(GDExtensionConstVariantPtr p_self); - GDExtensionBool (*variant_has_method)(GDExtensionConstVariantPtr p_self, GDExtensionConstStringNamePtr p_method); - GDExtensionBool (*variant_has_member)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member); - GDExtensionBool (*variant_has_key)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionBool *r_valid); - void (*variant_get_type_name)(GDExtensionVariantType p_type, GDExtensionStringPtr r_name); - GDExtensionBool (*variant_can_convert)(GDExtensionVariantType p_from, GDExtensionVariantType p_to); - GDExtensionBool (*variant_can_convert_strict)(GDExtensionVariantType p_from, GDExtensionVariantType p_to); - - /* ptrcalls */ - GDExtensionVariantFromTypeConstructorFunc (*get_variant_from_type_constructor)(GDExtensionVariantType p_type); - GDExtensionTypeFromVariantConstructorFunc (*get_variant_to_type_constructor)(GDExtensionVariantType p_type); - GDExtensionPtrOperatorEvaluator (*variant_get_ptr_operator_evaluator)(GDExtensionVariantOperator p_operator, GDExtensionVariantType p_type_a, GDExtensionVariantType p_type_b); - GDExtensionPtrBuiltInMethod (*variant_get_ptr_builtin_method)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, GDExtensionInt p_hash); - GDExtensionPtrConstructor (*variant_get_ptr_constructor)(GDExtensionVariantType p_type, int32_t p_constructor); - GDExtensionPtrDestructor (*variant_get_ptr_destructor)(GDExtensionVariantType p_type); - void (*variant_construct)(GDExtensionVariantType p_type, GDExtensionVariantPtr p_base, const GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error); - GDExtensionPtrSetter (*variant_get_ptr_setter)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member); - GDExtensionPtrGetter (*variant_get_ptr_getter)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member); - GDExtensionPtrIndexedSetter (*variant_get_ptr_indexed_setter)(GDExtensionVariantType p_type); - GDExtensionPtrIndexedGetter (*variant_get_ptr_indexed_getter)(GDExtensionVariantType p_type); - GDExtensionPtrKeyedSetter (*variant_get_ptr_keyed_setter)(GDExtensionVariantType p_type); - GDExtensionPtrKeyedGetter (*variant_get_ptr_keyed_getter)(GDExtensionVariantType p_type); - GDExtensionPtrKeyedChecker (*variant_get_ptr_keyed_checker)(GDExtensionVariantType p_type); - void (*variant_get_constant_value)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_constant, GDExtensionVariantPtr r_ret); - GDExtensionPtrUtilityFunction (*variant_get_ptr_utility_function)(GDExtensionConstStringNamePtr p_function, GDExtensionInt p_hash); - - /* extra utilities */ - void (*string_new_with_latin1_chars)(GDExtensionStringPtr r_dest, const char *p_contents); - void (*string_new_with_utf8_chars)(GDExtensionStringPtr r_dest, const char *p_contents); - void (*string_new_with_utf16_chars)(GDExtensionStringPtr r_dest, const char16_t *p_contents); - void (*string_new_with_utf32_chars)(GDExtensionStringPtr r_dest, const char32_t *p_contents); - void (*string_new_with_wide_chars)(GDExtensionStringPtr r_dest, const wchar_t *p_contents); - void (*string_new_with_latin1_chars_and_len)(GDExtensionStringPtr r_dest, const char *p_contents, GDExtensionInt p_size); - void (*string_new_with_utf8_chars_and_len)(GDExtensionStringPtr r_dest, const char *p_contents, GDExtensionInt p_size); - void (*string_new_with_utf16_chars_and_len)(GDExtensionStringPtr r_dest, const char16_t *p_contents, GDExtensionInt p_size); - void (*string_new_with_utf32_chars_and_len)(GDExtensionStringPtr r_dest, const char32_t *p_contents, GDExtensionInt p_size); - void (*string_new_with_wide_chars_and_len)(GDExtensionStringPtr r_dest, const wchar_t *p_contents, GDExtensionInt p_size); - /* Information about the following functions: - * - The return value is the resulting encoded string length. - * - The length returned is in characters, not in bytes. It also does not include a trailing zero. - * - These functions also do not write trailing zero, If you need it, write it yourself at the position indicated by the length (and make sure to allocate it). - * - Passing NULL in r_text means only the length is computed (again, without including trailing zero). - * - p_max_write_length argument is in characters, not bytes. It will be ignored if r_text is NULL. - * - p_max_write_length argument does not affect the return value, it's only to cap write length. - */ - GDExtensionInt (*string_to_latin1_chars)(GDExtensionConstStringPtr p_self, char *r_text, GDExtensionInt p_max_write_length); - GDExtensionInt (*string_to_utf8_chars)(GDExtensionConstStringPtr p_self, char *r_text, GDExtensionInt p_max_write_length); - GDExtensionInt (*string_to_utf16_chars)(GDExtensionConstStringPtr p_self, char16_t *r_text, GDExtensionInt p_max_write_length); - GDExtensionInt (*string_to_utf32_chars)(GDExtensionConstStringPtr p_self, char32_t *r_text, GDExtensionInt p_max_write_length); - GDExtensionInt (*string_to_wide_chars)(GDExtensionConstStringPtr p_self, wchar_t *r_text, GDExtensionInt p_max_write_length); - char32_t *(*string_operator_index)(GDExtensionStringPtr p_self, GDExtensionInt p_index); - const char32_t *(*string_operator_index_const)(GDExtensionConstStringPtr p_self, GDExtensionInt p_index); - - void (*string_operator_plus_eq_string)(GDExtensionStringPtr p_self, GDExtensionConstStringPtr p_b); - void (*string_operator_plus_eq_char)(GDExtensionStringPtr p_self, char32_t p_b); - void (*string_operator_plus_eq_cstr)(GDExtensionStringPtr p_self, const char *p_b); - void (*string_operator_plus_eq_wcstr)(GDExtensionStringPtr p_self, const wchar_t *p_b); - void (*string_operator_plus_eq_c32str)(GDExtensionStringPtr p_self, const char32_t *p_b); - - /* XMLParser extra utilities */ - - GDExtensionInt (*xml_parser_open_buffer)(GDExtensionObjectPtr p_instance, const uint8_t *p_buffer, size_t p_size); - - /* FileAccess extra utilities */ - - void (*file_access_store_buffer)(GDExtensionObjectPtr p_instance, const uint8_t *p_src, uint64_t p_length); - uint64_t (*file_access_get_buffer)(GDExtensionConstObjectPtr p_instance, uint8_t *p_dst, uint64_t p_length); - - /* WorkerThreadPool extra utilities */ - - int64_t (*worker_thread_pool_add_native_group_task)(GDExtensionObjectPtr p_instance, void (*p_func)(void *, uint32_t), void *p_userdata, int p_elements, int p_tasks, GDExtensionBool p_high_priority, GDExtensionConstStringPtr p_description); - int64_t (*worker_thread_pool_add_native_task)(GDExtensionObjectPtr p_instance, void (*p_func)(void *), void *p_userdata, GDExtensionBool p_high_priority, GDExtensionConstStringPtr p_description); - - /* Packed array functions */ - - uint8_t *(*packed_byte_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedByteArray - const uint8_t *(*packed_byte_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedByteArray - - GDExtensionTypePtr (*packed_color_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedColorArray, returns Color ptr - GDExtensionTypePtr (*packed_color_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedColorArray, returns Color ptr - - float *(*packed_float32_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedFloat32Array - const float *(*packed_float32_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedFloat32Array - double *(*packed_float64_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedFloat64Array - const double *(*packed_float64_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedFloat64Array - - int32_t *(*packed_int32_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedInt32Array - const int32_t *(*packed_int32_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedInt32Array - int64_t *(*packed_int64_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedInt32Array - const int64_t *(*packed_int64_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedInt32Array - - GDExtensionStringPtr (*packed_string_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedStringArray - GDExtensionStringPtr (*packed_string_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedStringArray - - GDExtensionTypePtr (*packed_vector2_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr - GDExtensionTypePtr (*packed_vector2_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedVector2Array, returns Vector2 ptr - GDExtensionTypePtr (*packed_vector3_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr - GDExtensionTypePtr (*packed_vector3_array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedVector3Array, returns Vector3 ptr - - GDExtensionVariantPtr (*array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be an Array ptr - GDExtensionVariantPtr (*array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be an Array ptr - void (*array_ref)(GDExtensionTypePtr p_self, GDExtensionConstTypePtr p_from); // p_self should be an Array ptr - void (*array_set_typed)(GDExtensionTypePtr p_self, GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstVariantPtr p_script); // p_self should be an Array ptr - - /* Dictionary functions */ - - GDExtensionVariantPtr (*dictionary_operator_index)(GDExtensionTypePtr p_self, GDExtensionConstVariantPtr p_key); // p_self should be an Dictionary ptr - GDExtensionVariantPtr (*dictionary_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionConstVariantPtr p_key); // p_self should be an Dictionary ptr - - /* OBJECT */ - - void (*object_method_bind_call)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionVariantPtr r_ret, GDExtensionCallError *r_error); - void (*object_method_bind_ptrcall)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret); - void (*object_destroy)(GDExtensionObjectPtr p_o); - GDExtensionObjectPtr (*global_get_singleton)(GDExtensionConstStringNamePtr p_name); - - void *(*object_get_instance_binding)(GDExtensionObjectPtr p_o, void *p_token, const GDExtensionInstanceBindingCallbacks *p_callbacks); - void (*object_set_instance_binding)(GDExtensionObjectPtr p_o, void *p_token, void *p_binding, const GDExtensionInstanceBindingCallbacks *p_callbacks); - - void (*object_set_instance)(GDExtensionObjectPtr p_o, GDExtensionConstStringNamePtr p_classname, GDExtensionClassInstancePtr p_instance); /* p_classname should be a registered extension class and should extend the p_o object's class. */ - - GDExtensionObjectPtr (*object_cast_to)(GDExtensionConstObjectPtr p_object, void *p_class_tag); - GDExtensionObjectPtr (*object_get_instance_from_id)(GDObjectInstanceID p_instance_id); - GDObjectInstanceID (*object_get_instance_id)(GDExtensionConstObjectPtr p_object); - - /* REFERENCE */ - - GDExtensionObjectPtr (*ref_get_object)(GDExtensionConstRefPtr p_ref); - void (*ref_set_object)(GDExtensionRefPtr p_ref, GDExtensionObjectPtr p_object); - - /* SCRIPT INSTANCE */ - - GDExtensionScriptInstancePtr (*script_instance_create)(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data); - - /* CLASSDB */ - - GDExtensionObjectPtr (*classdb_construct_object)(GDExtensionConstStringNamePtr p_classname); /* The passed class must be a built-in godot class, or an already-registered extension class. In both case, object_set_instance should be called to fully initialize the object. */ - GDExtensionMethodBindPtr (*classdb_get_method_bind)(GDExtensionConstStringNamePtr p_classname, GDExtensionConstStringNamePtr p_methodname, GDExtensionInt p_hash); - void *(*classdb_get_class_tag)(GDExtensionConstStringNamePtr p_classname); - - /* CLASSDB EXTENSION */ - - /* Provided parameters for `classdb_register_extension_*` can be safely freed once the function returns. */ - void (*classdb_register_extension_class)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs); - void (*classdb_register_extension_class_method)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info); - void (*classdb_register_extension_class_integer_constant)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield); - void (*classdb_register_extension_class_property)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter); - void (*classdb_register_extension_class_property_group)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringPtr p_group_name, GDExtensionConstStringPtr p_prefix); - void (*classdb_register_extension_class_property_subgroup)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringPtr p_subgroup_name, GDExtensionConstStringPtr p_prefix); - void (*classdb_register_extension_class_signal)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_signal_name, const GDExtensionPropertyInfo *p_argument_info, GDExtensionInt p_argument_count); - void (*classdb_unregister_extension_class)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name); /* Unregistering a parent class before a class that inherits it will result in failure. Inheritors must be unregistered first. */ - - void (*get_library_path)(GDExtensionClassLibraryPtr p_library, GDExtensionStringPtr r_path); - -} GDExtensionInterface; - -/* INITIALIZATION */ - -typedef enum { - GDEXTENSION_INITIALIZATION_CORE, - GDEXTENSION_INITIALIZATION_SERVERS, - GDEXTENSION_INITIALIZATION_SCENE, - GDEXTENSION_INITIALIZATION_EDITOR, - GDEXTENSION_MAX_INITIALIZATION_LEVEL, -} GDExtensionInitializationLevel; - -typedef struct { - /* Minimum initialization level required. - * If Core or Servers, the extension needs editor or game restart to take effect */ - GDExtensionInitializationLevel minimum_initialization_level; - /* Up to the user to supply when initializing */ - void *userdata; - /* This function will be called multiple times for each initialization level. */ - void (*initialize)(void *userdata, GDExtensionInitializationLevel p_level); - void (*deinitialize)(void *userdata, GDExtensionInitializationLevel p_level); -} GDExtensionInitialization; - -/* Define a C function prototype that implements the function below and expose it to dlopen() (or similar). - * This is the entry point of the GDExtension library and will be called on initialization. - * It can be used to set up different init levels, which are called during various stages of initialization/shutdown. - * The function name must be a unique one specified in the .gdextension config file. - */ -typedef GDExtensionBool (*GDExtensionInitializationFunction)(const GDExtensionInterface *p_interface, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization); - -#ifdef __cplusplus -} -#endif - -#endif // GDEXTENSION_INTERFACE_H diff --git a/godot-codegen/input/tweak.patch b/godot-codegen/input/tweak.patch deleted file mode 100644 index 896856f96..000000000 --- a/godot-codegen/input/tweak.patch +++ /dev/null @@ -1,40 +0,0 @@ -diff --git b/godot-codegen/input/gdextension_interface.h a/godot-codegen/input/gdextension_interface.h -index 0b7615f..6db266e 100644 ---- b/godot-codegen/input/gdextension_interface.h -+++ a/godot-codegen/input/gdextension_interface.h -@@ -140,22 +140,22 @@ typedef enum { - - } GDExtensionVariantOperator; - --typedef void *GDExtensionVariantPtr; --typedef const void *GDExtensionConstVariantPtr; --typedef void *GDExtensionStringNamePtr; --typedef const void *GDExtensionConstStringNamePtr; --typedef void *GDExtensionStringPtr; --typedef const void *GDExtensionConstStringPtr; --typedef void *GDExtensionObjectPtr; --typedef const void *GDExtensionConstObjectPtr; --typedef void *GDExtensionTypePtr; --typedef const void *GDExtensionConstTypePtr; --typedef const void *GDExtensionMethodBindPtr; -+typedef struct TagVariant *GDExtensionVariantPtr; -+typedef const struct TagVariant *GDExtensionConstVariantPtr; -+typedef struct TagStringName *GDExtensionStringNamePtr; -+typedef const struct TagStringName *GDExtensionConstStringNamePtr; -+typedef struct TagString *GDExtensionStringPtr; -+typedef const struct TagString *GDExtensionConstStringPtr; -+typedef struct TagObject *GDExtensionObjectPtr; -+typedef const struct TagObject *GDExtensionConstObjectPtr; -+typedef struct TagType *GDExtensionTypePtr; -+typedef const struct TagType *GDExtensionConstTypePtr; -+typedef struct TagMethodBind *GDExtensionMethodBindPtr; - typedef int64_t GDExtensionInt; - typedef uint8_t GDExtensionBool; - typedef uint64_t GDObjectInstanceID; --typedef void *GDExtensionRefPtr; --typedef const void *GDExtensionConstRefPtr; -+typedef struct TagExtensionRef *GDExtensionRefPtr; -+typedef const struct TagExtensionRef *GDExtensionConstRefPtr; - - /* VARIANT DATA I/O */ - diff --git a/godot-codegen/src/api_parser.rs b/godot-codegen/src/api_parser.rs index bd754c42a..3561a672c 100644 --- a/godot-codegen/src/api_parser.rs +++ b/godot-codegen/src/api_parser.rs @@ -8,8 +8,6 @@ #![allow(dead_code)] #![allow(clippy::question_mark)] // in #[derive(DeJson)] -use crate::{godot_exe, StopWatch}; - use nanoserde::DeJson; // ---------------------------------------------------------------------------------------------------------------------------------------------- @@ -218,7 +216,7 @@ impl MethodReturn { // ---------------------------------------------------------------------------------------------------------------------------------------------- // Implementation -pub fn load_extension_api(watch: &mut StopWatch) -> (ExtensionApi, &'static str) { +pub fn load_extension_api(watch: &mut godot_bindings::StopWatch) -> (ExtensionApi, &'static str) { // For float/double inference, see: // * https://github.com/godotengine/godot-proposals/issues/892 // * https://github.com/godotengine/godot-cpp/pull/728 @@ -227,9 +225,15 @@ pub fn load_extension_api(watch: &mut StopWatch) -> (ExtensionApi, &'static str) #[cfg(not(feature = "double-precision"))] let build_config = "float_64"; // TODO infer this - let json: String = godot_exe::load_extension_api_json(watch); + // Use type inference, so we can accept both String (dynamically resolved) and &str (prebuilt). + // #[allow]: as_ref() acts as impl AsRef, but with conditional compilation + + let json = godot_bindings::load_gdextension_json(watch); + #[allow(clippy::useless_asref)] + let json_str: &str = json.as_ref(); - let model: ExtensionApi = DeJson::deserialize_json(&json).expect("failed to deserialize JSON"); + let model: ExtensionApi = + DeJson::deserialize_json(json_str).expect("failed to deserialize JSON"); watch.record("deserialize_json"); (model, build_config) diff --git a/godot-codegen/src/godot_exe.rs b/godot-codegen/src/godot_exe.rs deleted file mode 100644 index e9f3beeba..000000000 --- a/godot-codegen/src/godot_exe.rs +++ /dev/null @@ -1,131 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -use crate::godot_version::parse_godot_version; -use crate::StopWatch; -use std::path::{Path, PathBuf}; -use std::process::Command; - -/// Commands related to Godot executable - -const GODOT_VERSION_PATH: &str = - concat!(env!("CARGO_MANIFEST_DIR"), "/input/gen/godot_version.txt"); - -const EXTENSION_API_PATH: &str = - concat!(env!("CARGO_MANIFEST_DIR"), "/input/gen/extension_api.json"); - -pub fn load_extension_api_json(watch: &mut StopWatch) -> String { - let json_path = Path::new(EXTENSION_API_PATH); - rerun_on_changed(json_path); - - 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); - if !json_path.exists() || has_version_changed(&version) { - dump_extension_api(&godot_bin, json_path); - update_version_file(&version); - - watch.record("dump_extension_api"); - } - - let result = std::fs::read_to_string(json_path) - .unwrap_or_else(|_| panic!("failed to open file {}", json_path.display())); - watch.record("read_json_file"); - result -} - -fn has_version_changed(current_version: &str) -> bool { - let version_path = Path::new(GODOT_VERSION_PATH); - - match std::fs::read_to_string(version_path) { - Ok(last_version) => current_version != last_version, - Err(_) => true, - } -} - -fn update_version_file(version: &str) { - let version_path = Path::new(GODOT_VERSION_PATH); - rerun_on_changed(version_path); - - std::fs::write(version_path, version) - .unwrap_or_else(|_| panic!("write Godot version to file {}", version_path.display())); -} - -fn read_godot_version(godot_bin: &Path) -> String { - let output = Command::new(godot_bin) - .arg("--version") - .output() - .unwrap_or_else(|_| { - panic!( - "failed to invoke Godot executable '{}'", - godot_bin.display() - ) - }); - - let output = String::from_utf8(output.stdout).expect("convert Godot version to UTF-8"); - println!("Godot version: {output}"); - - match parse_godot_version(&output) { - Ok(parsed) => { - assert_eq!( - parsed.major, - 4, - "Only Godot versions >= 4.0 are supported; found version {}.", - output.trim() - ); - - parsed.full_string - } - Err(e) => { - // Don't treat this as fatal error - panic!("failed to parse Godot version '{output}': {e}") - } - } -} - -fn dump_extension_api(godot_bin: &Path, out_file: &Path) { - let cwd = out_file.parent().unwrap(); - std::fs::create_dir_all(cwd).unwrap_or_else(|_| panic!("create directory '{}'", cwd.display())); - println!("Dump extension API to dir '{}'...", cwd.display()); - - Command::new(godot_bin) - .current_dir(cwd) - .arg("--headless") - .arg("--dump-extension-api") - .arg(cwd) - .output() - .unwrap_or_else(|_| { - panic!( - "failed to invoke Godot executable '{}'", - godot_bin.display() - ) - }); - - println!("Generated {}/extension_api.json.", cwd.display()); -} - -fn locate_godot_binary() -> PathBuf { - if let Ok(string) = std::env::var("GODOT4_BIN") { - println!("Found GODOT4_BIN with path to executable: '{string}'"); - PathBuf::from(string) - } else if let Ok(path) = which::which("godot4") { - println!("Found 'godot4' executable in PATH: {}", path.display()); - path - } else { - panic!( - "Bindings generation requires 'godot4' executable or a GODOT4_BIN \ - environment variable (with the path to the executable)." - ) - } -} - -fn rerun_on_changed(path: &Path) { - println!("cargo:rerun-if-changed={}", path.display()); - println!("cargo:rerun-if-env-changed=GODOT4_BIN"); -} diff --git a/godot-codegen/src/lib.rs b/godot-codegen/src/lib.rs index 772455643..37b2d07dd 100644 --- a/godot-codegen/src/lib.rs +++ b/godot-codegen/src/lib.rs @@ -8,12 +8,9 @@ mod api_parser; mod central_generator; mod class_generator; mod context; -mod godot_exe; -mod godot_version; mod special_cases; mod util; mod utilities_generator; -mod watch; #[cfg(test)] mod tests; @@ -25,22 +22,19 @@ use central_generator::{ }; use class_generator::{generate_builtin_class_files, generate_class_files}; use context::Context; -use util::ident; +use util::{ident, to_pascal_case, to_snake_case}; use utilities_generator::generate_utilities_file; -use watch::StopWatch; -use crate::util::{to_pascal_case, to_snake_case}; use proc_macro2::{Ident, TokenStream}; use quote::{quote, ToTokens}; use std::path::{Path, PathBuf}; -pub fn generate_sys_files(sys_gen_path: &Path) { +pub fn generate_sys_files(sys_gen_path: &Path, watch: &mut godot_bindings::StopWatch) { let mut out_files = vec![]; - let mut watch = StopWatch::start(); generate_sys_mod_file(sys_gen_path, &mut out_files); - let (api, build_config) = load_extension_api(&mut watch); + let (api, build_config) = load_extension_api(watch); let mut ctx = Context::build_from_api(&api); watch.record("build_context"); @@ -49,12 +43,11 @@ pub fn generate_sys_files(sys_gen_path: &Path) { rustfmt_if_needed(out_files); watch.record("rustfmt"); - watch.write_stats_to(&sys_gen_path.join("codegen-stats.txt")); } pub fn generate_core_files(core_gen_path: &Path) { let mut out_files = vec![]; - let mut watch = StopWatch::start(); + let mut watch = godot_bindings::StopWatch::start(); generate_core_mod_file(core_gen_path, &mut out_files); diff --git a/godot-core/Cargo.toml b/godot-core/Cargo.toml index 2898c12ca..e96c1777b 100644 --- a/godot-core/Cargo.toml +++ b/godot-core/Cargo.toml @@ -13,10 +13,10 @@ trace = [] codegen-fmt = ["godot-ffi/codegen-fmt"] codegen-full = ["godot-codegen/codegen-full"] double-precision = ["godot-codegen/double-precision"] +custom-godot = ["godot-ffi/custom-godot", "godot-codegen/custom-godot"] [dependencies] godot-ffi = { path = "../godot-ffi" } -once_cell = "1.8" # See https://docs.rs/glam/latest/glam/index.html#feature-gates glam = { version = "0.22", features = ["debug-glam-assert"] } diff --git a/godot-core/build.rs b/godot-core/build.rs index 55854aed8..4e208cad1 100644 --- a/godot-core/build.rs +++ b/godot-core/build.rs @@ -7,7 +7,9 @@ use std::path::Path; fn main() { - let gen_path = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/src/gen/")); + // It would be better to generate this in /.generated or /target/godot-gen, however IDEs currently + // struggle with static analysis when symbols are outside the crate directory (April 2023). + let gen_path = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/src/gen")); if gen_path.exists() { std::fs::remove_dir_all(gen_path).unwrap_or_else(|e| panic!("failed to delete dir: {e}")); diff --git a/godot-core/src/builtin/packed_array.rs b/godot-core/src/builtin/packed_array.rs index e07ef9752..6a9e3c26e 100644 --- a/godot-core/src/builtin/packed_array.rs +++ b/godot-core/src/builtin/packed_array.rs @@ -9,7 +9,10 @@ use godot_ffi as sys; use crate::builtin::*; use std::fmt; use sys::types::*; -use sys::{ffi_methods, interface_fn, GodotFfi, TagString, TagType}; +use sys::{ffi_methods, interface_fn, GodotFfi}; + +// FIXME remove dependency on these types +use sys::{__GdextString, __GdextType}; /// Defines and implements a single packed array type. This macro is not hygienic and is meant to /// be used only in the current module. @@ -496,7 +499,7 @@ impl_packed_array!( opaque_type: OpaquePackedStringArray, inner_type: InnerPackedStringArray, argument_type: GodotString, - return_type: TagString, + return_type: __GdextString, from_array: packed_string_array_from_array, operator_index: packed_string_array_operator_index, operator_index_const: packed_string_array_operator_index_const, @@ -514,7 +517,7 @@ impl_packed_array!( opaque_type: OpaquePackedVector2Array, inner_type: InnerPackedVector2Array, argument_type: Vector2, - return_type: TagType, + return_type: __GdextType, from_array: packed_vector2_array_from_array, operator_index: packed_vector2_array_operator_index, operator_index_const: packed_vector2_array_operator_index_const, @@ -532,7 +535,7 @@ impl_packed_array!( opaque_type: OpaquePackedVector3Array, inner_type: InnerPackedVector3Array, argument_type: Vector3, - return_type: TagType, + return_type: __GdextType, from_array: packed_vector3_array_from_array, operator_index: packed_vector3_array_operator_index, operator_index_const: packed_vector3_array_operator_index_const, @@ -550,7 +553,7 @@ impl_packed_array!( opaque_type: OpaquePackedColorArray, inner_type: InnerPackedColorArray, argument_type: Color, - return_type: TagType, + return_type: __GdextType, from_array: packed_color_array_from_array, operator_index: packed_color_array_operator_index, operator_index_const: packed_color_array_operator_index_const, diff --git a/godot-core/src/obj/gd.rs b/godot-core/src/obj/gd.rs index 45dbbb115..4544e183d 100644 --- a/godot-core/src/obj/gd.rs +++ b/godot-core/src/obj/gd.rs @@ -162,7 +162,7 @@ where let callbacks = crate::storage::nop_instance_callbacks(); unsafe { - let token = sys::get_library(); + let token = sys::get_library() as *mut std::ffi::c_void; let binding = interface_fn!(object_get_instance_binding)(self.obj_sys(), token, &callbacks); @@ -171,7 +171,7 @@ where "Class {} -- null instance; does the class have a Godot creator function?", std::any::type_name::() ); - crate::private::as_storage::(binding) + crate::private::as_storage::(binding as sys::GDExtensionClassInstancePtr) } } } diff --git a/godot-core/src/registry.rs b/godot-core/src/registry.rs index 8aa8ab390..73cf88254 100644 --- a/godot-core/src/registry.rs +++ b/godot-core/src/registry.rs @@ -299,15 +299,15 @@ pub mod callbacks { let user_instance = make_user_instance(base); let instance = InstanceStorage::::construct(user_instance); let instance_ptr = instance.into_raw(); - let instance_ptr = instance_ptr as *mut std::ffi::c_void; // TODO GDExtensionClassInstancePtr + let instance_ptr = instance_ptr as sys::GDExtensionClassInstancePtr; let binding_data_callbacks = crate::storage::nop_instance_callbacks(); unsafe { interface_fn!(object_set_instance)(base_ptr, class_name.string_sys(), instance_ptr); interface_fn!(object_set_instance_binding)( base_ptr, - sys::get_library(), - instance_ptr, + sys::get_library() as *mut std::ffi::c_void, + instance_ptr as *mut std::ffi::c_void, &binding_data_callbacks, ); } diff --git a/godot-ffi/Cargo.toml b/godot-ffi/Cargo.toml index 14ddf8433..89b986773 100644 --- a/godot-ffi/Cargo.toml +++ b/godot-ffi/Cargo.toml @@ -7,9 +7,8 @@ license = "MPL-2.0" keywords = ["gamedev", "godot", "engine", "ffi"] categories = ["game-engines", "graphics"] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [features] +custom-godot = ["godot-bindings/custom-godot"] codegen-fmt = ["godot-codegen/codegen-fmt"] #codegen-full = ["godot-codegen/codegen-full"] @@ -17,5 +16,5 @@ codegen-fmt = ["godot-codegen/codegen-fmt"] paste = "1" [build-dependencies] -bindgen = { version = "0.63.0", default-features = false, features = ["runtime"] } +godot-bindings = { path = "../godot-bindings" } godot-codegen = { path = "../godot-codegen" } diff --git a/godot-ffi/build.rs b/godot-ffi/build.rs index de26a0157..d1ab57d5b 100644 --- a/godot-ffi/build.rs +++ b/godot-ffi/build.rs @@ -4,105 +4,26 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::env; +// See also prebuilt's generator/build.rs which is similar in nature. + use std::path::Path; fn main() { - // For custom path on macOS, iOS, Android etc: see gdnative-sys/build.rs - let gen_path = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/src/gen/")); - - if gen_path.exists() { - std::fs::remove_dir_all(gen_path).unwrap_or_else(|e| panic!("failed to delete dir: {e}")); - } - - run_bindgen(&gen_path.join("gdextension_interface.rs")); - godot_codegen::generate_sys_files(gen_path); -} + let mut watch = godot_bindings::StopWatch::start(); -fn run_bindgen(out_file: &Path) { - let header_path = "../godot-codegen/input/gdextension_interface.h"; - println!("cargo:rerun-if-changed={header_path}"); + // It would be better to generate this in /.generated or /target/godot-gen, however IDEs currently + // struggle with static analysis when symbols are outside the crate directory (April 2023). + let gen_path = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/src/gen")); - let builder = bindgen::Builder::default() - .header(header_path) - // Tell cargo to invalidate the built crate whenever any of the - // included header files changed. - .parse_callbacks(Box::new(bindgen::CargoCallbacks)) - .prepend_enum_name(false); + // C header is not strictly required, however it is generated for debugging, and to allow CI + // to check for differences (tweak.patch). + let h_path = gen_path.join("gdextension_interface.h"); + let rs_path = gen_path.join("gdextension_interface.rs"); - std::fs::create_dir_all( - out_file - .parent() - .expect("bindgen output file has parent dir"), - ) - .expect("create bindgen output dir"); + godot_bindings::clear_dir(gen_path, &mut watch); + godot_bindings::write_gdextension_headers(&h_path, &rs_path, &mut watch); - let bindings = configure_platform_specific(builder) - .generate() - .expect("failed generate gdextension_interface.h bindings"); + godot_codegen::generate_sys_files(gen_path, &mut watch); - // Write the bindings to the $OUT_DIR/bindings.rs file. - bindings - .write_to_file(out_file) - .expect("failed write gdextension_interface.h bindings to file"); + watch.write_stats_to(&gen_path.join("ffi-stats.txt")); } - -//#[cfg(target_os = "macos")] -fn configure_platform_specific(builder: bindgen::Builder) -> bindgen::Builder { - let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap(); - if target_vendor == "apple" { - eprintln!("Build selected for macOS."); - let path = env::var("LLVM_PATH").expect("env var 'LLVM_PATH' not set"); - - builder - .clang_arg("-I") - // .clang_arg(format!("{path}/include")) - .clang_arg(apple_include_path().expect("apple include path")) - .clang_arg("-L") - .clang_arg(format!("{path}/lib")) - } else { - eprintln!("Build selected for Linux/Windows."); - builder - } -} - -fn apple_include_path() -> Result { - use std::process::Command; - - let target = std::env::var("TARGET").unwrap(); - let platform = if target.contains("apple-darwin") { - "macosx" - } else if target == "x86_64-apple-ios" || target == "aarch64-apple-ios-sim" { - "iphonesimulator" - } else if target == "aarch64-apple-ios" { - "iphoneos" - } else { - panic!("not building for macOS or iOS"); - }; - - // run `xcrun --sdk iphoneos --show-sdk-path` - let output = Command::new("xcrun") - .args(["--sdk", platform, "--show-sdk-path"]) - .output()? - .stdout; - let prefix = std::str::from_utf8(&output) - .expect("invalid output from `xcrun`") - .trim_end(); - - let suffix = "usr/include"; - let directory = format!("{prefix}/{suffix}"); - - Ok(directory) -} - -// #[cfg(not(target_os = "macos"))] -// fn configure_platform_specific(builder: Builder) -> Builder { -// println!("Build selected for Linux/Windows."); -// builder -// } - -/*fn rerun_if_any_changed(paths: &Vec){ - for path in paths { - println!("cargo:rerun-if-changed={}", path.display()); - } -}*/ diff --git a/godot-ffi/src/lib.rs b/godot-ffi/src/lib.rs index 0b0b8b9fb..7ba644997 100644 --- a/godot-ffi/src/lib.rs +++ b/godot-ffi/src/lib.rs @@ -32,6 +32,12 @@ pub use crate::godot_ffi::{GodotFfi, GodotFuncMarshal}; pub use gen::central::*; pub use gen::gdextension_interface::*; +// The impls only compile if those are different types -- ensures type safety through patch +trait Distinct {} +impl Distinct for GDExtensionVariantPtr {} +impl Distinct for GDExtensionTypePtr {} +impl Distinct for GDExtensionConstTypePtr {} + // ---------------------------------------------------------------------------------------------------------------------------------------------- struct GodotBinding { diff --git a/godot/Cargo.toml b/godot/Cargo.toml index b05d29ee4..766c6f2b5 100644 --- a/godot/Cargo.toml +++ b/godot/Cargo.toml @@ -10,11 +10,12 @@ categories = ["game-engines", "graphics"] [features] default = ["codegen-full"] formatted = ["godot-core/codegen-fmt"] -trace = ["godot-core/trace"] double-precision = ["godot-core/double-precision"] +custom-godot = ["godot-core/custom-godot"] # Private features, they are under no stability guarantee codegen-full = ["godot-core/codegen-full"] +trace = ["godot-core/trace"] [dependencies] godot-core = { path = "../godot-core" }