Merge pull request #91624 from Repiteo/scons/native-generation

SCons: Generate all scripts natively, implement wrapper function
This commit is contained in:
Rémi Verschelde 2024-05-10 09:55:51 +02:00
commit 6fcdd24468
No known key found for this signature in database
GPG key ID: C3336907360768E1
8 changed files with 362 additions and 281 deletions

View file

@ -61,7 +61,6 @@ _helper_module("platform_methods", "platform_methods.py")
_helper_module("version", "version.py")
_helper_module("core.core_builders", "core/core_builders.py")
_helper_module("main.main_builders", "main/main_builders.py")
_helper_module("modules.modules_builders", "modules/modules_builders.py")
# Local
import methods
@ -69,7 +68,7 @@ import glsl_builders
import gles3_builders
import scu_builders
from methods import print_warning, print_error
from platform_methods import architectures, architecture_aliases, generate_export_icons
from platform_methods import architectures, architecture_aliases
if ARGUMENTS.get("target", "editor") == "editor":
_helper_module("editor.editor_builders", "editor/editor_builders.py")
@ -107,7 +106,6 @@ for x in sorted(glob.glob("platform/*")):
if os.path.exists(x + "/export/export.cpp"):
platform_exporters.append(platform_name)
generate_export_icons(x, platform_name)
if os.path.exists(x + "/api/api.cpp"):
platform_apis.append(platform_name)
if detect.can_build():
@ -428,7 +426,7 @@ for name, path in modules_detected.items():
sys.path.remove(path)
sys.modules.pop("config")
methods.write_modules(modules_detected)
env.modules_detected = modules_detected
# Update the environment again after all the module options are added.
opts.Update(env)
@ -544,7 +542,7 @@ env.Append(CFLAGS=env.get("cflags", "").split())
env.Append(LINKFLAGS=env.get("linkflags", "").split())
# Feature build profile
disabled_classes = []
env.disabled_classes = []
if env["build_profile"] != "":
print('Using feature build profile: "{}"'.format(env["build_profile"]))
import json
@ -552,7 +550,7 @@ if env["build_profile"] != "":
try:
ft = json.load(open(env["build_profile"]))
if "disabled_classes" in ft:
disabled_classes = ft["disabled_classes"]
env.disabled_classes = ft["disabled_classes"]
if "disabled_build_options" in ft:
dbo = ft["disabled_build_options"]
for c in dbo:
@ -560,7 +558,6 @@ if env["build_profile"] != "":
except:
print_error('Failed to open feature build profile: "{}"'.format(env["build_profile"]))
Exit(255)
methods.write_disabled_classes(disabled_classes)
# Platform specific flags.
# These can sometimes override default options.
@ -926,7 +923,7 @@ if env.editor_build:
print_error("Not all modules required by editor builds are enabled.")
Exit(255)
methods.generate_version_header(env.module_version_string)
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"]

View file

@ -4,44 +4,9 @@ Import("env")
import core_builders
import methods
env.core_sources = []
# Generate AES256 script encryption key
import os
txt = "0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0"
if "SCRIPT_AES256_ENCRYPTION_KEY" in os.environ:
key = os.environ["SCRIPT_AES256_ENCRYPTION_KEY"]
ec_valid = True
if len(key) != 64:
ec_valid = False
else:
txt = ""
for i in range(len(key) >> 1):
if i > 0:
txt += ","
txts = "0x" + key[i * 2 : i * 2 + 2]
try:
int(txts, 16)
except Exception:
ec_valid = False
txt += txts
if not ec_valid:
methods.print_error(
f'Invalid AES256 encryption key, not 64 hexadecimal characters: "{key}".\n'
"Unset 'SCRIPT_AES256_ENCRYPTION_KEY' in your environment "
"or make sure that it contains exactly 64 hexadecimal characters."
)
Exit(255)
script_encryption_key_contents = (
'#include "core/config/project_settings.h"\nuint8_t script_encryption_key[32]={' + txt + "};\n"
)
methods.write_file_if_needed("script_encryption_key.gen.cpp", script_encryption_key_contents)
env.core_sources = []
# Add required thirdparty code.
@ -193,8 +158,96 @@ env.core_sources += thirdparty_obj
# Godot source files
env.add_source_files(env.core_sources, "*.cpp")
env.add_source_files(env.core_sources, "script_encryption_key.gen.cpp")
env.add_source_files(env.core_sources, "version_hash.gen.cpp")
# Generate disabled classes
def disabled_class_builder(target, source, env):
with methods.generated_wrapper(target) as file:
for c in source[0].read():
cs = c.strip()
if cs != "":
file.write(f"#define ClassDB_Disable_{cs} 1")
env.CommandNoCache("disabled_classes.gen.h", env.Value(env.disabled_classes), env.Run(disabled_class_builder))
# Generate version info
def version_info_builder(target, source, env):
with methods.generated_wrapper(target) as file:
file.write(
"""\
#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
""".format(
**env.version_info
)
)
env.CommandNoCache("version_generated.gen.h", "#version.py", env.Run(version_info_builder))
# Generate version hash
def version_hash_builder(target, source, env):
with methods.generated_wrapper(target) 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
)
)
gen_hash = env.CommandNoCache(
"version_hash.gen.cpp", env.Value(env.version_info["git_hash"]), 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:
file.write(
f"""\
#include "core/config/project_settings.h"
uint8_t script_encryption_key[32] = {{
{source[0]}
}};"""
)
gdkey = os.environ.get("SCRIPT_AES256_ENCRYPTION_KEY", "0" * 64)
ec_valid = len(gdkey) == 64
if ec_valid:
try:
gdkey = ", ".join([str(int(f"{a}{b}", 16)) for a, b in zip(gdkey[0::2], gdkey[1::2])])
except Exception:
ec_valid = False
if not ec_valid:
methods.print_error(
f'Invalid AES256 encryption key, not 64 hexadecimal characters: "{gdkey}".\n'
"Unset `SCRIPT_AES256_ENCRYPTION_KEY` in your environment "
"or make sure that it contains exactly 64 hexadecimal characters."
)
Exit(255)
gen_encrypt = env.CommandNoCache("script_encryption_key.gen.cpp", env.Value(gdkey), env.Run(encryption_key_builder))
env.add_source_files(env.core_sources, gen_encrypt)
# Certificates
env.Depends(

View file

@ -10,40 +10,59 @@ import editor_builders
import methods
def _make_doc_data_class_path(to_path):
file_path = os.path.join(to_path, "doc_data_class_path.gen.h")
class_path_data = ""
class_path_data += "static const int _doc_data_class_path_count = " + str(len(env.doc_class_path)) + ";\n"
class_path_data += "struct _DocDataClassPath { const char* name; const char* path; };\n"
class_path_data += (
"static const _DocDataClassPath _doc_data_class_paths[" + str(len(env.doc_class_path) + 1) + "] = {\n"
)
for c in sorted(env.doc_class_path):
class_path_data += '\t{"' + c + '", "' + env.doc_class_path[c] + '"},\n'
class_path_data += "\t{nullptr, nullptr}\n"
class_path_data += "};\n"
methods.write_file_if_needed(file_path, class_path_data)
if env.editor_build:
# Generate doc data paths
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:
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}] = {{
{data}
{{nullptr, nullptr}},
}};
"""
)
env.CommandNoCache("doc_data_class_path.gen.h", env.Value(env.doc_class_path), env.Run(doc_data_class_path_builder))
# Register exporters
reg_exporters_inc = '#include "register_exporters.h"\n\n'
reg_exporters = "void register_exporters() {\n"
def register_exporters_builder(target, source, env):
platforms = source[0].read()
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:
file.write(
f"""\
#include "register_exporters.h"
{exp_inc}
void register_exporters() {{
{exp_reg}
}}
void register_exporter_types() {{
{exp_type}
}}
"""
)
gen_exporters = env.CommandNoCache(
"register_exporters.gen.cpp", env.Value(env.platform_exporters), env.Run(register_exporters_builder)
)
for e in env.platform_exporters:
# Add all .cpp files in export folder
env.add_source_files(env.editor_sources, "../platform/" + e + "/export/" + "*.cpp")
reg_exporters += "\tregister_" + e + "_exporter();\n"
reg_exporters_inc += '#include "platform/' + e + '/export/export.h"\n'
reg_exporters += "}\n\n"
reg_exporters += "void register_exporter_types() {\n"
for e in env.platform_exporters:
reg_exporters += "\tregister_" + e + "_exporter_types();\n"
reg_exporters += "}\n"
methods.write_file_if_needed("register_exporters.gen.cpp", reg_exporters_inc + reg_exporters)
env.add_source_files(env.editor_sources, f"../platform/{e}/export/*.cpp")
# Core API documentation.
docs = []
@ -61,8 +80,6 @@ if env.editor_build:
else:
docs += Glob(d + "/*.xml") # Custom.
_make_doc_data_class_path(env.Dir("#editor").abspath)
docs = sorted(docs)
env.Depends("#editor/doc_data_compressed.gen.h", docs)
env.CommandNoCache(
@ -115,7 +132,7 @@ if env.editor_build:
)
env.add_source_files(env.editor_sources, "*.cpp")
env.add_source_files(env.editor_sources, "register_exporters.gen.cpp")
env.add_source_files(env.editor_sources, gen_exporters)
SConscript("debugger/SCsub")
SConscript("export/SCsub")

View file

@ -3,13 +3,16 @@ import sys
import re
import glob
import subprocess
import contextlib
from collections import OrderedDict
from collections.abc import Mapping
from enum import Enum
from typing import Iterator
from typing import Generator, Optional
from io import TextIOWrapper, StringIO
from pathlib import Path
from os.path import normpath, basename
# Get the "Godot" folder name ahead of time
base_folder_path = str(os.path.abspath(Path(__file__).parent)) + "/"
base_folder_only = os.path.basename(os.path.normpath(base_folder_path))
@ -277,79 +280,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()
@ -465,63 +395,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
@ -1649,3 +1522,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")

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python
import modules_builders
import methods
import os
Import("env")
@ -12,13 +12,49 @@ env_modules.Append(CPPDEFINES=["GODOT_MODULE"])
Export("env_modules")
# Header with MODULE_*_ENABLED defines.
env.Depends("modules_enabled.gen.h", Value(env.module_list))
env.CommandNoCache(
"modules_enabled.gen.h",
Value(env.module_list),
env.Run(modules_builders.generate_modules_enabled),
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_init = "\n".join(
[f"#ifdef MODULE_{n.upper()}_ENABLED\n\tinitialize_{n}_module(p_level);\n#endif" for n 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()]
)
with methods.generated_wrapper(target) as file:
file.write(
f"""\
#include "register_module_types.h"
#include "modules/modules_enabled.gen.h"
{mod_inc}
void initialize_modules(ModuleInitializationLevel p_level) {{
{mod_init}
}}
void uninitialize_modules(ModuleInitializationLevel p_level) {{
{mod_uninit}
}}
"""
)
register_module_types = env.CommandNoCache(
"register_module_types.gen.cpp", env.Value(env.modules_detected), env.Run(register_module_types_builder)
)
# 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():
file.write(f"#define MODULE_{module.upper()}_ENABLED\n")
env.CommandNoCache("modules_enabled.gen.h", env.Value(env.module_list), env.Run(modules_enabled_builder))
vs_sources = []
@ -47,18 +83,19 @@ for name, path in env.module_list.items():
# Generate header to be included in `tests/test_main.cpp` to run module-specific tests.
if env["tests"]:
env.Depends("modules_tests.gen.h", test_headers)
env.CommandNoCache(
"modules_tests.gen.h",
test_headers,
env.Run(modules_builders.generate_modules_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("\\", "/")))
env.CommandNoCache("modules_tests.gen.h", test_headers, env.Run(modules_tests_builder))
# libmodules.a with only register_module_types.
# Must be last so that all libmodule_<name>.a libraries are on the right side
# in the linker command.
env.modules_sources = []
env_modules.add_source_files(env.modules_sources, "register_module_types.gen.cpp")
env_modules.add_source_files(env.modules_sources, register_module_types)
lib = env_modules.add_library("modules", env.modules_sources)
env.Prepend(LIBS=[lib])
if env["vsproj"]:

View file

@ -1,15 +0,0 @@
"""Functions used to generate source files during build time"""
def generate_modules_enabled(target, source, env):
with open(target[0].path, "w", encoding="utf-8", newline="\n") as f:
for module in env.module_list:
f.write("#define %s\n" % ("MODULE_" + module.upper() + "_ENABLED"))
def generate_modules_tests(target, source, env):
import os
with open(target[0].path, "w", encoding="utf-8", newline="\n") as f:
for header in source:
f.write('#include "%s"\n' % (os.path.normpath(header.path)))

View file

@ -1,27 +1,64 @@
#!/usr/bin/env python
import methods
from glob import glob
from pathlib import Path
Import("env")
env.platform_sources = []
# Register platform-exclusive APIs
reg_apis_inc = '#include "register_platform_apis.h"\n'
reg_apis = "void register_platform_apis() {\n"
unreg_apis = "void unregister_platform_apis() {\n"
for platform in env.platform_apis:
platform_dir = env.Dir(platform)
env.add_source_files(env.platform_sources, platform + "/api/api.cpp")
reg_apis += "\tregister_" + platform + "_api();\n"
unreg_apis += "\tunregister_" + platform + "_api();\n"
reg_apis_inc += '#include "' + platform + '/api/api.h"\n'
reg_apis_inc += "\n"
reg_apis += "}\n\n"
unreg_apis += "}\n"
methods.write_file_if_needed("register_platform_apis.gen.cpp", reg_apis_inc + reg_apis + unreg_apis)
env.add_source_files(env.platform_sources, "register_platform_apis.gen.cpp")
# Generate export icons
def export_icon_builder(target, source, env):
src_path = Path(str(source[0]))
src_name = src_path.stem
platform = src_path.parent.parent.stem
with open(str(source[0]), "rb") as file:
svg = "".join([f"\\{hex(x)[1:]}" for x in file.read()])
with methods.generated_wrapper(target, prefix=platform) as file:
file.write(
f"""\
static const char *_{platform}_{src_name}_svg = "{svg}";
"""
)
for platform in env.platform_exporters:
for path in glob(f"{platform}/export/*.svg"):
env.CommandNoCache(path.replace(".svg", "_svg.gen.h"), path, env.Run(export_icon_builder))
# Register platform-exclusive APIs
def register_platform_apis_builder(target, source, env):
platforms = source[0].read()
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:
file.write(
f"""\
#include "register_platform_apis.h"
{api_inc}
void register_platform_apis() {{
{api_reg}
}}
void unregister_platform_apis() {{
{api_unreg}
}}
"""
)
register_platform_apis = env.CommandNoCache(
"register_platform_apis.gen.cpp", env.Value(env.platform_apis), env.Run(register_platform_apis_builder)
)
env.add_source_files(env.platform_sources, register_platform_apis)
for platform in env.platform_apis:
env.add_source_files(env.platform_sources, f"{platform}/api/api.cpp")
lib = env.add_library("platform", env.platform_sources)
env.Prepend(LIBS=[lib])

View file

@ -43,33 +43,6 @@ def detect_arch():
return "x86_64"
def generate_export_icons(platform_path, platform_name):
"""
Generate headers for logo and run icon for the export plugin.
"""
export_path = platform_path + "/export"
svg_names = []
if os.path.isfile(export_path + "/logo.svg"):
svg_names.append("logo")
if os.path.isfile(export_path + "/run_icon.svg"):
svg_names.append("run_icon")
for name in svg_names:
with open(export_path + "/" + name + ".svg", "rb") as svgf:
b = svgf.read(1)
svg_str = " /* AUTOGENERATED FILE, DO NOT EDIT */ \n"
svg_str += " static const char *_" + platform_name + "_" + name + '_svg = "'
while len(b) == 1:
svg_str += "\\" + hex(ord(b))[1:]
b = svgf.read(1)
svg_str += '";\n'
wf = export_path + "/" + name + "_svg.gen.h"
methods.write_file_if_needed(wf, svg_str)
def get_build_version(short):
import version