Bladeren bron

Make build profile project detection also set build options

Michael Alexsander 4 maanden geleden
bovenliggende
commit
454e4f817c

+ 13 - 0
core/extension/gdextension.cpp

@@ -862,6 +862,19 @@ String GDExtensionResourceLoader::get_resource_type(const String &p_path) const
 }
 }
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
+void GDExtensionResourceLoader::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
+	Ref<GDExtension> gdext = ResourceLoader::load(p_path);
+	if (gdext.is_null()) {
+		return;
+	}
+
+	for (const StringName class_name : gdext->get_classes_used()) {
+		if (ClassDB::class_exists(class_name)) {
+			r_classes->insert(class_name);
+		}
+	}
+}
+
 bool GDExtension::has_library_changed() const {
 bool GDExtension::has_library_changed() const {
 	return loader->has_library_changed();
 	return loader->has_library_changed();
 }
 }

+ 3 - 0
core/extension/gdextension.h

@@ -184,6 +184,9 @@ public:
 	virtual void get_recognized_extensions(List<String> *p_extensions) const override;
 	virtual void get_recognized_extensions(List<String> *p_extensions) const override;
 	virtual bool handles_type(const String &p_type) const override;
 	virtual bool handles_type(const String &p_type) const override;
 	virtual String get_resource_type(const String &p_path) const override;
 	virtual String get_resource_type(const String &p_path) const override;
+#ifdef TOOLS_ENABLED
+	virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes) override;
+#endif // TOOLS_ENABLED
 };
 };
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED

+ 35 - 4
core/io/resource_format_binary.cpp

@@ -36,6 +36,7 @@
 #include "core/io/missing_resource.h"
 #include "core/io/missing_resource.h"
 #include "core/object/script_language.h"
 #include "core/object/script_language.h"
 #include "core/version.h"
 #include "core/version.h"
+#include "scene/resources/packed_scene.h"
 
 
 //#define print_bl(m_what) print_line(m_what)
 //#define print_bl(m_what) print_line(m_what)
 #define print_bl(m_what) (void)(m_what)
 #define print_bl(m_what) (void)(m_what)
@@ -937,11 +938,10 @@ void ResourceLoaderBinary::get_classes_used(Ref<FileAccess> p_f, HashSet<StringN
 		return;
 		return;
 	}
 	}
 
 
-	for (int i = 0; i < internal_resources.size(); i++) {
-		p_f->seek(internal_resources[i].offset);
+	for (const IntResource &res : internal_resources) {
+		p_f->seek(res.offset);
 		String t = get_unicode_string();
 		String t = get_unicode_string();
-		ERR_FAIL_COND(p_f->get_error() != OK);
-		if (t != String()) {
+		if (!p_f->get_error() && t != String() && ClassDB::class_exists(t)) {
 			p_classes->insert(t);
 			p_classes->insert(t);
 		}
 		}
 	}
 	}
@@ -1518,6 +1518,37 @@ void ResourceFormatLoaderBinary::get_classes_used(const String &p_path, HashSet<
 	loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
 	loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
 	loader.res_path = loader.local_path;
 	loader.res_path = loader.local_path;
 	loader.get_classes_used(f, r_classes);
 	loader.get_classes_used(f, r_classes);
+
+	// Fetch the nodes inside scene files.
+	if (loader.type == "PackedScene") {
+		ERR_FAIL_COND(loader.load() != OK);
+
+		Ref<SceneState> state = Ref<PackedScene>(loader.get_resource())->get_state();
+		for (int i = 0; i < state->get_node_count(); i++) {
+			const StringName node_name = state->get_node_type(i);
+			if (ClassDB::class_exists(node_name)) {
+				r_classes->insert(node_name);
+			}
+
+			// Fetch the values of properties in the node.
+			for (int j = 0; j < state->get_node_property_count(i); j++) {
+				const Variant var = state->get_node_property_value(i, j);
+				if (var.get_type() != Variant::OBJECT) {
+					continue;
+				}
+
+				const Object *obj = var.get_validated_object();
+				if (obj == nullptr) {
+					continue;
+				}
+
+				const StringName obj_name = obj->get_class_name();
+				if (ClassDB::class_exists(obj_name)) {
+					r_classes->insert(obj_name);
+				}
+			}
+		}
+	}
 }
 }
 
 
 String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const {
 String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const {

+ 25 - 0
core/io/resource_importer.cpp

@@ -434,6 +434,7 @@ Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) cons
 
 
 	return pat.metadata;
 	return pat.metadata;
 }
 }
+
 void ResourceFormatImporter::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
 void ResourceFormatImporter::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
 	PathAndType pat;
 	PathAndType pat;
 	Error err = _get_path_and_type(p_path, pat, false);
 	Error err = _get_path_and_type(p_path, pat, false);
@@ -456,6 +457,18 @@ void ResourceFormatImporter::get_dependencies(const String &p_path, List<String>
 	ResourceLoader::get_dependencies(pat.path, p_dependencies, p_add_types);
 	ResourceLoader::get_dependencies(pat.path, p_dependencies, p_add_types);
 }
 }
 
 
