Browse Source

Merge pull request #1373 from dsnopek/4.1-cherrypicks-7

Cherry-picks for the godot-cpp 4.1 branch - 7th batch
David Snopek 1 year ago
parent
commit
4b63d795e4

+ 14 - 13
.github/workflows/ci.yml

@@ -65,7 +65,7 @@ jobs:
             platform: android
             artifact-name: godot-cpp-android-arm64-release
             artifact-path: bin/libgodot-cpp.android.template_release.arm64.a
-            flags: ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME arch=arm64
+            flags: arch=arm64
             run-tests: false
             cache-name: android-arm64
 
@@ -88,7 +88,7 @@ jobs:
 
     env:
       SCONS_CACHE: ${{ github.workspace }}/.scons-cache/
-      EM_VERSION: 3.1.45
+      EM_VERSION: 3.1.39
       EM_CACHE_FOLDER: "emsdk-cache"
 
     steps:
@@ -104,33 +104,34 @@ jobs:
         continue-on-error: true
 
       - name: Set up Python (for SCons)
-        uses: actions/setup-python@v4
+        uses: actions/setup-python@v5
         with:
           python-version: '3.x'
 
-      - name: Linux dependencies
-        if: ${{ matrix.platform == 'linux' }}
-        run: |
-          sudo apt-get update -qq
-          sudo apt-get install -qqq build-essential pkg-config
+      - 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@v12
+        uses: mymindstorm/setup-emsdk@v13
         with:
           version: ${{env.EM_VERSION}}
           actions-cache-folder: ${{env.EM_CACHE_FOLDER}}
 
-      - name: Install scons
-        run: |
-          python -m pip install scons==4.0.0
-
       - 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
+
       - name: Generate godot-cpp sources only
         run: |
           scons platform=${{ matrix.platform }} build_library=no ${{ matrix.flags }}

+ 2 - 14
CMakeLists.txt

@@ -47,11 +47,6 @@ 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/" )
 
-# Check if we are building ourself or being included
-if(${PROJECT_NAME} STREQUAL ${CMAKE_PROJECT_NAME})
-    set(GODOT_CPP_BUILDING_SELF ON)
-endif()
-
 # 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>" )
@@ -99,10 +94,8 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
 
 	add_definitions(-DNOMINMAX)
 else()  # GCC/Clang
-	set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -g")
-
 	if(CMAKE_BUILD_TYPE MATCHES Debug)
-		set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-omit-frame-pointer -O0")
+		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)
@@ -134,6 +127,7 @@ 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}
@@ -159,12 +153,6 @@ add_library(godot::cpp ALIAS ${PROJECT_NAME})
 
 include(GodotCompilerWarnings)
 
-# Treat warnings as errors if we are building ourself
-if(GODOT_CPP_BUILDING_SELF)
-    unset( GODOT_CPP_WARNING_AS_ERROR CACHE )
-    set_warning_as_error()
-endif()
-
 target_compile_features(${PROJECT_NAME}
 	PRIVATE
 		cxx_std_17

+ 5 - 2
README.md

@@ -57,7 +57,7 @@ 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/issues)
+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.
 
@@ -73,7 +73,10 @@ so formatting is done before your changes are submitted.
 
 ## Getting started
 
-It's a bit similar to what it was for 3.x but also a bit different.
+You need the same C++ pre-requisites installed that are required for the `godot` repository. Follow the [official build instructions for your target platform](https://docs.godotengine.org/en/latest/contributing/development/compiling/index.html#building-for-target-platforms).
+
+Getting started with GDExtensions is a bit similar to what it was for 3.x but also a bit different.
+
 This new approach is much more akin to how core Godot modules are structured.
 
 Compiling this repository generates a static library to be linked with your shared lib,

+ 4 - 5
binding_generator.py

@@ -135,9 +135,7 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False):
 
 
 def print_file_list(api_filepath, output_dir, headers=False, sources=False):
-    end = ";"
-    for f in get_file_list(api_filepath, output_dir, headers, sources):
-        print(f, end=end)
+    print(*get_file_list(api_filepath, output_dir, headers, sources), sep=";", end=None)
 
 
 def scons_emit_files(target, source, env):
