Skip to content

Instantly share code, notes, and snippets.

@jtpaasch
Created April 6, 2022 18:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jtpaasch/8a0ec61203a1b224300967472e774e50 to your computer and use it in GitHub Desktop.
Save jtpaasch/8a0ec61203a1b224300967472e774e50 to your computer and use it in GitHub Desktop.
An example of subcommands with OCaml's Cmdliner
(executable
(name main)
(libraries findlib.dynload bap))
(lang dune 3.0)
(** A simple example of using [Cmdliner] to make subcommands. *)
module C = Cmdliner
type config = { verbose : bool; dryrun : bool; }
module Common_cli = struct
let package_em (verbose : bool) (dryrun : bool) : config =
{ verbose; dryrun }
let docs = C.Manpage.s_common_options
let verbose =
let info = C.Arg.info ["v"; "verbose"]
~docs (* List under COMMON OPTIONS *)
~doc:"Print verbose output."
in
C.Arg.value (C.Arg.flag info)
let dryrun =
let info = C.Arg.info ["d"; "dryrun"]
~docs (* List under COMMON OPTIONS *)
~doc:"Do a dry run."
in
C.Arg.value (C.Arg.flag info)
let runner = C.Term.(const package_em $ verbose $ dryrun)
end
module Foo = struct
let run (conf : config) (a : string) : unit =
Printf.printf "Running Foo\n a: %s\n verbose: %b\n dryrun: %b\n%!"
a conf.verbose conf.dryrun
end
module Foo_cli = struct
let name = "foo"
let doc = "A simple 'foo' command"
let man = [
`S C.Manpage.s_description;
`P "Here is a longer description of the 'foo' command.";
]
let info = C.Cmd.info name ~doc ~man
let a =
let info = C.Arg.info ["a"; "option-a"]
~docv:"A"
~doc:"A command-line option 'a'"
in
let parser = C.Arg.string in
let default = "" in
C.Arg.value (C.Arg.opt parser default info)
let runner = C.Term.(const Foo.run $ Common_cli.runner $ a)
let cmd = C.Cmd.v info runner
end
module Bar = struct
let run (conf : config) (p : int) : unit =
Printf.printf "Running Bar\n p: %d\n verbose: %b\n dryrun: %b\n%!"
p conf.verbose conf.dryrun
end
module Bar_cli = struct
let name = "bar"
let doc = "A simple 'bar' command"
let man = [
`S C.Manpage.s_description;
`P "Here is a longer description of the 'bar' command.";
]
let info = C.Cmd.info name ~doc ~man
let b =
let info = C.Arg.info ["b"; "option-b"]
~docv:"B"
~doc:"A command-line option 'b'"
in
let parser = C.Arg.int in
let default = 0 in
C.Arg.value (C.Arg.opt parser default info)
let runner = C.Term.(const Bar.run $ Common_cli.runner $ b)
let cmd = C.Cmd.v info runner
end
module Main_cli = struct
let name = "main.exe"
let doc = "A simple command with some subcommands"
let info = C.Cmd.info name ~doc
let cmd = C.Cmd.group info [Foo_cli.cmd; Bar_cli.cmd]
end
let () = exit (C.Cmd.eval Main_cli.cmd)
TOOL := main.exe
#####################################################
# DEFAULT
#####################################################
.DEFAULT_GOAL := all
all: clean run
#####################################################
# THE TOOL
#####################################################
.PHONY: clean
clean:
dune clean
build:
dune build ./$(TOOL)
run: build
dune exec ./$(TOOL) -- foo -a "bizz bazz"
@jtpaasch
Copy link
Author

jtpaasch commented Apr 6, 2022

To run this, put the above files into a folder and run make.

Try also:

  • ./_build/default/main.exe --help
  • ./_build/default/main.exe foo --help
  • ./_build/default/main.exe bar --help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment