aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore76
-rw-r--r--.gitmodules3
-rw-r--r--SConstruct277
-rw-r--r--deps/SCsub45
m---------deps/lexy0
-rw-r--r--include/openvic-dataloader/ovscript/.gitkeep0
-rw-r--r--include/openvic-dataloader/v2script/Parser.hpp27
-rw-r--r--scripts/build/cache.py127
-rw-r--r--scripts/build/glob_recursive.py15
-rw-r--r--scripts/build/option_handler.py39
-rw-r--r--src/headless/main.cpp3
-rw-r--r--src/openvic-dataloader/ovscript/.gitkeep0
-rw-r--r--src/openvic-dataloader/v2script/Grammar.cpp74
-rw-r--r--tools/linux.py35
-rw-r--r--tools/macos.py51
-rw-r--r--tools/macos_osxcross.py29
-rw-r--r--tools/my_spawn.py53
-rw-r--r--tools/targets.py93
-rw-r--r--tools/windows.py73
19 files changed, 1020 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d8b9d7f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,76 @@
+# Prerequisites
+*.d
+
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+*.smod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+
+# clangd
+.cache/*
+
+# VSCode
+.vscode/*
+!.vscode/launch.json
+!.vscode/tasks.json
+
+# Visual Studio
+.vs/
+
+# Godot 4+ specific ignores
+.godot/
+game/bin/openvic/*
+.sconsign*.dblite
+
+# Binaries
+*.o
+*.os
+*.so
+*.obj
+*.bc
+*.pyc
+*.dblite
+*.pdb
+*.lib
+bin/*
+*.config
+*.creator
+*.creator.user
+*.files
+*.includes
+*.idb
+*.exp
+
+# Build configuarion.
+/custom.py
+
+# MacOS stuff
+.DS_Store
+
+*.translation
+!game/common/map/*.obj \ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..af9d8f4
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "deps/lexy"]
+ path = deps/lexy
+ url = https://github.com/foonathan/lexy
diff --git a/SConstruct b/SConstruct
new file mode 100644
index 0000000..423994d
--- /dev/null
+++ b/SConstruct
@@ -0,0 +1,277 @@
+#!/usr/bin/env python
+
+# This file is heavily based on https://github.com/godotengine/godot-cpp/blob/8155f35b29b4b08bc54b2eb0c57e1e9effe9f093/SConstruct
+import os
+import platform
+import sys
+import subprocess
+from glob import glob
+from pathlib import Path
+
+import SCons
+
+# Local
+from scripts.build.option_handler import OptionsClass
+from scripts.build.glob_recursive import GlobRecursive
+from scripts.build.cache import show_progress
+
+# Try to detect the host platform automatically.
+# This is used if no `platform` argument is passed
+if sys.platform.startswith("linux"):
+ default_platform = "linux"
+elif sys.platform == "darwin":
+ default_platform = "macos"
+elif sys.platform == "win32" or sys.platform == "msys":
+ default_platform = "windows"
+elif ARGUMENTS.get("platform", ""):
+ default_platform = ARGUMENTS.get("platform")
+else:
+ raise ValueError("Could not detect platform automatically, please specify with platform=<platform>")
+
+is_standalone = SCons.Script.sconscript_reading == 1
+
+try:
+ Import("env")
+ old_env = env
+ env = old_env.Clone()
+except:
+ # Default tools with no platform defaults to gnu toolchain.
+ # We apply platform specific toolchains via our custom tools.
+ env = Environment(tools=["default"], PLATFORM="")
+ old_env = env
+
+env.PrependENVPath("PATH", os.getenv("PATH"))
+
+# Default num_jobs to local cpu count if not user specified.
+# SCons has a peculiarity where user-specified options won't be overridden
+# by SetOption, so we can rely on this to know if we should use our default.
+initial_num_jobs = env.GetOption("num_jobs")
+altered_num_jobs = initial_num_jobs + 1
+env.SetOption("num_jobs", altered_num_jobs)
+if env.GetOption("num_jobs") == altered_num_jobs:
+ cpu_count = os.cpu_count()
+ if cpu_count is None:
+ print("Couldn't auto-detect CPU count to configure build parallelism. Specify it with the -j argument.")
+ else:
+ safer_cpu_count = cpu_count if cpu_count <= 4 else cpu_count - 1
+ print(
+ "Auto-detected %d CPU cores available for build parallelism. Using %d cores by default. You can override it with the -j argument."
+ % (cpu_count, safer_cpu_count)
+ )
+ env.SetOption("num_jobs", safer_cpu_count)
+
+opts = OptionsClass(ARGUMENTS)
+
+platforms = ("linux", "macos", "windows", "android", "ios", "javascript")
+unsupported_known_platforms = ("android", "ios", "javascript")
+opts.Add(
+ EnumVariable(
+ key="platform",
+ help="Target platform",
+ default=env.get("platform", default_platform),
+ allowed_values=platforms,
+ ignorecase=2,
+ )
+)
+
+opts.Add(
+ EnumVariable(
+ key="target",
+ help="Compilation target",
+ default=env.get("target", "template_debug"),
+ allowed_values=("editor", "template_release", "template_debug"),
+ )
+)
+
+opts.Add(BoolVariable(key="build_ovdl_library", help="Build the openvic dataloader library.", default=env.get("build_ovdl_library", not is_standalone)))
+opts.Add(
+ EnumVariable(
+ key="precision",
+ help="Set the floating-point precision level",
+ default=env.get("precision", "single"),
+ allowed_values=("single", "double"),
+ )
+)
+
+# Add platform options
+tools = {}
+for pl in set(platforms) - set(unsupported_known_platforms):
+ tool = Tool(pl, toolpath=["tools"])
+ if hasattr(tool, "options"):
+ tool.options(opts)
+ tools[pl] = tool
+
+# CPU architecture options.
+architecture_array = ["", "universal", "x86_32", "x86_64", "arm32", "arm64", "rv64", "ppc32", "ppc64", "wasm32"]
+architecture_aliases = {
+ "x64": "x86_64",
+ "amd64": "x86_64",
+ "armv7": "arm32",
+ "armv8": "arm64",
+ "arm64v8": "arm64",
+ "aarch64": "arm64",
+ "rv": "rv64",
+ "riscv": "rv64",
+ "riscv64": "rv64",
+ "ppcle": "ppc32",
+ "ppc": "ppc32",
+ "ppc64le": "ppc64",
+}
+opts.Add(
+ EnumVariable(
+ key="arch",
+ help="CPU architecture",
+ default=env.get("arch", ""),
+ allowed_values=architecture_array,
+ map=architecture_aliases,
+ )
+)
+
+opts.Add(BoolVariable("build_ovdl_headless", "Build the openvic dataloader headless executable", is_standalone))
+
+opts.Add(BoolVariable("compiledb", "Generate compilation DB (`compile_commands.json`) for external tools", False))
+opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", False))
+opts.Add(BoolVariable("intermediate_delete", "Enables automatically deleting unassociated intermediate binary files.", True))
+opts.Add(BoolVariable("progress", "Show a progress indicator during compilation", True))
+
+# Targets flags tool (optimizations, debug symbols)
+target_tool = Tool("targets", toolpath=["tools"])
+target_tool.options(opts)
+
+# Custom options and profile flags.
+opts.Make(["custom.py"])
+opts.Finalize(env)
+Help(opts.GenerateHelpText(env))
+
+if env["platform"] in unsupported_known_platforms:
+ print("Unsupported platform: " + env["platform"]+". Only supports " + ", ".join(set(platforms) - set(unsupported_known_platforms)))
+ Exit()
+
+# Process CPU architecture argument.
+if env["arch"] == "":
+ # No architecture specified. Default to arm64 if building for Android,
+ # universal if building for macOS or iOS, wasm32 if building for web,
+ # otherwise default to the host architecture.
+ if env["platform"] in ["macos", "ios"]:
+ env["arch"] = "universal"
+ elif env["platform"] == "android":
+ env["arch"] = "arm64"
+ elif env["platform"] == "javascript":
+ env["arch"] = "wasm32"
+ else:
+ host_machine = platform.machine().lower()
+ if host_machine in architecture_array:
+ env["arch"] = host_machine
+ elif host_machine in architecture_aliases.keys():
+ env["arch"] = architecture_aliases[host_machine]
+ elif "86" in host_machine:
+ # Catches x86, i386, i486, i586, i686, etc.
+ env["arch"] = "x86_32"
+ else:
+ print("Unsupported CPU architecture: " + host_machine)
+ Exit()
+
+tool = Tool(env["platform"], toolpath=["tools"])
+
+if tool is None or not tool.exists(env):
+ raise ValueError("Required toolchain not found for platform " + env["platform"])
+
+tool.generate(env)
+target_tool.generate(env)
+
+print("Building for architecture " + env["arch"] + " on platform " + env["platform"])
+
+# Require C++20
+if env.get("is_msvc", False):
+ env.Append(CXXFLAGS=["/std:c++20"])
+else:
+ env.Append(CXXFLAGS=["-std=c++20"])
+
+if env["precision"] == "double":
+ env.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"])
+
+scons_cache_path = os.environ.get("SCONS_CACHE")
+if scons_cache_path != None:
+ CacheDir(scons_cache_path)
+ print("Scons cache enabled... (path: '" + scons_cache_path + "')")
+
+if env["compiledb"]:
+ # Generating the compilation DB (`compile_commands.json`) requires SCons 4.0.0 or later.
+ from SCons import __version__ as scons_raw_version
+
+ scons_ver = env._get_major_minor_revision(scons_raw_version)
+
+ if scons_ver < (4, 0, 0):
+ print("The `compiledb=yes` option requires SCons 4.0 or later, but your version is %s." % scons_raw_version)
+ Exit(255)
+
+ env.Tool("compilation_db")
+ env.Alias("compiledb", env.CompilationDatabase())
+
+Export("env")
+env.GlobRecursive = GlobRecursive
+
+SConscript("deps/SCsub")
+
+# For the reference:
+# - CCFLAGS are compilation flags shared between C and C++
+# - CFLAGS are for C-specific compilation flags
+# - CXXFLAGS are for C++-specific compilation flags
+# - CPPFLAGS are for pre-processor flags
+# - CPPDEFINES are for pre-processor defines
+# - LINKFLAGS are for linking flags
+
+# tweak this if you want to use different folders, or more folders, to store your source code in.
+paths = ["include", "src/openvic-dataloader"]
+env.Append(CPPPATH=[[env.Dir(p) for p in paths]])
+sources = GlobRecursive("*.cpp", paths)
+env.dataloader_sources = sources
+
+suffix = ".{}.{}".format(env["platform"], env["target"])
+if env.dev_build:
+ suffix += ".dev"
+if env["precision"] == "double":
+ suffix += ".double"
+suffix += "." + env["arch"]
+
+# Expose it when included from another project
+env["suffix"] = suffix
+
+library = None
+env["OBJSUFFIX"] = suffix + env["OBJSUFFIX"]
+library_name = "libopenvic-dataloader{}{}".format(suffix, env["LIBSUFFIX"])
+
+if env["build_ovdl_library"]:
+ library = env.StaticLibrary(target=env.File("bin/%s" % library_name), source=sources)
+ Default(library)
+
+env.Append(LIBPATH=[env.Dir("bin")])
+
+if library != None or os.path.exists("bin/%s" % library_name):
+ env.Append(LIBS=[library_name])
+
+headless_program = None
+env["PROGSUFFIX"] = suffix + env["PROGSUFFIX"]
+
+if env["build_ovdl_headless"]:
+ headless_name = "openvic-dataloader"
+ headless_env = env.Clone()
+ headless_path = ["src/headless"]
+ headless_env.Append(CPPDEFINES=["OPENVIC_DATALOADER_HEADLESS"])
+ headless_env.Append(CPPPATH=[headless_env.Dir(headless_path)])
+ headless_env.headless_sources = GlobRecursive("*.cpp", headless_path)
+ if not env["build_ovdl_library"]:
+ headless_env.headless_sources += sources
+ headless_program = env.Program(
+ target="bin/%s" % headless_name,
+ source=headless_env.headless_sources,
+ PROGSUFFIX=".headless" + env["PROGSUFFIX"]
+ )
+ Default(headless_program)
+
+
+if "env" in locals():
+ # FIXME: This method mixes both cosmetic progress stuff and cache handling...
+ show_progress(env)
+
+Return("env") \ No newline at end of file
diff --git a/deps/SCsub b/deps/SCsub
new file mode 100644
index 0000000..16ee889
--- /dev/null
+++ b/deps/SCsub
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+
+Import("env")
+
+def build_lexy(env):
+ env.Append(CPPDEFINES=["LEXY_HAS_UNICODE_DATABASE=1"])
+ lexy_env = env.Clone()
+
+ # Require C++20
+ if lexy_env.get("is_msvc", False):
+ lexy_env.Append(CXXFLAGS=["/std:c++20"])
+
+ lexy_env.Append(CXXFLAGS=["/WX", "/W3", "/D", "_CRT_SECURE_NO_WARNINGS"])
+ if not lexy_env.get("use_clang_cl"):
+ lexy_env.Append(CXXFLAGS=["/wd5105"])
+ else:
+ lexy_env.Append(CXXFLAGS=["-std=c++20"])
+
+ lexy_env.Append(CXXFLAGS=["-pedantic-errors", "-Werror", "-Wall", "-Wextra", "-Wconversion", "-Wsign-conversion"])
+ if lexy_env.get("use_llvm"):
+ lexy_env.Append(CXXFLAGS=["-Wno-shift-op-parentheses", "-Wno-parentheses-equality"])
+ else:
+ lexy_env.Append(CXXFLAGS=[
+ "-Wno-parentheses", "-Wno-unused-local-typedefs", "-Wno-array-bounds", "-Wno-maybe-uninitialized", "-Wno-restrict"
+ ])
+
+ paths = ["lexy/include", "lexy/src"]
+ lexy_env.Append(CPPPATH=[[lexy_env.Dir(p) for p in paths]])
+ sources = env.GlobRecursive("*.cpp", paths)
+ env.lexy_sources = sources
+ library_name = "liblexy_file" + env["LIBSUFFIX"]
+ library = lexy_env.StaticLibrary(target="lexy/src/" + library_name, source=sources)
+ Default(library)
+
+ env.Append(CPPPATH=[env.Dir("lexy/include")])
+ if env.get("is_msvc", False):
+ env.Append(CXXFLAGS=["/external:I", env.Dir("lexy/include"), "/external:W0"])
+ else:
+ env.Append(CXXFLAGS=["-isystem", env.Dir("lexy/include")])
+ env.Append(CXXFLAGS=[""])
+ env.Append(LIBPATH=[env.Dir("lexy/src")])
+ env.Append(LIBS=[library_name])
+
+
+build_lexy(env) \ No newline at end of file
diff --git a/deps/lexy b/deps/lexy
new file mode 160000
+Subproject 5e2601b3de57e979b5d23b8936cf040eb228fb9
diff --git a/include/openvic-dataloader/ovscript/.gitkeep b/include/openvic-dataloader/ovscript/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/include/openvic-dataloader/ovscript/.gitkeep
diff --git a/include/openvic-dataloader/v2script/Parser.hpp b/include/openvic-dataloader/v2script/Parser.hpp
new file mode 100644
index 0000000..53aab90
--- /dev/null
+++ b/include/openvic-dataloader/v2script/Parser.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <cstddef>
+#include <cstdio>
+#include <ostream>
+
+namespace ovdl::v2script {
+ class Parser {
+ public:
+ static Parser from_buffer(char8_t* data, std::size_t size);
+ static Parser from_buffer(char8_t* start, char8_t* end);
+ static Parser from_file(const char8_t* path);
+
+ void set_error_log_to_stderr();
+ void set_error_log_path(const char8_t* path);
+ void set_error_log_to(std::basic_ostream<char8_t> stream);
+ void set_error_log_to(std::FILE* file);
+
+ bool parse();
+
+ bool has_error();
+ bool has_warning();
+
+ private:
+ Parser();
+ };
+} \ No newline at end of file
diff --git a/scripts/build/cache.py b/scripts/build/cache.py
new file mode 100644
index 0000000..d48b0e0
--- /dev/null
+++ b/scripts/build/cache.py
@@ -0,0 +1,127 @@
+# Copied from https://github.com/godotengine/godot/blob/c3b0a92c3cd9a219c1b1776b48c147f1d0602f07/methods.py#L1049-L1172
+def show_progress(env):
+ import os
+ import sys
+ import glob
+ from SCons.Script import Progress, Command, AlwaysBuild
+
+ screen = sys.stdout
+ # Progress reporting is not available in non-TTY environments since it
+ # messes with the output (for example, when writing to a file)
+ show_progress = env["progress"] and sys.stdout.isatty()
+ node_count = 0
+ node_count_max = 0
+ node_count_interval = 1
+ node_count_fname = str(env.Dir("#")) + "/.scons_node_count"
+
+ import time, math
+
+ class cache_progress:
+ # The default is 1 GB cache and 12 hours half life
+ def __init__(self, path=None, limit=1073741824, half_life=43200):
+ self.path = path
+ self.limit = limit
+ self.exponent_scale = math.log(2) / half_life
+ if env["verbose"] and path != None:
+ screen.write(
+ "Current cache limit is {} (used: {})\n".format(
+ self.convert_size(limit), self.convert_size(self.get_size(path))
+ )
+ )
+ self.delete(self.file_list())
+
+ def __call__(self, node, *args, **kw):
+ nonlocal node_count, node_count_max, node_count_interval, node_count_fname, show_progress
+ if show_progress:
+ # Print the progress percentage
+ node_count += node_count_interval
+ if node_count_max > 0 and node_count <= node_count_max:
+ screen.write("\r[%3d%%] " % (node_count * 100 / node_count_max))
+ screen.flush()
+ elif node_count_max > 0 and node_count > node_count_max:
+ screen.write("\r[100%] ")
+ screen.flush()
+ else:
+ screen.write("\r[Initial build] ")
+ screen.flush()
+
+ def delete(self, files):
+ if len(files) == 0:
+ return
+ if env["verbose"]:
+ # Utter something
+ screen.write("\rPurging %d %s from cache...\n" % (len(files), len(files) > 1 and "files" or "file"))
+ [os.remove(f) for f in files]
+
+ def file_list(self):
+ if self.path is None:
+ # Nothing to do
+ return []
+ # Gather a list of (filename, (size, atime)) within the
+ # cache directory
+ file_stat = [(x, os.stat(x)[6:8]) for x in glob.glob(os.path.join(self.path, "*", "*"))]
+ if file_stat == []:
+ # Nothing to do
+ return []
+ # Weight the cache files by size (assumed to be roughly
+ # proportional to the recompilation time) times an exponential
+ # decay since the ctime, and return a list with the entries
+ # (filename, size, weight).
+ current_time = time.time()
+ file_stat = [(x[0], x[1][0], (current_time - x[1][1])) for x in file_stat]
+ # Sort by the most recently accessed files (most sensible to keep) first
+ file_stat.sort(key=lambda x: x[2])
+ # Search for the first entry where the storage limit is
+ # reached
+ sum, mark = 0, None
+ for i, x in enumerate(file_stat):
+ sum += x[1]
+ if sum > self.limit:
+ mark = i
+ break
+ if mark is None:
+ return []
+ else:
+ return [x[0] for x in file_stat[mark:]]
+
+ def convert_size(self, size_bytes):
+ if size_bytes == 0:
+ return "0 bytes"
+ size_name = ("bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
+ i = int(math.floor(math.log(size_bytes, 1024)))
+ p = math.pow(1024, i)
+ s = round(size_bytes / p, 2)
+ return "%s %s" % (int(s) if i == 0 else s, size_name[i])
+
+ def get_size(self, start_path="."):
+ total_size = 0
+ for dirpath, dirnames, filenames in os.walk(start_path):
+ for f in filenames:
+ fp = os.path.join(dirpath, f)
+ total_size += os.path.getsize(fp)
+ return total_size
+
+ def progress_finish(target, source, env):
+ nonlocal node_count, progressor
+ try:
+ with open(node_count_fname, "w") as f:
+ f.write("%d\n" % node_count)
+ progressor.delete(progressor.file_list())
+ except Exception:
+ pass
+
+ try:
+ with open(node_count_fname) as f:
+ node_count_max = int(f.readline())
+ except Exception:
+ pass
+
+ cache_directory = os.environ.get("SCONS_CACHE")
+ # Simple cache pruning, attached to SCons' progress callback. Trim the
+ # cache directory to a size not larger than cache_limit.
+ cache_limit = float(os.getenv("SCONS_CACHE_LIMIT", 1024)) * 1024 * 1024
+ progressor = cache_progress(cache_directory, cache_limit)
+ Progress(progressor, interval=node_count_interval)
+
+ progress_finish_command = Command("progress_finish", [], progress_finish)
+ AlwaysBuild(progress_finish_command)
diff --git a/scripts/build/glob_recursive.py b/scripts/build/glob_recursive.py
new file mode 100644
index 0000000..db6eb80
--- /dev/null
+++ b/scripts/build/glob_recursive.py
@@ -0,0 +1,15 @@
+def GlobRecursive(pattern, nodes=['.']):
+ import SCons
+ import glob
+ fs = SCons.Node.FS.get_default_fs()
+ Glob = fs.Glob
+
+ results = []
+ for node in nodes:
+ nnodes = []
+ for f in Glob(str(node) + '/*', source=True):
+ if type(f) is SCons.Node.FS.Dir:
+ nnodes.append(f)
+ results += GlobRecursive(pattern, nnodes)
+ results += Glob(str(node) + '/' + pattern, source=True)
+ return results
diff --git a/scripts/build/option_handler.py b/scripts/build/option_handler.py
new file mode 100644
index 0000000..3cebc1a
--- /dev/null
+++ b/scripts/build/option_handler.py
@@ -0,0 +1,39 @@
+from typing import Tuple, Iterable
+from SCons.Variables import Variables
+
+class OptionsClass:
+ def __init__(self, args):
+ self.opts = None
+ self.opt_list = []
+ self.args = args
+ self.saved_args = args.copy()
+
+ def Add(self, variableOrKey, *argv, **kwarg):
+
+ self.opt_list.append([variableOrKey, argv, kwarg])
+ # Neccessary to have our own build options without errors
+ if isinstance(variableOrKey, str):
+ self.args.pop(variableOrKey, True)
+ else:
+ self.args.pop(variableOrKey[0], True)
+
+ def Make(self, customs : Iterable[str]):
+ self.args = self.saved_args
+ profile = self.args.get("profile", "")
+ if profile:
+ if os.path.isfile(profile):
+ customs.append(profile)
+ elif os.path.isfile(profile + ".py"):
+ customs.append(profile + ".py")
+ self.opts = Variables(customs, self.args)
+ for opt in self.opt_list:
+ if opt[1] == None and opt[2] == None:
+ self.opts.Add(opt[0])
+ else:
+ self.opts.Add(opt[0], *opt[1], **opt[2])
+
+ def Finalize(self, env):
+ self.opts.Update(env)
+
+ def GenerateHelpText(self, env):
+ return self.opts.GenerateHelpText(env)
diff --git a/src/headless/main.cpp b/src/headless/main.cpp
new file mode 100644
index 0000000..ffc6dab
--- /dev/null
+++ b/src/headless/main.cpp
@@ -0,0 +1,3 @@
+int main() {
+ return 0;
+} \ No newline at end of file
diff --git a/src/openvic-dataloader/ovscript/.gitkeep b/src/openvic-dataloader/ovscript/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/openvic-dataloader/ovscript/.gitkeep
diff --git a/src/openvic-dataloader/v2script/Grammar.cpp b/src/openvic-dataloader/v2script/Grammar.cpp
new file mode 100644
index 0000000..ec9fac2
--- /dev/null
+++ b/src/openvic-dataloader/v2script/Grammar.cpp
@@ -0,0 +1,74 @@
+#include <lexy/dsl.hpp>
+#include <openvic-dataloader/v2script/Parser.hpp>
+
+using namespace ovdl::v2script;
+
+// Node Definitions //
+namespace dsl = lexy::dsl;
+
+namespace ovdl::v2script::nodes {
+ struct StatementListBlock;
+
+ static constexpr auto whitespace_specifier = dsl::code_point.range<0x09, 0x0A>() / dsl::lit_cp<0x0D> / dsl::lit_cp<0x20>;
+ static constexpr auto comment_specifier = LEXY_LIT("#") >> dsl::until(dsl::newline).or_eof();
+
+ static constexpr auto data_specifier =
+ dsl::ascii::alpha_digit_underscore /
+ dsl::code_point.range<0x25, 0x27>() / dsl::lit_cp<0x2B> / dsl::code_point.range<0x2D, 0x2E>() /
+ dsl::lit_cp<0x3A> /
+ dsl::lit_cp<0x8A> / dsl::lit_cp<0x8C> / dsl::lit_cp<0x8E> /
+ dsl::lit_cp<0x92> / dsl::lit_cp<0x9A> / dsl::lit_cp<0x9C> / dsl::code_point.range<0x9E, 0x9F>() /
+ dsl::code_point.range<0xC0, 0xD6>() / dsl::code_point.range<0xD8, 0xF6>() / dsl::code_point.range<0xF8, 0xFF>();
+
+ static constexpr auto data_char_class = LEXY_CHAR_CLASS("DataSpecifier", data_specifier);
+
+ struct Identifier {
+ static constexpr auto rule = dsl::identifier(data_char_class);
+ };
+
+ struct StringExpression {
+ static constexpr auto escaped_symbols = lexy::symbol_table<char> //
+ .map<'"'>('"')
+ .map<'\''>('\'')
+ .map<'\\'>('\\')
+ .map<'/'>('/')
+ .map<'b'>('\b')
+ .map<'f'>('\f')
+ .map<'n'>('\n')
+ .map<'r'>('\r')
+ .map<'t'>('\t');
+ static constexpr auto rule = [] {
+ // Arbitrary code points that aren't control characters.
+ auto c = -dsl::unicode::control;
+
+ // Escape sequences start with a backlash.
+ // They either map one of the symbols,
+ // or a Unicode code point of the form uXXXX.
+ auto escape = dsl::backslash_escape //
+ .symbol<escaped_symbols>()
+ .rule(dsl::lit_c<'u'> >> dsl::code_point_id<4>);
+ return dsl::quoted(c, escape);
+ }();
+ };
+
+ struct AssignmentStatement {
+ static constexpr auto rule = dsl::p<Identifier> >>
+ (dsl::equal_sign >>
+ (dsl::p<Identifier> | dsl::p<StringExpression> | dsl::recurse_branch<StatementListBlock>) |
+ dsl::else_ >> dsl::return_);
+ };
+
+ struct StatementListBlock {
+ static constexpr auto rule =
+ dsl::curly_bracketed.open() >>
+ dsl::opt(dsl::list(dsl::p<AssignmentStatement>)) + dsl::opt(dsl::semicolon) +
+ dsl::curly_bracketed.close();
+ };
+
+ struct File {
+ // Allow arbitrary spaces between individual tokens.
+ static constexpr auto whitespace = whitespace_specifier | comment_specifier;
+
+ static constexpr auto rule = dsl::terminator(dsl::eof).list(dsl::p<AssignmentStatement>);
+ };
+}
diff --git a/tools/linux.py b/tools/linux.py
new file mode 100644
index 0000000..0a2c35c
--- /dev/null
+++ b/tools/linux.py
@@ -0,0 +1,35 @@
+# Copied from https://github.com/godotengine/godot-cpp/blob/2bf983e6382f5236948f7740faf130a3568f9dd0/tools/linux.py
+from SCons.Variables import *
+from SCons.Tool import clang, clangxx
+
+
+def options(opts):
+ opts.Add(BoolVariable("use_llvm", "Use the LLVM compiler - only effective when targeting Linux", False))
+
+
+def exists(env):
+ return True
+
+
+def generate(env):
+ if env["use_llvm"]:
+ clang.generate(env)
+ clangxx.generate(env)
+
+ env.Append(CCFLAGS=["-fPIC", "-Wwrite-strings"])
+ env.Append(LINKFLAGS=["-Wl,-R,'$$ORIGIN'"])
+
+ if env["arch"] == "x86_64":
+ # -m64 and -m32 are x86-specific already, but it doesn't hurt to
+ # be clear and also specify -march=x86-64. Similar with 32-bit.
+ env.Append(CCFLAGS=["-m64", "-march=x86-64"])
+ env.Append(LINKFLAGS=["-m64", "-march=x86-64"])
+ elif env["arch"] == "x86_32":
+ env.Append(CCFLAGS=["-m32", "-march=i686"])
+ env.Append(LINKFLAGS=["-m32", "-march=i686"])
+ elif env["arch"] == "arm64":
+ env.Append(CCFLAGS=["-march=armv8-a"])
+ env.Append(LINKFLAGS=["-march=armv8-a"])
+ elif env["arch"] == "rv64":
+ env.Append(CCFLAGS=["-march=rv64gc"])
+ env.Append(LINKFLAGS=["-march=rv64gc"])
diff --git a/tools/macos.py b/tools/macos.py
new file mode 100644
index 0000000..f0fb81a
--- /dev/null
+++ b/tools/macos.py
@@ -0,0 +1,51 @@
+# Copied from https://github.com/godotengine/godot-cpp/blob/2bf983e6382f5236948f7740faf130a3568f9dd0/tools/macos.py
+import os
+import sys
+import macos_osxcross
+
+
+def options(opts):
+ opts.Add("macos_deployment_target", "macOS deployment target", "default")
+ opts.Add("macos_sdk_path", "macOS SDK path", "")
+ macos_osxcross.options(opts)
+
+
+def exists(env):
+ return sys.platform == "darwin" or macos_osxcross.exists(env)
+
+
+def generate(env):
+ if env["arch"] not in ("universal", "arm64", "x86_64"):
+ print("Only universal, arm64, and x86_64 are supported on macOS. Exiting.")
+ Exit()
+
+ if sys.platform == "darwin":
+ # Use clang on macOS by default
+ env["CXX"] = "clang++"
+ env["CC"] = "clang"
+ else:
+ # Use osxcross
+ macos_osxcross.generate(env)
+
+ if env["arch"] == "universal":
+ env.Append(LINKFLAGS=["-arch", "x86_64", "-arch", "arm64"])
+ env.Append(CCFLAGS=["-arch", "x86_64", "-arch", "arm64"])
+ else:
+ env.Append(LINKFLAGS=["-arch", env["arch"]])
+ env.Append(CCFLAGS=["-arch", env["arch"]])
+
+ if env["macos_deployment_target"] != "default":
+ env.Append(CCFLAGS=["-mmacosx-version-min=" + env["macos_deployment_target"]])
+ env.Append(LINKFLAGS=["-mmacosx-version-min=" + env["macos_deployment_target"]])
+
+ if env["macos_sdk_path"]:
+ env.Append(CCFLAGS=["-isysroot", env["macos_sdk_path"]])
+ env.Append(LINKFLAGS=["-isysroot", env["macos_sdk_path"]])
+
+ env.Append(
+ LINKFLAGS=[
+ "-framework",
+ "Cocoa",
+ "-Wl,-undefined,dynamic_lookup",
+ ]
+ )
diff --git a/tools/macos_osxcross.py b/tools/macos_osxcross.py
new file mode 100644
index 0000000..8ed9a5d
--- /dev/null
+++ b/tools/macos_osxcross.py
@@ -0,0 +1,29 @@
+# Copied from https://github.com/godotengine/godot-cpp/blob/0ee980abae91c481009152cdccab8e61c9625303/tools/macos_osxcross.py
+import os
+
+
+def options(opts):
+ opts.Add("osxcross_sdk", "OSXCross SDK version", "darwin16")
+
+
+def exists(env):
+ return "OSXCROSS_ROOT" in os.environ
+
+
+def generate(env):
+ root = os.environ.get("OSXCROSS_ROOT", "")
+ if env["arch"] == "arm64":
+ basecmd = root + "/target/bin/arm64-apple-" + env["osxcross_sdk"] + "-"
+ else:
+ basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-"
+
+ env["CC"] = basecmd + "clang"
+ env["CXX"] = basecmd + "clang++"
+ env["AR"] = basecmd + "ar"
+ env["RANLIB"] = basecmd + "ranlib"
+ env["AS"] = basecmd + "as"
+
+ binpath = os.path.join(root, "target", "bin")
+ if binpath not in env["ENV"]["PATH"]:
+ # Add OSXCROSS bin folder to PATH (required for linking).
+ env["ENV"]["PATH"] = "%s:%s" % (binpath, env["ENV"]["PATH"])
diff --git a/tools/my_spawn.py b/tools/my_spawn.py
new file mode 100644
index 0000000..915f972
--- /dev/null
+++ b/tools/my_spawn.py
@@ -0,0 +1,53 @@
+# Copied from https://github.com/godotengine/godot-cpp/blob/93f2091185ff4390ca8fc8901ebc68ebc35a218f/tools/my_spawn.py
+import os
+
+
+def exists(env):
+ return os.name == "nt"
+
+
+# Workaround for MinGW. See:
+# http://www.scons.org/wiki/LongCmdLinesOnWin32
+def configure(env):
+ import subprocess
+
+ def mySubProcess(cmdline, env):
+ # print "SPAWNED : " + cmdline
+ startupinfo = subprocess.STARTUPINFO()
+ startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
+ proc = subprocess.Popen(
+ cmdline,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ startupinfo=startupinfo,
+ shell=False,
+ env=env,
+ )
+ data, err = proc.communicate()
+ rv = proc.wait()
+ if rv:
+ print("=====")
+ print(err.decode("utf-8"))
+ print("=====")
+ return rv
+
+ def mySpawn(sh, escape, cmd, args, env):
+
+ newargs = " ".join(args[1:])
+ cmdline = cmd + " " + newargs
+
+ rv = 0
+ if len(cmdline) > 32000 and cmd.endswith("ar"):
+ cmdline = cmd + " " + args[1] + " " + args[2] + " "
+ for i in range(3, len(args)):
+ rv = mySubProcess(cmdline + args[i], env)
+ if rv:
+ break
+ else:
+ rv = mySubProcess(cmdline, env)
+
+ return rv
+
+ env["SPAWN"] = mySpawn
+ env.Replace(ARFLAGS=["q"])
diff --git a/tools/targets.py b/tools/targets.py
new file mode 100644
index 0000000..5c33555
--- /dev/null
+++ b/tools/targets.py
@@ -0,0 +1,93 @@
+# Copied from https://github.com/godotengine/godot-cpp/blob/edf02f83194b58408ca241459c986e32c52fd9c7/tools/targets.py
+import os
+import sys
+from SCons.Script import ARGUMENTS
+from SCons.Variables import *
+from SCons.Variables.BoolVariable import _text2bool
+
+
+def get_cmdline_bool(option, default):
+ """We use `ARGUMENTS.get()` to check if options were manually overridden on the command line,
+ and SCons' _text2bool helper to convert them to booleans, otherwise they're handled as strings.
+ """
+ cmdline_val = ARGUMENTS.get(option)
+ if cmdline_val is not None:
+ return _text2bool(cmdline_val)
+ else:
+ return default
+
+
+def options(opts):
+ opts.Add(
+ EnumVariable(
+ "optimize",
+ "The desired optimization flags",
+ "speed_trace",
+ ("none", "custom", "debug", "speed", "speed_trace", "size"),
+ )
+ )
+ opts.Add(BoolVariable("debug_symbols", "Build with debugging symbols", True))
+ opts.Add(BoolVariable("dev_build", "Developer build with dev-only debugging code (DEV_ENABLED)", False))
+
+
+def exists(env):
+ return True
+
+
+def generate(env):
+ env.dev_build = env["dev_build"]
+ env.debug_features = env["target"] in ["editor", "template_debug"]
+ env.editor_build = env["target"] == "editor"
+
+ if env.editor_build:
+ env.AppendUnique(CPPDEFINES=["TOOLS_ENABLED"])
+
+ if env.debug_features:
+ env.AppendUnique(CPPDEFINES=["DEBUG_ENABLED", "DEBUG_METHODS_ENABLED"])
+
+ if env.dev_build:
+ opt_level = "none"
+ env.AppendUnique(CPPDEFINES=["DEV_ENABLED"])
+ elif env.debug_features:
+ opt_level = "speed_trace"
+ else: # Release
+ opt_level = "speed"
+
+ env["optimize"] = ARGUMENTS.get("optimize", opt_level)
+ env["debug_symbols"] = get_cmdline_bool("debug_symbols", env.dev_build)
+
+ if env.get("is_msvc", False):
+ if env["debug_symbols"]:
+ env.Append(CCFLAGS=["/Zi", "/FS"])
+ env.Append(LINKFLAGS=["/DEBUG:FULL"])
+
+ if env["optimize"] == "speed" or env["optimize"] == "speed_trace":
+ env.Append(CCFLAGS=["/O2"])
+ env.Append(LINKFLAGS=["/OPT:REF"])
+ elif env["optimize"] == "size":
+ env.Append(CCFLAGS=["/O1"])
+ env.Append(LINKFLAGS=["/OPT:REF"])
+
+ if env["optimize"] == "debug" or env["optimize"] == "none":
+ env.Append(CCFLAGS=["/MDd", "/Od"])
+ else:
+ env.Append(CCFLAGS=["/MD"])
+
+ else:
+ if env["debug_symbols"]:
+ if env.dev_build:
+ env.Append(CCFLAGS=["-g3"])
+ else:
+ env.Append(CCFLAGS=["-g2"])
+
+ if env["optimize"] == "speed":
+ env.Append(CCFLAGS=["-O3"])
+ # `-O2` is friendlier to debuggers than `-O3`, leading to better crash backtraces.
+ elif env["optimize"] == "speed_trace":
+ env.Append(CCFLAGS=["-O2"])
+ elif env["optimize"] == "size":
+ env.Append(CCFLAGS=["-Os"])
+ elif env["optimize"] == "debug":
+ env.Append(CCFLAGS=["-Og"])
+ elif env["optimize"] == "none":
+ env.Append(CCFLAGS=["-O0"])
diff --git a/tools/windows.py b/tools/windows.py
new file mode 100644
index 0000000..0fd86a6
--- /dev/null
+++ b/tools/windows.py
@@ -0,0 +1,73 @@
+# Copied from https://github.com/godotengine/godot-cpp/blob/edf02f83194b58408ca241459c986e32c52fd9c7/tools/windows.py
+import sys
+
+import my_spawn
+
+from SCons.Tool import msvc, mingw
+from SCons.Variables import *
+
+
+def options(opts):
+ opts.Add(BoolVariable("use_mingw", "Use the MinGW compiler instead of MSVC - only effective on Windows", False))
+ opts.Add(BoolVariable("use_clang_cl", "Use the clang driver instead of MSVC - only effective on Windows", False))
+
+
+def exists(env):
+ return True
+
+
+def generate(env):
+ base = None
+ if not env["use_mingw"] and msvc.exists(env):
+ if env["arch"] == "x86_64":
+ env["TARGET_ARCH"] = "amd64"
+ elif env["arch"] == "x86_32":
+ env["TARGET_ARCH"] = "x86"
+ env["is_msvc"] = True
+
+ # MSVC, linker, and archiver.
+ msvc.generate(env)
+ env.Tool("mslib")
+ env.Tool("mslink")
+
+ env.Append(CPPDEFINES=["TYPED_METHOD_BIND", "NOMINMAX"])
+ env.Append(CCFLAGS=["/EHsc", "/utf-8"])
+ env.Append(LINKFLAGS=["/WX"])
+
+ if env["use_clang_cl"]:
+ env["CC"] = "clang-cl"
+ env["CXX"] = "clang-cl"
+
+ elif sys.platform == "win32" or sys.platform == "msys":
+ env["use_mingw"] = True
+ mingw.generate(env)
+ # Don't want lib prefixes
+ env["IMPLIBPREFIX"] = ""
+ env["SHLIBPREFIX"] = ""
+ # Want dll suffix
+ env["SHLIBSUFFIX"] = ".dll"
+ # Long line hack. Use custom spawn, quick AR append (to avoid files with the same names to override each other).
+ my_spawn.configure(env)
+
+ else:
+ env["use_mingw"] = True
+ # Cross-compilation using MinGW
+ prefix = "i686" if env["arch"] == "x86_32" else env["arch"]
+ env["CXX"] = prefix + "-w64-mingw32-g++"
+ env["CC"] = prefix + "-w64-mingw32-gcc"
+ env["AR"] = prefix + "-w64-mingw32-ar"
+ env["RANLIB"] = prefix + "-w64-mingw32-ranlib"
+ env["LINK"] = prefix + "-w64-mingw32-g++"
+ # Want dll suffix
+ env["SHLIBSUFFIX"] = ".dll"
+
+ # These options are for a release build even using target=debug
+ env.Append(CCFLAGS=["-O3", "-Wwrite-strings"])
+ env.Append(
+ LINKFLAGS=[
+ "--static",
+ "-Wl,--no-undefined",
+ "-static-libgcc",
+ "-static-libstdc++",
+ ]
+ )