@@ -1718,9 +1716,9 @@ def generate_global_constant_binds(api, output_dir):
             continue
 
         if enum_def["is_bitfield"]:
-            header.append(f'VARIANT_BITFIELD_CAST(godot::{enum_def["name"]});')
+            header.append(f'VARIANT_BITFIELD_CAST({enum_def["name"]});')
         else:
-            header.append(f'VARIANT_ENUM_CAST(godot::{enum_def["name"]});')
+            header.append(f'VARIANT_ENUM_CAST({enum_def["name"]});')
 
     # Variant::Type is not a global enum, but only one line, it is worth to place in this file instead of creating new file.
     header.append(f"VARIANT_ENUM_CAST(godot::Variant::Type);")
@@ -2336,6 +2334,7 @@ def get_operator_id_name(op):
         "unary-": "negate",
         "unary+": "positive",
         "%": "module",
+        "**": "power",
         "<<": "shift_left",
         ">>": "shift_right",
         "&": "bit_and",

+ 25 - 7
include/godot_cpp/godot.hpp

@@ -187,26 +187,44 @@ enum ModuleInitializationLevel {
 	MODULE_INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE,
 	MODULE_INITIALIZATION_LEVEL_SERVERS = GDEXTENSION_INITIALIZATION_SERVERS,
 	MODULE_INITIALIZATION_LEVEL_SCENE = GDEXTENSION_INITIALIZATION_SCENE,
-	MODULE_INITIALIZATION_LEVEL_EDITOR = GDEXTENSION_INITIALIZATION_EDITOR
+	MODULE_INITIALIZATION_LEVEL_EDITOR = GDEXTENSION_INITIALIZATION_EDITOR,
+	MODULE_INITIALIZATION_LEVEL_MAX
 };
 
 class GDExtensionBinding {
 public:
 	using Callback = void (*)(ModuleInitializationLevel p_level);
 
-	static Callback init_callback;
-	static Callback terminate_callback;
-	static GDExtensionInitializationLevel minimum_initialization_level;
-	static GDExtensionBool init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization);
+	struct InitData {
+		GDExtensionInitializationLevel minimum_initialization_level = GDEXTENSION_INITIALIZATION_CORE;
+		Callback init_callback = nullptr;
+		Callback terminate_callback = nullptr;
+	};
+
+	class InitDataList {
+		int data_count = 0;
+		int data_capacity = 0;
+		InitData **data = nullptr;
+
+	public:
+		void add(InitData *p_cb);
+		~InitDataList();
+	};
+
+	static bool api_initialized;
+	static int level_initialized[MODULE_INITIALIZATION_LEVEL_MAX];
+	static InitDataList initdata;
+	static GDExtensionBool init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, InitData *p_init_data, GDExtensionInitialization *r_initialization);
 
 public:
