Skip to content

Merge commit godotengine/godot-cpp@714c9e2 #18

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 48 commits into from
Mar 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
b17e668
Bump actions/upload-artifact from 3 to 4
dependabot[bot] Dec 14, 2023
7f47d39
fix typed_dictionary compile-time regression
Oct 21, 2024
6f7293c
Alleviate CMake target name clashes, visibility, and grouping.
enetheru Dec 11, 2024
c4f1abe
[Bindings] Build profile now strips methods and skip files
Faless Dec 20, 2024
0cfe01e
[CI] Re-add generated files consistency check
Faless Dec 30, 2024
7d3870b
Merge pull request #1680 from Faless/build/profile_strip_json
dsnopek Jan 7, 2025
012b8ff
Merge pull request #1658 from enetheru/name_clash
dsnopek Jan 10, 2025
ae198fe
CMake: Support using build_profile.json
enetheru Jan 11, 2025
5c9529f
Update for virtual method compatibility system
dsnopek Dec 20, 2024
65046d0
Merge pull request #1676 from dsnopek/gdextension-virtual-method-compat
dsnopek Jan 12, 2025
94a1f4f
Merge pull request #1670 from enetheru/build_profile
dsnopek Jan 12, 2025
8814ac5
CMake: Support for XML documentation
enetheru Jan 12, 2025
befe3ee
Merge pull request #1682 from enetheru/gdext-docs-cmake
dsnopek Jan 13, 2025
bd3cf47
Update README.md with new pre-commit instructions
bgie Jan 20, 2025
7576dc5
Fix buffer overrun with enums pointers cast to int64_t* when enum is …
bgie Jan 20, 2025
dfc5196
gdextension: Sync with upstream commit d33da79d3f8fe84be2521d25b9ba8e…
dsnopek Jan 20, 2025
faf6fac
Merge pull request #1686 from bgie/readme_precommit
dsnopek Jan 21, 2025
b86cf32
Merge pull request #1687 from bgie/enum_size
dsnopek Jan 21, 2025
13cd2d9
Merge pull request #1628 from IvanInventor/typed_dict_regression_fix
dsnopek Jan 21, 2025
9ce7a71
CMake: Fix #1690 - DEBUG_FEATURES generator expression
enetheru Jan 23, 2025
3c55ca7
Merge pull request #1691 from enetheru/fix1690
dsnopek Jan 29, 2025
f06af65
gdextension: Sync with upstream commit a013481b0911e59cc3f3dea7ebb732…
dsnopek Feb 2, 2025
ee2a895
Merge pull request #1335 from godotengine/dependabot/github_actions/a…
dsnopek Feb 3, 2025
91f81f5
CMake: Implement 'threads' Option
enetheru Jan 31, 2025
560f786
Merge pull request #1698 from enetheru/threads
dsnopek Feb 7, 2025
f398ebb
gdextension: Sync with upstream commit 06acfccf89ad6b900ae694a4d58cea…
dsnopek Feb 8, 2025
9d9099a
Simplified architecture naming to reduce confusion
enetheru Feb 9, 2025
35469fd
Turn python_callouts.cmake into GodotCPPModule.cmake
enetheru Feb 9, 2025
18a926e
CMake: Fix for #1699 msvc runtime selection issues
enetheru Feb 5, 2025
847dca4
Merge pull request #1701 from enetheru/msvc_runtime
dsnopek Feb 17, 2025
3f54a86
Merge pull request #1707 from enetheru/cmake_module
dsnopek Feb 17, 2025
79f9bc9
Merge pull request #1708 from enetheru/arch_confusion
dsnopek Feb 17, 2025
23c2407
CMake: Create destination folder for doc_source.cpp generation
enetheru Jan 25, 2025
5eb6e6b
Merge pull request #1709 from enetheru/MinGWMakefiles
dsnopek Feb 17, 2025
89fd276
Style: Replace `_NO_DISCARD_` macro with `[[nodiscard]]`
Repiteo Feb 20, 2025
48baa0c
Merge pull request #1713 from Repiteo/style/nodiscard
dsnopek Feb 21, 2025
98ea2f6
gdextension: Sync with upstream commit 8ed125b42908d0d46d3b8967e3a3bc…
dsnopek Feb 24, 2025
e7f07da
CMake: Add generate_bindings custom target
enetheru Feb 26, 2025
375c0d1
gdextension: Sync with upstream commit 01545c995b0612c68f9dfce8f6cc67…
dsnopek Feb 26, 2025
4a9409a
Update `GDVIRTUAL*()` macros to match Godot 4.4
dsnopek Feb 25, 2025
c8c25cd
Merge pull request #1720 from enetheru/generator_target
dsnopek Feb 27, 2025
d0dd282
CMake: Rename all GODOT_ to GODOTCPP_
enetheru Feb 26, 2025
e381658
gdextension: Sync with upstream commit 15ff450680a40391aabbffde0a57ea…
dsnopek Mar 1, 2025
26358b5
Merge pull request #1719 from dsnopek/gdvirtual-required-macros
dsnopek Mar 1, 2025
06082d7
Merge pull request #1721 from enetheru/godotcpp
dsnopek Mar 1, 2025
714c9e2
gdextension: Sync with upstream commit 4c311cbee68c0b66ff8ebb8b0defdd…
dsnopek Mar 3, 2025
74feb15
Merge commit godotengine/godot-cpp@714c9e2c165db2dcb7e6ea57e62a04204d…
Spartan322 Mar 8, 2025
eb2aa8e
Sync with commit Redot-Engine/redot-engine@f178ebc70f2e1d34965e67fdf0…
Spartan322 Mar 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,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 }}
Expand All @@ -202,8 +202,8 @@ jobs:
run: |
mkdir cmake-build
cd cmake-build
cmake ../ -DTEST_TARGET=template_release
cmake --build . --verbose -j $(nproc) -t godot-cpp-test --config Release
cmake ../ -DGODOTCPP_ENABLE_TESTING=YES
cmake --build . --verbose -j $(nproc) -t godot-cpp.test.template_release --config Release

