Compare commits

...

61 Commits

Author SHA1 Message Date
David Snopek
d5cc777a89 Merge pull request #1803 from dsnopek/4.3-cherrypicks-5
Cherry-picks for the godot-cpp 4.3 branch - 5th batch
2025-07-03 08:52:49 -05:00
David Snopek
b1e2ac5cbb Update CI to use windows-2022
(cherry picked from commit 8938e7e4e3)
2025-06-25 09:16:25 -05:00
David Snopek
53caa6adbc Update CI to use ubuntu-22.04 2025-06-24 14:50:16 -05:00
Tom
a9abdb5f0c Cleaned up the MethodBind class
(cherry picked from commit ca5af3c861)
2025-06-24 12:49:39 -05:00
MJacred
9892f63abb Fix URL to gdextension cpp example in the official docs
(cherry picked from commit f25c4df5c4)
2025-06-24 12:49:28 -05:00
Lukas Tenbrink
7b4ca64761 Make ndk version configurable as a command-like argument.
(cherry picked from commit 16e47d7ae5)
2025-06-24 12:49:17 -05:00
Fabio Alessandrelli
6cdac999c2 fix: Add iOS min SDK version link flags
This is required when publishing to the App Store, and consistent with
what we already do for the macOS deployment target.

(cherry picked from commit 4879eb7bd0)
2025-06-24 12:45:39 -05:00
David Snopek
48544896ee Stop referring to GDExtension as experimental in the README
(cherry picked from commit 7660dd28b3)
2025-06-24 12:45:29 -05:00
Ben Lubar
443c075fa3 fix iterators making unintended copies
(cherry picked from commit 7fd0999b3c)
2025-06-24 12:45:14 -05:00
David Snopek
52937a3850 Fix classes without _to_string() always returning "[Wrapped:0]"
(cherry picked from commit f38c056b67)
2025-06-24 12:43:31 -05:00
Thaddeus Crews
888f46bbcc SCons: Add CPPEXTPATH for external includes
(cherry picked from commit 30bfa6f215)
2025-06-24 12:43:21 -05:00
Aaron Franke
de7e5b9145 Add missing Projection constructor with 16 real_t values
(cherry picked from commit aa03c32b3e)
2025-06-24 12:42:28 -05:00
David Snopek
8fffe3b558 Fix stack smashing when Godot methods return char32_t, char16_t or wchar_t
(cherry picked from commit 035add9d4f)
2025-06-24 12:42:10 -05:00
David Snopek
dcd0842fab Merge pull request #1744 from dsnopek/4.3-cherrypicks-4
Cherry-picks for the godot-cpp 4.3 branch - 4th batch
2025-03-18 08:08:42 -05:00
zhmt
dacaa81f2f binding_generator.py: Don't error if directory already exists
It should be ok when folders exist. Exception shouldn't be thrown.

Update binding_generator.py

It should be ok when folds exist. It will fail to build without this patch,  in vs code on windows with compiler ( visual studio community 2022 amd 64) .

Co-Authored-By: Chris Cranford <ccranfor@redhat.com>
(cherry picked from commit d79959c79e)
2025-03-17 10:52:48 -05:00
David Snopek
40e65753f9 Check that precision of extension_api.json matches build options
(cherry picked from commit 0a73df5f53)
2025-03-17 10:52:37 -05:00
Thaddeus Crews
2dee0cc4de Style: Replace _NO_DISCARD_ macro with [[nodiscard]]
(cherry picked from commit 89fd27608f)
2025-03-17 10:50:49 -05:00
dependabot[bot]
3f454d142d Bump actions/upload-artifact from 3 to 4
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit b17e668c15)
2025-03-17 10:48:14 -05:00
David Snopek
f3a1a2fd45 Merge pull request #1695 from dsnopek/4.3-cherrypicks-3
Cherry-picks for the godot-cpp 4.3 branch - 3rd batch
2025-01-28 11:28:57 -06:00
Brecht Kuppens
08e4c89da9 Fix buffer overrun with enums pointers cast to int64_t* when enum is only 32-bit
(cherry picked from commit 7576dc5930)
2025-01-28 10:37:36 -06:00
Brecht Kuppens
86d0dbe695 Update README.md with new pre-commit instructions
(cherry picked from commit bd3cf478c6)
2025-01-28 10:37:36 -06:00
Fabio Alessandrelli
dc87cb6b97 [CI] Re-add generated files consistency check
(cherry picked from commit 0cfe01eff2)
2025-01-28 10:37:36 -06:00
Fabio Alessandrelli
4b9cd6ae9d [Bindings] Build profile now strips methods and skip files
This allows removing dependencies that are not explicitly unused by the
gdextension being built and is implemented using an intermediate json
API file with the methods and classes stripped (i.e. without touching
the file generators).

(cherry picked from commit c4f1abe3f9)
2025-01-28 10:37:36 -06:00
David Snopek
fba9ecd0da Fix print_verbose() macro conflicting with UtilityFunctions::print_verbose()
(cherry picked from commit 47d9cb9bed)
2025-01-28 10:37:36 -06:00
Lukas Tenbrink
59ad323dd1 Add a separate setup-godot-cpp github action.
(cherry picked from commit 9943675dcb)
2025-01-28 10:37:36 -06:00
Aaron Franke
9dc2e15d90 Add print_line for compatibility with engine modules
(cherry picked from commit ac466e4766)
2025-01-28 10:37:36 -06:00
A Thousand Ships
2d96b62774 [Web] Don't cache emsdk
Due to how caches are accessed this cache is almost useless, it only
matters if it is from the same branch or a base branch, and is identical
between branches, so caching it just clutters the build cache

(cherry picked from commit 1e3b24f658)
2025-01-28 10:37:36 -06:00
David Snopek
2cd6221286 Sync Quaternion with the version in Godot
(cherry picked from commit 2004af63a0)
2025-01-28 10:37:36 -06:00
ruffenman
d6a47a28c7 Remove unimplemented static variant functions 'blend' and 'interpolate'. If a user attempts to call either of these it will introduce a linker error and it may not be immediately clear to them why. Also, variant interpolation can already be accessed via 'UtilityFunctions::lerp', making at least the interpolate function unecessary here.
(cherry picked from commit 42a35a1852)
2025-01-28 10:37:36 -06:00
Thaddeus Crews
f2303ba0cc CI: Add runner workflow to call other workflows
(cherry picked from commit c1524f7c86)
2025-01-28 10:37:36 -06:00
Zhehang Ding
c9da56cca2 Use namespace in defs.hpp
A global alias of godot::real_t is defined for backward compatibility

(cherry picked from commit 450c3d65cd)
2025-01-28 10:37:36 -06:00
David Snopek
3449c4e1d3 Don't print an error when decoding a null Ref<T>
(cherry picked from commit 7f02301a91)
2025-01-28 10:37:36 -06:00
Lukas Tenbrink
0899cf6098 Rename Vector4.components -> coords.
The use of .components is deprecated.

(cherry picked from commit 23c9d41d2a)
2025-01-28 10:37:36 -06:00
Lukas Tenbrink
06fbf3ce95 Add lto scons option, defaulting to "none".
(cherry picked from commit 5f7cf05043)
2025-01-28 10:37:36 -06:00
David Snopek
d5bdde9509 Avoid thread_local on MacOS to prevent issues with hot reload
(cherry picked from commit 91833c852e)
2025-01-28 10:37:36 -06:00
David Snopek
9ded2402dc Fix crash in ClassDB::add_virtual_method() if arguments metadata is the wrong size
(cherry picked from commit 1e169bb809)
2025-01-28 10:37:36 -06:00
David Snopek
575f317bf1 [4.3] Run tests against Godot 4.3-stable 2025-01-28 10:37:36 -06:00
David Snopek
56571dc584 Merge pull request #1632 from dsnopek/4.3-cherrypicks-2
Cherry-picks for the godot-cpp 4.3 branch - 2nd batch
2024-10-30 09:38:31 -05:00
Florent Guiocheau
94d74979ce Add p_use_model_front to Basis::looking_at()
(cherry picked from commit 02fd535454)
2024-10-28 16:32:13 -05:00
Thaddeus Crews
bf26191ead SCons: Don't cache librarys
(cherry picked from commit 83c0f15ab9)
2024-10-28 16:32:02 -05:00
Fabio Alessandrelli
af78f2778f [SCons] Enable WASM_BIGINT in web builds
Required since Godot 4.3, which is also the first Godot version with
wide WASM gdnative support (previous versions were Chrome-only, and very
brittle).

(cherry picked from commit 78498da7c3)
2024-10-28 16:31:51 -05:00
Fabio Alessandrelli
30a395bf43 [SCons] Remove use_clang_cl windows flag in favor of generic use_llvm
This is consistent with Godot upstream.

(cherry picked from commit 4717a78144)
2024-10-28 16:31:40 -05:00
Samuel Nicholas
a3d5d6d4d3 VSProj Configure type on build command - to resolve #1582
Visual Studio projects are multi-config projects like Ninja-MultiConfig which means you can't set the configuration at configure time as there are multiple, it always chooses the first one by default when not specified in the build command.

Instead of this:
cmake -DCMAKE_BUILD_TYPE=Release -G"Visual Studio 17 2022" .
cmake --build . --verbose

It should be this
cmake -G"Visual Studio 17 2022" .
cmake --build . --verbose --config Release

Update ci.yml

Because the current build system doesnt use generator expressions for multi config builds, both the CMAKE_BUILD_TYPE and the build --config options need to be set

(cherry picked from commit 07704f8f48)
2024-10-28 16:31:31 -05:00
Samuel Nicholas
e751531290 update .gitignore to add .idea for the Jetbrains CLion IDE
and also the default cmake build directory when building in clion cmake-build-*

(cherry picked from commit 9f5daa2d90)
2024-10-28 16:31:22 -05:00
Samuel Nicholas
2de650b82a Re-Structure cmake solution to be closer to the scons solution.
This is just a single step, re-arranging the code without actually changing its functionality.

new docs/cmake.md
moved the block of comments from the start of the CMakeLists.txt into the cmake.md file and converted content to markdown.

new cmake/godotcpp.cmake
Moved all exposed options into a new function godotcpp_options()
Moved configuration and generation code into godotcpp_generate()

To get all the options into the godotcpp_options() I changed the logic of GODOT_USE_HOT_RELOAD which I believe is a closer match to scons, that if the options is not set, and the build type is not release, then it defaults to ON.

I msvc builds require the default flags to be modified or it will throw errors. I have added the links to articles in the commit, but its about removing the runtime error checks /RTC1 from the CMAKE_CXX_FLAGS_DEBUG variable. This needs to happen before the files are included.
https://stackoverflow.com/questions/74426638/how-to-remove-rtc1-from-specific-target-or-file-in-cmake
https://discourse.cmake.org/t/how-do-i-remove-compile-options-from-target/5965

Renamed GodotCompilerWarnings.cmake to common_compiler_flags.cmake to match scons

Included files explicitly by path, as we dont need to append to the CMAKE_MODULES_PATH which effects the whole build tree.

This prevents consumers of the library from clobbering the names of the cmake include files and breaking the build.

(cherry picked from commit 2402a044eb)
2024-10-28 16:31:12 -05:00
David Snopek
5fe58bcd1e Correctly set instance bindings on reload
(cherry picked from commit cb543c192a)
2024-10-28 16:31:03 -05:00
Samuel Nicholas
daf8ac1c33 Updated all variable names to use GODOT_ prefix
changed cache type for api file and api dir to FILEPATH and PATH respectively.
Minor whitespace.
docstring parity

(cherry picked from commit 390a9a5590)
2024-10-28 16:30:54 -05:00
Samuel Nicholas
b12aeb1b38 Add GODOT_SYMBOL_VISIBILITY cache variable to match scons interface.
(cherry picked from commit 02bdc6665a)
2024-10-28 16:30:45 -05:00
Andreas Pokorny
fa0b4cffc0 Add visibility-hidden
This should make all symbols that are not marked otherwise have hidden
visibility. There still may be exposed symbols if marked with respective
attributes.

(cherry picked from commit d18fa929fb)
2024-10-28 16:30:35 -05:00
ytnuf
e52b4ea4fd Add hot reload support when building with GCC and CMake
(cherry picked from commit 05571971cc)
2024-10-28 16:30:21 -05:00
bruvzg
20459da676 Add support for LLVM/MinGW and ARM64 Windows builds.
(cherry picked from commit f2353da5a3)
2024-10-28 16:30:12 -05:00
David Snopek
1cce4d15ab Merge pull request #1592 from dsnopek/4.3-revert-unexpose-is-instance-valid
[4.3] Revert "Unexpose `UtilityFunctions::is_instance_valid()`"
2024-09-23 10:46:45 -05:00
David Snopek
17ca325aeb Revert "Unexpose UtilityFunctions::is_instance_valid()"
This reverts commit 56cd3fd99e.
2024-09-17 10:03:25 -05:00
David Snopek
e298f430b5 Merge pull request #1569 from dsnopek/4.3-cherrypicks-1
Cherry-picks for the godot-cpp 4.3 branch - 1st batch
2024-09-11 17:17:24 -05:00
David Snopek
1ac33c906e Add a test to ensure that library path is absolute
(cherry picked from commit 92ace04989)
2024-09-03 16:35:55 -05:00
George L. Albany
842a7f621f Fix GCC 14 -Wtemplate-id-cdtor warning
As was fixed with godotengine/godot#91208

(cherry picked from commit 7b31f39bea)
2024-09-03 16:35:31 -05:00
Fabio Alessandrelli
37d255af6c [Web/SCons] Use CCFLAGS for SIDE_MODULE option
Was using CPPFLAGS, but should use the explicit scons CCFLAGS which
makes it clear they are applied to both the C and C++ compiler.

CPPFLAGS was also fine (they are preprocessor flags, also applied to
both C and C++), but we should try to stay consistent with what we do
in Godot.

(cherry picked from commit f36acd8e31)
2024-09-03 16:35:21 -05:00
Aaron Franke
762db4e4d6 Fix missing MAKE_TYPED_ARRAY_INFO for Packed*Arrays
(cherry picked from commit 10c3d1bc5f)
2024-09-03 16:35:11 -05:00
Raul Santos
c823e84ff2 Correct type for char16 and char32 meta 2024-09-03 16:35:02 -05:00
Mikael Hermansson
26cb3292a0 Fix incorrect generation of some C++ operators 2024-09-03 16:34:54 -05:00
Raul Santos
150e45071b Avoid hardcoded type conversion for metadata
The engine uses the names `int` and `float` to refer to the 64-bit types, so in the bindings generator we have a hardcoded conversion for those types.

But this type conversion should not be used for metadata. Even though the underlying type should still be 64-bit for interop, metadata is meant to specify the correct type to expose. So if metadata says `float` it means the type is really meant to be a 32-bit `float` and not `double`. Other hardcoded type conversions (`int` and `Nil`) won't ever be metadata.

This change corrects the `float` type, to use the right type in the generated C++ code. Before we were always using `double` due to this type conversion.

(cherry picked from commit 4829199081)
2024-09-03 16:34:41 -05:00
62 changed files with 1352 additions and 730 deletions

View File

@@ -3,19 +3,22 @@ description: Restore Godot build cache.
inputs:
cache-name:
description: The cache base name (job name by default).
default: "${{github.job}}"
default: ${{ github.job }}
scons-cache:
description: The scons cache path.
default: "${{github.workspace}}/.scons-cache/"
description: The SCons cache path.
default: ${{ github.workspace }}/.scons-cache/
runs:
using: "composite"
using: composite
steps:
- name: Restore .scons_cache directory
uses: actions/cache/restore@v3
- name: Restore SCons cache directory
uses: actions/cache/restore@v4
with:
path: ${{inputs.scons-cache}}
key: ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
path: ${{ inputs.scons-cache }}
key: ${{ inputs.cache-name }}-${{ env.GODOT_BASE_BRANCH }}-${{ github.ref }}-${{ github.sha }}
restore-keys: |
${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}
${{ inputs.cache-name }}-${{ env.GODOT_BASE_BRANCH }}-${{ github.ref }}-${{ github.sha }}
${{ inputs.cache-name }}-${{ env.GODOT_BASE_BRANCH }}-${{ github.ref }}
${{ inputs.cache-name }}-${{ env.GODOT_BASE_BRANCH }}-refs/heads/${{ env.GODOT_BASE_BRANCH }}
${{ inputs.cache-name }}-${{ env.GODOT_BASE_BRANCH }}

View File

@@ -3,15 +3,16 @@ description: Save Godot build cache.
inputs:
cache-name:
description: The cache base name (job name by default).
default: "${{github.job}}"
default: ${{ github.job }}
scons-cache:
description: The SCons cache path.
default: "${{github.workspace}}/.scons-cache/"
default: ${{ github.workspace }}/.scons-cache/
runs:
using: "composite"
using: composite
steps:
- name: Save SCons cache directory
uses: actions/cache/save@v4
with:
path: ${{inputs.scons-cache}}
key: ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
path: ${{ inputs.scons-cache }}
key: ${{ inputs.cache-name }}-${{ env.GODOT_BASE_BRANCH }}-${{ github.ref }}-${{ github.sha }}

View File