-	static void initialize_level(void *userdata, GDExtensionInitializationLevel p_level);
-	static void deinitialize_level(void *userdata, GDExtensionInitializationLevel p_level);
+	static void initialize_level(void *p_userdata, GDExtensionInitializationLevel p_level);
+	static void deinitialize_level(void *p_userdata, GDExtensionInitializationLevel p_level);
 
 	class InitObject {
 		GDExtensionInterfaceGetProcAddress get_proc_address;
 		GDExtensionClassLibraryPtr library;
 		GDExtensionInitialization *initialization;
+		mutable InitData *init_data = nullptr;
 
 	public:
 		InitObject(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization);

+ 14 - 1
include/godot_cpp/variant/variant.hpp

@@ -122,6 +122,7 @@ public:
 		OP_NEGATE,
 		OP_POSITIVE,
 		OP_MODULE,
+		OP_POWER,
 		// bitwise
 		OP_SHIFT_LEFT,
 		OP_SHIFT_RIGHT,
@@ -154,10 +155,18 @@ public:
 	Variant(int64_t v);
 	Variant(int32_t v) :
 			Variant(static_cast<int64_t>(v)) {}
-	Variant(uint32_t v) :
+	Variant(int16_t v) :
+			Variant(static_cast<int64_t>(v)) {}
+	Variant(int8_t v) :
 			Variant(static_cast<int64_t>(v)) {}
 	Variant(uint64_t v) :
 			Variant(static_cast<int64_t>(v)) {}
+	Variant(uint32_t v) :
+			Variant(static_cast<int64_t>(v)) {}
+	Variant(uint16_t v) :
+			Variant(static_cast<int64_t>(v)) {}
+	Variant(uint8_t v) :
+			Variant(static_cast<int64_t>(v)) {}
 	Variant(double v);
 	Variant(float v) :
 			Variant((double)v) {}
@@ -209,8 +218,12 @@ public:
 	operator bool() const;
 	operator int64_t() const;
 	operator int32_t() const;
+	operator int16_t() const;
+	operator int8_t() const;
 	operator uint64_t() const;
 	operator uint32_t() const;
+	operator uint16_t() const;
+	operator uint8_t() const;
 	operator double() const;
 	operator float() const;
 	operator String() const;

+ 6 - 0
src/classes/wrapped.cpp

@@ -50,6 +50,12 @@ void Wrapped::_postinitialize() {
 		godot::internal::gdextension_interface_object_set_instance(_owner, reinterpret_cast<GDExtensionConstStringNamePtr>(extension_class), this);
 	}
 	godot::internal::gdextension_interface_object_set_instance_binding(_owner, godot::internal::token, this, _get_bindings_callbacks());
+	if (extension_class) {
+		Object *obj = dynamic_cast<Object *>(this);
+		if (obj) {
+			obj->notification(Object::NOTIFICATION_POSTINITIALIZE);
+		}
+	}
 }
 
 Wrapped::Wrapped(const StringName p_godot_class) {

+ 75 - 20
src/godot.cpp

@@ -189,9 +189,9 @@ GDExtensionInterfaceEditorRemovePlugin gdextension_interface_editor_remove_plugi
 
 } // namespace internal
 
-GDExtensionBinding::Callback GDExtensionBinding::init_callback = nullptr;
-GDExtensionBinding::Callback GDExtensionBinding::terminate_callback = nullptr;
-GDExtensionInitializationLevel GDExtensionBinding::minimum_initialization_level = GDEXTENSION_INITIALIZATION_CORE;
+bool GDExtensionBinding::api_initialized = false;
+int GDExtensionBinding::level_initialized[MODULE_INITIALIZATION_LEVEL_MAX] = { 0 };
+GDExtensionBinding::InitDataList GDExtensionBinding::initdata;
 
 #define ERR_PRINT_EARLY(m_msg) \
 	internal::gdextension_interface_print_error(m_msg, FUNCTION_STR, __FILE__, __LINE__, false)
@@ -218,7 +218,20 @@ typedef struct {
 	GDExtensionInterfacePrintErrorWithMessage print_error_with_message;
 } LegacyGDExtensionInterface;
 
-GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
+GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, InitData *p_init_data, GDExtensionInitialization *r_initialization) {
+	if (!p_init_data || !p_init_data->init_callback) {
+		ERR_FAIL_V_MSG(false, "Initialization callback must be defined.");
+	}
+
+	if (api_initialized) {
+		r_initialization->initialize = initialize_level;
+		r_initialization->deinitialize = deinitialize_level;
+		r_initialization->userdata = p_init_data;
+		r_initialization->minimum_initialization_level = p_init_data->minimum_initialization_level;
+
+		return true;
+	}
+
 	// Make sure we weren't passed the legacy struct.
 	uint32_t *raw_interface = (uint32_t *)(void *)p_get_proc_address;
 	if (raw_interface[0] == 4 && raw_interface[1] == 0) {
@@ -251,7 +264,12 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge
 	} else if (internal::godot_version.minor != GODOT_VERSION_MINOR) {
 		compatible = internal::godot_version.minor > GODOT_VERSION_MINOR;
 	} else {
+#if GODOT_VERSION_PATCH > 0
 		compatible = internal::godot_version.patch >= GODOT_VERSION_PATCH;
+#else
+		// Prevent -Wtype-limits warning due to unsigned comparison.
+		compatible = true;
+#endif
 	}
 	if (!compatible) {
 		// We need to use snprintf() here because vformat() uses Variant, and we haven't loaded
@@ -401,59 +419,96 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge
 
 	r_initialization->initialize = initialize_level;
 	r_initialization->deinitialize = deinitialize_level;
-	r_initialization->minimum_initialization_level = minimum_initialization_level;
-
-	ERR_FAIL_NULL_V_MSG(init_callback, false, "Initialization callback must be defined.");
+	r_initialization->userdata = p_init_data;
+	r_initialization->minimum_initialization_level = p_init_data->minimum_initialization_level;
 
 	Variant::init_bindings();
 	godot::internal::register_engine_classes();
 
+	api_initialized = true;
 	return true;
 }
 
 #undef LOAD_PROC_ADDRESS
 #undef ERR_PRINT_EARLY
 
-void GDExtensionBinding::initialize_level(void *userdata, GDExtensionInitializationLevel p_level) {
+void GDExtensionBinding::initialize_level(void *p_userdata, GDExtensionInitializationLevel p_level) {
+	ERR_FAIL_COND(static_cast<ModuleInitializationLevel>(p_level) >= MODULE_INITIALIZATION_LEVEL_MAX);
 	ClassDB::current_level = p_level;
 
-	if (init_callback) {
-		init_callback(static_cast<ModuleInitializationLevel>(p_level));
+	InitData *init_data = static_cast<InitData *>(p_userdata);
+	if (init_data && init_data->init_callback) {
+		init_data->init_callback(static_cast<ModuleInitializationLevel>(p_level));
 	}
 
-	ClassDB::initialize(p_level);
+	if (level_initialized[p_level] == 0) {
+		ClassDB::initialize(p_level);
+	}
+	level_initialized[p_level]++;
 }
 
-void GDExtensionBinding::deinitialize_level(void *userdata, GDExtensionInitializationLevel p_level) {
+void GDExtensionBinding::deinitialize_level(void *p_userdata, GDExtensionInitializationLevel p_level) {
+	ERR_FAIL_COND(static_cast<ModuleInitializationLevel>(p_level) >= MODULE_INITIALIZATION_LEVEL_MAX);
 	ClassDB::current_level = p_level;
 
-	if (terminate_callback) {
-		terminate_callback(static_cast<ModuleInitializationLevel>(p_level));
+	InitData *init_data = static_cast<InitData *>(p_userdata);
+	if (init_data && init_data->terminate_callback) {
+		init_data->terminate_callback(static_cast<ModuleInitializationLevel>(p_level));
+	}
+
+	level_initialized[p_level]--;
+	if (level_initialized[p_level] == 0) {
+		EditorPlugins::deinitialize(p_level);
+		ClassDB::deinitialize(p_level);
+	}
+}
+
+void GDExtensionBinding::InitDataList::add(InitData *p_data) {
+	if (data_count == data_capacity) {
+		void *new_ptr = realloc(data, sizeof(InitData *) * (data_capacity + 32));
+		if (new_ptr) {
+			data = (InitData **)(new_ptr);
+			data_capacity += 32;
+		} else {
+			ERR_FAIL_MSG("Unable to allocate memory for extension callbacks.");
+		}
 	}
+	data[data_count++] = p_data;
+}
 
-	EditorPlugins::deinitialize(p_level);
-	ClassDB::deinitialize(p_level);
+GDExtensionBinding::InitDataList::~InitDataList() {
+	for (int i = 0; i < data_count; i++) {
+		if (data[i]) {
+			delete data[i];
+		}
+	}
+	if (data) {
+		free(data);
+	}
 }
+
 GDExtensionBinding::InitObject::InitObject(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
 	get_proc_address = p_get_proc_address;
 	library = p_library;
 	initialization = r_initialization;
+	init_data = new InitData();
+	GDExtensionBinding::initdata.add(init_data);
 }
 
 void GDExtensionBinding::InitObject::register_initializer(Callback p_init) const {
-	GDExtensionBinding::init_callback = p_init;
+	init_data->init_callback = p_init;
 }
 
 void GDExtensionBinding::InitObject::register_terminator(Callback p_terminate) const {
-	GDExtensionBinding::terminate_callback = p_terminate;
+	init_data->terminate_callback = p_terminate;
 }
 
 void GDExtensionBinding::InitObject::set_minimum_library_initialization_level(ModuleInitializationLevel p_level) const {
-	GDExtensionBinding::minimum_initialization_level = static_cast<GDExtensionInitializationLevel>(p_level);
+	init_data->minimum_initialization_level = static_cast<GDExtensionInitializationLevel>(p_level);
 }
 
 GDExtensionBool GDExtensionBinding::InitObject::init() const {
-	return GDExtensionBinding::init(get_proc_address, library, initialization);
+	return GDExtensionBinding::init(get_proc_address, library, init_data, initialization);
 }
 
 } // namespace godot

+ 16 - 0
src/variant/variant.cpp

@@ -268,6 +268,14 @@ Variant::operator int32_t() const {
 	return static_cast<int32_t>(operator int64_t());
 }
 
+Variant::operator int16_t() const {
+	return static_cast<int16_t>(operator int64_t());
+}
+
+Variant::operator int8_t() const {
+	return static_cast<int8_t>(operator int64_t());
+}
+
 Variant::operator uint64_t() const {
 	return static_cast<uint64_t>(operator int64_t());
 }
@@ -276,6 +284,14 @@ Variant::operator uint32_t() const {
 	return static_cast<uint32_t>(operator int64_t());
 }
 
+Variant::operator uint16_t() const {
+	return static_cast<uint16_t>(operator int64_t());
+}
+
+Variant::operator uint8_t() const {
+	return static_cast<uint8_t>(operator int64_t());
+}
+
 Variant::operator double() const {
 	double result;
 	to_type_constructor[FLOAT](&result, _native_ptr());

+ 0 - 22
test/CMakeLists.txt

@@ -59,31 +59,9 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
 
 else()
 
-#elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
-	# using Clang
-#elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
-	# using GCC and maybe MinGW?
-
 	set(GODOT_LINKER_FLAGS "-static-libgcc -static-libstdc++ -Wl,-R,'$$ORIGIN'")
 
-	# Hmm.. maybe to strikt?
 	set(GODOT_COMPILE_FLAGS "-fPIC -g -Wwrite-strings")
-	set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wchar-subscripts -Wcomment -Wdisabled-optimization")
-	set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wformat -Wformat=2 -Wformat-security -Wformat-y2k")
-	set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wimport -Winit-self -Winline -Winvalid-pch -Werror")
-	set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wmissing-braces -Wmissing-format-attribute")
-	set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wmissing-include-dirs -Wmissing-noreturn -Wpacked -Wpointer-arith")
-	set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wredundant-decls -Wreturn-type -Wsequence-point")
-	set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wswitch -Wswitch-enum -Wtrigraphs")
-	set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wuninitialized -Wunknown-pragmas -Wunreachable-code -Wunused-label")
-	set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wunused-value -Wvariadic-macros -Wvolatile-register-var -Wno-error=attributes")
-
-	# -Wshadow -Wextra -Wall -Weffc++ -Wfloat-equal -Wstack-protector -Wunused-parameter -Wsign-compare -Wunused-variable -Wcast-align
-	# -Wunused-function -Wstrict-aliasing -Wstrict-aliasing=2 -Wmissing-field-initializers
-
-	if(NOT CMAKE_SYSTEM_NAME STREQUAL "Android")
-		set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wno-ignored-attributes")
-	endif()
 
 	if(CMAKE_BUILD_TYPE MATCHES Debug)
 		set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-omit-frame-pointer -O0")

+ 5 - 0
test/project/main.gd

@@ -169,6 +169,11 @@ func _ready():
 	get_viewport().push_input(event)
 	assert_equal(custom_signal_emitted, ["_input: H", 72])
 
+	# Check NOTIFICATION_POST_INITIALIZED, both when created from GDScript and godot-cpp.
+	var new_example_ref = ExampleRef.new()
+	assert_equal(new_example_ref.was_post_initialized(), true)
+	assert_equal(example.test_post_initialize(), true)
+
 	exit_with_status()
 
 func _on_Example_custom_signal(signal_name, value):

+ 15 - 0
test/src/example.cpp

@@ -23,10 +23,18 @@ int ExampleRef::get_id() const {
 	return id;
 }
 
+void ExampleRef::_notification(int p_what) {
+	if (p_what == NOTIFICATION_POSTINITIALIZE) {
+		post_initialized = true;
+	}
+}
+
 void ExampleRef::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_id", "id"), &ExampleRef::set_id);
 	ClassDB::bind_method(D_METHOD("get_id"), &ExampleRef::get_id);
 
+	ClassDB::bind_method(D_METHOD("was_post_initialized"), &ExampleRef::was_post_initialized);
+
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "id"), "set_id", "get_id");
 }
 
@@ -161,6 +169,7 @@ void Example::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("return_last_rpc_arg"), &Example::return_last_rpc_arg);
 
 	ClassDB::bind_method(D_METHOD("def_args", "a", "b"), &Example::def_args, DEFVAL(100), DEFVAL(200));
+	ClassDB::bind_method(D_METHOD("test_post_initialize"), &Example::test_post_initialize);
 
 	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);
