From 48a7186c58802b61ba09a84d8c059c4f6408a22b Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Fri, 11 Sep 2026 19:43:35 +0700 Subject: [PATCH] Asking for the other target, and being told where it cannot go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --target= carries a value, so the flag test becomes a prefix match and the residual-argument filter uses the same test — otherwise -o out --target=X fell into the usage error. A wasm build defaults to a .wasm name, since the extension is what tells a runtime, and a reader, what the file is. flan run refuses the flag by name. It builds and execs, a cross-built module is not something this host execs, and choosing a runtime for it is not a decision this command should be making quietly. --- bin/main.ml | 49 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/bin/main.ml b/bin/main.ml index 6c35efa..272c564 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -46,6 +46,24 @@ let dev_flag = "--dev" let flags = [ no_checks_flag; dev_flag ] +(* [--target=wasm32-wasi], the one cross target. Unlike the flags above it + carries a value, so it is matched by prefix and stripped from the residual + arguments by the same test — otherwise [-o out --target=X] falls into the + usage error. *) +let target_prefix = "--target=" + +let is_flag a = + List.mem a flags || String.starts_with ~prefix:target_prefix a + +let target_of args = + List.find_map + (fun a -> + if String.starts_with ~prefix:target_prefix a then + Some (String.sub a (String.length target_prefix) + (String.length a - String.length target_prefix)) + else None) + args + let () = match Array.to_list Sys.argv with | _ :: "read" :: files when files <> [] -> @@ -82,10 +100,10 @@ let () = (Flan.Types.to_string f.ret) (Array.length f.slots)) p.fns)) files - | _ :: "emit" :: args when List.exists (fun a -> not (List.mem a flags)) args -> + | _ :: "emit" :: args when List.exists (fun a -> not (is_flag a)) args -> let checks = not (List.mem no_checks_flag args) in let dev = List.mem dev_flag args in - let files = List.filter (fun a -> not (List.mem a flags)) args in + let files = List.filter (fun a -> not (is_flag a)) args in List.iter (fun path -> with_errors path (fun () -> @@ -94,20 +112,28 @@ let () = | _ :: "build" :: path :: rest -> let checks = not (List.mem no_checks_flag rest) in let dev = List.mem dev_flag rest in + let target = target_of rest in let out = - match List.filter (fun a -> not (List.mem a flags)) rest with + match List.filter (fun a -> not (is_flag a)) rest with | [ "-o"; o ] -> o - | [] -> Filename.remove_extension (Filename.basename path) + | [] -> + let base = Filename.remove_extension (Filename.basename path) in + (* A wasm module is not an executable and must not be named like one: + the extension is what tells a runtime, and a reader, what it is. *) + (match target with + | Some t when String.starts_with ~prefix:"wasm32" t -> base ^ ".wasm" + | _ -> base) | _ -> prerr_endline - "usage: flan build [-o out] [--no-bounds-checks] [--dev]"; + "usage: flan build [-o out] [--no-bounds-checks] \ + [--dev] [--target=wasm32-wasi]"; exit 2 in with_errors path (fun () -> let l = load path in let p = Flan.Check.program l.decls in ignore (Flan.Build.executable - ~opts:{ Flan.Build.default with checks; dev } + ~opts:{ Flan.Build.default with checks; dev; target } ~csrcs:l.csrcs ~lflags:l.lflags p ~out)) (* The daemon an editor talks to: one session, the program it belongs to running beside it, and a socket. Unlike [flan reload] the session persists, @@ -146,6 +172,14 @@ let () = Printf.eprintf "%s %s llc %.1fms ld %.1fms\n" out (String.concat " " c.Flan.Session.fns) timing.Flan.Build.llc_ms timing.Flan.Build.link_ms) + (* [run] builds and execs. A .wasm is not executable, and picking a runtime + for it is a decision this command has no business making, so a cross + target is refused here by name rather than half-supported. *) + | _ :: "run" :: _ :: args when target_of args <> None -> + prerr_endline + "flan run: --target is refused — a cross-built module is not something \ + this host can exec. Use flan build --target=... and a wasm runtime."; + exit 2 | _ :: "run" :: path :: args -> with_errors path (fun () -> let exe = @@ -163,7 +197,8 @@ let () = | _ -> prerr_endline "usage: flan (read|parse|check|emit) ...\n\ - \ flan build [-o out] [--no-bounds-checks] [--dev]\n\ + \ flan build [-o out] [--no-bounds-checks] [--dev] \ + [--target=wasm32-wasi]\n\ \ flan run [args...]\n\ \ flan reload [-o out.so]\n\ \ flan dev [-s socket]";