windows-msvc-cmake:
name: 🏁 Build (Windows, MSVC, CMake)
Expand All @@ -218,5 +218,5 @@ jobs:
run: |
mkdir cmake-build
cd cmake-build
cmake ../ -DTEST_TARGET=template_release
cmake --build . --verbose -t godot-cpp-test --config Release
cmake ../ -DGODOTCPP_ENABLE_TESTING=YES
cmake --build . --verbose -t godot-cpp.test.template_release --config Release
4 changes: 4 additions & 0 deletions .github/workflows/static_checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ jobs:
uses: pre-commit/[email protected]
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
15 changes: 10 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,8 @@ The CMake equivalent is below.
]=======================================================================]

include( cmake/godotcpp.cmake )
godotcpp_options()

#[[ Python is required for code generation ]]
find_package(Python3 3.4 REQUIRED) # pathlib should be present
godotcpp_options()

# Define our project.
project( godot-cpp
Expand All @@ -53,5 +51,12 @@ project( godot-cpp
compiler_detection()
godotcpp_generate()

# Test Example
add_subdirectory( test )
# Conditionally enable the godot-cpp.test.<target> integration testing targets
if( GODOTCPP_ENABLE_TESTING )
add_subdirectory( test )
endif()

# If this is the top level CMakeLists.txt, Generators which honor the
# USE_FOLDERS flag will organize godot-cpp targets under the subfolder
# 'godot-cpp'. This is enable by default from CMake version 3.26
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,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.redotengine.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

Expand Down
186 changes: 41 additions & 145 deletions binding_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,9 @@ def generate_wrappers(target):
f.write(txt)


def generate_virtual_version(argcount, const=False, returns=False):
def generate_virtual_version(argcount, const=False, returns=False, required=False):
s = """#define GDVIRTUAL$VER($RET m_name $ARG)\\
::godot::StringName _gdvirtual_##m_name##_sn = #m_name;\\
template <bool required>\\
_FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST {\\
if (::godot::internal::gdextension_interface_object_has_script_method(_owner, &_gdvirtual_##m_name##_sn)) { \\
GDExtensionCallError ce;\\
Expand All @@ -85,10 +84,8 @@ def generate_virtual_version(argcount, const=False, returns=False):
return true;\\
}\\
}\\
if (required) {\\
ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");\\
$RVOID\\
}\\
$REQCHECK\\
$RVOID\\
return false;\\
}\\
_FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const {\\
Expand All @@ -106,6 +103,7 @@ def generate_virtual_version(argcount, const=False, returns=False):

sproto = str(argcount)
method_info = ""
method_flags = "METHOD_FLAG_VIRTUAL"
if returns:
sproto += "R"
s = s.replace("$RET", "m_ret,")
Expand All @@ -114,16 +112,26 @@ def generate_virtual_version(argcount, const=False, returns=False):
method_info += "\t\tmethod_info.return_val_metadata = ::godot::GetTypeInfo<m_ret>::METADATA;"
else:
s = s.replace("$RET ", "")
s = s.replace("\t\t\t$RVOID\\\n", "")
s = s.replace("\t\t$RVOID\\\n", "")

if const:
sproto += "C"
method_flags += " | METHOD_FLAG_CONST"
s = s.replace("$CONST", "const")
s = s.replace("$METHOD_FLAGS", "::godot::METHOD_FLAG_VIRTUAL | ::godot::METHOD_FLAG_CONST")
else:
s = s.replace("$CONST ", "")
s = s.replace("$METHOD_FLAGS", "::godot::METHOD_FLAG_VIRTUAL")

if required:
sproto += "_REQUIRED"
method_flags += " | METHOD_FLAG_VIRTUAL_REQUIRED"
s = s.replace(
"$REQCHECK",
'ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");',
)
else:
s = s.replace("\t\t$REQCHECK\\\n", "")

s = s.replace("$METHOD_FLAGS", method_flags)
s = s.replace("$VER", sproto)
argtext = ""
callargtext = ""
Expand Down Expand Up @@ -190,20 +198,27 @@ def generate_virtuals(target):
txt += generate_virtual_version(i, False, True)
txt += generate_virtual_version(i, True, False)
txt += generate_virtual_version(i, True, True)
txt += generate_virtual_version(i, False, False, True)
txt += generate_virtual_version(i, False, True, True)
txt += generate_virtual_version(i, True, False, True)
txt += generate_virtual_version(i, True, True, True)

txt += "#endif // GDEXTENSION_GDVIRTUAL_GEN_H\n"

with open(target, "w", encoding="utf-8") as f:
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"
Expand Down Expand Up @@ -235,7 +250,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"]:
Expand Down Expand Up @@ -267,128 +282,19 @@ 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

target_dir = Path(output_dir) / "gen"

api = {}
with open(api_filepath, encoding="utf-8") as api_file:
api = json.load(api_file)
_generate_bindings(api, use_template_get_node, bits, precision, output_dir)


def _generate_bindings(api, use_template_get_node, bits="64", precision="single", output_dir="."):
target_dir = Path(output_dir) / "gen"

shutil.rmtree(target_dir, ignore_errors=True)
target_dir.mkdir(parents=True)
Expand Down Expand Up @@ -1788,7 +1694,7 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
# condition returns false (in such cases it can't compile due to ambiguity).
f"\t\tif constexpr (!std::is_same_v<decltype(&B::{method_name}), decltype(&T::{method_name})>) {{"
)
result.append(f"\t\t\tBIND_VIRTUAL_METHOD(T, {method_name});")
result.append(f"\t\t\tBIND_VIRTUAL_METHOD(T, {method_name}, {method['hash']});")
result.append("\t\t}")

result.append("\t}")
Expand Down Expand Up @@ -2457,6 +2363,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.
Expand Down Expand Up @@ -2767,20 +2677,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.
Expand Down
Loading