Skip to content

Instantly share code, notes, and snippets.

@jtpaasch
Last active April 6, 2022 18:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jtpaasch/fdc9283a04baed6d00788484d1ca37d2 to your computer and use it in GitHub Desktop.
Save jtpaasch/fdc9283a04baed6d00788484d1ca37d2 to your computer and use it in GitHub Desktop.
A simple example of using Cmdliner to create a command line tool
(executable
(name main)
(libraries findlib.dynload bap))
(lang dune 3.0)
(** A simple example of using [Cmdliner].
The basic idea is that you have a function you want to run (in the code
below, that function is [App.run]). That function might require arguments
(in the code below, [App.run] takes an optional string (a username).
With [Cmdliner], you declare the arguments you want to collect from the
command line, then you declare a "runner" (which the [Cmdliner]
documentation calls a "term"). Basically, a "runner" runs your function
(i.e., [App.run]) with the arguments it collects from the command line.
For more, see the documentation:
https://erratique.ch/software/cmdliner/doc/
*)
module App = struct
(* A simple app. This just prints an optional username given to it. *)
let run (username : string option) : unit =
Printf.printf "username: %s\n%!"
(match username with
| Some s -> s
| None -> "none given")
end
module Cli = struct
open Cmdliner
(* The following declares some info for the tool's help and manpage. *)
let version = "00"
let name = "tool"
let doc = "A simple command line tool"
let man = [
`S Manpage.s_description;
`P "This is the bit where we give a general description
of what this command line tool does.";
`P "We can put in multiple paragraphs of text here
but I'm actually not going to do that.";
`S Manpage.s_examples;
`P "I could give some examples here:";
`Pre "let foo = bar + 2";
]
let info = Cmd.info name ~doc ~man ~version
(* This declares a [-u foo] or [--username foo] command line argument. *)
let username =
let info = Arg.info ["u"; "username"]
~docv:"USERNAME"
~doc:"A username"
in
let parser = Arg.some Arg.string in
let default = None in
Arg.value (Arg.opt parser default info)
(* This creates a "runner," i.e., it parses the command line args and then
it feeds the parsed values as arguments into a specified function.
Here, we want to execute [App.run], but give it the value from the
the parsed [username] argument given on the command line. To declare
that, we define a [Term], where [const App.run] is the function we
want to execute, and then [$ username] says that the next argument
given to [App.run] should be the one we got from [username]. *)
let runner = Term.(const App.run $ username)
(* This declares the command. *)
let cmd = Cmd.v info runner
end
let () = exit (Cmdliner.Cmd.eval 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) -- -u dudleydoolittle
@jtpaasch
Copy link
Author

jtpaasch commented Apr 6, 2022

To run this, put the above files in a directory, then run make.

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