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]";