481 lines
26 KiB
Plaintext
481 lines
26 KiB
Plaintext
import lib.core.Gen.GenApi as GenApi
|
|
import lib.core.VLogger as VLogger
|
|
import lib.core.Repl as Repl
|
|
import lib.core.Gen.Translator as HandlesTranslator
|
|
import lib.Phase.BuildPipeline as BuildPipeline
|
|
import lib.core.IncludesScanner as IncludesScanner
|
|
import lib.Stub.StubMerger as StubMerger
|
|
import lib.Phase.Phase1 as Phase1
|
|
import lib.Phase.Phase2 as Phase2
|
|
import lib.core.CompilerFactory as CompilerFactory
|
|
import lib.core.Gen.Generator as GenGenerator
|
|
import lib.Projectrans.Config as Config
|
|
import lib.Projectrans.Utils as Utils
|
|
import lib.core.builtin.ViperBuild as ViperBuild
|
|
import lib
|
|
from stdint import *
|
|
import stdio
|
|
import string
|
|
import stdlib
|
|
import memhub
|
|
import argparse
|
|
import win32.win32console as win32cmd
|
|
import win32.win32base as win32base
|
|
import sys
|
|
import ast
|
|
import os
|
|
import os._win32
|
|
import os._posix
|
|
import os.path
|
|
import shutil
|
|
import mlirlite
|
|
import subprocess
|
|
import lib.Phase.Phase1.Phase1Main as Phase1Main
|
|
|
|
|
|
# Main Pool 1 GiB(不要简单调大:2 GiB 会让 MemBuddy 内部索引/位宽溢出 ——
|
|
# ASan: access-violation at memhub.vp:858 _fl_push,就在 __init__ 阶段。
|
|
# 池耗尽的正解是修泄漏:mlirlite 全局数组 dense 渲染的"每元素 ~5KB 不复用",见 includes e9e1933。)
|
|
define POOL_SIZE = 1073741824
|
|
# Permanent memory pool size: 2^29 + 264 = 512MB usable, then change to 1 GiB
|
|
define PERM_POOL_SIZE = 1073741824
|
|
# Code page: UTF-8 (Windows 65001)
|
|
define CODE_PAGE = 65001
|
|
# Source code buffer size (1MB)
|
|
define SRC_BUF_SIZE = 1048576
|
|
# Version (dev/beta/rc)
|
|
V_VPC_VERSION: __attribute__(section(".PRVERSN")) []char = "0.0.5-dev.479"
|
|
define V_VIPER_VERSION = "1.0.5-dev.970 2026-09-28 Non-LTS"
|
|
define V_RUNTIME_VERSION = "0.2.3+19-Non-LTS"
|
|
define V_TPC_COMMIT = "e376b949c9"
|
|
define V_VPC_COMMIT = "ab0f37e1a8"
|
|
define V_RUNTIME_COMMIT = "f087aa88e2"
|
|
define V_TARGET = "x86_64-w64-windows-gnu"
|
|
define V_LLVM = "22.1.8"
|
|
# --repl template-project lookup buffers (repl.vpj path / miss diagnostic text)
|
|
define REPL_PROJ_PATH_CAP = 1024
|
|
define REPL_PROJ_DIAG_CAP = 2048
|
|
|
|
static a: int
|
|
# main: Command-line entry
|
|
def main() -> int:
|
|
win32cmd.SetConsoleOutputCP(CODE_PAGE)
|
|
win32cmd.SetConsoleCP(CODE_PAGE)
|
|
|
|
detect_flag: int = 0
|
|
cmd_line_d: str = win32base.GetCommandLineA()
|
|
if cmd_line_d is not None and string.strstr(cmd_line_d, "--detection") is not None: detect_flag = 1
|
|
|
|
# --detection-deep: heavy diagnostics (per-alloc probe + periodic full-table scan; extremely slow, off by default); read via raw command-line strstr before MemBuddy
|
|
deep_detect_flag: int = 0
|
|
if cmd_line_d is not None and string.strstr(cmd_line_d, "--detection-deep") is not None: deep_detect_flag = 1
|
|
|
|
# Initialize the mbuddy memory pool
|
|
arena: bytes = stdlib.malloc(POOL_SIZE)
|
|
if arena is None:
|
|
stdio.printf("FAIL: malloc for arena failed\n")
|
|
return 1
|
|
# Create a global permanent memory pool (perm_pool, 512MB)
|
|
perm_arena: bytes = stdlib.malloc(PERM_POOL_SIZE)
|
|
if perm_arena is None:
|
|
stdio.printf("FAIL: malloc for perm_pool arena failed\n")
|
|
return 1
|
|
|
|
mb: memhub.MemBuddy* = memhub.MemBuddy(arena, POOL_SIZE, detect_flag, deep_detect_flag)
|
|
if mb is None:
|
|
stdio.printf("FAIL: MemBuddy init failed\n")
|
|
return 1
|
|
|
|
perm_pool: memhub.MemBuddy* = memhub.MemBuddy(perm_arena, PERM_POOL_SIZE, detect_flag, deep_detect_flag)
|
|
|
|
if perm_pool is None:
|
|
stdio.printf("FAIL: perm_pool init failed\n")
|
|
return 1
|
|
|
|
CompilerFactory.factory = CompilerFactory.CompilerFactory(mb, perm_pool)
|
|
|
|
# set the global mbuddy pointer (needed by sys and argparse)
|
|
sys._mbuddy = mb
|
|
argparse._mbuddy = mb
|
|
ast._mbuddy = mb
|
|
lib._mbuddy = mb
|
|
Config._mbuddy = mb
|
|
Utils._mbuddy = mb
|
|
HandlesTranslator._mbuddy = mb
|
|
BuildPipeline._mbuddy = mb
|
|
IncludesScanner._mbuddy = mb
|
|
StubMerger._mbuddy = mb
|
|
Phase1._mbuddy = mb
|
|
Phase2._mbuddy = mb
|
|
subprocess._mbuddy = mb
|
|
# Runtime-dep deploy primitives (BuildPipeline.deploy_runtime_deps): os.mkdir + shutil.copyfile.
|
|
os._mbuddy = mb
|
|
os._win32._mbuddy = mb
|
|
os._posix._mbuddy = mb
|
|
os.path._mbuddy = mb
|
|
shutil._mbuddy = mb
|
|
# hashlib._mbuddy = (mb)
|
|
VLogger._mbuddy = mb
|
|
lib._mbuddy = mb
|
|
|
|
# P2: create the project-level Generator singleton (reused across Phase1/Phase2/BuildPipeline;
|
|
gen: GenGenerator.Generator* = mb.alloc(GenGenerator.Generator.__sizeof__())
|
|
if gen is None:
|
|
stdio.printf("FAIL: Generator alloc failed\n")
|
|
return 1
|
|
gen.__before_init__()
|
|
gen.__init__(mb)
|
|
|
|
GenGenerator._g_gen = gen
|
|
# P2-P5: inject module-level Handles singletons (Phase1 calls their state funcs); module globals carry sha1 prefixes, assign each
|
|
GenApi._g_gen = gen
|
|
# P6: inject statement/assign/var/import/translator-driver module singletons
|
|
|
|
# initialize VLogger and print the startup log
|
|
VLogger.init_logger()
|
|
log: VLogger.Logger = VLogger.logger
|
|
if log is None: log.error("ViperC Failed to parse parameters")
|
|
|
|
# initialize command-line args (Windows: GetCommandLineA, POSIX: /proc/self/cmdline)
|
|
sys._init_argv()
|
|
|
|
# create the argument parser
|
|
parser: argparse.ArgumentParser = argparse.ArgumentParser("ViperC", "ViperC command-line parameter parser", pool=mb)
|
|
|
|
# register arguments (matching Projectrans.py main())
|
|
parser.add_argument("--project", None, argparse.STRING, 0, None, False, argparse.STORE, "Project VPJ path, by default it looks in the current directory")
|
|
parser.add_argument("--src", None, argparse.STRING, 0, None, False, argparse.STORE, "Source directory")
|
|
parser.add_argument("--temp", None, argparse.STRING, 0, None, False, argparse.STORE, "Declare a temporary directory for the interface")
|
|
parser.add_argument("--output", None, argparse.STRING, 0, None, False, argparse.STORE, "Output directory")
|
|
parser.add_argument("--phase", None, argparse.STRING, 0, None, False, argparse.STORE, "Phase: 1 = Generate statement, 2 = Translate and compile, all = Everything")
|
|
parser.add_argument("--cc", None, argparse.STRING, 0, None, False, argparse.STORE, "LLVM compiler command")
|
|
parser.add_argument("--clean", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Clean output and temp directories")
|
|
parser.add_argument("--run", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Run the generated executable after compilation")
|
|
parser.add_argument("--rebuild-includes", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Delete the includes.binary precompiled cache and rebuild all the includes")
|
|
parser.add_argument("--clear-cache", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Clear .transpyc_cache global cache")
|
|
parser.add_argument("--asan", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "ASan mode: .ll first opt -passes=asan; ASan runtime libs must be declared in project.vpj (linker.static_libs)")
|
|
parser.add_argument("--traceback", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Traceback mode: force-compile includes/traceback.vp + Install() ctor, llc -frame-pointer=all, link -ldbghelp, export <exe>.stackmap")
|
|
parser.add_argument("--phase2command", None, argparse.STRING, 0, None, False, argparse.STORE, "Do not run llc/link: write Phase2 llc(opt+llc) and clang link commands to the given .bat path")
|
|
parser.add_argument("--nowarn-register", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Suppress Phase A dependency WARN")
|
|
parser.add_argument("--detection", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Enable MemBuddy allocation detection (0xCDCD markers + record-table probe for out-of-bounds writes; applies to both mb and perm_pool)")
|
|
# Registered only so argparse accepts the flag: the value is read via raw
|
|
# command-line strstr before MemBuddy construction, same as --detection.
|
|
parser.add_argument("--detection-deep", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Deep detection: probe every allocation and enable the periodic full-table scan (implies --detection; extremely slow, diagnostics only)")
|
|
parser.add_argument("--show-args", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show all parsed arguments")
|
|
parser.add_argument("--repl", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Start the interactive REPL (M2 session variables + M3 cross-fragment functions: fragment -> DLL -> call)")
|
|
parser.add_argument("--build-dir", None, argparse.STRING, 0, None, False, argparse.STORE, "Override project.vpj build_dir (default ./__vpcbuild__; relative paths resolve against the project dir)")
|
|
parser.add_argument("--no-llvmpeephole", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Disable all local mlirlite peephole passes (T2 store/load forward & ret-zero, T3 no-op bitcast) in FunctionPrint serialization")
|
|
parser.add_argument("--version", "-v", argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show version and exit")
|
|
parser.add_argument("--help", "-h", argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show help and exit")
|
|
|
|
# parse command-line arguments
|
|
args: argparse.ParsedArgs = parser.parse_args(sys._argc, sys._argv)
|
|
if args is None:
|
|
log.error("Failed to parse arguments", "argparse")
|
|
return 1
|
|
|
|
if args.get_bool("help"):
|
|
parser.print_help()
|
|
return 0
|
|
|
|
if args.get_bool("version"):
|
|
stdio.printf("""Viper %s
|
|
Copyright (C) 2026 Viper Project
|
|
License MIT <https://opensource.org/licenses/MIT>
|
|
GVSDS(TM) Viper SE Runtime Environment and Library (VRTE) %s
|
|
Viper Frontend Compiler ViperC %s
|
|
Author: terminexus@gvsds.com
|
|
|
|
Tpv commit: %s
|
|
Tpc commit: %s
|
|
VRTE commit: %s
|
|
Target: %s
|
|
|
|
""", V_VIPER_VERSION, V_RUNTIME_VERSION, V_VPC_VERSION, V_VPC_COMMIT, V_TPC_COMMIT, V_RUNTIME_COMMIT, V_TARGET, V_LLVM)
|
|
return 0
|
|
|
|
# --repl guard: mutually exclusive with --phase2command / --asan, checked first
|
|
# before their global-state/cleanup side effects; both would break fragment DLLs.
|
|
repl_flag: bool = args.get_bool("repl")
|
|
if repl_flag:
|
|
if args.get_str("phase2command") is not None:
|
|
stdio.printf("FAIL: --repl and --phase2command are mutually exclusive\n")
|
|
stdio.printf(" Reason: under --phase2command, compile_ll_to_obj only writes the bat and does not run llc,\n")
|
|
stdio.printf(" so the fragment .obj is never produced and the subsequent clang++ -shared necessarily fails\n")
|
|
stdio.printf(" Fix: drop --phase2command (REPL must produce and load the fragment DLL immediately)\n")
|
|
argparse.release(args)
|
|
return 2
|
|
if args.get_bool("asan"):
|
|
stdio.printf("FAIL: --repl and --asan are mutually exclusive\n")
|
|
stdio.printf(" Reason: --asan first instruments the fragment .ll with ASan, but the fragment artifact is a runtime\n")
|
|
stdio.printf(" DLL loaded by LoadLibraryA, and the host process does not link the ASan runtime; instrumentation semantics are undefined\n")
|
|
stdio.printf(" Fix: drop --asan (to debug memory issues, build an ASan ViperC in project mode)\n")
|
|
argparse.release(args)
|
|
return 2
|
|
# --traceback is also rejected under --repl: the fragment is a runtime-loaded
|
|
# DLL (DI metadata / startup ctor semantics undefined) and it has no <exe>.stackmap.
|
|
if args.get_bool("traceback"):
|
|
stdio.printf("FAIL: --repl and --traceback are mutually exclusive\n")
|
|
stdio.printf(" Reason: --traceback relies on startup-ctor injection + a per-module DWARF side file,\n")
|
|
stdio.printf(" but the fragment is a runtime LoadLibraryA-loaded DLL; the two semantics are undefined\n")
|
|
stdio.printf(" Fix: drop --traceback (for crash stacks, build a --traceback ViperC in project mode)\n")
|
|
argparse.release(args)
|
|
return 2
|
|
|
|
# === --repl: M1 interactive loop (expression fragment -> DLL -> echo). Runs
|
|
# after project-config load and before Phase1/Phase2, then returns.
|
|
|
|
# print parse result
|
|
# log.banner("ViperC argument parse result")
|
|
|
|
# string arguments
|
|
project: str = args.get_str("project")
|
|
src: str = args.get_str("src")
|
|
temp: str = args.get_str("temp")
|
|
output: str = args.get_str("output")
|
|
phase: str = args.get_str("phase")
|
|
cc: str = args.get_str("cc")
|
|
build_dir: str = args.get_str("build-dir")
|
|
|
|
# === --project and --src are mutually exclusive: --project uses project.vpj,
|
|
# --src uses viperbuild built-in config; giving both fails fast.
|
|
if project is not None and src is not None:
|
|
stdio.printf("FAIL: --project and --src are mutually exclusive\n")
|
|
stdio.printf(" Reason: --project uses project.vpj project mode, --src uses the entry file's built-in viperbuild config\n")
|
|
stdio.printf(" Fix: choose one; in --src mode the build config goes in 'import viperbuild' within the entry file's dependency closure\n")
|
|
argparse.release(args)
|
|
return 2
|
|
|
|
_gb_clean: bool = args.get_bool("clean")
|
|
show_args: bool = args.get_bool("show-args")
|
|
if show_args:
|
|
if project is not None: stdio.printf(" --project: %s\n", project)
|
|
else: stdio.printf(" --project: (not specified)\n")
|
|
if src is not None: stdio.printf(" --src: %s\n", src)
|
|
else: stdio.printf(" --src: (not specified)\n")
|
|
if temp is not None: stdio.printf(" --temp: %s\n", temp)
|
|
else: stdio.printf(" --temp: (not specified)\n")
|
|
if output is not None: stdio.printf(" --output: %s\n", output)
|
|
else: stdio.printf(" --output: (not specified)\n")
|
|
if phase is not None: stdio.printf(" --phase: %s\n", phase)
|
|
else: stdio.printf(" --phase: (not specified, default all)\n")
|
|
if cc is not None: stdio.printf(" --cc: %s\n", cc)
|
|
else: stdio.printf(" --cc: (not specified)\n")
|
|
if _gb_clean: stdio.printf(" --clean: True\n")
|
|
else: stdio.printf(" --clean: False\n")
|
|
if args.get_bool("run"): stdio.printf(" --run: True\n")
|
|
else: stdio.printf(" --run: False\n")
|
|
if args.get_bool("rebuild-includes"): stdio.printf(" --rebuild-includes: True\n")
|
|
else: stdio.printf(" --rebuild-includes: False\n")
|
|
if args.get_bool("clear-cache"): stdio.printf(" --clear-cache: True\n")
|
|
else: stdio.printf(" --clear-cache: False\n")
|
|
stdio.printf("Argument parsing complete.\n\n")
|
|
|
|
# Mode switches: isolate includes caches by suffix so mode-specific objects do
|
|
# not mix (ASan/traceback objects are incompatible); suffix combines both flags.
|
|
gb_asan: bool = args.get_bool("asan")
|
|
gb_traceback: bool = args.get_bool("traceback")
|
|
if gb_asan: BuildPipeline._asan_mode = True
|
|
else: BuildPipeline._asan_mode = False
|
|
if gb_traceback: BuildPipeline._traceback_mode = True
|
|
else: BuildPipeline._traceback_mode = False
|
|
if gb_asan and gb_traceback: Config._inc_bin_suffix = ".binary_traceback_asan"
|
|
elif gb_traceback: Config._inc_bin_suffix = ".binary_traceback"
|
|
elif gb_asan: Config._inc_bin_suffix = ".binary_asan"
|
|
else: Config._inc_bin_suffix = ".binary"
|
|
if gb_asan: log.info("ASan mode enabled (opt -passes=asan instrumentation; runtime libs must be declared in project.vpj)", "asan")
|
|
if gb_traceback: log.info("Traceback mode enabled (llc keep frame pointer + link dbghelp + Install ctor + <exe>.stackmap)", "traceback")
|
|
# Enabling either mode clears that suffix's includes cache to force a rebuild
|
|
if gb_asan or gb_traceback:
|
|
mf_inc_bin_mode: str = Config.get_includes_binary_dir()
|
|
if mf_inc_bin_mode is not None:
|
|
n_mode: int = Utils.CleanDir(mf_inc_bin_mode)
|
|
log.info(f"Mode cache cleanup: {mf_inc_bin_mode} deleted {n_mode} files (will rebuild with {Config._inc_bin_suffix})", "mode")
|
|
|
|
# === --phase2command: only write llc/link commands to a bat, do not execute them ===
|
|
p2c: str = args.get_str("phase2command")
|
|
if p2c is not None:
|
|
BuildPipeline._phase2_cmd_path = p2c
|
|
log.info(f"--phase2command: not running llc/link, commands written to {p2c}", "phase2cmd")
|
|
|
|
# --nowarn-register: suppress Phase A dependency WARNs (cross-module text.ll not yet generated)
|
|
if args.get_bool("nowarn-register"):
|
|
VLogger._nowarn_register = 1
|
|
log.info("--nowarn-register enabled: suppress register-class WARN output", "vlogger")
|
|
|
|
# --no-llvmpeephole: disable all local peepholes (T2/T3); FunctionPrint serializes directly
|
|
if args.get_bool("no-llvmpeephole"):
|
|
mlirlite.G_PeepEnable = 0
|
|
log.info("--no-llvmpeephole enabled: disable FunctionPrint local peepholes (RenumberSSA kept)", "peep")
|
|
|
|
# === --repl convenience: without --project, search cwd then the exe dir for
|
|
# repl.vpj and feed it to Config.Load_project_config; miss fails fast.
|
|
proj_loaded: int = 0
|
|
proj_path: str = project
|
|
if repl_flag and proj_path is None:
|
|
repl_proj_buf: bytes = stdlib.malloc(REPL_PROJ_PATH_CAP)
|
|
repl_diag_buf: bytes = stdlib.malloc(REPL_PROJ_DIAG_CAP)
|
|
if repl_proj_buf is None or repl_diag_buf is None:
|
|
stdio.printf("FAIL: --repl failed to allocate the path buffer for locating repl.vpj\n")
|
|
argparse.release(args)
|
|
return 1
|
|
if Repl.ReplFindProject(repl_proj_buf, REPL_PROJ_PATH_CAP, repl_diag_buf, REPL_PROJ_DIAG_CAP) != 0:
|
|
proj_path = repl_proj_buf
|
|
log.info(f"--repl: --project not given, using the located template project {proj_path}", "repl")
|
|
else:
|
|
stdio.printf("FAIL: --repl could not find the project file repl.vpj\n")
|
|
stdio.printf("%s", repl_diag_buf)
|
|
stdio.printf(" You can also pass --project to specify the project file explicitly (e.g. ViperC/project.vpj in this repo)\n")
|
|
argparse.release(args)
|
|
return 1
|
|
# load project config (explicit --project / repl.vpj found by --repl)
|
|
if proj_path is not None:
|
|
log.banner("Project config")
|
|
cfg_ret: int = Config.Load_project_config(proj_path)
|
|
if cfg_ret == 0: proj_loaded = 1
|
|
else: log.warning(f"Failed to load project.vpj: {proj_path}", "config")
|
|
else:
|
|
# try the default project.vpj (current and parent dir); --src mode must not
|
|
# implicitly load project.vpj (it would pollute the config source).
|
|
if src is None:
|
|
proj_path = "project.vpj"
|
|
if Config.Load_project_config(proj_path) == 0: proj_loaded = 1
|
|
else:
|
|
proj_path = "../project.vpj"
|
|
if Config.Load_project_config(proj_path) == 0: proj_loaded = 1
|
|
if proj_loaded == 1:
|
|
# extract project.vpj's dir so relative paths resolve against it
|
|
proj_len: sizet = string.strlen(proj_path)
|
|
slash_pos: sizet = proj_len
|
|
for i in range(int(proj_len)):
|
|
idx: sizet = proj_len - 1 - i
|
|
ch: char = proj_path[idx]
|
|
if ch == '/' or ch == '\\':
|
|
slash_pos = idx
|
|
break
|
|
proj_dir: str = ""
|
|
if slash_pos > 0 and slash_pos < proj_len:
|
|
proj_dir = stdlib.malloc(slash_pos + 1)
|
|
if proj_dir is not None:
|
|
string.memcpy(proj_dir, proj_path, slash_pos)
|
|
proj_dir[slash_pos] = '\0'
|
|
# [--build-dir] overrides build_dir before resolve_paths so paths stay project_dir-based
|
|
if build_dir is not None: Config.BuildDir = build_dir
|
|
Config.resolve_paths(proj_dir)
|
|
Config.print_config()
|
|
|
|
# === --src: viperbuild built-in config discovery/apply (Pass 0), before the
|
|
# switches read Config; a miss or bad config fails fast.
|
|
if src is not None:
|
|
log.banner("viperbuild")
|
|
if ViperBuild.DiscoverAndApply(mb, src) != 0:
|
|
argparse.release(args)
|
|
return 1
|
|
Config.print_config()
|
|
|
|
# === --repl: M1 interactive loop (fragment -> IR -> llc -> clang++ -shared ->
|
|
# DLL -> echo), after config load and before Phase1/Phase2.
|
|
if args.get_bool("repl"):
|
|
Repl._perm_pool = perm_pool # jmp_buf/parse-table/session buffers must live across calls; must use PermPool
|
|
if log is not None:
|
|
log.set_jit_flag(True) # errors longjmp back to REPL (reset to False when Repl.run exits)
|
|
rc_repl: int = Repl.run(mb)
|
|
argparse.release(args)
|
|
return rc_repl
|
|
|
|
# --asan requires the project to declare the ASan runtime in project.vpj linker.static_libs (no hardcoded paths); missing fails fast
|
|
if BuildPipeline._asan_mode != 0 and Config.asan_runtime_declared() == 0:
|
|
log.error("Enabling --asan requires declaring the ASan runtime in project.vpj's linker.static_libs:", "asan")
|
|
log.error(" <MSYS2>/clang64/lib/clang/<ver>/lib/windows/libclang_rt.asan_dynamic-x86_64.dll.a", "asan")
|
|
log.error(" <MSYS2>/clang64/lib/clang/<ver>/lib/windows/libclang_rt.asan_dynamic_runtime_thunk-x86_64.a", "asan")
|
|
log.error("The clang64 root is auto-detected from PATH (see README section 8.1)", "asan")
|
|
return 1
|
|
|
|
# === --clean: remove temp, output and includes.binary directories ===
|
|
if _gb_clean:
|
|
log.info("Cleaning temp directory...", "clean")
|
|
if Config.TempDir is not None:
|
|
n: int = Utils.CleanDir(Config.TempDir)
|
|
log.info(f"{Config.TempDir}: deleted {n} files", "clean")
|
|
if Config.OutputDir is not None:
|
|
n2: int = Utils.CleanDir(Config.OutputDir)
|
|
log.info(f"{Config.OutputDir}: deleted {n2} files", "clean")
|
|
# Clean the includes.binary dir (stale .obj files would skip recompilation)
|
|
mf_inc_bin_clean: str = Config.get_includes_binary_dir()
|
|
if mf_inc_bin_clean is not None:
|
|
n3: int = Utils.CleanDir(mf_inc_bin_clean)
|
|
log.info(f"{mf_inc_bin_clean}: deleted {n3} files", "clean")
|
|
|
|
# === --rebuild-includes: delete the includes.binary precompile cache ===
|
|
# Forces all includes to recompile next time (temp/output untouched)
|
|
if args.get_bool("rebuild-includes"):
|
|
log.info("Rebuilding includes cache...", "rebuild-includes")
|
|
mf_inc_bin_rb: str = Config.get_includes_binary_dir()
|
|
if mf_inc_bin_rb is not None:
|
|
n_rb: int = Utils.CleanDir(mf_inc_bin_rb)
|
|
log.info(f"{mf_inc_bin_rb}: deleted {n_rb} files (includes will be recompiled)", "rebuild-includes")
|
|
|
|
# === --clear-cache: clear the temp dir cache (.vpi/.stub.ll/.text.ll) ===
|
|
# Forces Phase1 to regenerate stubs next time (output/includes.binary untouched)
|
|
if args.get_bool("clear-cache"):
|
|
log.info("Clearing temp cache...", "clear-cache")
|
|
if Config.TempDir is not None:
|
|
n_cc: int = Utils.CleanDir(Config.TempDir)
|
|
log.info(f"{Config.TempDir}: deleted {n_cc} files (.vpi/.stub.ll cache)", "clear-cache")
|
|
|
|
# === --phase: 1=stub split only; 2=stub merge+compile only; all=both (default) ===
|
|
phase_mode: str = phase
|
|
if phase_mode is None: phase_mode = "all"
|
|
do_phase1: int = 0
|
|
do_phase2: int = 0
|
|
# Note: use string.strcmp instead of == to avoid the compiler == semantics bug
|
|
if string.strcmp(phase_mode, "1") == 0 or string.strcmp(phase_mode, "all") == 0: do_phase1 = 1
|
|
if string.strcmp(phase_mode, "2") == 0 or string.strcmp(phase_mode, "all") == 0: do_phase2 = 1
|
|
log.info(f"Mode: {phase_mode} (phase1={do_phase1} phase2={do_phase2})", "phase")
|
|
|
|
# === Phase1: scan includes (on-demand translation) ===
|
|
# For each .vp/.py file under includes, translate and split the stub if absent
|
|
if do_phase1 != 0 and Config.IncludesDirs is not None:
|
|
ph1_temp: str = Config.TempDir
|
|
if ph1_temp is None: ph1_temp = "."
|
|
if Phase1Main.RunPhase1(mb, Config.IncludesDirs, ph1_temp, log) != 0:
|
|
VLogger.error("Phase1 failed, aborting compilation", "Phase1")
|
|
return 1
|
|
# Phase1-only (no Phase2): exit right away
|
|
if do_phase2 == 0:
|
|
log.info("Phase1 complete, exiting", "phase")
|
|
return 0
|
|
|
|
# AST parse: when --src or --project is given, read the file and parse it into an AST
|
|
src_path: str = args.get_str("src")
|
|
|
|
# No project config and no --src: fail fast (uninitialized Config fields are garbage pointers -> crash)
|
|
if proj_loaded == 0 and src_path is None:
|
|
log.error("project.vpj not found (current/parent dir) and --src not given; cannot build", "config")
|
|
argparse.release(args)
|
|
return 1
|
|
|
|
# Project mode (multi-file): --project scans source_dir for all .vp; --src + viperbuild
|
|
# reuses it (Pass 0 writes config back, SourceDir as root); the two are mutually exclusive.
|
|
if Config.SourceDir is not None and (project is not None or ViperBuild.IsApplied() != 0):
|
|
mf_temp: str = Config.TempDir if Config.TempDir is not None else "."
|
|
mf_output: str = Config.OutputDir if Config.OutputDir is not None else "."
|
|
mf_cc: str = Config.CompilerCmd if Config.CompilerCmd is not None else "llc"
|
|
mf_cc_flags: str = Config.CompilerFlags if Config.CompilerFlags is not None else "-filetype=obj -relocation-model=pic"
|
|
mf_linker: str = Config.LinkerCmd if Config.LinkerCmd is not None else "clang++"
|
|
mf_linker_flags: str = Config.LinkerFlags if Config.LinkerFlags is not None else "-lmsvcrt -lucrt -lpthread -lmingwex -lkernel32 -lgcc"
|
|
mf_linker_out: str = Config.LinkerOutput if Config.LinkerOutput is not None else "app.exe"
|
|
mf_static_libs: str = Config.LinkerStaticLibs
|
|
mf_dynamic_libs: str = Config.LinkerDynamicLibs
|
|
mf_includes_bin: str = Config.get_includes_binary_dir()
|
|
|
|
mf_ret: int = Phase2.RunMultiFileProject(mb, Config.SourceDir, mf_temp, mf_output, mf_cc, mf_cc_flags, mf_linker, mf_linker_flags, mf_linker_out, mf_includes_bin, Config.IncludesDirs, do_phase1, do_phase2, log, args, mf_static_libs, mf_dynamic_libs)
|
|
argparse.release(args)
|
|
log.success("ViperC complete")
|
|
return mf_ret
|
|
|
|
# Reaching here means no buildable project root (SourceDir missing): fail fast, no silent "done"
|
|
log.error("No buildable project root: project.vpj is missing source_dir, or viperbuild provided no source", "config")
|
|
argparse.release(args)
|
|
return 1
|