@@ -0,0 +1,62 @@
name: Setup godot-cpp
description: Setup build dependencies for godot-cpp.
inputs:
platform:
required: true
description: Target platform.
em-version:
default: 3.1.62
description: Emscripten version.
windows-compiler:
required: true
description: The compiler toolchain to use on Windows ('mingw' or 'msvc').
type: choice
options:
- mingw
- msvc
default: mingw
mingw-version:
default: 12.2.0
description: MinGW version.
ndk-version:
default: r23c
description: Android NDK version.
scons-version:
default: 4.4.0
description: SCons version.
runs:
using: composite
steps:
- name: Setup Python (for SCons)
uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Setup Android dependencies
if: inputs.platform == 'android'
uses: nttld/setup-ndk@v1
with:
ndk-version: ${{ inputs.ndk-version }}
link-to-sdk: true
- name: Setup Web dependencies
if: inputs.platform == 'web'
uses: mymindstorm/setup-emsdk@v14
with:
version: ${{ inputs.em-version }}
no-cache: true
- name: Setup MinGW for Windows/MinGW build
if: inputs.platform == 'windows' && inputs.windows-compiler == 'mingw'
uses: egor-tensin/setup-mingw@v2
with:
version: ${{ inputs.mingw-version }}
- name: Setup SCons
shell: bash
run: |
python -c "import sys; print(sys.version)"
python -m pip install scons==${{ inputs.scons-version }}
scons --version

View File

@@ -1,14 +1,15 @@
name: Continuous integration
on: [push, pull_request]
on:
workflow_call:
env:
# Only used for the cache key. Increment version to force clean build.
GODOT_BASE_BRANCH: master
# Used to select the version of Godot to run the tests with.
GODOT_TEST_VERSION: master
GODOT_TEST_VERSION: 4.3-stable
concurrency:
group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}
group: ci-${{ github.actor }}-${{ github.head_ref || github.run_number }}-${{ github.ref }}
cancel-in-progress: true
jobs:
@@ -20,7 +21,7 @@ jobs:
matrix:
include:
- name: 🐧 Linux (GCC)
os: ubuntu-20.04
os: ubuntu-22.04
platform: linux
artifact-name: godot-cpp-linux-glibc2.27-x86_64-release
artifact-path: bin/libgodot-cpp.linux.template_release.x86_64.a
@@ -28,7 +29,7 @@ jobs:
cache-name: linux-x86_64
- name: 🐧 Linux (GCC, Double Precision)
os: ubuntu-20.04
os: ubuntu-22.04
platform: linux
artifact-name: godot-cpp-linux-glibc2.27-x86_64-double-release
artifact-path: bin/libgodot-cpp.linux.template_release.double.x86_64.a
@@ -37,7 +38,7 @@ jobs:
cache-name: linux-x86_64-f64
- name: 🏁 Windows (x86_64, MSVC)
os: windows-2019
os: windows-2022
platform: windows
artifact-name: godot-cpp-windows-msvc2019-x86_64-release
artifact-path: bin/libgodot-cpp.windows.template_release.x86_64.lib
@@ -45,7 +46,7 @@ jobs:
cache-name: windows-x86_64-msvc
- name: 🏁 Windows (x86_64, MinGW)
os: windows-2019
os: windows-2022
platform: windows
artifact-name: godot-cpp-linux-mingw-x86_64-release
artifact-path: bin/libgodot-cpp.windows.template_release.x86_64.a
@@ -63,7 +64,7 @@ jobs:
cache-name: macos-universal
- name: 🤖 Android (arm64)
os: ubuntu-20.04
os: ubuntu-22.04
platform: android
artifact-name: godot-cpp-android-arm64-release
artifact-path: bin/libgodot-cpp.android.template_release.arm64.a
@@ -81,7 +82,7 @@ jobs:
cache-name: ios-arm64
- name: 🌐 Web (wasm32)
os: ubuntu-20.04
os: ubuntu-22.04
platform: web
artifact-name: godot-cpp-web-wasm32-release
artifact-path: bin/libgodot-cpp.web.template_release.wasm32.a
@@ -91,7 +92,6 @@ jobs:
env:
SCONS_CACHE: ${{ github.workspace }}/.scons-cache/
EM_VERSION: 3.1.39
EM_CACHE_FOLDER: "emsdk-cache"
steps:
- name: Checkout
@@ -105,34 +105,11 @@ jobs:
cache-name: ${{ matrix.cache-name }}
continue-on-error: true
- name: Set up Python (for SCons)
uses: actions/setup-python@v5
- name: Setup godot-cpp
uses: ./.github/actions/setup-godot-cpp
with:
python-version: '3.x'
- name: Android dependencies
if: ${{ matrix.platform == 'android' }}
uses: nttld/setup-ndk@v1
with:
ndk-version: r23c
link-to-sdk: true
- name: Web dependencies
if: ${{ matrix.platform == 'web' }}
uses: mymindstorm/setup-emsdk@v14
with:
version: ${{env.EM_VERSION}}
actions-cache-folder: ${{env.EM_CACHE_FOLDER}}
- name: Setup MinGW for Windows/MinGW build
if: ${{ matrix.platform == 'windows' && matrix.flags == 'use_mingw=yes' }}
uses: egor-tensin/setup-mingw@v2
with:
version: 12.2.0
- name: Install scons
run: |
python -m pip install scons==4.0.0
platform: ${{ matrix.platform }}
windows-compiler: ${{ contains(matrix.flags, 'use_mingw=yes') && 'mingw' || 'msvc' }}
- name: Generate godot-cpp sources only
run: |
@@ -161,7 +138,7 @@ jobs:
- name: Download latest Godot artifacts
uses: dsnopek/action-download-artifact@1322f74e2dac9feed2ee76a32d9ae1ca3b4cf4e9
if: ${{ matrix.run-tests && env.GODOT_TEST_VERSION == 'master' }}
if: matrix.run-tests && env.GODOT_TEST_VERSION == 'master'
with:
repo: godotengine/godot
branch: master
@@ -175,13 +152,13 @@ jobs:
path: godot-artifacts
- name: Prepare Godot artifacts for testing
if: ${{ matrix.run-tests && env.GODOT_TEST_VERSION == 'master' }}
if: matrix.run-tests && env.GODOT_TEST_VERSION == 'master'
run: |
chmod +x ./godot-artifacts/godot.linuxbsd.editor.x86_64.mono
echo "GODOT=$(pwd)/godot-artifacts/godot.linuxbsd.editor.x86_64.mono" >> $GITHUB_ENV
- name: Download requested Godot version for testing
if: ${{ matrix.run-tests && env.GODOT_TEST_VERSION != 'master' }}
if: matrix.run-tests && env.GODOT_TEST_VERSION != 'master'
run: |
wget "https://github.com/godotengine/godot-builds/releases/download/${GODOT_TEST_VERSION}/Godot_v${GODOT_TEST_VERSION}_linux.x86_64.zip" -O Godot.zip
unzip -a Godot.zip
@@ -189,7 +166,7 @@ jobs:
echo "GODOT=$(pwd)/Godot_v${GODOT_TEST_VERSION}_linux.x86_64" >> $GITHUB_ENV
- name: Run tests
if: ${{ matrix.run-tests }}
if: matrix.run-tests
run: |
$GODOT --headless --version
cd test
@@ -198,7 +175,7 @@ jobs:
./run-tests.sh
- name: Upload artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact-name }}
path: ${{ matrix.artifact-path }}
@@ -206,7 +183,7 @@ jobs:
linux-cmake:
name: 🐧 Build (Linux, GCC, CMake)
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -230,7 +207,7 @@ jobs:
linux-cmake-ninja:
name: 🐧 Build (Linux, GCC, CMake Ninja)
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -254,7 +231,7 @@ jobs:
windows-msvc-cmake:
name: 🏁 Build (Windows, MSVC, CMake)
runs-on: windows-2019
runs-on: windows-2022
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -263,10 +240,10 @@ jobs:
- name: Build godot-cpp
run: |
cmake -DCMAKE_BUILD_TYPE=Release -G"Visual Studio 16 2019" .
cmake --build . --verbose
cmake -DCMAKE_BUILD_TYPE=Release -G"Visual Studio 17 2022" .
cmake --build . --verbose --config Release
- name: Build test GDExtension library
run: |
cd test && cmake -DCMAKE_BUILD_TYPE=Release -DGODOT_HEADERS_PATH="../godot-headers" -DCPP_BINDINGS_PATH=".." -G"Visual Studio 16 2019" .
cmake --build . --verbose
cd test && cmake -DCMAKE_BUILD_TYPE=Release -DGODOT_HEADERS_PATH="../godot-headers" -DCPP_BINDINGS_PATH=".." -G"Visual Studio 17 2022" .
cmake --build . --verbose --config Release

21
.github/workflows/runner.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: 🔗 GHA
on: [push, pull_request, merge_group]
concurrency:
group: ci-${{ github.actor }}-${{ github.head_ref || github.run_number }}-${{ github.ref }}-runner
cancel-in-progress: true
jobs:
# First stage: Only static checks, fast and prevent expensive builds from running.
static-checks:
if: '!vars.DISABLE_GODOT_CI'
name: 📊 Static Checks
uses: ./.github/workflows/static_checks.yml
# Second stage: Run all the builds and some of the tests.
ci:
name: 🛠️ Continuous Integration
needs: static-checks
uses: ./.github/workflows/ci.yml

View File

@@ -1,8 +1,9 @@
name: 📊 Static Checks
on: [push, pull_request]
on:
workflow_call:
concurrency:
group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-static
group: ci-${{ github.actor }}-${{ github.head_ref || github.run_number }}-${{ github.ref }}-static
cancel-in-progress: true
jobs:
@@ -31,3 +32,7 @@ jobs:
uses: pre-commit/action@v3.0.1
with:
extra_args: --verbose --hook-stage manual --files ${{ env.CHANGED_FILES }}
- name: Check generated files consistency
run:
python misc/scripts/check_get_file_list.py

4
.gitignore vendored
View File

@@ -195,3 +195,7 @@ compile_commands.json
# Python development
.venv
venv
# Clion Configuration
.idea/
cmake-build-*

View File

@@ -1,228 +1,24 @@
# cmake arguments
# CMAKE_BUILD_TYPE: Compilation target (Debug or Release defaults to Debug)
#
# godot-cpp cmake arguments
# GODOT_GDEXTENSION_DIR: Path to the directory containing GDExtension interface header and API JSON file
# GODOT_CPP_SYSTEM_HEADERS Mark the header files as SYSTEM. This may be useful to suppress warnings in projects including this one.
# GODOT_CPP_WARNING_AS_ERROR Treat any warnings as errors
# GODOT_ENABLE_HOT_RELOAD Build with hot reload support. Defaults to YES for Debug-builds and NO for Release-builds.
# GODOT_CUSTOM_API_FILE: Path to a custom GDExtension API JSON file (takes precedence over `gdextension_dir`)
# FLOAT_PRECISION: Floating-point precision level ("single", "double")
#
# Android cmake arguments
# CMAKE_TOOLCHAIN_FILE: The path to the android cmake toolchain ($ANDROID_NDK/build/cmake/android.toolchain.cmake)
# ANDROID_NDK: The path to the android ndk root folder
# ANDROID_TOOLCHAIN_NAME: The android toolchain (arm-linux-androideabi-4.9 or aarch64-linux-android-4.9 or x86-4.9 or x86_64-4.9)
# ANDROID_PLATFORM: The android platform version (android-23)
# More info here: https://godot.readthedocs.io/en/latest/development/compiling/compiling_for_android.html
#
# Examples
#
# Builds a debug version:
# cmake .
# cmake --build .
#
# Builds a release version with clang
# CC=/usr/bin/clang CXX=/usr/bin/clang++ cmake -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" .
# cmake --build .
#
# Builds an android armeabi-v7a debug version:
# cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_NDK=$ANDROID_NDK \
# -DANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-4.9 -DANDROID_PLATFORM=android-23 -DCMAKE_BUILD_TYPE=Debug .
# cmake --build .
#
# Protip
# Generate the buildfiles in a sub directory to not clutter the root directory with build files:
# mkdir build && cd build && cmake -G "Unix Makefiles" .. && cmake --build .
#
# Todo
# Test build for Windows, Mac and mingw.
cmake_minimum_required(VERSION 3.13)
project(godot-cpp LANGUAGES CXX)
option(GENERATE_TEMPLATE_GET_NODE "Generate a template version of the Node class's get_node." ON)
option(GODOT_CPP_SYSTEM_HEADERS "Expose headers as SYSTEM." ON)
option(GODOT_CPP_WARNING_AS_ERROR "Treat warnings as errors" OFF)
# Add path to modules
list( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" )
# Set some helper variables for readability
set( compiler_is_clang "$<OR:$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:Clang>>" )
set( compiler_is_gnu "$<CXX_COMPILER_ID:GNU>" )
set( compiler_is_msvc "$<CXX_COMPILER_ID:MSVC>" )
# Default build type is Debug in the SConstruct
if("${CMAKE_BUILD_TYPE}" STREQUAL "")
set(CMAKE_BUILD_TYPE Debug)
endif()
# Hot reload is enabled by default in Debug-builds
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
option(GODOT_ENABLE_HOT_RELOAD "Build with hot reload support" ON)
else()
option(GODOT_ENABLE_HOT_RELOAD "Build with hot reload support" OFF)
endif()
if(NOT DEFINED BITS)
set(BITS 32)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(BITS 64)
endif(CMAKE_SIZEOF_VOID_P EQUAL 8)
endif()
# Input from user for GDExtension interface header and the API JSON file
set(GODOT_GDEXTENSION_DIR "gdextension" CACHE STRING "")
set(GODOT_CUSTOM_API_FILE "" CACHE STRING "")
set(GODOT_GDEXTENSION_API_FILE "${GODOT_GDEXTENSION_DIR}/extension_api.json")
if (NOT "${GODOT_CUSTOM_API_FILE}" STREQUAL "") # User-defined override.
set(GODOT_GDEXTENSION_API_FILE "${GODOT_CUSTOM_API_FILE}")
endif()
set(FLOAT_PRECISION "single" CACHE STRING "")
if ("${FLOAT_PRECISION}" STREQUAL "double")
add_definitions(-DREAL_T_IS_DOUBLE)
endif()
set(GODOT_COMPILE_FLAGS )
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# using Visual Studio C++
set(GODOT_COMPILE_FLAGS "/utf-8") # /GF /MP
if(CMAKE_BUILD_TYPE MATCHES Debug)
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /MDd") # /Od /RTC1 /Zi
else()
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /MD /O2") # /Oy /GL /Gy
STRING(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
endif(CMAKE_BUILD_TYPE MATCHES Debug)
add_definitions(-DNOMINMAX)
else() # GCC/Clang
if(CMAKE_BUILD_TYPE MATCHES Debug)
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-omit-frame-pointer -O0 -g")
else()
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -O3")
endif(CMAKE_BUILD_TYPE MATCHES Debug)
endif()
# Disable exception handling. Godot doesn't use exceptions anywhere, and this
# saves around 20% of binary size and very significant build time (GH-80513).
option(GODOT_DISABLE_EXCEPTIONS ON "Force disabling exception handling code")
if (GODOT_DISABLE_EXCEPTIONS)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -D_HAS_EXCEPTIONS=0")
else()
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-exceptions")
endif()
else()
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /EHsc")
endif()
endif()
if (GODOT_ENABLE_HOT_RELOAD)
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -D HOT_RELOAD_ENABLED")
endif()
# Generate source from the bindings file
find_package(Python3 3.4 REQUIRED) # pathlib should be present
if(GENERATE_TEMPLATE_GET_NODE)
set(GENERATE_BINDING_PARAMETERS "True")
else()
set(GENERATE_BINDING_PARAMETERS "False")
endif()
execute_process(COMMAND "${Python3_EXECUTABLE}" "-c" "import binding_generator; binding_generator.print_file_list(\"${GODOT_GDEXTENSION_API_FILE}\", \"${CMAKE_CURRENT_BINARY_DIR}\", headers=True, sources=True)"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE GENERATED_FILES_LIST
OUTPUT_STRIP_TRAILING_WHITESPACE
)
add_custom_command(OUTPUT ${GENERATED_FILES_LIST}
COMMAND "${Python3_EXECUTABLE}" "-c" "import binding_generator; binding_generator.generate_bindings(\"${GODOT_GDEXTENSION_API_FILE}\", \"${GENERATE_BINDING_PARAMETERS}\", \"${BITS}\", \"${FLOAT_PRECISION}\", \"${CMAKE_CURRENT_BINARY_DIR}\")"
VERBATIM
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
MAIN_DEPENDENCY ${GODOT_GDEXTENSION_API_FILE}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/binding_generator.py
COMMENT "Generating bindings"
)
# Get Sources
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS src/*.c**)
file(GLOB_RECURSE HEADERS CONFIGURE_DEPENDS include/*.h**)
# Define our godot-cpp library
add_library(${PROJECT_NAME} STATIC
${SOURCES}
${HEADERS}
${GENERATED_FILES_LIST}
)
add_library(godot::cpp ALIAS ${PROJECT_NAME})
include(GodotCompilerWarnings)
target_compile_features(${PROJECT_NAME}
PRIVATE
cxx_std_17
)
target_compile_definitions(${PROJECT_NAME} PUBLIC
$<$<CONFIG:Debug>:
DEBUG_ENABLED
DEBUG_METHODS_ENABLED
>
$<${compiler_is_msvc}:
TYPED_METHOD_BIND
>
)
target_link_options(${PROJECT_NAME} PRIVATE
$<$<NOT:${compiler_is_msvc}>:
-static-libgcc
-static-libstdc++
-Wl,-R,'$$ORIGIN'
>
)
# Optionally mark headers as SYSTEM
set(GODOT_CPP_SYSTEM_HEADERS_ATTRIBUTE "")
if (GODOT_CPP_SYSTEM_HEADERS)
set(GODOT_CPP_SYSTEM_HEADERS_ATTRIBUTE SYSTEM)
# Configure CMake
# https://discourse.cmake.org/t/how-do-i-remove-compile-options-from-target/5965
# https://stackoverflow.com/questions/74426638/how-to-remove-rtc1-from-specific-target-or-file-in-cmake
if(${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
if(NOT CMAKE_BUILD_TYPE MATCHES Debug)
STRING(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
endif ()
endif ()
target_include_directories(${PROJECT_NAME} ${GODOT_CPP_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC
include
${CMAKE_CURRENT_BINARY_DIR}/gen/include
${GODOT_GDEXTENSION_DIR}
)
include( ${PROJECT_SOURCE_DIR}/cmake/godotcpp.cmake )
# Add the compile flags
set_property(TARGET ${PROJECT_NAME} APPEND_STRING PROPERTY COMPILE_FLAGS ${GODOT_COMPILE_FLAGS})
# I know this doesn't look like a typical CMakeLists.txt, but as we are
# attempting mostly feature parity with SCons, and easy maintenance, the closer
# the two build systems look the easier they will be to keep in lockstep.
# Create the correct name (godot.os.build_type.system_bits)
string(TOLOWER "${CMAKE_SYSTEM_NAME}" SYSTEM_NAME)
string(TOLOWER "${CMAKE_BUILD_TYPE}" BUILD_TYPE)
# The typical target definitions are in ${PROJECT_SOURCE_DIR}/cmake/godotcpp.cmake
if(ANDROID)
# Added the android abi after system name
set(SYSTEM_NAME ${SYSTEM_NAME}.${ANDROID_ABI})
godotcpp_options()
# Android does not have the bits at the end if you look at the main godot repo build
set(OUTPUT_NAME "godot-cpp.${SYSTEM_NAME}.${BUILD_TYPE}")
else()
set(OUTPUT_NAME "godot-cpp.${SYSTEM_NAME}.${BUILD_TYPE}.${BITS}")
endif()
set_target_properties(${PROJECT_NAME}
PROPERTIES
CXX_EXTENSIONS OFF
POSITION_INDEPENDENT_CODE ON
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin"
OUTPUT_NAME "${OUTPUT_NAME}"
)
godotcpp_generate()

View File

@@ -7,6 +7,8 @@
> from Godot's `master` branch.
>
> For users of stable branches, switch to the branch matching your target Godot version:
> - [`4.4`](https://github.com/godotengine/godot-cpp/tree/4.4)
> - [`4.3`](https://github.com/godotengine/godot-cpp/tree/4.3)
> - [`4.2`](https://github.com/godotengine/godot-cpp/tree/4.2)
> - [`4.1`](https://github.com/godotengine/godot-cpp/tree/4.1)
> - [`4.0`](https://github.com/godotengine/godot-cpp/tree/4.0)
@@ -49,20 +51,13 @@ Godot version.**
## Compatibility
> [!WARNING]
>
> The GDExtension API is brand new in Godot 4.0, and is still
considered in **beta** stage, despite Godot 4.0 itself being released.
>
> This applies to both the GDExtension interface header, the API JSON, and this
first-party `godot-cpp` extension.
>
> Some compatibility breakage is to be expected as GDExtension and `godot-cpp`
> get more used, documented, and critical issues get resolved. See the
> [Godot issue tracker](https://github.com/godotengine/godot/issues?q=is%3Aissue+is%3Aopen+label%3Atopic%3Agdextension)
> and the [godot-cpp issue tracker](https://github.com/godotengine/godot-cpp/issues)
> for a list of known issues, and be sure to provide feedback on issues and PRs
> which affect your use of this extension.
GDExtensions targeting an earlier version of Godot should work in later minor versions,
but not vice-versa. For example, a GDExtension targeting Godot 4.2 should work just fine
in Godot 4.3, but one targeting Godot 4.3 won't work in Godot 4.2.
There is one exception to this: extensions targeting Godot 4.0 will _not_ work with
Godot 4.1 and later.
See [Updating your GDExtension for 4.1](https://docs.godotengine.org/en/latest/tutorials/migrating/upgrading_to_godot_4.1.html#updating-your-gdextension-for-godot-4-1).
## Contributing
@@ -71,8 +66,7 @@ wish to help out, ensure you have an account on GitHub and create a "fork" of
this repository. See [Pull request workflow](https://docs.godotengine.org/en/stable/community/contributing/pr_workflow.html)
for instructions.
Please install clang-format and copy the files in `misc/hooks` into `.git/hooks`
so formatting is done before your changes are submitted.
Please install clang-format and the [pre-commit](https://pre-commit.com/) Python framework so formatting is done before your changes are submitted. See the [code style guidelines](https://docs.godotengine.org/en/latest/contributing/development/code_style_guidelines.html#pre-commit-hook) for instructions.
## Getting started
@@ -146,4 +140,4 @@ See the [godot-cpp-template](https://github.com/godotengine/godot-cpp-template)
generic reusable template.
Or checkout the code for the [Summator example](https://github.com/paddy-exe/GDExtensionSummator)
as shown in the [official documentation](https://docs.godotengine.org/en/latest/tutorials/scripting/gdextension/gdextension_cpp_example.html).
as shown in the [official documentation](https://docs.godotengine.org/en/latest/tutorials/scripting/cpp/gdextension_cpp_example.html).

View File

@@ -197,13 +197,16 @@ def generate_virtuals(target):
f.write(txt)
def get_file_list(api_filepath, output_dir, headers=False, sources=False, profile_filepath=""):
def get_file_list(api_filepath, output_dir, headers=False, sources=False):
api = {}
files = []
with open(api_filepath, encoding="utf-8") as api_file:
api = json.load(api_file)
build_profile = parse_build_profile(profile_filepath, api)
return _get_file_list(api, output_dir, headers, sources)
def _get_file_list(api, output_dir, headers=False, sources=False):
files = []
core_gen_folder = Path(output_dir) / "gen" / "include" / "godot_cpp" / "core"
include_gen_folder = Path(output_dir) / "gen" / "include" / "godot_cpp"
@@ -235,7 +238,7 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False, profil
source_filename = source_gen_folder / "classes" / (camel_to_snake(engine_class["name"]) + ".cpp")
if headers:
files.append(str(header_filename.as_posix()))
if sources and is_class_included(engine_class["name"], build_profile):
if sources:
files.append(str(source_filename.as_posix()))
for native_struct in api["native_structures"]:
@@ -267,131 +270,27 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False, profil
return files
def print_file_list(api_filepath, output_dir, headers=False, sources=False, profile_filepath=""):
print(*get_file_list(api_filepath, output_dir, headers, sources, profile_filepath), sep=";", end=None)
def parse_build_profile(profile_filepath, api):
if profile_filepath == "":
return {}
print("Using feature build profile: " + profile_filepath)
with open(profile_filepath, encoding="utf-8") as profile_file:
profile = json.load(profile_file)
api_dict = {}
parents = {}
children = {}
for engine_class in api["classes"]:
api_dict[engine_class["name"]] = engine_class
parent = engine_class.get("inherits", "")
child = engine_class["name"]
parents[child] = parent
if parent == "":
continue
children[parent] = children.get(parent, [])
children[parent].append(child)
# Parse methods dependencies
deps = {}
reverse_deps = {}
for name, engine_class in api_dict.items():
ref_cls = set()
for method in engine_class.get("methods", []):
rtype = method.get("return_value", {}).get("type", "")
args = [a["type"] for a in method.get("arguments", [])]
if rtype in api_dict:
ref_cls.add(rtype)
elif is_enum(rtype) and get_enum_class(rtype) in api_dict:
ref_cls.add(get_enum_class(rtype))
for arg in args:
if arg in api_dict:
ref_cls.add(arg)
elif is_enum(arg) and get_enum_class(arg) in api_dict:
ref_cls.add(get_enum_class(arg))
deps[engine_class["name"]] = set(filter(lambda x: x != name, ref_cls))
for acls in ref_cls:
if acls == name:
continue
reverse_deps[acls] = reverse_deps.get(acls, set())
reverse_deps[acls].add(name)
included = []
front = list(profile.get("enabled_classes", []))
if front:
# These must always be included
front.append("WorkerThreadPool")
front.append("ClassDB")
front.append("ClassDBSingleton")
while front:
cls = front.pop()
if cls in included:
continue
included.append(cls)
parent = parents.get(cls, "")
if parent:
front.append(parent)
for rcls in deps.get(cls, set()):
if rcls in included or rcls in front:
continue
front.append(rcls)
excluded = []
front = list(profile.get("disabled_classes", []))
while front:
cls = front.pop()
if cls in excluded:
continue
excluded.append(cls)
front += children.get(cls, [])
for rcls in reverse_deps.get(cls, set()):
if rcls in excluded or rcls in front:
continue
front.append(rcls)
if included and excluded:
print(
"WARNING: Cannot specify both 'enabled_classes' and 'disabled_classes' in build profile. 'disabled_classes' will be ignored."
)
return {
"enabled_classes": included,
"disabled_classes": excluded,
}
def scons_emit_files(target, source, env):
profile_filepath = env.get("build_profile", "")
if profile_filepath and not Path(profile_filepath).is_absolute():
profile_filepath = str((Path(env.Dir("#").abspath) / profile_filepath).as_posix())
files = [env.File(f) for f in get_file_list(str(source[0]), target[0].abspath, True, True, profile_filepath)]
env.Clean(target, files)
env["godot_cpp_gen_dir"] = target[0].abspath
return files, source
def scons_generate_bindings(target, source, env):
generate_bindings(
str(source[0]),
env["generate_template_get_node"],
"32" if "32" in env["arch"] else "64",
env["precision"],
env["godot_cpp_gen_dir"],
)
return None
def print_file_list(api_filepath, output_dir, headers=False, sources=False):
print(*get_file_list(api_filepath, output_dir, headers, sources), sep=";", end=None)
def generate_bindings(api_filepath, use_template_get_node, bits="64", precision="single", output_dir="."):
api = None
api = {}
with open(api_filepath, encoding="utf-8") as api_file:
api = json.load(api_file)
_generate_bindings(api, api_filepath, use_template_get_node, bits, precision, output_dir)
def _generate_bindings(api, api_filepath, use_template_get_node, bits="64", precision="single", output_dir="."):
if "precision" in api["header"] and precision != api["header"]["precision"]:
raise Exception(
f"Cannot do a precision={precision} build using '{api_filepath}' which was generated by Godot built with precision={api['header']['precision']}"
)
target_dir = Path(output_dir) / "gen"
with open(api_filepath, encoding="utf-8") as api_file:
api = json.load(api_file)
shutil.rmtree(target_dir, ignore_errors=True)
target_dir.mkdir(parents=True)
target_dir.mkdir(parents=True, exist_ok=True)
real_t = "double" if precision == "double" else "float"
print("Built-in type config: " + real_t + "_" + bits)
@@ -852,14 +751,14 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl
if "operators" in builtin_api:
for operator in builtin_api["operators"]:
if operator["name"] not in ["in", "xor"]:
if is_valid_cpp_operator(operator["name"]):
if "right_type" in operator:
result.append(
f'\t{correct_type(operator["return_type"])} operator{operator["name"]}({type_for_parameter(operator["right_type"])}p_other) const;'
f'\t{correct_type(operator["return_type"])} operator{get_operator_cpp_name(operator["name"])}({type_for_parameter(operator["right_type"])}p_other) const;'
)
else:
result.append(
f'\t{correct_type(operator["return_type"])} operator{operator["name"].replace("unary", "")}() const;'
f'\t{correct_type(operator["return_type"])} operator{get_operator_cpp_name(operator["name"])}() const;'
)
# Copy assignment.
@@ -1291,10 +1190,10 @@ def generate_builtin_class_source(builtin_api, size, used_classes, fully_used_cl
if "operators" in builtin_api:
for operator in builtin_api["operators"]:
if operator["name"] not in ["in", "xor"]:
if is_valid_cpp_operator(operator["name"]):
if "right_type" in operator:
result.append(
f'{correct_type(operator["return_type"])} {class_name}::operator{operator["name"]}({type_for_parameter(operator["right_type"])}p_other) const {{'
f'{correct_type(operator["return_type"])} {class_name}::operator{get_operator_cpp_name(operator["name"])}({type_for_parameter(operator["right_type"])}p_other) const {{'
)
(encode, arg_name) = get_encoded_arg("other", operator["right_type"], None)
result += encode
@@ -1304,7 +1203,7 @@ def generate_builtin_class_source(builtin_api, size, used_classes, fully_used_cl
result.append("}")
else:
result.append(
f'{correct_type(operator["return_type"])} {class_name}::operator{operator["name"].replace("unary", "")}() const {{'
f'{correct_type(operator["return_type"])} {class_name}::operator{get_operator_cpp_name(operator["name"])}() const {{'
)
result.append(
f'\treturn internal::_call_builtin_operator_ptr<{get_gdextension_type(correct_type(operator["return_type"]))}>(_method_bindings.operator_{get_operator_id_name(operator["name"])}, (GDExtensionConstTypePtr)&opaque, (GDExtensionConstTypePtr)nullptr);'
@@ -1692,6 +1591,16 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append(f"\t~{class_name}();")
result.append("")
if class_name == "Object":
result.append('\tString _to_string() const { return "<" + get_class() + "#" + itos(get_instance_id()) + ">"; }')
result.append("")
if class_name == "Node":
result.append(
'\tString _to_string() const { return (!get_name().is_empty() ? String(get_name()) + ":" : "") + Object::_to_string(); }'
)
result.append("")
result.append("public:")
# Special cases.
@@ -2166,12 +2075,6 @@ def generate_utility_functions(api, output_dir):
header.append("public:")
for function in api["utility_functions"]:
if function["name"] == "is_instance_valid":
# The `is_instance_valid()` function doesn't work as developers expect, and unless used very
# carefully will cause crashes. Instead, developers should use `ObjectDB::get_instance()`
# with object ids to ensure that an instance is still valid.
continue
vararg = "is_vararg" in function and function["is_vararg"]
function_signature = "\t"
@@ -2206,9 +2109,6 @@ def generate_utility_functions(api, output_dir):
source.append("")
for function in api["utility_functions"]:
if function["name"] == "is_instance_valid":
continue
vararg = "is_vararg" in function and function["is_vararg"]
function_signature = make_signature("UtilityFunctions", function)
@@ -2339,6 +2239,10 @@ def get_encoded_arg(arg_name, type_name, type_meta):
result.append(f"\t{get_gdextension_type(arg_type)} {name}_encoded;")
result.append(f"\tPtrToArg<{correct_type(type_name)}>::encode({name}, &{name}_encoded);")
name = f"&{name}_encoded"
elif is_enum(type_name) and not is_bitfield(type_name):
result.append(f"\tint64_t {name}_encoded;")
result.append(f"\tPtrToArg<int64_t>::encode({name}, &{name}_encoded);")
name = f"&{name}_encoded"
elif is_engine_class(type_name):
# `{name}` is a C++ wrapper, it contains a field which is the object's pointer Godot expects.
# We have to check `nullptr` because when the caller sends `nullptr`, the wrapper itself will be null.
@@ -2655,20 +2559,6 @@ def is_refcounted(type_name):
return type_name in engine_classes and engine_classes[type_name]
def is_class_included(class_name, build_profile):
"""
Check if an engine class should be included.
This removes classes according to a build profile of enabled or disabled classes.
"""
included = build_profile.get("enabled_classes", [])
excluded = build_profile.get("disabled_classes", [])
if included:
return class_name in included
if excluded:
return class_name not in excluded
return True
def is_included(type_name, current_type):
"""
Check if a builtin type should be included.
@@ -2719,8 +2609,8 @@ def correct_type(type_name, meta=None, use_alias=True):
if meta is not None:
if "int" in meta:
return f"{meta}_t"
elif meta in type_conversion:
return type_conversion[type_name]
elif "char" in meta:
return f"{meta}_t"
else:
return meta
if type_name in type_conversion:
@@ -2832,6 +2722,38 @@ def get_operator_id_name(op):
return op_id_map[op]
def get_operator_cpp_name(op):
op_cpp_map = {
"==": "==",
"!=": "!=",
"<": "<",
"<=": "<=",
">": ">",
">=": ">=",
"+": "+",
"-": "-",
"*": "*",
"/": "/",
"unary-": "-",
"unary+": "+",
"%": "%",
"<<": "<<",
">>": ">>",
"&": "&",
"|": "|",
"^": "^",
"~": "~",
"and": "&&",
"or": "||",
"not": "!",
}
return op_cpp_map[op]
def is_valid_cpp_operator(op):
return op not in ["**", "xor", "in"]
def get_default_value_for_type(type_name):
if type_name == "int":
return "0"

183
build_profile.py Normal file
View File

@@ -0,0 +1,183 @@
import json
import sys
def parse_build_profile(profile_filepath, api):
if profile_filepath == "":
return {}
with open(profile_filepath, encoding="utf-8") as profile_file:
profile = json.load(profile_file)
api_dict = {}
parents = {}
children = {}
for engine_class in api["classes"]:
api_dict[engine_class["name"]] = engine_class
parent = engine_class.get("inherits", "")
child = engine_class["name"]
parents[child] = parent
if parent == "":
continue
children[parent] = children.get(parent, [])
children[parent].append(child)
included = []
front = list(profile.get("enabled_classes", []))
if front:
# These must always be included
front.append("WorkerThreadPool")
front.append("ClassDB")
front.append("ClassDBSingleton")
# In src/classes/low_level.cpp
front.append("FileAccess")
front.append("Image")
front.append("XMLParser")
# In include/godot_cpp/templates/thread_work_pool.hpp
front.append("Semaphore")
while front:
cls = front.pop()
if cls in included:
continue
included.append(cls)
parent = parents.get(cls, "")
if parent:
front.append(parent)
excluded = []
front = list(profile.get("disabled_classes", []))
while front:
cls = front.pop()
if cls in excluded:
continue
excluded.append(cls)
front += children.get(cls, [])
if included and excluded:
print(
"WARNING: Cannot specify both 'enabled_classes' and 'disabled_classes' in build profile. 'disabled_classes' will be ignored."
)
return {
"enabled_classes": included,
"disabled_classes": excluded,
}
def generate_trimmed_api(source_api_filepath, profile_filepath):
with open(source_api_filepath, encoding="utf-8") as api_file:
api = json.load(api_file)
if profile_filepath == "":
return api
build_profile = parse_build_profile(profile_filepath, api)
engine_classes = {}
for class_api in api["classes"]:
engine_classes[class_api["name"]] = class_api["is_refcounted"]
for native_struct in api["native_structures"]:
if native_struct["name"] == "ObjectID":
continue
engine_classes[native_struct["name"]] = False
classes = []
for class_api in api["classes"]:
if not is_class_included(class_api["name"], build_profile):
continue
if "methods" in class_api:
methods = []
for method in class_api["methods"]:
if not is_method_included(method, build_profile, engine_classes):
continue
methods.append(method)
class_api["methods"] = methods
classes.append(class_api)
api["classes"] = classes
return api
def is_class_included(class_name, build_profile):
"""
Check if an engine class should be included.
This removes classes according to a build profile of enabled or disabled classes.
"""
included = build_profile.get("enabled_classes", [])
excluded = build_profile.get("disabled_classes", [])
if included:
return class_name in included
if excluded:
return class_name not in excluded
return True
def is_method_included(method, build_profile, engine_classes):
"""
Check if an engine class method should be included.
This removes methods according to a build profile of enabled or disabled classes.
"""
included = build_profile.get("enabled_classes", [])
excluded = build_profile.get("disabled_classes", [])
ref_cls = set()
rtype = get_base_type(method.get("return_value", {}).get("type", ""))
args = [get_base_type(a["type"]) for a in method.get("arguments", [])]
if rtype in engine_classes:
ref_cls.add(rtype)
elif is_enum(rtype) and get_enum_class(rtype) in engine_classes:
ref_cls.add(get_enum_class(rtype))
for arg in args:
if arg in engine_classes:
ref_cls.add(arg)
elif is_enum(arg) and get_enum_class(arg) in engine_classes:
ref_cls.add(get_enum_class(arg))
for acls in ref_cls:
if len(included) > 0 and acls not in included:
return False
elif len(excluded) > 0 and acls in excluded:
return False
return True
def is_enum(type_name):
return type_name.startswith("enum::") or type_name.startswith("bitfield::")
def get_enum_class(enum_name: str):
if "." in enum_name:
if is_bitfield(enum_name):
return enum_name.replace("bitfield::", "").split(".")[0]
else:
return enum_name.replace("enum::", "").split(".")[0]
else:
return "GlobalConstants"
def get_base_type(type_name):
if type_name.startswith("const "):
type_name = type_name[6:]
if type_name.endswith("*"):
type_name = type_name[:-1]
if type_name.startswith("typedarray::"):
type_name = type_name.replace("typedarray::", "")
return type_name
def is_bitfield(type_name):
return type_name.startswith("bitfield::")
if __name__ == "__main__":
if len(sys.argv) < 3 or len(sys.argv) > 4:
print("Usage: %s BUILD_PROFILE INPUT_JSON [OUTPUT_JSON]" % (sys.argv[0]))
sys.exit(1)
profile = sys.argv[1]
infile = sys.argv[2]
outfile = sys.argv[3] if len(sys.argv) > 3 else ""
api = generate_trimmed_api(infile, profile)
if outfile:
with open(outfile, "w", encoding="utf-8") as f:
json.dump(api, f)
else:
json.dump(api, sys.stdout)

View File

@@ -89,6 +89,6 @@ function( set_warning_as_error )
endif()
endfunction()
if ( GODOT_CPP_WARNING_AS_ERROR )
if ( GODOT_WARNING_AS_ERROR )
set_warning_as_error()
endif()

240
cmake/godotcpp.cmake Normal file
View File

@@ -0,0 +1,240 @@
function( godotcpp_options )
#TODO platform
#TODO target
# Input from user for GDExtension interface header and the API JSON file
set(GODOT_GDEXTENSION_DIR "gdextension" CACHE PATH
"Path to a custom directory containing GDExtension interface header and API JSON file ( /path/to/gdextension_dir )" )
set(GODOT_CUSTOM_API_FILE "" CACHE FILEPATH
"Path to a custom GDExtension API JSON file (takes precedence over `gdextension_dir`) ( /path/to/custom_api_file )")
#TODO generate_bindings
option(GODOT_GENERATE_TEMPLATE_GET_NODE
"Generate a template version of the Node class's get_node. (ON|OFF)" ON)
#TODO build_library
set(GODOT_PRECISION "single" CACHE STRING
"Set the floating-point precision level (single|double)")
#TODO arch
#TODO threads
#TODO compiledb
#TODO compiledb_file
#TODO build_profile aka cmake preset
set(GODOT_USE_HOT_RELOAD "" CACHE BOOL
"Enable the extra accounting required to support hot reload. (ON|OFF)")
option(GODOT_DISABLE_EXCEPTIONS "Force disabling exception handling code (ON|OFF)" ON )
set( GODOT_SYMBOL_VISIBILITY "hidden" CACHE STRING
"Symbols visibility on GNU platforms. Use 'auto' to apply the default value. (auto|visible|hidden)")
set_property( CACHE GODOT_SYMBOL_VISIBILITY PROPERTY STRINGS "auto;visible;hidden" )
#TODO optimize
#TODO debug_symbols
#TODO dev_build
# FIXME These options are not present in SCons, and perhaps should be added there.
option(GODOT_SYSTEM_HEADERS "Expose headers as SYSTEM." ON)
option(GODOT_WARNING_AS_ERROR "Treat warnings as errors" OFF)
# Run options commands on the following to populate cache for all platforms.
# This type of thing is typically done conditionally
# But as scons shows all options so shall we.
#TODO ios_options()
#TODO linux_options()
#TODO macos_options()
#TODO web_options()
#TODO windows_options()
endfunction()
function( godotcpp_generate )
# Set some helper variables for readability
set( compiler_is_clang "$<OR:$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:Clang>>" )
set( compiler_is_gnu "$<CXX_COMPILER_ID:GNU>" )
set( compiler_is_msvc "$<CXX_COMPILER_ID:MSVC>" )
# CXX_VISIBILITY_PRESET supported values are: default, hidden, protected, and internal
# which is inline with the gcc -fvisibility=
# https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html
# To match the scons options we need to change the text to match the -fvisibility flag
# it is probably worth another PR which changes both to use the flag options
if( ${GODOT_SYMBOL_VISIBILITY} STREQUAL "auto" OR ${GODOT_SYMBOL_VISIBILITY} STREQUAL "visible" )
set( GODOT_SYMBOL_VISIBILITY "default" )
endif ()
# Default build type is Debug in the SConstruct
if("${CMAKE_BUILD_TYPE}" STREQUAL "")
set(CMAKE_BUILD_TYPE Debug)
endif()
# Hot reload is enabled by default in Debug-builds
if( GODOT_USE_HOT_RELOAD STREQUAL "" AND NOT CMAKE_BUILD_TYPE STREQUAL "Release")
set(GODOT_USE_HOT_RELOAD ON)
endif()
if(NOT DEFINED BITS)
set(BITS 32)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(BITS 64)
endif(CMAKE_SIZEOF_VOID_P EQUAL 8)
endif()
set(GODOT_GDEXTENSION_API_FILE "${GODOT_GDEXTENSION_DIR}/extension_api.json")
if (NOT "${GODOT_CUSTOM_API_FILE}" STREQUAL "") # User-defined override.
set(GODOT_GDEXTENSION_API_FILE "${GODOT_CUSTOM_API_FILE}")
endif()
if ("${GODOT_PRECISION}" STREQUAL "double")
add_definitions(-DREAL_T_IS_DOUBLE)
endif()
set( GODOT_COMPILE_FLAGS )
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# using Visual Studio C++
set(GODOT_COMPILE_FLAGS "/utf-8") # /GF /MP
if(CMAKE_BUILD_TYPE MATCHES Debug)
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /MDd") # /Od /RTC1 /Zi
else()
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /MD /O2") # /Oy /GL /Gy
endif(CMAKE_BUILD_TYPE MATCHES Debug)
add_definitions(-DNOMINMAX)
else() # GCC/Clang
if(CMAKE_BUILD_TYPE MATCHES Debug)
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-omit-frame-pointer -O0 -g")
else()
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -O3")
endif(CMAKE_BUILD_TYPE MATCHES Debug)
endif()
# Disable exception handling. Godot doesn't use exceptions anywhere, and this
# saves around 20% of binary size and very significant build time (GH-80513).
if (GODOT_DISABLE_EXCEPTIONS)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -D_HAS_EXCEPTIONS=0")
else()
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-exceptions")
endif()
else()
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /EHsc")
endif()
endif()
# Generate source from the bindings file
find_package(Python3 3.4 REQUIRED) # pathlib should be present
if(GODOT_GENERATE_TEMPLATE_GET_NODE)
set(GENERATE_BINDING_PARAMETERS "True")
else()
set(GENERATE_BINDING_PARAMETERS "False")
endif()
execute_process(COMMAND "${Python3_EXECUTABLE}" "-c" "import binding_generator; binding_generator.print_file_list(\"${GODOT_GDEXTENSION_API_FILE}\", \"${CMAKE_CURRENT_BINARY_DIR}\", headers=True, sources=True)"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE GENERATED_FILES_LIST
OUTPUT_STRIP_TRAILING_WHITESPACE
)
add_custom_command(OUTPUT ${GENERATED_FILES_LIST}
COMMAND "${Python3_EXECUTABLE}" "-c" "import binding_generator; binding_generator.generate_bindings(\"${GODOT_GDEXTENSION_API_FILE}\", \"${GENERATE_BINDING_PARAMETERS}\", \"${BITS}\", \"${GODOT_PRECISION}\", \"${CMAKE_CURRENT_BINARY_DIR}\")"
VERBATIM
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
MAIN_DEPENDENCY ${GODOT_GDEXTENSION_API_FILE}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/binding_generator.py
COMMENT "Generating bindings"
)
# Get Sources
# As this cmake file was added using 'include(godotcpp)' from the root CMakeLists.txt,
# the ${CMAKE_CURRENT_SOURCE_DIR} is still the root dir.
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS src/*.c**)
file(GLOB_RECURSE HEADERS CONFIGURE_DEPENDS include/*.h**)
# Define our godot-cpp library
add_library(${PROJECT_NAME} STATIC
${SOURCES}
${HEADERS}
${GENERATED_FILES_LIST}
)
add_library(godot::cpp ALIAS ${PROJECT_NAME})
include(${PROJECT_SOURCE_DIR}/cmake/common_compiler_flags.cmake)
target_compile_features(${PROJECT_NAME}
PRIVATE
cxx_std_17
)
if(GODOT_USE_HOT_RELOAD)
target_compile_definitions(${PROJECT_NAME} PUBLIC HOT_RELOAD_ENABLED)
target_compile_options(${PROJECT_NAME} PUBLIC $<${compiler_is_gnu}:-fno-gnu-unique>)
endif()
target_compile_definitions(${PROJECT_NAME} PUBLIC
$<$<CONFIG:Debug>:
DEBUG_ENABLED
DEBUG_METHODS_ENABLED
>
$<${compiler_is_msvc}:
TYPED_METHOD_BIND
>
)
target_link_options(${PROJECT_NAME} PRIVATE
$<$<NOT:${compiler_is_msvc}>:
-static-libgcc
-static-libstdc++
-Wl,-R,'$$ORIGIN'
>
)
# Optionally mark headers as SYSTEM
set(GODOT_SYSTEM_HEADERS_ATTRIBUTE "")
if (GODOT_SYSTEM_HEADERS)
set(GODOT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM)
endif ()
target_include_directories(${PROJECT_NAME} ${GODOT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC
include
${CMAKE_CURRENT_BINARY_DIR}/gen/include
${GODOT_GDEXTENSION_DIR}
)
# Add the compile flags
set_property(TARGET ${PROJECT_NAME} APPEND_STRING PROPERTY COMPILE_FLAGS ${GODOT_COMPILE_FLAGS})
# Create the correct name (godot.os.build_type.system_bits)
string(TOLOWER "${CMAKE_SYSTEM_NAME}" SYSTEM_NAME)
string(TOLOWER "${CMAKE_BUILD_TYPE}" BUILD_TYPE)
if(ANDROID)
# Added the android abi after system name
set(SYSTEM_NAME ${SYSTEM_NAME}.${ANDROID_ABI})
# Android does not have the bits at the end if you look at the main godot repo build
set(OUTPUT_NAME "godot-cpp.${SYSTEM_NAME}.${BUILD_TYPE}")
else()
set(OUTPUT_NAME "godot-cpp.${SYSTEM_NAME}.${BUILD_TYPE}.${BITS}")
endif()
set_target_properties(${PROJECT_NAME}
PROPERTIES
CXX_EXTENSIONS OFF
POSITION_INDEPENDENT_CODE ON
CXX_VISIBILITY_PRESET ${GODOT_SYMBOL_VISIBILITY}
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin"
OUTPUT_NAME "${OUTPUT_NAME}"
)
endfunction()

57
doc/cmake.md Normal file
View File

@@ -0,0 +1,57 @@
## CMake
### cmake arguments
`CMAKE_BUILD_TYPE`: Compilation target (Debug or Release defaults to Debug)
### godot-cpp cmake arguments
- `GODOT_GDEXTENSION_DIR`: Path to the directory containing GDExtension interface header and API JSON file
- `GODOT_SYSTEM_HEADERS`: Mark the header files as SYSTEM. This may be useful to suppress warnings in projects including this one.
- `GODOT_WARNING_AS_ERROR`: Treat any warnings as errors
- `GODOT_USE_HOT_RELOAD`: Build with hot reload support. Defaults to YES for Debug-builds and NO for Release-builds.
- `GODOT_CUSTOM_API_FILE`: Path to a custom GDExtension API JSON file (takes precedence over `gdextension_dir`)
- `GODOT_PRECISION`: Floating-point precision level ("single", "double")
### Android cmake arguments
- `CMAKE_TOOLCHAIN_FILE`: The path to the android cmake toolchain ($ANDROID_NDK/build/cmake/android.toolchain.cmake)
- `ANDROID_NDK`: The path to the android ndk root folder
- `ANDROID_TOOLCHAIN_NAME`: The android toolchain (arm-linux-androideabi-4.9 or aarch64-linux-android-4.9 or x86-4.9 or x86_64-4.9)
- `ANDROID_PLATFORM`: The android platform version (android-23)
- More info [here](https://godot.readthedocs.io/en/latest/development/compiling/compiling_for_android.html)
## Examples
```shell
Builds a debug version:
cmake .
cmake --build .
```
Builds a release version with clang
```shell
CC=/usr/bin/clang CXX=/usr/bin/clang++ cmake -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" .
cmake --build .
```
Builds an android armeabi-v7a debug version:
``` shell
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_NDK=$ANDROID_NDK \
-DANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-4.9 -DANDROID_PLATFORM=android-23 -DCMAKE_BUILD_TYPE=Debug .
cmake --build .
```
## Protip
Generate the buildfiles in a sub directory to not clutter the root directory with build files:
```shell
mkdir build && cd build && cmake -G "Unix Makefiles" .. && cmake --build .
```
Ensure that you avoid exposing godot-cpp symbols - this might lead to hard to debug errors if you ever load multiple
plugins using difference godot-cpp versions. Use visibility hidden whenever possible:
```cmake
set_target_properties(<all-my-plugin-related-targets> PROPERTIES CXX_VISIBILITY_PRESET hidden)
```
## Todo
Test build for Windows, Mac and mingw.

View File

@@ -230,7 +230,9 @@ template <typename T>
struct PtrToArg<Ref<T>> {
_FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) {
GDExtensionRefPtr ref = (GDExtensionRefPtr)p_ptr;
ERR_FAIL_NULL_V(p_ptr, Ref<T>());
if (unlikely(!p_ptr)) {
return Ref<T>();
}
return Ref<T>(reinterpret_cast<T *>(godot::internal::get_object_instance_binding(godot::internal::gdextension_interface_ref_get_object(ref))));
}
@@ -254,7 +256,9 @@ struct PtrToArg<const Ref<T> &> {
_FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) {
GDExtensionRefPtr ref = const_cast<GDExtensionRefPtr>(p_ptr);
ERR_FAIL_NULL_V(p_ptr, Ref<T>());
if (unlikely(!p_ptr)) {
return Ref<T>();
}
return Ref<T>(reinterpret_cast<T *>(godot::internal::get_object_instance_binding(godot::internal::gdextension_interface_ref_get_object(ref))));
}
};

View File

@@ -40,6 +40,14 @@
#include <godot_cpp/godot.hpp>
#if defined(MACOS_ENABLED) && defined(HOT_RELOAD_ENABLED)
#include <mutex>
#define _GODOT_CPP_AVOID_THREAD_LOCAL
#define _GODOT_CPP_THREAD_LOCAL
#else
#define _GODOT_CPP_THREAD_LOCAL thread_local
#endif
namespace godot {
class ClassDB;
@@ -58,8 +66,16 @@ class Wrapped {
template <typename T, std::enable_if_t<std::is_base_of<::godot::Wrapped, T>::value, bool>>
friend _ALWAYS_INLINE_ void _pre_initialize();
thread_local static const StringName *_constructing_extension_class_name;
thread_local static const GDExtensionInstanceBindingCallbacks *_constructing_class_binding_callbacks;
#ifdef _GODOT_CPP_AVOID_THREAD_LOCAL
static std::recursive_mutex _constructing_mutex;
#endif
_GODOT_CPP_THREAD_LOCAL static const StringName *_constructing_extension_class_name;
_GODOT_CPP_THREAD_LOCAL static const GDExtensionInstanceBindingCallbacks *_constructing_class_binding_callbacks;
#ifdef HOT_RELOAD_ENABLED
_GODOT_CPP_THREAD_LOCAL static GDExtensionObjectPtr _constructing_recreate_owner;
#endif
template <typename T>
_ALWAYS_INLINE_ static void _set_construct_info() {
@@ -71,15 +87,6 @@ protected:
virtual bool _is_extension_class() const { return false; }
static const StringName *_get_extension_class_name(); // This is needed to retrieve the class name before the godot object has its _extension and _extension_instance members assigned.
#ifdef HOT_RELOAD_ENABLED
struct RecreateInstance {
GDExtensionClassInstancePtr wrapper;
GDExtensionObjectPtr owner;
RecreateInstance *next;
};
inline static RecreateInstance *recreate_instance = nullptr;
#endif
void _notification(int p_what) {}
bool _set(const StringName &p_name, const Variant &p_property) { return false; }
bool _get(const StringName &p_name, Variant &r_property) const { return false; }
@@ -87,7 +94,7 @@ protected:
bool _property_can_revert(const StringName &p_name) const { return false; }
bool _property_get_revert(const StringName &p_name, Variant &r_property) const { return false; }
void _validate_property(PropertyInfo &p_property) const {}
String _to_string() const { return "[" + String(get_class_static()) + ":" + itos(get_instance_id()) + "]"; }
String _to_string() const { return "<Wrapped#0>"; }
static void notification_bind(GDExtensionClassInstancePtr p_instance, int32_t p_what, GDExtensionBool p_reversed) {}
static GDExtensionBool set_bind(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionConstVariantPtr p_value) { return false; }
@@ -116,16 +123,15 @@ public:
return string_name;
}
uint64_t get_instance_id() const {
return 0;
}
// Must be public but you should not touch this.
GodotObject *_owner = nullptr;
};
template <typename T, std::enable_if_t<std::is_base_of<::godot::Wrapped, T>::value, bool>>
_ALWAYS_INLINE_ void _pre_initialize() {
#ifdef _GODOT_CPP_AVOID_THREAD_LOCAL
Wrapped::_constructing_mutex.lock();
#endif
Wrapped::_set_construct_info<T>();
}

View File

@@ -37,6 +37,7 @@
#include <godot_cpp/core/error_macros.hpp>
#include <godot_cpp/core/method_bind.hpp>
#include <godot_cpp/core/object.hpp>
#include <godot_cpp/core/print_string.hpp>
#include <godot_cpp/classes/class_db_singleton.hpp>
@@ -129,9 +130,11 @@ private:
static GDExtensionClassInstancePtr _recreate_instance_func(void *data, GDExtensionObjectPtr obj) {
if constexpr (!std::is_abstract_v<T>) {
#ifdef HOT_RELOAD_ENABLED
#ifdef _GODOT_CPP_AVOID_THREAD_LOCAL
std::lock_guard<std::recursive_mutex> lk(Wrapped::_constructing_mutex);
#endif
Wrapped::_constructing_recreate_owner = obj;
T *new_instance = (T *)memalloc(sizeof(T));
Wrapped::RecreateInstance recreate_data = { new_instance, obj, Wrapped::recreate_instance };
Wrapped::recreate_instance = &recreate_data;
memnew_placement(new_instance, T);
return new_instance;
#else

View File

@@ -35,6 +35,8 @@
#include <cstdint>
#include <cstring>
namespace godot {
#if !defined(GDE_EXPORT)
#if defined(_WIN32)
#define GDE_EXPORT __declspec(dllexport)
@@ -72,10 +74,6 @@
#endif
#endif
#ifndef _NO_DISCARD_
#define _NO_DISCARD_ [[nodiscard]]
#endif
// Windows badly defines a lot of stuff we'll never use. Undefine it.
#ifdef _WIN32
#undef min // override standard definition
@@ -127,4 +125,10 @@ struct BuildIndexSequence : BuildIndexSequence<N - 1, N - 1, Is...> {};
template <size_t... Is>
struct BuildIndexSequence<0, Is...> : IndexSequence<Is...> {};
} //namespace godot
// To maintain compatibility an alias is defined outside the namespace.
// Consider it deprecated.
using real_t = godot::real_t;
#endif // GODOT_DEFS_HPP

View File

@@ -56,10 +56,10 @@ O *_call_native_mb_ret_obj(const GDExtensionMethodBindPtr mb, void *instance, co
template <typename R, typename... Args>
R _call_native_mb_ret(const GDExtensionMethodBindPtr mb, void *instance, const Args &...args) {
R ret;
typename PtrToArg<R>::EncodeT ret;
std::array<GDExtensionConstTypePtr, sizeof...(Args)> mb_args = { { (GDExtensionConstTypePtr)args... } };
internal::gdextension_interface_object_method_bind_ptrcall(mb, instance, mb_args.data(), &ret);
return ret;
return static_cast<R>(ret);
}
template <typename... Args>
@@ -70,10 +70,10 @@ void _call_native_mb_no_ret(const GDExtensionMethodBindPtr mb, void *instance, c
template <typename R, typename... Args>
R _call_utility_ret(GDExtensionPtrUtilityFunction func, const Args &...args) {
R ret;
typename PtrToArg<R>::EncodeT ret;
std::array<GDExtensionConstTypePtr, sizeof...(Args)> mb_args = { { (GDExtensionConstTypePtr)args... } };
func(&ret, mb_args.data(), mb_args.size());
return ret;
return static_cast<R>(ret);
}
template <typename... Args>

View File

@@ -48,14 +48,14 @@
namespace godot {
class MethodBind {
uint32_t hint_flags = METHOD_FLAGS_DEFAULT;
StringName name;
StringName instance_class;
int argument_count = 0;
uint32_t hint_flags = METHOD_FLAGS_DEFAULT;
bool _static = false;
bool _is_const = false;
bool _has_return = false;
bool _const = false;
bool _returns = false;
bool _vararg = false;
std::vector<StringName> argument_names;
@@ -63,20 +63,20 @@ class MethodBind {
std::vector<Variant> default_arguments;
protected:
void _set_const(bool p_const);
void _set_static(bool p_static);
void _set_returns(bool p_returns);
void _set_vararg(bool p_vararg);
virtual GDExtensionVariantType gen_argument_type(int p_arg) const = 0;
virtual PropertyInfo gen_argument_type_info(int p_arg) const = 0;
void generate_argument_types(int p_count);
void set_const(bool p_const);
void set_return(bool p_return);
void set_static(bool p_static);
void set_vararg(bool p_vararg);
void set_argument_count(int p_count);
void _generate_argument_types(int p_count);
void set_argument_count(int p_count) { argument_count = p_count; }
public:
StringName get_name() const;
void set_name(const StringName &p_name);
_FORCE_INLINE_ int get_default_argument_count() const { return (int)default_arguments.size(); }
_FORCE_INLINE_ const std::vector<Variant> &get_default_arguments() const { return default_arguments; }
_FORCE_INLINE_ int get_default_argument_count() const { return (int)default_arguments.size(); }
_FORCE_INLINE_ Variant has_default_argument(int p_arg) const {
const int num_default_args = (int)(default_arguments.size());
const int idx = p_arg - (argument_count - num_default_args);
@@ -97,19 +97,6 @@ public:
return default_arguments[idx];
}
}
_FORCE_INLINE_ StringName get_instance_class() const { return instance_class; }
_FORCE_INLINE_ void set_instance_class(StringName p_class) { instance_class = p_class; }
_FORCE_INLINE_ int get_argument_count() const { return argument_count; }
_FORCE_INLINE_ bool is_const() const { return _is_const; }
_FORCE_INLINE_ bool is_static() const { return _static; }
_FORCE_INLINE_ bool is_vararg() const { return _vararg; }
_FORCE_INLINE_ bool has_return() const { return _has_return; }
_FORCE_INLINE_ uint32_t get_hint_flags() const { return hint_flags | (is_const() ? GDEXTENSION_METHOD_FLAG_CONST : 0) | (is_vararg() ? GDEXTENSION_METHOD_FLAG_VARARG : 0) | (is_static() ? GDEXTENSION_METHOD_FLAG_STATIC : 0); }
_FORCE_INLINE_ void set_hint_flags(uint32_t p_hint_flags) { hint_flags = p_hint_flags; }
void set_argument_names(const std::vector<StringName> &p_names);
std::vector<StringName> get_argument_names() const;
void set_default_arguments(const std::vector<Variant> &p_default_arguments) { default_arguments = p_default_arguments; }
_FORCE_INLINE_ GDExtensionVariantType get_argument_type(int p_argument) const {
ERR_FAIL_COND_V(p_argument < -1 || p_argument > argument_count, GDEXTENSION_VARIANT_TYPE_NIL);
@@ -117,7 +104,6 @@ public:
}
PropertyInfo get_argument_info(int p_argument) const;
virtual GDExtensionClassMethodArgumentMetadata get_argument_metadata(int p_argument) const = 0;
std::vector<PropertyInfo> get_arguments_info_list() const {
std::vector<PropertyInfo> vec;
@@ -128,6 +114,31 @@ public:
}
return vec;
}
void set_argument_names(const std::vector<StringName> &p_names);
std::vector<StringName> get_argument_names() const;
virtual GDExtensionClassMethodArgumentMetadata get_argument_metadata(int p_argument) const = 0;
_FORCE_INLINE_ void set_hint_flags(uint32_t p_hint_flags) { hint_flags = p_hint_flags; }
_FORCE_INLINE_ uint32_t get_hint_flags() const { return hint_flags | (is_const() ? GDEXTENSION_METHOD_FLAG_CONST : 0) | (is_vararg() ? GDEXTENSION_METHOD_FLAG_VARARG : 0) | (is_static() ? GDEXTENSION_METHOD_FLAG_STATIC : 0); }
_FORCE_INLINE_ StringName get_instance_class() const { return instance_class; }
_FORCE_INLINE_ void set_instance_class(StringName p_class) { instance_class = p_class; }
_FORCE_INLINE_ int get_argument_count() const { return argument_count; }
virtual Variant call(GDExtensionClassInstancePtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionCallError &r_error) const = 0;
virtual void ptrcall(GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_return) const = 0;
StringName get_name() const;
void set_name(const StringName &p_name);
_FORCE_INLINE_ bool is_const() const { return _const; }
_FORCE_INLINE_ bool is_static() const { return _static; }
_FORCE_INLINE_ bool is_vararg() const { return _vararg; }
_FORCE_INLINE_ bool has_return() const { return _returns; }
void set_default_arguments(const std::vector<Variant> &p_default_arguments) { default_arguments = p_default_arguments; }
std::vector<GDExtensionClassMethodArgumentMetadata> get_arguments_metadata_list() const {
std::vector<GDExtensionClassMethodArgumentMetadata> vec;
// First element is return value
@@ -138,9 +149,6 @@ public:
return vec;
}
virtual Variant call(GDExtensionClassInstancePtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionCallError &r_error) const = 0;
virtual void ptrcall(GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_return) const = 0;
static void bind_call(void *p_method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
static void bind_ptrcall(void *p_method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_return);
@@ -182,8 +190,8 @@ public:
const MethodInfo &p_method_info,
bool p_return_nil_is_variant) :
method(p_method) {
set_vararg(true);
set_const(true);
_set_vararg(true);
_set_const(true);
set_argument_count(p_method_info.arguments.size());
if (p_method_info.arguments.size()) {
arguments = p_method_info.arguments;
@@ -196,8 +204,8 @@ public:
set_argument_names(names);
}
generate_argument_types((int)p_method_info.arguments.size());
set_return(should_returns);
_generate_argument_types((int)p_method_info.arguments.size());
_set_returns(should_returns);
}
~MethodBindVarArgBase() {}
@@ -334,7 +342,7 @@ public:
MethodBindT(void (MB_T::*p_method)(P...)) {
method = p_method;
generate_argument_types(sizeof...(P));
_generate_argument_types(sizeof...(P));
set_argument_count(sizeof...(P));
}
};
@@ -410,9 +418,9 @@ public:
MethodBindTC(void (MB_T::*p_method)(P...) const) {
method = p_method;
generate_argument_types(sizeof...(P));
_generate_argument_types(sizeof...(P));
set_argument_count(sizeof...(P));
set_const(true);
_set_const(true);
}
};
@@ -493,9 +501,9 @@ public:
MethodBindTR(R (MB_T::*p_method)(P...)) {
method = p_method;
generate_argument_types(sizeof...(P));
_generate_argument_types(sizeof...(P));
set_argument_count(sizeof...(P));
set_return(true);
_set_returns(true);
}
};
@@ -576,10 +584,10 @@ public:
MethodBindTRC(R (MB_T::*p_method)(P...) const) {
method = p_method;
generate_argument_types(sizeof...(P));
_generate_argument_types(sizeof...(P));
set_argument_count(sizeof...(P));
set_return(true);
set_const(true);
_set_returns(true);
_set_const(true);
}
};
@@ -648,9 +656,9 @@ public:
MethodBindTS(void (*p_function)(P...)) {
function = p_function;
generate_argument_types(sizeof...(P));
_generate_argument_types(sizeof...(P));
set_argument_count(sizeof...(P));
set_static(true);
_set_static(true);
}
};
@@ -717,10 +725,10 @@ public:
MethodBindTRS(R (*p_function)(P...)) {
function = p_function;
generate_argument_types(sizeof...(P));
_generate_argument_types(sizeof...(P));
set_argument_count(sizeof...(P));
set_static(true);
set_return(true);
_set_static(true);
_set_returns(true);
}
};

View File

@@ -122,6 +122,9 @@ MAKE_PTRARGCONV(uint16_t, int64_t);
MAKE_PTRARGCONV(int16_t, int64_t);
MAKE_PTRARGCONV(uint32_t, int64_t);
MAKE_PTRARGCONV(int32_t, int64_t);
MAKE_PTRARGCONV(char16_t, int64_t);
MAKE_PTRARGCONV(char32_t, int64_t);
MAKE_PTRARGCONV(wchar_t, int64_t);
MAKE_PTRARG(int64_t);
MAKE_PTRARG(uint64_t);
// Float types

View File

@@ -0,0 +1,73 @@
/**************************************************************************/
/* print_string.hpp */
/**************************************************************************/
/* 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 GODOT_PRINT_STRING_HPP
#define GODOT_PRINT_STRING_HPP
#include <godot_cpp/variant/utility_functions.hpp>
namespace godot {
inline void print_error(const Variant &p_variant) {
UtilityFunctions::printerr(p_variant);
}
inline void print_line(const Variant &p_variant) {
UtilityFunctions::print(p_variant);
}
inline void print_line_rich(const Variant &p_variant) {
UtilityFunctions::print_rich(p_variant);
}
template <typename... Args>
void print_error(const Variant &p_variant, Args... p_args) {
UtilityFunctions::printerr(p_variant, p_args...);
}
template <typename... Args>
void print_line(const Variant &p_variant, Args... p_args) {
UtilityFunctions::print(p_variant, p_args...);
}
template <typename... Args>
void print_line_rich(const Variant &p_variant, Args... p_args) {
UtilityFunctions::print_rich(p_variant, p_args...);
}
template <typename... Args>
void print_verbose(const Variant &p_variant, Args... p_args) {
UtilityFunctions::print_verbose(p_variant, p_args...);
}
bool is_print_verbose_enabled();
} // namespace godot
#endif // GODOT_PRINT_STRING_HPP

View File

@@ -397,16 +397,17 @@ MAKE_TYPED_ARRAY_INFO(Callable, Variant::CALLABLE)
MAKE_TYPED_ARRAY_INFO(Signal, Variant::SIGNAL)
MAKE_TYPED_ARRAY_INFO(Dictionary, Variant::DICTIONARY)
MAKE_TYPED_ARRAY_INFO(Array, Variant::ARRAY)
MAKE_TYPED_ARRAY_INFO(PackedByteArray, Variant::PACKED_BYTE_ARRAY)
MAKE_TYPED_ARRAY_INFO(PackedInt32Array, Variant::PACKED_INT32_ARRAY)
MAKE_TYPED_ARRAY_INFO(PackedInt64Array, Variant::PACKED_INT64_ARRAY)
MAKE_TYPED_ARRAY_INFO(PackedFloat32Array, Variant::PACKED_FLOAT32_ARRAY)
MAKE_TYPED_ARRAY_INFO(PackedFloat64Array, Variant::PACKED_FLOAT64_ARRAY)
MAKE_TYPED_ARRAY_INFO(PackedStringArray, Variant::PACKED_STRING_ARRAY)
MAKE_TYPED_ARRAY_INFO(PackedVector2Array, Variant::PACKED_VECTOR2_ARRAY)
MAKE_TYPED_ARRAY_INFO(PackedVector3Array, Variant::PACKED_VECTOR3_ARRAY)
MAKE_TYPED_ARRAY_INFO(PackedVector4Array, Variant::PACKED_VECTOR4_ARRAY)
MAKE_TYPED_ARRAY_INFO(PackedColorArray, Variant::PACKED_COLOR_ARRAY)
/*
MAKE_TYPED_ARRAY_INFO(Vector<uint8_t>, Variant::PACKED_BYTE_ARRAY)
MAKE_TYPED_ARRAY_INFO(Vector<int32_t>, Variant::PACKED_INT32_ARRAY)
MAKE_TYPED_ARRAY_INFO(Vector<int64_t>, Variant::PACKED_INT64_ARRAY)
MAKE_TYPED_ARRAY_INFO(Vector<float>, Variant::PACKED_FLOAT32_ARRAY)
MAKE_TYPED_ARRAY_INFO(Vector<double>, Variant::PACKED_FLOAT64_ARRAY)
MAKE_TYPED_ARRAY_INFO(Vector<String>, Variant::PACKED_STRING_ARRAY)
MAKE_TYPED_ARRAY_INFO(Vector<Vector2>, Variant::PACKED_VECTOR2_ARRAY)
MAKE_TYPED_ARRAY_INFO(Vector<Vector3>, Variant::PACKED_VECTOR3_ARRAY)
MAKE_TYPED_ARRAY_INFO(Vector<Color>, Variant::PACKED_COLOR_ARRAY)
MAKE_TYPED_ARRAY_INFO(IPAddress, Variant::STRING)
*/

View File

@@ -132,7 +132,7 @@ public:
}
}
_ALWAYS_INLINE_ explicit SafeNumeric<T>(T p_value = static_cast<T>(0)) {
_ALWAYS_INLINE_ explicit SafeNumeric(T p_value = static_cast<T>(0)) {
set(p_value);
}
};

