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:
parent
63d9f61bf1
commit
55b0e2ebf6
22 changed files with 112450 additions and 818 deletions
|
|
@ -8,15 +8,15 @@
|
|||
|
||||
# write `body` to `path`, creating parent directories
|
||||
function pt_write(path: pointer, body: pointer) -> void {
|
||||
run(`mkdir -p "$(dirname {path})"`)
|
||||
shell(`mkdir -p "$(dirname {path})"`)
|
||||
write_file(path, body)
|
||||
}
|
||||
|
||||
# commit the current tree of `dir` and tag it `tag`
|
||||
function pt_commit_tag(dir: pointer, tag: pointer) -> void {
|
||||
run(`git -C {dir} add -A`)
|
||||
run(`git -C {dir} -c user.email=t@t.test -c user.name=tester commit -q -m {tag}`)
|
||||
run(`git -C {dir} tag {tag}`)
|
||||
shell(`git -C {dir} add -A`)
|
||||
shell(`git -C {dir} -c user.email=t@t.test -c user.name=tester commit -q -m {tag}`)
|
||||
shell(`git -C {dir} tag {tag}`)
|
||||
}
|
||||
|
||||
function cmd_test_pkg() -> int {
|
||||
|
|
@ -34,13 +34,13 @@ function cmd_test_pkg() -> int {
|
|||
let proxy = `{work}/proxy`
|
||||
let store = `{work}/store`
|
||||
let proj = `{work}/proj`
|
||||
run(`rm -rf {work}`)
|
||||
run(`mkdir -p {proxy} {store} {proj}`)
|
||||
shell(`rm -rf {work}`)
|
||||
shell(`mkdir -p {proxy} {store} {proj}`)
|
||||
|
||||
# ---- fixture packages -----------------------------------------------------
|
||||
# example.test/util: a leaf source package, two published versions.
|
||||
let util = `{proxy}/example.test/util`
|
||||
run(`git -C {util} init -q 2>/dev/null || ( mkdir -p {util} && git -C {util} init -q )`)
|
||||
shell(`git -C {util} init -q 2>/dev/null || ( mkdir -p {util} && git -C {util} init -q )`)
|
||||
pt_write(`{util}/package.ludic`, `package "example.test/util"` + nl() + `version "1.0.0"` + nl() + "kind source" + nl() + `provides "Util"` + nl())
|
||||
pt_write(`{util}/util.ludic`, "function util_tag() -> pointer { return \"u1\" }\n")
|
||||
pt_commit_tag(util, "v1.0.0")
|
||||
|
|
@ -50,7 +50,7 @@ function cmd_test_pkg() -> int {
|
|||
|
||||
# example.test/greeter: requires util >=1.0.0, provides Greet.
|
||||
let greeter = `{proxy}/example.test/greeter`
|
||||
run(`mkdir -p {greeter} && git -C {greeter} init -q`)
|
||||
shell(`mkdir -p {greeter} && git -C {greeter} init -q`)
|
||||
pt_write(`{greeter}/package.ludic`, `package "example.test/greeter"` + nl() + `version "1.0.0"` + nl() + "kind source" + nl() + `provides "Greet"` + nl() + `require "example.test/util" "1.0.0"` + nl())
|
||||
pt_write(`{greeter}/greet.ludic`, "function greet_hello() -> pointer { return \"hi\" }\n")
|
||||
pt_commit_tag(greeter, "v1.0.0")
|
||||
|
|
@ -105,14 +105,14 @@ function cmd_test_pkg() -> int {
|
|||
|
||||
# tamper with a stored file — verify must now flag it
|
||||
let uhash = capture_line(`grep 'example.test/util' {proj}/package.lock.ludic | sed 's/.*sha256://; s/\".*//'`)
|
||||
run(`echo tampered >> {store}/{uhash}/util.ludic`)
|
||||
shell(`echo tampered >> {store}/{uhash}/util.ludic`)
|
||||
if not shq(`( cd {proj} && {envp} {root}/bin/ludic verify > {work}/verify2.out 2>&1 )`) {
|
||||
ok("ludic verify detects a tampered store entry")
|
||||
} else { bad("ludic verify missed a tampered store entry") }
|
||||
# heal the store: a content-addressed entry is keyed by its hash-named dir, so
|
||||
# `ludic get` trusts an existing one — drop it first, then re-fetch a clean copy.
|
||||
run(`rm -rf {store}/{uhash}`)
|
||||
run(`( cd {proj} && {envp} {root}/bin/ludic get > /dev/null 2>&1 )`)
|
||||
shell(`rm -rf {store}/{uhash}`)
|
||||
shell(`( cd {proj} && {envp} {root}/bin/ludic get > /dev/null 2>&1 )`)
|
||||
if shq(`( cd {proj} && {envp} {root}/bin/ludic verify > {work}/verify3.out 2>&1 )`) {
|
||||
ok("re-fetching heals a tampered store entry (ludic verify green again)")
|
||||
} else { bad2("store did not heal after re-fetch", capture_line(`tail -1 {work}/verify3.out`)) }
|
||||
|
|
@ -125,7 +125,7 @@ function cmd_test_pkg() -> int {
|
|||
# relative to the compile CWD, so — exactly like `ludic build` — the game is compiled
|
||||
# from the repo root with the package linked in via an absolute LUDIC_MODULES.
|
||||
let epkg = `{proxy}/example.test/enginepkg`
|
||||
run(`mkdir -p {epkg} && git -C {epkg} init -q`)
|
||||
shell(`mkdir -p {epkg} && git -C {epkg} init -q`)
|
||||
pt_write(`{epkg}/package.ludic`, `package "example.test/enginepkg"` + nl() + `version "1.0.0"` + nl() + "kind source" + nl() + `provides "EnginePkg"` + nl())
|
||||
pt_write(`{epkg}/lib.ludic`, "property Health { hp: int = 0, max: int = 0 }\nmodel Unit { Health }\n@Public @OnSpawn(Unit) handler Init { Health.hp = Health.max }\nfunction bonus() -> int { return 7 }\n")
|
||||
pt_commit_tag(epkg, "v1.0.0")
|
||||
|
|
@ -143,7 +143,7 @@ function cmd_test_pkg() -> int {
|
|||
|
||||
# ---- namespace collision policy (v1: hard error) --------------------------
|
||||
let dupe = `{proxy}/example.test/dupe`
|
||||
run(`mkdir -p {dupe} && git -C {dupe} init -q`)
|
||||
shell(`mkdir -p {dupe} && git -C {dupe} init -q`)
|
||||
pt_write(`{dupe}/package.ludic`, `package "example.test/dupe"` + nl() + `version "1.0.0"` + nl() + "kind source" + nl() + `provides "Util"` + nl())
|
||||
pt_write(`{dupe}/dupe.ludic`, "function dupe_tag() -> pointer { return \"d\" }\n")
|
||||
pt_commit_tag(dupe, "v1.0.0")
|
||||
|
|
@ -155,7 +155,7 @@ function cmd_test_pkg() -> int {
|
|||
|
||||
# ---- prebuilt target matrix ----------------------------------------------
|
||||
let pb = `{proxy}/example.test/prebuilt`
|
||||
run(`mkdir -p {pb} && git -C {pb} init -q`)
|
||||
shell(`mkdir -p {pb} && git -C {pb} init -q`)
|
||||
pt_write(`{pb}/package.ludic`, `package "example.test/prebuilt"` + nl() + `version "1.0.0"` + nl() + "kind prebuilt" + nl() + `provides "Blob"` + nl() + `targets "made-up-target"` + nl())
|
||||
pt_write(`{pb}/README`, "a prebuilt lib shipping no artifact for this host\n")
|
||||
pt_commit_tag(pb, "v1.0.0")
|
||||
|
|
@ -165,7 +165,7 @@ function cmd_test_pkg() -> int {
|
|||
pt_write(`{proj}/package.ludic`, `package "app"` + nl() + `version "0.0.0"` + nl() + `require "example.test/greeter" "1.0.0"` + nl() + `require "example.test/util" "1.2.0"` + nl())
|
||||
|
||||
# ---- ludic vendor: hermetic copy ---------------------------------------------
|
||||
run(`( cd {proj} && {envp} {root}/bin/ludic get > /dev/null 2>&1 )`)
|
||||
shell(`( cd {proj} && {envp} {root}/bin/ludic get > /dev/null 2>&1 )`)
|
||||
if shq(`( cd {proj} && {envp} {root}/bin/ludic vendor > {work}/vendor.out 2>&1 )`) and file_exists(`{proj}/vendor/example.test/util/util.ludic`) {
|
||||
ok("ludic vendor copies the resolved packages into ./vendor")
|
||||
} else { bad2("ludic vendor failed", capture_line(`tail -1 {work}/vendor.out`)) }
|
||||
|
|
@ -175,7 +175,7 @@ function cmd_test_pkg() -> int {
|
|||
# Foo.* namespace (@Namespace) with no compiler edit; a consumer game imports it
|
||||
# and both take effect. Source path → runs everywhere (not dylib/macOS-gated).
|
||||
let cp = `{proxy}/example.test/coachpack`
|
||||
run(`mkdir -p {cp} && git -C {cp} init -q`)
|
||||
shell(`mkdir -p {cp} && git -C {cp} init -q`)
|
||||
pt_write(`{cp}/package.ludic`, `package "example.test/coachpack"` + nl() + `version "1.0.0"` + nl() + "kind source" + nl() + `provides "Coach"` + nl())
|
||||
pt_write(`{cp}/coach.ludic`, "property Score { value: int = 0 }\n@EngineSystem(Score, Update) function esys_score() -> void {\n let P = world_prop_id(\"Score\")\n let e = world_query_next(P, 0)\n if e >= 0 { world_set(e, P, 0, world_get(e, P, 0) + 1) }\n}\n@Namespace(Coach) function coach_bonus() -> int { return 99 }\n")
|
||||
pt_commit_tag(cp, "v1.0.0")
|
||||
|
|
@ -183,7 +183,7 @@ function cmd_test_pkg() -> int {
|
|||
pt_write(`{proj4}/package.ludic`, `package "game"` + nl() + `version "0.0.0"` + nl() + `require "example.test/coachpack" "1.0.0"` + nl())
|
||||
pt_write(`{proj4}/game.ludic`, "program Game {\n import \"example.test/coachpack/coach.ludic\"\n model Player { Score }\n @OnSpawn(Player) handler Init { }\n var frames: int = 0\n handler Boot phase Start { let e = world_spawn(world_model_id(\"Player\")) }\n handler Run phase Update {\n frames = frames + 1\n if frames >= 5 {\n let P = world_prop_id(\"Score\")\n let e = world_query_next(P, 0)\n print(world_get(e, P, 0))\n print(Coach.bonus())\n quit()\n }\n }\n}\n")
|
||||
if shq(`( cd {proj4} && {envp} {root}/bin/ludic get > {work}/get4.out 2>&1 )`) {
|
||||
run(`LUDIC_HOME={root} LUDIC_MODULES={proj4}/ludic_modules {root}/bin/ludicc --headless {proj4}/game.ludic -o {proj4}/game 2>{work}/gc4.err`)
|
||||
shell(`LUDIC_HOME={root} LUDIC_MODULES={proj4}/ludic_modules {root}/bin/ludicc --headless {proj4}/game.ludic -o {proj4}/game 2>{work}/gc4.err`)
|
||||
if file_exists(`{proj4}/game`) {
|
||||
let got = capture_line(`printf 'aaaaa' | {proj4}/game`)
|
||||
if got == "4 99" {
|
||||
|
|
@ -203,7 +203,7 @@ function cmd_test_pkg() -> int {
|
|||
}
|
||||
let tgt = `native-{capture_line("uname -m")}`
|
||||
let mp = `{proxy}/example.test/manapack`
|
||||
run(`mkdir -p {mp} && git -C {mp} init -q`)
|
||||
shell(`mkdir -p {mp} && git -C {mp} init -q`)
|
||||
pt_write(`{mp}/package.ludic`, `package "example.test/manapack"` + nl() + `version "1.0.0"` + nl() + "kind prebuilt" + nl() + `provides "Mana"` + nl() + `targets "{tgt}"` + nl())
|
||||
pt_write(`{mp}/manapack.ludic`, "program ManaPack {\n function module_init() -> void { world_register_prop(\"Mana\", 2) }\n @System(Update) function mana_regen() -> void {\n let M = world_prop_id(\"Mana\")\n let e = world_query_next(M, 0)\n if e >= 0 { let v = world_get(e, M, 0); world_set(e, M, 0, v + 1) }\n }\n function mana_bonus() -> int { return 42 }\n}\n")
|
||||
if shq(`( cd {mp} && LUDICC={root}/bin/ludicc {root}/bin/ludic build-lib manapack.ludic > {work}/bl.out 2>&1 )`) and file_exists(`{mp}/lib/{tgt}/libmanapack.dylib`) {
|
||||
|
|
@ -219,7 +219,7 @@ function cmd_test_pkg() -> int {
|
|||
} else { bad2("prebuilt ludic get failed", capture_line(`tail -1 {work}/get3.out`)) }
|
||||
# compile the ECS consumer from the repo root (so runtime/native resolves), then
|
||||
# link the module dylib via `ludic link-flags` (a real external build's link step).
|
||||
run(`LUDIC_HOME={root} {root}/bin/ludicc --headless {proj3}/game.ludic --emit-llvm -o {proj3}/game.ll 2>{work}/gc.err`)
|
||||
shell(`LUDIC_HOME={root} {root}/bin/ludicc --headless {proj3}/game.ludic --emit-llvm -o {proj3}/game.ll 2>{work}/gc.err`)
|
||||
let flags = capture_line(`( cd {proj3} && LUDIC_STORE={store} {root}/bin/ludic link-flags )`)
|
||||
if shq(`{cc()} -O2 -Wno-override-module {proj3}/game.ll {flags} -o {proj3}/game 2>{work}/gl.err`) {
|
||||
let got = capture_line(`printf 'aaaaaa' | {proj3}/game`)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue