linol/cmdliner/cookbook.html
2025-12-01 22:21:55 +00:00

196 lines
40 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>cookbook (cmdliner.cookbook)</title><meta charset="utf-8"/><link rel="stylesheet" href="../_odoc-theme/odoc.css"/><meta name="generator" content="odoc 3.1.0"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><script src="../highlight.pack.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body class="odoc"><nav class="odoc-nav"><a href="index.html">Up</a> <a href="../index.html">Index</a> &#x00BB; <a href="index.html">cmdliner</a> &#x00BB; cookbook</nav><header class="odoc-preamble"><h1 id="cmdliner-cookbook"><a href="#cmdliner-cookbook" class="anchor"></a><code>Cmdliner</code> cookbook</h1><p>A few recipes and starting <a href="#blueprints" title="blueprints">blueprints</a> to describe your command lines with <a href="Cmdliner/index.html"><code>Cmdliner</code></a>.</p><p><b>Note.</b> Some of the code snippets here assume they are done after:</p><pre class="language-ocaml"><code>open Cmdliner
open Cmdliner.Term.Syntax</code></pre></header><div class="odoc-tocs"><nav class="odoc-toc odoc-local-toc"><ul><li><a href="#tips">Tips and pitfalls</a><ul><li><a href="#tip_avoid_default_command">Avoid default commands in groups</a></li><li><a href="#tip_avoid_default_option_values">Avoid default option values</a></li><li><a href="#tip_avoid_required_opt">Avoid required optional arguments</a></li><li><a href="#tip_avoid_manpages">Avoid making manpages your main documentation</a></li><li><a href="#tip_migrating">Migrating from other conventions</a></li><li><a href="#tip_src_structure">Source code structure</a></li><li><a href="#tip_tool_support">Installing completions and manpages</a><ul><li><a href="#tip_tool_support_with_opam">With <code>opam</code></a></li><li><a href="#top_tool_support_with_dune_install">With <code>dune build @install</code></a></li><li><a href="#tip_tool_support_with_opam_dune">With <code>opam</code> and <code>dune</code></a></li></ul></li></ul></li><li><a href="#conventions">Conventions</a><ul><li><a href="#conv_use_dash">Use <code>&quot;-&quot;</code> to specify <code>stdio</code> in file path arguments</a></li><li><a href="#conv_env_defaults">Environment variables as default modifiers</a></li></ul></li><li><a href="#args">Arguments</a><ul><li><a href="#args_positional">How do I define a positional argument?</a></li><li><a href="#args_optional">How do I define an optional argument?</a></li><li><a href="#args_required">How do I define a required argument?</a></li><li><a href="#args_detect_absent">How can I know if an argument was absent?</a></li><li><a href="#args_absent_doc">How do I document absent argument behaviours?</a></li><li><a href="#args_completion">How can I customize positional and option value completion?</a></li></ul></li><li><a href="#envs">Environment variables</a><ul><li><a href="#env_args">How can environment variables define defaults?</a></li><li><a href="#env_cmd">How do I document environment variables influencing a command?</a></li></ul></li><li><a href="#cmds">Commands</a><ul><li><a href="#cmds_exit_code_docs">How do I document command exit codes?</a></li><li><a href="#cmds_show_docs">How do I show help in a command group's default?</a></li><li><a href="#cmds_which_eval">Which <code>Cmd</code> evaluation function should I use?</a></li><li><a href="#cmds_howto_complete">How can my tool support command line completion?</a></li><li><a href="#cmds_listing">How can I list all the commands of my tool?</a></li><li><a href="#cmds_errmsg_styling">How can I suppress error message styling?</a></li></ul></li><li><a href="#manpage">Manpages</a><ul><li><a href="#manpage_hide">How do I prevent an item from being automatically listed?</a></li><li><a href="#manpage_synopsis">How can I write a better command synopsis section?</a></li><li><a href="#manpage_install">How can I install all the manpages of my tool?</a></li></ul></li><li><a href="#blueprints">Blueprints</a><ul><li><a href="#blueprint_min">Minimal</a></li><li><a href="#blueprint_tool">A simple tool</a></li><li><a href="#blueprint_cmds">A tool with subcommands</a></li></ul></li></ul></nav></div><div class="odoc-content"><h2 id="tips"><a href="#tips" class="anchor"></a>Tips and pitfalls</h2><p>Command line interfaces are a rather crude and inexpressive user interaction medium. It is tempting to try to be nice to users in various ways but this often backfires in confusing context sensitive behaviours. Here are a few tips and Cmdliner features you <b>should rather not use</b>.</p><h3 id="tip_avoid_default_command"><a href="#tip_avoid_default_command" class="anchor"></a>Avoid default commands in groups</h3><p>Command <a href="Cmdliner/Cmd/index.html#val-group" title="Cmdliner.Cmd.group">groups</a> can have a default command, that is be of the form <code>tool [CMD]</code>. Except perhaps at the top level of your tool, it's better to avoid them. They increase command line parsing ambiguities.</p><p>In particular if the default command has positional arguments, users are forced to use the <a href="cli.html#posargs" title="posargs">disambiguation token <code>--</code></a> to specify them so that they can be distinguished from command names. For example:</p><pre class="language-sh"><code>tool -- file …</code></pre><p>One thing that is acceptable is to have a default command that simply <a href="#cmds_show_docs" title="cmds_show_docs">shows documentation</a> for the group of subcommands as this not interfere with tool operation.</p><h3 id="tip_avoid_default_option_values"><a href="#tip_avoid_default_option_values" class="anchor"></a>Avoid default option values</h3><p>Optional arguments <a href="Cmdliner/Arg/index.html#val-opt" title="Cmdliner.Arg.opt">with values</a> can have a default value, that is be of the form <code>--opt[=VALUE]</code>. In general it is better to avoid them as they lead to context sensitive command lines specifications and surprises when users refine invocations. For examples suppose you have the synopsis</p><pre class="language-sh"><code>tool --opt[=VALUE] [FILE]</code></pre><p>Trying to refine the following invocation to add a <code>FILE</code> parameter is error prone and painful:</p><pre class="language-sh"><code>tool --opt</code></pre><p>There is more than one way but the easiest way is to specify:</p><pre class="language-sh"><code>tool --opt -- FILE</code></pre><p>which is not obvious unless you have <code>tool</code>'s cli hard wired in your brain. This would have been a careless refinement if <code>--opt</code> did not have a default option value.</p><h3 id="tip_avoid_required_opt"><a href="#tip_avoid_required_opt" class="anchor"></a>Avoid required optional arguments</h3><p>Cmdliner allows to define required optional arguments. Avoid doing this, it's a contradiction in the terms. In command line interfaces optional arguments are defined to be… optional, not doing so is surprising for your users. Use required positional arguments if arguments are required by your command invocation.</p><p>Required optional arguments can be useful though if your tool is not meant to be invoked manually but rather through scripts and has many required arguments. In this case they become a form of labelled arguments which can make invocations easier to understand.</p><h3 id="tip_avoid_manpages"><a href="#tip_avoid_manpages" class="anchor"></a>Avoid making manpages your main documentation</h3><p>Unless your tool is very simple, avoid making manpages the main documentation medium of your tool. The medium is rather limited and even though you can convert them to HTML, its cross references capabilities are rather limited which makes discussing your tool online more difficult.</p><p>Keep information in manpages to the minimum needed to operate your tool without having to leave the terminal too much and defer reference manuals, conceptual information and tutorials to a more evolved medium like HTML.</p><h3 id="tip_migrating"><a href="#tip_migrating" class="anchor"></a>Migrating from other conventions</h3><p>If you are porting your command line parsing to <code>Cmdliner</code> and that you have conventions that clash with <code>Cmdliner</code>'s ones but you need to preserve backward compatibility, one way of proceeding is to pre-process <code>Sys.argv</code> into a new array of the right shape before giving it to command <a href="Cmdliner/Cmd/index.html#eval" title="eval">evaluation functions</a> via the <code>?argv</code> optional argument.</p><p>These are two common cases:</p><ul><li>Long option names with a single dash like <code>-warn-error</code>. In this case simply prefix an additional <code>-</code> to these arguments when they occur in <code>Sys.argv</code> before the <code>--</code> argument; after it, all arguments are positional and to be treated literally.</li><li>Long option names with a single letter like <code>--X</code>. In this case simply chop the first <code>-</code> to make it a short option when they occur in <code>Sys.argv</code> before the <code>--</code> argument; after it all arguments are positional and to be treated literally.</li></ul><h3 id="tip_src_structure"><a href="#tip_src_structure" class="anchor"></a>Source code structure</h3><p>In general Cmdliner wants you to see your tools as regular OCaml functions that you make available to the shell. This means adopting the following source structure:</p><pre class="language-ocaml"><code>(* Implementation of your command. Except for exit codes does not deal with
command line interface related matters and is independent from
Cmdliner. *)
let exit_ok = 0
let tool … = …; exit_ok
(* Command line interface. Adds metadata to your [tool] function arguments
so that they can be parsed from the command line and documented. *)
open Cmdliner
open Cmdliner.Term.Syntax
let cmd = … (* Has a term that invokes [tool] *)
let main () = Cmd.eval' cmd
let () = if !Sys.interactive then () else exit (main ())</code></pre><p>In particular it is good for your readers' understanding that your program has a single point where it <a href="../ocaml/Stdlib/index.html#val-exit"><code>Stdlib.exit</code></a>s. This structure is also useful for playing with your program in the OCaml toplevel (REPL), you can invoke its <code>main</code> function without having the risk of it <code>exit</code>ing the toplevel.</p><p>If your tool named <code>tool</code> is growing into multiple commands which have a lot of definitions it is advised to:</p><ul><li>Gather command line definition commonalities such as argument converters or common options in a module called <code>Tool_cli</code>.</li><li>Define each command named <code>name</code> in a separate module <code>Cmd_name</code> which exports its command as a <code>val cmd : int Cmd.t</code> value.</li><li>Gather the commands with <a href="Cmdliner/Cmd/index.html#val-group"><code>Cmdliner.Cmd.group</code></a> in a source file called <code>tool_main.ml</code>.</li></ul><p>For an hypothetic tool named <code>tool</code> with commands <code>import</code>, <code>serve</code> and <code>user</code>, this leads to the following set of files:</p><pre class="language-ocaml"><code>cmd_import.ml cmd_serve.ml cmd_user.ml tool_cli.ml tool_main.ml
cmd_import.mli cmd_serve.mli cmd_user.mli tool_cli.mli</code></pre><p>The <code>.mli</code> files simply export commands:</p><pre class="language-ocaml"><code>val cmd : int Cmdliner.Cmd.t</code></pre><p>And the <code>tool_main.ml</code> gathers them with a <a href="Cmdliner/Cmd/index.html#val-group"><code>Cmdliner.Cmd.group</code></a>:</p><pre class="language-ocaml"><code>let cmd =
let default = Term.(ret (const (`Help (`Auto, None)))) (* show help *) in
Cmd.group (Cmd.info &quot;tool&quot;) ~default @@
[Cmd_import.cmd; Cmd_serve.cmd; Cmd_user.cmd]
let main () = Cmd.eval' cmd
let () = if !Sys.interactive then () else exit (main ())</code></pre><h3 id="tip_tool_support"><a href="#tip_tool_support" class="anchor"></a>Installing completions and manpages</h3><p>The <code>cmdliner</code> tool can be used to install completion scripts and manpages for you tool and its subcommands by using the dedicated <a href="cli.html#install_tool_completion" title="install_tool_completion"><code>install tool-completion</code></a> and <a href="cli.html#install_tool_manpages" title="install_tool_manpages"><code>install tool-manpages</code></a> subcommands.</p><p>To install both directly (and possibly other support files in the future) it is more concise to use the <code>install tool-support</code> command. Invoke with <code>--help</code> for more information.</p><h4 id="tip_tool_support_with_opam"><a href="#tip_tool_support_with_opam" class="anchor"></a>With <code>opam</code></h4><p>If you are installing your package with <code>opam</code> for a tool named <code>tool</code> located in the build at the path <code>$BUILD/tool</code>, you can add the following instruction after your build instructions in the <code>build:</code> field of your <code>opam</code> file (also works if your build system is not using a <code>.install</code> file).</p><pre class="language-sh"><code>build: [
[ … ] # Your regular build instructions
[&quot;cmdliner&quot; &quot;install&quot; &quot;tool-support&quot;
&quot;--update-opam-install=%{_:name}%.install&quot;
&quot;$BUILD/tool&quot; &quot;_build/cmdliner-install&quot;]]</code></pre><p>You need to specify the path to the built executable, as it cannot be looked up in the <code>PATH</code> yet. Also more than one tool can be specified in a single invocation and there is a syntax for specifying the actual tool name if it is renamed on install; see <code>--help</code> for more details.</p><p>If <code>cmdliner</code> is only an optional dependency of your package use the opam filter <code>{cmdliner:installed}</code> after the closing bracket of the command invocation.</p><h4 id="top_tool_support_with_dune_install"><a href="#top_tool_support_with_dune_install" class="anchor"></a>With <code>dune build @install</code></h4><p>Users of <code>dune</code> 3.5 or later can use the <code>cmdliner install tool-support</code> command described above, along with the <a href="https://dune.readthedocs.io/en/stable/reference/dune/rule.html#directory-targets"><code>directory-targets</code></a> feature and <a href="https://dune.readthedocs.io/en/latest/reference/dune/install.html"><code>install</code></a> stanza, to hook into the existing <code>dune build @install</code> command.</p><p>For an executable named <code>mytool</code> defined in a <code>dune</code> file, you can add the following to the same <code>dune</code> file to populate the completion scripts and manpages when running <code>dune build @install</code> (which is what is used by <code>opam install</code>):</p><pre class="language-dune"><code>(rule
(target (dir cmdliner-support))
(deps mytool.exe)
(action
(ignore-stdout
(run cmdliner install tool-support ./mytool.exe:mytool cmdliner-support))))
(install
(section share_root)
(dirs (cmdliner-support/share as .)))</code></pre><p>If these features of dune are not available to your project, you may instead need to update the <code>opam</code> file, see <a href="#tip_tool_support_with_opam_dune" title="tip_tool_support_with_opam_dune">these instructions</a>.</p><h4 id="tip_tool_support_with_opam_dune"><a href="#tip_tool_support_with_opam_dune" class="anchor"></a>With <code>opam</code> and <code>dune</code></h4><p>First make sure your understand the <a href="#tip_tool_support_with_opam" title="tip_tool_support_with_opam">above basic instructions</a> for <code>opam</code>. You then <a href="https://dune.readthedocs.io/en/stable/reference/packages.html#generating-opam-files">need to add a <code>.opam.template</code></a> file which inserts the <code>cmdliner install</code> instruction to the <code>build:</code> field of the opam file after your <code>dune</code> build instructions. For a tool named <code>mytool</code> the <code>mytool.opam.template</code> file should contain:</p><pre class="language-sh"><code>build: [
# These first two commands are what dune generates if you don't have
# a template
[&quot;dune&quot; &quot;subst&quot;] {dev}
[
&quot;dune&quot;
&quot;build&quot;
&quot;-p&quot;
name
&quot;-j&quot;
jobs
&quot;@install&quot;
&quot;@runtest&quot; {with-test}
&quot;@doc&quot; {with-doc}
]
# This is the important command for cmdliner's files
[&quot;cmdliner&quot; &quot;install&quot; &quot;tool-support&quot;
&quot;--update-opam-install=%{_:name}%.install&quot;
&quot;_build/install/default/bin/mytool&quot; {os-family != &quot;windows&quot;}
&quot;_build/install/default/bin/mytool.exe&quot; {os-family = &quot;windows&quot;}
&quot;_build/cmdliner-install&quot;]]</code></pre><h2 id="conventions"><a href="#conventions" class="anchor"></a>Conventions</h2><p>By simply using Cmdliner you are already abiding to a great deal of command line interface conventions. Here are a few other ones that are not necessarily enforced by the library but that are good to adopt for your users.</p><h3 id="conv_use_dash"><a href="#conv_use_dash" class="anchor"></a>Use <code>&quot;-&quot;</code> to specify <code>stdio</code> in file path arguments</h3><p>Whenever a command line argument specifies a file path to read or write you should let the user specify <code>-</code> to denote standard in or standard out, if possible. If you worry about a file sporting this name, note that the user can always specify it using <code>./-</code> for the argument.</p><p>Very often tools default to <code>stdin</code> or <code>stdout</code> when a file input or output is unspecified, here is typical argument definitions to support these conventions:</p><pre class="language-ocaml"><code>let infile =
let doc = &quot;$(docv) is the file to read from. Use $(b,-) for $(b,stdin)&quot; in
Arg.(value &amp; opt filepath &quot;-&quot; &amp; info [&quot;i&quot;, &quot;input-file&quot;] ~doc ~docv:&quot;FILE&quot;)
let outfile =
let doc = &quot;$(docv) is the file to write to. Use $(b,-) for $(b,stdout)&quot; in
Arg.(value &amp; opt filepath &quot;-&quot; &amp; info [&quot;o&quot;, &quot;output-file&quot;] ~doc ~docv:&quot;FILE&quot;)</code></pre><p>Here is <a href="../ocaml/Stdlib/index.html"><code>Stdlib</code></a> based code to read to a string a file or standard input if <code>-</code> is specified:</p><pre class="language-ocaml"><code>let read_file file =
let read file ic = try Ok (In_channel.input_all ic) with
| Sys_error e -&gt; Error (Printf.sprintf &quot;%s: %s&quot; file e)
in
let binary_stdin () = In_channel.set_binary_mode In_channel.stdin true in
try match file with
| &quot;-&quot; -&gt; binary_stdin (); read file In_channel.stdin
| file -&gt; In_channel.with_open_bin file (read file)
with Sys_error e -&gt; Error e</code></pre><p>Here is <a href="../ocaml/Stdlib/index.html"><code>Stdlib</code></a> based code to write a string to a file or standard output if <code>-</code> is specified:</p><pre class="language-ocaml"><code>let write_file file s =
let write file s oc = try Ok (Out_channel.output_string oc s) with
| Sys_error e -&gt; Error (Printf.sprintf &quot;%s: %s&quot; file e)
in
let binary_stdout () = Out_channel.(set_binary_mode stdout true) in
try match file with
| &quot;-&quot; -&gt; binary_stdout (); write file s Out_channel.stdout
| file -&gt; Out_channel.with_open_bin file (write file s)
with Sys_error e -&gt; Error e</code></pre><h3 id="conv_env_defaults"><a href="#conv_env_defaults" class="anchor"></a>Environment variables as default modifiers</h3><p>Cmdliner has support to back values defined by arguments with environment variables. The value specified via an environment variable should never take over an argument specified explicitely on the command line. The environment variable should be seen as providing the default value when the argument is absent.</p><p>This is exactly what Cmdliner's support for environment variables does, see <a href="#env_args" title="env_args">How can environment variables define defaults?</a></p><h2 id="args"><a href="#args" class="anchor"></a>Arguments</h2><h3 id="args_positional"><a href="#args_positional" class="anchor"></a>How do I define a positional argument?</h3><p>Positional arguments are extracted from the command line using <a href="Cmdliner/Arg/index.html#posargs" title="posargs">these combinators</a> which use zero-based indexing. The following example extracts the first argument and if the argument is absent from the command line it evaluates to <code>&quot;Revolt!&quot;</code>.</p><pre class="language-ocaml"><code>let msg =
let doc = &quot;$(docv) is the message to utter.&quot; and docv = &quot;MSG&quot; in
Arg.(value &amp; pos 0 string &quot;Revolt!&quot; &amp; info [] ~doc ~docv)</code></pre><h3 id="args_optional"><a href="#args_optional" class="anchor"></a>How do I define an optional argument?</h3><p>Optional arguments are extracted from the command line using <a href="Cmdliner/Arg/index.html#optargs" title="optargs">these combinators</a>. The actual option name is defined in the <a href="Cmdliner/Arg/index.html#val-info"><code>Cmdliner.Arg.info</code></a> structure without dashes. One character strings define short options, others long options (see the <a href="cli.html#optargs" title="optargs">parsed syntax</a>).</p><p>The following defines the <code>-l</code> and <code>--loud</code> options. This is a simple command line argument without a value also known as a command line <em>flag</em>. The term <code>loud</code> evaluates to <code>false</code> when the argument is absent on the command line and <code>true</code> otherwise.</p><pre class="language-ocaml"><code>let loud =
let doc = &quot;Say the message loudly.&quot; in
Arg.(value &amp; flag &amp; info [&quot;l&quot;; &quot;loud&quot;] ~doc)</code></pre><p>The following defines the <code>-m</code> and <code>--message</code> options. The term <code>msg</code> evalutes to <code>&quot;Revolt!&quot;</code> when the option is absent on the command line.</p><pre class="language-ocaml"><code>let msg =
let doc = &quot;$(docv) is the message to utter.&quot; and docv = &quot;MSG&quot; in
Arg.(value &amp; opt string &quot;Revolt!&quot; &amp; info [&quot;m&quot;; &quot;message&quot;] ~doc ~docv)</code></pre><h3 id="args_required"><a href="#args_required" class="anchor"></a>How do I define a required argument?</h3><p>Some of the constraints on the presence of arguments occur when the specification of arguments is <a href="Cmdliner/Arg/index.html#argterms" title="argterms">converted</a> to terms. The following says that the first positional argument is required:</p><pre class="language-ocaml"><code>let msg =
let msg = &quot;$(docv) is the message to utter.&quot; and docv = &quot;MSG&quot; in
Arg.(required &amp; pos 0 (some string) None &amp; info [] ~absent ~doc ~docv)</code></pre><p>The value <code>msg</code> ends up being a term of type <code>string</code>. If the argument is not provided, Cmdliner will automatically bail out during evaluation with an error message.</p><p>Note that while it is possible to define required positional argument it is <a href="#tip_avoid_required_opt" title="tip_avoid_required_opt">discouraged</a>.</p><h3 id="args_detect_absent"><a href="#args_detect_absent" class="anchor"></a>How can I know if an argument was absent?</h3><p>Most <a href="Cmdliner/Arg/index.html#posargs" title="posargs">positional</a> and <a href="Cmdliner/Arg/index.html#optargs" title="optargs">optional</a> arguments have a default value. You can use a <code>None</code> for the default argument and the <a href="Cmdliner/Arg/index.html#val-some"><code>Cmdliner.Arg.some</code></a> or <a href="Cmdliner/Arg/index.html#val-some'"><code>Cmdliner.Arg.some'</code></a> combinators on your argument converter which simply wrap its result in a <code>Some</code>.</p><pre class="language-ocaml"><code>let msg =
let msg = &quot;$(docv) is the message to utter.&quot; in
let absent = &quot;Random quote.&quot; in
Arg.(value &amp; pos 0 (some string) None &amp; info [] ~absent ~doc ~docv:&quot;MSG&quot;)</code></pre><p>There is more than one way to document the value when it is absent. See <a href="#args_absent_doc" title="args_absent_doc">How do I document absent argument behaviours?</a></p><h3 id="args_absent_doc"><a href="#args_absent_doc" class="anchor"></a>How do I document absent argument behaviours?</h3><p>There are three ways to document the behaviour when an argument is unspecified on the command line.</p><ul><li>If you specify a default value in the argument combinator, this value gets printed in bold using the <a href="Cmdliner/Arg/index.html#val-conv_printer" title="Cmdliner.Arg.conv_printer">printer</a> of the converter.</li><li>If you are using the <a href="Cmdliner/Arg/index.html#val-some'"><code>Cmdliner.Arg.some'</code></a> and <a href="Cmdliner/Arg/index.html#val-some"><code>Cmdliner.Arg.some</code></a> there is an optional <code>none</code> argument that allows you to specify the default value. If you can exhibit this value at definition point use <a href="Cmdliner/Arg/index.html#val-some'"><code>Cmdliner.Arg.some'</code></a>, the underlying converter's <a href="Cmdliner/Arg/index.html#val-conv_printer" title="Cmdliner.Arg.conv_printer">printer</a> will be used. If not you can specify it as a string rendered in bold via <a href="Cmdliner/Arg/index.html#val-some"><code>Cmdliner.Arg.some</code></a>.</li><li>If you want to describe a more complex, but short, behaviour use the <code>~absent</code> parameter of <a href="Cmdliner/Arg/index.html#val-info"><code>Cmdliner.Arg.info</code></a>. Using this parameter overrides the two previous ways. See <a href="#args_detect_absent" title="args_detect_absent">this</a> example.</li></ul><h3 id="args_completion"><a href="#args_completion" class="anchor"></a>How can I customize positional and option value completion?</h3><p>Positional argument values and option values are completed according to the <a href="Cmdliner/Arg/index.html#argconv" title="argconv">argument converter</a> you use for defining the optional or positional argument.</p><p>A couple of predefined argument converter like <a href="Cmdliner/Arg/index.html#val-path"><code>Cmdliner.Arg.path</code></a>, <a href="Cmdliner/Arg/index.html#val-filepath"><code>Cmdliner.Arg.filepath</code></a> and <a href="Cmdliner/Arg/index.html#val-dirpath"><code>Cmdliner.Arg.dirpath</code></a> or <a href="Cmdliner/Arg/index.html#val-enum"><code>Cmdliner.Arg.enum</code></a> automatically handle this for you.</p><p>If you would like to perform custom or more elaborate context sensitive completions you can define your own argument converter with a completion defined with <a href="Cmdliner/Arg/Completion/index.html#val-make"><code>Cmdliner.Arg.Completion.make</code></a>.</p><p>Here is an example where the first positional argument is completed with the filenames found in a directory specified via the <code>--dir</code> option (which defaults to the current working directory if unspecified).</p><pre class="language-ocaml"><code>let dir = Arg.(value &amp; opt dirpath &quot;.&quot; &amp; info [&quot;d&quot;; &quot;dir&quot;])
let dir_filenames_conv =
let complete dir ~token = match dir with
| None -&gt; Error &quot;Could not determine directory to lookup&quot;
| Some dir -&gt;
match Array.to_list (Sys.readdir dir) with
| exception Sys_error e -&gt; Error (String.concat &quot;: &quot; [dir; e])
| fnames -&gt;
let fnames = List.filter (String.starts_with ~prefix:token) fnames in
Ok (List.map Arg.Completion.string fnames)
in
let completion = Arg.Completion.make ~context:dir complete in
Arg.Conv.of_conv ~completion Arg.string
let pos0 = Arg.(required &amp; pos 0 (some dir_filenames_conv) None &amp; info [])</code></pre><p>Note that when you use <code>pos0</code> in a command line definition you also need to make sure <code>dir</code> is part of the term otherwise the context will always be <code>None</code>:</p><pre class="language-ocaml"><code>let+ pos0 and+ dir and+ … in …</code></pre><h2 id="envs"><a href="#envs" class="anchor"></a>Environment variables</h2><h3 id="env_args"><a href="#env_args" class="anchor"></a>How can environment variables define defaults?</h3><p>As mentioned in <a href="#conv_env_defaults" title="conv_env_defaults">Environment variables as default modifiers</a>, any non-required argument can be defined by an environment variable when absent. This works by specifying the <code>env</code> argument in the argument's <a href="Cmdliner/Arg/index.html#val-info"><code>Cmdliner.Arg.info</code></a> information. For example:</p><pre class="language-ocaml"><code>let msg =
let doc = &quot;$(docv) is the message to utter.&quot; and docv = &quot;MSG&quot; in
let env = Cmd.Env.info &quot;MESSAGE&quot; in
Arg.(value &amp; pos 0 string &quot;Revolt!&quot; &amp; info [] ~env ~doc ~docv)</code></pre><p>When the first positional argument is absent it takes the default value <code>&quot;Revolt!&quot;</code>, unless the <code>MESSAGE</code> variable is defined in the environment in which case it takes its value.</p><p>Cmdliner handles the environment variable lookup for you. By using the <code>msg</code> term in your command definition all this gets automatically documented in the tool help.</p><h3 id="env_cmd"><a href="#env_cmd" class="anchor"></a>How do I document environment variables influencing a command?</h3><p>Environment variable that are used to change <a href="#env_args" title="env_args">argument defaults</a> automatically get documented in a command's man page when you use the argument's term in the command's term.</p><p>However if your command implementation looks up other variables and you wish to document them in the command's man page, use the <code>envs</code> argument of <a href="Cmdliner/Cmd/index.html#val-info"><code>Cmdliner.Cmd.info</code></a> or the <code>docs_env</code> argument of <a href="Cmdliner/Arg/index.html#val-info"><code>Cmdliner.Arg.info</code></a>.</p><p>This documents in the <a href="Cmdliner/Manpage/index.html#val-s_environment"><code>Cmdliner.Manpage.s_environment</code></a> manual section of <code>tool</code> that <code>EDITOR</code> is looked up to find the tool to invoke to edit the files:</p><pre class="language-ocaml"><code>let editor_env = &quot;EDITOR&quot;
let tool … = … Sys.getenv_opt editor_env
let cmd =
let env = Cmd.Env.info editor_env ~doc:&quot;The editor used to edit files.&quot; in
Cmd.make (Cmd.info &quot;tool&quot; ~envs:[env]) @@
</code></pre><h2 id="cmds"><a href="#cmds" class="anchor"></a>Commands</h2><h3 id="cmds_exit_code_docs"><a href="#cmds_exit_code_docs" class="anchor"></a>How do I document command exit codes?</h3><p>Exit codes are documentd by <a href="Cmdliner/Cmd/Exit/index.html#type-info"><code>Cmdliner.Cmd.Exit.info</code></a> values and must be given to the command's <a href="Cmdliner/Cmd/index.html#type-info"><code>Cmdliner.Cmd.info</code></a> value via the <code>exits</code> optional arguments. For example:</p><pre class="language-ocaml"><code>let conf_not_found = 1
let tool … =
let tool_cmd =
let exits =
Cmd.Exit.info conf_not_found &quot;if no configuration could be found.&quot; ::
Cmd.Exit.defaults
in
Cmd.make (Cmd.info &quot;mycmd&quot; ~exits) @@
</code></pre><h3 id="cmds_show_docs"><a href="#cmds_show_docs" class="anchor"></a>How do I show help in a command group's default?</h3><p>While it is usually <a href="#tip_avoid_default_command" title="tip_avoid_default_command">not advised</a> to have a default command in a group, just showing docs is acceptable. A term can request Cmdliner's generated help by using <a href="Cmdliner/Term/index.html#val-ret"><code>Cmdliner.Term.ret</code></a>:</p><pre class="language-ocaml"><code>let group_cmd =
let default = Term.(ret (const (`Help (`Auto, None)))) (* show help *) in
Cmd.group (Cmd.info &quot;group&quot;) ~default @@
[first_cmd; second_cmd]</code></pre><h3 id="cmds_which_eval"><a href="#cmds_which_eval" class="anchor"></a>Which <code>Cmd</code> evaluation function should I use?</h3><p>There are (too) many <a href="Cmdliner/Cmd/index.html#eval" title="eval">command evaluation</a> functions. They have grown organically in a rather ad-hoc manner. Some of these are there for backwards compatibility reasons and advanced usage for complex tools.</p><p>Here are the main ones to use and why you may want to use them which essentially depends on how you want to handle errors and exit codes in your tool function.</p><ul><li><a href="Cmdliner/Cmd/index.html#val-eval"><code>Cmdliner.Cmd.eval</code></a>. This forces your tool function to return <code>()</code>. The evaluation function always returns an exit code of <code>0</code> unless a command line parsing error occurs.</li><li><a href="Cmdliner/Cmd/index.html#val-eval'"><code>Cmdliner.Cmd.eval'</code></a>. <b>Recommended</b>. This forces your tool function to return an exit code <code>exit</code> which is returned by the evaluation function unless a command line parsing error occurs. This is the recommended function to use as it forces you to think about how to report errors and design useful exit codes for users.</li><li><a href="Cmdliner/Cmd/index.html#val-eval_result"><code>Cmdliner.Cmd.eval_result</code></a> is akin to <a href="Cmdliner/Cmd/index.html#val-eval"><code>Cmdliner.Cmd.eval</code></a> except it forces your function to return either <code>Ok ()</code> or <code>Error msg</code>. The evaluation function returns with exit code <code>0</code> unless <code>Error msg</code> is computed in which case <code>msg</code> is printed on the error stream prefixed by the executable name and the evaluation function returns with exit code <a href="Cmdliner/Cmd/Exit/index.html#val-some_error"><code>Cmdliner.Cmd.Exit.some_error</code></a>.</li><li><a href="Cmdliner/Cmd/index.html#val-eval_result'"><code>Cmdliner.Cmd.eval_result'</code></a> is akin to <a href="Cmdliner/Cmd/index.html#val-eval_result"><code>Cmdliner.Cmd.eval_result</code></a>, except the <code>Ok</code> case carries an exit code which is returned by the evaluation function.</li></ul><h3 id="cmds_howto_complete"><a href="#cmds_howto_complete" class="anchor"></a>How can my tool support command line completion?</h3><p>The command line interface manual has all <a href="cli.html#cli_completion" title="cli_completion">the details</a> and <a href="cli.html#install_tool_completion" title="install_tool_completion">specific instructions</a> for complementing your tool install. See also <a href="#tip_tool_support" title="tip_tool_support">Installing completions and manpages</a>.</p><h3 id="cmds_listing"><a href="#cmds_listing" class="anchor"></a>How can I list all the commands of my tool?</h3><p>In a shell the invocation <code>cmdliner tool-commands $TOOL</code> lists every command of the tool $TOOL.</p><h3 id="cmds_errmsg_styling"><a href="#cmds_errmsg_styling" class="anchor"></a>How can I suppress error message styling?</h3><p>Since Cmdliner 2.0, error message printed on <code>stderr</code> use styled text with ANSI escapes. Styled text is disabled if one of the conditions mentioned <a href="cli.html#error_message_styling" title="error_message_styling">here</a> is met.</p><p>If you want to be more aggressive in suppressing them you can use the <code>err</code> formatter argument of <a href="Cmdliner/Cmd/index.html#eval" title="eval">command evaluation</a> functions with a suitable formatter on which a function like <a href="https://erratique.ch/software/more/doc/More/Fmt/index.html#val-strip_styles">this one</a> has been applied that automatically strips the styling.</p><h2 id="manpage"><a href="#manpage" class="anchor"></a>Manpages</h2><h3 id="manpage_hide"><a href="#manpage_hide" class="anchor"></a>How do I prevent an item from being automatically listed?</h3><p>In general it's not a good idea to hide stuff from your users but in case an item needs to be hidden you can use the special <a href="Cmdliner/Manpage/index.html#val-s_none"><code>Cmdliner.Manpage.s_none</code></a> section name. This ensures the item does not get listed in any section.</p><pre class="language-ocaml"><code>let secret = Arg.(value &amp; flag &amp; info [&quot;super-secret&quot;] ~docs:Manpage.s_none)</code></pre><h3 id="manpage_synopsis"><a href="#manpage_synopsis" class="anchor"></a>How can I write a better command synopsis section?</h3><p>Define the <a href="Cmdliner/Manpage/index.html#val-s_synopsis"><code>Cmdliner.Manpage.s_synopsis</code></a> section in the manpage of your command. It takes over the one generated by Cmdliner. For example:</p><pre class="language-ocaml"><code>let man = [
`S Manpage.s_synopsis;
`P &quot;$(cmd) $(b,--) $(i,TOOL) [$(i,ARG)]…&quot;; `Noblank;
`P &quot;$(cmd) $(i,COMMAND) …&quot;;
`S Manpage.s_description;
`P &quot;Without a command $(cmd) invokes $(i,TOOL)&quot;; ]</code></pre><h3 id="manpage_install"><a href="#manpage_install" class="anchor"></a>How can I install all the manpages of my tool?</h3><p>The command line interface manual <a href="cli.html#install_tool_manpages" title="install_tool_manpages">the details</a> on how to install the manpages of your tool and its subcommands. See also <a href="#tip_tool_support" title="tip_tool_support">Installing completions and manpages</a>.</p><h2 id="blueprints"><a href="#blueprints" class="anchor"></a>Blueprints</h2><p>These blueprints when copied to a <code>src.ml</code> file can be compiled and run with:</p><pre class="language-sh"><code>ocamlfind ocamlopt -package cmdliner -linkgpkg src.ml
./a.out --help</code></pre><p>More concrete examples can be found on the <a href="examples.html" title="examples">examples page</a> and the <a href="tutorial.html" title="tutorial">tutorial</a> may help too.</p><p>These examples follow a conventional <a href="#tip_src_structure" title="tip_src_structure">Source code structure</a>.</p><h3 id="blueprint_min"><a href="#blueprint_min" class="anchor"></a>Minimal</h3><p>A minimal example.</p><pre class="language-ocaml"><code>let tool () = Cmdliner.Cmd.Exit.ok
open Cmdliner
open Cmdliner.Term.Syntax
let cmd =
Cmd.make (Cmd.info &quot;TODO&quot; ~version:&quot;v2.1.0&quot;) @@
let+ unit = Term.const () in
tool unit
let main () = Cmd.eval' cmd
let () = if !Sys.interactive then () else exit (main ())</code></pre><h3 id="blueprint_tool"><a href="#blueprint_tool" class="anchor"></a>A simple tool</h3><p>This is a tool that has a flag, an optional positional argument for specifying an input file. It also responds to the <code>--version</code> option.</p><pre class="language-ocaml"><code>let exit_todo = 1
let tool ~flag ~infile = exit_todo
open Cmdliner
open Cmdliner.Term.Syntax
let flag = Arg.(value &amp; flag &amp; info [&quot;flag&quot;] ~doc:&quot;The flag&quot;)
let infile =
let doc = &quot;$(docv) is the input file. Use $(b,-) for $(b,stdin).&quot; in
Arg.(value &amp; pos 0 filepath &quot;-&quot; &amp; info [] ~doc ~docv:&quot;FILE&quot;)
let cmd =
let doc = &quot;The tool synopsis is TODO&quot; in
let man = [
`S Manpage.s_description;
`P &quot;$(cmd) does TODO&quot; ]
in
let exits =
Cmd.Exit.info exit_todo ~doc:&quot;When there is stuff todo&quot; ::
Cmd.Exit.defaults
in
Cmd.make (Cmd.info &quot;TODO&quot; ~version:&quot;v2.1.0&quot; ~doc ~man ~exits) @@
let+ flag and+ infile in
tool ~flag ~infile
let main () = Cmd.eval' cmd
let () = if !Sys.interactive then () else exit (main ())</code></pre><h3 id="blueprint_cmds"><a href="#blueprint_cmds" class="anchor"></a>A tool with subcommands</h3><p>This is a tool with two subcommands <code>hey</code> and <code>ho</code>. If your tools grows many subcommands you may want to follow these <a href="#tip_src_structure" title="tip_src_structure">source code conventions</a>.</p><pre class="language-ocaml"><code>let hey () = Cmdliner.Cmd.Exit.ok
let ho () = Cmdliner.Cmd.Exit.ok
open Cmdliner
open Cmdliner.Term.Syntax
let flag = Arg.(value &amp; flag &amp; info [&quot;flag&quot;] ~doc:&quot;The flag&quot;)
let infile =
let doc = &quot;$(docv) is the input file. Use $(b,-) for $(b,stdin).&quot; in
Arg.(value &amp; pos 0 filepath &quot;-&quot; &amp; info [] ~doc ~docv:&quot;FILE&quot;)
let hey_cmd =
let doc = &quot;The hey command synopsis is TODO&quot; in
Cmd.make (Cmd.info &quot;hey&quot; ~doc) @@
let+ unit = Term.const () in
ho ()
let ho_cmd =
let doc = &quot;The ho command synopsis is TODO&quot; in
Cmd.make (Cmd.info &quot;ho&quot; ~doc) @@
let+ unit = Term.const () in
ho unit
let cmd =
let doc = &quot;The tool synopsis is TODO&quot; in
Cmd.group (Cmd.info &quot;TODO&quot; ~version:&quot;v2.1.0&quot; ~doc) @@
[hey_cmd; ho_cmd]
let main () = Cmd.eval' cmd
let () = if !Sys.interactive then () else exit (main ())</code></pre></div></body></html>