Kaynağa Gözat

Integrate `.pre-commit-config.yaml`

Thaddeus Crews 1 yıl önce
ebeveyn
işleme
e0d363aad8
45 değiştirilmiş dosya ile 426 ekleme ve 1015 silme
  1. 21 42
      .github/workflows/static_checks.yml
  2. 1 1
      .gitignore
  3. 64 0
      .pre-commit-config.yaml
  4. 1 1
      CMakeLists.txt
  5. 2 7
      SConstruct
  6. 6 7
      binding_generator.py
  7. 5 0
      include/godot_cpp/variant/array_helpers.hpp
  8. 5 0
      include/godot_cpp/variant/color_names.inc.hpp
  9. 1 1
      include/godot_cpp/variant/rect2.hpp
  10. 1 1
      include/godot_cpp/variant/rect2i.hpp
  11. 0 37
      misc/hooks/README.md
  12. 0 48
      misc/hooks/canonicalize_filename.sh
  13. 0 50
      misc/hooks/pre-commit
  14. 0 202
      misc/hooks/pre-commit-black
  15. 0 242
      misc/hooks/pre-commit-clang-format
  16. 0 103
      misc/hooks/winmessage.ps1
  17. 0 25
      misc/scripts/black_format.sh
  18. 4 2
      misc/scripts/check_ci_log.py
  19. 3 3
      misc/scripts/check_get_file_list.py
  20. 0 38
      misc/scripts/clang_format.sh
  21. 0 5
      misc/scripts/codespell.sh
  22. 50 49
      misc/scripts/copyright_headers.py
  23. 46 0
      misc/scripts/file_format.py
  24. 0 41
      misc/scripts/file_format.sh
  25. 127 0
      misc/scripts/header_guards.py
  26. 0 60
      misc/scripts/header_guards.sh
  27. 0 11
      misc/scripts/mypy.ini
  28. 0 6
      misc/scripts/mypy_check.sh
  29. 58 0
      pyproject.toml
  30. 3 3
      src/core/memory.cpp
  31. 1 1
      src/variant/basis.cpp
  32. 1 1
      src/variant/color.cpp
  33. 0 2
      test/SConstruct
  34. 2 2
      test/project/main.gd
  35. 5 5
      test/src/example.cpp
  36. 1 1
      test/src/example.h
  37. 2 2
      tools/android.py
  38. 0 1
      tools/common_compiler_flags.py
  39. 7 6
      tools/godotcpp.py
  40. 3 2
      tools/ios.py
  41. 1 1
      tools/linux.py
  42. 1 0
      tools/macos.py
  43. 0 1
      tools/my_spawn.py
  44. 0 1
      tools/web.py
  45. 4 4
      tools/windows.py

+ 21 - 42
.github/workflows/static_checks.yml

@@ -7,48 +7,27 @@ concurrency:
 
 jobs:
   static-checks:
-    name: Format (clang-format, black format, file format)
-    runs-on: ubuntu-20.04
+    name: Format (clang-format, ruff format, file format)
+    runs-on: ubuntu-22.04
     steps:
       - name: Checkout
         uses: actions/checkout@v4
-
-      # Azure repositories are not reliable, we need to prevent Azure giving us packages.
-      - name: Make apt sources.list use the default Ubuntu repositories
-        run: |
-          sudo rm -f /etc/apt/sources.list.d/*
-          sudo cp -f misc/ci/sources.list /etc/apt/sources.list
-          wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
-          sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-15 main"
-          sudo apt-get update
-
-      - name: Install dependencies
-        run: |
-          sudo apt-get install -qq dos2unix recode clang-format-15 libxml2-utils python3-pip moreutils
-          sudo update-alternatives --remove-all clang-format || true
-          sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-15 100
-          sudo pip3 install black==22.3.0 pygments pytest==7.1.2 mypy==0.971
-
-      - name: File formatting checks (file_format.sh)
-        run: |
-          bash ./misc/scripts/file_format.sh
-
-      - name: Header guards formatting checks (header_guards.sh)
-        run: |
-          bash ./misc/scripts/header_guards.sh
-
-      - name: Python style checks via black (black_format.sh)
-        run: |
-          bash ./misc/scripts/black_format.sh
-
-      - name: Python scripts static analysis (mypy_check.sh)
-        run: |
-          bash ./misc/scripts/mypy_check.sh
-
-      - name: Bindings generation checks (ensures get_file_list returns all generated files)
-        run: |
-          python ./misc/scripts/check_get_file_list.py
-
-      - name: Style checks via clang-format (clang_format.sh)
-        run: |
-          bash ./misc/scripts/clang_format.sh
+        with:
+          fetch-depth: 2
+
+      - name: Get changed files
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        run: |
+          if [ "${{ github.event_name }}" == "pull_request" ]; then
+            files=$(git diff-tree --no-commit-id --name-only -r HEAD^1..HEAD 2> /dev/null || true)
+          elif [ "${{ github.event_name }}" == "push" -a "${{ github.event.forced }}" == "false" -a "${{ github.event.created }}" == "false" ]; then
+            files=$(git diff-tree --no-commit-id --name-only -r ${{ github.event.before }}..${{ github.event.after }} 2> /dev/null || true)
+          fi
+          files=$(echo "$files" | grep -v 'thirdparty' | xargs -I {} sh -c 'echo "\"./{}\""' | tr '\n' ' ')
+          echo "CHANGED_FILES=$files" >> $GITHUB_ENV
+
+      - name: Style checks via pre-commit
+        uses: pre-commit/[email protected]
+        with:
+          extra_args: --verbose --hook-stage manual --files ${{ env.CHANGED_FILES }}

+ 1 - 1
.gitignore

@@ -8,7 +8,7 @@
 include/gen
 src/gen
 
-# Build configuarion.
+# Build configuration.
 /custom.py
 
 # Misc

+ 64 - 0
.pre-commit-config.yaml

@@ -0,0 +1,64 @@
+default_language_version:
+  python: python3
+
+exclude: |
+  (?x)^(
+    gdextension/extension_api\.json|
+    gdextension/gdextension_interface\.h
+  )$
+
+repos:
+  - repo: https://github.com/pre-commit/mirrors-clang-format
+    rev: v17.0.6
+    hooks:
+      - id: clang-format
+
+  - repo: https://github.com/astral-sh/ruff-pre-commit
+    rev: v0.4.4
+    hooks:
+      - id: ruff
+        args: [--fix]
+      - id: ruff-format
+
+  - repo: https://github.com/pre-commit/mirrors-mypy
+    rev: v0.971
+    hooks:
+      - id: mypy
+        files: \.py$
+        types_or: [text]
+
+  - repo: https://github.com/codespell-project/codespell
+    rev: v2.3.0
+    hooks:
+      - id: codespell
+        additional_dependencies: [tomli]
+
+  - repo: local
+    hooks:
+      - id: copyright-headers
+        name: copyright-headers
+        language: python
+        entry: python misc/scripts/copyright_headers.py
+        files: \.(c|h)pp$
+        exclude: ^test/
+
+      - id: header-guards
+        name: header-guards
+        language: python
+        entry: python misc/scripts/header_guards.py
+        files: \.hpp$
+        exclude: ^test/
+
+      - id: file-format
+        name: file-format
+        language: python
+        entry: python misc/scripts/file_format.py
+        types_or: [text]
+
+      - id: check-get-file-list
+        name: check-get-file-list
+        language: python
+        entry: python misc/scripts/check_get_file_list.py
+        pass_filenames: false
+        always_run: true
+        stages: [manual]

+ 1 - 1
CMakeLists.txt

@@ -3,7 +3,7 @@
 #
 # godot-cpp cmake arguments
 # GODOT_GDEXTENSION_DIR:		Path to the directory containing GDExtension interface header and API JSON file
-# GODOT_CPP_SYSTEM_HEADERS		Mark the header files as SYSTEM. This may be useful to supress warnings in projects including this one.
+# GODOT_CPP_SYSTEM_HEADERS		Mark the header files as SYSTEM. This may be useful to suppress warnings in projects including this one.
 # GODOT_CPP_WARNING_AS_ERROR	Treat any warnings as errors
 # GODOT_CUSTOM_API_FILE:		Path to a custom GDExtension API JSON file (takes precedence over `gdextension_dir`)
 # FLOAT_PRECISION:				Floating-point precision level ("single", "double")

+ 2 - 7
SConstruct

@@ -1,18 +1,13 @@
 #!/usr/bin/env python
 
 import os
-import platform
-import sys
-import subprocess
-from binding_generator import scons_generate_bindings, scons_emit_files
-
 
 EnsureSConsVersion(4, 0)
 
 
 try:
     Import("env")
-except:
+except Exception:
     # Default tools with no platform defaults to gnu toolchain.
     # We apply platform specific toolchains via our custom tools.
     env = Environment(tools=["default"], PLATFORM="")
@@ -23,7 +18,7 @@ env.PrependENVPath("PATH", os.getenv("PATH"))
 customs = ["custom.py"]
 try:
     customs += Import("customs")
-except:
+except Exception:
     pass
 profile = ARGUMENTS.get("profile", "")
 if profile:

+ 6 - 7
binding_generator.py

@@ -591,7 +591,7 @@ def generate_builtin_class_vararg_method_implements_header(builtin_classes):
     result.append(f"#define {header_guard}")
     result.append("")
     for builtin_api in builtin_classes:
-        if not "methods" in builtin_api:
+        if "methods" not in builtin_api:
             continue
         class_name = builtin_api["name"]
         for method in builtin_api["methods"]:
@@ -1513,7 +1513,7 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
         for field in expanded_format.split(";"):
             field_type = field.strip().split(" ")[0].split("::")[0]
             if field_type != "" and not is_included_type(field_type) and not is_pod_type(field_type):
-                if not field_type in used_classes:
+                if field_type not in used_classes:
                     used_classes.append(field_type)
 
         result.append("")
@@ -2127,7 +2127,7 @@ def generate_global_constant_binds(api, output_dir):
             header.append(f'VARIANT_ENUM_CAST({enum_def["name"]});')
 
     # Variant::Type is not a global enum, but only one line, it is worth to place in this file instead of creating new file.
-    header.append(f"VARIANT_ENUM_CAST(godot::Variant::Type);")
+    header.append("VARIANT_ENUM_CAST(godot::Variant::Type);")
 
     header.append("")
 
@@ -2583,7 +2583,7 @@ def is_packed_array(type_name):
 
 def needs_copy_instead_of_move(type_name):
     """
-    Those are types which need initialised data or we'll get warning spam so need a copy instead of move.
+    Those are types which need initialized data or we'll get warning spam so need a copy instead of move.
     """
     return type_name in [
         "Dictionary",
@@ -2691,7 +2691,7 @@ def correct_default_value(value, type_name):
     if value == "":
         return f"{type_name}()"
     if value.startswith("Array["):
-        return f"{{}}"
+        return "{}"
     if value.startswith("&"):
         return value[1::]
     if value.startswith("^"):
@@ -2707,7 +2707,7 @@ def correct_typed_array(type_name):
 
 def correct_type(type_name, meta=None, use_alias=True):
     type_conversion = {"float": "double", "int": "int64_t", "Nil": "Variant"}
-    if meta != None:
+    if meta is not None:
         if "int" in meta:
             return f"{meta}_t"
         elif meta in type_conversion:
@@ -2818,7 +2818,6 @@ def get_operator_id_name(op):
         "or": "or",
         "xor": "xor",
         "not": "not",
-        "and": "and",
         "in": "in",
     }
     return op_id_map[op]

+ 5 - 0
include/godot_cpp/variant/array_helpers.hpp

@@ -28,6 +28,9 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 /**************************************************************************/
 