+void ResourceFormatImporter::get_build_dependencies(const String &p_path, HashSet<String> *r_dependencies) {
+	if (!exists(p_path)) {
+		return;
+	}
+
+	List<Ref<ResourceImporter>> valid_importers;
+	get_importers_for_file(p_path, &valid_importers);
+	for (Ref<ResourceImporter> importer : valid_importers) {
+		importer->get_build_dependencies(p_path, r_dependencies);
+	}
+}
+
 Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_name(const String &p_name) const {
 Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_name(const String &p_name) const {
 	for (int i = 0; i < importers.size(); i++) {
 	for (int i = 0; i < importers.size(); i++) {
 		if (importers[i]->get_importer_name() == p_name) {
 		if (importers[i]->get_importer_name() == p_name) {
@@ -555,9 +568,21 @@ ResourceFormatImporter::ResourceFormatImporter() {
 
 
 //////////////
 //////////////
 
 
+void ResourceImporter::get_build_dependencies(const String &p_path, HashSet<String> *r_dependencies) {
+	Vector<String> ret;
+	if (GDVIRTUAL_CALL(_get_build_dependencies, p_path, ret)) {
+		for (int i = 0; i < ret.size(); i++) {
+			r_dependencies->insert(ret[i]);
+		}
+		return;
+	}
+}
+
 void ResourceImporter::_bind_methods() {
 void ResourceImporter::_bind_methods() {
 	BIND_ENUM_CONSTANT(IMPORT_ORDER_DEFAULT);
 	BIND_ENUM_CONSTANT(IMPORT_ORDER_DEFAULT);
 	BIND_ENUM_CONSTANT(IMPORT_ORDER_SCENE);
 	BIND_ENUM_CONSTANT(IMPORT_ORDER_SCENE);
+
+	GDVIRTUAL_BIND(_get_build_dependencies, "path");
 }
 }
 
 
 /////
 /////

+ 6 - 0
core/io/resource_importer.h

@@ -78,6 +78,8 @@ public:
 	virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes) override;
 	virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes) override;
 	virtual bool exists(const String &p_path) const override;
 	virtual bool exists(const String &p_path) const override;
 
 
+	void get_build_dependencies(const String &p_path, HashSet<String> *r_dependencies);
+
 	virtual int get_import_order(const String &p_path) const override;
 	virtual int get_import_order(const String &p_path) const override;
 
 
 	Error get_import_order_threads_and_importer(const String &p_path, int &r_order, bool &r_can_threads, String &r_importer) const;
 	Error get_import_order_threads_and_importer(const String &p_path, int &r_order, bool &r_can_threads, String &r_importer) const;
@@ -106,6 +108,8 @@ class ResourceImporter : public RefCounted {
 	GDCLASS(ResourceImporter, RefCounted);
 	GDCLASS(ResourceImporter, RefCounted);
 
 
 protected:
 protected:
+	GDVIRTUAL1RC(Vector<String>, _get_build_dependencies, String)
+
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 public:
 public:
@@ -155,6 +159,8 @@ public:
 	virtual Error import_group_file(const String &p_group_file, const HashMap<String, HashMap<StringName, Variant>> &p_source_file_options, const HashMap<String, String> &p_base_paths) { return ERR_UNAVAILABLE; }
 	virtual Error import_group_file(const String &p_group_file, const HashMap<String, HashMap<StringName, Variant>> &p_source_file_options, const HashMap<String, String> &p_base_paths) { return ERR_UNAVAILABLE; }
 	virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const { return true; }
 	virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const { return true; }
 	virtual String get_import_settings_string() const { return String(); }
 	virtual String get_import_settings_string() const { return String(); }
+
+	virtual void get_build_dependencies(const String &p_path, HashSet<String> *r_build_dependencies);
 };
 };
 
 
 VARIANT_ENUM_CAST(ResourceImporter::ImportOrder);
 VARIANT_ENUM_CAST(ResourceImporter::ImportOrder);

+ 24 - 0
core/object/class_db.cpp

@@ -2152,6 +2152,30 @@ bool ClassDB::is_class_runtime(const StringName &p_class) {
 	return ti->is_runtime;
 	return ti->is_runtime;
 }
 }
 
 
+#ifdef TOOLS_ENABLED
+void ClassDB::add_class_dependency(const StringName &p_class, const StringName &p_dependency) {
+	Locker::Lock lock(Locker::STATE_WRITE);
+
+	ERR_FAIL_COND_MSG(!classes.has(p_class), vformat("Request for nonexistent class '%s'.", p_class));
+	if (classes[p_class].dependency_list.find(p_dependency)) {
+		ERR_FAIL();
+	}
+
+	classes[p_class].dependency_list.push_back(p_dependency);
+}
+
+void ClassDB::get_class_dependencies(const StringName &p_class, List<StringName> *r_rependencies) {
+	Locker::Lock lock(Locker::STATE_READ);
+
+	ClassInfo *ti = classes.getptr(p_class);
+	ERR_FAIL_NULL_MSG(ti, vformat("Cannot get class '%s'.", String(p_class)));
+
+	for (const StringName &dep : ti->dependency_list) {
+		r_rependencies->push_back(dep);
+	}
+}
+#endif // TOOLS_ENABLED
+
 void ClassDB::add_resource_base_extension(const StringName &p_extension, const StringName &p_class) {
 void ClassDB::add_resource_base_extension(const StringName &p_extension, const StringName &p_class) {
 	if (resource_base_extensions.has(p_extension)) {
 	if (resource_base_extensions.has(p_extension)) {
 		return;
 		return;

+ 11 - 0
core/object/class_db.h

@@ -118,6 +118,7 @@ public:
 		HashMap<StringName, MethodInfo> signal_map;
 		HashMap<StringName, MethodInfo> signal_map;
 		List<PropertyInfo> property_list;
 		List<PropertyInfo> property_list;
 		HashMap<StringName, PropertyInfo> property_map;
 		HashMap<StringName, PropertyInfo> property_map;
+
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 		List<StringName> constant_order;
 		List<StringName> constant_order;
 		List<StringName> method_order;
 		List<StringName> method_order;
@@ -127,6 +128,11 @@ public:
 		HashMap<StringName, Vector<Error>> method_error_values;
 		HashMap<StringName, Vector<Error>> method_error_values;
 		HashMap<StringName, List<StringName>> linked_properties;
 		HashMap<StringName, List<StringName>> linked_properties;
 #endif // DEBUG_ENABLED
 #endif // DEBUG_ENABLED
+
+#ifdef TOOLS_ENABLED
+		List<StringName> dependency_list;
+#endif
+
 		HashMap<StringName, PropertySetGet> property_setget;
 		HashMap<StringName, PropertySetGet> property_setget;
 		HashMap<StringName, Vector<uint32_t>> virtual_methods_compat;
 		HashMap<StringName, Vector<uint32_t>> virtual_methods_compat;
 
 
@@ -499,6 +505,11 @@ public:
 	static bool is_class_reloadable(const StringName &p_class);
 	static bool is_class_reloadable(const StringName &p_class);
 	static bool is_class_runtime(const StringName &p_class);
 	static bool is_class_runtime(const StringName &p_class);
 
 
+#ifdef TOOLS_ENABLED
+	static void add_class_dependency(const StringName &p_class, const StringName &p_dependency);
+	static void get_class_dependencies(const StringName &p_class, List<StringName> *r_rependencies);
+#endif
+
 	static void add_resource_base_extension(const StringName &p_extension, const StringName &p_class);
 	static void add_resource_base_extension(const StringName &p_extension, const StringName &p_class);
 	static void get_resource_base_extensions(List<String> *p_extensions);
 	static void get_resource_base_extensions(List<String> *p_extensions);
 	static void get_extensions_for_type(const StringName &p_class, List<String> *p_extensions);
 	static void get_extensions_for_type(const StringName &p_class, List<String> *p_extensions);

+ 6 - 1
core/object/object.h

@@ -147,6 +147,12 @@ enum PropertyUsageFlags {
 #define ADD_SUBGROUP_INDENT(m_name, m_prefix, m_depth) ::ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix, m_depth)
 #define ADD_SUBGROUP_INDENT(m_name, m_prefix, m_depth) ::ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix, m_depth)
 #define ADD_LINKED_PROPERTY(m_property, m_linked_property) ::ClassDB::add_linked_property(get_class_static(), m_property, m_linked_property)
 #define ADD_LINKED_PROPERTY(m_property, m_linked_property) ::ClassDB::add_linked_property(get_class_static(), m_property, m_linked_property)
 
 
+#ifdef TOOLS_ENABLED
+#define ADD_CLASS_DEPENDENCY(m_class) ::ClassDB::add_class_dependency(get_class_static(), m_class)
+#else
+#define ADD_CLASS_DEPENDENCY(m_class)
+#endif
+
 #define ADD_ARRAY_COUNT(m_label, m_count_property, m_count_property_setter, m_count_property_getter, m_prefix) ClassDB::add_property_array_count(get_class_static(), m_label, m_count_property, StringName(m_count_property_setter), StringName(m_count_property_getter), m_prefix)
 #define ADD_ARRAY_COUNT(m_label, m_count_property, m_count_property_setter, m_count_property_getter, m_prefix) ClassDB::add_property_array_count(get_class_static(), m_label, m_count_property, StringName(m_count_property_setter), StringName(m_count_property_getter), m_prefix)
 #define ADD_ARRAY_COUNT_WITH_USAGE_FLAGS(m_label, m_count_property, m_count_property_setter, m_count_property_getter, m_prefix, m_property_usage_flags) ClassDB::add_property_array_count(get_class_static(), m_label, m_count_property, StringName(m_count_property_setter), StringName(m_count_property_getter), m_prefix, m_property_usage_flags)
 #define ADD_ARRAY_COUNT_WITH_USAGE_FLAGS(m_label, m_count_property, m_count_property_setter, m_count_property_getter, m_prefix, m_property_usage_flags) ClassDB::add_property_array_count(get_class_static(), m_label, m_count_property, StringName(m_count_property_setter), StringName(m_count_property_getter), m_prefix, m_property_usage_flags)
 #define ADD_ARRAY(m_array_path, m_prefix) ClassDB::add_property_array(get_class_static(), m_array_path, m_prefix)
 #define ADD_ARRAY(m_array_path, m_prefix) ClassDB::add_property_array(get_class_static(), m_array_path, m_prefix)
@@ -979,7 +985,6 @@ public:
 	bool editor_is_section_unfolded(const String &p_section);
 	bool editor_is_section_unfolded(const String &p_section);
 	const HashSet<String> &editor_get_section_folding() const { return editor_section_folding; }
 	const HashSet<String> &editor_get_section_folding() const { return editor_section_folding; }
 	void editor_clear_section_folding() { editor_section_folding.clear(); }
 	void editor_clear_section_folding() { editor_section_folding.clear(); }
-
 #endif
 #endif
 
 
 	// Used by script languages to store binding data.
 	// Used by script languages to store binding data.

+ 19 - 0
doc/classes/ResourceImporter.xml

@@ -9,6 +9,25 @@
 	<tutorials>
 	<tutorials>
 		<link title="Import plugins">$DOCS_URL/tutorials/plugins/editor/import_plugins.html</link>
 		<link title="Import plugins">$DOCS_URL/tutorials/plugins/editor/import_plugins.html</link>
 	</tutorials>
 	</tutorials>
+	<methods>
+		<method name="_get_build_dependencies" qualifiers="virtual const">
+			<return type="PackedStringArray" />
+			<param index="0" name="path" type="String" />
+			<description>
+				Called when the engine compilation profile editor wants to check what build options an imported resource needs. For example, [ResourceImporterDynamicFont] has a property called [member ResourceImporterDynamicFont.multichannel_signed_distance_field], that depends on the engine to be build with the "msdfgen" module. If that resource happened to be a custom one, it would be handled like this:
+				[codeblock]
+				func _get_build_dependencies(path):
+				    var resource = load(path)
+				    var dependencies = PackedStringArray()
+
+				    if resource.multichannel_signed_distance_field:
+				        dependencies.push_back("module_msdfgen_enabled")
+
+				    return dependencies
+				[/codeblock]
+			</description>
+		</method>
+	</methods>
 	<constants>
 	<constants>
 		<constant name="IMPORT_ORDER_DEFAULT" value="0" enum="ImportOrder">
 		<constant name="IMPORT_ORDER_DEFAULT" value="0" enum="ImportOrder">
 			The default import order.
 			The default import order.

+ 541 - 62
editor/editor_build_profile.cpp

@@ -30,6 +30,7 @@
 
 
 #include "editor_build_profile.h"
 #include "editor_build_profile.h"
 
 
+#include "core/config/project_settings.h"
 #include "core/io/json.h"
 #include "core/io/json.h"
 #include "editor/editor_file_system.h"
 #include "editor/editor_file_system.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
@@ -41,36 +42,60 @@
 #include "scene/gui/line_edit.h"
 #include "scene/gui/line_edit.h"
 #include "scene/gui/separator.h"
 #include "scene/gui/separator.h"
 
 
+#include "modules/modules_enabled.gen.h" // For mono.
+
 const char *EditorBuildProfile::build_option_identifiers[BUILD_OPTION_MAX] = {
 const char *EditorBuildProfile::build_option_identifiers[BUILD_OPTION_MAX] = {
 	// This maps to SCons build options.
 	// This maps to SCons build options.
 	"disable_3d",
 	"disable_3d",
-	"disable_2d_physics",
-	"disable_3d_physics",
-	"disable_navigation",
+	"disable_navigation_2d",
+	"disable_navigation_3d",
 	"disable_xr",
 	"disable_xr",
-	"rendering_device", // FIXME: there's no scons option to disable rendering device
-	"opengl3",
+	"module_openxr_enabled",
+	"wayland",
+	"x11",
+	"rendering_device", // FIXME: There's no scons option to disable rendering device.
+	"forward_plus_renderer",
+	"forward_mobile_renderer",
 	"vulkan",
 	"vulkan",
+	"d3d12",
+	"metal",
+	"opengl3",
+	"disable_physics_2d",
+	"module_godot_physics_2d_enabled",
+	"disable_physics_3d",
+	"module_godot_physics_3d_enabled",
+	"module_jolt_physics_enabled",
 	"module_text_server_fb_enabled",
 	"module_text_server_fb_enabled",
 	"module_text_server_adv_enabled",
 	"module_text_server_adv_enabled",
 	"module_freetype_enabled",
 	"module_freetype_enabled",
 	"brotli",
 	"brotli",
 	"graphite",
 	"graphite",
-	"module_msdfgen_enabled"
+	"module_msdfgen_enabled",
 };
 };
 
 
 const bool EditorBuildProfile::build_option_disabled_by_default[BUILD_OPTION_MAX] = {
 const bool EditorBuildProfile::build_option_disabled_by_default[BUILD_OPTION_MAX] = {
 	// This maps to SCons build options.
 	// This maps to SCons build options.
 	false, // 3D
 	false, // 3D
-	false, // PHYSICS_2D
-	false, // PHYSICS_3D
-	false, // NAVIGATION
+	false, // NAVIGATION_2D
+	false, // NAVIGATION_3D
 	false, // XR
 	false, // XR
+	false, // OPENXR
+	false, // WAYLAND
+	false, // X11
 	false, // RENDERING_DEVICE
 	false, // RENDERING_DEVICE
-	false, // OPENGL
+	false, // FORWARD_RENDERER
+	false, // MOBILE_RENDERER
 	false, // VULKAN
 	false, // VULKAN
+	false, // D3D12
+	false, // METAL
+	false, // OPENGL
+	false, // PHYSICS_2D
+	false, // PHYSICS_GODOT_2D
+	false, // PHYSICS_3D
+	false, // PHYSICS_GODOT_3D
+	false, // PHYSICS_JOLT
 	true, // TEXT_SERVER_FALLBACK
 	true, // TEXT_SERVER_FALLBACK
-	false, // TEXT_SERVER_COMPLEX
+	false, // TEXT_SERVER_ADVANCED
 	false, // DYNAMIC_FONTS
 	false, // DYNAMIC_FONTS
 	false, // WOFF2_FONTS
 	false, // WOFF2_FONTS
 	false, // GRAPHITE_FONTS
 	false, // GRAPHITE_FONTS
@@ -80,38 +105,215 @@ const bool EditorBuildProfile::build_option_disabled_by_default[BUILD_OPTION_MAX
 const bool EditorBuildProfile::build_option_disable_values[BUILD_OPTION_MAX] = {
 const bool EditorBuildProfile::build_option_disable_values[BUILD_OPTION_MAX] = {
 	// This maps to SCons build options.
 	// This maps to SCons build options.
 	true, // 3D
 	true, // 3D
-	true, // PHYSICS_2D
-	true, // PHYSICS_3D
-	true, // NAVIGATION
+	true, // NAVIGATION_2D
+	true, // NAVIGATION_3D
 	true, // XR
 	true, // XR
+	false, // OPENXR
+	false, // WAYLAND
+	false, // X11
 	false, // RENDERING_DEVICE
 	false, // RENDERING_DEVICE
-	false, // OPENGL
+	false, // FORWARD_RENDERER
+	false, // MOBILE_RENDERER
 	false, // VULKAN
 	false, // VULKAN
+	false, // D3D12
+	false, // METAL
+	false, // OPENGL
+	true, // PHYSICS_2D
+	false, // PHYSICS_GODOT_2D
+	true, // PHYSICS_3D
+	false, // PHYSICS_GODOT_3D
+	false, // PHYSICS_JOLT
 	false, // TEXT_SERVER_FALLBACK
 	false, // TEXT_SERVER_FALLBACK
-	false, // TEXT_SERVER_COMPLEX
+	false, // TEXT_SERVER_ADVANCED
 	false, // DYNAMIC_FONTS
 	false, // DYNAMIC_FONTS
 	false, // WOFF2_FONTS
 	false, // WOFF2_FONTS
 	false, // GRAPHITE_FONTS
 	false, // GRAPHITE_FONTS
 	false, // MSDFGEN
 	false, // MSDFGEN
 };
 };
 
 
+// Options that require some resource explicitly asking for them when detecting from the project.
+const bool EditorBuildProfile::build_option_explicit_use[BUILD_OPTION_MAX] = {
+	false, // 3D
+	false, // NAVIGATION_2D
+	false, // NAVIGATION_3D
+	false, // XR
+	false, // OPENXR
+	false, // WAYLAND
+	false, // X11
+	false, // RENDERING_DEVICE
+	false, // FORWARD_RENDERER
+	false, // MOBILE_RENDERER
+	false, // VULKAN
+	false, // D3D12
+	false, // METAL
+	false, // OPENGL
+	false, // PHYSICS_2D
+	false, // PHYSICS_GODOT_2D
+	false, // PHYSICS_3D
+	false, // PHYSICS_GODOT_3D
+	false, // PHYSICS_JOLT
+	false, // TEXT_SERVER_FALLBACK
+	false, // TEXT_SERVER_ADVANCED
+	false, // DYNAMIC_FONTS
+	false, // WOFF2_FONTS
+	false, // GRAPHITE_FONTS
+	true, // MSDFGEN
+};
+
 const EditorBuildProfile::BuildOptionCategory EditorBuildProfile::build_option_category[BUILD_OPTION_MAX] = {
 const EditorBuildProfile::BuildOptionCategory EditorBuildProfile::build_option_category[BUILD_OPTION_MAX] = {
 	BUILD_OPTION_CATEGORY_GENERAL, // 3D
 	BUILD_OPTION_CATEGORY_GENERAL, // 3D
-	BUILD_OPTION_CATEGORY_GENERAL, // PHYSICS_2D
-	BUILD_OPTION_CATEGORY_GENERAL, // PHYSICS_3D
-	BUILD_OPTION_CATEGORY_GENERAL, // NAVIGATION
+	BUILD_OPTION_CATEGORY_GENERAL, // NAVIGATION_2D
+	BUILD_OPTION_CATEGORY_GENERAL, // NAVIGATION_3D
 	BUILD_OPTION_CATEGORY_GENERAL, // XR
 	BUILD_OPTION_CATEGORY_GENERAL, // XR
-	BUILD_OPTION_CATEGORY_GENERAL, // RENDERING_DEVICE
-	BUILD_OPTION_CATEGORY_GENERAL, // OPENGL
-	BUILD_OPTION_CATEGORY_GENERAL, // VULKAN
+	BUILD_OPTION_CATEGORY_GENERAL, // OPENXR
+	BUILD_OPTION_CATEGORY_GENERAL, // WAYLAND
+	BUILD_OPTION_CATEGORY_GENERAL, // X11
+	BUILD_OPTION_CATEGORY_GRAPHICS, // RENDERING_DEVICE
+	BUILD_OPTION_CATEGORY_GRAPHICS, // FORWARD_RENDERER
+	BUILD_OPTION_CATEGORY_GRAPHICS, // MOBILE_RENDERER
+	BUILD_OPTION_CATEGORY_GRAPHICS, // VULKAN
+	BUILD_OPTION_CATEGORY_GRAPHICS, // D3D12
+	BUILD_OPTION_CATEGORY_GRAPHICS, // METAL
+	BUILD_OPTION_CATEGORY_GRAPHICS, // OPENGL
+	BUILD_OPTION_CATEGORY_PHYSICS, // PHYSICS_2D
+	BUILD_OPTION_CATEGORY_PHYSICS, // PHYSICS_GODOT_2D
+	BUILD_OPTION_CATEGORY_PHYSICS, // PHYSICS_3D
+	BUILD_OPTION_CATEGORY_PHYSICS, // PHYSICS_GODOT_3D
+	BUILD_OPTION_CATEGORY_PHYSICS, // PHYSICS_JOLT
 	BUILD_OPTION_CATEGORY_TEXT_SERVER, // TEXT_SERVER_FALLBACK
 	BUILD_OPTION_CATEGORY_TEXT_SERVER, // TEXT_SERVER_FALLBACK
-	BUILD_OPTION_CATEGORY_TEXT_SERVER, // TEXT_SERVER_COMPLEX
+	BUILD_OPTION_CATEGORY_TEXT_SERVER, // TEXT_SERVER_ADVANCED
 	BUILD_OPTION_CATEGORY_TEXT_SERVER, // DYNAMIC_FONTS
 	BUILD_OPTION_CATEGORY_TEXT_SERVER, // DYNAMIC_FONTS
 	BUILD_OPTION_CATEGORY_TEXT_SERVER, // WOFF2_FONTS
 	BUILD_OPTION_CATEGORY_TEXT_SERVER, // WOFF2_FONTS
 	BUILD_OPTION_CATEGORY_TEXT_SERVER, // GRAPHITE_FONTS
 	BUILD_OPTION_CATEGORY_TEXT_SERVER, // GRAPHITE_FONTS
 	BUILD_OPTION_CATEGORY_TEXT_SERVER, // MSDFGEN
 	BUILD_OPTION_CATEGORY_TEXT_SERVER, // MSDFGEN
 };
 };
 
 
+// Can't assign HashMaps to a HashMap at declaration, so do it in the class' constructor.
+HashMap<EditorBuildProfile::BuildOption, HashMap<String, LocalVector<Variant>>> EditorBuildProfile::build_option_settings = {};
+
+/* clang-format off */
+
+const HashMap<EditorBuildProfile::BuildOption, LocalVector<EditorBuildProfile::BuildOption>> EditorBuildProfile::build_option_dependencies = {
+	{ BUILD_OPTION_OPENXR, {
+			BUILD_OPTION_XR,
+	} },
+	{ BUILD_OPTION_FORWARD_RENDERER, {
+			BUILD_OPTION_RENDERING_DEVICE,
+	} },
+	{ BUILD_OPTION_MOBILE_RENDERER, {
+			BUILD_OPTION_RENDERING_DEVICE,
+	} },
+	{ BUILD_OPTION_VULKAN, {
+			BUILD_OPTION_FORWARD_RENDERER,
+			BUILD_OPTION_MOBILE_RENDERER,
+	} },
+	{ BUILD_OPTION_D3D12, {
+			BUILD_OPTION_FORWARD_RENDERER,
+			BUILD_OPTION_MOBILE_RENDERER,
+	} },
+	{ BUILD_OPTION_METAL, {
+			BUILD_OPTION_FORWARD_RENDERER,
+			BUILD_OPTION_MOBILE_RENDERER,
+	} },
+	{ BUILD_OPTION_PHYSICS_GODOT_2D, {
+			BUILD_OPTION_PHYSICS_2D,
+	} },
+	{ BUILD_OPTION_PHYSICS_GODOT_3D, {
+			BUILD_OPTION_PHYSICS_3D,
+	} },
+	{ BUILD_OPTION_PHYSICS_JOLT, {
+			BUILD_OPTION_PHYSICS_3D,
+	} },
+	{ BUILD_OPTION_DYNAMIC_FONTS, {
+			BUILD_OPTION_TEXT_SERVER_ADVANCED,
+	} },
+	{ BUILD_OPTION_WOFF2_FONTS, {
+			BUILD_OPTION_TEXT_SERVER_ADVANCED,
+	} },
+	{ BUILD_OPTION_GRAPHITE_FONTS, {
+			BUILD_OPTION_TEXT_SERVER_ADVANCED,
+	} },
+};
+
+const HashMap<EditorBuildProfile::BuildOption, LocalVector<String>> EditorBuildProfile::build_option_classes = {
+	{ BUILD_OPTION_3D, {
+			"Node3D",
+	} },
+	{ BUILD_OPTION_NAVIGATION_2D, {
+			"NavigationAgent2D",
+			"NavigationLink2D",
+			"NavigationMeshSourceGeometryData2D",
+			"NavigationObstacle2D"
+			"NavigationPolygon",
+			"NavigationRegion2D",
+	} },
+	{ BUILD_OPTION_NAVIGATION_3D, {
+			"NavigationAgent3D",
+			"NavigationLink3D",
+			"NavigationMeshSourceGeometryData3D",
+			"NavigationObstacle3D",
+			"NavigationRegion3D",
+	} },
+	{ BUILD_OPTION_XR, {
+			"XRBodyModifier3D",
+			"XRBodyTracker",
+			"XRControllerTracker",
+			"XRFaceModifier3D",
+			"XRFaceTracker",
+			"XRHandModifier3D",
+			"XRHandTracker",
+			"XRInterface",
+			"XRInterfaceExtension",
+			"XRNode3D",
+			"XROrigin3D",
+			"XRPose",
+			"XRPositionalTracker",
+			"XRServer",
+			"XRTracker",
+			"XRVRS",
+	} },
+	{ BUILD_OPTION_RENDERING_DEVICE, {
+			"RenderingDevice",
+	} },
+	{ BUILD_OPTION_PHYSICS_2D, {
+			"CollisionObject2D",
+			"CollisionPolygon2D",
+			"CollisionShape2D",
+			"Joint2D",
+			"PhysicsServer2D",
+			"PhysicsServer2DManager",
+			"ShapeCast2D",
+			"RayCast2D",
+			"TouchScreenButton",
+	} },
+	{ BUILD_OPTION_PHYSICS_3D, {
+			"CollisionObject3D",
+			"CollisionPolygon3D",
+			"CollisionShape3D",
+			"CSGShape3D",
+			"GPUParticlesAttractor3D",
+			"GPUParticlesCollision3D",
+			"Joint3D",
+			"PhysicalBoneSimulator3D",
+			"PhysicsServer3D",
+			"PhysicsServer3DManager",
+			"PhysicsServer3DRenderingServerHandler",
+			"RayCast3D",
+			"SoftBody3D",
+			"SpringArm3D",
+			"SpringBoneCollision3D",
+			"SpringBoneSimulator3D",
+			"VehicleWheel3D",
+	} },
+	{ BUILD_OPTION_TEXT_SERVER_ADVANCED, {
+			"CanvasItem",
+			"Label3D",
+			"TextServerAdvanced",
+	} },
+};
+
+/* clang-format on */
+
 void EditorBuildProfile::set_disable_class(const StringName &p_class, bool p_disabled) {
 void EditorBuildProfile::set_disable_class(const StringName &p_class, bool p_disabled) {
 	if (p_disabled) {
 	if (p_disabled) {
 		disabled_classes.insert(p_class);
 		disabled_classes.insert(p_class);
@@ -159,6 +361,17 @@ bool EditorBuildProfile::get_build_option_disable_value(BuildOption p_build_opti
 	return build_option_disable_values[p_build_option];
 	return build_option_disable_values[p_build_option];
 }
 }
 
 
+bool EditorBuildProfile::get_build_option_explicit_use(BuildOption p_build_option) {
+	ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, false);
+	return build_option_explicit_use[p_build_option];
+}
+
+void EditorBuildProfile::reset_build_options() {
+	for (int i = 0; i < EditorBuildProfile::BUILD_OPTION_MAX; i++) {
+		build_options_disabled[i] = build_option_disabled_by_default[i];
+	}
+}
+
 void EditorBuildProfile::set_force_detect_classes(const String &p_classes) {
 void EditorBuildProfile::set_force_detect_classes(const String &p_classes) {
 	force_detect_classes = p_classes;
 	force_detect_classes = p_classes;
 }
 }
@@ -171,13 +384,24 @@ String EditorBuildProfile::get_build_option_name(BuildOption p_build_option) {
 	ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, String());
 	ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, String());
 	const char *build_option_names[BUILD_OPTION_MAX] = {
 	const char *build_option_names[BUILD_OPTION_MAX] = {
 		TTRC("3D Engine"),
 		TTRC("3D Engine"),
-		TTRC("2D Physics"),
-		TTRC("3D Physics"),
-		TTRC("Navigation"),
+		TTRC("Navigation (2D)"),
+		TTRC("Navigation (3D)"),
 		TTRC("XR"),
 		TTRC("XR"),
+		TTRC("OpenXR"),
+		TTRC("Wayland"),
+		TTRC("X11"),
 		TTRC("RenderingDevice"),
 		TTRC("RenderingDevice"),
-		TTRC("OpenGL"),
+		TTRC("Forward+ Renderer"),
+		TTRC("Mobile Renderer"),
 		TTRC("Vulkan"),
 		TTRC("Vulkan"),
+		TTRC("D3D12"),
+		TTRC("Metal"),
+		TTRC("OpenGL"),
+		TTRC("Physics Server (2D)"),
+		TTRC("Godot Physics (2D)"),
+		TTRC("Physics Server (3D)"),
+		TTRC("Godot Physics (3D)"),
+		TTRC("Jolt Physics"),
 		TTRC("Text Server: Fallback"),
 		TTRC("Text Server: Fallback"),
 		TTRC("Text Server: Advanced"),
 		TTRC("Text Server: Advanced"),
 		TTRC("TTF, OTF, Type 1, WOFF1 Fonts"),
 		TTRC("TTF, OTF, Type 1, WOFF1 Fonts"),
@@ -193,13 +417,24 @@ String EditorBuildProfile::get_build_option_description(BuildOption p_build_opti
 
 
 	const char *build_option_descriptions[BUILD_OPTION_MAX] = {
 	const char *build_option_descriptions[BUILD_OPTION_MAX] = {
 		TTRC("3D Nodes as well as RenderingServer access to 3D features."),
 		TTRC("3D Nodes as well as RenderingServer access to 3D features."),
-		TTRC("2D Physics nodes and PhysicsServer2D."),
-		TTRC("3D Physics nodes and PhysicsServer3D."),
-		TTRC("Navigation, both 2D and 3D."),
+		TTRC("Navigation Server and capabilities for 2D."),
+		TTRC("Navigation Server and capabilities for 3D."),
 		TTRC("XR (AR and VR)."),
 		TTRC("XR (AR and VR)."),
-		TTRC("RenderingDevice based rendering (if disabled, the OpenGL back-end is required)."),
-		TTRC("OpenGL back-end (if disabled, the RenderingDevice back-end is required)."),
-		TTRC("Vulkan back-end of RenderingDevice."),
+		TTRC("OpenXR standard implementation (requires XR to be enabled)."),
+		TTRC("Wayland display (Linux only)."),
+		TTRC("X11 display (Linux only)."),
+		TTRC("RenderingDevice based rendering (if disabled, the OpenGL backend is required)."),
+		TTRC("Forward+ renderer for advanced 3D graphics."),
+		TTRC("Mobile renderer for less advanced 3D graphics."),
+		TTRC("Vulkan backend of RenderingDevice."),
+		TTRC("Direct3D 12 backend of RenderingDevice."),
+		TTRC("Metal backend of RenderingDevice (Apple arm64 only)."),
+		TTRC("OpenGL backend (if disabled, the RenderingDevice backend is required)."),
+		TTRC("Physics Server and capabilities for 2D."),
+		TTRC("Godot Physics backend (2D)."),
+		TTRC("Physics Server and capabilities for 3D."),
+		TTRC("Godot Physics backend (3D)."),
+		TTRC("Jolt Physics backend (3D only)."),
 		TTRC("Fallback implementation of Text Server\nSupports basic text layouts."),
 		TTRC("Fallback implementation of Text Server\nSupports basic text layouts."),
 		TTRC("Text Server implementation powered by ICU and HarfBuzz libraries.\nSupports complex text layouts, BiDi, and contextual OpenType font features."),
 		TTRC("Text Server implementation powered by ICU and HarfBuzz libraries.\nSupports complex text layouts, BiDi, and contextual OpenType font features."),
 		TTRC("TrueType, OpenType, Type 1, and WOFF1 font format support using FreeType library (if disabled, WOFF2 support is also disabled)."),
 		TTRC("TrueType, OpenType, Type 1, and WOFF1 font format support using FreeType library (if disabled, WOFF2 support is also disabled)."),
@@ -211,16 +446,38 @@ String EditorBuildProfile::get_build_option_description(BuildOption p_build_opti
 	return TTRGET(build_option_descriptions[p_build_option]);
 	return TTRGET(build_option_descriptions[p_build_option]);
 }
 }
 
 
+String EditorBuildProfile::get_build_option_identifier(BuildOption p_build_option) {
+	ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, String());
+	return build_option_identifiers[p_build_option];
+}
+
 EditorBuildProfile::BuildOptionCategory EditorBuildProfile::get_build_option_category(BuildOption p_build_option) {
 EditorBuildProfile::BuildOptionCategory EditorBuildProfile::get_build_option_category(BuildOption p_build_option) {
 	ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, BUILD_OPTION_CATEGORY_GENERAL);
 	ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, BUILD_OPTION_CATEGORY_GENERAL);
 	return build_option_category[p_build_option];
 	return build_option_category[p_build_option];
 }
 }
 
 
+LocalVector<EditorBuildProfile::BuildOption> EditorBuildProfile::get_build_option_dependencies(BuildOption p_build_option) {
+	ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, LocalVector<EditorBuildProfile::BuildOption>());
+	return build_option_dependencies.has(p_build_option) ? build_option_dependencies[p_build_option] : LocalVector<EditorBuildProfile::BuildOption>();
+}
+
+HashMap<String, LocalVector<Variant>> EditorBuildProfile::get_build_option_settings(BuildOption p_build_option) {
+	ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, (HashMap<String, LocalVector<Variant>>()));
+	return build_option_settings.has(p_build_option) ? build_option_settings[p_build_option] : HashMap<String, LocalVector<Variant>>();
+}
+
+LocalVector<String> EditorBuildProfile::get_build_option_classes(BuildOption p_build_option) {
+	ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, LocalVector<String>());
+	return build_option_classes.has(p_build_option) ? build_option_classes[p_build_option] : LocalVector<String>();
+}
+
 String EditorBuildProfile::get_build_option_category_name(BuildOptionCategory p_build_option_category) {
 String EditorBuildProfile::get_build_option_category_name(BuildOptionCategory p_build_option_category) {
 	ERR_FAIL_INDEX_V(p_build_option_category, BUILD_OPTION_CATEGORY_MAX, String());
 	ERR_FAIL_INDEX_V(p_build_option_category, BUILD_OPTION_CATEGORY_MAX, String());
 
 
 	const char *build_option_subcategories[BUILD_OPTION_CATEGORY_MAX]{
 	const char *build_option_subcategories[BUILD_OPTION_CATEGORY_MAX]{
 		TTRC("General Features:"),
 		TTRC("General Features:"),
+		TTRC("Graphics and Rendering:"),
+		TTRC("Physics Systems:"),
 		TTRC("Text Rendering and Font Options:"),
 		TTRC("Text Rendering and Font Options:"),
 	};
 	};
 
 
@@ -332,13 +589,24 @@ void EditorBuildProfile::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("load_from_file", "path"), &EditorBuildProfile::load_from_file);
 	ClassDB::bind_method(D_METHOD("load_from_file", "path"), &EditorBuildProfile::load_from_file);
 
 
 	BIND_ENUM_CONSTANT(BUILD_OPTION_3D);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_3D);
-	BIND_ENUM_CONSTANT(BUILD_OPTION_PHYSICS_2D);
-	BIND_ENUM_CONSTANT(BUILD_OPTION_PHYSICS_3D);
-	BIND_ENUM_CONSTANT(BUILD_OPTION_NAVIGATION);
+	BIND_ENUM_CONSTANT(BUILD_OPTION_NAVIGATION_2D);
+	BIND_ENUM_CONSTANT(BUILD_OPTION_NAVIGATION_3D);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_XR);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_XR);
+	BIND_ENUM_CONSTANT(BUILD_OPTION_OPENXR);
+	BIND_ENUM_CONSTANT(BUILD_OPTION_WAYLAND);
+	BIND_ENUM_CONSTANT(BUILD_OPTION_X11);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_RENDERING_DEVICE);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_RENDERING_DEVICE);
-	BIND_ENUM_CONSTANT(BUILD_OPTION_OPENGL);
+	BIND_ENUM_CONSTANT(BUILD_OPTION_FORWARD_RENDERER);
+	BIND_ENUM_CONSTANT(BUILD_OPTION_MOBILE_RENDERER);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_VULKAN);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_VULKAN);
+	BIND_ENUM_CONSTANT(BUILD_OPTION_D3D12);
+	BIND_ENUM_CONSTANT(BUILD_OPTION_METAL);
+	BIND_ENUM_CONSTANT(BUILD_OPTION_OPENGL);
+	BIND_ENUM_CONSTANT(BUILD_OPTION_PHYSICS_2D);
+	BIND_ENUM_CONSTANT(BUILD_OPTION_PHYSICS_GODOT_2D);
+	BIND_ENUM_CONSTANT(BUILD_OPTION_PHYSICS_3D);
+	BIND_ENUM_CONSTANT(BUILD_OPTION_PHYSICS_GODOT_3D);
+	BIND_ENUM_CONSTANT(BUILD_OPTION_PHYSICS_JOLT);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_TEXT_SERVER_FALLBACK);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_TEXT_SERVER_FALLBACK);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_TEXT_SERVER_ADVANCED);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_TEXT_SERVER_ADVANCED);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_DYNAMIC_FONTS);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_DYNAMIC_FONTS);
@@ -348,14 +616,78 @@ void EditorBuildProfile::_bind_methods() {
 	BIND_ENUM_CONSTANT(BUILD_OPTION_MAX);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_MAX);
 
 
 	BIND_ENUM_CONSTANT(BUILD_OPTION_CATEGORY_GENERAL);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_CATEGORY_GENERAL);
+	BIND_ENUM_CONSTANT(BUILD_OPTION_CATEGORY_GRAPHICS);
+	BIND_ENUM_CONSTANT(BUILD_OPTION_CATEGORY_PHYSICS);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_CATEGORY_TEXT_SERVER);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_CATEGORY_TEXT_SERVER);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_CATEGORY_MAX);
 	BIND_ENUM_CONSTANT(BUILD_OPTION_CATEGORY_MAX);
 }
 }
 
 
 EditorBuildProfile::EditorBuildProfile() {
 EditorBuildProfile::EditorBuildProfile() {
-	for (int i = 0; i < EditorBuildProfile::BUILD_OPTION_MAX; i++) {
-		build_options_disabled[i] = build_option_disabled_by_default[i];
-	}
+	reset_build_options();
+
+	HashMap<String, LocalVector<Variant>> settings_openxr = {
+		{ "xr/openxr/enabled", { true } },
+	};
+	build_option_settings.insert(BUILD_OPTION_OPENXR, settings_openxr);
+	HashMap<String, LocalVector<Variant>> settings_wayland = {
+		{ "display/display_server/driver.linuxbsd", { "default", "wayland" } },
+	};
+	build_option_settings.insert(BUILD_OPTION_OPENXR, settings_wayland);
+	HashMap<String, LocalVector<Variant>> settings_x11 = {
+		{ "display/display_server/driver.linuxbsd", { "default", "x11" } },
+	};
+	build_option_settings.insert(BUILD_OPTION_OPENXR, settings_x11);
+	HashMap<String, LocalVector<Variant>> settings_rd = {
+		{ "rendering/renderer/rendering_method", { "forward_plus", "mobile" } },
+		{ "rendering/renderer/rendering_method.mobile", { "forward_plus", "mobile" } },
+		{ "rendering/renderer/rendering_method.web", { "forward_plus", "mobile" } },
+	};
+	build_option_settings.insert(BUILD_OPTION_RENDERING_DEVICE, settings_rd);
+	HashMap<String, LocalVector<Variant>> settings_vulkan = {
+		{ "rendering/rendering_device/driver", { "vulkan" } },
+		{ "rendering/rendering_device/driver.windows", { "vulkan" } },
+		{ "rendering/rendering_device/driver.linuxbsd", { "vulkan" } },
+		{ "rendering/rendering_device/driver.android", { "vulkan" } },
+		{ "rendering/rendering_device/driver.ios", { "vulkan" } },
+		{ "rendering/rendering_device/driver.macos", { "vulkan" } },
+		{ "rendering/rendering_device/fallback_to_vulkan", { true } },
+	};
+	build_option_settings.insert(BUILD_OPTION_VULKAN, settings_vulkan);
+	HashMap<String, LocalVector<Variant>> settings_d3d12 = {
+		{ "rendering/rendering_device/driver", { "d3d12" } },
+		{ "rendering/rendering_device/driver.windows", { "d3d12" } },
+		{ "rendering/rendering_device/driver.linuxbsd", { "d3d12" } },
+		{ "rendering/rendering_device/driver.android", { "d3d12" } },
+		{ "rendering/rendering_device/driver.ios", { "d3d12" } },
+		{ "rendering/rendering_device/driver.macos", { "d3d12" } },
+		{ "rendering/rendering_device/fallback_to_d3d12", { true } },
+	};
+	build_option_settings.insert(BUILD_OPTION_VULKAN, settings_vulkan);
+	HashMap<String, LocalVector<Variant>> settings_metal = {
+		{ "rendering/rendering_device/driver", { "metal" } },
+		{ "rendering/rendering_device/driver.ios", { "metal" } },
+		{ "rendering/rendering_device/driver.macos", { "metal" } },
+	};
+	build_option_settings.insert(BUILD_OPTION_METAL, settings_metal);
+	HashMap<String, LocalVector<Variant>> settings_opengl = {
+		{ "rendering/renderer/rendering_method", { "gl_compatibility" } },
+		{ "rendering/renderer/rendering_method.mobile", { "gl_compatibility" } },
+		{ "rendering/renderer/rendering_method.web", { "gl_compatibility" } },
+		{ "rendering/rendering_device/fallback_to_opengl3", { true } },
+	};
+	build_option_settings.insert(BUILD_OPTION_OPENGL, settings_opengl);
+	HashMap<String, LocalVector<Variant>> settings_phy_godot_3d = {
+		{ "physics/3d/physics_engine", { "DEFAULT", "GodotPhysics3D" } },
+	};
+	build_option_settings.insert(BUILD_OPTION_PHYSICS_GODOT_3D, settings_phy_godot_3d);
+	HashMap<String, LocalVector<Variant>> settings_jolt = {
+		{ "physics/3d/physics_engine", { "Jolt Physics" } },
+	};
+	build_option_settings.insert(BUILD_OPTION_PHYSICS_JOLT, settings_jolt);
+	HashMap<String, LocalVector<Variant>> settings_msdfgen = {
+		{ "gui/theme/default_font_multichannel_signed_distance_field", { true } },
+	};
+	build_option_settings.insert(BUILD_OPTION_MSDFGEN, settings_msdfgen);
 }
 }
 
 
 //////////////////////////
 //////////////////////////
