summaryrefslogtreecommitdiffstats
path: root/methods.py
diff options
context:
space:
mode:
Diffstat (limited to 'methods.py')
-rw-r--r--methods.py293
1 files changed, 136 insertions, 157 deletions
diff --git a/methods.py b/methods.py
index 0c29632f10..99c47ca077 100644
--- a/methods.py
+++ b/methods.py
@@ -1,14 +1,14 @@
+import contextlib
+import glob
import os
-import sys
import re
-import glob
import subprocess
+import sys
from collections import OrderedDict
-from collections.abc import Mapping
from enum import Enum
-from typing import Iterator
+from io import StringIO, TextIOWrapper
from pathlib import Path
-from os.path import normpath, basename
+from typing import Generator, Optional
# Get the "Godot" folder name ahead of time
base_folder_path = str(os.path.abspath(Path(__file__).parent)) + "/"
@@ -196,7 +196,7 @@ def add_module_version_string(self, s):
def get_version_info(module_version_string="", silent=False):
build_name = "custom_build"
- if os.getenv("BUILD_NAME") != None:
+ if os.getenv("BUILD_NAME") is not None:
build_name = str(os.getenv("BUILD_NAME"))
if not silent:
print(f"Using custom build name: '{build_name}'.")
@@ -218,7 +218,7 @@ def get_version_info(module_version_string="", silent=False):
# For dev snapshots (alpha, beta, RC, etc.) we do not commit status change to Git,
# so this define provides a way to override it without having to modify the source.
- if os.getenv("GODOT_VERSION_STATUS") != None:
+ if os.getenv("GODOT_VERSION_STATUS") is not None:
version_info["status"] = str(os.getenv("GODOT_VERSION_STATUS"))
if not silent:
print(f"Using version status '{version_info['status']}', overriding the original '{version.status}'.")
@@ -277,79 +277,6 @@ def get_version_info(module_version_string="", silent=False):
return version_info
-_cleanup_env = None
-_cleanup_bool = False
-
-
-def write_file_if_needed(path, string):
- """Generates a file only if it doesn't already exist or the content has changed.
-
- Utilizes a dedicated SCons environment to ensure the files are properly removed
- during cleanup; will not attempt to create files during cleanup.
-
- - `path` - Path to the file in question; used to create cleanup logic.
- - `string` - Content to compare against an existing file.
- """
- global _cleanup_env
- global _cleanup_bool
-
- if _cleanup_env is None:
- from SCons.Environment import Environment
-
- _cleanup_env = Environment()
- _cleanup_bool = _cleanup_env.GetOption("clean")
-
- _cleanup_env.Clean("#", path)
- if _cleanup_bool:
- return
-
- try:
- with open(path, "r", encoding="utf-8", newline="\n") as f:
- if f.read() == string:
- return
- except FileNotFoundError:
- pass
-
- with open(path, "w", encoding="utf-8", newline="\n") as f:
- f.write(string)
-
-
-def generate_version_header(module_version_string=""):
- version_info = get_version_info(module_version_string)
-
- version_info_header = """\
-/* THIS FILE IS GENERATED DO NOT EDIT */
-#ifndef VERSION_GENERATED_GEN_H
-#define VERSION_GENERATED_GEN_H
-#define VERSION_SHORT_NAME "{short_name}"
-#define VERSION_NAME "{name}"
-#define VERSION_MAJOR {major}
-#define VERSION_MINOR {minor}
-#define VERSION_PATCH {patch}
-#define VERSION_STATUS "{status}"
-#define VERSION_BUILD "{build}"
-#define VERSION_MODULE_CONFIG "{module_config}"
-#define VERSION_WEBSITE "{website}"
-#define VERSION_DOCS_BRANCH "{docs_branch}"
-#define VERSION_DOCS_URL "https://docs.godotengine.org/en/" VERSION_DOCS_BRANCH
-#endif // VERSION_GENERATED_GEN_H
-""".format(
- **version_info
- )
-
- version_hash_data = """\
-/* THIS FILE IS GENERATED DO NOT EDIT */
-#include "core/version.h"
-const char *const VERSION_HASH = "{git_hash}";
-const uint64_t VERSION_TIMESTAMP = {git_timestamp};
-""".format(
- **version_info
- )
-
- write_file_if_needed("core/version_generated.gen.h", version_info_header)
- write_file_if_needed("core/version_hash.gen.cpp", version_hash_data)
-
-
def parse_cg_file(fname, uniforms, sizes, conditionals):
with open(fname, "r", encoding="utf-8") as fs:
line = fs.readline()
@@ -420,7 +347,7 @@ def detect_modules(search_path, recursive=False):
# Godot sources when using `custom_modules` build option.
version_path = os.path.join(path, "version.py")
if os.path.exists(version_path):
- with open(version_path) as f:
+ with open(version_path, "r", encoding="utf-8") as f:
if 'short_name = "godot"' in f.read():
return True
return False
@@ -465,63 +392,6 @@ def is_module(path):
return True
-def write_disabled_classes(class_list):
- file_contents = ""
-
- file_contents += "/* THIS FILE IS GENERATED DO NOT EDIT */\n"
- file_contents += "#ifndef DISABLED_CLASSES_GEN_H\n"
- file_contents += "#define DISABLED_CLASSES_GEN_H\n\n"
- for c in class_list:
- cs = c.strip()
- if cs != "":
- file_contents += "#define ClassDB_Disable_" + cs + " 1\n"
- file_contents += "\n#endif\n"
-
- write_file_if_needed("core/disabled_classes.gen.h", file_contents)
-
-
-def write_modules(modules):
- includes_cpp = ""
- initialize_cpp = ""
- uninitialize_cpp = ""
-
- for name, path in modules.items():
- try:
- with open(os.path.join(path, "register_types.h")):
- includes_cpp += '#include "' + path + '/register_types.h"\n'
- initialize_cpp += "#ifdef MODULE_" + name.upper() + "_ENABLED\n"
- initialize_cpp += "\tinitialize_" + name + "_module(p_level);\n"
- initialize_cpp += "#endif\n"
- uninitialize_cpp += "#ifdef MODULE_" + name.upper() + "_ENABLED\n"
- uninitialize_cpp += "\tuninitialize_" + name + "_module(p_level);\n"
- uninitialize_cpp += "#endif\n"
- except OSError:
- pass
-
- modules_cpp = """// register_module_types.gen.cpp
-/* THIS FILE IS GENERATED DO NOT EDIT */
-#include "register_module_types.h"
-
-#include "modules/modules_enabled.gen.h"
-
-%s
-
-void initialize_modules(ModuleInitializationLevel p_level) {
-%s
-}
-
-void uninitialize_modules(ModuleInitializationLevel p_level) {
-%s
-}
-""" % (
- includes_cpp,
- initialize_cpp,
- uninitialize_cpp,
- )
-
- write_file_if_needed("modules/register_module_types.gen.cpp", modules_cpp)
-
-
def convert_custom_modules_path(path):
if not path:
return path
@@ -562,7 +432,7 @@ def module_check_dependencies(self, module):
required_deps = self.module_dependencies[module][0] if module in self.module_dependencies else []
for dep in required_deps:
opt = "module_{}_enabled".format(dep)
- if not opt in self or not self[opt]:
+ if opt not in self or not self[opt]:
missing_deps.append(dep)
if missing_deps != []:
@@ -577,7 +447,6 @@ def module_check_dependencies(self, module):
def sort_module_list(env):
- out = OrderedDict()
deps = {k: v[0] + list(filter(lambda x: x in env.module_list, v[1])) for k, v in env.module_dependencies.items()}
frontier = list(env.module_list.keys())
@@ -662,6 +531,7 @@ def no_verbose(env):
link_shared_library_message = "{}Linking Shared Library {}$TARGET{} ...{}".format(*colors)
java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(*colors)
compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(*colors)
+ zip_archive_message = "{}Archiving {}$TARGET{} ...{}".format(*colors)
generated_file_message = "{}Generating {}$TARGET{} ...{}".format(*colors)
env["CXXCOMSTR"] = compile_source_message
@@ -675,6 +545,7 @@ def no_verbose(env):
env["JARCOMSTR"] = java_library_message
env["JAVACCOMSTR"] = java_compile_source_message
env["RCCOMSTR"] = compiled_resource_message
+ env["ZIPCOMSTR"] = zip_archive_message
env["GENCOMSTR"] = generated_file_message
@@ -777,7 +648,7 @@ def detect_visual_c_compiler_version(tools_env):
def find_visual_c_batch_file(env):
- from SCons.Tool.MSCommon.vc import get_default_version, get_host_target, find_batch_file, find_vc_pdir
+ from SCons.Tool.MSCommon.vc import find_batch_file, find_vc_pdir, get_default_version, get_host_target
msvc_version = get_default_version(env)
@@ -823,10 +694,7 @@ def glob_recursive(pattern, node="."):
def add_to_vs_project(env, sources):
for x in sources:
- if type(x) == type(""):
- fname = env.File(x).path
- else:
- fname = env.File(x)[0].path
+ fname = env.File(x).path if isinstance(x, str) else env.File(x)[0].path
pieces = fname.split(".")
if len(pieces) > 0:
basename = pieces[0]
@@ -1011,7 +879,8 @@ def show_progress(env):
return
import sys
- from SCons.Script import Progress, Command, AlwaysBuild
+
+ from SCons.Script import AlwaysBuild, Command, Progress
screen = sys.stdout
# Progress reporting is not available in non-TTY environments since it
@@ -1022,7 +891,8 @@ def show_progress(env):
node_count_interval = 1
node_count_fname = str(env.Dir("#")) + "/.scons_node_count"
- import time, math
+ import math
+ import time
class cache_progress:
# The default is 1 GB cache and 12 hours half life
@@ -1030,7 +900,7 @@ def show_progress(env):
self.path = path
self.limit = limit
self.exponent_scale = math.log(2) / half_life
- if env["verbose"] and path != None:
+ if env["verbose"] and path is not None:
screen.write(
"Current cache limit is {} (used: {})\n".format(
self.convert_size(limit), self.convert_size(self.get_size(path))
@@ -1119,7 +989,7 @@ def show_progress(env):
pass
try:
- with open(node_count_fname) as f:
+ with open(node_count_fname, "r", encoding="utf-8") as f:
node_count_max = int(f.readline())
except Exception:
pass
@@ -1176,11 +1046,11 @@ def generate_vs_project(env, original_args, project_name="godot"):
if type(f) is Node.FS.Dir:
results += glob_recursive_2(pattern, dirs, f)
r = Glob(str(node) + "/" + pattern, source=True)
- if len(r) > 0 and not str(node) in dirs:
+ if len(r) > 0 and str(node) not in dirs:
d = ""
for part in str(node).split("\\"):
d += part
- if not d in dirs:
+ if d not in dirs:
dirs.append(d)
d += "\\"
results += r
@@ -1193,7 +1063,7 @@ def generate_vs_project(env, original_args, project_name="godot"):
if val is not None:
try:
return _text2bool(val)
- except:
+ except (ValueError, AttributeError):
return default
else:
return default
@@ -1366,13 +1236,13 @@ def generate_vs_project(env, original_args, project_name="godot"):
others_active = []
for x in envsources:
fname = ""
- if type(x) == type(""):
+ if isinstance(x, str):
fname = env.File(x).path
else:
# Some object files might get added directly as a File object and not a list.
try:
fname = env.File(x)[0].path
- except:
+ except Exception:
fname = x.path
pass
@@ -1451,7 +1321,7 @@ def generate_vs_project(env, original_args, project_name="godot"):
itemlist = {}
for item in activeItems:
key = os.path.dirname(item).replace("\\", "_")
- if not key in itemlist:
+ if key not in itemlist:
itemlist[key] = [item]
else:
itemlist[key] += [item]
@@ -1592,14 +1462,14 @@ def generate_vs_project(env, original_args, project_name="godot"):
if godot_platform != "windows":
configurations += [
f'<ProjectConfiguration Include="editor|{proj_plat}">',
- f" <Configuration>editor</Configuration>",
+ " <Configuration>editor</Configuration>",
f" <Platform>{proj_plat}</Platform>",
"</ProjectConfiguration>",
]
properties += [
f"<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='editor|{proj_plat}'\">",
- f" <GodotConfiguration>editor</GodotConfiguration>",
+ " <GodotConfiguration>editor</GodotConfiguration>",
f" <GodotPlatform>{proj_plat}</GodotPlatform>",
"</PropertyGroup>",
]
@@ -1649,3 +1519,112 @@ def generate_vs_project(env, original_args, project_name="godot"):
if get_bool(original_args, "vsproj_gen_only", True):
sys.exit()
+
+
+def generate_copyright_header(filename: str) -> str:
+ MARGIN = 70
+ TEMPLATE = """\
+/**************************************************************************/
+/* %s*/
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+"""
+ filename = filename.split("/")[-1].ljust(MARGIN)
+ if len(filename) > MARGIN:
+ print(f'WARNING: Filename "{filename}" too large for copyright header.')
+ return TEMPLATE % filename
+
+
+@contextlib.contextmanager
+def generated_wrapper(
+ path, # FIXME: type with `Union[str, Node, List[Node]]` when pytest conflicts are resolved
+ guard: Optional[bool] = None,
+ prefix: str = "",
+ suffix: str = "",
+) -> Generator[TextIOWrapper, None, None]:
+ """
+ Wrapper class to automatically handle copyright headers and header guards
+ for generated scripts. Meant to be invoked via `with` statement similar to
+ creating a file.
+
+ - `path`: The path of the file to be created. Can be passed a raw string, an
+ isolated SCons target, or a full SCons target list. If a target list contains
+ multiple entries, produces a warning & only creates the first entry.
+ - `guard`: Optional bool to determine if a header guard should be added. If
+ unassigned, header guards are determined by the file extension.
+ - `prefix`: Custom prefix to prepend to a header guard. Produces a warning if
+ provided a value when `guard` evaluates to `False`.
+ - `suffix`: Custom suffix to append to a header guard. Produces a warning if
+ provided a value when `guard` evaluates to `False`.
+ """
+
+ # Handle unfiltered SCons target[s] passed as path.
+ if not isinstance(path, str):
+ if isinstance(path, list):
+ if len(path) > 1:
+ print_warning(
+ "Attempting to use generated wrapper with multiple targets; "
+ f"will only use first entry: {path[0]}"
+ )
+ path = path[0]
+ if not hasattr(path, "get_abspath"):
+ raise TypeError(f'Expected type "str", "Node" or "List[Node]"; was passed {type(path)}.')
+ path = path.get_abspath()
+
+ path = str(path).replace("\\", "/")
+ if guard is None:
+ guard = path.endswith((".h", ".hh", ".hpp", ".inc"))
+ if not guard and (prefix or suffix):
+ print_warning(f'Trying to assign header guard prefix/suffix while `guard` is disabled: "{path}".')
+
+ header_guard = ""
+ if guard:
+ if prefix:
+ prefix += "_"
+ if suffix:
+ suffix = f"_{suffix}"
+ split = path.split("/")[-1].split(".")
+ header_guard = (f"{prefix}{split[0]}{suffix}.{'.'.join(split[1:])}".upper()
+ .replace(".", "_").replace("-", "_").replace(" ", "_").replace("__", "_")) # fmt: skip
+
+ with open(path, "wt", encoding="utf-8", newline="\n") as file:
+ file.write(generate_copyright_header(path))
+ file.write("\n/* THIS FILE IS GENERATED. EDITS WILL BE LOST. */\n\n")
+
+ if guard:
+ file.write(f"#ifndef {header_guard}\n")
+ file.write(f"#define {header_guard}\n\n")
+
+ with StringIO(newline="\n") as str_io:
+ yield str_io
+ file.write(str_io.getvalue().strip() or "/* NO CONTENT */")
+
+ if guard:
+ file.write(f"\n\n#endif // {header_guard}")
+
+ file.write("\n")