+#ifndef GODOT_ARRAY_HELPERS_HPP
+#define GODOT_ARRAY_HELPERS_HPP
+
 namespace godot {
 namespace helpers {
 template <typename T, typename ValueT>
@@ -48,3 +51,5 @@ T append_all(T appendable) {
 }
 } // namespace helpers
 } // namespace godot
+
+#endif // GODOT_ARRAY_HELPERS_HPP

+ 5 - 0
include/godot_cpp/variant/color_names.inc.hpp

@@ -28,6 +28,9 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 /**************************************************************************/
 
+#ifndef GODOT_COLOR_NAMES_INC_HPP
+#define GODOT_COLOR_NAMES_INC_HPP
+
 namespace godot {
 
 // Names from https://en.wikipedia.org/wiki/X11_color_names
@@ -189,3 +192,5 @@ static NamedColor named_colors[] = {
 };
 
 } // namespace godot
+
+#endif // GODOT_COLOR_NAMES_INC_HPP

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

@@ -146,7 +146,7 @@ struct _NO_DISCARD_ Rect2 {
 		return size.x > 0.0f && size.y > 0.0f;
 	}
 
-	// Returns the instersection between two Rect2s or an empty Rect2 if there is no intersection
+	// Returns the intersection between two Rect2s or an empty Rect2 if there is no intersection
 	inline Rect2 intersection(const Rect2 &p_rect) const {
 		Rect2 new_rect = p_rect;
 

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

@@ -89,7 +89,7 @@ struct _NO_DISCARD_ Rect2i {
 		return size.x > 0 && size.y > 0;
 	}
 
-	// Returns the instersection between two Rect2is or an empty Rect2i if there is no intersection
+	// Returns the intersection between two Rect2is or an empty Rect2i if there is no intersection
 	inline Rect2i intersection(const Rect2i &p_rect) const {
 		Rect2i new_rect = p_rect;
 

+ 0 - 37
misc/hooks/README.md

@@ -1,37 +0,0 @@
-# Git hooks for Godot Engine
-
-This folder contains Git hooks meant to be installed locally by Godot Engine
-contributors to make sure they comply with our requirements.
-
-## List of hooks
-
-- Pre-commit hook for `clang-format`: Applies `clang-format` to the staged
-  files before accepting a commit; blocks the commit and generates a patch if
-  the style is not respected.
-  You may need to edit the file if your `clang-format` binary is not in the
-  `PATH`, or if you want to enable colored output with `pygmentize`.
-- Pre-commit hook for `black`: Applies `black` to the staged Python files
-  before accepting a commit.
-- Pre-commit hook for `make_rst`: Checks the class reference syntax using
-  `make_rst.py`.
-
-## Installation
-
-Copy all the files from this folder into your `.git/hooks` folder, and make
-sure the hooks and helper scripts are executable.
-
-#### Linux/MacOS
-
-The hooks rely on bash scripts and tools which should be in the system `PATH`,
-so they should work out of the box on Linux/macOS.
-
-#### Windows
-
-##### clang-format
-- Download LLVM for Windows (version 13 or later) from
-  <https://releases.llvm.org/download.html>
-- Make sure LLVM is added to the `PATH` during installation
-
-##### black
-- Python installation: make sure Python is added to the `PATH`
-- Install `black` - in any console: `pip3 install black`

+ 0 - 48
misc/hooks/canonicalize_filename.sh

@@ -1,48 +0,0 @@
-#!/bin/sh
-
-# Provide the canonicalize filename (physical filename with out any symlinks)
-# like the GNU version readlink with the -f option regardless of the version of
-# readlink (GNU or BSD).
-
-# This file is part of a set of unofficial pre-commit hooks available
-# at github.
-# Link:    https://github.com/githubbrowser/Pre-commit-hooks
-# Contact: David Martin, [email protected]
-
-###########################################################
-# There should be no need to change anything below this line.
-
-# Canonicalize by recursively following every symlink in every component of the
-# specified filename. This should reproduce the results of the GNU version of
-# readlink with the -f option.
-#
-# Reference: https://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac
-canonicalize_filename () {
-    local target_file="$1"
-    local physical_directory=""
-    local result=""
-
-    # Need to restore the working directory after work.
-    local working_dir="`pwd`"
-
-    cd -- "$(dirname -- "$target_file")"
-    target_file="$(basename -- "$target_file")"
-
-    # Iterate down a (possible) chain of symlinks
-    while [ -L "$target_file" ]
-    do
-        target_file="$(readlink -- "$target_file")"
-        cd -- "$(dirname -- "$target_file")"
-        target_file="$(basename -- "$target_file")"
-    done
-
-    # Compute the canonicalized name by finding the physical path
-    # for the directory we're in and appending the target file.
-    physical_directory="`pwd -P`"
-    result="$physical_directory/$target_file"
-
-    # restore the working directory after work.
-    cd -- "$working_dir"
-
-    echo "$result"
-}

+ 0 - 50
misc/hooks/pre-commit

@@ -1,50 +0,0 @@
-#!/bin/sh
-# Git pre-commit hook that runs multiple hooks specified in $HOOKS.
-# Make sure this script is executable. Bypass hooks with git commit --no-verify.
-
-# This file is part of a set of unofficial pre-commit hooks available
-# at github.
-# Link:    https://github.com/githubbrowser/Pre-commit-hooks
-# Contact: David Martin, [email protected]
-
-
-###########################################################
-# CONFIGURATION:
-# pre-commit hooks to be executed. They should be in the same .git/hooks/ folder
-# as this script. Hooks should return 0 if successful and nonzero to cancel the
-# commit. They are executed in the order in which they are listed.
-#HOOKS="pre-commit-compile pre-commit-uncrustify"
-HOOKS="pre-commit-clang-format pre-commit-black"
-###########################################################
-# There should be no need to change anything below this line.
-
-. "$(dirname -- "$0")/canonicalize_filename.sh"
-
-# exit on error
-set -e
-
-# Absolute path to this script, e.g. /home/user/bin/foo.sh
-SCRIPT="$(canonicalize_filename "$0")"
-
-# Absolute path this script is in, thus /home/user/bin
-SCRIPTPATH="$(dirname -- "$SCRIPT")"
-
-
-for hook in $HOOKS
-do
-    echo "Running hook: $hook"
-    # run hook if it exists
-    # if it returns with nonzero exit with 1 and thus abort the commit
-    if [ -f "$SCRIPTPATH/$hook" ]; then
-        "$SCRIPTPATH/$hook"
-        if [ $? != 0 ]; then
-            exit 1
-        fi
-    else
-        echo "Error: file $hook not found."
-        echo "Aborting commit. Make sure the hook is in $SCRIPTPATH and executable."
-        echo "You can disable it by removing it from the list in $SCRIPT."
-        echo "You can skip all pre-commit hooks with --no-verify (not recommended)."
-        exit 1
-    fi
-done

+ 0 - 202
misc/hooks/pre-commit-black

@@ -1,202 +0,0 @@
-#!/usr/bin/env bash
-
-# git pre-commit hook that runs a black stylecheck.
-# Based on pre-commit-clang-format.
-
-##################################################################
-# SETTINGS
-# Set path to black binary.
-BLACK=`which black 2>/dev/null`
-BLACK_OPTIONS="-l 120"
-
-# Remove any older patches from previous commits. Set to true or false.
-DELETE_OLD_PATCHES=false
-
-# File types to parse.
-FILE_NAMES="SConstruct SCsub"
-FILE_EXTS=".py"
-
-# Use pygmentize instead of cat to parse diff with highlighting.
-# Install it with `pip install pygments` (Linux) or `easy_install Pygments` (Mac)
-PYGMENTIZE=`which pygmentize 2>/dev/null`
-if [ ! -z "$PYGMENTIZE" ]; then
-  READER="pygmentize -l diff"
-else
-  READER=cat
-fi
-
-# Path to zenity
-ZENITY=`which zenity 2>/dev/null`
-
-# Path to xmessage
-XMSG=`which xmessage 2>/dev/null`
-
-# Path to powershell (Windows only)
-PWSH=`which powershell 2>/dev/null`
-
-##################################################################
-# There should be no need to change anything below this line.
-
-. "$(dirname -- "$0")/canonicalize_filename.sh"
-
-# exit on error
-set -e
-
-# check whether the given file matches any of the set extensions
-matches_name_or_extension() {
-    local filename=$(basename "$1")
-    local extension=".${filename##*.}"
-
-    for name in $FILE_NAMES; do [[ "$name" == "$filename" ]] && return 0; done
-    for ext in $FILE_EXTS; do [[ "$ext" == "$extension" ]] && return 0; done
-
-    return 1
-}
-
-# necessary check for initial commit
-if git rev-parse --verify HEAD >/dev/null 2>&1 ; then
-    against=HEAD
-else
-    # Initial commit: diff against an empty tree object
-    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
-fi
-
-if [ ! -x "$BLACK" ] ; then
-    if [ ! -t 1 ] ; then
-        if [ -x "$ZENITY" ] ; then
-            $ZENITY --error --title="Error" --text="Error: black executable not found."
-            exit 1
-        elif [ -x "$XMSG" ] ; then
-            $XMSG -center -title "Error" "Error: black executable not found."
-            exit 1
-        elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then
-            winmessage="$(canonicalize_filename "./.git/hooks/winmessage.ps1")"
-            $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -center -title "Error" --text "Error: black executable not found."
-            exit 1
-        fi
-    fi
-    printf "Error: black executable not found.\n"
-    printf "Set the correct path in $(canonicalize_filename "$0").\n"
-    exit 1
-fi
-
-# create a random filename to store our generated patch
-prefix="pre-commit-black"
-suffix="$(date +%s)"
-patch="/tmp/$prefix-$suffix.patch"
-
-# clean up any older black patches
-$DELETE_OLD_PATCHES && rm -f /tmp/$prefix*.patch
-
-# create one patch containing all changes to the files
-git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file;
-do
-    # ignore thirdparty files
-    if grep -q "thirdparty" <<< $file; then
-        continue;
-    fi
-
-    # ignore file if not one of the names or extensions we handle
-    if ! matches_name_or_extension "$file"; then
-        continue;
-    fi
-
-    # format our file with black, create a patch with diff and append it to our $patch
-    # The sed call is necessary to transform the patch from
-    #    --- $file timestamp
-    #    +++ $file timestamp
-    # to both lines working on the same file and having a/ and b/ prefix.
-    # Else it can not be applied with 'git apply'.
-    "$BLACK" "$BLACK_OPTIONS" --diff "$file" | \
-        sed -e "1s|--- |--- a/|" -e "2s|+++ |+++ b/|" >> "$patch"
-done
-
-# if no patch has been generated all is ok, clean up the file stub and exit
-if [ ! -s "$patch" ] ; then
-    printf "Files in this commit comply with the black formatter rules.\n"
-    rm -f "$patch"
-    exit 0
-fi
-
-# a patch has been created, notify the user and exit
-printf "\nThe following differences were found between the code to commit "
-printf "and the black formatter rules:\n\n"
-
-if [ -t 1 ] ; then
-    $READER "$patch"
-    printf "\n"
-    # Allows us to read user input below, assigns stdin to keyboard
-    exec < /dev/tty
-    terminal="1"
-else
-    cat "$patch"
-    printf "\n"
-    # Allows non zero zenity/powershell output
-    set +e
-    terminal="0"
-fi
-
-while true; do
-    if [ $terminal = "0" ] ; then
-        if [ -x "$ZENITY" ] ; then
-            choice=$($ZENITY --text-info --filename="$patch" --width=800 --height=600 --title="Do you want to apply that patch?" --ok-label="Apply" --cancel-label="Do not apply" --extra-button="Apply and stage")
-            if [ "$?" = "0" ] ; then
-                yn="Y"
-            else
-                if [ "$choice" = "Apply and stage" ] ; then
-                    yn="S"
-                else
-                    yn="N"
-                fi
-            fi
-        elif [ -x "$XMSG" ] ; then
-            $XMSG -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?"
-            choice=$?
-            if [ "$choice" = "100" ] ; then
-                yn="Y"
-            elif [ "$choice" = "200" ] ; then
-                yn="S"
-            else
-                yn="N"
-            fi
-        elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then
-            winmessage="$(canonicalize_filename "./.git/hooks/winmessage.ps1")"
-            $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?"
-            choice=$?
-            if [ "$choice" = "100" ] ; then
-                yn="Y"
-            elif [ "$choice" = "200" ] ; then
-                yn="S"
-            else
-                yn="N"
-            fi
-        else
-            printf "Error: zenity, xmessage, or powershell executable not found.\n"
-            exit 1
-        fi
-    else
-        read -p "Do you want to apply that patch (Y - Apply, N - Do not apply, S - Apply and stage files)? [Y/N/S] " yn
-    fi
-    case $yn in
-        [Yy] ) git apply $patch;
-        printf "The patch was applied. You can now stage the changes and commit again.\n\n";
-        break
-        ;;
-        [Nn] ) printf "\nYou can apply these changes with:\n  git apply $patch\n";
-        printf "(may need to be called from the root directory of your repository)\n";
-        printf "Aborting commit. Apply changes and commit again or skip checking with";
-        printf " --no-verify (not recommended).\n\n";
-        break
-        ;;
-        [Ss] ) git apply $patch;
-        git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file;
-        do git add $file;
-        done
-        printf "The patch was applied and the changed files staged. You can now commit.\n\n";
-        break
-        ;;
-        * ) echo "Please answer yes or no."
-        ;;
-    esac
-done
-exit 1 # we don't commit in any case

+ 0 - 242
misc/hooks/pre-commit-clang-format

@@ -1,242 +0,0 @@
-#!/usr/bin/env bash
-
-# git pre-commit hook that runs a clang-format stylecheck.
-# Features:
-#  - abort commit when commit does not comply with the style guidelines
-#  - create a patch of the proposed style changes
-# Modifications for clang-format by [email protected]
-
-# This file is part of a set of unofficial pre-commit hooks available
-# at github.
-# Link:    https://github.com/githubbrowser/Pre-commit-hooks
-# Contact: David Martin, [email protected]
-
-# Some quality of life modifications made for Godot Engine.
-
-##################################################################
-# SETTINGS
-# Set path to clang-format binary.
-CLANG_FORMAT=`which clang-format 2>/dev/null`
-
-# Remove any older patches from previous commits. Set to true or false.
-DELETE_OLD_PATCHES=false
-
-# Only parse files with the extensions in FILE_EXTS. Set to true or false.
-# If false every changed file in the commit will be parsed with clang-format.
-# If true only files matching one of the extensions are parsed with clang-format.
-PARSE_EXTS=true
-
-# File types to parse. Only effective when PARSE_EXTS is true.
-FILE_EXTS=".c .h .cpp .hpp .cc .hh .cxx .m .mm .inc .java .glsl"
-
-# Use pygmentize instead of cat to parse diff with highlighting.
-# Install it with `pip install pygments` (Linux) or `easy_install Pygments` (Mac)
-PYGMENTIZE=`which pygmentize 2>/dev/null`
-if [ ! -z "$PYGMENTIZE" ]; then
-  READER="pygmentize -l diff"
-else
-  READER=cat
-fi
-
-# Path to zenity
-ZENITY=`which zenity 2>/dev/null`
-
-# Path to xmessage
-XMSG=`which xmessage 2>/dev/null`
-
-# Path to powershell (Windows only)
-PWSH=`which powershell 2>/dev/null`
-
-##################################################################
-# There should be no need to change anything below this line.
-
-. "$(dirname -- "$0")/canonicalize_filename.sh"
-
-# exit on error
-set -e
-
-# check whether the given file matches any of the set extensions
-matches_extension() {
-    local filename=$(basename "$1")
-    local extension=".${filename##*.}"
-    local ext
-
-    for ext in $FILE_EXTS; do [[ "$ext" == "$extension" ]] && return 0; done
-
-    return 1
-}
-
-# necessary check for initial commit
-if git rev-parse --verify HEAD >/dev/null 2>&1 ; then
-    against=HEAD
-else
-    # Initial commit: diff against an empty tree object
-    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
-fi
-
-# To get consistent formatting, we recommend contributors to use the same
-# clang-format version as CI.
-RECOMMENDED_CLANG_FORMAT_MAJOR_MIN="12"
-RECOMMENDED_CLANG_FORMAT_MAJOR_MAX="13"
-
-if [ ! -x "$CLANG_FORMAT" ] ; then
-	message="Error: clang-format executable not found. Please install clang-format $RECOMMENDED_CLANG_FORMAT_MAJOR_MAX."
-
-    if [ ! -t 1 ] ; then
-        if [ -x "$ZENITY" ] ; then
-            $ZENITY --error --title="Error" --text="$message"
-            exit 1
-        elif [ -x "$XMSG" ] ; then
-            $XMSG -center -title "Error" "$message"
-            exit 1
-        elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then
-            winmessage="$(canonicalize_filename "./.git/hooks/winmessage.ps1")"
-            $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -center -title "Error" --text "$message"
-            exit 1
-        fi
-    fi
-    printf "$message\n"
-    printf "Set the correct path in $(canonicalize_filename "$0").\n"
-    exit 1
-fi
-
-# The returned string can be inconsistent depending on where clang-format comes from.
-# Example output strings reported by `clang-format --version`:
-# - Ubuntu: "Ubuntu clang-format version 11.0.0-2"
-# - Fedora: "clang-format version 11.0.0 (Fedora 11.0.0-2.fc33)"
-CLANG_FORMAT_VERSION="$(clang-format --version | sed "s/[^0-9\.]*\([0-9\.]*\).*/\1/")"
-CLANG_FORMAT_MAJOR="$(echo "$CLANG_FORMAT_VERSION" | cut -d. -f1)"
-
-if [[ "$CLANG_FORMAT_MAJOR" -lt "$RECOMMENDED_CLANG_FORMAT_MAJOR_MIN" || "$CLANG_FORMAT_MAJOR" -gt "$RECOMMENDED_CLANG_FORMAT_MAJOR_MAX" ]]; then
-    echo "Warning: Your clang-format binary is the wrong version ($CLANG_FORMAT_VERSION, expected between $RECOMMENDED_CLANG_FORMAT_MAJOR_MIN and $RECOMMENDED_CLANG_FORMAT_MAJOR_MAX)."
-    echo "         Consider upgrading or downgrading clang-format as formatting may not be applied correctly."
-fi
-
-# create a random filename to store our generated patch
-prefix="pre-commit-clang-format"
-suffix="$(date +%s)"
-patch="/tmp/$prefix-$suffix.patch"
-
-# clean up any older clang-format patches
-$DELETE_OLD_PATCHES && rm -f /tmp/$prefix*.patch
-
-# create one patch containing all changes to the files
-git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file;
-do
-    # ignore thirdparty files
-    if grep -q "thirdparty" <<< $file; then
-        continue;
-    fi
-    if grep -q "platform/android/java/lib/src/com" <<< $file; then
-        continue;
-    fi
-    if grep -q "\-so_wrap." <<< $file; then
-        continue;
-    fi
-
-    # ignore file if we do check for file extensions and the file
-    # does not match any of the extensions specified in $FILE_EXTS
-    if $PARSE_EXTS && ! matches_extension "$file"; then
-        continue;
-    fi
-
-    # clang-format our sourcefile, create a patch with diff and append it to our $patch
-    # The sed call is necessary to transform the patch from
-    #    --- $file timestamp
-    #    +++ - timestamp
-    # to both lines working on the same file and having a/ and b/ prefix.
-    # Else it can not be applied with 'git apply'.
-    "$CLANG_FORMAT" -style=file "$file" --Wno-error=unknown | \
-        diff -u "$file" - | \
-        sed -e "1s|--- |--- a/|" -e "2s|+++ -|+++ b/$file|" >> "$patch"
-done
-
-# if no patch has been generated all is ok, clean up the file stub and exit
-if [ ! -s "$patch" ] ; then
-    printf "Files in this commit comply with the clang-format rules.\n"
-    rm -f "$patch"
-    exit 0
-fi
-
-# a patch has been created, notify the user and exit
-printf "\nThe following differences were found between the code to commit "
-printf "and the clang-format rules:\n\n"
-
-if [ -t 1 ] ; then
-    $READER "$patch"
-    printf "\n"
-    # Allows us to read user input below, assigns stdin to keyboard
-    exec < /dev/tty
-    terminal="1"
-else
-    cat "$patch"
-    printf "\n"
-    # Allows non zero zenity/powershell output
-    set +e
-    terminal="0"
-fi
-
-while true; do
-    if [ $terminal = "0" ] ; then
-        if [ -x "$ZENITY" ] ; then
-            choice=$($ZENITY --text-info --filename="$patch" --width=800 --height=600 --title="Do you want to apply that patch?" --ok-label="Apply" --cancel-label="Do not apply" --extra-button="Apply and stage")
-            if [ "$?" = "0" ] ; then
-                yn="Y"
-            else
-                if [ "$choice" = "Apply and stage" ] ; then
-                    yn="S"
-                else
-                    yn="N"
-                fi
-            fi
-        elif [ -x "$XMSG" ] ; then
-            $XMSG -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?"
-            choice=$?
-            if [ "$choice" = "100" ] ; then
-                yn="Y"
-            elif [ "$choice" = "200" ] ; then
-                yn="S"
-            else
-                yn="N"
-            fi
-        elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then
-            winmessage="$(canonicalize_filename "./.git/hooks/winmessage.ps1")"
-            $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?"
-            choice=$?
-            if [ "$choice" = "100" ] ; then
-                yn="Y"
-            elif [ "$choice" = "200" ] ; then
-                yn="S"
-            else
-                yn="N"
-            fi
-        else
-            printf "Error: zenity, xmessage, or powershell executable not found.\n"
-            exit 1
-        fi
-    else
-        read -p "Do you want to apply that patch (Y - Apply, N - Do not apply, S - Apply and stage files)? [Y/N/S] " yn
-    fi
-    case $yn in
-        [Yy] ) git apply $patch;
-        printf "The patch was applied. You can now stage the changes and commit again.\n\n";
-        break
-        ;;
-        [Nn] ) printf "\nYou can apply these changes with:\n  git apply $patch\n";
-        printf "(may need to be called from the root directory of your repository)\n";
-        printf "Aborting commit. Apply changes and commit again or skip checking with";
-        printf " --no-verify (not recommended).\n\n";
-        break
-        ;;
-        [Ss] ) git apply $patch;
-        git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file;
-        do git add $file;
-        done
-        printf "The patch was applied and the changed files staged. You can now commit.\n\n";
-        break
-        ;;
-        * ) echo "Please answer yes or no."
-        ;;
-    esac
-done
-exit 1 # we don't commit in any case