@@ -406,7 +738,11 @@ void EditorBuildProfileManager::_profile_action(int p_action) {
 			confirm_dialog->popup_centered();
 			confirm_dialog->popup_centered();
 		} break;
 		} break;
 		case ACTION_DETECT: {
 		case ACTION_DETECT: {
-			confirm_dialog->set_text(TTR("This will scan all files in the current project to detect used classes."));
+			String text = TTR("This will scan all files in the current project to detect used classes.\nNote that the first scan may take a while, specially in larger projects.");
+#ifdef MODULE_MONO_ENABLED
+			text += "\n\n" + TTR("Warning: Class detection for C# scripts is not currently available, and such files will be ignored.");
+#endif // MODULE_MONO_ENABLED
+			confirm_dialog->set_text(text);
 			confirm_dialog->popup_centered();
 			confirm_dialog->popup_centered();
 		} break;
 		} break;
 		case ACTION_MAX: {
 		case ACTION_MAX: {
@@ -415,23 +751,35 @@ void EditorBuildProfileManager::_profile_action(int p_action) {
 }
 }
 
 
 void EditorBuildProfileManager::_find_files(EditorFileSystemDirectory *p_dir, const HashMap<String, DetectedFile> &p_cache, HashMap<String, DetectedFile> &r_detected) {
 void EditorBuildProfileManager::_find_files(EditorFileSystemDirectory *p_dir, const HashMap<String, DetectedFile> &p_cache, HashMap<String, DetectedFile> &r_detected) {
-	if (p_dir == nullptr) {
+	if (p_dir == nullptr || p_dir->get_path().get_file().begins_with(".")) {
 		return;
 		return;
 	}
 	}
 
 
 	for (int i = 0; i < p_dir->get_file_count(); i++) {
 	for (int i = 0; i < p_dir->get_file_count(); i++) {
 		String p = p_dir->get_file_path(i);
 		String p = p_dir->get_file_path(i);
 
 
+		if (EditorNode::get_singleton()->progress_task_step("detect_classes_from_project", p, 1)) {
+			project_scan_canceled = true;
+			return;
+		}
+
+		String p_check = p;
+		// Make so that the import file is the one checked if available,
+		// so the cache can be updated when it changes.
+		if (ResourceFormatImporter::get_singleton()->exists(p_check)) {
+			p_check += ".import";
+		}
+
 		uint64_t timestamp = 0;
 		uint64_t timestamp = 0;
 		String md5;
 		String md5;
 
 
 		if (p_cache.has(p)) {
 		if (p_cache.has(p)) {
 			const DetectedFile &cache = p_cache[p];
 			const DetectedFile &cache = p_cache[p];
 			// Check if timestamp and MD5 match.
 			// Check if timestamp and MD5 match.
-			timestamp = FileAccess::get_modified_time(p);
+			timestamp = FileAccess::get_modified_time(p_check);
 			bool cache_valid = true;
 			bool cache_valid = true;
 			if (cache.timestamp != timestamp) {
 			if (cache.timestamp != timestamp) {
-				md5 = FileAccess::get_md5(p);
+				md5 = FileAccess::get_md5(p_check);
 				if (md5 != cache.md5) {
 				if (md5 != cache.md5) {
 					cache_valid = false;
 					cache_valid = false;
 				}
 				}
@@ -449,14 +797,19 @@ void EditorBuildProfileManager::_find_files(EditorFileSystemDirectory *p_dir, co
 
 
 		HashSet<StringName> classes;
 		HashSet<StringName> classes;
 		ResourceLoader::get_classes_used(p, &classes);
 		ResourceLoader::get_classes_used(p, &classes);
-
 		for (const StringName &E : classes) {
 		for (const StringName &E : classes) {
 			cache.classes.push_back(E);
 			cache.classes.push_back(E);
 		}
 		}
 
 
+		HashSet<String> build_deps;
+		ResourceFormatImporter::get_singleton()->get_build_dependencies(p, &build_deps);
+		for (const String &E : build_deps) {
+			cache.build_deps.push_back(E);
+		}
+
 		if (md5.is_empty()) {
 		if (md5.is_empty()) {
-			cache.timestamp = FileAccess::get_modified_time(p);
-			cache.md5 = FileAccess::get_md5(p);
+			cache.timestamp = FileAccess::get_modified_time(p_check);
+			cache.md5 = FileAccess::get_md5(p_check);
 		} else {
 		} else {
 			cache.timestamp = timestamp;
 			cache.timestamp = timestamp;
 			cache.md5 = md5;
 			cache.md5 = md5;
@@ -470,7 +823,9 @@ void EditorBuildProfileManager::_find_files(EditorFileSystemDirectory *p_dir, co
 	}
 	}
 }
 }
 
 
-void EditorBuildProfileManager::_detect_classes() {
+void EditorBuildProfileManager::_detect_from_project() {
+	EditorNode::get_singleton()->progress_add_task("detect_classes_from_project", TTRC("Scanning Project for Used Classes"), 3, true);
+
 	HashMap<String, DetectedFile> previous_file_cache;
 	HashMap<String, DetectedFile> previous_file_cache;
 
 
 	Ref<FileAccess> f = FileAccess::open(EditorPaths::get_singleton()->get_project_settings_dir().path_join("used_class_cache"), FileAccess::READ);
 	Ref<FileAccess> f = FileAccess::open(EditorPaths::get_singleton()->get_project_settings_dir().path_join("used_class_cache"), FileAccess::READ);
@@ -478,12 +833,13 @@ void EditorBuildProfileManager::_detect_classes() {
 		while (!f->eof_reached()) {
 		while (!f->eof_reached()) {
 			String l = f->get_line();
 			String l = f->get_line();
 			Vector<String> fields = l.split("::");
 			Vector<String> fields = l.split("::");
-			if (fields.size() == 4) {
+			if (fields.size() == 5) {
 				const String &path = fields[0];
 				const String &path = fields[0];
 				DetectedFile df;
 				DetectedFile df;
 				df.timestamp = fields[1].to_int();
 				df.timestamp = fields[1].to_int();
 				df.md5 = fields[2];
 				df.md5 = fields[2];
-				df.classes = fields[3].split(",");
+				df.classes = fields[3].split(",", false);
+				df.build_deps = fields[4].split(",", false);
 				previous_file_cache.insert(path, df);
 				previous_file_cache.insert(path, df);
 			}
 			}
 		}
 		}
@@ -494,7 +850,17 @@ void EditorBuildProfileManager::_detect_classes() {
 
 
 	_find_files(EditorFileSystem::get_singleton()->get_filesystem(), previous_file_cache, updated_file_cache);
 	_find_files(EditorFileSystem::get_singleton()->get_filesystem(), previous_file_cache, updated_file_cache);
 
 
+	if (project_scan_canceled) {
+		project_scan_canceled = false;
+		EditorNode::get_singleton()->progress_end_task("detect_classes_from_project");
+
+		return;
+	}
+
+	EditorNode::get_singleton()->progress_task_step("detect_classes_from_project", TTRC("Processing Classes Found"), 2);
+
 	HashSet<StringName> used_classes;
 	HashSet<StringName> used_classes;
+	LocalVector<String> used_build_deps;
 
 
 	// Find classes and update the disk cache in the process.
 	// Find classes and update the disk cache in the process.
 	f = FileAccess::open(EditorPaths::get_singleton()->get_project_settings_dir().path_join("used_class_cache"), FileAccess::WRITE);
 	f = FileAccess::open(EditorPaths::get_singleton()->get_project_settings_dir().path_join("used_class_cache"), FileAccess::WRITE);
@@ -509,20 +875,41 @@ void EditorBuildProfileManager::_detect_classes() {
 			l += c;
 			l += c;
 			used_classes.insert(c);
 			used_classes.insert(c);
 		}
 		}
+		l += "::";
+		for (int i = 0; i < E.value.build_deps.size(); i++) {
+			String c = E.value.build_deps[i];
+			if (i > 0) {
+				l += ",";
+			}
+			l += c;
+			used_build_deps.push_back(c);
+		}
 		f->store_line(l);
 		f->store_line(l);
 	}
 	}
 
 
 	f.unref();
 	f.unref();
 
 
-	// Add forced ones.
+	// Add classes that are either necessary for the engine to work properly, or there isn't a way to infer their use.
 
 
-	Vector<String> force_detect = edited->get_force_detect_classes().split(",");
-	for (int i = 0; i < force_detect.size(); i++) {
-		String c = force_detect[i].strip_edges();
-		if (c.is_empty()) {
-			continue;
+	const LocalVector<String> hardcoded_classes = { "InputEvent", "MainLoop", "StyleBox" };
+	for (const String &hc_class : hardcoded_classes) {
+		used_classes.insert(hc_class);
+
+		LocalVector<StringName> inheriters;
+		ClassDB::get_inheriters_from_class(hc_class, inheriters);
+		for (const StringName &inheriter : inheriters) {
+			used_classes.insert(inheriter);
+		}
+	}
+
+	// Add forced classes typed by the user.
+
+	const Vector<String> force_detect = edited->get_force_detect_classes().split(",");
+	for (const String &class_name : force_detect) {
+		const String class_stripped = class_name.strip_edges();
+		if (!class_stripped.is_empty()) {
+			used_classes.insert(class_stripped);
 		}
 		}
-		used_classes.insert(c);
 	}
 	}
 
 
 	// Filter all classes to discard inherited ones.
 	// Filter all classes to discard inherited ones.
@@ -532,13 +919,23 @@ void EditorBuildProfileManager::_detect_classes() {
 	for (const StringName &E : used_classes) {
 	for (const StringName &E : used_classes) {
 		StringName c = E;
 		StringName c = E;
 		if (!ClassDB::class_exists(c)) {
 		if (!ClassDB::class_exists(c)) {
-			// Maybe this is an old class that got replaced? try getting compat class.
+			// Maybe this is an old class that got replaced? Try getting compat class.
 			c = ClassDB::get_compatibility_class(c);
 			c = ClassDB::get_compatibility_class(c);
 			if (!c) {
 			if (!c) {
 				// No luck, skip.
 				// No luck, skip.
 				continue;
 				continue;
 			}
 			}
 		}
 		}
+
+		List<StringName> dependencies;
+		ClassDB::get_class_dependencies(E, &dependencies);
+		for (const StringName &dep : dependencies) {
+			if (!all_used_classes.has(dep)) {
+				// Add classes which this class depends upon.
+				all_used_classes.insert(dep);
+			}
+		}
+
 		while (c) {
 		while (c) {
 			all_used_classes.insert(c);
 			all_used_classes.insert(c);
 			c = ClassDB::get_parent_class(c);
 			c = ClassDB::get_parent_class(c);
@@ -551,8 +948,8 @@ void EditorBuildProfileManager::_detect_classes() {
 	ClassDB::get_class_list(&all_classes);
 	ClassDB::get_class_list(&all_classes);
 
 
 	for (const StringName &E : all_classes) {
 	for (const StringName &E : all_classes) {
-		if (all_used_classes.has(E)) {
-			// This class is valid, do nothing.
+		if (String(E).begins_with("Editor") || ClassDB::get_api_type(E) != ClassDB::API_CORE || all_used_classes.has(E)) {
+			// This class is valid or editor-only, do nothing.
 			continue;
 			continue;
 		}
 		}
 
 
@@ -563,6 +960,88 @@ void EditorBuildProfileManager::_detect_classes() {
 			edited->set_disable_class(E, true);
 			edited->set_disable_class(E, true);
 		}
 		}
 	}
 	}
+
+	edited->reset_build_options();
+
+	for (int i = 0; i < EditorBuildProfile::BUILD_OPTION_MAX; i++) {
+		// Check if the build option requires other options that are currently disabled.
+		LocalVector<EditorBuildProfile::BuildOption> dependencies = EditorBuildProfile::get_build_option_dependencies(EditorBuildProfile::BuildOption(i));
+		if (!dependencies.is_empty()) {
+			bool disable = true;
+			for (EditorBuildProfile::BuildOption dependency : dependencies) {
+				if (!edited->is_build_option_disabled(dependency)) {
+					disable = false;
+					break;
+				}
+			}
+
+			if (disable) {
+				edited->set_disable_build_option(EditorBuildProfile::BuildOption(i), true);
+				continue;
+			}
+		}
+
+		bool skip = false;
+		bool ignore = true;
+
+		// Check if the build option has enabled classes using it.
+		const LocalVector<String> classes = EditorBuildProfile::get_build_option_classes(EditorBuildProfile::BuildOption(i));
+		if (!classes.is_empty()) {
+			for (StringName class_name : classes) {
+				if (!edited->is_class_disabled(class_name)) {
+					skip = true;
+					break;
+				}
+			}
+
+			if (skip) {
+				continue;
+			}
+
+			ignore = false;
+		}
+
+		// Check if there's project settings requiring it.
+		const HashMap<String, LocalVector<Variant>> settings_list = EditorBuildProfile::get_build_option_settings(EditorBuildProfile::BuildOption(i));
+		if (!settings_list.is_empty()) {
+			for (KeyValue<String, LocalVector<Variant>> KV : settings_list) {
+				Variant proj_value = GLOBAL_GET(KV.key);
+				for (Variant value : KV.value) {
+					if (proj_value == value) {
+						skip = true;
+						break;
+					}
+				}
+
+				if (skip) {
+					break;
+				}
+			}
+
+			if (skip) {
+				continue;
+			}
+
+			ignore = false;
+		}
+
+		// Check if a resource setting depends on it.
+		if (used_build_deps.has(EditorBuildProfile::get_build_option_identifier(EditorBuildProfile::BuildOption(i)))) {
+			continue;
+		} else if (EditorBuildProfile::get_build_option_explicit_use(EditorBuildProfile::BuildOption(i))) {
+			ignore = false;
+		}
+
+		if (!skip && !ignore) {
+			edited->set_disable_build_option(EditorBuildProfile::BuildOption(i), true);
+		}
+	}
+
+	if (edited->is_build_option_disabled(EditorBuildProfile::BUILD_OPTION_TEXT_SERVER_ADVANCED)) {
+		edited->set_disable_build_option(EditorBuildProfile::BUILD_OPTION_TEXT_SERVER_FALLBACK, false);
+	}
+
+	EditorNode::get_singleton()->progress_end_task("detect_classes_from_project");
 }
 }
 
 
 void EditorBuildProfileManager::_action_confirm() {
 void EditorBuildProfileManager::_action_confirm() {
@@ -583,7 +1062,7 @@ void EditorBuildProfileManager::_action_confirm() {
 			_update_edited_profile();
 			_update_edited_profile();
 		} break;
 		} break;
 		case ACTION_DETECT: {
 		case ACTION_DETECT: {
-			_detect_classes();
+			_detect_from_project();
 			_update_edited_profile();
 			_update_edited_profile();
 		} break;
 		} break;
 		case ACTION_MAX: {
 		case ACTION_MAX: {
@@ -869,7 +1348,7 @@ EditorBuildProfileManager::EditorBuildProfileManager() {
 	add_child(import_profile);
 	add_child(import_profile);
 	import_profile->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
 	import_profile->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
 	import_profile->add_filter("*.gdbuild,*.build", TTR("Engine Compilation Profile"));
 	import_profile->add_filter("*.gdbuild,*.build", TTR("Engine Compilation Profile"));
-	import_profile->connect("files_selected", callable_mp(this, &EditorBuildProfileManager::_import_profile));
+	import_profile->connect("file_selected", callable_mp(this, &EditorBuildProfileManager::_import_profile));
 	import_profile->set_title(TTR("Load Profile"));
 	import_profile->set_title(TTR("Load Profile"));
 	import_profile->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
 	import_profile->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
 
 

+ 33 - 5
editor/editor_build_profile.h

@@ -41,13 +41,24 @@ class EditorBuildProfile : public RefCounted {
 public:
 public:
 	enum BuildOption {
 	enum BuildOption {
 		BUILD_OPTION_3D,
 		BUILD_OPTION_3D,
-		BUILD_OPTION_PHYSICS_2D,
-		BUILD_OPTION_PHYSICS_3D,
-		BUILD_OPTION_NAVIGATION,
+		BUILD_OPTION_NAVIGATION_2D,
+		BUILD_OPTION_NAVIGATION_3D,
 		BUILD_OPTION_XR,
 		BUILD_OPTION_XR,
+		BUILD_OPTION_OPENXR,
+		BUILD_OPTION_WAYLAND,
+		BUILD_OPTION_X11,
 		BUILD_OPTION_RENDERING_DEVICE,
 		BUILD_OPTION_RENDERING_DEVICE,
-		BUILD_OPTION_OPENGL,
+		BUILD_OPTION_FORWARD_RENDERER,
+		BUILD_OPTION_MOBILE_RENDERER,
 		BUILD_OPTION_VULKAN,
 		BUILD_OPTION_VULKAN,
+		BUILD_OPTION_D3D12,
+		BUILD_OPTION_METAL,
+		BUILD_OPTION_OPENGL,
+		BUILD_OPTION_PHYSICS_2D,
+		BUILD_OPTION_PHYSICS_GODOT_2D,
+		BUILD_OPTION_PHYSICS_3D,
+		BUILD_OPTION_PHYSICS_GODOT_3D,
+		BUILD_OPTION_PHYSICS_JOLT,
 		BUILD_OPTION_TEXT_SERVER_FALLBACK,
 		BUILD_OPTION_TEXT_SERVER_FALLBACK,
 		BUILD_OPTION_TEXT_SERVER_ADVANCED,
 		BUILD_OPTION_TEXT_SERVER_ADVANCED,
 		BUILD_OPTION_DYNAMIC_FONTS,
 		BUILD_OPTION_DYNAMIC_FONTS,
@@ -59,6 +70,8 @@ public:
 
 
 	enum BuildOptionCategory {
 	enum BuildOptionCategory {
 		BUILD_OPTION_CATEGORY_GENERAL,
 		BUILD_OPTION_CATEGORY_GENERAL,
+		BUILD_OPTION_CATEGORY_GRAPHICS,
+		BUILD_OPTION_CATEGORY_PHYSICS,
 		BUILD_OPTION_CATEGORY_TEXT_SERVER,
 		BUILD_OPTION_CATEGORY_TEXT_SERVER,
 		BUILD_OPTION_CATEGORY_MAX,
 		BUILD_OPTION_CATEGORY_MAX,
 	};
 	};
@@ -74,7 +87,11 @@ private:
 	static const char *build_option_identifiers[BUILD_OPTION_MAX];
 	static const char *build_option_identifiers[BUILD_OPTION_MAX];
 	static const bool build_option_disabled_by_default[BUILD_OPTION_MAX];
 	static const bool build_option_disabled_by_default[BUILD_OPTION_MAX];
 	static const bool build_option_disable_values[BUILD_OPTION_MAX];
 	static const bool build_option_disable_values[BUILD_OPTION_MAX];
+	static const bool build_option_explicit_use[BUILD_OPTION_MAX];
 	static const BuildOptionCategory build_option_category[BUILD_OPTION_MAX];
 	static const BuildOptionCategory build_option_category[BUILD_OPTION_MAX];
+	static const HashMap<BuildOption, LocalVector<BuildOption>> build_option_dependencies;
+	static HashMap<BuildOption, HashMap<String, LocalVector<Variant>>> build_option_settings;
+	static const HashMap<BuildOption, LocalVector<String>> build_option_classes;
 
 
 	String _get_build_option_name(BuildOption p_build_option) { return get_build_option_name(p_build_option); }
 	String _get_build_option_name(BuildOption p_build_option) { return get_build_option_name(p_build_option); }
 
 
@@ -91,6 +108,8 @@ public:
 	void set_disable_build_option(BuildOption p_build_option, bool p_disable);
 	void set_disable_build_option(BuildOption p_build_option, bool p_disable);
 	bool is_build_option_disabled(BuildOption p_build_option) const;
 	bool is_build_option_disabled(BuildOption p_build_option) const;
 
 
+	void reset_build_options();
+
 	void set_force_detect_classes(const String &p_classes);
 	void set_force_detect_classes(const String &p_classes);
 	String get_force_detect_classes() const;
 	String get_force_detect_classes() const;
 
 
@@ -101,8 +120,13 @@ public:
 
 
 	static String get_build_option_name(BuildOption p_build_option);
 	static String get_build_option_name(BuildOption p_build_option);
 	static String get_build_option_description(BuildOption p_build_option);
 	static String get_build_option_description(BuildOption p_build_option);
+	static String get_build_option_identifier(BuildOption p_build_option);
 	static bool get_build_option_disable_value(BuildOption p_build_option);
 	static bool get_build_option_disable_value(BuildOption p_build_option);
+	static bool get_build_option_explicit_use(BuildOption p_build_option);
 	static BuildOptionCategory get_build_option_category(BuildOption p_build_option);
 	static BuildOptionCategory get_build_option_category(BuildOption p_build_option);
+	static LocalVector<BuildOption> get_build_option_dependencies(BuildOption p_build_option);
+	static HashMap<String, LocalVector<Variant>> get_build_option_settings(BuildOption p_build_option);
+	static LocalVector<String> get_build_option_classes(BuildOption p_build_option);
 
 
 	static String get_build_option_category_name(BuildOptionCategory p_build_option_category);
 	static String get_build_option_category_name(BuildOptionCategory p_build_option_category);
 
 
@@ -160,7 +184,10 @@ class EditorBuildProfileManager : public AcceptDialog {
 	void _class_list_item_selected();
 	void _class_list_item_selected();
 	void _class_list_item_edited();
 	void _class_list_item_edited();
 	void _class_list_item_collapsed(Object *p_item);
 	void _class_list_item_collapsed(Object *p_item);
-	void _detect_classes();
+
+	bool project_scan_canceled = false;
+
+	void _detect_from_project();
 
 
 	void _force_detect_classes_changed(const String &p_text);
 	void _force_detect_classes_changed(const String &p_text);
 
 
@@ -168,6 +195,7 @@ class EditorBuildProfileManager : public AcceptDialog {
 		uint32_t timestamp = 0;
 		uint32_t timestamp = 0;
 		String md5;
 		String md5;
 		Vector<String> classes;
 		Vector<String> classes;
+		Vector<String> build_deps;
 	};
 	};
 
 
 	void _find_files(EditorFileSystemDirectory *p_dir, const HashMap<String, DetectedFile> &p_cache, HashMap<String, DetectedFile> &r_detected);
 	void _find_files(EditorFileSystemDirectory *p_dir, const HashMap<String, DetectedFile> &p_cache, HashMap<String, DetectedFile> &r_detected);

+ 7 - 0
editor/import/resource_importer_dynamic_font.cpp

@@ -65,6 +65,13 @@ String ResourceImporterDynamicFont::get_resource_type() const {
 	return "FontFile";
 	return "FontFile";
 }
 }
 
 
+void ResourceImporterDynamicFont::get_build_dependencies(const String &p_path, HashSet<String> *r_dependencies) {
+	Ref<FontFile> font = ResourceLoader::load(p_path);
+	if (font.is_valid() && font->is_multichannel_signed_distance_field()) {
+		r_dependencies->insert("module_msdfgen_enabled");
+	}
+}
+
 bool ResourceImporterDynamicFont::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
 bool ResourceImporterDynamicFont::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
 	if (p_option == "msdf_pixel_range" && !bool(p_options["multichannel_signed_distance_field"])) {
 	if (p_option == "msdf_pixel_range" && !bool(p_options["multichannel_signed_distance_field"])) {
 		return false;
 		return false;

+ 1 - 0
editor/import/resource_importer_dynamic_font.h

@@ -47,6 +47,7 @@ public:
 	virtual void get_recognized_extensions(List<String> *p_extensions) const override;
 	virtual void get_recognized_extensions(List<String> *p_extensions) const override;
 	virtual String get_save_extension() const override;
 	virtual String get_save_extension() const override;
 	virtual String get_resource_type() const override;
 	virtual String get_resource_type() const override;
+	virtual void get_build_dependencies(const String &p_path, HashSet<String> *r_build_dependencies) override;
 
 
 	virtual int get_preset_count() const override;
 	virtual int get_preset_count() const override;
 	virtual String get_preset_name(int p_idx) const override;
 	virtual String get_preset_name(int p_idx) const override;

+ 4 - 0
scene/gui/color_picker.cpp

@@ -2114,6 +2114,10 @@ void ColorPicker::_bind_methods() {
 	BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, ColorPicker, mode_button_normal, "tab_unselected", "TabContainer");
 	BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, ColorPicker, mode_button_normal, "tab_unselected", "TabContainer");
 	BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, ColorPicker, mode_button_pressed, "tab_selected", "TabContainer");
 	BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, ColorPicker, mode_button_pressed, "tab_selected", "TabContainer");
 	BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, ColorPicker, mode_button_hover, "tab_selected", "TabContainer");
 	BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, ColorPicker, mode_button_hover, "tab_selected", "TabContainer");
+
+	ADD_CLASS_DEPENDENCY("LineEdit");
+	ADD_CLASS_DEPENDENCY("MenuButton");
+	ADD_CLASS_DEPENDENCY("PopupMenu");
 }
 }
 
 
 ColorPicker::ColorPicker() {
 ColorPicker::ColorPicker() {

+ 4 - 0
scene/gui/dialogs.cpp

@@ -443,6 +443,8 @@ void AcceptDialog::_bind_methods() {
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, AcceptDialog, buttons_separation);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, AcceptDialog, buttons_separation);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, AcceptDialog, buttons_min_width);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, AcceptDialog, buttons_min_width);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, AcceptDialog, buttons_min_height);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, AcceptDialog, buttons_min_height);
+
+	ADD_CLASS_DEPENDENCY("Button");
 }
 }
 
 
 void AcceptDialog::_validate_property(PropertyInfo &p_property) const {
 void AcceptDialog::_validate_property(PropertyInfo &p_property) const {
@@ -510,6 +512,8 @@ void ConfirmationDialog::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_cancel_button_text"), &ConfirmationDialog::get_cancel_button_text);
 	ClassDB::bind_method(D_METHOD("get_cancel_button_text"), &ConfirmationDialog::get_cancel_button_text);
 
 
 	ADD_PROPERTY(PropertyInfo(Variant::STRING, "cancel_button_text"), "set_cancel_button_text", "get_cancel_button_text");
 	ADD_PROPERTY(PropertyInfo(Variant::STRING, "cancel_button_text"), "set_cancel_button_text", "get_cancel_button_text");
+
+	ADD_CLASS_DEPENDENCY("Button");
 }
 }
 
 
 Button *ConfirmationDialog::get_cancel_button() {
 Button *ConfirmationDialog::get_cancel_button() {

+ 6 - 0
scene/gui/file_dialog.cpp

@@ -1900,6 +1900,12 @@ void FileDialog::_bind_methods() {
 	base_property_helper.register_property(PropertyInfo(Variant::PACKED_STRING_ARRAY, "values"), defaults.values, &FileDialog::set_option_values, &FileDialog::get_option_values);
 	base_property_helper.register_property(PropertyInfo(Variant::PACKED_STRING_ARRAY, "values"), defaults.values, &FileDialog::set_option_values, &FileDialog::get_option_values);
 	base_property_helper.register_property(PropertyInfo(Variant::INT, "default"), defaults.default_idx, &FileDialog::set_option_default, &FileDialog::get_option_default);
 	base_property_helper.register_property(PropertyInfo(Variant::INT, "default"), defaults.default_idx, &FileDialog::set_option_default, &FileDialog::get_option_default);
 	PropertyListHelper::register_base_helper(&base_property_helper);
 	PropertyListHelper::register_base_helper(&base_property_helper);
+
+	ADD_CLASS_DEPENDENCY("Button");
+	ADD_CLASS_DEPENDENCY("ConfirmationDialog");
+	ADD_CLASS_DEPENDENCY("LineEdit");
+	ADD_CLASS_DEPENDENCY("OptionButton");
+	ADD_CLASS_DEPENDENCY("Tree");
 }
 }
 
 
 void FileDialog::set_show_hidden_files(bool p_show) {
 void FileDialog::set_show_hidden_files(bool p_show) {

+ 7 - 0
scene/gui/graph_edit.cpp

@@ -3129,6 +3129,13 @@ void GraphEdit::_bind_methods() {
 
 
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, GraphEdit, port_hotzone_inner_extent);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, GraphEdit, port_hotzone_inner_extent);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, GraphEdit, port_hotzone_outer_extent);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, GraphEdit, port_hotzone_outer_extent);
+
+	ADD_CLASS_DEPENDENCY("Button");
+	ADD_CLASS_DEPENDENCY("GraphFrame");
+	ADD_CLASS_DEPENDENCY("GraphNode");
+	ADD_CLASS_DEPENDENCY("HScrollBar");
+	ADD_CLASS_DEPENDENCY("SpinBox");
+	ADD_CLASS_DEPENDENCY("VScrollBar");
 }
 }
 
 
 GraphEdit::GraphEdit() {
 GraphEdit::GraphEdit() {

+ 2 - 0
scene/gui/line_edit.cpp

@@ -3305,6 +3305,8 @@ void LineEdit::_bind_methods() {
 	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, LineEdit, clear_icon, "clear");
 	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, LineEdit, clear_icon, "clear");
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LineEdit, clear_button_color);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LineEdit, clear_button_color);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LineEdit, clear_button_color_pressed);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, LineEdit, clear_button_color_pressed);
+
+	ADD_CLASS_DEPENDENCY("PopupMenu");
 }
 }
 
 
 LineEdit::LineEdit(const String &p_placeholder) {
 LineEdit::LineEdit(const String &p_placeholder) {

+ 2 - 0
scene/gui/menu_bar.cpp

@@ -763,6 +763,8 @@ void MenuBar::_bind_methods() {
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, MenuBar, font_focus_color);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, MenuBar, font_focus_color);
 
 
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, MenuBar, h_separation);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, MenuBar, h_separation);
+
+	ADD_CLASS_DEPENDENCY("PopupMenu");
 }
 }
 
 
 void MenuBar::set_switch_on_hover(bool p_enabled) {
 void MenuBar::set_switch_on_hover(bool p_enabled) {

+ 2 - 0
scene/gui/menu_button.cpp

@@ -194,6 +194,8 @@ void MenuButton::_bind_methods() {
 
 
 	ADD_SIGNAL(MethodInfo("about_to_popup"));
 	ADD_SIGNAL(MethodInfo("about_to_popup"));
 
 
+	ADD_CLASS_DEPENDENCY("PopupMenu");
+
 	PopupMenu::Item defaults(true);
 	PopupMenu::Item defaults(true);
 
 
 	base_property_helper.set_prefix("popup/item_");
 	base_property_helper.set_prefix("popup/item_");

+ 2 - 0
scene/gui/option_button.cpp

@@ -625,6 +625,8 @@ void OptionButton::_bind_methods() {
 	base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled, &OptionButton::_dummy_setter, &OptionButton::is_item_disabled);
 	base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled, &OptionButton::_dummy_setter, &OptionButton::is_item_disabled);
 	base_property_helper.register_property(PropertyInfo(Variant::BOOL, "separator"), defaults.separator, &OptionButton::_dummy_setter, &OptionButton::is_item_separator);
 	base_property_helper.register_property(PropertyInfo(Variant::BOOL, "separator"), defaults.separator, &OptionButton::_dummy_setter, &OptionButton::is_item_separator);
 	PropertyListHelper::register_base_helper(&base_property_helper);
 	PropertyListHelper::register_base_helper(&base_property_helper);
+
+	ADD_CLASS_DEPENDENCY("PopupMenu");
 }
 }
 
 
 void OptionButton::set_disable_shortcuts(bool p_disabled) {
 void OptionButton::set_disable_shortcuts(bool p_disabled) {

+ 2 - 0
scene/gui/rich_text_label.cpp

@@ -7416,6 +7416,8 @@ void RichTextLabel::_bind_methods() {
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, RichTextLabel, table_odd_row_bg);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, RichTextLabel, table_odd_row_bg);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, RichTextLabel, table_even_row_bg);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, RichTextLabel, table_even_row_bg);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, RichTextLabel, table_border);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, RichTextLabel, table_border);
+
+	ADD_CLASS_DEPENDENCY("PopupMenu");
 }
 }
 
 
 TextServer::VisibleCharactersBehavior RichTextLabel::get_visible_characters_behavior() const {
 TextServer::VisibleCharactersBehavior RichTextLabel::get_visible_characters_behavior() const {

+ 2 - 0
scene/gui/spin_box.cpp

@@ -691,6 +691,8 @@ void SpinBox::_bind_methods() {
 
 
 	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, field_and_buttons_separator, "field_and_buttons_separator");
 	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, field_and_buttons_separator, "field_and_buttons_separator");
 	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, up_down_buttons_separator, "up_down_buttons_separator");
 	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, up_down_buttons_separator, "up_down_buttons_separator");
+
+	ADD_CLASS_DEPENDENCY("LineEdit");
 }
 }
 
 
 SpinBox::SpinBox() {
 SpinBox::SpinBox() {

+ 2 - 0
scene/gui/tab_container.cpp

@@ -1128,6 +1128,8 @@ void TabContainer::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_tab_focus_mode", "get_tab_focus_mode");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_tab_focus_mode", "get_tab_focus_mode");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_enabled"), "set_deselect_enabled", "get_deselect_enabled");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_enabled"), "set_deselect_enabled", "get_deselect_enabled");
 
 
+	ADD_CLASS_DEPENDENCY("TabBar");
+
 	BIND_ENUM_CONSTANT(POSITION_TOP);
 	BIND_ENUM_CONSTANT(POSITION_TOP);
 	BIND_ENUM_CONSTANT(POSITION_BOTTOM);
 	BIND_ENUM_CONSTANT(POSITION_BOTTOM);
 	BIND_ENUM_CONSTANT(POSITION_MAX);
 	BIND_ENUM_CONSTANT(POSITION_MAX);

+ 6 - 0
scene/gui/text_edit.cpp

@@ -7629,6 +7629,12 @@ void TextEdit::_bind_methods() {
 	/* Settings. */
 	/* Settings. */
 	GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "gui/timers/text_edit_idle_detect_sec", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 3);
 	GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "gui/timers/text_edit_idle_detect_sec", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 3);
 	GLOBAL_DEF(PropertyInfo(Variant::INT, "gui/common/text_edit_undo_stack_max_size", PROPERTY_HINT_RANGE, "0,10000,1,or_greater"), 1024);
 	GLOBAL_DEF(PropertyInfo(Variant::INT, "gui/common/text_edit_undo_stack_max_size", PROPERTY_HINT_RANGE, "0,10000,1,or_greater"), 1024);
