#!/usr/bin/env bash # selfhost_build.sh — Hybrid selfhost build (Stage‑B → Program(JSON v0) → optional run via Core‑Direct) # # Goals (80/20): # - Take a Hako source (.hako), compile with Hakorune Stage‑B to Program(JSON v0). # - Optionally run via Gate‑C/Core Direct (in‑proc) to verify exit code. # - (Future) Optionally convert to MIR and build an executable via ny-llvmc. # # Usage: # tools/selfhost/selfhost_build.sh --in source.hako [--json out.json] [--run] # Env: # NYASH_BIN: path to hakorune/nyash binary (auto-detected if omitted) # NYASH_ROOT: repo root (auto-detected) # set -euo pipefail ROOT="${NYASH_ROOT:-$(cd "$(dirname "$0")/../.." && pwd)}" BIN="${NYASH_BIN:-}" if [ -z "${BIN}" ]; then if [ -x "$ROOT/target/release/hakorune" ]; then BIN="$ROOT/target/release/hakorune"; elif [ -x "$ROOT/target/release/nyash" ]; then BIN="$ROOT/target/release/nyash"; else echo "[selfhost] error: NYASH_BIN not set and no binary found under target/release" >&2; exit 2; fi fi IN="" JSON_OUT="" MIR_OUT="" EXE_OUT="" DO_RUN=0 KEEP_TMP=0 while [ $# -gt 0 ]; do case "$1" in --in) IN="$2"; shift 2;; --json) JSON_OUT="$2"; shift 2;; --run) DO_RUN=1; shift;; --mir) MIR_OUT="$2"; shift 2;; --keep-tmp) KEEP_TMP=1; shift;; --exe) EXE_OUT="$2"; shift 2;; *) echo "[selfhost] unknown arg: $1" >&2; exit 2;; esac done if [ -z "$IN" ]; then echo "[selfhost] --in is required" >&2; exit 2; fi if [ ! -f "$IN" ]; then echo "[selfhost] input not found: $IN" >&2; exit 2; fi tmp_json="${JSON_OUT:-/tmp/hako_stageb_$$.json}" # Emit Program(JSON v0; prefer BuildBox for emit-only when HAKO_USE_BUILDBOX=1) RAW="/tmp/hako_stageb_raw_$$.txt" SRC_CONTENT="$(cat "$IN")" if [ "${HAKO_USE_BUILDBOX:-0}" = "1" ] && [ "$DO_RUN" = "0" ] && [ -z "$EXE_OUT" ]; then WRAP="/tmp/hako_buildbox_wrap_$$.hako" cat > "$WRAP" <<'HAKO' include "lang/src/compiler/build/build_box.hako" static box Main { method main(args) { local src = env.get("HAKO_SRC"); local j = BuildBox.emit_program_json_v0(src, null); print(j); return 0; } } HAKO ( export HAKO_SRC="$SRC_CONTENT" export NYASH_QUIET=0 HAKO_QUIET=0 NYASH_CLI_VERBOSE=0 cd "$ROOT" && "$BIN" --backend vm "$WRAP" ) > "$RAW" 2>&1 || true rm -f "$WRAP" 2>/dev/null || true else ( export NYASH_PARSER_ALLOW_SEMICOLON=1 export NYASH_ALLOW_USING_FILE=0 export HAKO_ALLOW_USING_FILE=0 export NYASH_USING_AST=1 export HAKO_PARSER_STAGE3=1 export NYASH_PARSER_STAGE3=1 export NYASH_VARMAP_GUARD_STRICT=0 export NYASH_BLOCK_SCHEDULE_VERIFY=0 export NYASH_QUIET=0 HAKO_QUIET=0 NYASH_CLI_VERBOSE=0 cd "$ROOT" && \ "$BIN" --backend vm \ "$ROOT/lang/src/compiler/entry/compiler_stageb.hako" -- \ --source "$SRC_CONTENT" ) > "$RAW" 2>&1 || true fi if ! awk '(/"version":0/ && /"kind":"Program"/){print;found=1;exit} END{exit(found?0:1)}' "$RAW" > "$tmp_json"; then echo "[selfhost] Stage‑B emit failed" >&2 tail -n 120 "$RAW" >&2 || true rm -f "$RAW" 2>/dev/null || true exit 1 fi rm -f "$RAW" 2>/dev/null || true if [ -n "$JSON_OUT" ]; then echo "[selfhost] JSON v0 written: $tmp_json" >&2 fi # Optional: emit MIR(JSON) from source (runner compiles .hako directly; Stage‑B JSON is for reference) if [ -n "$MIR_OUT" ]; then echo "[selfhost] emitting MIR JSON → $MIR_OUT" >&2 "$BIN" --backend mir --emit-mir-json "$MIR_OUT" "$IN" >/dev/null fi # Optional: build native EXE via ny-llvmc harness (fallback path; parses original source) if [ -n "$EXE_OUT" ]; then # Requirements: ny-llvmc present and harness envs NYLL="${NYASH_NY_LLVM_COMPILER:-$ROOT/target/release/ny-llvmc}" if [ ! -x "$NYLL" ] && [ ! -f "$NYLL" ]; then echo "[selfhost] ny-llvmc not found: $NYLL (Set NYASH_NY_LLVM_COMPILER or build ny-llvmc)" >&2 exit 2 fi NYRT_DIR="${NYASH_EMIT_EXE_NYRT:-$ROOT/target/release}" export NYASH_LLVM_USE_HARNESS=1 export NYASH_NY_LLVM_COMPILER="$NYLL" export NYASH_EMIT_EXE_NYRT="$NYRT_DIR" # Convert Program(JSON v0) → MIR(JSON) via runner MIR_TMP="${MIR_OUT:-/tmp/hako_stageb_mir_$$.json}" echo "[selfhost] converting Program(JSON v0) → MIR(JSON) → EXE" >&2 "$BIN" --json-file "$tmp_json" --program-json-to-mir "$MIR_TMP" # Build EXE via ny-llvmc "$NYLL" --in "$MIR_TMP" --emit exe --nyrt "$NYRT_DIR" --out "$EXE_OUT" # Cleanup if [ "$KEEP_TMP" != "1" ]; then if [ -z "$JSON_OUT" ]; then rm -f "$tmp_json" 2>/dev/null || true; fi if [ -z "$MIR_OUT" ]; then rm -f "$MIR_TMP" 2>/dev/null || true; fi fi exit 0 fi if [ "$DO_RUN" = "1" ]; then # Run via Core‑Direct (in‑proc), quiet set +e NYASH_GATE_C_CORE=1 HAKO_GATE_C_CORE=1 HAKO_CORE_DIRECT=1 HAKO_CORE_DIRECT_INPROC=1 \ NYASH_QUIET=1 HAKO_QUIET=1 NYASH_CLI_VERBOSE=0 NYASH_NYRT_SILENT_RESULT=1 \ "$BIN" --json-file "$tmp_json" >/dev/null 2>&1 rc=$? set -e if [ "$KEEP_TMP" != "1" ] && [ -z "$JSON_OUT" ]; then rm -f "$tmp_json" 2>/dev/null || true; fi exit $rc else # Emit-only echo "$tmp_json" fi