View File

@@ -43,7 +43,7 @@ namespace godot {
class Variant;
struct _NO_DISCARD_ AABB {
struct [[nodiscard]] AABB {
Vector3 position;
Vector3 size;

View File

@@ -37,7 +37,7 @@
namespace godot {
struct _NO_DISCARD_ Basis {
struct [[nodiscard]] Basis {
Vector3 rows[3] = {
Vector3(1, 0, 0),
Vector3(0, 1, 0),
@@ -224,7 +224,7 @@ struct _NO_DISCARD_ Basis {
operator Quaternion() const { return get_quaternion(); }
static Basis looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0));
static Basis looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0), bool p_use_model_front = false);
Basis(const Quaternion &p_quaternion) { set_quaternion(p_quaternion); }
Basis(const Quaternion &p_quaternion, const Vector3 &p_scale) { set_quaternion_scale(p_quaternion, p_scale); }

View File

@@ -37,7 +37,7 @@ namespace godot {
class String;
struct _NO_DISCARD_ Color {
struct [[nodiscard]] Color {
union {
struct {
float r;

View File

@@ -38,7 +38,7 @@ namespace godot {
class Variant;
struct _NO_DISCARD_ Plane {
struct [[nodiscard]] Plane {
Vector3 normal;
real_t d = 0;

View File

@@ -44,7 +44,7 @@ struct Rect2;
struct Transform3D;
struct Vector2;
struct _NO_DISCARD_ Projection {
struct [[nodiscard]] Projection {
enum Planes {
PLANE_NEAR,
PLANE_FAR,
@@ -153,6 +153,7 @@ struct _NO_DISCARD_ Projection {
Projection();
Projection(const Vector4 &p_x, const Vector4 &p_y, const Vector4 &p_z, const Vector4 &p_w);
Projection(real_t p_xx, real_t p_xy, real_t p_xz, real_t p_xw, real_t p_yx, real_t p_yy, real_t p_yz, real_t p_yw, real_t p_zx, real_t p_zy, real_t p_zz, real_t p_zw, real_t p_wx, real_t p_wy, real_t p_wz, real_t p_ww);
Projection(const Transform3D &p_transform);
~Projection();
};

View File

@@ -31,12 +31,13 @@
#ifndef GODOT_QUATERNION_HPP
#define GODOT_QUATERNION_HPP
#include <godot_cpp/classes/global_constants.hpp>
#include <godot_cpp/core/math.hpp>
#include <godot_cpp/variant/vector3.hpp>
namespace godot {
struct _NO_DISCARD_ Quaternion {
struct [[nodiscard]] Quaternion {
union {
struct {
real_t x;
@@ -47,11 +48,11 @@ struct _NO_DISCARD_ Quaternion {
real_t components[4] = { 0, 0, 0, 1.0 };
};
_FORCE_INLINE_ real_t &operator[](int idx) {
return components[idx];
_FORCE_INLINE_ real_t &operator[](int p_idx) {
return components[p_idx];
}
_FORCE_INLINE_ const real_t &operator[](int idx) const {
return components[idx];
_FORCE_INLINE_ const real_t &operator[](int p_idx) const {
return components[p_idx];
}
_FORCE_INLINE_ real_t length_squared() const;
bool is_equal_approx(const Quaternion &p_quaternion) const;
@@ -66,14 +67,13 @@ struct _NO_DISCARD_ Quaternion {
_FORCE_INLINE_ real_t dot(const Quaternion &p_q) const;
real_t angle_to(const Quaternion &p_to) const;
Vector3 get_euler_xyz() const;
Vector3 get_euler_yxz() const;
Vector3 get_euler() const { return get_euler_yxz(); }
Vector3 get_euler(EulerOrder p_order = EulerOrder::EULER_ORDER_YXZ) const;
static Quaternion from_euler(const Vector3 &p_euler);
Quaternion slerp(const Quaternion &p_to, const real_t &p_weight) const;
Quaternion slerpni(const Quaternion &p_to, const real_t &p_weight) const;
Quaternion spherical_cubic_interpolate(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight) const;
Quaternion spherical_cubic_interpolate_in_time(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight, const real_t &p_b_t, const real_t &p_pre_a_t, const real_t &p_post_b_t) const;
Quaternion slerp(const Quaternion &p_to, real_t p_weight) const;
Quaternion slerpni(const Quaternion &p_to, real_t p_weight) const;
Quaternion spherical_cubic_interpolate(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, real_t p_weight) const;
Quaternion spherical_cubic_interpolate_in_time(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, real_t p_weight, real_t p_b_t, real_t p_pre_a_t, real_t p_post_b_t) const;
Vector3 get_axis() const;
real_t get_angle() const;
@@ -89,28 +89,28 @@ struct _NO_DISCARD_ Quaternion {
void operator*=(const Quaternion &p_q);
Quaternion operator*(const Quaternion &p_q) const;
_FORCE_INLINE_ Vector3 xform(const Vector3 &v) const {
_FORCE_INLINE_ Vector3 xform(const Vector3 &p_v) const {
#ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(!is_normalized(), v, "The quaternion must be normalized.");
ERR_FAIL_COND_V_MSG(!is_normalized(), p_v, "The quaternion " + operator String() + " must be normalized.");
#endif
Vector3 u(x, y, z);
Vector3 uv = u.cross(v);
return v + ((uv * w) + u.cross(uv)) * ((real_t)2);
Vector3 uv = u.cross(p_v);
return p_v + ((uv * w) + u.cross(uv)) * ((real_t)2);
}
_FORCE_INLINE_ Vector3 xform_inv(const Vector3 &v) const {
return inverse().xform(v);
_FORCE_INLINE_ Vector3 xform_inv(const Vector3 &p_v) const {
return inverse().xform(p_v);
}
_FORCE_INLINE_ void operator+=(const Quaternion &p_q);
_FORCE_INLINE_ void operator-=(const Quaternion &p_q);
_FORCE_INLINE_ void operator*=(const real_t &s);
_FORCE_INLINE_ void operator/=(const real_t &s);
_FORCE_INLINE_ Quaternion operator+(const Quaternion &q2) const;
_FORCE_INLINE_ Quaternion operator-(const Quaternion &q2) const;
_FORCE_INLINE_ void operator*=(real_t p_s);
_FORCE_INLINE_ void operator/=(real_t p_s);
_FORCE_INLINE_ Quaternion operator+(const Quaternion &p_q2) const;
_FORCE_INLINE_ Quaternion operator-(const Quaternion &p_q2) const;
_FORCE_INLINE_ Quaternion operator-() const;
_FORCE_INLINE_ Quaternion operator*(const real_t &s) const;
_FORCE_INLINE_ Quaternion operator/(const real_t &s) const;
_FORCE_INLINE_ Quaternion operator*(real_t p_s) const;
_FORCE_INLINE_ Quaternion operator/(real_t p_s) const;
_FORCE_INLINE_ bool operator==(const Quaternion &p_quaternion) const;
_FORCE_INLINE_ bool operator!=(const Quaternion &p_quaternion) const;
@@ -128,8 +128,6 @@ struct _NO_DISCARD_ Quaternion {
Quaternion(const Vector3 &p_axis, real_t p_angle);
Quaternion(const Vector3 &p_euler);
Quaternion(const Quaternion &p_q) :
x(p_q.x),
y(p_q.y),
@@ -144,9 +142,9 @@ struct _NO_DISCARD_ Quaternion {
w = p_q.w;
}
Quaternion(const Vector3 &v0, const Vector3 &v1) { // Shortest arc.
Vector3 c = v0.cross(v1);
real_t d = v0.dot(v1);
Quaternion(const Vector3 &p_v0, const Vector3 &p_v1) { // Shortest arc.
Vector3 c = p_v0.cross(p_v1);
real_t d = p_v0.dot(p_v1);
if (d < -1.0f + (real_t)CMP_EPSILON) {
x = 0;
@@ -187,25 +185,25 @@ void Quaternion::operator-=(const Quaternion &p_q) {
w -= p_q.w;
}
void Quaternion::operator*=(const real_t &s) {
x *= s;
y *= s;
z *= s;
w *= s;
void Quaternion::operator*=(real_t p_s) {
x *= p_s;
y *= p_s;
z *= p_s;
w *= p_s;
}
void Quaternion::operator/=(const real_t &s) {
*this *= 1.0f / s;
void Quaternion::operator/=(real_t p_s) {
*this *= 1.0f / p_s;
}
Quaternion Quaternion::operator+(const Quaternion &q2) const {
Quaternion Quaternion::operator+(const Quaternion &p_q2) const {
const Quaternion &q1 = *this;
return Quaternion(q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w);
return Quaternion(q1.x + p_q2.x, q1.y + p_q2.y, q1.z + p_q2.z, q1.w + p_q2.w);
}
Quaternion Quaternion::operator-(const Quaternion &q2) const {
Quaternion Quaternion::operator-(const Quaternion &p_q2) const {
const Quaternion &q1 = *this;
return Quaternion(q1.x - q2.x, q1.y - q2.y, q1.z - q2.z, q1.w - q2.w);
return Quaternion(q1.x - p_q2.x, q1.y - p_q2.y, q1.z - p_q2.z, q1.w - p_q2.w);
}
Quaternion Quaternion::operator-() const {
@@ -213,12 +211,12 @@ Quaternion Quaternion::operator-() const {
return Quaternion(-q2.x, -q2.y, -q2.z, -q2.w);
}
Quaternion Quaternion::operator*(const real_t &s) const {
return Quaternion(x * s, y * s, z * s, w * s);
Quaternion Quaternion::operator*(real_t p_s) const {
return Quaternion(x * p_s, y * p_s, z * p_s, w * p_s);
}
Quaternion Quaternion::operator/(const real_t &s) const {
return *this * (1.0f / s);
Quaternion Quaternion::operator/(real_t p_s) const {
return *this * (1.0f / p_s);
}
bool Quaternion::operator==(const Quaternion &p_quaternion) const {
@@ -229,7 +227,7 @@ bool Quaternion::operator!=(const Quaternion &p_quaternion) const {
return x != p_quaternion.x || y != p_quaternion.y || z != p_quaternion.z || w != p_quaternion.w;
}
_FORCE_INLINE_ Quaternion operator*(const real_t &p_real, const Quaternion &p_quaternion) {
_FORCE_INLINE_ Quaternion operator*(real_t p_real, const Quaternion &p_quaternion) {
return p_quaternion * p_real;
}

View File

@@ -40,7 +40,7 @@ class String;
struct Rect2i;
struct Transform2D;
struct _NO_DISCARD_ Rect2 {
struct [[nodiscard]] Rect2 {
Point2 position;
Size2 size;

View File

@@ -39,7 +39,7 @@ namespace godot {
class String;
struct Rect2;
struct _NO_DISCARD_ Rect2i {
struct [[nodiscard]] Rect2i {
Point2i position;
Size2i size;

View File

@@ -39,7 +39,7 @@ namespace godot {
class String;
struct _NO_DISCARD_ Transform2D {
struct [[nodiscard]] Transform2D {
// Warning #1: basis of Transform2D is stored differently from Basis. In terms of columns array, the basis matrix looks like "on paper":
// M = (columns[0][0] columns[1][0])
// (columns[0][1] columns[1][1])

View File

@@ -39,7 +39,7 @@
namespace godot {
struct _NO_DISCARD_ Transform3D {
struct [[nodiscard]] Transform3D {
Basis basis;
Vector3 origin;

View File

@@ -327,8 +327,6 @@ public:
bool booleanize() const;
String stringify() const;
Variant duplicate(bool deep = false) const;
static void blend(const Variant &a, const Variant &b, float c, Variant &r_dst);
static void interpolate(const Variant &a, const Variant &b, float c, Variant &r_dst);
static String get_type_name(Variant::Type type);
static bool can_convert(Variant::Type from, Variant::Type to);

View File

@@ -39,7 +39,7 @@ namespace godot {
class String;
struct Vector2i;
struct _NO_DISCARD_ Vector2 {
struct [[nodiscard]] Vector2 {
static const int AXIS_COUNT = 2;
enum Axis {

View File

@@ -39,7 +39,7 @@ namespace godot {
class String;
struct Vector2;
struct _NO_DISCARD_ Vector2i {
struct [[nodiscard]] Vector2i {
static const int AXIS_COUNT = 2;
enum Axis {

View File

@@ -41,7 +41,7 @@ struct Basis;
struct Vector2;
struct Vector3i;
struct _NO_DISCARD_ Vector3 {
struct [[nodiscard]] Vector3 {
static const int AXIS_COUNT = 3;
enum Axis {

View File

@@ -39,7 +39,7 @@ namespace godot {
class String;
struct Vector3;
struct _NO_DISCARD_ Vector3i {
struct [[nodiscard]] Vector3i {
static const int AXIS_COUNT = 3;
enum Axis {

View File

@@ -38,7 +38,7 @@ namespace godot {
class String;
struct _NO_DISCARD_ Vector4 {
struct [[nodiscard]] Vector4 {
static const int AXIS_COUNT = 4;
enum Axis {
@@ -55,16 +55,17 @@ struct _NO_DISCARD_ Vector4 {
real_t z;
real_t w;
};
real_t components[4] = { 0, 0, 0, 0 };
[[deprecated("Use coord instead")]] real_t components[4];
real_t coord[4] = { 0, 0, 0, 0 };
};
_FORCE_INLINE_ real_t &operator[](const int p_axis) {
DEV_ASSERT((unsigned int)p_axis < 4);
return components[p_axis];
return coord[p_axis];
}
_FORCE_INLINE_ const real_t &operator[](const int p_axis) const {
DEV_ASSERT((unsigned int)p_axis < 4);
return components[p_axis];
return coord[p_axis];
}
Vector4::Axis min_axis_index() const;

View File

@@ -39,7 +39,7 @@ namespace godot {
class String;
struct Vector4;
struct _NO_DISCARD_ Vector4i {
struct [[nodiscard]] Vector4i {
static const int AXIS_COUNT = 4;
enum Axis {

View File

@@ -6,26 +6,41 @@ from pathlib import Path
sys.path.insert(1, os.path.join(os.path.dirname(__file__), "..", ".."))
from binding_generator import generate_bindings, get_file_list
from binding_generator import _generate_bindings, _get_file_list
from build_profile import generate_trimmed_api
api_filepath = "gdextension/extension_api.json"
bits = "64"
precision = "single"
output_dir = "self_test"
generate_bindings(api_filepath, use_template_get_node=False, bits=bits, precision=precision, output_dir=output_dir)
flist = get_file_list(api_filepath, output_dir, headers=True, sources=True)
p = Path(output_dir) / "gen"
allfiles = [str(f.as_posix()) for f in p.glob("**/*.*")]
missing = list(filter((lambda f: f not in flist), allfiles))
extras = list(filter((lambda f: f not in allfiles), flist))
if len(missing) > 0 or len(extras) > 0:
print("Error!")
for f in missing:
print("MISSING: " + str(f))
for f in extras:
print("EXTRA: " + str(f))
sys.exit(1)
else:
print("OK!")
def test(profile_filepath=""):
api = generate_trimmed_api(api_filepath, profile_filepath)
_generate_bindings(
api,
api_filepath,
use_template_get_node=False,
bits=bits,
precision=precision,
output_dir=output_dir,
)
flist = _get_file_list(api, output_dir, headers=True, sources=True)
p = Path(output_dir) / "gen"
allfiles = [str(f.as_posix()) for f in p.glob("**/*.*")]
missing = list(filter((lambda f: f not in flist), allfiles))
extras = list(filter((lambda f: f not in allfiles), flist))
if len(missing) > 0 or len(extras) > 0:
print("Error!")
for f in missing:
print("MISSING: " + str(f))
for f in extras:
print("EXTRA: " + str(f))
sys.exit(1)
else:
print("OK!")
test()
test("test/build_profile.json")

View File

@@ -39,14 +39,27 @@
#include <godot_cpp/core/class_db.hpp>
namespace godot {
thread_local const StringName *Wrapped::_constructing_extension_class_name = nullptr;
thread_local const GDExtensionInstanceBindingCallbacks *Wrapped::_constructing_class_binding_callbacks = nullptr;
#ifdef _GODOT_CPP_AVOID_THREAD_LOCAL
std::recursive_mutex Wrapped::_constructing_mutex;
#endif
_GODOT_CPP_THREAD_LOCAL const StringName *Wrapped::_constructing_extension_class_name = nullptr;
_GODOT_CPP_THREAD_LOCAL const GDExtensionInstanceBindingCallbacks *Wrapped::_constructing_class_binding_callbacks = nullptr;
#ifdef HOT_RELOAD_ENABLED
_GODOT_CPP_THREAD_LOCAL GDExtensionObjectPtr Wrapped::_constructing_recreate_owner = nullptr;
#endif
const StringName *Wrapped::_get_extension_class_name() {
return nullptr;
}
void Wrapped::_postinitialize() {
#ifdef _GODOT_CPP_AVOID_THREAD_LOCAL
Wrapped::_constructing_mutex.unlock();
#endif
// Only send NOTIFICATION_POSTINITIALIZE for extension classes.
if (_is_extension_class()) {
_notificationv(Object::NOTIFICATION_POSTINITIALIZE);
@@ -55,25 +68,14 @@ void Wrapped::_postinitialize() {
Wrapped::Wrapped(const StringName p_godot_class) {
#ifdef HOT_RELOAD_ENABLED
if (unlikely(Wrapped::recreate_instance)) {
RecreateInstance *recreate_data = Wrapped::recreate_instance;
RecreateInstance *previous = nullptr;
while (recreate_data) {
if (recreate_data->wrapper == this) {
_owner = recreate_data->owner;
if (previous) {
previous->next = recreate_data->next;
} else {
Wrapped::recreate_instance = recreate_data->next;
}
return;
}
previous = recreate_data;
recreate_data = recreate_data->next;
}
}
if (unlikely(Wrapped::_constructing_recreate_owner)) {
_owner = Wrapped::_constructing_recreate_owner;
Wrapped::_constructing_recreate_owner = nullptr;
} else
#endif
_owner = godot::internal::gdextension_interface_classdb_construct_object(reinterpret_cast<GDExtensionConstStringNamePtr>(p_godot_class._native_ptr()));
{
_owner = godot::internal::gdextension_interface_classdb_construct_object(reinterpret_cast<GDExtensionConstStringNamePtr>(p_godot_class._native_ptr()));
}
if (_constructing_extension_class_name) {
godot::internal::gdextension_interface_object_set_instance(_owner, reinterpret_cast<GDExtensionConstStringNamePtr>(_constructing_extension_class_name), this);

View File

@@ -353,9 +353,14 @@ void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_
if (mi.argument_count > 0) {
mi.arguments = (GDExtensionPropertyInfo *)memalloc(sizeof(GDExtensionPropertyInfo) * mi.argument_count);
mi.arguments_metadata = (GDExtensionClassMethodArgumentMetadata *)memalloc(sizeof(GDExtensionClassMethodArgumentMetadata) * mi.argument_count);
if (mi.argument_count != p_method.arguments_metadata.size()) {
WARN_PRINT("Mismatch argument metadata count for virtual method: " + String(p_class) + "::" + p_method.name);
}
for (uint32_t i = 0; i < mi.argument_count; i++) {
mi.arguments[i] = p_method.arguments[i]._to_gdextension();
mi.arguments_metadata[i] = p_method.arguments_metadata[i];
if (i < p_method.arguments_metadata.size()) {
mi.arguments_metadata[i] = p_method.arguments_metadata[i];
}
}
} else {
mi.arguments = nullptr;
@@ -384,7 +389,7 @@ void ClassDB::initialize_class(const ClassInfo &p_cl) {
}
void ClassDB::initialize(GDExtensionInitializationLevel p_level) {
for (const std::pair<StringName, ClassInfo> pair : classes) {
for (const std::pair<const StringName, ClassInfo> &pair : classes) {
const ClassInfo &cl = pair.second;
if (cl.level != p_level) {
continue;

View File

@@ -32,6 +32,22 @@
namespace godot {
void MethodBind::_set_const(bool p_const) {
_const = p_const;
}
void MethodBind::_set_static(bool p_static) {
_static = p_static;
}
void MethodBind::_set_returns(bool p_returns) {
_returns = p_returns;
}
void MethodBind::_set_vararg(bool p_vararg) {
_vararg = p_vararg;
}
StringName MethodBind::get_name() const {
return name;
}
@@ -40,26 +56,6 @@ void MethodBind::set_name(const StringName &p_name) {
name = p_name;
}
void MethodBind::set_argument_count(int p_count) {
argument_count = p_count;
}
void MethodBind::set_const(bool p_const) {
_is_const = p_const;
}
void MethodBind::set_return(bool p_return) {
_has_return = p_return;
}
void MethodBind::set_static(bool p_static) {
_static = p_static;
}
void MethodBind::set_vararg(bool p_vararg) {
_vararg = p_vararg;
}
void MethodBind::set_argument_names(const std::vector<StringName> &p_names) {
argument_names = p_names;
}
@@ -68,7 +64,7 @@ std::vector<StringName> MethodBind::get_argument_names() const {
return argument_names;
}
void MethodBind::generate_argument_types(int p_count) {
void MethodBind::_generate_argument_types(int p_count) {
set_argument_count(p_count);
if (argument_types != nullptr) {

39
src/core/print_string.cpp Normal file
View File

@@ -0,0 +1,39 @@
/**************************************************************************/
/* print_string.cpp */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#include <godot_cpp/core/print_string.hpp>
#include <godot_cpp/classes/os.hpp>
namespace godot {
bool is_print_verbose_enabled() {
return OS::get_singleton()->is_stdout_verbose();
}
} // namespace godot

View File

@@ -1037,12 +1037,15 @@ void Basis::rotate_sh(real_t *p_values) {
p_values[8] = d4 * s_scale_dst4;
}
Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up) {
Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up, bool p_use_model_front) {
#ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(p_target.is_zero_approx(), Basis(), "The target vector can't be zero.");
ERR_FAIL_COND_V_MSG(p_up.is_zero_approx(), Basis(), "The up vector can't be zero.");
#endif
Vector3 v_z = -p_target.normalized();
Vector3 v_z = p_target.normalized();
if (!p_use_model_front) {
v_z = -v_z;
}
Vector3 v_x = p_up.cross(v_z);
#ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(v_x.is_zero_approx(), Basis(), "The target vector and up vector can't be parallel to each other.");

View File

@@ -913,6 +913,13 @@ Projection::Projection(const Vector4 &p_x, const Vector4 &p_y, const Vector4 &p_
columns[3] = p_w;
}
Projection::Projection(real_t p_xx, real_t p_xy, real_t p_xz, real_t p_xw, real_t p_yx, real_t p_yy, real_t p_yz, real_t p_yw, real_t p_zx, real_t p_zy, real_t p_zz, real_t p_zw, real_t p_wx, real_t p_wy, real_t p_wz, real_t p_ww) {
columns[0] = Vector4(p_xx, p_xy, p_xz, p_xw);
columns[1] = Vector4(p_yx, p_yy, p_yz, p_yw);
columns[2] = Vector4(p_zx, p_zy, p_zz, p_zw);
columns[3] = Vector4(p_wx, p_wy, p_wz, p_ww);
}
Projection::Projection(const Transform3D &p_transform) {
const Transform3D &tr = p_transform;
real_t *m = &columns[0][0];

View File

@@ -37,28 +37,15 @@ namespace godot {
real_t Quaternion::angle_to(const Quaternion &p_to) const {
real_t d = dot(p_to);
return Math::acos(CLAMP(d * d * 2 - 1, -1, 1));
// acos does clamping.
return Math::acos(d * d * 2 - 1);
}
// get_euler_xyz returns a vector containing the Euler angles in the format
// (ax,ay,az), where ax is the angle of rotation around x axis,
// and similar for other axes.
// This implementation uses XYZ convention (Z is the first rotation).
Vector3 Quaternion::get_euler_xyz() const {
Basis m(*this);
return m.get_euler(EULER_ORDER_XYZ);
}
// get_euler_yxz returns a vector containing the Euler angles in the format
// (ax,ay,az), where ax is the angle of rotation around x axis,
// and similar for other axes.
// This implementation uses YXZ convention (Z is the first rotation).
Vector3 Quaternion::get_euler_yxz() const {
Vector3 Quaternion::get_euler(EulerOrder p_order) const {
#ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(!is_normalized(), Vector3(0, 0, 0), "The quaternion must be normalized.");
ERR_FAIL_COND_V_MSG(!is_normalized(), Vector3(0, 0, 0), "The quaternion " + operator String() + " must be normalized.");
#endif
Basis m(*this);
return m.get_euler(EULER_ORDER_YXZ);
return Basis(*this).get_euler(p_order);
}
void Quaternion::operator*=(const Quaternion &p_q) {
@@ -103,7 +90,7 @@ bool Quaternion::is_normalized() const {
Quaternion Quaternion::inverse() const {
#ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The quaternion must be normalized.");
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The quaternion " + operator String() + " must be normalized.");
#endif
return Quaternion(-x, -y, -z, w);
}
@@ -125,10 +112,10 @@ Quaternion Quaternion::exp() const {
return Quaternion(src_v, theta);
}
Quaternion Quaternion::slerp(const Quaternion &p_to, const real_t &p_weight) const {
Quaternion Quaternion::slerp(const Quaternion &p_to, real_t p_weight) const {
#ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized.");
ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion must be normalized.");
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion " + operator String() + " must be normalized.");
ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion " + p_to.operator String() + " must be normalized.");
#endif
Quaternion to1;
real_t omega, cosom, sinom, scale0, scale1;
@@ -166,10 +153,10 @@ Quaternion Quaternion::slerp(const Quaternion &p_to, const real_t &p_weight) con
scale0 * w + scale1 * to1.w);
}
Quaternion Quaternion::slerpni(const Quaternion &p_to, const real_t &p_weight) const {
Quaternion Quaternion::slerpni(const Quaternion &p_to, real_t p_weight) const {
#ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized.");
ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion must be normalized.");
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion " + operator String() + " must be normalized.");
ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion " + p_to.operator String() + " must be normalized.");
#endif
const Quaternion &from = *this;
@@ -190,10 +177,10 @@ Quaternion Quaternion::slerpni(const Quaternion &p_to, const real_t &p_weight) c
invFactor * from.w + newFactor * p_to.w);
}
Quaternion Quaternion::spherical_cubic_interpolate(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight) const {
Quaternion Quaternion::spherical_cubic_interpolate(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, real_t p_weight) const {
#ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized.");
ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion must be normalized.");
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion " + operator String() + " must be normalized.");
ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion " + p_b.operator String() + " must be normalized.");
#endif
Quaternion from_q = *this;
Quaternion pre_q = p_pre_a;
@@ -236,15 +223,15 @@ Quaternion Quaternion::spherical_cubic_interpolate(const Quaternion &p_b, const
ln.z = Math::cubic_interpolate(ln_from.z, ln_to.z, ln_pre.z, ln_post.z, p_weight);
Quaternion q2 = to_q * ln.exp();
// To cancel error made by Expmap ambiguity, do blends.
// To cancel error made by Expmap ambiguity, do blending.
return q1.slerp(q2, p_weight);
}
Quaternion Quaternion::spherical_cubic_interpolate_in_time(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight,
const real_t &p_b_t, const real_t &p_pre_a_t, const real_t &p_post_b_t) const {
Quaternion Quaternion::spherical_cubic_interpolate_in_time(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, real_t p_weight,
real_t p_b_t, real_t p_pre_a_t, real_t p_post_b_t) const {
#ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized.");
ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion must be normalized.");
ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion " + operator String() + " must be normalized.");
ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion " + p_b.operator String() + " must be normalized.");
#endif
Quaternion from_q = *this;
Quaternion pre_q = p_pre_a;
@@ -287,7 +274,7 @@ Quaternion Quaternion::spherical_cubic_interpolate_in_time(const Quaternion &p_b
ln.z = Math::cubic_interpolate_in_time(ln_from.z, ln_to.z, ln_pre.z, ln_post.z, p_weight, p_b_t, p_pre_a_t, p_post_b_t);
Quaternion q2 = to_q * ln.exp();
// To cancel error made by Expmap ambiguity, do blends.
// To cancel error made by Expmap ambiguity, do blending.
return q1.slerp(q2, p_weight);
}
@@ -309,7 +296,7 @@ real_t Quaternion::get_angle() const {
Quaternion::Quaternion(const Vector3 &p_axis, real_t p_angle) {
#ifdef MATH_CHECKS
ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "The axis Vector3 must be normalized.");
ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "The axis Vector3 " + p_axis.operator String() + " must be normalized.");
#endif
real_t d = p_axis.length();
if (d == 0) {
@@ -332,7 +319,7 @@ Quaternion::Quaternion(const Vector3 &p_axis, real_t p_angle) {
// (ax, ay, az), where ax is the angle of rotation around x axis,
// and similar for other axes.
// This implementation uses YXZ convention (Z is the first rotation).
Quaternion::Quaternion(const Vector3 &p_euler) {
Quaternion Quaternion::from_euler(const Vector3 &p_euler) {
real_t half_a1 = p_euler.y * 0.5f;
real_t half_a2 = p_euler.x * 0.5f;
real_t half_a3 = p_euler.z * 0.5f;
@@ -348,10 +335,11 @@ Quaternion::Quaternion(const Vector3 &p_euler) {
real_t cos_a3 = Math::cos(half_a3);
real_t sin_a3 = Math::sin(half_a3);
x = sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3;
y = sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3;
z = -sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3;
w = sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3;
return Quaternion(
sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3,
sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3,
-sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3,
sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3);
}
} // namespace godot

View File

@@ -42,4 +42,5 @@ else:
source=sources,
)
env.NoCache(library)
Default(library)

View File

@@ -1,9 +1,13 @@
{
"enabled_classes": [
"Control",
"InputEventKey",
"Label",
"MultiplayerAPI",
"MultiplayerPeer",
"OS",
"TileMap",
"InputEventKey"
"TileSet",
"Viewport"
]
}

View File

@@ -18,8 +18,7 @@ func _ready():
# To string.
assert_equal(example.to_string(),'[ GDExtension::Example <--> Instance ID:%s ]' % example.get_instance_id())
# It appears there's a bug with instance ids :-(
#assert_equal($Example/ExampleMin.to_string(), 'ExampleMin:[Wrapped:%s]' % $Example/ExampleMin.get_instance_id())
assert_equal($Example/ExampleMin.to_string(), 'ExampleMin:<ExampleMin#%s>' % $Example/ExampleMin.get_instance_id())
# Call static methods.
assert_equal(Example.test_static(9, 100), 109);
@@ -270,6 +269,12 @@ func _ready():
assert_equal(example_child.get_value1(), 11)
assert_equal(example_child.get_value2(), 22)
# Test that the extension's library path is absolute and valid.
var library_path = Example.test_library_path()
assert_equal(library_path.begins_with("res://"), false)
assert_equal(library_path, ProjectSettings.globalize_path(library_path))
assert_equal(FileAccess.file_exists(library_path), true)
exit_with_status()
func _on_Example_custom_signal(signal_name, value):

View File

@@ -204,6 +204,7 @@ void Example::_bind_methods() {
ClassDB::bind_method(D_METHOD("test_str_utility"), &Example::test_str_utility);
ClassDB::bind_method(D_METHOD("test_string_is_forty_two"), &Example::test_string_is_forty_two);
ClassDB::bind_method(D_METHOD("test_string_resize"), &Example::test_string_resize);
ClassDB::bind_method(D_METHOD("test_typed_array_of_packed"), &Example::test_typed_array_of_packed);
ClassDB::bind_method(D_METHOD("test_vector_ops"), &Example::test_vector_ops);
ClassDB::bind_method(D_METHOD("test_vector_init_list"), &Example::test_vector_init_list);
@@ -248,6 +249,8 @@ void Example::_bind_methods() {
ClassDB::bind_static_method("Example", D_METHOD("test_static", "a", "b"), &Example::test_static);
ClassDB::bind_static_method("Example", D_METHOD("test_static2"), &Example::test_static2);
ClassDB::bind_static_method("Example", D_METHOD("test_library_path"), &Example::test_library_path);
{
MethodInfo mi;
mi.arguments.push_back(PropertyInfo(Variant::STRING, "some_argument"));
@@ -424,6 +427,19 @@ String Example::test_string_resize(String p_string) const {
return p_string;
}
TypedArray<PackedInt32Array> Example::test_typed_array_of_packed() const {
TypedArray<PackedInt32Array> arr;
PackedInt32Array packed_arr1;
packed_arr1.push_back(1);
packed_arr1.push_back(2);
arr.push_back(packed_arr1);
PackedInt32Array packed_arr2;
packed_arr2.push_back(3);
packed_arr2.push_back(4);
arr.push_back(packed_arr2);
return arr;
}
int Example::test_vector_ops() const {
PackedInt32Array arr;
arr.push_back(10);
@@ -695,6 +711,12 @@ String Example::test_use_engine_singleton() const {
return OS::get_singleton()->get_name();
}
String Example::test_library_path() {
String library_path;
internal::gdextension_interface_get_library_path(internal::library, library_path._native_ptr());
return library_path;
}
void ExampleRuntime::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_prop_value", "value"), &ExampleRuntime::set_prop_value);
ClassDB::bind_method(D_METHOD("get_prop_value"), &ExampleRuntime::get_prop_value);

View File

@@ -134,6 +134,7 @@ public:
String test_str_utility() const;
bool test_string_is_forty_two(const String &p_str) const;
String test_string_resize(String p_original) const;
TypedArray<PackedInt32Array> test_typed_array_of_packed() const;
int test_vector_ops() const;
int test_vector_init_list() const;
@@ -194,6 +195,8 @@ public:
GDVIRTUAL1(_do_something_virtual_with_control, Control *);
String test_use_engine_singleton() const;
static String test_library_path();
};
VARIANT_ENUM_CAST(Example::Constants);

View File

@@ -11,6 +11,11 @@ def options(opts):
"Target Android API level",
"21",
)
opts.Add(
"ndk_version",
"Fully qualified version of ndk to use for compilation.",
"23.2.8568313",
)
opts.Add(
"ANDROID_HOME",
"Path to your Android SDK installation. By default, uses ANDROID_HOME from your defined environment variables.",
@@ -22,14 +27,9 @@ def exists(env):
return get_android_ndk_root(env) is not None
# This must be kept in sync with the value in https://github.com/godotengine/godot/blob/master/platform/android/detect.py#L58.
def get_ndk_version():
return "23.2.8568313"
def get_android_ndk_root(env):
if env["ANDROID_HOME"]:
return env["ANDROID_HOME"] + "/ndk/" + get_ndk_version()
return env["ANDROID_HOME"] + "/ndk/" + env["ndk_version"]
else:
return os.environ.get("ANDROID_NDK_ROOT")
@@ -68,7 +68,7 @@ def generate(env):
if not os.path.exists(toolchain):
print("ERROR: Could not find NDK toolchain at " + toolchain + ".")
print("Make sure NDK version " + get_ndk_version() + " is installed.")
print("Make sure NDK version " + env["ndk_version"] + " is installed.")
env.Exit(1)
env.PrependENVPath("PATH", toolchain + "/bin") # This does nothing half of the time, but we'll put it here anyways
@@ -120,4 +120,9 @@ def generate(env):
env.Append(CPPDEFINES=["ANDROID_ENABLED", "UNIX_ENABLED"])
# Refer to https://github.com/godotengine/godot/blob/master/platform/android/detect.py
# LTO benefits for Android (size, performance) haven't been clearly established yet.
if env["lto"] == "auto":
env["lto"] = "none"
common_compiler_flags.generate(env)

View File

@@ -22,6 +22,10 @@ def exists(env):
def generate(env):
assert env["lto"] in ["thin", "full", "none"], "Unrecognized lto: {}".format(env["lto"])
if env["lto"] != "none":
print("Using LTO: " + env["lto"])
# Require C++17
if env.get("is_msvc", False):
env.Append(CXXFLAGS=["/std:c++17"])
@@ -64,6 +68,22 @@ def generate(env):
env.Append(LINKFLAGS=["/OPT:REF"])
elif env["optimize"] == "debug" or env["optimize"] == "none":
env.Append(CCFLAGS=["/Od"])
if env["lto"] == "thin":
if not env["use_llvm"]:
print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
env.Exit(255)
env.Append(CCFLAGS=["-flto=thin"])
env.Append(LINKFLAGS=["-flto=thin"])
elif env["lto"] == "full":
if env["use_llvm"]:
env.Append(CCFLAGS=["-flto"])
env.Append(LINKFLAGS=["-flto"])
else:
env.AppendUnique(CCFLAGS=["/GL"])
env.AppendUnique(ARFLAGS=["/LTCG"])
env.AppendUnique(LINKFLAGS=["/LTCG"])
else:
if env["debug_symbols"]:
# Adding dwarf-4 explicitly makes stacktraces work with clang builds,
@@ -74,7 +94,7 @@ def generate(env):
else:
env.Append(CCFLAGS=["-g2"])
else:
if using_clang(env) and not is_vanilla_clang(env):
if using_clang(env) and not is_vanilla_clang(env) and not env["use_mingw"]:
# Apple Clang, its linker doesn't like -s.
env.Append(LINKFLAGS=["-Wl,-S", "-Wl,-x", "-Wl,-dead_strip"])
else:
@@ -91,3 +111,13 @@ def generate(env):
env.Append(CCFLAGS=["-Og"])
elif env["optimize"] == "none":
env.Append(CCFLAGS=["-O0"])
if env["lto"] == "thin":
if (env["platform"] == "windows" or env["platform"] == "linux") and not env["use_llvm"]:
print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
env.Exit(255)
env.Append(CCFLAGS=["-flto=thin"])
env.Append(LINKFLAGS=["-flto=thin"])
elif env["lto"] == "full":
env.Append(CCFLAGS=["-flto"])
env.Append(LINKFLAGS=["-flto"])

View File

@@ -2,6 +2,7 @@ import os
import platform
import sys
from SCons import __version__ as scons_raw_version
from SCons.Action import Action
from SCons.Builder import Builder
from SCons.Errors import UserError
@@ -10,7 +11,8 @@ from SCons.Tool import Tool
from SCons.Variables import BoolVariable, EnumVariable, PathVariable
from SCons.Variables.BoolVariable import _text2bool
from binding_generator import scons_emit_files, scons_generate_bindings
from binding_generator import _generate_bindings, _get_file_list, get_file_list
from build_profile import generate_trimmed_api
def add_sources(sources, dir, extension):
@@ -129,6 +131,38 @@ def no_verbose(env):
env.Append(GENCOMSTR=[generated_file_message])
def scons_emit_files(target, source, env):
profile_filepath = env.get("build_profile", "")
if profile_filepath:
profile_filepath = normalize_path(profile_filepath, env)
# Always clean all files
env.Clean(target, [env.File(f) for f in get_file_list(str(source[0]), target[0].abspath, True, True)])
api = generate_trimmed_api(str(source[0]), profile_filepath)
files = [env.File(f) for f in _get_file_list(api, target[0].abspath, True, True)]
env["godot_cpp_gen_dir"] = target[0].abspath
return files, source
def scons_generate_bindings(target, source, env):
profile_filepath = env.get("build_profile", "")
if profile_filepath:
profile_filepath = normalize_path(profile_filepath, env)
api = generate_trimmed_api(str(source[0]), profile_filepath)
_generate_bindings(
api,
str(source[0]),
env["generate_template_get_node"],
"32" if "32" in env["arch"] else "64",
env["precision"],
env["godot_cpp_gen_dir"],
)
return None
platforms = ["linux", "macos", "windows", "android", "ios", "web"]
# CPU architecture options.
@@ -326,6 +360,14 @@ def options(opts, env):
("none", "custom", "debug", "speed", "speed_trace", "size"),
)
)
opts.Add(
EnumVariable(
"lto",
"Link-time optimization",
"none",
("none", "auto", "thin", "full"),
)
)
opts.Add(BoolVariable("debug_symbols", "Build with debugging symbols", True))
opts.Add(BoolVariable("dev_build", "Developer build with dev-only debugging code (DEV_ENABLED)", False))
opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", False))
@@ -383,6 +425,8 @@ def make_doc_source(target, source, env):
def generate(env):
env.scons_version = env._get_major_minor_revision(scons_raw_version)
# Default num_jobs to local cpu count if not user specified.
# SCons has a peculiarity where user-specified options won't be overridden
# by SetOption, so we can rely on this to know if we should use our default.
@@ -440,6 +484,17 @@ def generate(env):
else: # Release
opt_level = "speed"
# Allow marking includes as external/system to avoid raising warnings.
if env.scons_version < (4, 2):
env["_CPPEXTINCFLAGS"] = "${_concat(EXTINCPREFIX, CPPEXTPATH, EXTINCSUFFIX, __env__, RDirs, TARGET, SOURCE)}"
else:
env["_CPPEXTINCFLAGS"] = (
"${_concat(EXTINCPREFIX, CPPEXTPATH, EXTINCSUFFIX, __env__, RDirs, TARGET, SOURCE, affect_signature=False)}"
)
env["CPPEXTPATH"] = []
env["EXTINCPREFIX"] = "-isystem "
env["EXTINCSUFFIX"] = ""
env["optimize"] = ARGUMENTS.get("optimize", opt_level)
env["debug_symbols"] = get_cmdline_bool("debug_symbols", env.dev_build)
@@ -552,6 +607,7 @@ def _godot_cpp(env):
if env["build_library"]:
library = env.StaticLibrary(target=env.File("bin/%s" % library_name), source=sources)
env.NoCache(library)
default_args = [library]
# Add compiledb if the option is set

View File

@@ -36,9 +36,11 @@ def generate(env):
if env["ios_simulator"]:
sdk_name = "iphonesimulator"
env.Append(CCFLAGS=["-mios-simulator-version-min=" + env["ios_min_version"]])
env.Append(LINKFLAGS=["-mios-simulator-version-min=" + env["ios_min_version"]])
else:
sdk_name = "iphoneos"
env.Append(CCFLAGS=["-miphoneos-version-min=" + env["ios_min_version"]])
env.Append(LINKFLAGS=["-miphoneos-version-min=" + env["ios_min_version"]])
if sys.platform == "darwin":
if env["IOS_SDK_PATH"] == "":
@@ -97,4 +99,9 @@ def generate(env):
env.Append(CPPDEFINES=["IOS_ENABLED", "UNIX_ENABLED"])
# Refer to https://github.com/godotengine/godot/blob/master/platform/ios/detect.py:
# Disable by default as it makes linking in Xcode very slow.
if env["lto"] == "auto":
env["lto"] = "none"
common_compiler_flags.generate(env)

View File

@@ -39,4 +39,8 @@ def generate(env):
env.Append(CPPDEFINES=["LINUX_ENABLED", "UNIX_ENABLED"])
# Refer to https://github.com/godotengine/godot/blob/master/platform/linuxbsd/detect.py
if env["lto"] == "auto":
env["lto"] = "full"
common_compiler_flags.generate(env)

View File

@@ -73,4 +73,9 @@ def generate(env):
env.Append(CPPDEFINES=["MACOS_ENABLED", "UNIX_ENABLED"])
# Refer to https://github.com/godotengine/godot/blob/master/platform/macos/detect.py
# LTO benefits for macOS (size, performance) haven't been clearly established yet.
if env["lto"] == "auto":
env["lto"] = "none"
common_compiler_flags.generate(env)

View File

@@ -39,13 +39,21 @@ def generate(env):
env.Append(LINKFLAGS=["-sUSE_PTHREADS=1"])
# Build as side module (shared library).
env.Append(CPPFLAGS=["-sSIDE_MODULE=1"])
env.Append(CCFLAGS=["-sSIDE_MODULE=1"])
env.Append(LINKFLAGS=["-sSIDE_MODULE=1"])
# Enable WebAssembly BigInt <-> i64 conversion.
# This must match the flag used to build Godot (true in official builds since 4.3)
env.Append(LINKFLAGS=["-sWASM_BIGINT"])
# Force wasm longjmp mode.
env.Append(CCFLAGS=["-sSUPPORT_LONGJMP='wasm'"])
env.Append(LINKFLAGS=["-sSUPPORT_LONGJMP='wasm'"])
env.Append(CPPDEFINES=["WEB_ENABLED", "UNIX_ENABLED"])
# Refer to https://github.com/godotengine/godot/blob/master/platform/web/detect.py
if env["lto"] == "auto":
env["lto"] = "full"
common_compiler_flags.generate(env)

View File

@@ -1,3 +1,4 @@
import os
import sys
import common_compiler_flags
@@ -72,10 +73,13 @@ def silence_msvc(env):
def options(opts):
mingw = os.getenv("MINGW_PREFIX", "")
opts.Add(BoolVariable("use_mingw", "Use the MinGW compiler instead of MSVC - only effective on Windows", False))
opts.Add(BoolVariable("use_clang_cl", "Use the clang driver instead of MSVC - only effective on Windows", False))
opts.Add(BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True))
opts.Add(BoolVariable("silence_msvc", "Silence MSVC's cl/link stdout bloat, redirecting errors to stderr.", True))
opts.Add(BoolVariable("use_llvm", "Use the LLVM compiler (MVSC or MinGW depending on the use_mingw flag)", False))
opts.Add("mingw_prefix", "MinGW prefix", mingw)
def exists(env):
@@ -86,12 +90,22 @@ def generate(env):
if not env["use_mingw"] and msvc.exists(env):
if env["arch"] == "x86_64":
env["TARGET_ARCH"] = "amd64"
elif env["arch"] == "arm64":
env["TARGET_ARCH"] = "arm64"
elif env["arch"] == "arm32":
env["TARGET_ARCH"] = "arm"
elif env["arch"] == "x86_32":
env["TARGET_ARCH"] = "x86"
env["MSVC_SETUP_RUN"] = False # Need to set this to re-run the tool
env["MSVS_VERSION"] = None
env["MSVC_VERSION"] = None
env["is_msvc"] = True
# MSVC, linker, and archiver.
msvc.generate(env)
env.Tool("msvc")
env.Tool("mslib")
env.Tool("mslink")
@@ -99,7 +113,7 @@ def generate(env):
env.Append(CCFLAGS=["/utf-8"])
env.Append(LINKFLAGS=["/WX"])
if env["use_clang_cl"]:
if env["use_llvm"]:
env["CC"] = "clang-cl"
env["CXX"] = "clang-cl"
@@ -111,7 +125,12 @@ def generate(env):
if env["silence_msvc"] and not env.GetOption("clean"):
silence_msvc(env)
elif sys.platform == "win32" or sys.platform == "msys":
if not env["use_llvm"]:
env.AppendUnique(CCFLAGS=["/experimental:external", "/external:anglebrackets"])
env.AppendUnique(CCFLAGS=["/external:W0"])
env["EXTINCPREFIX"] = "/external:I"
elif (sys.platform == "win32" or sys.platform == "msys") and not env["mingw_prefix"]:
env["use_mingw"] = True
mingw.generate(env)
# Don't want lib prefixes
@@ -137,12 +156,32 @@ def generate(env):
else:
env["use_mingw"] = True
# Cross-compilation using MinGW
prefix = "i686" if env["arch"] == "x86_32" else env["arch"]
env["CXX"] = prefix + "-w64-mingw32-g++"
env["CC"] = prefix + "-w64-mingw32-gcc"
env["AR"] = prefix + "-w64-mingw32-ar"
env["RANLIB"] = prefix + "-w64-mingw32-ranlib"
env["LINK"] = prefix + "-w64-mingw32-g++"
prefix = ""
if env["mingw_prefix"]:
prefix = env["mingw_prefix"] + "/bin/"
if env["arch"] == "x86_64":
prefix += "x86_64"
elif env["arch"] == "arm64":
prefix += "aarch64"
elif env["arch"] == "arm32":
prefix += "armv7"
elif env["arch"] == "x86_32":
prefix += "i686"
if env["use_llvm"]:
env["CXX"] = prefix + "-w64-mingw32-clang++"
env["CC"] = prefix + "-w64-mingw32-clang"
env["AR"] = prefix + "-w64-mingw32-llvm-ar"
env["RANLIB"] = prefix + "-w64-mingw32-ranlib"
env["LINK"] = prefix + "-w64-mingw32-clang"
else:
env["CXX"] = prefix + "-w64-mingw32-g++"
env["CC"] = prefix + "-w64-mingw32-gcc"
env["AR"] = prefix + "-w64-mingw32-gcc-ar"
env["RANLIB"] = prefix + "-w64-mingw32-ranlib"
env["LINK"] = prefix + "-w64-mingw32-g++"
# Want dll suffix
env["SHLIBSUFFIX"] = ".dll"
@@ -156,7 +195,20 @@ def generate(env):
"-static-libstdc++",
]
)
if env["use_llvm"]:
env.Append(LINKFLAGS=["-lstdc++"])
if sys.platform == "win32" or sys.platform == "msys":
my_spawn.configure(env)
env.Append(CPPDEFINES=["WINDOWS_ENABLED"])
# Refer to https://github.com/godotengine/godot/blob/master/platform/windows/detect.py
if env["lto"] == "auto":
if env.get("is_msvc", False):
# No LTO by default for MSVC, doesn't help.
env["lto"] = "none"
else: # Release
env["lto"] = "full"
common_compiler_flags.generate(env)