+
+	/* Dependencies */
+	ADD_CLASS_DEPENDENCY("HScrollBar");
+	ADD_CLASS_DEPENDENCY("PopupMenu");
+	ADD_CLASS_DEPENDENCY("Timer");
+	ADD_CLASS_DEPENDENCY("VScrollBar");
 }
 }
 
 
 /* Internal API for CodeEdit. */
 /* Internal API for CodeEdit. */

+ 8 - 0
scene/gui/tree.cpp

@@ -6655,6 +6655,14 @@ void Tree::_bind_methods() {
 	BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, title_button_pressed);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, title_button_pressed);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, title_button_hover);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, title_button_hover);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, title_button_color);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, title_button_color);
+
+	ADD_CLASS_DEPENDENCY("HScrollBar");
+	ADD_CLASS_DEPENDENCY("HSlider");
+	ADD_CLASS_DEPENDENCY("LineEdit");
+	ADD_CLASS_DEPENDENCY("Popup");
+	ADD_CLASS_DEPENDENCY("TextEdit");
+	ADD_CLASS_DEPENDENCY("Timer");
+	ADD_CLASS_DEPENDENCY("VScrollBar");
 }
 }
 
 
 Tree::Tree() {
 Tree::Tree() {

+ 8 - 0
scene/resources/3d/world_3d.cpp

@@ -177,18 +177,26 @@ World3D::World3D() {
 
 
 World3D::~World3D() {
 World3D::~World3D() {
 	ERR_FAIL_NULL(RenderingServer::get_singleton());
 	ERR_FAIL_NULL(RenderingServer::get_singleton());
+
 #ifndef PHYSICS_3D_DISABLED
 #ifndef PHYSICS_3D_DISABLED
 	ERR_FAIL_NULL(PhysicsServer3D::get_singleton());
 	ERR_FAIL_NULL(PhysicsServer3D::get_singleton());
 #endif // PHYSICS_3D_DISABLED
 #endif // PHYSICS_3D_DISABLED
+
+#ifndef NAVIGATION_3D_DISABLED
 	ERR_FAIL_NULL(NavigationServer3D::get_singleton());
 	ERR_FAIL_NULL(NavigationServer3D::get_singleton());
+#endif // NAVIGATION_3D_DISABLED
 
 
 	RenderingServer::get_singleton()->free(scenario);
 	RenderingServer::get_singleton()->free(scenario);
+
 #ifndef PHYSICS_3D_DISABLED
 #ifndef PHYSICS_3D_DISABLED
 	if (space.is_valid()) {
 	if (space.is_valid()) {
 		PhysicsServer3D::get_singleton()->free(space);
 		PhysicsServer3D::get_singleton()->free(space);
 	}
 	}
 #endif // PHYSICS_3D_DISABLED
 #endif // PHYSICS_3D_DISABLED
+
+#ifndef NAVIGATION_3D_DISABLED
 	if (navigation_map.is_valid()) {
 	if (navigation_map.is_valid()) {
 		NavigationServer3D::get_singleton()->free(navigation_map);
 		NavigationServer3D::get_singleton()->free(navigation_map);
 	}
 	}
+#endif // NAVIGATION_3D_DISABLED
 }
 }

+ 5 - 10
scene/resources/resource_format_text.cpp

@@ -1223,15 +1223,10 @@ Error ResourceLoaderText::get_classes_used(HashSet<StringName> *r_classes) {
 			return error;
 			return error;
 		}
 		}
 
 
-		if (!next_tag.fields.has("type")) {
-			error = ERR_FILE_CORRUPT;
-			error_text = "Missing 'type' in external resource tag";
-			_printerr();
-			return error;
+		if (next_tag.fields.has("type")) {
+			r_classes->insert(next_tag.fields["type"]);
 		}
 		}
 
 
-		r_classes->insert(next_tag.fields["type"]);
-
 		while (true) {
 		while (true) {
 			String assign;
 			String assign;
 			Variant value;
 			Variant value;
@@ -1447,9 +1442,9 @@ bool ResourceFormatLoaderText::handles_type(const String &p_type) const {
 }
 }
 
 
 void ResourceFormatLoaderText::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
 void ResourceFormatLoaderText::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
-	String ext = p_path.get_extension().to_lower();
-	if (ext == "tscn") {
-		r_classes->insert("PackedScene");
+	const String type = get_resource_type(p_path);
+	if (!type.is_empty()) {
+		r_classes->insert(type);
 	}
 	}
 
 
 	// ...for anything else must test...
 	// ...for anything else must test...