Просмотр исходного кода

Merge pull request #1281 from dsnopek/4.1-cherrypicks-5

Cherry-picks for the godot-cpp 4.1 branch - 5th batch
Rémi Verschelde 1 год назад
Родитель
Сommit
04b34077d8

+ 5 - 5
.github/workflows/ci.yml

@@ -80,11 +80,11 @@ jobs:
 
           - name: 🌐 Web (wasm32)
             os: ubuntu-20.04
-            platform: javascript
-            artifact-name: godot-cpp-javascript-wasm32-release
-            artifact-path: bin/libgodot-cpp.javascript.template_release.wasm32.a
+            platform: web
+            artifact-name: godot-cpp-web-wasm32-release
+            artifact-path: bin/libgodot-cpp.web.template_release.wasm32.a
             run-tests: false
-            cache-name: javascript-wasm32
+            cache-name: web-wasm32
 
     env:
       SCONS_CACHE: ${{ github.workspace }}/.scons-cache/
@@ -115,7 +115,7 @@ jobs:
           sudo apt-get install -qqq build-essential pkg-config
 
       - name: Web dependencies
-        if: ${{ matrix.platform == 'javascript' }}
+        if: ${{ matrix.platform == 'web' }}
         uses: mymindstorm/setup-emsdk@v12
         with:
           version: ${{env.EM_VERSION}}

+ 21 - 5
CMakeLists.txt

@@ -72,21 +72,22 @@ 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(FLOAT_PRECISION "single" CACHE STRING "")
-if ("${FLOAT_PRECISION}" STREQUAL "double")
-	add_definitions(-DREAL_T_IS_DOUBLE)
-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()
 
+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 "/EHsc /utf-8") # /GF /MP
+	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
@@ -107,6 +108,21 @@ else()  # GCC/Clang
 	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 OFF "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()
+
 # Generate source from the bindings file
 find_package(Python3 3.4 REQUIRED) # pathlib should be present
 if(GENERATE_TEMPLATE_GET_NODE)

+ 1 - 63
binding_generator.py

@@ -130,8 +130,6 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False):
     if sources:
         utility_functions_source_path = source_gen_folder / "variant" / "utility_functions.cpp"
         files.append(str(utility_functions_source_path.as_posix()))
-        register_engine_classes_source_path = source_gen_folder / "register_engine_classes.cpp"
-        files.append(str(register_engine_classes_source_path.as_posix()))
 
     return files
 
@@ -1170,10 +1168,6 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
                 generate_engine_class_source(class_api, used_classes, fully_used_classes, use_template_get_node)
             )
 
-    register_engine_classes_filename = Path(output_dir) / "src" / "register_engine_classes.cpp"
-    with register_engine_classes_filename.open("w+", encoding="utf-8") as source_file:
-        source_file.write(generate_register_engine_classes_source(api))
-
     for native_struct in api["native_structures"]:
         struct_name = native_struct["name"]
         snake_struct_name = camel_to_snake(struct_name)
@@ -1248,7 +1242,7 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
             result.append(f"#include <godot_cpp/{get_include_path(included)}>")
 
     if class_name == "EditorPlugin":
-        result.append("#include <godot_cpp/templates/vector.hpp>")
+        result.append("#include <godot_cpp/classes/editor_plugin_registration.hpp>")
 
     if len(fully_used_classes) > 0:
         result.append("")
@@ -1400,30 +1394,6 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
     result.append("};")
     result.append("")
 
-    if class_name == "EditorPlugin":
-        result.append("class EditorPlugins {")
-        result.append("private:")
-        result.append("\tstatic Vector<StringName> plugin_classes;")
-        result.append("")
-        result.append("public:")
-        result.append("\tstatic void add_plugin_class(const StringName &p_class_name);")
-        result.append("\tstatic void remove_plugin_class(const StringName &p_class_name);")
-        result.append("\tstatic void deinitialize(GDExtensionInitializationLevel p_level);")
-        result.append("")
-
-        result.append("\ttemplate <class T>")
-        result.append("\tstatic void add_by_type() {")
-        result.append("\t\tadd_plugin_class(T::get_class_static());")
-        result.append("\t}")
-
-        result.append("\ttemplate <class T>")
-        result.append("\tstatic void remove_by_type() {")
-        result.append("\t\tremove_plugin_class(T::get_class_static());")
-        result.append("\t}")
-
-        result.append("};")
-        result.append("")
-
     result.append("} // namespace godot")
     result.append("")
 
@@ -1648,38 +1618,6 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
     return "\n".join(result)
 
 
-def generate_register_engine_classes_source(api):
-    includes = []
-    registrations = []
-
-    for class_api in api["classes"]:
-        if class_api["name"] == "ClassDB":
-            continue
-
-        class_name = class_api["name"]
-        snake_class_name = camel_to_snake(class_name)
-
-        includes.append(f"#include <godot_cpp/classes/{snake_class_name}.hpp>")
-        registrations.append(f"\tClassDB::register_engine_class<{class_name}>();")
-
-    result = []
-    add_header(f"register_engine_classes.cpp", result)
-
-    result.append("#include <godot_cpp/godot.hpp>")
-    result.append("")
-    result = result + includes
-    result.append("")
-    result.append("namespace godot {")
-    result.append("")
-    result.append("void GDExtensionBinding::register_engine_classes() {")
-    result = result + registrations
-    result.append("}")
-    result.append("")
-    result.append("} // namespace godot ")
-
-    return "\n".join(result)
-
-
 def generate_global_constants(api, output_dir):
     include_gen_folder = Path(output_dir) / "include" / "godot_cpp" / "classes"
     source_gen_folder = Path(output_dir) / "src" / "classes"

+ 62 - 0
include/godot_cpp/classes/editor_plugin_registration.hpp

