- 新增 copyself:延迟后删除目标并复制文件/目录 - ViperC 新增 --build-self:自举到 __vpcbuild_2__ 后由 copyself 替换 __vpcbuild__ - 同步当前 ViperC 工作区已有的 Gen/Diag/Stub 改动 - .gitignore 忽略 __vpcbuild_* 与 _sha1_map.txt
210 lines
7.4 KiB
Plaintext
210 lines
7.4 KiB
Plaintext
from stdint import *
|
|
import stdio
|
|
import stdlib
|
|
import string
|
|
import viperlib
|
|
import os
|
|
import os.path
|
|
import win32.win32base
|
|
import win32.win32file
|
|
import win32.win32process
|
|
import lib.Phase.BuildPipeline as BuildPipeline
|
|
|
|
|
|
define SELF_EXE_CAP = 4096
|
|
define SELF_BUILD_DIR = "__vpcbuild_2__"
|
|
define COPY_DELAY_SECONDS = 5
|
|
|
|
|
|
def _GetSelfExe() -> str:
|
|
buf: bytes = stdlib.malloc(sizet(SELF_EXE_CAP))
|
|
if buf is None:
|
|
return None
|
|
n: uint32 = win32.win32file.GetModuleFileNameA(None, buf, uint32(SELF_EXE_CAP))
|
|
if int(n) <= 0:
|
|
stdlib.free(buf)
|
|
return None
|
|
ni: int = int(n)
|
|
if ni >= SELF_EXE_CAP:
|
|
ni = SELF_EXE_CAP - 1
|
|
buf[ni] = 0
|
|
return str(buf)
|
|
|
|
|
|
def _TryRoot(dir: str) -> str:
|
|
if dir is None or len(dir) == 0:
|
|
return None
|
|
root: str = os.path.normpath(dir)
|
|
if root is None:
|
|
return None
|
|
project: str = os.path.join(root, "project.vpj")
|
|
copyself_project: str = os.path.join(os.path.join(root, "copyself"), "project.vpj")
|
|
if project is not None and copyself_project is not None:
|
|
if os.isfile(project) and os.isfile(copyself_project):
|
|
return root
|
|
return None
|
|
|
|
|
|
def _FindViperCRoot(self_exe: str) -> str:
|
|
base: str = os.path.dirname(self_exe)
|
|
if base is None:
|
|
base = ""
|
|
parent1: str = os.path.dirname(base)
|
|
parent2: str = os.path.dirname(parent1)
|
|
|
|
# Executable-derived candidates first: cwd is normally the workspace root,
|
|
# not the compiler tree.
|
|
root: str = _TryRoot(base)
|
|
if root is not None:
|
|
return root
|
|
root = _TryRoot(parent1)
|
|
if root is not None:
|
|
return root
|
|
root = _TryRoot(parent2)
|
|
if root is not None:
|
|
return root
|
|
root = _TryRoot(os.path.join(base, "ViperC"))
|
|
if root is not None:
|
|
return root
|
|
root = _TryRoot(os.path.join(base, "../ViperC"))
|
|
if root is not None:
|
|
return root
|
|
root = _TryRoot(os.path.join(base, "../../ViperC"))
|
|
if root is not None:
|
|
return root
|
|
|
|
# Fallback only when the executable is detached from the compiler tree.
|
|
cwd: str = os.getcwd()
|
|
root = _TryRoot(cwd)
|
|
if root is not None:
|
|
return root
|
|
if cwd is not None:
|
|
root = _TryRoot(os.path.join(cwd, "ViperC"))
|
|
if root is not None:
|
|
return root
|
|
root = _TryRoot(os.path.join(os.path.dirname(cwd), "ViperC"))
|
|
if root is not None:
|
|
return root
|
|
return None
|
|
|
|
|
|
def _FormatSelfBuildCommand(self_exe: str, project: str, build_dir: str) -> str:
|
|
need: sizet = len(self_exe) + len(project) + len(build_dir) + 64
|
|
buf: bytes = stdlib.malloc(need)
|
|
if buf is None:
|
|
return None
|
|
viperlib.snprintf(buf, need, "\"%s\" --project \"%s\" --clean --build-dir \"%s\"",
|
|
self_exe, project, build_dir)
|
|
return str(buf)
|
|
|
|
|
|
def _FindCopySelfExe(root: str) -> str:
|
|
base: str = os.path.join(root, "copyself")
|
|
standard: str = os.path.join(os.path.join(os.path.join(base, "__vpcbuild__"), "output"), "copyself.exe")
|
|
if os.isfile(standard):
|
|
return standard
|
|
legacy: str = os.path.join(os.path.join(base, "output"), "copyself.exe")
|
|
if os.isfile(legacy):
|
|
return legacy
|
|
return None
|
|
|
|
|
|
def _FormatCopySelfCommand(copyself_exe: str) -> str:
|
|
need: sizet = len(copyself_exe) + 128
|
|
buf: bytes = stdlib.malloc(need)
|
|
if buf is None:
|
|
return None
|
|
viperlib.snprintf(buf, need, "\"%s\" %d ../../__vpcbuild_2__ ../../__vpcbuild__",
|
|
copyself_exe, COPY_DELAY_SECONDS)
|
|
return str(buf)
|
|
|
|
|
|
def _SpawnDetached(cmd: str, cwd: str) -> int:
|
|
if cmd is None or cwd is None:
|
|
stdio.printf("FAIL: --build-self cannot launch copyself without a command and working directory\n")
|
|
return -1
|
|
clen: sizet = len(cmd)
|
|
cmd_line: bytes = stdlib.malloc(clen + 1)
|
|
if cmd_line is None:
|
|
stdio.printf("FAIL: --build-self failed to allocate the copyself command line\n")
|
|
return -1
|
|
string.strcpy(cmd_line, cmd)
|
|
|
|
si: win32.win32process.STARTUPINFOA = win32.win32process.STARTUPINFOA()
|
|
pi: win32.win32process.PROCESS_INFORMATION = win32.win32process.PROCESS_INFORMATION()
|
|
string.memset(&si, 0, win32.win32process.STARTUPINFOA.__sizeof__())
|
|
string.memset(&pi, 0, win32.win32process.PROCESS_INFORMATION.__sizeof__())
|
|
si.cb = win32.win32process.STARTUPINFOA.__sizeof__()
|
|
|
|
ok: int = win32.win32process.CreateProcessA(None, cmd_line, None, None, 1, 0,
|
|
None, cwd, &si, &pi)
|
|
if ok == 0:
|
|
err: uint32 = win32.win32base.GetLastError()
|
|
stdio.printf("FAIL: --build-self could not launch copyself (Win32 error %d)\n", int(err))
|
|
stdio.printf(" command: %s\n", cmd)
|
|
stdlib.free(cmd_line)
|
|
return -1
|
|
|
|
stdio.printf("build-self: copyself launched (pid=%d)\n", int(pi.dwProcessId))
|
|
win32.win32base.CloseHandle(pi.hProcess)
|
|
win32.win32base.CloseHandle(pi.hThread)
|
|
stdlib.free(cmd_line)
|
|
return 0
|
|
|
|
|
|
def RunSelfBuild() -> int:
|
|
self_exe: str = _GetSelfExe()
|
|
if self_exe is None:
|
|
stdio.printf("FAIL: --build-self could not resolve the compiler executable path\n")
|
|
return 1
|
|
root: str = _FindViperCRoot(self_exe)
|
|
if root is None:
|
|
stdio.printf("FAIL: --build-self could not locate the ViperC root\n")
|
|
stdio.printf(" expected both: project.vpj and copyself/project.vpj\n")
|
|
return 1
|
|
|
|
project: str = os.path.join(root, "project.vpj")
|
|
build_dir: str = os.path.join(root, SELF_BUILD_DIR)
|
|
copyself_exe: str = _FindCopySelfExe(root)
|
|
if copyself_exe is None:
|
|
stdio.printf("FAIL: copyself.exe not found under %s\n", os.path.join(root, "copyself"))
|
|
stdio.printf(" expected: output/copyself.exe or __vpcbuild__/output/copyself.exe\n")
|
|
stdio.printf(" build it first: viperc --project %s --clean\n",
|
|
os.path.join(os.path.join(root, "copyself"), "project.vpj"))
|
|
return 1
|
|
|
|
cmd: str = _FormatSelfBuildCommand(self_exe, project, build_dir)
|
|
if cmd is None:
|
|
stdio.printf("FAIL: --build-self failed to build the compiler command line\n")
|
|
return 1
|
|
stdio.printf("build-self: %s\n", cmd)
|
|
rc: int = BuildPipeline._run_cmd_wait(cmd)
|
|
if rc != 0:
|
|
stdio.printf("FAIL: --build-self compilation failed (exit=%d)\n", rc)
|
|
return 1
|
|
|
|
built_exe: str = os.path.join(os.path.join(build_dir, "output"), "viperc.exe")
|
|
if not os.isfile(built_exe):
|
|
stdio.printf("FAIL: --build-self did not produce %s\n", built_exe)
|
|
return 1
|
|
|
|
# The requested arguments are relative to ViperC/copyself/output. Use that
|
|
# directory as the launch cwd even when copyself.exe lives under
|
|
# copyself/__vpcbuild__/output.
|
|
copyself_cwd: str = os.path.join(os.path.join(root, "copyself"), "output")
|
|
if not os.isdir(copyself_cwd):
|
|
if os.mkdir(copyself_cwd) != 0:
|
|
if not os.isdir(copyself_cwd):
|
|
err_mkdir: ULONG = win32.win32base.GetLastError()
|
|
stdio.printf("FAIL: --build-self could not create %s (Win32 error %d)\n",
|
|
copyself_cwd, int(err_mkdir))
|
|
return 1
|
|
copy_cmd: str = _FormatCopySelfCommand(copyself_exe)
|
|
if copy_cmd is None:
|
|
stdio.printf("FAIL: --build-self failed to build the copyself command line\n")
|
|
return 1
|
|
if _SpawnDetached(copy_cmd, copyself_cwd) != 0:
|
|
return 1
|
|
stdio.printf("build-self: __vpcbuild__ will be replaced after %d seconds\n", COPY_DELAY_SECONDS)
|
|
return 0
|