@@ -426,6 +435,12 @@ Vector4 Example::get_v4() const {
 	return Vector4(1.2, 3.4, 5.6, 7.8);
 }
 
+bool Example::test_post_initialize() const {
+	Ref<ExampleRef> new_example_ref;
+	new_example_ref.instantiate();
+	return new_example_ref->was_post_initialized();
+}
+
 // Virtual function override.
 bool Example::_has_point(const Vector2 &point) const {
 	Label *label = get_node<Label>("Label");

+ 7 - 0
test/src/example.h

@@ -35,16 +35,21 @@ private:
 	static int last_id;
 
 	int id;
+	bool post_initialized = false;
 
 protected:
 	static void _bind_methods();
 
+	void _notification(int p_what);
+
 public:
 	ExampleRef();
 	~ExampleRef();
 
 	void set_id(int p_id);
 	int get_id() const;
+
+	bool was_post_initialized() const { return post_initialized; }
 };
 
 class ExampleMin : public Control {
@@ -148,6 +153,8 @@ public:
 	Vector2 get_custom_position() const;
 	Vector4 get_v4() const;
 
+	bool test_post_initialize() const;
+
 	// Static method.
 	static int test_static(int p_a, int p_b);
 	static void test_static2();

+ 9 - 5
tools/android.py

@@ -8,7 +8,7 @@ def options(opts):
     opts.Add(
         "android_api_level",
         "Target Android API level",
-        "18" if "32" in ARGUMENTS.get("arch", "arm64") else "21",
+        "21",
     )
     opts.Add(
         "ANDROID_HOME",
@@ -47,11 +47,9 @@ def generate(env):
         my_spawn.configure(env)
 
     # Validate API level
-    api_level = int(env["android_api_level"])
-    if "64" in env["arch"] and api_level < 21:
-        print("WARN: 64-bit Android architectures require an API level of at least 21; setting android_api_level=21")
+    if int(env["android_api_level"]) < 21:
+        print("WARNING: minimum supported Android target api is 21. Forcing target api 21.")
         env["android_api_level"] = "21"
-        api_level = 21
 
     # Setup toolchain
     toolchain = get_android_ndk_root(env) + "/toolchains/llvm/prebuilt/"
@@ -66,6 +64,12 @@ def generate(env):
     elif sys.platform == "darwin":
         toolchain += "darwin-x86_64"
         env.Append(LINKFLAGS=["-shared"])
+
+    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.")
+        env.Exit(1)
+
     env.PrependENVPath("PATH", toolchain + "/bin")  # This does nothing half of the time, but we'll put it here anyways
 
     # Get architecture info

+ 3 - 0
tools/godotcpp.py

@@ -267,6 +267,9 @@ def generate(env):
     if env["precision"] == "double":
         env.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"])
 
+    # Allow detecting when building as a GDExtension.
+    env.Append(CPPDEFINES=["GDEXTENSION"])
+
     # Suffix
     suffix = ".{}.{}".format(env["platform"], env["target"])
     if env.dev_build: