From 19650f40bb17330487d64f2ab49a1d51794312e3 Mon Sep 17 00:00:00 2001 From: DmitriySalnikov Date: Mon, 5 Aug 2024 07:36:42 +0300 Subject: [PATCH] the build system has been updated. fixed an error message in the c# generator. fixed a crash in the release build. --- .../actions/compile_gdextension/action.yml | 20 +- SConstruct | 159 ++++++++- examples_dd3d/DebugDrawDemoScene.gd | 7 + examples_dd3d/DebugDrawDemoScene.tscn | 3 +- examples_dd3d/DebugDrawDemoSceneCS.cs | 13 + examples_dd3d/DebugDrawDemoSceneCS.tscn | 2 +- lib_utils.py | 307 ++++-------------- lib_utils_external.py | 164 ++++++++++ src/2d/debug_draw_2d.cpp | 2 +- src/editor/generate_csharp_bindings.cpp | 6 +- 10 files changed, 424 insertions(+), 259 deletions(-) create mode 100644 lib_utils_external.py diff --git a/.github/actions/compile_gdextension/action.yml b/.github/actions/compile_gdextension/action.yml index 1b5502fc..d2d0f14c 100644 --- a/.github/actions/compile_gdextension/action.yml +++ b/.github/actions/compile_gdextension/action.yml @@ -94,20 +94,17 @@ runs: shell: bash run: | echo "::group::🛠️ GDExtesion Compilation 🛠️" - cd godot-cpp - git apply --ignore-space-change --ignore-whitespace ../patches/godot_cpp_exclude_unused_classes.patch - git apply --ignore-space-change --ignore-whitespace ../patches/unity_build.patch - git apply --ignore-space-change --ignore-whitespace ../patches/web_threads.patch - cd .. + scons apply_patches telemetry_args="" if [ "${{env.PRODUCTION_BUILD}}" == "true" ] && [ "${{inputs.target}}" == "editor" ] && [ "${{steps.checkout_tele_repo.conclusion}}" == "success" ]; then telemetry_args="telemetry_enabled=yes" fi + scons_params="platform=${{inputs.platform}} arch=${{inputs.arch}} target=${{inputs.target}} addon_output_dir=${{inputs.output_libs_path}} ${{inputs.additional}}" - scons platform=${{inputs.platform}} arch=${{inputs.arch}} target=${{inputs.target}} addon_output_dir=${{inputs.output_libs_path}} ${{inputs.additional}} $telemetry_args + scons $scons_params $telemetry_args if [ "${{inputs.target}}" == "template_release" ] && [ "${{inputs.additional_enabled_dd3d}}" == "true" ]; then - scons platform=${{inputs.platform}} arch=${{inputs.arch}} target=${{inputs.target}} addon_output_dir=${{inputs.output_libs_path}} force_enabled_dd3d=yes ${{inputs.additional}} + scons $scons_params force_enabled_dd3d=yes fi echo "::endgroup::" @@ -115,14 +112,11 @@ runs: if: runner.os != 'Windows' && inputs.platform != 'android' && inputs.platform != 'web' shell: bash run: | - if [ "${{inputs.platform}}" == 'macos' ]; then - found_files=$(find -L ${{inputs.output_libs_path}} -type f -exec file {} + | grep "Mach-O universal" | cut -d: -f1) - echo "Found files: $found_files" - strip -u $found_files - elif [ "${{inputs.platform}}" == 'ios' ]; then - found_files=$(find -L ${{inputs.output_libs_path}} -type f -exec file {} + | grep ".dylib" | cut -d: -f1) + if [ "${{inputs.platform}}" == 'ios' ] || [ "${{inputs.platform}}" == 'macos' ]; then + found_files=$(find -L ${{inputs.output_libs_path}} -type f -iname "*.dylib") echo "Found files: $found_files" while IFS= read -r file; do + echo "Strip '$file'" strip -u "$file" done <<< "$found_files" else diff --git a/SConstruct b/SConstruct index d9fa4d97..e36b2d49 100644 --- a/SConstruct +++ b/SConstruct @@ -1,6 +1,157 @@ #!/usr/bin/env python -import lib_utils -env = SConscript("godot-cpp/SConstruct") -new_env = lib_utils.get_library_object(env.Clone(), ARGUMENTS, lambda e, o: Help(o.GenerateHelpText(e))) -Return("new_env") +from SCons.Script import SConscript +from SCons.Script.SConscript import SConsEnvironment + +import SCons, SCons.Script +import os, platform +import lib_utils, lib_utils_external + +# Fixing the encoding of the console +if platform.system() == "Windows": + os.system("chcp 65001") + +# Project config +project_name = "Debug Draw 3D" +lib_name = "dd3d" +default_output_dir = os.path.join("addons", "debug_draw_3d", "libs") +src_folder = "src" + +# If necessary, add patches from the code +patches_to_apply = [ + "patches/godot_cpp_exclude_unused_classes.patch", # Removes unused godot-cpp classes from the build process + "patches/unity_build.patch", # Speeds up the build by merging the source files. It can increase the size of assemblies. + "patches/web_threads.patch", # Adds the build flag that appeared in Godot 4.3. Required for a web build compatible with Godot 4.3. +] + +print( + f"If you add new source files (e.g. .cpp, .c), do not forget to specify them in '{src_folder}/default_sources.json'.\n\tOr add them to 'setup_defines_and_flags' inside 'SConstruct'." +) +print("To apply git patches, use 'scons apply_patches'.") +# print("To build cmake libraries, use 'scons build_cmake'.") + + +# Additional console arguments +def setup_options(env: SConsEnvironment, arguments): + from SCons.Variables import Variables, BoolVariable, EnumVariable, PathVariable + + opts = Variables([], arguments) + + # It must be here for lib_utils.py + opts.Add( + PathVariable( + "addon_output_dir", "Path to the output directory", default_output_dir, PathVariable.PathIsDirCreate + ) + ) + + opts.Add(BoolVariable("telemetry_enabled", "Enable the telemetry module", False)) + opts.Add(BoolVariable("tracy_enabled", "Enable tracy profiler", False)) + opts.Add(BoolVariable("force_enabled_dd3d", "Keep the rendering code in the release build", False)) + opts.Add(BoolVariable("lto", "Link-time optimization", False)) + + opts.Update(env) + env.Help(opts.GenerateHelpText(env)) + + +# Additional compilation flags +def setup_defines_and_flags(env: SConsEnvironment, src_out): + # Add more sources to `src_out` if needed + + if "release" in env["target"] and not env["force_enabled_dd3d"]: + env.Append(CPPDEFINES=["DISABLE_DEBUG_RENDERING"]) + + if env["telemetry_enabled"]: + tele_src = "src/editor/my_telemetry_modules/GDExtension/usage_time_reporter.cpp" + if os.path.exists(tele_src): + env.Append(CPPDEFINES=["TELEMETRY_ENABLED"]) + src_out.append(tele_src) + print("Compiling with telemetry support!") + else: + print("No telemetry source file found. telemetry_enabled will be ignored!") + + if env["lto"]: + if env.get("is_msvc", False): + env.AppendUnique(CCFLAGS=["/GL"]) + env.AppendUnique(ARFLAGS=["/LTCG"]) + env.AppendUnique(LINKFLAGS=["/LTCG"]) + else: + env.AppendUnique(CCFLAGS=["-flto"]) + env.AppendUnique(LINKFLAGS=["-flto"]) + + if env["tracy_enabled"]: + env.Append(CPPDEFINES=["TRACY_ENABLE", "TRACY_ON_DEMAND", "TRACY_DELAYED_INIT", "TRACY_MANUAL_LIFETIME"]) + src_out.append("src/thirdparty/tracy/public/TracyClient.cpp") + + if env.get("is_msvc", False): + env.Append(LINKFLAGS=["/WX:NO"]) + + if env["platform"] in ["linux"]: # , "android"? + env.Append( + LINKFLAGS=[ + "-static-libgcc", + "-static-libstdc++", + ] + ) + if env["platform"] == "android": + env.Append( + LIBS=[ + "log", + ] + ) + print() + + +def generate_sources_for_resources(env, src_out): + # Array of (path, is_text) + editor_files = [ + ("images/icon_3d_32.png", False), + ] + lib_utils.generate_resources_cpp_h_files( + editor_files, + "DD3DEditorResources", + src_folder, + "editor_resources.gen", + src_out if "editor" in env["target"] else [], + ) + + shared_files = [ + ("src/resources/extendable_meshes.gdshader", True), + ("src/resources/wireframe_unshaded.gdshader", True), + ("src/resources/billboard_unshaded.gdshader", True), + ("src/resources/plane_unshaded.gdshader", True), + ] + lib_utils.generate_resources_cpp_h_files(shared_files, "DD3DResources", src_folder, "shared_resources.gen", src_out) + + print("The generation of C++ sources with the contents of resources has been completed") + print() + + +def apply_patches(target, source, env: SConsEnvironment): + return lib_utils_external.apply_git_patches(env, patches_to_apply, "godot-cpp") + + +# Additional build of the projects via CMake +# def build_cmake(target, source, env: SConsEnvironment): +# extra_flags = [] +# return lib_utils_external.cmake_build_project(env, "opus", extra_flags) + +env: SConsEnvironment = SConscript("godot-cpp/SConstruct") +env = env.Clone() + +args = ARGUMENTS +additional_src = [] +setup_options(env, args) +setup_defines_and_flags(env, additional_src) +generate_sources_for_resources(env, additional_src) + +extra_tags = "" +if "release" in env["target"] and env["force_enabled_dd3d"]: + extra_tags += ".enabled" + +lib_utils.get_library_object( + env, project_name, lib_name, extra_tags, env["addon_output_dir"], src_folder, additional_src +) + +# Register console commands +env.Command("apply_patches", [], apply_patches) +# env.Command("build_cmake", [], build_cmake) diff --git a/examples_dd3d/DebugDrawDemoScene.gd b/examples_dd3d/DebugDrawDemoScene.gd index 64ac3974..1d2531b1 100644 --- a/examples_dd3d/DebugDrawDemoScene.gd +++ b/examples_dd3d/DebugDrawDemoScene.gd @@ -111,12 +111,19 @@ func main_update(delta: float) -> void: DebugDraw2D.set_text("Frames drawn", Engine.get_frames_drawn()) DebugDraw2D.set_text("FPS", Engine.get_frames_per_second()) DebugDraw2D.set_text("delta", delta) + $HitTest.visible = false $LagTest.visible = false + $PlaneOrigin.visible = false + $OtherWorld.visible = false + %ZDepthTestCube.visible = false return $HitTest.visible = true $LagTest.visible = true + $PlaneOrigin.visible = true + $OtherWorld.visible = true + %ZDepthTestCube.visible = true # Testing the rendering layers by showing the image from the second camera inside the 2D panel DebugDraw3D.config.geometry_render_layers = 1 if !Input.is_key_pressed(KEY_ALT) else 0b10010 diff --git a/examples_dd3d/DebugDrawDemoScene.tscn b/examples_dd3d/DebugDrawDemoScene.tscn index a25d45e4..7e7d953f 100644 --- a/examples_dd3d/DebugDrawDemoScene.tscn +++ b/examples_dd3d/DebugDrawDemoScene.tscn @@ -564,7 +564,8 @@ transform = Transform3D(1.51514, 0.589536, 1.00858, -1.34875, 0.662262, 1.133, 0 [node name="GizmoNormal" type="Node3D" parent="Misc"] transform = Transform3D(0.965926, 0, -0.258819, 0, 1, 0, 0.258819, 0, 0.965926, 0.890203, -0.306246, 0.356159) -[node name="MeshInstance3D" type="MeshInstance3D" parent="Misc/GizmoNormal"] +[node name="ZDepthTestCube" type="MeshInstance3D" parent="Misc/GizmoNormal"] +unique_name_in_owner = true transform = Transform3D(0.591801, 0, 4.47035e-08, 0, 0.591801, 0, -4.47035e-08, 0, 0.591801, 0, 0, 0) mesh = SubResource("BoxMesh_b14rm") diff --git a/examples_dd3d/DebugDrawDemoSceneCS.cs b/examples_dd3d/DebugDrawDemoSceneCS.cs index 20e7b02f..d45c4eb2 100644 --- a/examples_dd3d/DebugDrawDemoSceneCS.cs +++ b/examples_dd3d/DebugDrawDemoSceneCS.cs @@ -94,6 +94,9 @@ public partial class DebugDrawDemoSceneCS : Node3D Node3D dCylinder3a; Node3D dCylinder3b; + MeshInstance3D dPlaneOrigin; + MeshInstance3D pZDepthTestCube; + MeshInstance3D dOtherWorld; SubViewport dOtherWorldViewport; Node3D dOtherWorldBox; @@ -154,6 +157,9 @@ public override async void _Ready() dCylinder3a = GetNode("Cylinders/Cylinder3/1"); dCylinder3b = GetNode("Cylinders/Cylinder3/2"); + dPlaneOrigin = GetNode("PlaneOrigin"); + pZDepthTestCube = GetNode("%ZDepthTestCube"); + dOtherWorld = GetNode("OtherWorld"); dOtherWorldViewport = GetNode("OtherWorld/SubViewport"); dOtherWorldBox = GetNode("OtherWorld/SubViewport/OtherWorldBox"); @@ -295,13 +301,20 @@ void MainUpdate(double delta) DebugDraw2D.SetText("Frames drawn", Engine.GetFramesDrawn()); DebugDraw2D.SetText("FPS", Engine.GetFramesPerSecond()); DebugDraw2D.SetText("delta", delta); + dHitTest.Visible = false; dLagTest.Visible = false; + dPlaneOrigin.Visible = false; + pZDepthTestCube.Visible = false; + dOtherWorld.Visible = false; return; } dHitTest.Visible = true; dLagTest.Visible = true; + dPlaneOrigin.Visible = true; + pZDepthTestCube.Visible = true; + dOtherWorld.Visible = true; // Testing the rendering layers by showing the image from the second camera inside the 2D panel DebugDraw3D.Config.GeometryRenderLayers = !Input.IsKeyPressed(Key.Alt) ? 1 : 0b10010; diff --git a/examples_dd3d/DebugDrawDemoSceneCS.tscn b/examples_dd3d/DebugDrawDemoSceneCS.tscn index 44c4a400..dfcbabd2 100644 --- a/examples_dd3d/DebugDrawDemoSceneCS.tscn +++ b/examples_dd3d/DebugDrawDemoSceneCS.tscn @@ -6,7 +6,7 @@ [node name="DebugDrawDemoSceneCS" instance=ExtResource("2")] script = ExtResource("2_ipqea") -[node name="Settings" parent="." index="21"] +[node name="Settings" parent="." index="22"] switch_to_scene = "res://examples_dd3d/DebugDrawDemoScene.tscn" [node name="Label" parent="Settings/HBox/VBoxContainer" index="1"] diff --git a/lib_utils.py b/lib_utils.py index d8405e1c..e916a13f 100644 --- a/lib_utils.py +++ b/lib_utils.py @@ -1,78 +1,19 @@ #!/usr/bin/env python3 -import os -import json -import re +from SCons.Script.SConscript import SConsEnvironment from patches import unity_tools -lib_readable_name = "Debug Draw 3D" -lib_name = "dd3d" -output_dir = os.path.join("addons", "debug_draw_3d", "libs") -src_folder = "src" - - -def setup_options(env, arguments, gen_help): - from SCons.Variables import Variables, BoolVariable, EnumVariable, PathVariable - - opts = Variables([], arguments) - - opts.Add(BoolVariable("telemetry_enabled", "Enable the telemetry module", False)) - opts.Add(BoolVariable("tracy_enabled", "Enable tracy profiler", False)) - opts.Add(BoolVariable("force_enabled_dd3d", "Keep the rendering code in the release build", False)) - opts.Add(BoolVariable("lto", "Link-time optimization", False)) - opts.Add(PathVariable("addon_output_dir", "Path to the output directory", output_dir, PathVariable.PathIsDirCreate)) - opts.Update(env) - - gen_help(env, opts) - - -def setup_defines_and_flags(env, src_out): - if "release" in env["target"] and not env["force_enabled_dd3d"]: - env.Append(CPPDEFINES=["DISABLE_DEBUG_RENDERING"]) - - if env["telemetry_enabled"]: - tele_src = "src/editor/my_telemetry_modules/GDExtension/usage_time_reporter.cpp" - if os.path.exists(tele_src): - env.Append(CPPDEFINES=["TELEMETRY_ENABLED"]) - src_out.append(tele_src) - print("Compiling with telemetry support!") - else: - print("No telemetry source file found. telemetry_enabled will be ignored!") - - if env["lto"]: - if env.get("is_msvc", False): - env.AppendUnique(CCFLAGS=["/GL"]) - env.AppendUnique(ARFLAGS=["/LTCG"]) - env.AppendUnique(LINKFLAGS=["/LTCG"]) - else: - env.AppendUnique(CCFLAGS=["-flto"]) - env.AppendUnique(LINKFLAGS=["-flto"]) - - if env["tracy_enabled"]: - env.Append(CPPDEFINES=["TRACY_ENABLE", "TRACY_ON_DEMAND", "TRACY_DELAYED_INIT", "TRACY_MANUAL_LIFETIME"]) - src_out.append("src/thirdparty/tracy/public/TracyClient.cpp") - - if env["platform"] == "linux": - env.Append( - LINKFLAGS=[ - "-static-libgcc", - "-static-libstdc++", - ] - ) - print() +import SCons +import os, json, re -def get_sources(src): +def get_sources(src: list, src_folder: str, lib_name: str = "unity_"): res = [src_folder + "/" + file for file in src] - res = unity_tools.generate_unity_build(res, "dd3d_") + res = unity_tools.generate_unity_build(res, lib_name + "_") return res -def get_library_object(env, arguments=None, gen_help=None): - if arguments != None and gen_help: - setup_options(env, arguments, gen_help) - additional_src = [] - setup_defines_and_flags(env, additional_src) +def get_library_object(env: SConsEnvironment, project_name: str, lib_name: str, extra_tags: str, output_path: str, src_folder: str, additional_src: list) -> str: env.Append(CPPPATH=src_folder) src = [] @@ -83,54 +24,32 @@ def get_library_object(env, arguments=None, gen_help=None): if scons_cache_path is None: # store all obj's in a dedicated folder env["SHOBJPREFIX"] = "#obj/" + else: + env.CacheDir(scons_cache_path) + env.Decider("MD5") - generate_sources_for_resources(env, src) - - # some additional tags + # some additional tags if needed additional_tags = "" if env["platform"] == "web" and env.get("threads", True): additional_tags = ".threads" - if "release" in env["target"] and env["force_enabled_dd3d"]: - additional_tags += ".enabled" - - library_name = "lib{}.{}.{}.{}{}".format(lib_name, env["platform"], env["target"], env["arch"], additional_tags) - library_full_path = os.path.join(env["addon_output_dir"], library_name + env["SHLIBSUFFIX"]) - - # using the library with `reloadable = true` and with the debugger block the PDB file, - # so it needs to be renamed to something not blocked - if env.get("is_msvc", False) and env["target"] != "template_release": - msvc_pdb_rename(env, library_full_path) + lib_filename = "lib{}.{}.{}.{}{}".format(lib_name, env["platform"], env["target"], env["arch"], additional_tags + extra_tags) + env["SHLIBSUFFIX"] - if env["platform"] != "macos": - env.Default( - env.SharedLibrary( - target=env.File(library_full_path), - source=additional_src + get_sources(src), - SHLIBSUFFIX=env["SHLIBSUFFIX"], - ) - ) + if env["platform"] == "macos": + generate_framework_folder(env, project_name, lib_name, lib_filename, output_path) + lib_filename = os.path.join(output_path, os.path.splitext(lib_filename)[0] + ".framework", lib_filename) else: - generate_framework_folder(env, library_name) - env.Default( - env.SharedLibrary( - target=env.File(os.path.join(env["addon_output_dir"], library_name + ".framework", library_name)), - source=additional_src + get_sources(src), - SHLIBSUFFIX="", - ) - ) + lib_filename = os.path.join(output_path, lib_filename) - # Needed for easy reuse of this library in other build scripts - # TODO: not tested at the moment. Probably need some changes in the C++ code - env = env.Clone() - env.Append(LIBPATH=[env.Dir(env["addon_output_dir"])]) - if env.get("is_msvc", False): - env.Append(LIBS=[library_full_path.replace(".dll", ".lib")]) - else: - env.Append(LIBS=[library_full_path]) + env.Default( + env.SharedLibrary( + target=env.File(lib_filename), + source=get_sources(additional_src + src, src_folder, lib_name) + ) + ) - return env + return lib_filename def get_library_version(): @@ -141,51 +60,67 @@ def get_library_version(): minor_match = re.search(r"#define .*_MINOR (\d+)", header_content) patch_match = re.search(r"#define .*_PATCH (\d+)", header_content) - if major_match: - major_value = int(major_match.group(1)) - else: - major_value = 0 - - if minor_match: - minor_value = int(minor_match.group(1)) - else: - minor_value = 0 - - if patch_match: - patch_value = int(patch_match.group(1)) - else: - patch_value = 0 + major_value = int(major_match.group(1)) if major_match else 0 + minor_value = int(minor_match.group(1)) if minor_match else 0 + patch_value = int(patch_match.group(1)) if patch_match else 0 return f"{major_value}.{minor_value}.{patch_value}" -def generate_sources_for_resources(env, src_out): - # Array of (path, is_text) - editor_files = [ - ("images/icon_3d_32.png", False), - ] - generate_resources_cpp_h_files( - editor_files, "DD3DEditorResources", "editor_resources.gen", src_out if "editor" in env["target"] else [] - ) +def generate_framework_folder(env: SConsEnvironment, project_name: str, lib_name: str, lib_filename: str, output_path: str): + min_version = env.get("macos_deployment_target", "10.15") + lib_version = get_library_version() + lib_filename_noext = os.path.splitext(lib_filename)[0] + + info_plist = f""" + + + + CFBundleInfoDictionaryVersion + 6.0 + CFBundleDevelopmentRegion + en + CFBundleExecutable + {lib_filename} + CFBundleName + {project_name} + CFBundleDisplayName + {project_name} + CFBundleIdentifier + ru.dmitriysalnikov.{lib_name} + NSHumanReadableCopyright + Copyright (c) Dmitriy Salnikov. + CFBundleVersion + {lib_version} + CFBundleShortVersionString + {lib_version} + CFBundlePackageType + FMWK + CSResourcesFileMapped + + DTPlatformName + macosx + LSMinimumSystemVersion + {min_version} + + + """ - shared_files = [ - ("src/resources/extendable_meshes.gdshader", True), - ("src/resources/wireframe_unshaded.gdshader", True), - ("src/resources/billboard_unshaded.gdshader", True), - ("src/resources/plane_unshaded.gdshader", True), - ] - generate_resources_cpp_h_files(shared_files, "DD3DResources", "shared_resources.gen", src_out) + res_folder = os.path.join(output_path, lib_filename_noext + ".framework", "Resources") + os.makedirs(res_folder, exist_ok=True) - print("The generation of C++ sources with the contents of resources has been completed") - print() + with open(os.path.join(res_folder, "Info.plist"), "w") as info_plist_file: + info_plist_file.write(info_plist) -def generate_resources_cpp_h_files(input_files, namespace, output_no_ext, src_out): +def generate_resources_cpp_h_files(input_files: list, namespace: str, src_folder: str, output_no_ext: str, src_out: list): """ - input_files - (path, is_text) + input_files : list of tuples + (path: str, is_text: bool) namespace : str source code namespace + src_folder : str + source folder in which the 'gen/' folder will be created output_no_ext : str name of the source pair (cpp/h) src_out : list of str @@ -251,101 +186,3 @@ def generate_resources_cpp_h_files(input_files, namespace, output_no_ext, src_ou h_file.write("}\n") cpp_file.write("}\n") - - -def generate_framework_folder(env, library_name): - min_version = env.get("macos_deployment_target", "10.14") - lib_version = get_library_version() - - info_plist = f""" - - - - CFBundleInfoDictionaryVersion - 6.0 - CFBundleDevelopmentRegion - en - CFBundleExecutable - {library_name} - CFBundleName - {lib_readable_name} - CFBundleDisplayName - {lib_readable_name} - CFBundleIdentifier - ru.dmitriysalnikov.{lib_name} - NSHumanReadableCopyright - Copyright (c) Dmitriy Salnikov. - CFBundleVersion - {lib_version} - CFBundleShortVersionString - {lib_version} - CFBundlePackageType - FMWK - CSResourcesFileMapped - - DTPlatformName - macosx - LSMinimumSystemVersion - {min_version} - - - """ - - res_folder = os.path.join(env["addon_output_dir"], library_name + ".framework", "Resources") - os.makedirs(res_folder, exist_ok=True) - - with open(os.path.join(res_folder, "Info.plist"), "w") as info_plist_file: - info_plist_file.write(info_plist) - - -def replace_flag(arr, flag, new_flag): - if flag in arr: - arr.remove(flag) - arr.append(new_flag) - - -# A utility function for getting the name of an unblocked file -def get_unblocked_file_name(original_file_path, new_file_ext, max_files=256, keep_newest_one=True): - lib_dir = os.path.normpath(os.path.dirname(original_file_path)) - lib_name = os.path.splitext(os.path.basename(original_file_path))[0] - - # Collect all matching files - found_files = [ - f - for f in os.listdir(lib_dir) - if os.path.isfile(os.path.join(lib_dir, f)) and f.startswith(lib_name) and f.endswith("." + new_file_ext) - ] - found_files = sorted(found_files, key=lambda x: os.path.getmtime(os.path.join(lib_dir, x))) - - # Clean up the old files if possible, except for the newest one - if found_files: - if keep_newest_one: - found_files.pop() - for f in found_files: - try: - os.remove(os.path.join(lib_dir, f)) - except: - pass - - # Search for a unblocked file name - file_name = "" - for s in range(max_files): - file_name = "{}_{}.{}".format(os.path.join(lib_dir, lib_name), s, new_file_ext) - if not os.path.exists(file_name): - break - try: - with open(file_name, "a") as f: - pass - except IOError: - continue - break - - return file_name - - -# This is necessary to support debugging and Hot-Reload at the same time when building using MSVC -def msvc_pdb_rename(env, full_lib_path): - pdb_name = get_unblocked_file_name(full_lib_path, "pdb") - print("New path to the PDB: " + pdb_name) - # explicit assignment of the PDB name - env.Append(LINKFLAGS=["/PDB:" + pdb_name]) diff --git a/lib_utils_external.py b/lib_utils_external.py new file mode 100644 index 00000000..421e9f2b --- /dev/null +++ b/lib_utils_external.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 + +from SCons.Script.SConscript import SConsEnvironment +from SCons.Util import WhereIs + +import SCons +import os, subprocess, shutil + + +# Get the name of the cmake build directory +def get_cmake_build_dir_name(env: SConsEnvironment) -> str: + if env.get("threads", True) and env["platform"] == "web": + return f"{env["platform"]}_{env["arch"]}_threads" + return f"{env["platform"]}_{env["arch"]}" + + +# Get a path to the build folder of the cmake library +def get_cmake_build_dir(env: SConsEnvironment, lib_path: str) -> str: + abs_scons_root = os.path.dirname(os.path.abspath(__file__)) + # CMake doesn't seem to be able to work with the cache at all... + # + #scons_cache_path = os.environ.get("SCONS_CACHE") + #if scons_cache_path: + # return os.path.join(abs_scons_root, scons_cache_path, "cmake", lib_path, get_cmake_build_dir_name(env)) + #else: + return os.path.join(abs_scons_root, lib_path, get_cmake_build_dir_name(env)) + + +# Get a path to the output folder of the cmake library +def get_cmake_output_lib_dir(env: SConsEnvironment, lib_path: str) -> str: + return os.path.join(get_cmake_build_dir(env, lib_path), "Debug" if env["dev_build"] else "Release") + + +def print_subprocess_result(result, prefix: str): + if result.stdout: + print(f"{prefix} output: {result.stdout}") + if result.stderr: + print(f"{prefix} errors: {result.stderr}") + + +def apply_git_patches(env: SConsEnvironment, patches_to_apply: list, working_dir: str): + for patch in patches_to_apply: + print() + try: + result = subprocess.run( + [ + "git", + "apply", + f"--directory={working_dir}", + "--ignore-space-change", + "--ignore-whitespace", + "--reverse", + "--check", + patch, + ], + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf-8", + ) + print(f"Already applied patch: '{patch}'") + continue + except subprocess.CalledProcessError as e: + print(f"Trying to apply a patch: '{patch}'") + + try: + result = subprocess.run( + ["git", "apply", f"--directory={working_dir}", "--ignore-space-change", "--ignore-whitespace", patch], + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf-8", + ) + print_subprocess_result(result, "git") + print(f"Successfully applied patch: {patch}") + except subprocess.CalledProcessError as e: + print_subprocess_result(e, "git") + print("Please fix the patches, disable them, or try to git reset!") + return 1 + print() + return 0 + + +def cmake_build_project(env: SConsEnvironment, lib_path: str, extra_args: list, extra_c_compiler_flags: dict = {}): + print() + arch = env["arch"] + platform = env["platform"] + platform_args = [] + build_args = [] + compiler_flags = extra_c_compiler_flags.get("c_flags", []).copy() + linker_flags = extra_c_compiler_flags.get("linker_flags", []).copy() + + if platform == "windows": + arch_map = { "arm32": "ARM", "arm64": "ARM64", "x86_32": "Win32", "x86_64":"x64" } + platform_args += ["-G", "Visual Studio 17 2022", + "-A", arch_map[arch], + "-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded" + ("" if env["use_static_cpp"] else "DLL")] + elif platform == "linux": + platform_args += ["-G", "Ninja Multi-Config",] + elif platform == "macos": + platform_args += ["-G", "Ninja Multi-Config", + "-DCMAKE_SYSTEM_NAME=Darwin",] + elif platform == "ios": + platform_args += ["-G", "Ninja Multi-Config", + "-DCMAKE_SYSTEM_NAME=iOS",] + elif platform == "android": + arch_map = { "arm32": "armeabi-v7a", "arm64": "arm64-v8a", "x86_32": "x86", "x86_64":"x86_64" } + platform_args += ["-G", "Ninja Multi-Config", + f"-DCMAKE_TOOLCHAIN_FILE={os.getenv("ANDROID_HOME")}/ndk/25.2.9519653/build/cmake/android.toolchain.cmake", + f"-DANDROID_ABI={arch_map[arch]}", f"-DANDROID_PLATFORM={env.get("android_api_level", 21)}",] + elif platform == "web": + platform_args += ["-G", "Ninja Multi-Config", + f"-DCMAKE_TOOLCHAIN_FILE={os.path.dirname(WhereIs("emcc"))}/cmake/Modules/Platform/Emscripten.cmake",] + if env.get("threads", True): + compiler_flags += ["-sUSE_PTHREADS=1"] + linker_flags += ["-sUSE_PTHREADS=1"] + + build_args += ["--config", "Debug" if env["dev_build"] else "Release"] + + if len(compiler_flags): + platform_args += [f"-DCMAKE_C_FLAGS={";".join(compiler_flags)}", f"-DCMAKE_CXX_FLAGS={";".join(compiler_flags)}"] + if len(linker_flags): + platform_args += [f"-DCMAKE_EXE_LINKER_FLAGS={";".join(linker_flags)}"] + + curdir = os.curdir + os.chdir(lib_path) + try: + def config(): + result = subprocess.run( + ["cmake", f"-B{get_cmake_build_dir(env, lib_path)}"] + platform_args + extra_args, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf-8", + ) + print_subprocess_result(result, "cmake config") + + try: + config() + except subprocess.CalledProcessError as e: + print_subprocess_result(e, "cmake config") + print(f"Attempt to clean up the build directory and reconfigure it...\n") + shutil.rmtree(get_cmake_build_dir(env, lib_path)) + config() + + result = subprocess.run( + ["cmake", "--build", get_cmake_build_dir(env, lib_path)] + build_args, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf-8", + ) + print_subprocess_result(result, "cmake build") + + print(f"Successfully build: {lib_path}") + except subprocess.CalledProcessError as e: + print_subprocess_result(e, "cmake") + print(f"cmake can't build {lib_path}. Please fix the errors!") + return 1 + finally: + os.chdir(curdir) + + print() + return 0 diff --git a/src/2d/debug_draw_2d.cpp b/src/2d/debug_draw_2d.cpp index d8b51824..15ced9b0 100644 --- a/src/2d/debug_draw_2d.cpp +++ b/src/2d/debug_draw_2d.cpp @@ -313,7 +313,7 @@ void DebugDraw2D::set_custom_canvas(Control *_canvas) { #ifndef DISABLE_DEBUG_RENDERING _set_custom_canvas_internal(_canvas); #else - custom_control_id = _canvas->get_instance_id(); + custom_control_id = _canvas ? _canvas->get_instance_id() : 0; #endif } diff --git a/src/editor/generate_csharp_bindings.cpp b/src/editor/generate_csharp_bindings.cpp index d755326a..a8a320ae 100644 --- a/src/editor/generate_csharp_bindings.cpp +++ b/src/editor/generate_csharp_bindings.cpp @@ -35,8 +35,7 @@ bool GenerateCSharpBindingsPlugin::is_need_to_update() { if (FileAccess::file_exists(old_api_path)) { return true; } - - + const String api_path = output_directory.path_join(api_file_name); if (FileAccess::file_exists(api_path)) { auto file = FileAccess::open(api_path, FileAccess::READ); @@ -71,10 +70,9 @@ void GenerateCSharpBindingsPlugin::generate() { // Delete the file with the older naming convention const String old_api_path = output_directory.path_join("DebugDrawGeneratedAPI.cs"); if (FileAccess::file_exists(old_api_path)) { - PRINT("Attempt to delete API file with older naming convention: " + out_path); + PRINT("Attempt to delete API file with older naming convention: " + old_api_path); ERR_FAIL_COND(dir->remove(old_api_path) != Error::OK); } - // First, delete the old file to check for locks if (FileAccess::file_exists(out_path)) {