@@ -0,0 +1,62 @@
+/**************************************************************************/
+/*  editor_plugin_registration.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_EDITOR_PLUGIN_REGISTRATION_HPP
+#define GODOT_EDITOR_PLUGIN_REGISTRATION_HPP
+
+#include <godot_cpp/templates/vector.hpp>
+
+namespace godot {
+
+class EditorPlugin;
+class StringName;
+
+class EditorPlugins {
+private:
+	static Vector<StringName> plugin_classes;
+
+public:
+	static void add_plugin_class(const StringName &p_class_name);
+	static void remove_plugin_class(const StringName &p_class_name);
+	static void deinitialize(GDExtensionInitializationLevel p_level);
+
+	template <class T>
+	static void add_by_type() {
+		add_plugin_class(T::get_class_static());
+	}
+	template <class T>
+	static void remove_by_type() {
+		remove_plugin_class(T::get_class_static());
+	}
+};
+
+} // namespace godot
+
+#endif // GODOT_EDITOR_PLUGIN_REGISTRATION_HPP

+ 36 - 0
include/godot_cpp/classes/wrapped.hpp

@@ -95,6 +95,26 @@ public:
 	GodotObject *_owner = nullptr;
 };
 
+namespace internal {
+
+typedef void (*EngineClassRegistrationCallback)();
+void add_engine_class_registration_callback(EngineClassRegistrationCallback p_callback);
+void register_engine_class(const StringName &p_name, const GDExtensionInstanceBindingCallbacks *p_callbacks);
+void register_engine_classes();
+
+template <class T>
+struct EngineClassRegistration {
+	EngineClassRegistration() {
+		add_engine_class_registration_callback(&EngineClassRegistration<T>::callback);
+	}
+
+	static void callback() {
+		register_engine_class(T::get_class_static(), &T::_gde_binding_callbacks);
+	}
+};
+
+} // namespace internal
+
 } // namespace godot
 
 #define GDCLASS(m_class, m_inherits)                                                                                                                                                   \
@@ -150,6 +170,8 @@ protected:
 	}                                                                                                                                                                                  \
                                                                                                                                                                                        \
 public:                                                                                                                                                                                \
+	typedef m_class self_type;                                                                                                                                                         \
+                                                                                                                                                                                       \
 	static void initialize_class() {                                                                                                                                                   \
 		static bool initialized = false;                                                                                                                                               \
 		if (initialized) {                                                                                                                                                             \
@@ -308,6 +330,7 @@ public:
 // Don't use this for your classes, use GDCLASS() instead.
 #define GDEXTENSION_CLASS_ALIAS(m_class, m_alias_for, m_inherits)                                                          \
 private:                                                                                                                   \
+	inline static ::godot::internal::EngineClassRegistration<m_class> _gde_engine_class_registration_helper;               \
 	void operator=(const m_class &p_rval) {}                                                                               \
                                                                                                                            \
 protected:                                                                                                                 \
@@ -351,6 +374,8 @@ protected:
 	}                                                                                                                      \
                                                                                                                            \
 public:                                                                                                                    \
+	typedef m_class self_type;                                                                                             \
+                                                                                                                           \
 	static void initialize_class() {}                                                                                      \
                                                                                                                            \
 	static ::godot::StringName &get_class_static() {                                                                       \
@@ -360,6 +385,17 @@ public:
                                                                                                                            \
 	static ::godot::StringName &get_parent_class_static() {                                                                \
 		return m_inherits::get_class_static();                                                                             \
+	}                                                                                                                      \
+                                                                                                                           \
+	static GDExtensionObjectPtr create(void *data) {                                                                       \
+		return nullptr;                                                                                                    \
+	}                                                                                                                      \
+                                                                                                                           \
+	static GDExtensionClassInstancePtr recreate(void *data, GDExtensionObjectPtr obj) {                                    \
+		return nullptr;                                                                                                    \
+	}                                                                                                                      \
+                                                                                                                           \
+	static void free(void *data, GDExtensionClassInstancePtr ptr) {                                                        \
 	}                                                                                                                      \
                                                                                                                            \
 	static void *_gde_binding_create_callback(void *p_token, void *p_instance) {                                           \

+ 5 - 7
include/godot_cpp/core/class_db.hpp

@@ -122,8 +122,10 @@ public:
 	static void register_class(bool p_virtual = false);
 	template <class T>
 	static void register_abstract_class();
-	template <class T>
-	static void register_engine_class();
+
+	_FORCE_INLINE_ static void _register_engine_class(const StringName &p_name, const GDExtensionInstanceBindingCallbacks *p_callbacks) {
+		instance_binding_callbacks[p_name] = p_callbacks;
+	}
 
 	template <class N, class M, typename... VarArgs>
 	static MethodBind *bind_method(N p_method_name, M p_method, VarArgs... p_args);
@@ -171,6 +173,7 @@ public:
 
 template <class T, bool is_abstract>
 void ClassDB::_register_class(bool p_virtual) {
+	static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
 	instance_binding_callbacks[T::get_class_static()] = &T::_gde_binding_callbacks;
 
 	// Register this class within our plugin
@@ -226,11 +229,6 @@ void ClassDB::register_abstract_class() {
 	ClassDB::_register_class<T, true>();
 }
 
-template <class T>
-void ClassDB::register_engine_class() {
-	instance_binding_callbacks[T::get_class_static()] = &T::_gde_binding_callbacks;
-}
-
 template <class N, class M, typename... VarArgs>
 MethodBind *ClassDB::bind_method(N p_method_name, M p_method, VarArgs... p_args) {
 	Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.

+ 0 - 3
include/godot_cpp/godot.hpp

@@ -191,9 +191,6 @@ enum ModuleInitializationLevel {
 };
 
 class GDExtensionBinding {
-private:
-	static void register_engine_classes();
-
 public:
 	using Callback = void (*)(ModuleInitializationLevel p_level);
 

+ 12 - 0
include/godot_cpp/variant/char_string.hpp

@@ -120,6 +120,18 @@ protected:
 	void copy_from(const T *p_cstr);
 };
 
+template <>
+const char *CharStringT<char>::get_data() const;
+
+template <>
+const char16_t *CharStringT<char16_t>::get_data() const;
+
+template <>
+const char32_t *CharStringT<char32_t>::get_data() const;
+
+template <>
+const wchar_t *CharStringT<wchar_t>::get_data() const;
+
 typedef CharStringT<char> CharString;
 typedef CharStringT<char16_t> Char16String;
 typedef CharStringT<char32_t> Char32String;

+ 3 - 3
src/classes/editor_plugin.cpp → src/classes/editor_plugin_registration.cpp

@@ -1,5 +1,5 @@
 /**************************************************************************/
