feat(windows): the ludic CLI and ludic bundle on Windows

The CLI's shell commands go through shell(), which is run() on POSIX and a
scratch script handed to Git for Windows' bash on Windows (exit codes read
directly there). compile_app links through `ludicc -o` on Windows, ludic run
starts the .exe, and ludic-dev build and ensure_ludicc assemble
selfhost/ludicc.win.seed.ll, which ludic-dev reseed now writes beside the
macOS seed. ludic bundle makes build/<name>/ with a GUI-subsystem exe carrying
the .ico beside `app icon` as an llvm-rc resource, game.lpak and packs.index;
ludicc gains --gui and --link, and quotes its whole link line for cmd.exe.

Verified: ludic-dev test 135/135, selfhost-test 32/32; on the PC a checkout
bootstraps from the Windows seed, ludic-dev build makes the toolchain, and
`ludic bundle` makes build/Maroon Lake/, which runs from its own folder.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-13 03:01:33 +03:00
parent 63d9f61bf1
commit 55b0e2ebf6
22 changed files with 112450 additions and 818 deletions

View file

@ -242,10 +242,10 @@ function repo_url(module: pointer) -> pointer {
function ensure_clone(module: pointer) -> pointer {
let cache = `{store_root()}cache/{flat(module)}`
if file_exists(cache) {
run(`git -C {cache} fetch -q --tags 2>/dev/null`)
shell(`git -C {cache} fetch -q --tags 2>/dev/null`)
return cache
}
run(`mkdir -p {store_root()}cache`)
shell(`mkdir -p {store_root()}cache`)
if not shq(`git clone -q {repo_url(module)} {cache} 2>/dev/null`) {
err(`ludic: cannot fetch {module} (git clone {repo_url(module)} failed)\n`)
return ""
@ -320,10 +320,10 @@ function snapshot(module: pointer, ver: pointer) -> pointer {
if slen(h) == 0 { return "" }
let dest = `{store_root()}{h}`
if not file_exists(dest) {
run(`rm -rf {dest}.tmp`)
run(`cp -R {cache} {dest}.tmp`)
run(`rm -rf {dest}.tmp/.git`)
run(`mv {dest}.tmp {dest}`)
shell(`rm -rf {dest}.tmp`)
shell(`cp -R {cache} {dest}.tmp`)
shell(`rm -rf {dest}.tmp/.git`)
shell(`mv {dest}.tmp {dest}`)
}
return h
}
@ -332,9 +332,9 @@ function snapshot(module: pointer, ver: pointer) -> pointer {
function link_module(module: pointer, hash: pointer) -> void {
let dest = `{store_root()}{hash}`
let link = `ludic_modules/{module}`
run(`mkdir -p "$(dirname {link})"`)
run(`rm -rf {link}`)
run(`ln -s {dest} {link}`)
shell(`mkdir -p "$(dirname {link})"`)
shell(`rm -rf {link}`)
shell(`ln -s {dest} {link}`)
}
# ---- MVS resolution ----------------------------------------------------------
@ -645,7 +645,7 @@ function cmd_pkg_vendor() -> int {
let txt = read_file("package.lock.ludic")
if txt == null { err("ludic: no package.lock.ludic (run ludic get first)\n"); return 1 }
let locked = parse_lock(txt)
run("rm -rf vendor")
shell("rm -rf vendor")
var i = 0
while i < len(locked) {
let m = locked[i]
@ -653,8 +653,8 @@ function cmd_pkg_vendor() -> int {
let dest = `{store_root()}{raw}`
if not file_exists(dest) { err(`ludic: {m.module}@{m.ver} not in the store — run ludic get\n`); return 1 }
let vdir = `vendor/{m.module}`
run(`mkdir -p "$(dirname {vdir})"`)
run(`cp -R {dest} {vdir}`)
shell(`mkdir -p "$(dirname {vdir})"`)
shell(`cp -R {dest} {vdir}`)
print(` vendored {m.module}@{m.ver}`)
i += 1
}
@ -680,7 +680,7 @@ function cmd_pkg_build_lib() -> int {
if man.module != "" { name = capture_line(`basename {man.module}`) }
if name == "" { name = capture_line(`basename {src} .ludic`) }
let t = target_id()
run(`mkdir -p lib/{t}`)
shell(`mkdir -p lib/{t}`)
let ludicc = getenv_or("LUDICC", "bin/ludicc")
let ll = `{tmp_dir()}/buildlib.ll`
let blerr = tmp_path("bl.err")
@ -694,7 +694,7 @@ function cmd_pkg_build_lib() -> int {
let why2 = capture_line(`tail -1 {blerr}`)
err(`ludic build-lib: link failed — {why2}\n`); return 1
}
run(`rm -f {ll}`)
shell(`rm -f {ll}`)
print(`built {out} (target {t})`)
print(`add to package.ludic: kind prebuilt / targets "{t}" — then commit lib/ and git tag`)
return 0