FriendlyFruit
FriendlyFruit

Reputation: 103

OCaml - Parsing command-line options with arguments using Arg

I want to parse command line options with arguments in OCaml.

The module Arg of the standard library seems to do everything I need and there are some tutorials which explain how to use this module.

My problem is, that they all seem to share the same strange behavior when the argument of an option is missing. For example, executing the program from this example with ./a.out -d produces the following output:

./a.out: option '-d' needs an argument.
usage: ./a.out [-b] [-s string] [-d int]
  -b : set somebool to true
  -s : what follows -s sets some string
  -d : some int parameter
  -help  Display this list of options
  --help  Display this list of options
./a.out: ./a.out: option '-d' needs an argument.
usage: ./a.out [-b] [-s string] [-d int]
  -b : set somebool to true
  -s : what follows -s sets some string
  -d : some int parameter
  -help  Display this list of options
  --help  Display this list of options
.
usage: ./a.out [-b] [-s string] [-d int]
  -b : set somebool to true
  -s : what follows -s sets some string
  -d : some int parameter
  -help  Display this list of options
  --help  Display this list of options

I was not able to find out why the error/usage message is printed three times. This also happens to all the other code examples I found online. Is this a problem in the Arg module or is it somehow not used correctly in these examples?

Upvotes: 2

Views: 2167

Answers (1)

Richard-Degenne
Richard-Degenne

Reputation: 2949

I have managed to reproduce the bug with OCaml 4.04.2, but not with 4.02.3, so it would seem that there is some sort of regression going on there.

So, one thing you could do is sticking to an older version of OCaml, but I wouldn't recommend that.

Instead, you could use an alternative standard library, such as Jane Street's Core. It has a module named Command which allows you to write command-line interfaces just like the one you're trying to run.

An extensive tutorial for this module is available here.

As an example, here is the CLI from Rosetta using Command:

open Core

let spec =
  let open Command.Spec in
  empty
    +> flag "-b" (no_arg) ~doc:"Sets some flag"
    +> flag "-s" (optional_with_default "" string) ~doc:"STRING Some string parameter"
    +> flag "-d" (optional_with_default 0 int) ~doc:"INTEGER Some int parameter"

let command =
  Command.basic
    ~summary:"My awesome CLI"
    spec
    (fun some_flag some_string some_int () ->
       printf " %b '%s' %d\n" some_flag some_string some_int
    )

let () =
  Command.run command

EDIT : This bug was known and is going to be fixed in OCaml 4.05.

Upvotes: 4

Related Questions