Multiline shebang in OCaml?
In short, I'd like to abstract this shebang so I can literally copy and paste it into other .ML files without having to specify the filename each time:
#!/usr/bin/env ocamlscript -o hello
print_endline "Hello World!"
I realize I could just drop the -o hello
bit, but I'd like all the binaries to have开发者_StackOverflow社区 UNIX names (hello
), instead of Windows names (hello.ml.exe
).
You need a complex shebang to do this. A Clojure example that has the desired behavior:
":";exec clj -m `basename $0 .clj` $0 ${1+"$@"}
":";exit
Clojure is Java-based, which is why clj
needs the basename of the file (something
, not something.clj
). In order to get the basename, you need a multiline shebang, because a single line shebang can only handle a single, simple, static command line argument. In order to do multiline shebangs, you need a syntax which simultaneously:
- Sends shell commands to the shell
- Hides the shell commands from the main language
Does anyone know of OCaml trickery to do this? I've tried the following with no success:
(*
exec ocamlscript -o `basename $0 .ml` $0 ${1+"$@"}
exit
*)
let rec main = print_endline "Hello World!"
What you're looking for is a shell and Objective Caml polyglot (where the shell part invokes an ocaml interpreter to perform the real work). Here's a relatively simple one. Adapt to use ocamlscript
if necessary, though I don't see the point.
#!/bin/sh
"true" = let exec _ _ _ = "-*-ocaml-*- vim:set syntax=ocaml: " in
exec "ocaml" "$0" "$@"
;;
(* OCaml code proper starts here *)
print_endline "hello"
After some trials, I found this shebang:
#!/bin/sh
"true" = let x' = "" in (*'
sh script here
*) x'
It is sort of an improvement of Gilles’ proposal, as it permits to write a full shell script inside the OCaml comment, without being bothered at all with syntax incompatibilities.
The script must terminate (eg. with exec
or exit
) without reaching the end of the comment, otherwise a syntax error will occur. This can be fixed easily, but it is not very useful regarding the intended use of such a trick.
Here is a variant that entails zero runtime overhead on the OCaml side, but declares a new type name (choose it arbitrarily complicated if this is bothering):
#!/bin/sh
type int' (*' >&- 2>&-
sh script here
*)
For example, here is a script that executes the OCaml code with modules Str and Unix, and can also compile it when passed the parameter --compile
:
#!/bin/sh
type int' (*' >&- 2>&-
if [ "$1" = "--compile" ]; then
name="${0%.ml}"
ocamlopt -pp 'sed "1s/^#\!.*//"' \
str.cmxa unix.cmxa "$name.ml" -o "$name" \
|| exit
rm "$name".{cm*,o}
exit
else
exec ocaml str.cma unix.cma "$0" "$@"
fi
*)
I do not think that ocamlscript supports this. It may be worth submitting a feature request to the author to allow customization of the compiled binary's extension without specifying the full output basename.
精彩评论