-/*  editor_plugin.cpp                                                     */
+/*  editor_plugin_registration.cpp                                        */
 /**************************************************************************/
 /*                         This file is part of:                          */
 /*                             GODOT ENGINE                               */
@@ -28,9 +28,9 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 /**************************************************************************/
 
-#include <godot_cpp/classes/editor_plugin.hpp>
+#include <godot_cpp/classes/editor_plugin_registration.hpp>
 
-#include <godot_cpp/variant/string_name.hpp>
+#include <godot_cpp/variant/variant.hpp>
 
 namespace godot {
 

+ 29 - 0
src/classes/wrapped.cpp

@@ -28,12 +28,16 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 /**************************************************************************/
 
+#include <vector>
+
 #include <godot_cpp/classes/wrapped.hpp>
 
 #include <godot_cpp/variant/builtin_types.hpp>
 
 #include <godot_cpp/classes/object.hpp>
 
+#include <godot_cpp/core/class_db.hpp>
+
 namespace godot {
 
 const StringName *Wrapped::_get_extension_class_name() const {
@@ -60,4 +64,29 @@ void postinitialize_handler(Wrapped *p_wrapped) {
 	p_wrapped->_postinitialize();
 }
 
+namespace internal {
+
+std::vector<EngineClassRegistrationCallback> &get_engine_class_registration_callbacks() {
+	static std::vector<EngineClassRegistrationCallback> engine_class_registration_callbacks;
+	return engine_class_registration_callbacks;
+}
+
+void add_engine_class_registration_callback(EngineClassRegistrationCallback p_callback) {
+	get_engine_class_registration_callbacks().push_back(p_callback);
+}
+
+void register_engine_class(const StringName &p_name, const GDExtensionInstanceBindingCallbacks *p_callbacks) {
+	ClassDB::_register_engine_class(p_name, p_callbacks);
+}
+
+void register_engine_classes() {
+	std::vector<EngineClassRegistrationCallback> &engine_class_registration_callbacks = get_engine_class_registration_callbacks();
+	for (EngineClassRegistrationCallback cb : engine_class_registration_callbacks) {
+		cb();
+	}
+	engine_class_registration_callbacks.clear();
+}
+
+} // namespace internal
+
 } // namespace godot

+ 2 - 2
src/godot.cpp

@@ -30,7 +30,7 @@
 
 #include <godot_cpp/godot.hpp>
 
-#include <godot_cpp/classes/editor_plugin.hpp>
+#include <godot_cpp/classes/editor_plugin_registration.hpp>
 #include <godot_cpp/classes/wrapped.hpp>
 #include <godot_cpp/core/class_db.hpp>
 #include <godot_cpp/core/memory.hpp>
@@ -406,7 +406,7 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge
 	ERR_FAIL_NULL_V_MSG(init_callback, false, "Initialization callback must be defined.");
 
 	Variant::init_bindings();
-	register_engine_classes();
+	godot::internal::register_engine_classes();
 
 	return true;
 }

+ 6 - 4
src/variant/variant.cpp

@@ -638,14 +638,16 @@ bool Variant::in(const Variant &index, bool *r_valid) const {
 
 bool Variant::iter_init(Variant &r_iter, bool &r_valid) const {
 	GDExtensionBool valid;
-	internal::gdextension_interface_variant_iter_init(_native_ptr(), r_iter._native_ptr(), &valid);
-	return PtrToArg<bool>::convert(&valid);
+	GDExtensionBool result = internal::gdextension_interface_variant_iter_init(_native_ptr(), r_iter._native_ptr(), &valid);
+	r_valid = PtrToArg<bool>::convert(&valid);
+	return PtrToArg<bool>::convert(&result);
 }
 
 bool Variant::iter_next(Variant &r_iter, bool &r_valid) const {
 	GDExtensionBool valid;
-	internal::gdextension_interface_variant_iter_next(_native_ptr(), r_iter._native_ptr(), &valid);
-	return PtrToArg<bool>::convert(&valid);
+	GDExtensionBool result = internal::gdextension_interface_variant_iter_next(_native_ptr(), r_iter._native_ptr(), &valid);
+	r_valid = PtrToArg<bool>::convert(&valid);
+	return PtrToArg<bool>::convert(&result);
 }
 
 Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const {

+ 16 - 1
test/CMakeLists.txt

@@ -36,7 +36,7 @@ set(GODOT_LINKER_FLAGS )
 
 if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
 	# using Visual Studio C++
-	set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /EHsc /WX") # /GF /MP
+	set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /WX") # /GF /MP
 	set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /DTYPED_METHOD_BIND")
 
 	if(CMAKE_BUILD_TYPE MATCHES Debug)
@@ -92,6 +92,21 @@ else()
 	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 OFF "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()
+
 # Get Sources
 file(GLOB_RECURSE SOURCES src/*.c**)
 file(GLOB_RECURSE HEADERS include/*.h**)

+ 2 - 2
test/project/example.gdextension

@@ -21,5 +21,5 @@ android.debug.x86_64 = "res://bin/libgdexample.android.template_debug.x86_64.so"
 android.release.x86_64 = "res://bin/libgdexample.android.template_release.x86_64.so"
 android.debug.arm64 = "res://bin/libgdexample.android.template_debug.arm64.so"
 android.release.arm64 = "res://bin/libgdexample.android.template_release.arm64.so"
-web.debug.wasm32 = "res://bin/libgdexample.javascript.template_debug.wasm32.wasm"
-web.release.wasm32 = "res://bin/libgdexample.javascript.template_release.wasm32.wasm"
+web.debug.wasm32 = "res://bin/libgdexample.web.template_debug.wasm32.wasm"
+web.release.wasm32 = "res://bin/libgdexample.web.template_release.wasm32.wasm"

+ 30 - 7
tools/godotcpp.py

@@ -33,7 +33,7 @@ def validate_parent_dir(key, val, env):
         raise UserError("'%s' is not a directory: %s" % (key, os.path.dirname(val)))
 
 
-platforms = ("linux", "macos", "windows", "android", "ios", "javascript")
+platforms = ("linux", "macos", "windows", "android", "ios", "web")
 
 # CPU architecture options.
 architecture_array = [
@@ -175,6 +175,14 @@ def options(opts, env):
         )
     )
 
+    opts.Add(
+        BoolVariable(
+            "disable_exceptions",
+            "Force disabling exception handling code",
+            default=env.get("disable_exceptions", False),
+        )
+    )
+
     # Add platform options
     for pl in platforms:
         tool = Tool(pl, toolpath=["tools"])
@@ -214,7 +222,7 @@ def generate(env):
             env["arch"] = "universal"
         elif env["platform"] == "android":
             env["arch"] = "arm64"
-        elif env["platform"] == "javascript":
+        elif env["platform"] == "web":
             env["arch"] = "wasm32"
         else:
             host_machine = platform.machine().lower()
@@ -231,6 +239,16 @@ def generate(env):
 
     print("Building for architecture " + env["arch"] + " on platform " + env["platform"])
 
+    # Disable exception handling. Godot doesn't use exceptions anywhere, and this
+    # saves around 20% of binary size and very significant build time.
+    if env["disable_exceptions"]:
+        if env.get("is_msvc", False):
+            env.Append(CPPDEFINES=[("_HAS_EXCEPTIONS", 0)])
+        else:
+            env.Append(CXXFLAGS=["-fno-exceptions"])
+    elif env.get("is_msvc", False):
+        env.Append(CXXFLAGS=["/EHsc"])
+
     tool = Tool(env["platform"], toolpath=["tools"])
 
     if tool is None or not tool.exists(env):
@@ -263,9 +281,8 @@ def generate(env):
     env["OBJSUFFIX"] = suffix + env["OBJSUFFIX"]
 
     # compile_commands.json
-    if env.get("compiledb", False):
-        env.Tool("compilation_db")
-        env.Alias("compiledb", env.CompilationDatabase(normalize_path(env["compiledb_file"], env)))
+    env.Tool("compilation_db")
+    env.Alias("compiledb", env.CompilationDatabase(normalize_path(env["compiledb_file"], env)))
 
     # Builders
     env.Append(BUILDERS={"GodotCPPBindings": Builder(action=scons_generate_bindings, emitter=scons_emit_files)})
@@ -273,8 +290,8 @@ def generate(env):
 
 
 def _godot_cpp(env):
-    api_file = normalize_path(env.get("custom_api_file", env.File("gdextension/extension_api.json").abspath), env)
     extension_dir = normalize_path(env.get("gdextension_dir", env.Dir("gdextension").abspath), env)
+    api_file = normalize_path(env.get("custom_api_file", env.File(extension_dir + "/extension_api.json").abspath), env)
     bindings = env.GodotCPPBindings(
         env.Dir("."),
         [
@@ -304,7 +321,13 @@ def _godot_cpp(env):
 
     if env["build_library"]:
         library = env.StaticLibrary(target=env.File("bin/%s" % library_name), source=sources)
-        env.Default(library)
+        default_args = [library]
+
+        # Add compiledb if the option is set
+        if env.get("compiledb", False):
+            default_args += ["compiledb"]
+
+        env.Default(*default_args)
 
     env.AppendUnique(LIBS=[env.File("bin/%s" % library_name)])
     return library

+ 16 - 16
tools/javascript.py → tools/web.py

@@ -11,34 +11,34 @@ def generate(env):
         print("Only wasm32 supported on web. Exiting.")
         env.Exit(1)
 
+    # Emscripten toolchain
     env["CC"] = "emcc"
     env["CXX"] = "em++"
     env["AR"] = "emar"
     env["RANLIB"] = "emranlib"
-    env.Append(CPPFLAGS=["-s", "SIDE_MODULE=1"])
-    env.Append(LINKFLAGS=["-s", "SIDE_MODULE=1"])
-    env["SHOBJSUFFIX"] = ".bc"
-    env["SHLIBSUFFIX"] = ".wasm"
+
     # Use TempFileMunge since some AR invocations are too long for cmd.exe.
     # Use POSIX-style paths, required with TempFileMunge.
     env["ARCOM_POSIX"] = env["ARCOM"].replace("$TARGET", "$TARGET.posix").replace("$SOURCES", "$SOURCES.posix")
     env["ARCOM"] = "${TEMPFILE(ARCOM_POSIX)}"
 
+    # All intermediate files are just object files.
+    env["OBJSUFFIX"] = ".o"
+    env["SHOBJSUFFIX"] = ".o"
+
+    # Static libraries clang-style.
+    env["LIBPREFIX"] = "lib"
+    env["LIBSUFFIX"] = ".a"
+
+    # Shared library as wasm.
+    env["SHLIBSUFFIX"] = ".wasm"
+
     # Thread support (via SharedArrayBuffer).
     env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"])
     env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"])
 
-    # All intermediate files are just LLVM bitcode.
-    env["OBJPREFIX"] = ""
-    env["OBJSUFFIX"] = ".bc"
-    env["PROGPREFIX"] = ""
-    # Program() output consists of multiple files, so specify suffixes manually at builder.
-    env["PROGSUFFIX"] = ""
-    env["LIBPREFIX"] = "lib"
-    env["LIBSUFFIX"] = ".a"
-    env["LIBPREFIXES"] = ["$LIBPREFIX"]
-    env["LIBSUFFIXES"] = ["$LIBSUFFIX"]
-    env.Replace(SHLINKFLAGS="$LINKFLAGS")
-    env.Replace(SHLINKFLAGS="$LINKFLAGS")
+    # Build as side module (shared library).
+    env.Append(CPPFLAGS=["-s", "SIDE_MODULE=1"])
+    env.Append(LINKFLAGS=["-s", "SIDE_MODULE=1"])
 
     env.Append(CPPDEFINES=["WEB_ENABLED", "UNIX_ENABLED"])

+ 1 - 1
tools/windows.py

@@ -31,7 +31,7 @@ def generate(env):
         env.Tool("mslink")
 
         env.Append(CPPDEFINES=["TYPED_METHOD_BIND", "NOMINMAX"])
-        env.Append(CCFLAGS=["/EHsc", "/utf-8"])
+        env.Append(CCFLAGS=["/utf-8"])
         env.Append(LINKFLAGS=["/WX"])
 
         if env["use_clang_cl"]: