mirror of
https://github.com/godotengine/godot.git
synced 2025-04-01 00:41:35 +08:00
SCons: Make builders prettier, utilize constexpr
This commit is contained in:
parent
7893202fba
commit
be429eb404
@ -968,8 +968,6 @@ if env.editor_build:
|
||||
print_error("Not all modules required by editor builds are enabled.")
|
||||
Exit(255)
|
||||
|
||||
env.version_info = methods.get_version_info(env.module_version_string)
|
||||
|
||||
env["PROGSUFFIX_WRAP"] = suffix + env.module_version_string + ".console" + env["PROGSUFFIX"]
|
||||
env["PROGSUFFIX"] = suffix + env.module_version_string + env["PROGSUFFIX"]
|
||||
env["OBJSUFFIX"] = suffix + env["OBJSUFFIX"]
|
||||
|
42
core/SCsub
42
core/SCsub
@ -167,10 +167,9 @@ env.add_source_files(env.core_sources, "*.cpp")
|
||||
|
||||
# Generate disabled classes
|
||||
def disabled_class_builder(target, source, env):
|
||||
with methods.generated_wrapper(target) as file:
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
for c in source[0].read():
|
||||
cs = c.strip()
|
||||
if cs != "":
|
||||
if cs := c.strip():
|
||||
file.write(f"#define ClassDB_Disable_{cs} 1\n")
|
||||
|
||||
|
||||
@ -179,7 +178,7 @@ env.CommandNoCache("disabled_classes.gen.h", env.Value(env.disabled_classes), en
|
||||
|
||||
# Generate version info
|
||||
def version_info_builder(target, source, env):
|
||||
with methods.generated_wrapper(target) as file:
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
file.write(
|
||||
"""\
|
||||
#define VERSION_SHORT_NAME "{short_name}"
|
||||
@ -193,35 +192,37 @@ def version_info_builder(target, source, env):
|
||||
#define VERSION_WEBSITE "{website}"
|
||||
#define VERSION_DOCS_BRANCH "{docs_branch}"
|
||||
#define VERSION_DOCS_URL "https://docs.godotengine.org/en/" VERSION_DOCS_BRANCH
|
||||
""".format(**env.version_info)
|
||||
""".format(**source[0].read())
|
||||
)
|
||||
|
||||
|
||||
env.CommandNoCache("version_generated.gen.h", env.Value(env.version_info), env.Run(version_info_builder))
|
||||
env.CommandNoCache(
|
||||
"version_generated.gen.h",
|
||||
env.Value(methods.get_version_info(env.module_version_string)),
|
||||
env.Run(version_info_builder),
|
||||
)
|
||||
|
||||
|
||||
# Generate version hash
|
||||
def version_hash_builder(target, source, env):
|
||||
with methods.generated_wrapper(target) as file:
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
file.write(
|
||||
"""\
|
||||
#include "core/version.h"
|
||||
|
||||
const char *const VERSION_HASH = "{git_hash}";
|
||||
const uint64_t VERSION_TIMESTAMP = {git_timestamp};
|
||||
""".format(**env.version_info)
|
||||
""".format(**source[0].read())
|
||||
)
|
||||
|
||||
|
||||
gen_hash = env.CommandNoCache(
|
||||
"version_hash.gen.cpp", env.Value(env.version_info["git_hash"]), env.Run(version_hash_builder)
|
||||
)
|
||||
gen_hash = env.CommandNoCache("version_hash.gen.cpp", env.Value(methods.get_git_info()), env.Run(version_hash_builder))
|
||||
env.add_source_files(env.core_sources, gen_hash)
|
||||
|
||||
|
||||
# Generate AES256 script encryption key
|
||||
def encryption_key_builder(target, source, env):
|
||||
with methods.generated_wrapper(target) as file:
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
file.write(
|
||||
f"""\
|
||||
#include "core/config/project_settings.h"
|
||||
@ -251,30 +252,21 @@ env.add_source_files(env.core_sources, gen_encrypt)
|
||||
|
||||
|
||||
# Certificates
|
||||
env.Depends(
|
||||
"#core/io/certs_compressed.gen.h",
|
||||
["#thirdparty/certs/ca-certificates.crt", env.Value(env["builtin_certs"]), env.Value(env["system_certs_path"])],
|
||||
)
|
||||
env.CommandNoCache(
|
||||
"#core/io/certs_compressed.gen.h",
|
||||
"#thirdparty/certs/ca-certificates.crt",
|
||||
["#thirdparty/certs/ca-certificates.crt", env.Value(env["builtin_certs"]), env.Value(env["system_certs_path"])],
|
||||
env.Run(core_builders.make_certs_header),
|
||||
)
|
||||
|
||||
# Authors
|
||||
env.Depends("#core/authors.gen.h", "../AUTHORS.md")
|
||||
env.CommandNoCache("#core/authors.gen.h", "../AUTHORS.md", env.Run(core_builders.make_authors_header))
|
||||
env.CommandNoCache("#core/authors.gen.h", "#AUTHORS.md", env.Run(core_builders.make_authors_header))
|
||||
|
||||
# Donors
|
||||
env.Depends("#core/donors.gen.h", "../DONORS.md")
|
||||
env.CommandNoCache("#core/donors.gen.h", "../DONORS.md", env.Run(core_builders.make_donors_header))
|
||||
env.CommandNoCache("#core/donors.gen.h", "#DONORS.md", env.Run(core_builders.make_donors_header))
|
||||
|
||||
# License
|
||||
env.Depends("#core/license.gen.h", ["../COPYRIGHT.txt", "../LICENSE.txt"])
|
||||
env.CommandNoCache(
|
||||
"#core/license.gen.h",
|
||||
["../COPYRIGHT.txt", "../LICENSE.txt"],
|
||||
env.Run(core_builders.make_license_header),
|
||||
"#core/license.gen.h", ["#COPYRIGHT.txt", "#LICENSE.txt"], env.Run(core_builders.make_license_header)
|
||||
)
|
||||
|
||||
# Chain load SCsubs
|
||||
|
@ -1,171 +1,104 @@
|
||||
"""Functions used to generate source files during build time"""
|
||||
|
||||
import zlib
|
||||
from collections import OrderedDict
|
||||
from io import TextIOWrapper
|
||||
|
||||
|
||||
def escape_string(s):
|
||||
def charcode_to_c_escapes(c):
|
||||
rev_result = []
|
||||
while c >= 256:
|
||||
c, low = (c // 256, c % 256)
|
||||
rev_result.append("\\%03o" % low)
|
||||
rev_result.append("\\%03o" % c)
|
||||
return "".join(reversed(rev_result))
|
||||
|
||||
result = ""
|
||||
if isinstance(s, str):
|
||||
s = s.encode("utf-8")
|
||||
for c in s:
|
||||
if not (32 <= c < 127) or c in (ord("\\"), ord('"')):
|
||||
result += charcode_to_c_escapes(c)
|
||||
else:
|
||||
result += chr(c)
|
||||
return result
|
||||
import methods
|
||||
|
||||
|
||||
def make_certs_header(target, source, env):
|
||||
src = str(source[0])
|
||||
dst = str(target[0])
|
||||
with open(src, "rb") as f, open(dst, "w", encoding="utf-8", newline="\n") as g:
|
||||
buf = f.read()
|
||||
decomp_size = len(buf)
|
||||
|
||||
# Use maximum zlib compression level to further reduce file size
|
||||
# (at the cost of initial build times).
|
||||
buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION)
|
||||
|
||||
g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
|
||||
g.write("#ifndef CERTS_COMPRESSED_GEN_H\n")
|
||||
g.write("#define CERTS_COMPRESSED_GEN_H\n")
|
||||
buffer = methods.get_buffer(str(source[0]))
|
||||
decomp_size = len(buffer)
|
||||
buffer = methods.compress_buffer(buffer)
|
||||
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
# System certs path. Editor will use them if defined. (for package maintainers)
|
||||
path = env["system_certs_path"]
|
||||
g.write('#define _SYSTEM_CERTS_PATH "%s"\n' % str(path))
|
||||
file.write('#define _SYSTEM_CERTS_PATH "{}"\n'.format(env["system_certs_path"]))
|
||||
if env["builtin_certs"]:
|
||||
# Defined here and not in env so changing it does not trigger a full rebuild.
|
||||
g.write("#define BUILTIN_CERTS_ENABLED\n")
|
||||
g.write("static const int _certs_compressed_size = " + str(len(buf)) + ";\n")
|
||||
g.write("static const int _certs_uncompressed_size = " + str(decomp_size) + ";\n")
|
||||
g.write("static const unsigned char _certs_compressed[] = {\n")
|
||||
for i in range(len(buf)):
|
||||
g.write("\t" + str(buf[i]) + ",\n")
|
||||
g.write("};\n")
|
||||
g.write("#endif // CERTS_COMPRESSED_GEN_H")
|
||||
file.write(f"""\
|
||||
#define BUILTIN_CERTS_ENABLED
|
||||
|
||||
inline constexpr int _certs_compressed_size = {len(buffer)};
|
||||
inline constexpr int _certs_uncompressed_size = {decomp_size};
|
||||
inline constexpr unsigned char _certs_compressed[] = {{
|
||||
{methods.format_buffer(buffer, 1)}
|
||||
}};
|
||||
""")
|
||||
|
||||
|
||||
def make_authors_header(target, source, env):
|
||||
sections = [
|
||||
"Project Founders",
|
||||
"Lead Developer",
|
||||
"Project Manager",
|
||||
"Developers",
|
||||
]
|
||||
sections_id = [
|
||||
"AUTHORS_FOUNDERS",
|
||||
"AUTHORS_LEAD_DEVELOPERS",
|
||||
"AUTHORS_PROJECT_MANAGERS",
|
||||
"AUTHORS_DEVELOPERS",
|
||||
]
|
||||
SECTIONS = {
|
||||
"Project Founders": "AUTHORS_FOUNDERS",
|
||||
"Lead Developer": "AUTHORS_LEAD_DEVELOPERS",
|
||||
"Project Manager": "AUTHORS_PROJECT_MANAGERS",
|
||||
"Developers": "AUTHORS_DEVELOPERS",
|
||||
}
|
||||
buffer = methods.get_buffer(str(source[0]))
|
||||
reading = False
|
||||
|
||||
src = str(source[0])
|
||||
dst = str(target[0])
|
||||
with open(src, "r", encoding="utf-8") as f, open(dst, "w", encoding="utf-8", newline="\n") as g:
|
||||
g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
|
||||
g.write("#ifndef AUTHORS_GEN_H\n")
|
||||
g.write("#define AUTHORS_GEN_H\n")
|
||||
|
||||
reading = False
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
|
||||
def close_section():
|
||||
g.write("\t0\n")
|
||||
g.write("};\n")
|
||||
file.write("\tnullptr,\n};\n\n")
|
||||
|
||||
for line in f:
|
||||
if reading:
|
||||
if line.startswith(" "):
|
||||
g.write('\t"' + escape_string(line.strip()) + '",\n')
|
||||
continue
|
||||
if line.startswith("## "):
|
||||
for line in buffer.decode().splitlines():
|
||||
if line.startswith(" ") and reading:
|
||||
file.write(f'\t"{methods.to_escaped_cstring(line).strip()}",\n')
|
||||
elif line.startswith("## "):
|
||||
if reading:
|
||||
close_section()
|
||||
reading = False
|
||||
for section, section_id in zip(sections, sections_id):
|
||||
if line.strip().endswith(section):
|
||||
current_section = escape_string(section_id)
|
||||
reading = True
|
||||
g.write("const char *const " + current_section + "[] = {\n")
|
||||
break
|
||||
section = SECTIONS[line[3:].strip()]
|
||||
if section:
|
||||
file.write(f"inline constexpr const char *{section}[] = {{\n")
|
||||
reading = True
|
||||
|
||||
if reading:
|
||||
close_section()
|
||||
|
||||
g.write("#endif // AUTHORS_GEN_H\n")
|
||||
|
||||
|
||||
def make_donors_header(target, source, env):
|
||||
sections = [
|
||||
"Patrons",
|
||||
"Platinum sponsors",
|
||||
"Gold sponsors",
|
||||
"Silver sponsors",
|
||||
"Diamond members",
|
||||
"Titanium members",
|
||||
"Platinum members",
|
||||
"Gold members",
|
||||
]
|
||||
sections_id = [
|
||||
"DONORS_PATRONS",
|
||||
"DONORS_SPONSORS_PLATINUM",
|
||||
"DONORS_SPONSORS_GOLD",
|
||||
"DONORS_SPONSORS_SILVER",
|
||||
"DONORS_MEMBERS_DIAMOND",
|
||||
"DONORS_MEMBERS_TITANIUM",
|
||||
"DONORS_MEMBERS_PLATINUM",
|
||||
"DONORS_MEMBERS_GOLD",
|
||||
]
|
||||
SECTIONS = {
|
||||
"Patrons": "DONORS_PATRONS",
|
||||
"Platinum sponsors": "DONORS_SPONSORS_PLATINUM",
|
||||
"Gold sponsors": "DONORS_SPONSORS_GOLD",
|
||||
"Silver sponsors": "DONORS_SPONSORS_SILVER",
|
||||
"Diamond members": "DONORS_MEMBERS_DIAMOND",
|
||||
"Titanium members": "DONORS_MEMBERS_TITANIUM",
|
||||
"Platinum members": "DONORS_MEMBERS_PLATINUM",
|
||||
"Gold members": "DONORS_MEMBERS_GOLD",
|
||||
}
|
||||
buffer = methods.get_buffer(str(source[0]))
|
||||
reading = False
|
||||
|
||||
src = str(source[0])
|
||||
dst = str(target[0])
|
||||
with open(src, "r", encoding="utf-8") as f, open(dst, "w", encoding="utf-8", newline="\n") as g:
|
||||
g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
|
||||
g.write("#ifndef DONORS_GEN_H\n")
|
||||
g.write("#define DONORS_GEN_H\n")
|
||||
|
||||
reading = False
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
|
||||
def close_section():
|
||||
g.write("\t0\n")
|
||||
g.write("};\n")
|
||||
file.write("\tnullptr,\n};\n\n")
|
||||
|
||||
for line in f:
|
||||
if reading >= 0:
|
||||
if line.startswith(" "):
|
||||
g.write('\t"' + escape_string(line.strip()) + '",\n')
|
||||
continue
|
||||
if line.startswith("## "):
|
||||
for line in buffer.decode().splitlines():
|
||||
if line.startswith(" ") and reading:
|
||||
file.write(f'\t"{methods.to_escaped_cstring(line).strip()}",\n')
|
||||
elif line.startswith("## "):
|
||||
if reading:
|
||||
close_section()
|
||||
reading = False
|
||||
for section, section_id in zip(sections, sections_id):
|
||||
if line.strip().endswith(section):
|
||||
current_section = escape_string(section_id)
|
||||
reading = True
|
||||
g.write("const char *const " + current_section + "[] = {\n")
|
||||
break
|
||||
section = SECTIONS.get(line[3:].strip())
|
||||
if section:
|
||||
file.write(f"inline constexpr const char *{section}[] = {{\n")
|
||||
reading = True
|
||||
|
||||
if reading:
|
||||
close_section()
|
||||
|
||||
g.write("#endif // DONORS_GEN_H\n")
|
||||
|
||||
|
||||
def make_license_header(target, source, env):
|
||||
src_copyright = str(source[0])
|
||||
src_license = str(source[1])
|
||||
dst = str(target[0])
|
||||
|
||||
class LicenseReader:
|
||||
def __init__(self, license_file):
|
||||
def __init__(self, license_file: TextIOWrapper):
|
||||
self._license_file = license_file
|
||||
self.line_num = 0
|
||||
self.current = self.next_line()
|
||||
@ -188,9 +121,7 @@ def make_license_header(target, source, env):
|
||||
lines.append(self.current.strip())
|
||||
return (tag, lines)
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
projects: dict = OrderedDict()
|
||||
projects = OrderedDict()
|
||||
license_list = []
|
||||
|
||||
with open(src_copyright, "r", encoding="utf-8") as copyright_file:
|
||||
@ -212,7 +143,7 @@ def make_license_header(target, source, env):
|
||||
part = {}
|
||||
reader.next_line()
|
||||
|
||||
data_list: list = []
|
||||
data_list = []
|
||||
for project in iter(projects.values()):
|
||||
for part in project:
|
||||
part["file_index"] = len(data_list)
|
||||
@ -220,96 +151,76 @@ def make_license_header(target, source, env):
|
||||
part["copyright_index"] = len(data_list)
|
||||
data_list += part["Copyright"]
|
||||
|
||||
with open(dst, "w", encoding="utf-8", newline="\n") as f:
|
||||
f.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
|
||||
f.write("#ifndef LICENSE_GEN_H\n")
|
||||
f.write("#define LICENSE_GEN_H\n")
|
||||
f.write("const char *const GODOT_LICENSE_TEXT =")
|
||||
with open(src_license, "r", encoding="utf-8") as file:
|
||||
license_text = file.read()
|
||||
|
||||
with open(src_license, "r", encoding="utf-8") as license_file:
|
||||
for line in license_file:
|
||||
escaped_string = escape_string(line.strip())
|
||||
f.write('\n\t\t"' + escaped_string + '\\n"')
|
||||
f.write(";\n\n")
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
file.write(f"""\
|
||||
inline constexpr const char *GODOT_LICENSE_TEXT = {{
|
||||
{methods.to_raw_cstring(license_text)}
|
||||
}};
|
||||
|
||||
f.write(
|
||||
"struct ComponentCopyrightPart {\n"
|
||||
"\tconst char *license;\n"
|
||||
"\tconst char *const *files;\n"
|
||||
"\tconst char *const *copyright_statements;\n"
|
||||
"\tint file_count;\n"
|
||||
"\tint copyright_count;\n"
|
||||
"};\n\n"
|
||||
)
|
||||
struct ComponentCopyrightPart {{
|
||||
const char *license;
|
||||
const char *const *files;
|
||||
const char *const *copyright_statements;
|
||||
int file_count;
|
||||
int copyright_count;
|
||||
}};
|
||||
|
||||
f.write(
|
||||
"struct ComponentCopyright {\n"
|
||||
"\tconst char *name;\n"
|
||||
"\tconst ComponentCopyrightPart *parts;\n"
|
||||
"\tint part_count;\n"
|
||||
"};\n\n"
|
||||
)
|
||||
struct ComponentCopyright {{
|
||||
const char *name;
|
||||
const ComponentCopyrightPart *parts;
|
||||
int part_count;
|
||||
}};
|
||||
|
||||
f.write("const char *const COPYRIGHT_INFO_DATA[] = {\n")
|
||||
""")
|
||||
|
||||
file.write("inline constexpr const char *COPYRIGHT_INFO_DATA[] = {\n")
|
||||
for line in data_list:
|
||||
f.write('\t"' + escape_string(line) + '",\n')
|
||||
f.write("};\n\n")
|
||||
file.write(f'\t"{methods.to_escaped_cstring(line)}",\n')
|
||||
file.write("};\n\n")
|
||||
|
||||
f.write("const ComponentCopyrightPart COPYRIGHT_PROJECT_PARTS[] = {\n")
|
||||
file.write("inline constexpr ComponentCopyrightPart COPYRIGHT_PROJECT_PARTS[] = {\n")
|
||||
part_index = 0
|
||||
part_indexes = {}
|
||||
for project_name, project in iter(projects.items()):
|
||||
part_indexes[project_name] = part_index
|
||||
for part in project:
|
||||
f.write(
|
||||
'\t{ "'
|
||||
+ escape_string(part["License"][0])
|
||||
+ '", '
|
||||
+ "©RIGHT_INFO_DATA["
|
||||
+ str(part["file_index"])
|
||||
+ "], "
|
||||
+ "©RIGHT_INFO_DATA["
|
||||
+ str(part["copyright_index"])
|
||||
+ "], "
|
||||
+ str(len(part["Files"]))
|
||||
+ ", "
|
||||
+ str(len(part["Copyright"]))
|
||||
+ " },\n"
|
||||
file.write(
|
||||
f'\t{{ "{methods.to_escaped_cstring(part["License"][0])}", '
|
||||
+ f"©RIGHT_INFO_DATA[{part['file_index']}], "
|
||||
+ f"©RIGHT_INFO_DATA[{part['copyright_index']}], "
|
||||
+ f"{len(part['Files'])}, {len(part['Copyright'])} }},\n"
|
||||
)
|
||||
part_index += 1
|
||||
f.write("};\n\n")
|
||||
file.write("};\n\n")
|
||||
|
||||
f.write("const int COPYRIGHT_INFO_COUNT = " + str(len(projects)) + ";\n")
|
||||
file.write(f"inline constexpr int COPYRIGHT_INFO_COUNT = {len(projects)};\n")
|
||||
|
||||
f.write("const ComponentCopyright COPYRIGHT_INFO[] = {\n")
|
||||
file.write("inline constexpr ComponentCopyright COPYRIGHT_INFO[] = {\n")
|
||||
for project_name, project in iter(projects.items()):
|
||||
f.write(
|
||||
'\t{ "'
|
||||
+ escape_string(project_name)
|
||||
+ '", '
|
||||
+ "©RIGHT_PROJECT_PARTS["
|
||||
+ str(part_indexes[project_name])
|
||||
+ "], "
|
||||
+ str(len(project))
|
||||
+ " },\n"
|
||||
file.write(
|
||||
f'\t{{ "{methods.to_escaped_cstring(project_name)}", '
|
||||
+ f"©RIGHT_PROJECT_PARTS[{part_indexes[project_name]}], "
|
||||
+ f"{len(project)} }},\n"
|
||||
)
|
||||
f.write("};\n\n")
|
||||
file.write("};\n\n")
|
||||
|
||||
f.write("const int LICENSE_COUNT = " + str(len(license_list)) + ";\n")
|
||||
file.write(f"inline constexpr int LICENSE_COUNT = {len(license_list)};\n")
|
||||
|
||||
f.write("const char *const LICENSE_NAMES[] = {\n")
|
||||
file.write("inline constexpr const char *LICENSE_NAMES[] = {\n")
|
||||
for license in license_list:
|
||||
f.write('\t"' + escape_string(license[0]) + '",\n')
|
||||
f.write("};\n\n")
|
||||
file.write(f'\t"{methods.to_escaped_cstring(license[0])}",\n')
|
||||
file.write("};\n\n")
|
||||
|
||||
f.write("const char *const LICENSE_BODIES[] = {\n\n")
|
||||
file.write("inline constexpr const char *LICENSE_BODIES[] = {\n\n")
|
||||
for license in license_list:
|
||||
to_raw = []
|
||||
for line in license[1:]:
|
||||
if line == ".":
|
||||
f.write('\t"\\n"\n')
|
||||
to_raw += [""]
|
||||
else:
|
||||
f.write('\t"' + escape_string(line) + '\\n"\n')
|
||||
f.write('\t"",\n\n')
|
||||
f.write("};\n\n")
|
||||
|
||||
f.write("#endif // LICENSE_GEN_H\n")
|
||||
to_raw += [line]
|
||||
file.write(f"{methods.to_raw_cstring(to_raw)},\n\n")
|
||||
file.write("};\n\n")
|
||||
|
@ -1,52 +1,37 @@
|
||||
import zlib
|
||||
import methods
|
||||
|
||||
|
||||
def run(target, source, env):
|
||||
src = str(source[0])
|
||||
dst = str(target[0])
|
||||
with open(src, "rb") as f, open(dst, "w", encoding="utf-8", newline="\n") as g:
|
||||
buf = f.read()
|
||||
decomp_size = len(buf)
|
||||
|
||||
# Use maximum zlib compression level to further reduce file size
|
||||
# (at the cost of initial build times).
|
||||
buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION)
|
||||
|
||||
g.write(
|
||||
"""/* THIS FILE IS GENERATED DO NOT EDIT */
|
||||
#pragma once
|
||||
buffer = methods.get_buffer(str(source[0]))
|
||||
decomp_size = len(buffer)
|
||||
buffer = methods.compress_buffer(buffer)
|
||||
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
file.write(f"""\
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
#include "core/io/compression.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
"""
|
||||
)
|
||||
inline constexpr int _gdextension_interface_data_compressed_size = {len(buffer)};
|
||||
inline constexpr int _gdextension_interface_data_uncompressed_size = {decomp_size};
|
||||
inline constexpr unsigned char _gdextension_interface_data_compressed[] = {{
|
||||
{methods.format_buffer(buffer, 1)}
|
||||
}};
|
||||
|
||||
g.write("static const int _gdextension_interface_data_compressed_size = " + str(len(buf)) + ";\n")
|
||||
g.write("static const int _gdextension_interface_data_uncompressed_size = " + str(decomp_size) + ";\n")
|
||||
g.write("static const unsigned char _gdextension_interface_data_compressed[] = {\n")
|
||||
for i in range(len(buf)):
|
||||
g.write("\t" + str(buf[i]) + ",\n")
|
||||
g.write("};\n")
|
||||
|
||||
g.write(
|
||||
"""
|
||||
class GDExtensionInterfaceDump {
|
||||
public:
|
||||
static void generate_gdextension_interface_file(const String &p_path) {
|
||||
Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND_MSG(fa.is_null(), vformat("Cannot open file '%s' for writing.", p_path));
|
||||
Vector<uint8_t> data;
|
||||
data.resize(_gdextension_interface_data_uncompressed_size);
|
||||
int ret = Compression::decompress(data.ptrw(), _gdextension_interface_data_uncompressed_size, _gdextension_interface_data_compressed, _gdextension_interface_data_compressed_size, Compression::MODE_DEFLATE);
|
||||
ERR_FAIL_COND_MSG(ret == -1, "Compressed file is corrupt.");
|
||||
fa->store_buffer(data.ptr(), data.size());
|
||||
};
|
||||
};
|
||||
class GDExtensionInterfaceDump {{
|
||||
public:
|
||||
static void generate_gdextension_interface_file(const String &p_path) {{
|
||||
Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND_MSG(fa.is_null(), vformat("Cannot open file '%s' for writing.", p_path));
|
||||
Vector<uint8_t> data;
|
||||
data.resize(_gdextension_interface_data_uncompressed_size);
|
||||
int ret = Compression::decompress(data.ptrw(), _gdextension_interface_data_uncompressed_size, _gdextension_interface_data_compressed, _gdextension_interface_data_compressed_size, Compression::MODE_DEFLATE);
|
||||
ERR_FAIL_COND_MSG(ret == -1, "Compressed file is corrupt.");
|
||||
fa->store_buffer(data.ptr(), data.size());
|
||||
}};
|
||||
}};
|
||||
|
||||
#endif // TOOLS_ENABLED
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
@ -2,18 +2,22 @@
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
import methods
|
||||
|
||||
|
||||
def make_default_controller_mappings(target, source, env):
|
||||
dst = str(target[0])
|
||||
with open(dst, "w", encoding="utf-8", newline="\n") as g:
|
||||
g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
|
||||
g.write('#include "core/typedefs.h"\n')
|
||||
g.write('#include "core/input/default_controller_mappings.h"\n')
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
file.write("""\
|
||||
#include "core/input/default_controller_mappings.h"
|
||||
|
||||
#include "core/typedefs.h"
|
||||
|
||||
""")
|
||||
|
||||
# ensure mappings have a consistent order
|
||||
platform_mappings: dict = OrderedDict()
|
||||
for src_path in source:
|
||||
with open(str(src_path), "r", encoding="utf-8") as f:
|
||||
platform_mappings = OrderedDict()
|
||||
for src_path in map(str, source):
|
||||
with open(src_path, "r", encoding="utf-8") as f:
|
||||
# read mapping file and skip header
|
||||
mapping_file_lines = f.readlines()[2:]
|
||||
|
||||
@ -32,28 +36,28 @@ def make_default_controller_mappings(target, source, env):
|
||||
line_parts = line.split(",")
|
||||
guid = line_parts[0]
|
||||
if guid in platform_mappings[current_platform]:
|
||||
g.write(
|
||||
file.write(
|
||||
"// WARNING: DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format(
|
||||
src_path, current_platform, platform_mappings[current_platform][guid]
|
||||
)
|
||||
)
|
||||
platform_mappings[current_platform][guid] = line
|
||||
|
||||
platform_variables = {
|
||||
"Linux": "#ifdef LINUXBSD_ENABLED",
|
||||
"Windows": "#ifdef WINDOWS_ENABLED",
|
||||
"Mac OS X": "#ifdef MACOS_ENABLED",
|
||||
"Android": "#ifdef ANDROID_ENABLED",
|
||||
"iOS": "#ifdef IOS_ENABLED",
|
||||
"Web": "#ifdef WEB_ENABLED",
|
||||
PLATFORM_VARIABLES = {
|
||||
"Linux": "LINUXBSD",
|
||||
"Windows": "WINDOWS",
|
||||
"Mac OS X": "MACOS",
|
||||
"Android": "ANDROID",
|
||||
"iOS": "IOS",
|
||||
"Web": "WEB",
|
||||
}
|
||||
|
||||
g.write("const char* DefaultControllerMappings::mappings[] = {\n")
|
||||
file.write("const char *DefaultControllerMappings::mappings[] = {\n")
|
||||
for platform, mappings in platform_mappings.items():
|
||||
variable = platform_variables[platform]
|
||||
g.write("{}\n".format(variable))
|
||||
variable = PLATFORM_VARIABLES[platform]
|
||||
file.write(f"#ifdef {variable}_ENABLED\n")
|
||||
for mapping in mappings.values():
|
||||
g.write('\t"{}",\n'.format(mapping))
|
||||
g.write("#endif\n")
|
||||
file.write(f'\t"{mapping}",\n')
|
||||
file.write(f"#endif // {variable}_ENABLED\n")
|
||||
|
||||
g.write("\tnullptr\n};\n")
|
||||
file.write("\tnullptr\n};\n")
|
||||
|
36
editor/SCsub
36
editor/SCsub
@ -5,7 +5,6 @@ Import("env")
|
||||
|
||||
env.editor_sources = []
|
||||
|
||||
import glob
|
||||
import os
|
||||
|
||||
import editor_builders
|
||||
@ -17,17 +16,16 @@ if env.editor_build:
|
||||
def doc_data_class_path_builder(target, source, env):
|
||||
paths = dict(sorted(source[0].read().items()))
|
||||
data = "\n".join([f'\t{{"{key}", "{value}"}},' for key, value in paths.items()])
|
||||
with methods.generated_wrapper(target) as file:
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
file.write(
|
||||
f"""\
|
||||
static const int _doc_data_class_path_count = {len(paths)};
|
||||
|
||||
struct _DocDataClassPath {{
|
||||
const char *name;
|
||||
const char *path;
|
||||
}};
|
||||
|
||||
static const _DocDataClassPath _doc_data_class_paths[{len(env.doc_class_path) + 1}] = {{
|
||||
inline constexpr int _doc_data_class_path_count = {len(paths)};
|
||||
inline constexpr _DocDataClassPath _doc_data_class_paths[{len(env.doc_class_path) + 1}] = {{
|
||||
{data}
|
||||
{{nullptr, nullptr}},
|
||||
}};
|
||||
@ -42,7 +40,7 @@ static const _DocDataClassPath _doc_data_class_paths[{len(env.doc_class_path) +
|
||||
exp_inc = "\n".join([f'#include "platform/{p}/export/export.h"' for p in platforms])
|
||||
exp_reg = "\n".join([f"\tregister_{p}_exporter();" for p in platforms])
|
||||
exp_type = "\n".join([f"\tregister_{p}_exporter_types();" for p in platforms])
|
||||
with methods.generated_wrapper(target) as file:
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
file.write(
|
||||
f"""\
|
||||
#include "register_exporters.h"
|
||||
@ -83,7 +81,6 @@ void register_exporter_types() {{
|
||||
docs += Glob(d + "/*.xml") # Custom.
|
||||
|
||||
docs = sorted(docs)
|
||||
env.Depends("#editor/doc_data_compressed.gen.h", docs)
|
||||
env.CommandNoCache(
|
||||
"#editor/doc_data_compressed.gen.h",
|
||||
docs,
|
||||
@ -97,40 +94,31 @@ void register_exporter_types() {{
|
||||
# Generated with `make include-list` for each resource.
|
||||
|
||||
# Editor translations
|
||||
tlist = glob.glob(env.Dir("#editor/translations/editor").abspath + "/*.po")
|
||||
env.Depends("#editor/editor_translations.gen.h", tlist)
|
||||
env.CommandNoCache(
|
||||
"#editor/editor_translations.gen.h",
|
||||
tlist,
|
||||
env.Run(editor_builders.make_editor_translations_header),
|
||||
Glob("#editor/translations/editor/*"),
|
||||
env.Run(editor_builders.make_translations_header),
|
||||
)
|
||||
|
||||
# Property translations
|
||||
tlist = glob.glob(env.Dir("#editor/translations/properties").abspath + "/*.po")
|
||||
env.Depends("#editor/property_translations.gen.h", tlist)
|
||||
env.CommandNoCache(
|
||||
"#editor/property_translations.gen.h",
|
||||
tlist,
|
||||
env.Run(editor_builders.make_property_translations_header),
|
||||
Glob("#editor/translations/properties/*"),
|
||||
env.Run(editor_builders.make_translations_header),
|
||||
)
|
||||
|
||||
# Documentation translations
|
||||
tlist = glob.glob(env.Dir("#doc/translations").abspath + "/*.po")
|
||||
env.Depends("#editor/doc_translations.gen.h", tlist)
|
||||
env.CommandNoCache(
|
||||
"#editor/doc_translations.gen.h",
|
||||
tlist,
|
||||
env.Run(editor_builders.make_doc_translations_header),
|
||||
Glob("#doc/translations/*"),
|
||||
env.Run(editor_builders.make_translations_header),
|
||||
)
|
||||
|
||||
# Extractable translations
|
||||
tlist = glob.glob(env.Dir("#editor/translations/extractable").abspath + "/*.po")
|
||||
tlist.extend(glob.glob(env.Dir("#editor/translations/extractable").abspath + "/extractable.pot"))
|
||||
env.Depends("#editor/extractable_translations.gen.h", tlist)
|
||||
env.CommandNoCache(
|
||||
"#editor/extractable_translations.gen.h",
|
||||
tlist,
|
||||
env.Run(editor_builders.make_extractable_translations_header),
|
||||
Glob("#editor/translations/extractable/*"),
|
||||
env.Run(editor_builders.make_translations_header),
|
||||
)
|
||||
|
||||
env.add_source_files(env.editor_sources, "*.cpp")
|
||||
|
@ -2,141 +2,95 @@
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import uuid
|
||||
import zlib
|
||||
|
||||
from methods import print_warning
|
||||
import methods
|
||||
|
||||
|
||||
def make_doc_header(target, source, env):
|
||||
dst = str(target[0])
|
||||
with open(dst, "w", encoding="utf-8", newline="\n") as g:
|
||||
buf = ""
|
||||
docbegin = ""
|
||||
docend = ""
|
||||
for src in source:
|
||||
src = str(src)
|
||||
if not src.endswith(".xml"):
|
||||
continue
|
||||
with open(src, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
buf += content
|
||||
buffer = b"".join([methods.get_buffer(src) for src in map(str, source)])
|
||||
decomp_size = len(buffer)
|
||||
buffer = methods.compress_buffer(buffer)
|
||||
|
||||
buf = (docbegin + buf + docend).encode("utf-8")
|
||||
decomp_size = len(buf)
|
||||
|
||||
# Use maximum zlib compression level to further reduce file size
|
||||
# (at the cost of initial build times).
|
||||
buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION)
|
||||
|
||||
g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
|
||||
g.write("#ifndef _DOC_DATA_RAW_H\n")
|
||||
g.write("#define _DOC_DATA_RAW_H\n")
|
||||
g.write('static const char *_doc_data_hash = "' + str(hash(buf)) + '";\n')
|
||||
g.write("static const int _doc_data_compressed_size = " + str(len(buf)) + ";\n")
|
||||
g.write("static const int _doc_data_uncompressed_size = " + str(decomp_size) + ";\n")
|
||||
g.write("static const unsigned char _doc_data_compressed[] = {\n")
|
||||
for i in range(len(buf)):
|
||||
g.write("\t" + str(buf[i]) + ",\n")
|
||||
g.write("};\n")
|
||||
|
||||
g.write("#endif")
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
file.write(f"""\
|
||||
inline constexpr const char *_doc_data_hash = "{hash(buffer)}";
|
||||
inline constexpr int _doc_data_compressed_size = {len(buffer)};
|
||||
inline constexpr int _doc_data_uncompressed_size = {decomp_size};
|
||||
inline constexpr const unsigned char _doc_data_compressed[] = {{
|
||||
{methods.format_buffer(buffer, 1)}
|
||||
}};
|
||||
""")
|
||||
|
||||
|
||||
def make_translations_header(target, source, env, category):
|
||||
dst = str(target[0])
|
||||
def make_translations_header(target, source, env):
|
||||
category = os.path.basename(str(target[0])).split("_")[0]
|
||||
sorted_paths = sorted([src.abspath for src in source], key=lambda path: os.path.splitext(os.path.basename(path))[0])
|
||||
|
||||
with open(dst, "w", encoding="utf-8", newline="\n") as g:
|
||||
g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
|
||||
g.write("#ifndef _{}_TRANSLATIONS_H\n".format(category.upper()))
|
||||
g.write("#define _{}_TRANSLATIONS_H\n".format(category.upper()))
|
||||
xl_names = []
|
||||
msgfmt = env.Detect("msgfmt")
|
||||
if not msgfmt:
|
||||
methods.print_warning("msgfmt not found, using .po files instead of .mo")
|
||||
|
||||
sorted_paths = sorted([str(x) for x in source], key=lambda path: os.path.splitext(os.path.basename(path))[0])
|
||||
|
||||
msgfmt_available = shutil.which("msgfmt") is not None
|
||||
|
||||
if not msgfmt_available:
|
||||
print_warning("msgfmt is not found, using .po files instead of .mo")
|
||||
|
||||
xl_names = []
|
||||
for i in range(len(sorted_paths)):
|
||||
name = os.path.splitext(os.path.basename(sorted_paths[i]))[0]
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
for path in sorted_paths:
|
||||
name = os.path.splitext(os.path.basename(path))[0]
|
||||
# msgfmt erases non-translated messages, so avoid using it if exporting the POT.
|
||||
if msgfmt_available and name != category:
|
||||
if msgfmt and name != category:
|
||||
mo_path = os.path.join(tempfile.gettempdir(), uuid.uuid4().hex + ".mo")
|
||||
cmd = "msgfmt " + sorted_paths[i] + " --no-hash -o " + mo_path
|
||||
cmd = f"{msgfmt} {path} --no-hash -o {mo_path}"
|
||||
try:
|
||||
subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE).communicate()
|
||||
with open(mo_path, "rb") as f:
|
||||
buf = f.read()
|
||||
buffer = methods.get_buffer(mo_path)
|
||||
except OSError as e:
|
||||
print_warning(
|
||||
methods.print_warning(
|
||||
"msgfmt execution failed, using .po file instead of .mo: path=%r; [%s] %s"
|
||||
% (sorted_paths[i], e.__class__.__name__, e)
|
||||
% (path, e.__class__.__name__, e)
|
||||
)
|
||||
with open(sorted_paths[i], "rb") as f:
|
||||
buf = f.read()
|
||||
buffer = methods.get_buffer(path)
|
||||
finally:
|
||||
try:
|
||||
os.remove(mo_path)
|
||||
if os.path.exists(mo_path):
|
||||
os.remove(mo_path)
|
||||
except OSError as e:
|
||||
# Do not fail the entire build if it cannot delete a temporary file.
|
||||
print_warning(
|
||||
methods.print_warning(
|
||||
"Could not delete temporary .mo file: path=%r; [%s] %s" % (mo_path, e.__class__.__name__, e)
|
||||
)
|
||||
else:
|
||||
with open(sorted_paths[i], "rb") as f:
|
||||
buf = f.read()
|
||||
|
||||
buffer = methods.get_buffer(path)
|
||||
if name == category:
|
||||
name = "source"
|
||||
|
||||
decomp_size = len(buf)
|
||||
# Use maximum zlib compression level to further reduce file size
|
||||
# (at the cost of initial build times).
|
||||
buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION)
|
||||
decomp_size = len(buffer)
|
||||
buffer = methods.compress_buffer(buffer)
|
||||
|
||||
g.write("static const unsigned char _{}_translation_{}_compressed[] = {{\n".format(category, name))
|
||||
for j in range(len(buf)):
|
||||
g.write("\t" + str(buf[j]) + ",\n")
|
||||
file.write(f"""\
|
||||
inline constexpr const unsigned char _{category}_translation_{name}_compressed[] = {{
|
||||
{methods.format_buffer(buffer, 1)}
|
||||
}};
|
||||
|
||||
g.write("};\n")
|
||||
""")
|
||||
|
||||
xl_names.append([name, len(buf), str(decomp_size)])
|
||||
xl_names.append([name, len(buffer), decomp_size])
|
||||
|
||||
file.write(f"""\
|
||||
struct {category.capitalize()}TranslationList {{
|
||||
const char* lang;
|
||||
int comp_size;
|
||||
int uncomp_size;
|
||||
const unsigned char* data;
|
||||
}};
|
||||
|
||||
inline constexpr {category.capitalize()}TranslationList _{category}_translations[] = {{
|
||||
""")
|
||||
|
||||
g.write("struct {}TranslationList {{\n".format(category.capitalize()))
|
||||
g.write("\tconst char* lang;\n")
|
||||
g.write("\tint comp_size;\n")
|
||||
g.write("\tint uncomp_size;\n")
|
||||
g.write("\tconst unsigned char* data;\n")
|
||||
g.write("};\n\n")
|
||||
g.write("static {}TranslationList _{}_translations[] = {{\n".format(category.capitalize(), category))
|
||||
for x in xl_names:
|
||||
g.write(
|
||||
'\t{{ "{}", {}, {}, _{}_translation_{}_compressed }},\n'.format(
|
||||
x[0], str(x[1]), str(x[2]), category, x[0]
|
||||
)
|
||||
)
|
||||
g.write("\t{nullptr, 0, 0, nullptr}\n")
|
||||
g.write("};\n")
|
||||
file.write(f'\t{{ "{x[0]}", {x[1]}, {x[2]}, _{category}_translation_{x[0]}_compressed }},\n')
|
||||
|
||||
g.write("#endif")
|
||||
|
||||
|
||||
def make_editor_translations_header(target, source, env):
|
||||
make_translations_header(target, source, env, "editor")
|
||||
|
||||
|
||||
def make_property_translations_header(target, source, env):
|
||||
make_translations_header(target, source, env, "property")
|
||||
|
||||
|
||||
def make_doc_translations_header(target, source, env):
|
||||
make_translations_header(target, source, env, "doc")
|
||||
|
||||
|
||||
def make_extractable_translations_header(target, source, env):
|
||||
make_translations_header(target, source, env, "extractable")
|
||||
file.write("""\
|
||||
{ nullptr, 0, 0, nullptr },
|
||||
};
|
||||
""")
|
||||
|
@ -42,7 +42,7 @@
|
||||
Vector<String> get_editor_locales() {
|
||||
Vector<String> locales;
|
||||
|
||||
EditorTranslationList *etl = _editor_translations;
|
||||
const EditorTranslationList *etl = _editor_translations;
|
||||
while (etl->data) {
|
||||
const String &locale = etl->lang;
|
||||
locales.push_back(locale);
|
||||
@ -56,7 +56,7 @@ Vector<String> get_editor_locales() {
|
||||
void load_editor_translations(const String &p_locale) {
|
||||
const Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_or_add_domain("godot.editor");
|
||||
|
||||
EditorTranslationList *etl = _editor_translations;
|
||||
const EditorTranslationList *etl = _editor_translations;
|
||||
while (etl->data) {
|
||||
if (etl->lang == p_locale) {
|
||||
Vector<uint8_t> data;
|
||||
@ -84,7 +84,7 @@ void load_editor_translations(const String &p_locale) {
|
||||
void load_property_translations(const String &p_locale) {
|
||||
const Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_or_add_domain("godot.properties");
|
||||
|
||||
PropertyTranslationList *etl = _property_translations;
|
||||
const PropertyTranslationList *etl = _property_translations;
|
||||
while (etl->data) {
|
||||
if (etl->lang == p_locale) {
|
||||
Vector<uint8_t> data;
|
||||
@ -112,7 +112,7 @@ void load_property_translations(const String &p_locale) {
|
||||
void load_doc_translations(const String &p_locale) {
|
||||
const Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_or_add_domain("godot.documentation");
|
||||
|
||||
DocTranslationList *dtl = _doc_translations;
|
||||
const DocTranslationList *dtl = _doc_translations;
|
||||
while (dtl->data) {
|
||||
if (dtl->lang == p_locale) {
|
||||
Vector<uint8_t> data;
|
||||
@ -140,7 +140,7 @@ void load_doc_translations(const String &p_locale) {
|
||||
void load_extractable_translations(const String &p_locale) {
|
||||
const Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_or_add_domain("godot.editor");
|
||||
|
||||
ExtractableTranslationList *etl = _extractable_translations;
|
||||
const ExtractableTranslationList *etl = _extractable_translations;
|
||||
while (etl->data) {
|
||||
if (etl->lang == p_locale) {
|
||||
Vector<uint8_t> data;
|
||||
@ -166,7 +166,7 @@ void load_extractable_translations(const String &p_locale) {
|
||||
}
|
||||
|
||||
Vector<Vector<String>> get_extractable_message_list() {
|
||||
ExtractableTranslationList *etl = _extractable_translations;
|
||||
const ExtractableTranslationList *etl = _extractable_translations;
|
||||
Vector<Vector<String>> list;
|
||||
|
||||
while (etl->data) {
|
||||
|
@ -1,71 +1,47 @@
|
||||
"""Functions used to generate source files during build time"""
|
||||
|
||||
import os
|
||||
from io import StringIO
|
||||
|
||||
from methods import to_raw_cstring
|
||||
import methods
|
||||
|
||||
|
||||
# See also `scene/theme/icons/default_theme_icons_builders.py`.
|
||||
def make_editor_icons_action(target, source, env):
|
||||
dst = str(target[0])
|
||||
svg_icons = source
|
||||
icons_names = []
|
||||
icons_raw = []
|
||||
icons_med = []
|
||||
icons_big = []
|
||||
|
||||
with StringIO() as icons_string, StringIO() as s:
|
||||
for svg in svg_icons:
|
||||
with open(str(svg), "r") as svgf:
|
||||
icons_string.write("\t%s,\n" % to_raw_cstring(svgf.read()))
|
||||
for idx, svg in enumerate(source):
|
||||
path = str(svg)
|
||||
with open(path, encoding="utf-8", newline="\n") as file:
|
||||
icons_raw.append(methods.to_raw_cstring(file.read()))
|
||||
|
||||
s.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
|
||||
s.write("#ifndef _EDITOR_ICONS_H\n")
|
||||
s.write("#define _EDITOR_ICONS_H\n")
|
||||
s.write("static const int editor_icons_count = {};\n".format(len(svg_icons)))
|
||||
s.write("static const char *editor_icons_sources[] = {\n")
|
||||
s.write(icons_string.getvalue())
|
||||
s.write("};\n\n")
|
||||
s.write("static const char *editor_icons_names[] = {\n")
|
||||
name = os.path.splitext(os.path.basename(path))[0]
|
||||
icons_names.append(f'"{name}"')
|
||||
|
||||
# this is used to store the indices of thumbnail icons
|
||||
thumb_medium_indices = []
|
||||
thumb_big_indices = []
|
||||
index = 0
|
||||
for f in svg_icons:
|
||||
fname = str(f)
|
||||
if name.endswith("MediumThumb"):
|
||||
icons_med.append(str(idx))
|
||||
elif name.endswith(("BigThumb", "GodotFile")):
|
||||
icons_big.append(str(idx))
|
||||
|
||||
# Trim the `.svg` extension from the string.
|
||||
icon_name = os.path.basename(fname)[:-4]
|
||||
# some special cases
|
||||
if icon_name.endswith("MediumThumb"): # don't know a better way to handle this
|
||||
thumb_medium_indices.append(str(index))
|
||||
if icon_name.endswith("BigThumb"): # don't know a better way to handle this
|
||||
thumb_big_indices.append(str(index))
|
||||
if icon_name.endswith("GodotFile"): # don't know a better way to handle this
|
||||
thumb_big_indices.append(str(index))
|
||||
icons_names_str = ",\n\t".join(icons_names)
|
||||
icons_raw_str = ",\n\t".join(icons_raw)
|
||||
|
||||
s.write('\t"{0}"'.format(icon_name))
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
file.write(f"""\
|
||||
inline constexpr int editor_icons_count = {len(icons_names)};
|
||||
inline constexpr const char *editor_icons_sources[] = {{
|
||||
{icons_raw_str}
|
||||
}};
|
||||
|
||||
if fname != svg_icons[-1]:
|
||||
s.write(",")
|
||||
s.write("\n")
|
||||
inline constexpr const char *editor_icons_names[] = {{
|
||||
{icons_names_str}
|
||||
}};
|
||||
|
||||
index += 1
|
||||
inline constexpr int editor_md_thumbs_count = {len(icons_med)};
|
||||
inline constexpr int editor_md_thumbs_indices[] = {{ {", ".join(icons_med)} }};
|
||||
|
||||
s.write("};\n")
|
||||
|
||||
if thumb_medium_indices:
|
||||
s.write("\n\n")
|
||||
s.write("static const int editor_md_thumbs_count = {};\n".format(len(thumb_medium_indices)))
|
||||
s.write("static const int editor_md_thumbs_indices[] = {")
|
||||
s.write(", ".join(thumb_medium_indices))
|
||||
s.write("};\n")
|
||||
if thumb_big_indices:
|
||||
s.write("\n\n")
|
||||
s.write("static const int editor_bg_thumbs_count = {};\n".format(len(thumb_big_indices)))
|
||||
s.write("static const int editor_bg_thumbs_indices[] = {")
|
||||
s.write(", ".join(thumb_big_indices))
|
||||
s.write("};\n")
|
||||
|
||||
s.write("#endif\n")
|
||||
|
||||
with open(dst, "w", encoding="utf-8", newline="\n") as f:
|
||||
f.write(s.getvalue())
|
||||
inline constexpr int editor_bg_thumbs_count = {len(icons_big)};
|
||||
inline constexpr int editor_bg_thumbs_indices[] = {{ {", ".join(icons_big)} }};
|
||||
""")
|
||||
|
@ -1,7 +1,8 @@
|
||||
"""Functions used to generate source files during build time"""
|
||||
|
||||
import os
|
||||
from io import StringIO
|
||||
|
||||
import methods
|
||||
|
||||
|
||||
def parse_template(inherits, source, delimiter):
|
||||
@ -36,54 +37,36 @@ def parse_template(inherits, source, delimiter):
|
||||
script_template["script"].replace('"', '\\"').lstrip().replace("\n", "\\n").replace("\t", "_TS_")
|
||||
)
|
||||
return (
|
||||
'{ String("'
|
||||
+ script_template["inherits"]
|
||||
+ '"), String("'
|
||||
+ script_template["name"]
|
||||
+ '"), String("'
|
||||
+ script_template["description"]
|
||||
+ '"), String("'
|
||||
+ script_template["script"]
|
||||
+ '")'
|
||||
+ " },\n"
|
||||
f'{{ String("{script_template["inherits"]}"), '
|
||||
+ f'String("{script_template["name"]}"), '
|
||||
+ f'String("{script_template["description"]}"), '
|
||||
+ f'String("{script_template["script"]}") }},'
|
||||
)
|
||||
|
||||
|
||||
def make_templates(target, source, env):
|
||||
dst = str(target[0])
|
||||
with StringIO() as s:
|
||||
s.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n\n")
|
||||
s.write("#ifndef _CODE_TEMPLATES_H\n")
|
||||
s.write("#define _CODE_TEMPLATES_H\n\n")
|
||||
s.write('#include "core/object/object.h"\n')
|
||||
s.write('#include "core/object/script_language.h"\n')
|
||||
delimiter = "#" # GDScript single line comment delimiter by default.
|
||||
if source:
|
||||
ext = os.path.splitext(str(source[0]))[1]
|
||||
if ext == ".cs":
|
||||
delimiter = "//"
|
||||
|
||||
delimiter = "#" # GDScript single line comment delimiter by default.
|
||||
if source:
|
||||
ext = os.path.splitext(str(source[0]))[1]
|
||||
if ext == ".cs":
|
||||
delimiter = "//"
|
||||
parsed_templates = []
|
||||
|
||||
parsed_template_string = ""
|
||||
number_of_templates = 0
|
||||
for filepath in source:
|
||||
filepath = str(filepath)
|
||||
node_name = os.path.basename(os.path.dirname(filepath))
|
||||
parsed_templates.append(parse_template(node_name, filepath, delimiter))
|
||||
|
||||
for filepath in source:
|
||||
filepath = str(filepath)
|
||||
node_name = os.path.basename(os.path.dirname(filepath))
|
||||
parsed_template = parse_template(node_name, filepath, delimiter)
|
||||
parsed_template_string += "\t" + parsed_template
|
||||
number_of_templates += 1
|
||||
parsed_template_string = "\n\t".join(parsed_templates)
|
||||
|
||||
s.write("\nstatic const int TEMPLATES_ARRAY_SIZE = " + str(number_of_templates) + ";\n")
|
||||
s.write(
|
||||
"\nstatic const struct ScriptLanguage::ScriptTemplate TEMPLATES[" + str(number_of_templates) + "] = {\n"
|
||||
)
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
file.write(f"""\
|
||||
#include "core/object/object.h"
|
||||
#include "core/object/script_language.h"
|
||||
|
||||
s.write(parsed_template_string)
|
||||
|
||||
s.write("};\n")
|
||||
|
||||
s.write("\n#endif\n")
|
||||
|
||||
with open(dst, "w", encoding="utf-8", newline="\n") as f:
|
||||
f.write(s.getvalue())
|
||||
inline constexpr int TEMPLATES_ARRAY_SIZE = {len(parsed_templates)};
|
||||
static const struct ScriptLanguage::ScriptTemplate TEMPLATES[TEMPLATES_ARRAY_SIZE] = {{
|
||||
{parsed_template_string}
|
||||
}};
|
||||
""")
|
||||
|
@ -3,17 +3,14 @@ from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
import glob
|
||||
|
||||
import editor_theme_builders
|
||||
|
||||
# Fonts
|
||||
flist = glob.glob(env.Dir("#thirdparty").abspath + "/fonts/*.ttf")
|
||||
flist.extend(glob.glob(env.Dir("#thirdparty").abspath + "/fonts/*.otf"))
|
||||
flist.extend(glob.glob(env.Dir("#thirdparty").abspath + "/fonts/*.woff"))
|
||||
flist.extend(glob.glob(env.Dir("#thirdparty").abspath + "/fonts/*.woff2"))
|
||||
flist = Glob("#thirdparty/fonts/*.ttf")
|
||||
flist.extend(Glob("#thirdparty/fonts/*.otf"))
|
||||
flist.extend(Glob("#thirdparty/fonts/*.woff"))
|
||||
flist.extend(Glob("#thirdparty/fonts/*.woff2"))
|
||||
flist.sort()
|
||||
env.Depends("#editor/themes/builtin_fonts.gen.h", flist)
|
||||
env.CommandNoCache(
|
||||
"#editor/themes/builtin_fonts.gen.h",
|
||||
flist,
|
||||
|
@ -2,28 +2,20 @@
|
||||
|
||||
import os
|
||||
|
||||
import methods
|
||||
|
||||
|
||||
def make_fonts_header(target, source, env):
|
||||
dst = str(target[0])
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
for src in map(str, source):
|
||||
# Saving uncompressed, since FreeType will reference from memory pointer.
|
||||
buffer = methods.get_buffer(src)
|
||||
name = os.path.splitext(os.path.basename(src))[0]
|
||||
|
||||
with open(dst, "w", encoding="utf-8", newline="\n") as g:
|
||||
g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
|
||||
g.write("#ifndef _EDITOR_FONTS_H\n")
|
||||
g.write("#define _EDITOR_FONTS_H\n")
|
||||
file.write(f"""\
|
||||
inline constexpr int _font_{name}_size = {len(buffer)};
|
||||
inline constexpr unsigned char _font_{name}[] = {{
|
||||
{methods.format_buffer(buffer, 1)}
|
||||
}};
|
||||
|
||||
# Saving uncompressed, since FreeType will reference from memory pointer.
|
||||
for i in range(len(source)):
|
||||
file = str(source[i])
|
||||
with open(file, "rb") as f:
|
||||
buf = f.read()
|
||||
|
||||
name = os.path.splitext(os.path.basename(file))[0]
|
||||
|
||||
g.write("static const int _font_" + name + "_size = " + str(len(buf)) + ";\n")
|
||||
g.write("static const unsigned char _font_" + name + "[] = {\n")
|
||||
for j in range(len(buf)):
|
||||
g.write("\t" + str(buf[j]) + ",\n")
|
||||
|
||||
g.write("};\n")
|
||||
|
||||
g.write("#endif")
|
||||
""")
|
||||
|
@ -17,7 +17,6 @@ if env["steamapi"] and env.editor_build:
|
||||
if env["tests"]:
|
||||
env_main.Append(CPPDEFINES=["TESTS_ENABLED"])
|
||||
|
||||
env_main.Depends("#main/splash.gen.h", "#main/splash.png")
|
||||
env_main.CommandNoCache(
|
||||
"#main/splash.gen.h",
|
||||
"#main/splash.png",
|
||||
@ -25,14 +24,12 @@ env_main.CommandNoCache(
|
||||
)
|
||||
|
||||
if env_main.editor_build and not env_main["no_editor_splash"]:
|
||||
env_main.Depends("#main/splash_editor.gen.h", "#main/splash_editor.png")
|
||||
env_main.CommandNoCache(
|
||||
"#main/splash_editor.gen.h",
|
||||
"#main/splash_editor.png",
|
||||
env.Run(main_builders.make_splash_editor),
|
||||
)
|
||||
|
||||
env_main.Depends("#main/app_icon.gen.h", "#main/app_icon.png")
|
||||
env_main.CommandNoCache(
|
||||
"#main/app_icon.gen.h",
|
||||
"#main/app_icon.png",
|
||||
|
@ -1,60 +1,42 @@
|
||||
"""Functions used to generate source files during build time"""
|
||||
|
||||
import methods
|
||||
|
||||
|
||||
def make_splash(target, source, env):
|
||||
src = str(source[0])
|
||||
dst = str(target[0])
|
||||
buffer = methods.get_buffer(str(source[0]))
|
||||
|
||||
with open(src, "rb") as f:
|
||||
buf = f.read()
|
||||
|
||||
with open(dst, "w", encoding="utf-8", newline="\n") as g:
|
||||
g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
|
||||
g.write("#ifndef BOOT_SPLASH_H\n")
|
||||
g.write("#define BOOT_SPLASH_H\n")
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
# Use a neutral gray color to better fit various kinds of projects.
|
||||
g.write("static const Color boot_splash_bg_color = Color(0.14, 0.14, 0.14);\n")
|
||||
g.write("static const unsigned char boot_splash_png[] = {\n")
|
||||
for i in range(len(buf)):
|
||||
g.write(str(buf[i]) + ",\n")
|
||||
g.write("};\n")
|
||||
g.write("#endif")
|
||||
file.write(f"""\
|
||||
static const Color boot_splash_bg_color = Color(0.14, 0.14, 0.14);
|
||||
inline constexpr const unsigned char boot_splash_png[] = {{
|
||||
{methods.format_buffer(buffer, 1)}
|
||||
}};
|
||||
""")
|
||||
|
||||
|
||||
def make_splash_editor(target, source, env):
|
||||
src = str(source[0])
|
||||
dst = str(target[0])
|
||||
buffer = methods.get_buffer(str(source[0]))
|
||||
|
||||
with open(src, "rb") as f:
|
||||
buf = f.read()
|
||||
|
||||
with open(dst, "w", encoding="utf-8", newline="\n") as g:
|
||||
g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
|
||||
g.write("#ifndef BOOT_SPLASH_EDITOR_H\n")
|
||||
g.write("#define BOOT_SPLASH_EDITOR_H\n")
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
# The editor splash background color is taken from the default editor theme's background color.
|
||||
# This helps achieve a visually "smoother" transition between the splash screen and the editor.
|
||||
g.write("static const Color boot_splash_editor_bg_color = Color(0.125, 0.145, 0.192);\n")
|
||||
g.write("static const unsigned char boot_splash_editor_png[] = {\n")
|
||||
for i in range(len(buf)):
|
||||
g.write(str(buf[i]) + ",\n")
|
||||
g.write("};\n")
|
||||
g.write("#endif")
|
||||
file.write(f"""\
|
||||
static const Color boot_splash_editor_bg_color = Color(0.125, 0.145, 0.192);
|
||||
inline constexpr const unsigned char boot_splash_editor_png[] = {{
|
||||
{methods.format_buffer(buffer, 1)}
|
||||
}};
|
||||
""")
|
||||
|
||||
|
||||
def make_app_icon(target, source, env):
|
||||
src = str(source[0])
|
||||
dst = str(target[0])
|
||||
buffer = methods.get_buffer(str(source[0]))
|
||||
|
||||
with open(src, "rb") as f:
|
||||
buf = f.read()
|
||||
|
||||
with open(dst, "w", encoding="utf-8", newline="\n") as g:
|
||||
g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
|
||||
g.write("#ifndef APP_ICON_H\n")
|
||||
g.write("#define APP_ICON_H\n")
|
||||
g.write("static const unsigned char app_icon_png[] = {\n")
|
||||
for i in range(len(buf)):
|
||||
g.write(str(buf[i]) + ",\n")
|
||||
g.write("};\n")
|
||||
g.write("#endif")
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
# Use a neutral gray color to better fit various kinds of projects.
|
||||
file.write(f"""\
|
||||
inline constexpr const unsigned char app_icon_png[] = {{
|
||||
{methods.format_buffer(buffer, 1)}
|
||||
}};
|
||||
""")
|
||||
|
125
methods.py
125
methods.py
@ -6,6 +6,8 @@ import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
import zlib
|
||||
from collections import OrderedDict
|
||||
from io import StringIO, TextIOBase
|
||||
from pathlib import Path
|
||||
@ -144,30 +146,36 @@ def get_version_info(module_version_string="", silent=False):
|
||||
if not silent:
|
||||
print_info(f"Using version status '{version_info['status']}', overriding the original '{version.status}'.")
|
||||
|
||||
return version_info
|
||||
|
||||
|
||||
def get_git_info():
|
||||
os.chdir(base_folder_path)
|
||||
|
||||
# Parse Git hash if we're in a Git repo.
|
||||
githash = ""
|
||||
gitfolder = ".git"
|
||||
git_hash = ""
|
||||
git_folder = ".git"
|
||||
|
||||
if os.path.isfile(".git"):
|
||||
with open(".git", "r", encoding="utf-8") as file:
|
||||
module_folder = file.readline().strip()
|
||||
if module_folder.startswith("gitdir: "):
|
||||
gitfolder = module_folder[8:]
|
||||
git_folder = module_folder[8:]
|
||||
|
||||
if os.path.isfile(os.path.join(gitfolder, "HEAD")):
|
||||
with open(os.path.join(gitfolder, "HEAD"), "r", encoding="utf8") as file:
|
||||
if os.path.isfile(os.path.join(git_folder, "HEAD")):
|
||||
with open(os.path.join(git_folder, "HEAD"), "r", encoding="utf8") as file:
|
||||
head = file.readline().strip()
|
||||
if head.startswith("ref: "):
|
||||
ref = head[5:]
|
||||
# If this directory is a Git worktree instead of a root clone.
|
||||
parts = gitfolder.split("/")
|
||||
parts = git_folder.split("/")
|
||||
if len(parts) > 2 and parts[-2] == "worktrees":
|
||||
gitfolder = "/".join(parts[0:-2])
|
||||
head = os.path.join(gitfolder, ref)
|
||||
packedrefs = os.path.join(gitfolder, "packed-refs")
|
||||
git_folder = "/".join(parts[0:-2])
|
||||
head = os.path.join(git_folder, ref)
|
||||
packedrefs = os.path.join(git_folder, "packed-refs")
|
||||
if os.path.isfile(head):
|
||||
with open(head, "r", encoding="utf-8") as file:
|
||||
githash = file.readline().strip()
|
||||
git_hash = file.readline().strip()
|
||||
elif os.path.isfile(packedrefs):
|
||||
# Git may pack refs into a single file. This code searches .git/packed-refs file for the current ref's hash.
|
||||
# https://mirrors.edge.kernel.org/pub/software/scm/git/docs/git-pack-refs.html
|
||||
@ -176,26 +184,26 @@ def get_version_info(module_version_string="", silent=False):
|
||||
continue
|
||||
(line_hash, line_ref) = line.split(" ")
|
||||
if ref == line_ref:
|
||||
githash = line_hash
|
||||
git_hash = line_hash
|
||||
break
|
||||
else:
|
||||
githash = head
|
||||
|
||||
version_info["git_hash"] = githash
|
||||
# Fallback to 0 as a timestamp (will be treated as "unknown" in the engine).
|
||||
version_info["git_timestamp"] = 0
|
||||
git_hash = head
|
||||
|
||||
# Get the UNIX timestamp of the build commit.
|
||||
git_timestamp = 0
|
||||
if os.path.exists(".git"):
|
||||
try:
|
||||
version_info["git_timestamp"] = subprocess.check_output(
|
||||
["git", "log", "-1", "--pretty=format:%ct", "--no-show-signature", githash]
|
||||
).decode("utf-8")
|
||||
git_timestamp = subprocess.check_output(
|
||||
["git", "log", "-1", "--pretty=format:%ct", "--no-show-signature", git_hash], encoding="utf-8"
|
||||
)
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
# `git` not found in PATH.
|
||||
pass
|
||||
|
||||
return version_info
|
||||
return {
|
||||
"git_hash": git_hash,
|
||||
"git_timestamp": git_timestamp,
|
||||
}
|
||||
|
||||
|
||||
def get_cmdline_bool(option, default):
|
||||
@ -1417,6 +1425,11 @@ def generate_vs_project(env, original_args, project_name="godot"):
|
||||
sys.exit()
|
||||
|
||||
|
||||
############################################################
|
||||
# FILE GENERATION & FORMATTING
|
||||
############################################################
|
||||
|
||||
|
||||
def generate_copyright_header(filename: str) -> str:
|
||||
MARGIN = 70
|
||||
TEMPLATE = """\
|
||||
@ -1450,15 +1463,14 @@ def generate_copyright_header(filename: str) -> str:
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
"""
|
||||
filename = filename.split("/")[-1].ljust(MARGIN)
|
||||
if len(filename) > MARGIN:
|
||||
if len(filename := os.path.basename(filename).ljust(MARGIN)) > MARGIN:
|
||||
print_warning(f'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
|
||||
path: str,
|
||||
guard: Optional[bool] = None,
|
||||
) -> Generator[TextIOBase, None, None]:
|
||||
"""
|
||||
@ -1466,26 +1478,11 @@ def generated_wrapper(
|
||||
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.
|
||||
- `path`: The path of the file to be created.
|
||||
- `guard`: Optional bool to determine if `#pragma once` should be added. If
|
||||
unassigned, the value is determined by file extension.
|
||||
"""
|
||||
|
||||
# Handle unfiltered SCons target[s] passed as path.
|
||||
if not isinstance(path, str):
|
||||
if isinstance(path, list):
|
||||
if len(path) > 1:
|
||||
print_warning(
|
||||
f"Attempting to use generated wrapper with multiple targets; 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", ".hxx", ".inc"))
|
||||
|
||||
@ -1503,6 +1500,50 @@ def generated_wrapper(
|
||||
file.write("\n")
|
||||
|
||||
|
||||
def get_buffer(path: str) -> bytes:
|
||||
with open(path, "rb") as file:
|
||||
return file.read()
|
||||
|
||||
|
||||
def compress_buffer(buffer: bytes) -> bytes:
|
||||
# Use maximum zlib compression level to further reduce file size
|
||||
# (at the cost of initial build times).
|
||||
return zlib.compress(buffer, zlib.Z_BEST_COMPRESSION)
|
||||
|
||||
|
||||
def format_buffer(buffer: bytes, indent: int = 0, width: int = 120, initial_indent: bool = False) -> str:
|
||||
return textwrap.fill(
|
||||
", ".join(str(byte) for byte in buffer),
|
||||
width=width,
|
||||
initial_indent="\t" * indent if initial_indent else "",
|
||||
subsequent_indent="\t" * indent,
|
||||
tabsize=4,
|
||||
)
|
||||
|
||||
|
||||
############################################################
|
||||
# CSTRING PARSING
|
||||
############################################################
|
||||
|
||||
C_ESCAPABLES = [
|
||||
("\\", "\\\\"),
|
||||
("\a", "\\a"),
|
||||
("\b", "\\b"),
|
||||
("\f", "\\f"),
|
||||
("\n", "\\n"),
|
||||
("\r", "\\r"),
|
||||
("\t", "\\t"),
|
||||
("\v", "\\v"),
|
||||
# ("'", "\\'"), # Skip, as we're only dealing with full strings.
|
||||
('"', '\\"'),
|
||||
]
|
||||
C_ESCAPE_TABLE = str.maketrans(dict((x, y) for x, y in C_ESCAPABLES))
|
||||
|
||||
|
||||
def to_escaped_cstring(value: str) -> str:
|
||||
return value.translate(C_ESCAPE_TABLE)
|
||||
|
||||
|
||||
def to_raw_cstring(value: Union[str, List[str]]) -> str:
|
||||
MAX_LITERAL = 16 * 1024
|
||||
|
||||
@ -1540,4 +1581,8 @@ def to_raw_cstring(value: Union[str, List[str]]) -> str:
|
||||
|
||||
split += [segment]
|
||||
|
||||
return " ".join(f'R"<!>({x.decode()})<!>"' for x in split)
|
||||
if len(split) == 1:
|
||||
return f'R"<!>({split[0].decode()})<!>"'
|
||||
else:
|
||||
# Wrap multiple segments in parenthesis to suppress `string-concatenation` warnings on clang.
|
||||
return "({})".format(" ".join(f'R"<!>({segment.decode()})<!>"' for segment in split))
|
||||
|
@ -17,8 +17,9 @@ Export("env_modules")
|
||||
|
||||
# Header with MODULE_*_ENABLED defines.
|
||||
def modules_enabled_builder(target, source, env):
|
||||
with methods.generated_wrapper(target) as file:
|
||||
for module in source[0].read():
|
||||
modules = sorted(source[0].read())
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
for module in modules:
|
||||
file.write(f"#define MODULE_{module.upper()}_ENABLED\n")
|
||||
|
||||
|
||||
@ -29,14 +30,26 @@ modules_enabled = env.CommandNoCache(
|
||||
|
||||
def register_module_types_builder(target, source, env):
|
||||
modules = source[0].read()
|
||||
mod_inc = "\n".join([f'#include "{p}/register_types.h"' for p in modules.values()])
|
||||
mod_inc = "\n".join([f'#include "{value}/register_types.h"' for value in modules.values()])
|
||||
mod_init = "\n".join(
|
||||
[f"#ifdef MODULE_{n.upper()}_ENABLED\n\tinitialize_{n}_module(p_level);\n#endif" for n in modules.keys()]
|
||||
[
|
||||
f"""\
|
||||
#ifdef MODULE_{key.upper()}_ENABLED
|
||||
initialize_{key}_module(p_level);
|
||||
#endif"""
|
||||
for key in modules.keys()
|
||||
]
|
||||
)
|
||||
mod_uninit = "\n".join(
|
||||
[f"#ifdef MODULE_{n.upper()}_ENABLED\n\tuninitialize_{n}_module(p_level);\n#endif" for n in modules.keys()]
|
||||
[
|
||||
f"""\
|
||||
#ifdef MODULE_{key.upper()}_ENABLED
|
||||
uninitialize_{key}_module(p_level);
|
||||
#endif"""
|
||||
for key in modules.keys()
|
||||
]
|
||||
)
|
||||
with methods.generated_wrapper(target) as file:
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
file.write(
|
||||
f"""\
|
||||
#include "register_module_types.h"
|
||||
@ -88,9 +101,10 @@ for name, path in env.module_list.items():
|
||||
if env["tests"]:
|
||||
|
||||
def modules_tests_builder(target, source, env):
|
||||
with methods.generated_wrapper(target) as file:
|
||||
for header in source:
|
||||
file.write('#include "{}"\n'.format(os.path.normpath(header.path).replace("\\", "/")))
|
||||
headers = sorted([os.path.relpath(src.path, methods.base_folder_path).replace("\\", "/") for src in source])
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
for header in headers:
|
||||
file.write(f'#include "{header}"\n')
|
||||
|
||||
env.CommandNoCache("modules_tests.gen.h", test_headers, env.Run(modules_tests_builder))
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
import methods
|
||||
|
||||
Import("env")
|
||||
Import("env_modules")
|
||||
|
||||
@ -8,28 +10,21 @@ env_text_server_adv = env_modules.Clone()
|
||||
|
||||
|
||||
def make_icu_data(target, source, env):
|
||||
dst = target[0].srcnode().abspath
|
||||
buffer = methods.get_buffer(str(source[0]))
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
file.write(f"""\
|
||||
/* (C) 2016 and later: Unicode, Inc. and others. */
|
||||
/* License & terms of use: https://www.unicode.org/copyright.html */
|
||||
|
||||
with open(dst, "w", encoding="utf-8", newline="\n") as g:
|
||||
g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
|
||||
g.write("/* (C) 2016 and later: Unicode, Inc. and others. */\n")
|
||||
g.write("/* License & terms of use: https://www.unicode.org/copyright.html */\n")
|
||||
g.write("#ifndef _ICU_DATA_H\n")
|
||||
g.write("#define _ICU_DATA_H\n")
|
||||
g.write('#include "unicode/utypes.h"\n')
|
||||
g.write('#include "unicode/udata.h"\n')
|
||||
g.write('#include "unicode/uversion.h"\n')
|
||||
#include <unicode/utypes.h>
|
||||
#include <unicode/udata.h>
|
||||
#include <unicode/uversion.h>
|
||||
|
||||
with open(source[0].srcnode().abspath, "rb") as f:
|
||||
buf = f.read()
|
||||
|
||||
g.write('extern "C" U_EXPORT const size_t U_ICUDATA_SIZE = ' + str(len(buf)) + ";\n")
|
||||
g.write('extern "C" U_EXPORT const unsigned char U_ICUDATA_ENTRY_POINT[] = {\n')
|
||||
for i in range(len(buf)):
|
||||
g.write("\t" + str(buf[i]) + ",\n")
|
||||
|
||||
g.write("};\n")
|
||||
g.write("#endif")
|
||||
extern "C" U_EXPORT const size_t U_ICUDATA_SIZE = {len(buffer)};
|
||||
extern "C" U_EXPORT const unsigned char U_ICUDATA_ENTRY_POINT[] = {{
|
||||
{methods.format_buffer(buffer, 1)}
|
||||
}};
|
||||
""")
|
||||
|
||||
|
||||
# Thirdparty source files
|
||||
|
@ -18,10 +18,10 @@ def export_icon_builder(target, source, env):
|
||||
platform = src_path.parent.parent.stem
|
||||
with open(str(source[0]), "r") as file:
|
||||
svg = file.read()
|
||||
with methods.generated_wrapper(target) as file:
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
file.write(
|
||||
f"""\
|
||||
static const char *_{platform}_{src_name}_svg = {methods.to_raw_cstring(svg)};
|
||||
inline constexpr const char *_{platform}_{src_name}_svg = {methods.to_raw_cstring(svg)};
|
||||
"""
|
||||
)
|
||||
|
||||
@ -37,7 +37,7 @@ def register_platform_apis_builder(target, source, env):
|
||||
api_inc = "\n".join([f'#include "{p}/api/api.h"' for p in platforms])
|
||||
api_reg = "\n".join([f"\tregister_{p}_api();" for p in platforms])
|
||||
api_unreg = "\n".join([f"\tunregister_{p}_api();" for p in platforms])
|
||||
with methods.generated_wrapper(target) as file:
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
file.write(
|
||||
f"""\
|
||||
#include "register_platform_apis.h"
|
||||
|
@ -9,7 +9,6 @@ env.add_source_files(env.scene_sources, "*.cpp")
|
||||
|
||||
SConscript("icons/SCsub")
|
||||
|
||||
env.Depends("#scene/theme/default_font.gen.h", "#thirdparty/fonts/OpenSans_SemiBold.woff2")
|
||||
env.CommandNoCache(
|
||||
"#scene/theme/default_font.gen.h",
|
||||
"#thirdparty/fonts/OpenSans_SemiBold.woff2",
|
||||
|
@ -1,30 +1,21 @@
|
||||
"""Functions used to generate source files during build time"""
|
||||
|
||||
import os
|
||||
import os.path
|
||||
|
||||
import methods
|
||||
|
||||
|
||||
def make_fonts_header(target, source, env):
|
||||
dst = str(target[0])
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
for src in map(str, source):
|
||||
# Saving uncompressed, since FreeType will reference from memory pointer.
|
||||
buffer = methods.get_buffer(src)
|
||||
name = os.path.splitext(os.path.basename(src))[0]
|
||||
|
||||
with open(dst, "w", encoding="utf-8", newline="\n") as g:
|
||||
g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
|
||||
g.write("#ifndef _DEFAULT_FONTS_H\n")
|
||||
g.write("#define _DEFAULT_FONTS_H\n")
|
||||
file.write(f"""\
|
||||
inline constexpr int _font_{name}_size = {len(buffer)};
|
||||
inline constexpr unsigned char _font_{name}[] = {{
|
||||
{methods.format_buffer(buffer, 1)}
|
||||
}};
|
||||
|
||||
# Saving uncompressed, since FreeType will reference from memory pointer.
|
||||
for i in range(len(source)):
|
||||
file = str(source[i])
|
||||
with open(file, "rb") as f:
|
||||
buf = f.read()
|
||||
|
||||
name = os.path.splitext(os.path.basename(file))[0]
|
||||
|
||||
g.write("static const int _font_" + name + "_size = " + str(len(buf)) + ";\n")
|
||||
g.write("static const unsigned char _font_" + name + "[] = {\n")
|
||||
for j in range(len(buf)):
|
||||
g.write("\t" + str(buf[j]) + ",\n")
|
||||
|
||||
g.write("};\n")
|
||||
|
||||
g.write("#endif")
|
||||
""")
|
||||
|
@ -1,51 +1,35 @@
|
||||
"""Functions used to generate source files during build time"""
|
||||
|
||||
import os
|
||||
from io import StringIO
|
||||
|
||||
from methods import to_raw_cstring
|
||||
import methods
|
||||
|
||||
|
||||
# See also `editor/icons/editor_icons_builders.py`.
|
||||
def make_default_theme_icons_action(target, source, env):
|
||||
dst = str(target[0])
|
||||
svg_icons = [str(x) for x in source]
|
||||
icons_names = []
|
||||
icons_raw = []
|
||||
|
||||
with StringIO() as icons_string, StringIO() as s:
|
||||
for svg in svg_icons:
|
||||
with open(svg, "r") as svgf:
|
||||
icons_string.write("\t%s,\n" % to_raw_cstring(svgf.read()))
|
||||
for src in map(str, source):
|
||||
with open(src, encoding="utf-8", newline="\n") as file:
|
||||
icons_raw.append(methods.to_raw_cstring(file.read()))
|
||||
|
||||
s.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n\n")
|
||||
s.write('#include "modules/modules_enabled.gen.h"\n\n')
|
||||
s.write("#ifndef _DEFAULT_THEME_ICONS_H\n")
|
||||
s.write("#define _DEFAULT_THEME_ICONS_H\n")
|
||||
s.write("static const int default_theme_icons_count = {};\n\n".format(len(svg_icons)))
|
||||
s.write("#ifdef MODULE_SVG_ENABLED\n")
|
||||
s.write("static const char *default_theme_icons_sources[] = {\n")
|
||||
s.write(icons_string.getvalue())
|
||||
s.write("};\n")
|
||||
s.write("#endif // MODULE_SVG_ENABLED\n\n")
|
||||
s.write("static const char *default_theme_icons_names[] = {\n")
|
||||
name = os.path.splitext(os.path.basename(src))[0]
|
||||
icons_names.append(f'"{name}"')
|
||||
|
||||
index = 0
|
||||
for f in svg_icons:
|
||||
fname = str(f)
|
||||
icons_names_str = ",\n\t".join(icons_names)
|
||||
icons_raw_str = ",\n\t".join(icons_raw)
|
||||
|
||||
# Trim the `.svg` extension from the string.
|
||||
icon_name = os.path.basename(fname)[:-4]
|
||||
with methods.generated_wrapper(str(target[0])) as file:
|
||||
file.write(f"""\
|
||||
#include "modules/modules_enabled.gen.h"
|
||||
|
||||
s.write('\t"{0}"'.format(icon_name))
|
||||
inline constexpr int default_theme_icons_count = {len(icons_names)};
|
||||
inline constexpr const char *default_theme_icons_sources[] = {{
|
||||
{icons_raw_str}
|
||||
}};
|
||||
|
||||
if fname != svg_icons[-1]:
|
||||
s.write(",")
|
||||
s.write("\n")
|
||||
|
||||
index += 1
|
||||
|
||||
s.write("};\n")
|
||||
|
||||
s.write("#endif\n")
|
||||
|
||||
with open(dst, "w", encoding="utf-8", newline="\n") as f:
|
||||
f.write(s.getvalue())
|
||||
inline constexpr const char *default_theme_icons_names[] = {{
|
||||
{icons_names_str}
|
||||
}};
|
||||
""")
|
||||
|
Loading…
x
Reference in New Issue
Block a user