소스 검색

Single Compilation Unit build.

Adds support for simple SCU build.
This speeds up compilation by compiling multiple cpp files within a single translation unit.
lawnjelly 2 년 전
부모
커밋
43e181a00a

+ 6 - 0
SConstruct

@@ -14,6 +14,7 @@ from collections import OrderedDict
 # Local
 import methods
 import gles_builders
+import scu_builders
 from platform_methods import run_in_subprocess
 
 # scan possible build platforms
@@ -155,6 +156,7 @@ opts.Add(BoolVariable("disable_advanced_gui", "Disable advanced GUI nodes and be
 opts.Add(BoolVariable("no_editor_splash", "Don't use the custom splash screen for the editor", True))
 opts.Add("system_certs_path", "Use this path as SSL certificates default for editor (for package maintainers)", "")
 opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise epsilon (debug option)", False))
+opts.Add(BoolVariable("scu_build", "Use single compilation unit build", False))
 opts.Add(
     EnumVariable(
         "rids",
@@ -442,6 +444,10 @@ if selected_platform in platform_list:
                 "for an optimized template with debug features)."
             )
 
+    # Run SCU file generation script if in a SCU build.
+    if env["scu_build"]:
+        methods.set_scu_folders(scu_builders.generate_scu_files(env["verbose"], env_base["target"] != "debug"))
+
     # Must happen after the flags' definition, as configure is when most flags
     # are actually handled to change compile options, etc.
     detect.configure(env)

+ 2 - 0
core/color_names.inc

@@ -1,4 +1,6 @@
 // Names from https://en.wikipedia.org/wiki/X11_color_names
+#pragma once
+
 #include "core/map.h"
 
 static Map<String, Color> _named_colors;

+ 2 - 1
core/make_binders.py

@@ -86,7 +86,8 @@ MethodBind* create_method_bind($ifret R$ $ifnoret void$ (T::*p_method)($arg, P@$
 #endif
 """
 
-template = """
+template = """#pragma once
+
 #ifndef TYPED_METHOD_BIND
 $iftempl template<$ $ifret class R$ $ifretargs ,$ $arg, class P@$ $iftempl >$
 class MethodBind$argc$$ifret R$$ifconst C$ : public MethodBind {

+ 2 - 2
drivers/gles2/rasterizer_scene_gles2.cpp

@@ -53,7 +53,7 @@
 #endif
 #endif
 
-static const GLenum _cube_side_enum[6] = {
+const GLenum RasterizerSceneGLES2::_cube_side_enum[6] = {
 
 	GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
 	GL_TEXTURE_CUBE_MAP_POSITIVE_X,
@@ -1332,7 +1332,7 @@ void RasterizerSceneGLES2::_fill_render_list(InstanceBase **p_cull_result, int p
 	}
 }
 
-static const GLenum gl_primitive[] = {
+const GLenum RasterizerSceneGLES2::gl_primitive[] = {
 	GL_POINTS,
 	GL_LINES,
 	GL_LINE_STRIP,

+ 2 - 0
drivers/gles2/rasterizer_scene_gles2.h

@@ -89,6 +89,8 @@ public:
 
 private:
 	uint32_t _light_counter;
+	static const GLenum gl_primitive[];
+	static const GLenum _cube_side_enum[6];
 
 public:
 	RasterizerStorageGLES2 *storage;

+ 1 - 1
drivers/gles2/shader_gles2.cpp

@@ -126,7 +126,7 @@ static void _display_error_with_code(const String &p_error, const Vector<const c
 	ERR_PRINT(p_error);
 }
 
-static String _mkid(const String &p_id) {
+String ShaderGLES2::_mkid(const String &p_id) {
 	String id = "m_" + p_id;
 	return id.replace("__", "_dus_"); //doubleunderscore is reserved in glsl
 }

+ 2 - 0
drivers/gles2/shader_gles2.h

@@ -51,6 +51,8 @@
 class RasterizerStorageGLES2;
 
 class ShaderGLES2 {
+	static String _mkid(const String &p_id);
+
 protected:
 	struct Enum {
 		uint64_t mask;

+ 4 - 4
drivers/gles3/rasterizer_scene_gles3.cpp

@@ -41,7 +41,7 @@
 #define glClearDepth glClearDepthf
 #endif
 
-static const GLenum _cube_side_enum[6] = {
+const GLenum RasterizerSceneGLES3::_cube_side_enum[6] = {
 
 	GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
 	GL_TEXTURE_CUBE_MAP_POSITIVE_X,
@@ -52,7 +52,7 @@ static const GLenum _cube_side_enum[6] = {
 
 };
 
-static _FORCE_INLINE_ void store_transform(const Transform &p_mtx, float *p_array) {
+void RasterizerSceneGLES3::store_transform(const Transform &p_mtx, float *p_array) {
 	p_array[0] = p_mtx.basis.elements[0][0];
 	p_array[1] = p_mtx.basis.elements[1][0];
 	p_array[2] = p_mtx.basis.elements[2][0];
@@ -71,7 +71,7 @@ static _FORCE_INLINE_ void store_transform(const Transform &p_mtx, float *p_arra
 	p_array[15] = 1;
 }
 
-static _FORCE_INLINE_ void store_camera(const CameraMatrix &p_mtx, float *p_array) {
+void RasterizerSceneGLES3::store_camera(const CameraMatrix &p_mtx, float *p_array) {
 	for (int i = 0; i < 4; i++) {
 		for (int j = 0; j < 4; j++) {
 			p_array[i * 4 + j] = p_mtx.matrix[i][j];
@@ -1482,7 +1482,7 @@ void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e, const Transfo
 	}
 }
 
-static const GLenum gl_primitive[] = {
+const GLenum RasterizerSceneGLES3::gl_primitive[] = {
 	GL_POINTS,
 	GL_LINES,
 	GL_LINE_STRIP,

+ 7 - 0
drivers/gles3/rasterizer_scene_gles3.h

@@ -863,6 +863,13 @@ public:
 	virtual void set_scene_pass(uint64_t p_pass);
 	virtual void set_debug_draw_mode(VS::ViewportDebugDraw p_debug_draw);
 
+private:
+	_FORCE_INLINE_ void store_transform(const Transform &p_mtx, float *p_array);
+	_FORCE_INLINE_ void store_camera(const CameraMatrix &p_mtx, float *p_array);
+	static const GLenum gl_primitive[];
+	static const GLenum _cube_side_enum[6];
+
+public:
 	void iteration();
 	void initialize();
 	void finalize();

+ 7 - 2
editor/SCsub

@@ -32,13 +32,18 @@ if env["tools"]:
     reg_exporters = "void register_exporters() {\n"
     for e in env.platform_exporters:
         # Glob all .cpp files in export folder
-        files = Glob("#platform/" + e + "/export/" + "*.cpp")
-        env.add_source_files(env.editor_sources, files)
+        if not env["scu_build"]:
+            files = Glob("#platform/" + e + "/export/" + "*.cpp")
+            env.add_source_files(env.editor_sources, files)
 
         reg_exporters += "\tregister_" + e + "_exporter();\n"
         reg_exporters_inc += '#include "platform/' + e + '/export/export.h"\n'
     reg_exporters += "}\n"
 
+    # If we are in a SCU build, we add all the platform exporters in one SCU file.
+    if env["scu_build"]:
+        env.add_source_files(env.editor_sources, "../platform/*.cpp")
+
     # NOTE: It is safe to generate this file here, since this is still executed serially
     with open_utf8("register_exporters.gen.cpp", "w") as f:
         f.write(reg_exporters_inc)

+ 1 - 1
editor/editor_about.cpp

@@ -37,7 +37,7 @@
 #include "editor_node.h"
 
 // The metadata key used to store and retrieve the version text to copy to the clipboard.
-static const String META_TEXT_TO_COPY = "text_to_copy";
+const String EditorAbout::META_TEXT_TO_COPY = "text_to_copy";
 
 void EditorAbout::_notification(int p_what) {
 	switch (p_what) {

+ 1 - 0
editor/editor_about.h

@@ -53,6 +53,7 @@ class EditorAbout : public AcceptDialog {
 	GDCLASS(EditorAbout, AcceptDialog);
 
 private:
+	static const String META_TEXT_TO_COPY;
 	void _license_tree_selected();
 	void _version_button_pressed();
 	ScrollContainer *_populate_list(const String &p_name, const List<String> &p_sections, const char *const *const p_src[], const int p_flag_single_column = 0);

+ 14 - 14
editor/editor_inspector.cpp

@@ -43,20 +43,6 @@
 #include "scene/property_utils.h"
 #include "scene/resources/packed_scene.h"
 
-static bool _property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style) {
-	if (p_property_path.findn(p_filter) != -1) {
-		return true;
-	}
-
-	const Vector<String> sections = p_property_path.split("/");
-	for (int i = 0; i < sections.size(); i++) {
-		if (p_filter.is_subsequence_ofi(EditorPropertyNameProcessor::get_singleton()->process_name(sections[i], p_style))) {
-			return true;
-		}
-	}
-	return false;
-}
-
 Size2 EditorProperty::get_minimum_size() const {
 	Size2 ms;
 	Ref<Font> font = get_font("font", "Tree");
@@ -1232,6 +1218,20 @@ EditorInspectorSection::~EditorInspectorSection() {
 Ref<EditorInspectorPlugin> EditorInspector::inspector_plugins[MAX_PLUGINS];
 int EditorInspector::inspector_plugin_count = 0;
 
+bool EditorInspector::_property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style) {
+	if (p_property_path.findn(p_filter) != -1) {
+		return true;
+	}
+
+	const Vector<String> sections = p_property_path.split("/");
+	for (int i = 0; i < sections.size(); i++) {
+		if (p_filter.is_subsequence_ofi(EditorPropertyNameProcessor::get_singleton()->process_name(sections[i], p_style))) {
+			return true;
+		}
+	}
+	return false;
+}
+
 EditorProperty *EditorInspector::instantiate_property_editor(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) {
 	for (int i = inspector_plugin_count - 1; i >= 0; i--) {
 		inspector_plugins[i]->parse_property(p_object, p_type, p_path, p_hint, p_hint_text, p_usage);

+ 1 - 0
editor/editor_inspector.h

@@ -319,6 +319,7 @@ class EditorInspector : public ScrollContainer {
 	void _property_keyed_with_value(const String &p_path, const Variant &p_value, bool p_advance);
 	void _property_checked(const String &p_path, bool p_checked);
 	void _property_pinned(const String &p_path, bool p_pinned);
+	bool _property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style);
 
 	void _resource_selected(const String &p_path, RES p_resource);
 	void _property_selected(const String &p_path, int p_focusable);

+ 14 - 14
editor/editor_sectioned_inspector.cpp

@@ -34,20 +34,6 @@
 #include "editor_scale.h"
 #include "editor_settings.h"
 
-static bool _property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style) {
-	if (p_property_path.findn(p_filter) != -1) {
-		return true;
-	}
-
-	const Vector<String> sections = p_property_path.split("/");
-	for (int i = 0; i < sections.size(); i++) {
-		if (p_filter.is_subsequence_ofi(EditorPropertyNameProcessor::get_singleton()->process_name(sections[i], p_style))) {
-			return true;
-		}
-	}
-	return false;
-}
-
 class SectionedInspectorFilter : public Object {
 	GDCLASS(SectionedInspectorFilter, Object);
 
@@ -145,6 +131,20 @@ public:
 	}
 };
 
+bool SectionedInspector::_property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style) {
+	if (p_property_path.findn(p_filter) != -1) {
+		return true;
+	}
+
+	const Vector<String> sections = p_property_path.split("/");
+	for (int i = 0; i < sections.size(); i++) {
+		if (p_filter.is_subsequence_ofi(EditorPropertyNameProcessor::get_singleton()->process_name(sections[i], p_style))) {
+			return true;
+		}
+	}
+	return false;
+}
+
 void SectionedInspector::_bind_methods() {
 	ClassDB::bind_method("_section_selected", &SectionedInspector::_section_selected);
 	ClassDB::bind_method("_search_changed", &SectionedInspector::_search_changed);

+ 1 - 0
editor/editor_sectioned_inspector.h

@@ -55,6 +55,7 @@ class SectionedInspector : public HSplitContainer {
 	void _section_selected();
 
 	void _search_changed(const String &p_what);
+	bool _property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style);
 
 protected:
 	void _notification(int p_what);

+ 1 - 1
editor/plugins/text_editor.cpp

@@ -494,7 +494,7 @@ void TextEditor::_bind_methods() {
 	ClassDB::bind_method("_prepare_edit_menu", &TextEditor::_prepare_edit_menu);
 }
 
-static ScriptEditorBase *create_editor(const RES &p_resource) {
+ScriptEditorBase *TextEditor::create_editor(const RES &p_resource) {
 	if (Object::cast_to<TextFile>(*p_resource)) {
 		return memnew(TextEditor);
 	}

+ 1 - 0
editor/plugins/text_editor.h

@@ -96,6 +96,7 @@ private:
 		BOOKMARK_GOTO_PREV,
 		BOOKMARK_REMOVE_ALL,
 	};
+	static ScriptEditorBase *create_editor(const RES &p_resource);
 
 protected:
 	static void _bind_methods();

+ 2 - 0
main/tests/test_string.cpp

@@ -1330,3 +1330,5 @@ MainLoop *test() {
 	return nullptr;
 }
 } // namespace TestString
+
+#undef CHECK

+ 2 - 0
main/tests/test_theme.cpp

@@ -301,3 +301,5 @@ MainLoop *test() {
 	return nullptr;
 }
 } // namespace TestTheme
+
+#undef CHECK

+ 82 - 2
methods.py

@@ -13,8 +13,23 @@ from SCons.Script import ARGUMENTS
 from SCons.Script import Glob
 from SCons.Variables.BoolVariable import _text2bool
 
+from pathlib import Path
+from os.path import normpath, basename
 
-def add_source_files(self, sources, files):
+# Get the "Godot" folder name ahead of time
+base_folder_path = str(os.path.abspath(Path(__file__).parent)) + "/"
+base_folder_only = os.path.basename(os.path.normpath(base_folder_path))
+# Listing all the folders we have converted
+# for SCU in scu_builders.py
+_scu_folders = set()
+
+
+def set_scu_folders(scu_folders):
+    global _scu_folders
+    _scu_folders = scu_folders
+
+
+def add_source_files_orig(self, sources, files, allow_gen=False):
     # Convert string to list of absolute paths (including expanding wildcard)
     if isbasestring(files):
         # Keep SCons project-absolute path as they are (no wildcard support)
@@ -29,7 +44,7 @@ def add_source_files(self, sources, files):
             skip_gen_cpp = "*" in files
             dir_path = self.Dir(".").abspath
             files = sorted(glob.glob(dir_path + "/" + files))
-            if skip_gen_cpp:
+            if skip_gen_cpp and not allow_gen:
                 files = [f for f in files if not f.endswith(".gen.cpp")]
 
     # Add each path as compiled Object following environment (self) configuration
@@ -41,6 +56,71 @@ def add_source_files(self, sources, files):
         sources.append(obj)
 
 
+# The section name is used for checking
+# the hash table to see whether the folder
+# is included in the SCU build.
+# It will be something like "core/math".
+def _find_scu_section_name(subdir):
+    section_path = os.path.abspath(subdir) + "/"
+
+    folders = []
+    folder = ""
+
+    for i in range(8):
+        folder = os.path.dirname(section_path)
+        folder = os.path.basename(folder)
+        if folder == base_folder_only:
+            break
+        folders += [folder]
+        section_path += "../"
+        section_path = os.path.abspath(section_path) + "/"
+
+    section_name = ""
+    for n in range(len(folders)):
+        section_name += folders[len(folders) - n - 1]
+        if n != (len(folders) - 1):
+            section_name += "/"
+
+    return section_name
+
+
+def add_source_files_scu(self, sources, files, allow_gen=False):
+    if self["scu_build"] and isinstance(files, str):
+        if "*." not in files:
+            return False
+
+        # If the files are in a subdirectory, we want to create the scu gen
+        # files inside this subdirectory.
+        subdir = os.path.dirname(files)
+        if subdir != "":
+            subdir += "/"
+
+        section_name = _find_scu_section_name(subdir)
+        # if the section name is in the hash table?
+        # i.e. is it part of the SCU build?
+        global _scu_folders
+        if section_name not in (_scu_folders):
+            return False
+
+        if self["verbose"]:
+            print("SCU building " + section_name)
+
+        # Add all the gen.cpp files in the SCU directory
+        add_source_files_orig(self, sources, subdir + ".scu/scu_*.gen.cpp", True)
+        return True
+    return False
+
+
+# Either builds the folder using the SCU system,
+# or reverts to regular build.
+def add_source_files(self, sources, files, allow_gen=False):
+    if not add_source_files_scu(self, sources, files, allow_gen):
+        # Wraps the original function when scu build is not active.
+        add_source_files_orig(self, sources, files, allow_gen)
+        return False
+    return True
+
+
 def disable_warnings(self):
     # 'self' is the environment
     if self.msvc:

+ 3 - 24
modules/visual_script/visual_script_editor.cpp

@@ -2227,27 +2227,6 @@ bool VisualScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &
 	return false;
 }
 
-static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const Ref<Script> &script) {
-	if (p_edited_scene != p_current_node && p_current_node->get_owner() != p_edited_scene) {
-		return nullptr;
-	}
-
-	Ref<Script> scr = p_current_node->get_script();
-
-	if (scr.is_valid() && scr == script) {
-		return p_current_node;
-	}
-
-	for (int i = 0; i < p_current_node->get_child_count(); i++) {
-		Node *n = _find_script_node(p_edited_scene, p_current_node->get_child(i), script);
-		if (n) {
-			return n;
-		}
-	}
-
-	return nullptr;
-}
-
 void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
 	if (p_from != graph) {
 		return;
@@ -2441,7 +2420,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
 	}
 
 	if (String(d["type"]) == "nodes") {
-		Node *sn = _find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script);
+		Node *sn = NSVisualScript::_find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script);
 
 		if (!sn) {
 			EditorNode::get_singleton()->show_warning(vformat(TTR("Can't drop nodes because script '%s' is not used in this scene."), get_name()));
@@ -2495,7 +2474,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
 	}
 
 	if (String(d["type"]) == "obj_property") {
-		Node *sn = _find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script);
+		Node *sn = NSVisualScript::_find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script);
 
 		if (!sn && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
 			EditorNode::get_singleton()->show_warning(vformat(TTR("Can't drop properties because script '%s' is not used in this scene.\nDrop holding 'Shift' to just copy the signature."), get_name()));
@@ -4145,7 +4124,7 @@ void VisualScriptEditor::_default_value_edited(Node *p_button, int p_id, int p_i
 	if (pinfo.type == Variant::NODE_PATH) {
 		Node *edited_scene = get_tree()->get_edited_scene_root();
 		if (edited_scene) { // Fixing an old crash bug ( Visual Script Crashes on editing NodePath with an empty scene open)
-			Node *script_node = _find_script_node(edited_scene, edited_scene, script);
+			Node *script_node = NSVisualScript::_find_script_node(edited_scene, edited_scene, script);
 
 			if (script_node) {
 				//pick a node relative to the script, IF the script exists

+ 3 - 26
modules/visual_script/visual_script_func_nodes.cpp

@@ -53,30 +53,7 @@ int VisualScriptFunctionCall::get_output_sequence_port_count() const {
 bool VisualScriptFunctionCall::has_input_sequence_port() const {
 	return !((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function)));
 }
-#ifdef TOOLS_ENABLED
-
-static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const Ref<Script> &script) {
-	if (p_edited_scene != p_current_node && p_current_node->get_owner() != p_edited_scene) {
-		return nullptr;
-	}
-
-	Ref<Script> scr = p_current_node->get_script();
-
-	if (scr.is_valid() && scr == script) {
-		return p_current_node;
-	}
 
-	for (int i = 0; i < p_current_node->get_child_count(); i++) {
-		Node *n = _find_script_node(p_edited_scene, p_current_node->get_child(i), script);
-		if (n) {
-			return n;
-		}
-	}
-
-	return nullptr;
-}
-
-#endif
 Node *VisualScriptFunctionCall::_get_base_node() const {
 #ifdef TOOLS_ENABLED
 	Ref<Script> script = get_visual_script();
@@ -97,7 +74,7 @@ Node *VisualScriptFunctionCall::_get_base_node() const {
 		return nullptr;
 	}
 
-	Node *script_node = _find_script_node(edited_scene, edited_scene, script);
+	Node *script_node = NSVisualScript::_find_script_node(edited_scene, edited_scene, script);
 
 	if (!script_node) {
 		return nullptr;
@@ -933,7 +910,7 @@ Node *VisualScriptPropertySet::_get_base_node() const {
 		return nullptr;
 	}
 
-	Node *script_node = _find_script_node(edited_scene, edited_scene, script);
+	Node *script_node = NSVisualScript::_find_script_node(edited_scene, edited_scene, script);
 
 	if (!script_node) {
 		return nullptr;
@@ -1682,7 +1659,7 @@ Node *VisualScriptPropertyGet::_get_base_node() const {
 		return nullptr;
 	}
 
-	Node *script_node = _find_script_node(edited_scene, edited_scene, script);
+	Node *script_node = NSVisualScript::_find_script_node(edited_scene, edited_scene, script);
 
 	if (!script_node) {
 		return nullptr;

+ 6 - 4
modules/visual_script/visual_script_nodes.cpp

@@ -2342,7 +2342,8 @@ VisualScriptNodeInstance *VisualScriptSceneNode::instance(VisualScriptInstance *
 
 #ifdef TOOLS_ENABLED
 
-static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const Ref<Script> &script) {
+namespace NSVisualScript {
+Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const Ref<Script> &script) {
 	if (p_edited_scene != p_current_node && p_current_node->get_owner() != p_edited_scene) {
 		return nullptr;
 	}
@@ -2354,7 +2355,7 @@ static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const
 	}
 
 	for (int i = 0; i < p_current_node->get_child_count(); i++) {
-		Node *n = _find_script_node(p_edited_scene, p_current_node->get_child(i), script);
+		Node *n = NSVisualScript::_find_script_node(p_edited_scene, p_current_node->get_child(i), script);
 		if (n) {
 			return n;
 		}
@@ -2362,6 +2363,7 @@ static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const
 
 	return nullptr;
 }
+} //namespace NSVisualScript
 
 #endif
 
@@ -2389,7 +2391,7 @@ VisualScriptSceneNode::TypeGuess VisualScriptSceneNode::guess_output_type(TypeGu
 		return tg;
 	}
 
-	Node *script_node = _find_script_node(edited_scene, edited_scene, script);
+	Node *script_node = NSVisualScript::_find_script_node(edited_scene, edited_scene, script);
 
 	if (!script_node) {
 		return tg;
@@ -2426,7 +2428,7 @@ void VisualScriptSceneNode::_validate_property(PropertyInfo &property) const {
 			return;
 		}
 
-		Node *script_node = _find_script_node(edited_scene, edited_scene, script);
+		Node *script_node = NSVisualScript::_find_script_node(edited_scene, edited_scene, script);
 
 		if (!script_node) {
 			return;

+ 6 - 0
modules/visual_script/visual_script_nodes.h

@@ -1060,4 +1060,10 @@ public:
 void register_visual_script_nodes();
 void unregister_visual_script_nodes();
 
+#ifdef TOOLS_ENABLED
+namespace NSVisualScript {
+Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const Ref<Script> &script);
+}
+#endif
+
 #endif // VISUAL_SCRIPT_NODES_H

+ 1 - 24
modules/visual_script/visual_script_yield_nodes.cpp

@@ -217,30 +217,7 @@ int VisualScriptYieldSignal::get_output_sequence_port_count() const {
 bool VisualScriptYieldSignal::has_input_sequence_port() const {
 	return true;
 }
-#ifdef TOOLS_ENABLED
-
-static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const Ref<Script> &script) {
-	if (p_edited_scene != p_current_node && p_current_node->get_owner() != p_edited_scene) {
-		return nullptr;
-	}
-
-	Ref<Script> scr = p_current_node->get_script();
-
-	if (scr.is_valid() && scr == script) {
-		return p_current_node;
-	}
 
-	for (int i = 0; i < p_current_node->get_child_count(); i++) {
-		Node *n = _find_script_node(p_edited_scene, p_current_node->get_child(i), script);
-		if (n) {
-			return n;
-		}
-	}
-
-	return nullptr;
-}
-
-#endif
 Node *VisualScriptYieldSignal::_get_base_node() const {
 #ifdef TOOLS_ENABLED
 	Ref<Script> script = get_visual_script();
@@ -261,7 +238,7 @@ Node *VisualScriptYieldSignal::_get_base_node() const {
 		return nullptr;
 	}
 
-	Node *script_node = _find_script_node(edited_scene, edited_scene, script);
+	Node *script_node = NSVisualScript::_find_script_node(edited_scene, edited_scene, script);
 
 	if (!script_node) {
 		return nullptr;

+ 0 - 6
scene/gui/box_container.cpp

@@ -32,12 +32,6 @@
 #include "label.h"
 #include "margin_container.h"
 
-struct _MinSizeCache {
-	int min_size;
-	bool will_stretch;
-	int final_size;
-};
-
 void BoxContainer::_resort() {
 	/** First pass, determine minimum size AND amount of stretchable elements */
 

+ 6 - 0
scene/gui/box_container.h

@@ -36,6 +36,12 @@
 class BoxContainer : public Container {
 	GDCLASS(BoxContainer, Container);
 
+	struct _MinSizeCache {
+		int min_size;
+		bool will_stretch;
+		int final_size;
+	};
+
 public:
 	enum AlignMode {
 		ALIGN_BEGIN,

+ 1 - 1
scene/gui/line_edit.cpp

@@ -44,7 +44,7 @@
 #include "editor/editor_settings.h"
 #endif
 
-static bool _is_text_char(CharType c) {
+bool LineEdit::_is_text_char(CharType c) {
 	return !is_symbol(c);
 }
 

+ 1 - 0
scene/gui/line_edit.h

@@ -150,6 +150,7 @@ private:
 	void _text_changed();
 	void _emit_text_change();
 	bool expand_to_text_length;
+	bool _is_text_char(CharType c);
 
 	void update_cached_width();
 	void update_placeholder_width();

+ 351 - 0
scu_builders.py

@@ -0,0 +1,351 @@
+"""Functions used to generate scu build source files during build time
+
+"""
+import glob, os
+import math
+from pathlib import Path
+from os.path import normpath, basename
+
+base_folder_path = str(Path(__file__).parent) + "/"
+base_folder_only = os.path.basename(os.path.normpath(base_folder_path))
+_verbose = False
+_is_release_build = False
+_scu_folders = set()
+
+
+def clear_out_existing_files(output_folder, extension):
+    output_folder = os.path.abspath(output_folder)
+    # print("clear_out_existing_files from folder: " + output_folder)
+
+    if not os.path.isdir(output_folder):
+        # folder does not exist or has not been created yet,
+        # no files to clearout. (this is not an error)
+        return
+
+    os.chdir(output_folder)
+
+    for file in glob.glob("*." + extension):
+        # print("removed pre-existing file: " + file)
+        os.remove(file)
+
+
+def folder_not_found(folder):
+    abs_folder = base_folder_path + folder + "/"
+    return not os.path.isdir(abs_folder)
+
+
+def find_files_in_folder(folder, sub_folder, include_list, extension, sought_exceptions, found_exceptions):
+    abs_folder = base_folder_path + folder + "/" + sub_folder
+
+    if not os.path.isdir(abs_folder):
+        print("ERROR " + abs_folder + " not found.")
+        return include_list, found_exceptions
+
+    os.chdir(abs_folder)
+
+    sub_folder_slashed = ""
+    if sub_folder != "":
+        sub_folder_slashed = sub_folder + "/"
+
+    for file in glob.glob("*." + extension):
+
+        simple_name = Path(file).stem
+
+        if file.endswith(".gen.cpp"):
+            continue
+
+        li = '#include "' + folder + "/" + sub_folder_slashed + file + '"'
+
+        if not simple_name in sought_exceptions:
+            include_list.append(li)
+        else:
+            found_exceptions.append(li)
+
+    return include_list, found_exceptions
+
+
+def write_output_file(file_count, include_list, start_line, end_line, output_folder, output_filename_prefix, extension):
+
+    output_folder = os.path.abspath(output_folder)
+
+    if not os.path.isdir(output_folder):
+        # create
+        os.mkdir(output_folder)
+        if not os.path.isdir(output_folder):
+            print("ERROR " + output_folder + " could not be created.")
+            return
+        print("CREATING folder " + output_folder)
+
+    file_text = ""
+
+    for l in range(start_line, end_line):
+        if l < len(include_list):
+            line = include_list[l]
+            li = line + "\n"
+            file_text += li
+
+    num_string = ""
+    if file_count > 0:
+        num_string = "_" + str(file_count)
+
+    short_filename = output_filename_prefix + num_string + ".gen." + extension
+    output_filename = output_folder + "/" + short_filename
+    if _verbose:
+        print("generating: " + short_filename)
+
+    output_path = Path(output_filename)
+    output_path.write_text(file_text, encoding="utf8")
+
+
+def write_exception_output_file(file_count, exception_string, output_folder, output_filename_prefix, extension):
+    output_folder = os.path.abspath(output_folder)
+    if not os.path.isdir(output_folder):
+        print("ERROR " + output_folder + " does not exist.")
+        return
+
+    file_text = exception_string + "\n"
+
+    num_string = ""
+    if file_count > 0:
+        num_string = "_" + str(file_count)
+
+    short_filename = output_filename_prefix + "_exception" + num_string + ".gen." + extension
+    output_filename = output_folder + "/" + short_filename
+
+    if _verbose:
+        print("generating: " + short_filename)
+
+    output_path = Path(output_filename)
+    output_path.write_text(file_text, encoding="utf8")
+
+
+def find_section_name(sub_folder):
+    # Construct a useful name for the section from the path for debug logging
+    section_path = os.path.abspath(base_folder_path + sub_folder) + "/"
+
+    folders = []
+    folder = ""
+
+    for i in range(8):
+        folder = os.path.dirname(section_path)
+        folder = os.path.basename(folder)
+        if folder == base_folder_only:
+            break
+        folders.append(folder)
+        section_path += "../"
+        section_path = os.path.abspath(section_path) + "/"
+
+    section_name = ""
+    for n in range(len(folders)):
+        section_name += folders[len(folders) - n - 1]
+        if n != (len(folders) - 1):
+            section_name += "_"
+
+    return section_name
+
+
+# "folders" is a list of folders to add all the files from to add to the SCU
+# "section (like a module)". The name of the scu file will be derived from the first folder
+# (thus e.g. scene/3d becomes scu_scene_3d.gen.cpp)
+
+# "includes_per_scu" limits the number of includes in a single scu file.
+# This allows the module to be built in several translation units instead of just 1.
+# This will usually be slower to compile but will use less memory per compiler instance, which
+# is most relevant in release builds.
+
+# "sought_exceptions" are a list of files (without extension) that contain
+# e.g. naming conflicts, and are therefore not suitable for the scu build.
+# These will automatically be placed in their own separate scu file,
+# which is slow like a normal build, but prevents the naming conflicts.
+# Ideally in these situations, the source code should be changed to prevent naming conflicts.
+
+# "extension" will usually be cpp, but can also be set to c (for e.g. third party libraries that use c)
+def process_folder(folders, sought_exceptions=[], includes_per_scu=0, extension="cpp"):
+    if len(folders) == 0:
+        return
+
+    # Construct the filename prefix from the FIRST folder name
+    # e.g. "scene_3d"
+    out_filename = find_section_name(folders[0])
+
+    found_includes = []
+    found_exceptions = []
+
+    main_folder = folders[0]
+    abs_main_folder = base_folder_path + main_folder
+
+    # Keep a record of all folders that have been processed for SCU,
+    # this enables deciding what to do when we call "add_source_files()"
+    global _scu_folders
+    _scu_folders.add(main_folder)
+
+    # main folder (first)
+    found_includes, found_exceptions = find_files_in_folder(
+        main_folder, "", found_includes, extension, sought_exceptions, found_exceptions
+    )
+
+    # sub folders
+    for d in range(1, len(folders)):
+        found_includes, found_exceptions = find_files_in_folder(
+            main_folder, folders[d], found_includes, extension, sought_exceptions, found_exceptions
+        )
+
+    found_includes = sorted(found_includes)
+
+    # calculate how many lines to write in each file
+    total_lines = len(found_includes)
+
+    # adjust number of output files according to whether DEV or release
+    num_output_files = 1
+    if _is_release_build:
+        # always have a maximum in release
+        includes_per_scu = 8
+        num_output_files = max(math.ceil(total_lines / includes_per_scu), 1)
+    else:
+        if includes_per_scu > 0:
+            num_output_files = max(math.ceil(total_lines / includes_per_scu), 1)
+
+    # error condition
+    if total_lines == 0:
+        return
+
+    lines_per_file = math.ceil(total_lines / num_output_files)
+    lines_per_file = max(lines_per_file, 1)
+
+    start_line = 0
+    file_number = 0
+
+    # These do not vary throughout the loop
+    output_folder = abs_main_folder + "/.scu/"
+    output_filename_prefix = "scu_" + out_filename
+
+    # Clear out any existing files (usually we will be overwriting,
+    # but we want to remove any that are pre-existing that will not be
+    # overwritten, so as to not compile anything stale)
+    clear_out_existing_files(output_folder, extension)
+
+    for file_count in range(0, num_output_files):
+        end_line = start_line + lines_per_file
+
+        # special case to cover rounding error in final file
+        if file_count == (num_output_files - 1):
+            end_line = len(found_includes)
+
+        write_output_file(
+            file_count, found_includes, start_line, end_line, output_folder, output_filename_prefix, extension
+        )
+
+        start_line = end_line
+
+    # Write the exceptions each in their own scu gen file,
+    # so they can effectively compile in "old style / normal build".
+    for exception_count in range(len(found_exceptions)):
+        write_exception_output_file(
+            exception_count, found_exceptions[exception_count], output_folder, output_filename_prefix, extension
+        )
+
+
+def generate_scu_files(verbose, is_release_build):
+
+    print("=============================")
+    print("Single Compilation Unit Build")
+    print("=============================")
+    print("Generating SCU build files")
+    global _verbose
+    _verbose = verbose
+    global _is_release_build
+    _is_release_build = is_release_build
+
+    curr_folder = os.path.abspath("./")
+
+    # check we are running from the correct folder
+    if folder_not_found("core") or folder_not_found("platform") or folder_not_found("scene"):
+        raise RuntimeError("scu_builders.py must be run from the godot folder.")
+        return
+
+    process_folder(["core"])
+    process_folder(["core/math"])
+    process_folder(["core/os"])
+    process_folder(["core/io"])
+    process_folder(["core/crypto"])
+
+    process_folder(["drivers/gles2"])
+    process_folder(["drivers/gles3"])
+    process_folder(["drivers/unix"])
+    process_folder(["drivers/png"])
+
+    process_folder(["editor"])
+    process_folder(["editor/import"])
+    process_folder(["editor/plugins"])
+
+    process_folder(["main"])
+    process_folder(["main/tests"])
+
+    process_folder(
+        [
+            "platform",
+            "android/export",
+            "iphone/export",
+            "javascript/export",
+            "osx/export",
+            "uwp/export",
+            "windows/export",
+            "x11/export",
+        ]
+    )
+
+    process_folder(["modules/gltf"])
+    process_folder(["modules/gltf/structures"])
+    process_folder(["modules/gltf/extensions"])
+    process_folder(["modules/gltf/extensions/physics"])
+
+    process_folder(["modules/bullet"])
+    process_folder(["modules/navigation"])
+    process_folder(["modules/visual_script"])
+    process_folder(["modules/webrtc"])
+    process_folder(["modules/webxr"])
+    process_folder(["modules/websocket"])
+    process_folder(["modules/gridmap"])
+
+    process_folder(["modules/csg"])
+    process_folder(["modules/gdscript"])
+    process_folder(["modules/gdscript/language_server"])
+
+    process_folder(["modules/fbx"])
+    process_folder(["modules/fbx/tools"])
+    process_folder(["modules/fbx/data"])
+    process_folder(["modules/fbx/fbx_parser"])
+
+    process_folder(["modules/gdnative"])
+    process_folder(["modules/gdnative/gdnative"])
+    process_folder(["modules/gdnative/nativescript"])
+
+    process_folder(["modules/gdnative/arvr"])
+    process_folder(["modules/gdnative/pluginscript"])
+    process_folder(["modules/gdnative/net"])
+
+    process_folder(["modules/mbedtls"])
+
+    process_folder(["scene"])
+    process_folder(["scene/audio"])
+    process_folder(["scene/debugger"])
+    process_folder(["scene/2d"])
+    process_folder(["scene/3d"])
+    process_folder(["scene/animation"])
+    process_folder(["scene/gui"])
+    process_folder(["scene/main"])
+    process_folder(["scene/resources"])
+
+    process_folder(["servers"])
+    process_folder(["servers/visual"])
+    process_folder(["servers/visual/portals"])
+    process_folder(["servers/physics_2d"])
+    process_folder(["servers/physics"])
+    process_folder(["servers/physics/joints"])
+    process_folder(["servers/audio"])
+    process_folder(["servers/audio/effects"])
+
+    # Finally change back the path to the calling folder
+    os.chdir(curr_folder)
+
+    return _scu_folders

+ 2 - 2
servers/physics/joints/hinge_joint_sw.cpp

@@ -49,7 +49,7 @@ subject to the following restrictions:
 
 #include "hinge_joint_sw.h"
 
-static void plane_space(const Vector3 &n, Vector3 &p, Vector3 &q) {
+void HingeJointSW::plane_space(const Vector3 &n, Vector3 &p, Vector3 &q) {
 	if (Math::abs(n.z) > Math_SQRT12) {
 		// choose p in y-z plane
 		real_t a = n[1] * n[1] + n[2] * n[2];
@@ -382,7 +382,7 @@ void	HingeJointSW::updateRHS(real_t	timeStep)
 }
 */
 
-static _FORCE_INLINE_ real_t atan2fast(real_t y, real_t x) {
+real_t HingeJointSW::atan2fast(real_t y, real_t x) {
 	real_t coeff_1 = Math_PI / 4.0f;
 	real_t coeff_2 = 3.0f * coeff_1;
 	real_t abs_y = Math::abs(y);

+ 3 - 0
servers/physics/joints/hinge_joint_sw.h

@@ -95,6 +95,9 @@ class HingeJointSW : public JointSW {
 
 	real_t m_appliedImpulse;
 
+	void plane_space(const Vector3 &n, Vector3 &p, Vector3 &q);
+	_FORCE_INLINE_ real_t atan2fast(real_t y, real_t x);
+
 public:
 	virtual PhysicsServer::JointType get_type() const { return PhysicsServer::JOINT_HINGE; }
 

+ 1 - 1
servers/physics/joints/slider_joint_sw.cpp

@@ -57,7 +57,7 @@ April 04, 2008
 
 //-----------------------------------------------------------------------------
 
-static _FORCE_INLINE_ real_t atan2fast(real_t y, real_t x) {
+real_t SliderJointSW::atan2fast(real_t y, real_t x) {
 	real_t coeff_1 = Math_PI / 4.0f;
 	real_t coeff_2 = 3.0f * coeff_1;
 	real_t abs_y = Math::abs(y);

+ 3 - 0
servers/physics/joints/slider_joint_sw.h

@@ -150,6 +150,9 @@ protected:
 	//------------------------
 	void initParams();
 
+private:
+	_FORCE_INLINE_ real_t atan2fast(real_t y, real_t x);
+
 public:
 	// constructors
 	SliderJointSW(BodySW *rbA, BodySW *rbB, const Transform &frameInA, const Transform &frameInB);