+ 0 - 103
misc/hooks/winmessage.ps1

@@ -1,103 +0,0 @@
-Param (
-	[string]$file = "",
-	[string]$text = "",
-	[string]$buttons = "OK:0",
-	[string]$default = "",
-	[switch]$nearmouse = $false,
-	[switch]$center = $false,
-	[string]$geometry = "",
-	[int32]$timeout = 0,
-	[string]$title = "Message"
-)
-Add-Type -assembly System.Windows.Forms
-
-$global:Result = 0
-
-$main_form = New-Object System.Windows.Forms.Form
-$main_form.Text = $title
-
-$geometry_data = $geometry.Split("+")
-if ($geometry_data.Length -ge 1) {
-	$size_data = $geometry_data[0].Split("x")
-	if ($size_data.Length -eq 2) {
-		$main_form.Width = $size_data[0]
-		$main_form.Height = $size_data[1]
-	}
-}
-if ($geometry_data.Length -eq 3) {
-	$main_form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual
-	$main_form.Location = New-Object System.Drawing.Point($geometry_data[1], $geometry_data[2])
-}
-if ($nearmouse) {
-	$main_form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual
-	$main_form.Location = System.Windows.Forms.Cursor.Position
-}
-if ($center) {
-	$main_form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
-}
-
-$main_form.SuspendLayout()
-
-$button_panel = New-Object System.Windows.Forms.FlowLayoutPanel
-$button_panel.SuspendLayout()
-$button_panel.FlowDirection = [System.Windows.Forms.FlowDirection]::RightToLeft
-$button_panel.Dock = [System.Windows.Forms.DockStyle]::Bottom
-$button_panel.Autosize = $true
-
-if ($file -ne "") {
-	$text = [IO.File]::ReadAllText($file).replace("`n", "`r`n")
-}
-
-if ($text -ne "") {
-	$text_box = New-Object System.Windows.Forms.TextBox
-	$text_box.Multiline = $true
-	$text_box.ReadOnly = $true
-	$text_box.Autosize = $true
-	$text_box.Text = $text
-	$text_box.Select(0,0)
-	$text_box.Dock = [System.Windows.Forms.DockStyle]::Fill
-	$main_form.Controls.Add($text_box)
-}
-
-$buttons_array = $buttons.Split(",")
-foreach ($button in $buttons_array) {
-	$button_data = $button.Split(":")
-	$button_ctl = New-Object System.Windows.Forms.Button
-	if ($button_data.Length -eq 2) {
-		$button_ctl.Tag = $button_data[1]
-	} else {
-		$button_ctl.Tag = 100 + $buttons_array.IndexOf($button)
-	}
-	if ($default -eq $button_data[0]) {
-		$main_form.AcceptButton = $button_ctl
-	}
-	$button_ctl.Autosize = $true
-	$button_ctl.Text = $button_data[0]
-	$button_ctl.Add_Click(
-		{
-			Param($sender)
-			$global:Result = $sender.Tag
-			$main_form.Close()
-		}
-	)
-	$button_panel.Controls.Add($button_ctl)
-}
-$main_form.Controls.Add($button_panel)
-
-$button_panel.ResumeLayout($false)
-$main_form.ResumeLayout($false)
-
-if ($timeout -gt 0) {
-	$timer = New-Object System.Windows.Forms.Timer
-	$timer.Add_Tick(
-		{
-			$global:Result = 0
-			$main_form.Close()
-		}
-	)
-	$timer.Interval = $timeout
-	$timer.Start()
-}
-$dlg_res = $main_form.ShowDialog()
-
-[Environment]::Exit($global:Result)

