Question

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 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!"
Was it helpful?

Solution

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"

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top