+ 0 - 25
misc/scripts/black_format.sh

@@ -1,25 +0,0 @@
-#!/usr/bin/env bash
-
-# This script runs black on all Python files in the repo.
-
-set -uo pipefail
-
-# Apply black.
-echo -e "Formatting Python files..."
-PY_FILES=$(git ls-files -- '*SConstruct' '*SCsub' '*.py' ':!:.git/*' ':!:thirdparty/*')
-black -l 120 $PY_FILES
-
-diff=$(git diff --color)
-
-# If no patch has been generated all is OK, clean up, and exit.
-if [ -z "$diff" ] ; then
-    printf "Files in this commit comply with the black style rules.\n"
-    exit 0
-fi
-
-# A patch has been created, notify the user, clean up, and exit.
-printf "\n*** The following differences were found between the code "
-printf "and the formatting rules:\n\n"
-echo "$diff"
-printf "\n*** Aborting, please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\n"
-exit 1

+ 4 - 2
misc/scripts/check_ci_log.py

@@ -9,8 +9,8 @@ if len(sys.argv) < 2:
 
 fname = sys.argv[1]
 
-fileread = open(fname.strip(), "r")
-file_contents = fileread.read()
+with open(fname.strip(), "r", encoding="utf-8") as fileread:
+    file_contents = fileread.read()
 
 # If find "ERROR: AddressSanitizer:", then happens invalid read or write
 # This is critical bug, so we need to fix this as fast as possible
@@ -25,6 +25,8 @@ if (
     file_contents.find("Program crashed with signal") != -1
     or file_contents.find("Dumping the backtrace") != -1
     or file_contents.find("Segmentation fault (core dumped)") != -1
+    or file_contents.find("Aborted (core dumped)") != -1
+    or file_contents.find("terminate called without an active exception") != -1
 ):
     print("FATAL ERROR: Godot has been crashed.")
     sys.exit(52)

+ 3 - 3
misc/scripts/check_get_file_list.py

@@ -1,12 +1,12 @@
 #!/usr/bin/env python
 
-import os, sys
-
+import os
+import sys
 from pathlib import Path
 
 sys.path.insert(1, os.path.join(os.path.dirname(__file__), "..", ".."))
 
-from binding_generator import get_file_list, generate_bindings
+from binding_generator import generate_bindings, get_file_list
 
 api_filepath = "gdextension/extension_api.json"
 bits = "64"

+ 0 - 38
misc/scripts/clang_format.sh

@@ -1,38 +0,0 @@
-#!/usr/bin/env bash
-
-# This script runs clang-format and fixes copyright headers on all relevant files in the repo.
-# This is the primary script responsible for fixing style violations.
-
-set -uo pipefail
-
-# Loops through all code files tracked by Git.
-git ls-files -- '*.c' '*.h' '*.cpp' '*.hpp' '*.cc' '*.hh' '*.cxx' '*.m' '*.mm' '*.inc' |
-while read -r f; do
-    # Run clang-format.
-    clang-format --Wno-error=unknown -i "$f"
-
-    # Fix copyright headers, but not all files get them.
-    if [[ "$f" == *"inc" ]]; then
-        continue
-    elif [[ "$f" == *"glsl" ]]; then
-        continue
-    elif [[ "$f" == "test/"* ]]; then
-        continue
-    fi
-
-    python misc/scripts/copyright_headers.py "$f"
-done
-
-diff=$(git diff --color)
-
-# If no patch has been generated all is OK, clean up, and exit.
-if [ -z "$diff" ] ; then
-    printf "Files in this commit comply with the clang-tidy style rules.\n"
-    exit 0
-fi
-
-# A patch has been created, notify the user, clean up, and exit.
-printf "\n*** The following changes have been made to comply with the formatting rules:\n\n"
-echo "$diff"
-printf "\n*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\n"
-exit 1

+ 0 - 5
misc/scripts/codespell.sh

@@ -1,5 +0,0 @@
-#!/bin/sh
-SKIP_LIST="./thirdparty,*.gen.*,*.po,*.pot,package-lock.json,./core/string/locales.h,./DONORS.md,./misc/scripts/codespell.sh"
-IGNORE_LIST="ba,childs,curvelinear,expct,fave,findn,gird,inout,lod,nd,numer,ois,ro,statics,te,varn"
-
-codespell -w -q 3 -S "${SKIP_LIST}" -L "${IGNORE_LIST}"

+ 50 - 49
misc/scripts/copyright_headers.py

@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
+import os
 import sys
 
 header = """\
@@ -35,61 +36,61 @@ header = """\
 /**************************************************************************/
 """
 
-fname = sys.argv[1]
+if len(sys.argv) < 2:
+    print("Invalid usage of copyright_headers.py, it should be called with a path to one or multiple files.")
+    sys.exit(1)
 
-# Handle replacing $filename with actual filename and keep alignment
-fsingle = fname.strip()
-if fsingle.find("/") != -1:
-    fsingle = fsingle[fsingle.rfind("/") + 1 :]
-rep_fl = "$filename"
-rep_fi = fsingle
-len_fl = len(rep_fl)
-len_fi = len(rep_fi)
-# Pad with spaces to keep alignment
-if len_fi < len_fl:
-    for x in range(len_fl - len_fi):
-        rep_fi += " "
-elif len_fl < len_fi:
-    for x in range(len_fi - len_fl):
-        rep_fl += " "
-if header.find(rep_fl) != -1:
-    text = header.replace(rep_fl, rep_fi)
-else:
-    text = header.replace("$filename", fsingle)
-text += "\n"
+for f in sys.argv[1:]:
+    fname = f
 
-# We now have the proper header, so we want to ignore the one in the original file
-# and potentially empty lines and badly formatted lines, while keeping comments that
-# come after the header, and then keep everything non-header unchanged.
-# To do so, we skip empty lines that may be at the top in a first pass.
-# In a second pass, we skip all consecutive comment lines starting with "/*",
-# then we can append the rest (step 2).
+    # Handle replacing $filename with actual filename and keep alignment
+    fsingle = os.path.basename(fname.strip())
+    rep_fl = "$filename"
+    rep_fi = fsingle
+    len_fl = len(rep_fl)
+    len_fi = len(rep_fi)
+    # Pad with spaces to keep alignment
+    if len_fi < len_fl:
+        for x in range(len_fl - len_fi):
+            rep_fi += " "
+    elif len_fl < len_fi:
+        for x in range(len_fi - len_fl):
+            rep_fl += " "
+    if header.find(rep_fl) != -1:
+        text = header.replace(rep_fl, rep_fi)
+    else:
+        text = header.replace("$filename", fsingle)
+    text += "\n"
 
-fileread = open(fname.strip(), "r")
-line = fileread.readline()
-header_done = False
+    # We now have the proper header, so we want to ignore the one in the original file
+    # and potentially empty lines and badly formatted lines, while keeping comments that
+    # come after the header, and then keep everything non-header unchanged.
+    # To do so, we skip empty lines that may be at the top in a first pass.
+    # In a second pass, we skip all consecutive comment lines starting with "/*",
+    # then we can append the rest (step 2).
 
-while line.strip() == "":  # Skip empty lines at the top
-    line = fileread.readline()
+    with open(fname.strip(), "r", encoding="utf-8") as fileread:
+        line = fileread.readline()
+        header_done = False
 
-if line.find("/**********") == -1:  # Godot header starts this way
-    # Maybe starting with a non-Godot comment, abort header magic
-    header_done = True
+        while line.strip() == "" and line != "":  # Skip empty lines at the top
+            line = fileread.readline()
 
-while not header_done:  # Handle header now
-    if line.find("/*") != 0:  # No more starting with a comment
-        header_done = True
-        if line.strip() != "":
-            text += line
-    line = fileread.readline()
+        if line.find("/**********") == -1:  # Godot header starts this way
+            # Maybe starting with a non-Godot comment, abort header magic
+            header_done = True
 
-while line != "":  # Dump everything until EOF
-    text += line
-    line = fileread.readline()
+        while not header_done:  # Handle header now
+            if line.find("/*") != 0:  # No more starting with a comment
+                header_done = True
+                if line.strip() != "":
+                    text += line
+            line = fileread.readline()
 
-fileread.close()
+        while line != "":  # Dump everything until EOF
+            text += line
+            line = fileread.readline()
 
-# Write
-filewrite = open(fname.strip(), "w")
-filewrite.write(text)
-filewrite.close()
+    # Write
+    with open(fname.strip(), "w", encoding="utf-8", newline="\n") as filewrite:
+        filewrite.write(text)

+ 46 - 0
misc/scripts/file_format.py

@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+
+if len(sys.argv) < 2:
+    print("Invalid usage of file_format.py, it should be called with a path to one or multiple files.")
+    sys.exit(1)
+
+BOM = b"\xef\xbb\xbf"
+
+changed = []
+invalid = []
+
+for file in sys.argv[1:]:
+    try:
+        with open(file, "rt", encoding="utf-8") as f:
+            original = f.read()
+    except UnicodeDecodeError:
+        invalid.append(file)
+        continue
+
+    if original == "":
+        continue
+
+    revamp = "\n".join([line.rstrip("\n\r\t ") for line in original.splitlines(True)]).rstrip("\n") + "\n"
+
+    new_raw = revamp.encode(encoding="utf-8")
+    if new_raw.startswith(BOM):
+        new_raw = new_raw[len(BOM) :]
+
+    with open(file, "rb") as f:
+        old_raw = f.read()
+
+    if old_raw != new_raw:
+        changed.append(file)
+        with open(file, "wb") as f:
+            f.write(new_raw)
+
+if changed:
+    for file in changed:
+        print(f"FIXED: {file}")
+if invalid:
+    for file in invalid:
+        print(f"REQUIRES MANUAL CHANGES: {file}")
+    sys.exit(1)

+ 0 - 41
misc/scripts/file_format.sh

@@ -1,41 +0,0 @@
-#!/usr/bin/env bash
-
-# This script ensures proper POSIX text file formatting and a few other things.
-# This is supplementary to clang_format.sh and black_format.sh, but should be
-# run before them.
-
-# We need dos2unix and recode.
-if [ ! -x "$(command -v dos2unix)" -o ! -x "$(command -v recode)" ]; then
-    printf "Install 'dos2unix' and 'recode' to use this script.\n"
-fi
-
-set -uo pipefail
-IFS=$'\n\t'
-
-# Loops through all text files tracked by Git.
-git grep -zIl '' |
-while IFS= read -rd '' f; do
-    # Ensure that files are UTF-8 formatted.
-    recode UTF-8 "$f" 2> /dev/null
-    # Ensure that files have LF line endings and do not contain a BOM.
-    dos2unix "$f" 2> /dev/null
-    # Remove trailing space characters and ensures that files end
-    # with newline characters. -l option handles newlines conveniently.
-    perl -i -ple 's/\s*$//g' "$f"
-done
-
-diff=$(git diff --color)
-
-# If no patch has been generated all is OK, clean up, and exit.
-if [ -z "$diff" ] ; then
-    printf "Files in this commit comply with the formatting rules.\n"
-    rm -f patch.patch
-    exit 0
-fi
-
-# A patch has been created, notify the user, clean up, and exit.
-printf "\n*** The following differences were found between the code "
-printf "and the formatting rules:\n\n"
-echo "$diff"
-printf "\n*** Aborting, please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\n"
-exit 1

+ 127 - 0
misc/scripts/header_guards.py

@@ -0,0 +1,127 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+from pathlib import Path
+
+if len(sys.argv) < 2:
+    print("Invalid usage of header_guards.py, it should be called with a path to one or multiple files.")
+    sys.exit(1)
+
+HEADER_CHECK_OFFSET = 30
+HEADER_BEGIN_OFFSET = 31
+HEADER_END_OFFSET = -1
+
+changed = []
+invalid = []
+
+for file in sys.argv[1:]:
+    with open(file, "rt", encoding="utf-8", newline="\n") as f:
+        lines = f.readlines()
+
+    if len(lines) <= HEADER_BEGIN_OFFSET:
+        continue  # Most likely a dummy file.
+
+    if lines[HEADER_CHECK_OFFSET].startswith("#import"):
+        continue  # Early catch obj-c file.
+
+    name = f"GODOT_{Path(file).name}".upper().replace(".", "_").replace("-", "_").replace(" ", "_")
+
+    HEADER_CHECK = f"#ifndef {name}\n"
+    HEADER_BEGIN = f"#define {name}\n"
+    HEADER_END = f"#endif // {name}\n"
+
+    if (
+        lines[HEADER_CHECK_OFFSET] == HEADER_CHECK
+        and lines[HEADER_BEGIN_OFFSET] == HEADER_BEGIN
+        and lines[HEADER_END_OFFSET] == HEADER_END
+    ):
+        continue
+
+    # Guards might exist but with the wrong names.
+    if (
+        lines[HEADER_CHECK_OFFSET].startswith("#ifndef")
+        and lines[HEADER_BEGIN_OFFSET].startswith("#define")
+        and lines[HEADER_END_OFFSET].startswith("#endif")
+    ):
+        lines[HEADER_CHECK_OFFSET] = HEADER_CHECK
+        lines[HEADER_BEGIN_OFFSET] = HEADER_BEGIN
+        lines[HEADER_END_OFFSET] = HEADER_END
+        with open(file, "wt", encoding="utf-8", newline="\n") as f:
+            f.writelines(lines)
+        changed.append(file)
+        continue
+
+    header_check = -1
+    header_begin = -1
+    header_end = -1
+    pragma_once = -1
+    objc = False
+
+    for idx, line in enumerate(lines):
+        if not line.startswith("#"):
+            continue
+        elif line.startswith("#ifndef") and header_check == -1:
+            header_check = idx
+        elif line.startswith("#define") and header_begin == -1:
+            header_begin = idx
+        elif line.startswith("#endif") and header_end == -1:
+            header_end = idx
+        elif line.startswith("#pragma once"):
+            pragma_once = idx
+            break
+        elif line.startswith("#import"):
+            objc = True
+            break
+
+    if objc:
+        continue
+
+    if pragma_once != -1:
+        lines.pop(pragma_once)
+        lines.insert(HEADER_CHECK_OFFSET, HEADER_CHECK)
+        lines.insert(HEADER_BEGIN_OFFSET, HEADER_BEGIN)
+        lines.append("\n")
+        lines.append(HEADER_END)
+        with open(file, "wt", encoding="utf-8", newline="\n") as f:
+            f.writelines(lines)
+        changed.append(file)
+        continue
+
+    if header_check == -1 and header_begin == -1 and header_end == -1:
+        # Guards simply didn't exist
+        lines.insert(HEADER_CHECK_OFFSET, HEADER_CHECK)
+        lines.insert(HEADER_BEGIN_OFFSET, HEADER_BEGIN)
+        lines.append("\n")
+        lines.append(HEADER_END)
+        with open(file, "wt", encoding="utf-8", newline="\n") as f:
+            f.writelines(lines)
+        changed.append(file)
+        continue
+
+    if header_check != -1 and header_begin != -1 and header_end != -1:
+        # All prepends "found", see if we can salvage this.
+        if header_check == header_begin - 1 and header_begin < header_end:
+            lines.pop(header_check)
+            lines.pop(header_begin - 1)
+            lines.pop(header_end - 2)
+            if lines[header_end - 3] == "\n":
+                lines.pop(header_end - 3)
+            lines.insert(HEADER_CHECK_OFFSET, HEADER_CHECK)
+            lines.insert(HEADER_BEGIN_OFFSET, HEADER_BEGIN)
+            lines.append("\n")
+            lines.append(HEADER_END)
+            with open(file, "wt", encoding="utf-8", newline="\n") as f:
+                f.writelines(lines)
+            changed.append(file)
+            continue
+
+    invalid.append(file)
+
+if changed:
+    for file in changed:
+        print(f"FIXED: {file}")
+if invalid:
+    for file in invalid:
+        print(f"REQUIRES MANUAL CHANGES: {file}")
+    sys.exit(1)

+ 0 - 60
misc/scripts/header_guards.sh

@@ -1,60 +0,0 @@
-#!/bin/bash
-
-if [ ! -f "SConstruct" ]; then
-  echo "Warning: This script is intended to be run from the root of the Godot repository."
-  echo "Some of the paths checks may not work as intended from a different folder."
-fi
-
-files_invalid_guard=""
-
-for file in $(find . -name "*.hpp" -print); do
-  # Skip generated files.
-  if [[ "$file" == "./gen/"* || "$file" == "./include/gen/"* ]]; then continue; fi
-  # Skip the test project.
-  if [[ "$file" == "./test/"* ]]; then continue; fi
-
-  bname=$(basename $file .hpp)
-
-  # NOTE: The "GODOT_CPP_" prefix is already used by the generated
-  # bindings, so we can't use that. We'll use "GODOT_" instead.
-  prefix="GODOT_"
-
-  # ^^ is bash builtin for UPPERCASE.
-  guard="${prefix}${bname^^}_HPP"
-
-  # Replaces guards to use computed name.
-  # We also add some \n to make sure there's a proper separation.
-  sed -i $file -e "0,/ifndef/s/#ifndef.*/\n#ifndef $guard/"
-  sed -i $file -e "0,/define/s/#define.*/#define $guard\n/"
-  sed -i $file -e "$ s/#endif.*/\n#endif \/\/ $guard/"
-  # Removes redundant \n added before, if they weren't needed.
-  sed -i $file -e "/^$/N;/^\n$/D"
-
-  # Check that first ifndef (should be header guard) is at the expected position.
-  # If not it can mean we have some code before the guard that should be after.
-  # "31" is the expected line with the copyright header.
-  first_ifndef=$(grep -n -m 1 "ifndef" $file | sed 's/\([0-9]*\).*/\1/')
-  if [[ "$first_ifndef" != "31" ]]; then
-    files_invalid_guard+="$file\n"
-  fi
-done
-
-if [[ ! -z "$files_invalid_guard" ]]; then
-  echo -e "The following files were found to have potentially invalid header guard:\n"
-  echo -e "$files_invalid_guard"
-fi
-
-diff=$(git diff --color)
-
-# If no diff has been generated all is OK, clean up, and exit.
-if [ -z "$diff" ] ; then
-    printf "Files in this commit comply with the header guards formatting rules.\n"
-    exit 0
-fi
-
-# A diff has been created, notify the user, clean up, and exit.
-printf "\n*** The following differences were found between the code "
-printf "and the header guards formatting rules:\n\n"
-echo "$diff"
-printf "\n*** Aborting, please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\n"
-exit 1

+ 0 - 11
misc/scripts/mypy.ini

@@ -1,11 +0,0 @@
-[mypy]
-ignore_missing_imports = true
-disallow_any_generics = True
-pretty = True
-show_column_numbers = True
-warn_redundant_casts = True
-warn_return_any = True
-warn_unreachable = True
-
-namespace_packages = True
-explicit_package_bases = True

+ 0 - 6
misc/scripts/mypy_check.sh

@@ -1,6 +0,0 @@
-#!/usr/bin/env bash
-
-set -uo pipefail
-
-echo -e "Python: mypy static analysis..."
-mypy --config-file=./misc/scripts/mypy.ini .

+ 58 - 0
pyproject.toml

@@ -0,0 +1,58 @@
+[tool.mypy]
+disallow_any_generics = true
+explicit_package_bases = true
+ignore_missing_imports = true
+namespace_packages = true
+no_implicit_optional = true
+pretty = true
+scripts_are_modules = true
+show_column_numbers = true
+warn_redundant_casts = true
+warn_return_any = true
+warn_unreachable = true
+
+[tool.ruff]
+extend-include = ["SConstruct"]
+line-length = 120
+target-version = "py37"
+
+[tool.ruff.lint]
+extend-select = [
+	"I", # isort
+]
+
+[tool.ruff.lint.per-file-ignores]
+"SConstruct" = [
+	"F821", # Undefined name
+]
+
+[tool.codespell]
+enable-colors = ""
+write-changes = ""
+check-hidden = ""
+quiet-level = 3
+builtin = "clear,rare,en-GB_to_en-US"
+ignore-words-list = """\
+	breaked,
+	cancelled,
+	checkin,
+	curvelinear,
+	doubleclick,
+	expct,
+	findn,
+	gird,
+	hel,
+	inout,
+	labelin,
+	lod,
+	mis,
+	nd,
+	numer,
+	ot,
+	outin,
+	requestor,
+	te,
+	textin,
+	thirdparty,
+	vai
+"""

+ 3 - 3
src/core/memory.cpp

@@ -36,7 +36,7 @@ namespace godot {
 
 void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) {
 #ifdef DEBUG_ENABLED
-	bool prepad = false; // Alredy pre paded in the engine.
+	bool prepad = false; // Already pre paded in the engine.
 #else
 	bool prepad = p_pad_align;
 #endif
@@ -63,7 +63,7 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) {
 	uint8_t *mem = (uint8_t *)p_memory;
 
 #ifdef DEBUG_ENABLED
-	bool prepad = false; // Alredy pre paded in the engine.
+	bool prepad = false; // Already pre paded in the engine.
 #else
 	bool prepad = p_pad_align;
 #endif
@@ -82,7 +82,7 @@ void Memory::free_static(void *p_ptr, bool p_pad_align) {
 	uint8_t *mem = (uint8_t *)p_ptr;
 
 #ifdef DEBUG_ENABLED
-	bool prepad = false; // Alredy pre paded in the engine.
+	bool prepad = false; // Already pre paded in the engine.
 #else
 	bool prepad = p_pad_align;
 #endif

+ 1 - 1
src/variant/basis.cpp

@@ -816,7 +816,7 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
 		return;
 	}
 	// As we have reached here there are no singularities so we can handle normally.
-	double s = Math::sqrt((rows[2][1] - rows[1][2]) * (rows[2][1] - rows[1][2]) + (rows[0][2] - rows[2][0]) * (rows[0][2] - rows[2][0]) + (rows[1][0] - rows[0][1]) * (rows[1][0] - rows[0][1])); // Used to normalise.
+	double s = Math::sqrt((rows[2][1] - rows[1][2]) * (rows[2][1] - rows[1][2]) + (rows[0][2] - rows[2][0]) * (rows[0][2] - rows[2][0]) + (rows[1][0] - rows[0][1]) * (rows[1][0] - rows[0][1])); // Used to normalize.
 
 	if (Math::abs(s) < CMP_EPSILON) {
 		// Prevent divide by zero, should not happen if matrix is orthogonal and should be caught by singularity test above.

+ 1 - 1
src/variant/color.cpp

@@ -192,7 +192,7 @@ void Color::set_hsv(float p_h, float p_s, float p_v, float p_alpha) {
 	a = p_alpha;
 
 	if (p_s == 0.0f) {
-		// Achromatic (grey)
+		// Achromatic (gray)
 		r = g = b = p_v;
 		return;
 	}

+ 0 - 2
test/SConstruct

@@ -1,6 +1,4 @@
 #!/usr/bin/env python
-import os
-import sys
 
 env = SConscript("../SConstruct")
 

+ 2 - 2
test/project/main.gd

@@ -96,8 +96,8 @@ func _ready():
 	assert_equal(example.test_str_utility(), "Hello, World! The answer is 42")
 
 	# Test converting string to char* and doing comparison.
-	assert_equal(example.test_string_is_fourty_two("blah"), false)
-	assert_equal(example.test_string_is_fourty_two("fourty two"), true)
+	assert_equal(example.test_string_is_forty_two("blah"), false)
+	assert_equal(example.test_string_is_forty_two("forty two"), true)
 
 	# String::resize().
 	assert_equal(example.test_string_resize("What"), "What!?")

+ 5 - 5
test/src/example.cpp

@@ -202,7 +202,7 @@ void Example::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("test_node_argument"), &Example::test_node_argument);
 	ClassDB::bind_method(D_METHOD("test_string_ops"), &Example::test_string_ops);
 	ClassDB::bind_method(D_METHOD("test_str_utility"), &Example::test_str_utility);
-	ClassDB::bind_method(D_METHOD("test_string_is_fourty_two"), &Example::test_string_is_fourty_two);
+	ClassDB::bind_method(D_METHOD("test_string_is_forty_two"), &Example::test_string_is_forty_two);
 	ClassDB::bind_method(D_METHOD("test_string_resize"), &Example::test_string_resize);
 	ClassDB::bind_method(D_METHOD("test_vector_ops"), &Example::test_vector_ops);
 	ClassDB::bind_method(D_METHOD("test_vector_init_list"), &Example::test_vector_init_list);
@@ -299,7 +299,7 @@ bool Example::has_object_instance_binding() const {
 
 Example::Example() :
 		object_instance_binding_set_by_parent_constructor(has_object_instance_binding()) {
-	// Test conversion, to ensure users can use all parent calss functions at this time.
+	// Test conversion, to ensure users can use all parent class functions at this time.
 	// It would crash if instance binding still not be initialized.
 	Variant v = Variant(this);
 	Object *o = (Object *)v;
@@ -361,7 +361,7 @@ ExampleRef *Example::return_extended_ref() const {
 }
 
 Ref<ExampleRef> Example::extended_ref_checks(Ref<ExampleRef> p_ref) const {
-	// This is therefor the prefered way of instancing and returning a refcounted object:
+	// This is therefore the preferred way of instancing and returning a refcounted object:
 	Ref<ExampleRef> ref;
 	ref.instantiate();
 	return ref;
@@ -410,8 +410,8 @@ String Example::test_str_utility() const {
 	return UtilityFunctions::str("Hello, ", "World", "! The answer is ", 42);
 }
 
-bool Example::test_string_is_fourty_two(const String &p_string) const {
-	return strcmp(p_string.utf8().ptr(), "fourty two") == 0;
+bool Example::test_string_is_forty_two(const String &p_string) const {
+	return strcmp(p_string.utf8().ptr(), "forty two") == 0;
 }
 
 String Example::test_string_resize(String p_string) const {

+ 1 - 1
test/src/example.h

@@ -132,7 +132,7 @@ public:
 	Example *test_node_argument(Example *p_node) const;
 	String test_string_ops() const;
 	String test_str_utility() const;
-	bool test_string_is_fourty_two(const String &p_str) const;
+	bool test_string_is_forty_two(const String &p_str) const;
 	String test_string_resize(String p_original) const;
 	int test_vector_ops() const;
 	int test_vector_init_list() const;

+ 2 - 2
tools/android.py

@@ -1,8 +1,8 @@
 import os
 import sys
-import my_spawn
+
 import common_compiler_flags
-from SCons.Script import ARGUMENTS
+import my_spawn
 
 
 def options(opts):

+ 0 - 1
tools/common_compiler_flags.py

@@ -1,6 +1,5 @@
 import os
 import subprocess
-import sys
 
 
 def using_clang(env):

+ 7 - 6
tools/godotcpp.py

@@ -1,15 +1,16 @@
-import os, sys, platform
+import os
+import platform
+import sys
 
-from SCons.Variables import EnumVariable, PathVariable, BoolVariable
-from SCons.Variables.BoolVariable import _text2bool
-from SCons.Tool import Tool
 from SCons.Action import Action
 from SCons.Builder import Builder
 from SCons.Errors import UserError
 from SCons.Script import ARGUMENTS
+from SCons.Tool import Tool
+from SCons.Variables import BoolVariable, EnumVariable, PathVariable
+from SCons.Variables.BoolVariable import _text2bool
 
-
-from binding_generator import scons_generate_bindings, scons_emit_files
+from binding_generator import scons_emit_files, scons_generate_bindings
 
 
 def add_sources(sources, dir, extension):

+ 3 - 2
tools/ios.py

@@ -1,8 +1,9 @@
 import os
-import sys
 import subprocess
+import sys
+
 import common_compiler_flags
-from SCons.Variables import *
+from SCons.Variables import BoolVariable
 
 if sys.version_info < (3,):
 

+ 1 - 1
tools/linux.py

@@ -1,6 +1,6 @@
 import common_compiler_flags
-from SCons.Variables import *
 from SCons.Tool import clang, clangxx
+from SCons.Variables import BoolVariable
 
 
 def options(opts):

+ 1 - 0
tools/macos.py

@@ -1,5 +1,6 @@
 import os
 import sys
+
 import common_compiler_flags
 
 

+ 0 - 1
tools/my_spawn.py

@@ -32,7 +32,6 @@ def configure(env):
         return rv
 
     def mySpawn(sh, escape, cmd, args, env):
-
         newargs = " ".join(args[1:])
         cmdline = cmd + " " + newargs
 

+ 0 - 1
tools/web.py

@@ -1,4 +1,3 @@
-import os
 import common_compiler_flags
 from SCons.Util import WhereIs
 

+ 4 - 4
tools/windows.py

@@ -1,8 +1,9 @@
 import sys
-import my_spawn
+
 import common_compiler_flags
-from SCons.Tool import msvc, mingw
-from SCons.Variables import *
+import my_spawn
+from SCons.Tool import mingw, msvc
+from SCons.Variables import BoolVariable
 
 
 def options(opts):
@@ -16,7 +17,6 @@ def exists(env):
 
 
 def generate(env):
-    base = None
     if not env["use_mingw"] and msvc.exists(env):
         if env["arch"] == "x86_64":
             env["TARGET_ARCH"] = "amd64"