linol/cmdliner/_doc-dir/odoc-pages/cli.mld
2025-12-01 22:21:55 +00:00

524 lines
19 KiB
Text
Raw 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.

{0:cmdline Command line interface}
This manual describes how your tool ends up interacting
with shells when you use Cmdliner.
{1:invocation Tool invocation}
For tools evaluating a command without subcommands the most general
form of invocation is:
{v
tool [OPTION]… [ARG]…
v}
The tool automatically reponds to the [--help] option by printing
{{!help}the help}. If a version string is provided in the
{{!Cmdliner.Cmd.val-info}command information}, it also automatically
responds to the [--version] option by printing this string on standard
output.
Command line arguments are either {{!optargs}{e optional}} or
{{!posargs}{e positional}}. Both can be freely interleaved but since
[Cmdliner] accepts many optional forms this may result in
ambiguities. The special {{!posargs} token [--]} can be used to
resolve them: anything that follows it is treated as a positional
argument.
Tools evaluating commands with subcommands have this form of invocation
{v
tool [COMMAND]… [OPTION]… [ARG]…
v}
Commands automatically respond to the [--help] option by printing
{{!help}their help}. The sequence of [COMMAND] strings must be the first
strings following the tool name as soon as an optional argument is
seen the search for a subcommand stops.
{1:args Arguments}
{2:optargs Optional arguments}
An optional argument is specified on the command line by a {e name}
possibly followed by a {e value}.
The name of an option can be short or long.
{ul
{- A {e short} name is a dash followed by a single alphanumeric
character: [-h], [-q], [-I].}
{- A {e long} name is two dashes followed by alphanumeric
characters and dashes: [--help], [--silent], [--ignore-case].}}
More than one name may refer to the same optional argument. For
example in a given program the names [-q], [--quiet] and [--silent]
may all stand for the same boolean argument indicating the program to
be quiet.
The value of an option can be specified in three different ways.
{ul
{- As the next token on the command line: [-o a.out], [--output a.out].}
{- Glued to a short name: [-oa.out].}
{- Glued to a long name after an equal character: [--output=a.out].}}
Glued forms are especially useful if the value itself starts with a
dash as is the case for negative numbers, [--min=-10].
An optional argument without a value is either a {e flag} (see
{!Cmdliner.Arg.flag}, {!Cmdliner.Arg.vflag}) or an optional argument with
an optional value (see the [~vopt] argument of {!Cmdliner.Arg.opt}).
Short flags can be grouped together to share a single dash and the
group can end with a short option. For example assuming [-v] and
[-x] are flags and [-f] is a short option:
{ul
{- [-vx] will be parsed as [-v -x].}
{- [-vxfopt] will be parsed as [-v -x -fopt].}
{- [-vxf opt] will be parsed as [-v -x -fopt].}
{- [-fvx] will be parsed as [-f=vx].}}
{2:posargs Positional arguments}
Positional arguments are tokens on the command line that are not
option names and are not the value of an optional argument. They are
numbered from left to right starting with zero.
Since positional arguments may be mistaken as the optional value of an
optional argument or they may need to look like option names, anything
that follows the special token ["--"] on the command line is
considered to be a positional argument:
{v
tool --option -- but --now we -are --all positional --argu=ments
v}
{2:constraints Constraints on option names}
Using the cmdliner library puts the following constraints on your
command line interface:
{ul
{- The option names [--cmdliner] and [--__complete] are reserved by the
library.}
{- The option name [--help], (and [--version] if you specify a version
string) is reserved by the library. Using it as a term or option
name may result in undefined behaviour.}
{- Defining the same option or command name via two different
arguments or terms is illegal and raises [Invalid_argument].}}
{1:envlookup Environment variables}
Non-required command line arguments can be backed up by an environment
variable. If the argument is absent from the command line and
the environment variable is defined, its value is parsed using the
argument converter and defines the value of the argument.
For {!Cmdliner.Arg.flag} and {!Cmdliner.Arg.flag_all} that do not have an
argument converter a boolean is parsed from the lowercased variable value
as follows:
{ul
{- [""], ["false"], ["no"], ["n"] or ["0"] is [false].}
{- ["true"], ["yes"], ["y"] or ["1"] is [true].}
{- Any other string is an error.}}
Note that environment variables are not supported for
{!Cmdliner.Arg.vflag} and {!Cmdliner.Arg.vflag_all}.
{1:help Help and man pages}
Help and man pages are are generated when you call your tool or a subcommand
with [--help]. By default, if the [TERM] environment variable
is not [dumb] or unset, the tool tries to {{!paging}page} the manual
so that you can directly search it. Otherwise it outputs the manual
as plain text.
Alternative help formats can be specified with the optional argument
of [--help], see your own [tool --help] for more information.
{@sh[
tool --help
tool cmd --help
tool --help=groff > tool.1
]}
{2:paging Paging}
The pager is selected by looking up, in order:
{ol
{- The [MANPAGER] variable.}
{- The [PAGER] variable.}
{- The tool [less].}
{- The tool [more].}}
Regardless of the pager, it is invoked with [LESS=FRX] set in the
environment unless, the [LESS] environment variable is set in your
environment.
{2:install_tool_manpages Install}
The manpages of a tool and its subcommands can be installed to a root
[man] directory [$MANDIR] by invoking:
{@shell[
cmdliner install tool-manpages thetool $MANDIR
]}
This looks up [thetool] in the [PATH]. Use an explicit file path like
[./thetool] to directly specify an executable.
If you are also {{!install_tool_completion}installing completions}
rather use the [install tool-support] command, see this
{{!page-cookbook.tip_tool_support}cookbook tip} which also has
instructions on how to install if you are using [opam].
{1:cli_completion Command line completion}
Cmdliner programs automatically get support for shell command line
completion.
The completion process happens via a {{!completion_protocol}protocol}
which is interpreted by generic shell completion scripts that are
installed by the library. For now the [zsh], [bash], and PowerShell
([pwsh]) shells are supported.
Tool developers can easily {{!install_tool_completion}install}
completion definitions that invoke these completion scripts. Tool
end-users need to {{!user_configuration}make sure} these definitions are
looked up by their shell.
{2:user_configuration End-user configuration}
If you are the user of a cmdliner based tool, the following
shell-dependent steps need to be performed in order to benefit from
command line completion.
{3:user_zsh For [zsh]}
The [FPATH] environment variable must be setup to include the
directory where the generic cmdliner completion function is
{{!install_completion} installed} {b before} properly initializing the
completion system.
For example, {{:https://github.com/ocaml/opam/issues/6427}for now}, if
you are using [opam]. You should add something like this to your
[.zshrc]:
{@sh[
FPATH="$(opam var share)/zsh/site-functions:${FPATH}"
autoload -Uz compinit
compinit -u
]}
Also make sure this {b happens before} [opam]'s [zsh] init script
inclusion, see {{:https://github.com/ocaml/opam/issues/6428}this
issue}. Note that these instruction do not react dynamically
to [opam] switches changes so you may see odd completion behaviours
when you do so, see this {{:https://github.com/ocaml/opam/issues/6427}this
opam issue}.
After this, to test everything is right, check that the [_cmdliner_generic]
function can be looked by invoking it (this will result in an error).
{@sh[
> autoload _cmdliner_generic
> _cmdliner_generic
_cmdliner_generic:1: words: assignment to invalid subscript range
]}
If the function cannnot be found make sure the [cmdliner] library is
installed, that the generic scripts were
{{!install_generic_completion}installed} and that the
[_cmdliner_generic] file can be found in one of the directories
mentioned in the [FPATH] variable.
With this setup, if you are using a cmdliner based tool named
[thetool] that did not {{!install_tool_completion}install} a completion
definition. You can always do it yourself by invoking:
{@sh[
autoload _cmdliner_generic
compdef _cmdliner_generic thetool
]}
{3:user_bash For [bash]}
These instructions assume that you have
{{:https://repology.org/project/bash-completion/versions}[bash-completion]}
installed and setup in some way in your [.bashrc].
The [XDG_DATA_DIRS] environment variable must be setup to include the
[share] directory where the generic cmdliner completion function is
{{!install_completion}installed}.
For example, {{:https://github.com/ocaml/opam/issues/6427}for now}, if
you are using [opam]. You should add something like this to your
[.bashrc]:
{@sh[
XDG_DATA_DIRS="$(opam var share):${XDG_DATA_DIRS}"
]}
Note that these instruction do not react dynamically to [opam]
switches changes so you may see odd completion behaviours when you do
so, see this {{:https://github.com/ocaml/opam/issues/6427}this opam
issue}.
After this, to test everything is right, check that the [_cmdliner_generic]
function can be looked up:
{@sh[
> _completion_loader _cmdliner_generic
> declare -F _cmdliner_generic &>/dev/null && echo "Found" || echo "Not found"
Found!
]}
If the function cannot be found make sure the [cmdliner] library is
installed, that the generic scripts were
{{!install_generic_completion}installed} and that the
[_cmdliner_generic] file can be looked up by [_completion_loader].
With this setup, if you are using a cmdliner based tool named
[thetool] that did not {{!install_tool_completion}install} a completion
definition. You can always do it yourself by invoking:
{@sh[
_completion_loader _cmdliner_generic
complete -F _cmdliner_generic thetool
]}
{b Note.} {{:https://github.com/scop/bash-completion/commit/9efc596735c4509001178f0cf28e02f66d1f7703}It seems} [_completion_loader] was deprecated in
bash-completion [2.12] in favour of [_comp_load] but many distributions
are on [< 2.12] and in [2.12] [_completion_loader] simply calls
[_comp_load].
{3:user_pwsh For PowerShell ([pwsh])}
PowerShell Core supports tab completion but lacks the kind of
discovery mechanism that other shells have for these scripts.
Therefore, you need to explicitly source the completion script
for cmdliner based tools. This can be done in the
{{:https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles} [$profile]}
file by adding something like the following:
{@ps1[
# first, load the generic cmdliner completion function
. "$(opam var share)/powershell/cmdliner_generic_completion.ps1"
# then, register each tool you want completions for
# for example:
. "$(opam var share)/powershell/cmdliner_completion.ps1"
. "$(opam var share)/powershell/odoc_completion.ps1"
# ...
]}
Note that these instruction do not react dynamically to [opam]
switches changes so you may see odd completion behaviours when you do
so, see this {{:https://github.com/ocaml/opam/issues/6427}this opam
issue}.
With this setup, if you are using a cmdliner based tool named
[thetool] that did not {{!install_tool_completion}install} a completion
definition. You can always do it yourself by invoking:
{@ps1[
Invoke-Expression $(cmdliner tool-completion pwsh thetool)
]}
or if you find that easier to remember:
{@ps1[
Register-ArgumentCompleter -Native -CommandName thetool -ScriptBlock $Global:_cmdliner_generic
]}
{2:install_completion Install}
Completion scripts need to be installed in subdirectories of a
{{:https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch04s11.html}[share]}
directory which we denote by the [$SHAREDIR] variable below. In a
package installation script this variable is typically defined by:
{@sh[
SHAREDIR="$DESTDIR/$PREFIX/share"
]}
The final destination directory in [share] depends on the shell:
{ul
{- For [zsh] it is [$SHAREDIR/zsh/site-functions]}
{- For [bash] it is [$SHAREDIR/bash-completion/completions]}
{- For [pwsh] it is [$SHAREDIR/powershell]}}
If that is unsatisfying you can output the completion scripts directly
where you want with the [cmdliner generic-completion] and
[cmdliner tool-completion] commands.
{3:install_generic_completion Generic completion scripts}
The generic completion scripts must be installed by the
[cmdliner] library. They should not be part of your tool install. If
they are not installed you can inspect and install them with the
following invocations, invoke with [--help] for more information.
{@sh[
cmdliner generic-completion zsh # Output generic zsh script on stdout
cmdliner install generic-completion $SHAREDIR # All shells
cmdliner install generic-completion --shell zsh $SHAREDIR # Only zsh
]}
Directories are created as needed. Use option [--dry-run] to see which
paths would be written by an [install] invocation.
{3:install_tool_completion Tool completion scripts}
If your tool named [thetool] uses Cmdliner you should install completion
definitions for them. They rely on the {{!install_generic_completion}generic
scripts} to be installed. These tool specific scripts can be inspected
and installed via these invocations:
{@sh[
cmdliner tool-completion zsh thetool # Output tool zsh script on stdout.
cmdliner install tool-completion thetool $SHAREDIR # All shells
cmdliner install tool-completion --shell zsh thetool $SHAREDIR # Only zsh
]}
Directories are created as needed. Use option [--dry-run] to see which
paths would be written by an [install] invocation.
If you are also {{!install_tool_manpages}installing manpages} rather
use the [install tool-support] command, see this
{{!page-cookbook.tip_tool_support}cookbook tip} which also has
instructions on how to install if you are using [opam].
{2:completion_protocol Completion protocol}
There is no standard that allows tools and shells to interact to
perform shell command line completion. Completion is supposed to
happen through idiosyncratic, ad-hoc, obscure and brain damaging
shell-specific completion scripts.
To alleviate this, Cmdliner defines one generic script per shell and
interacts with it using the protocol described below. The protocol can
be used to implement generic completion scripts for other shells. The
protocol is versioned but can change even between minor versions of
Cmdliner. Generic scripts for popular shells can be inspected via
the [cmdliner generic-completion] command.
The protocol betwen the shell completion {e script} and a
cmdliner based {e tool} is as follows:
{ol
{- When completion is requested the script invokes the tool with a
modified command line:
{ul
{- The first argument to the tool ([Sys.argv.(1)]) must be the
option [--__complete].}
{- The (possibly empty) argument [ARG] on which the completion is
requested must be replaced by {e exactly} [--__complete=ARG]. Note
that this can happen after the [--] token, this is the reason
why we have an explicit [--__complete] argument in [Sys.argv.(1)]:
it indicates the command line parser must operate in a special mode.}}}
{- The tool responds by writing on standard output a list of
completion directives which match the [completions] rule of the grammar
given below.}
{- The script interprets the completion directives according
to the given semantics below so that the shell can display the
completions. The script is free to ignore directives
or data that it is unable to present.}}
The following ABNF grammar is described using the notations of
{{:https://www.rfc-editor.org/rfc/rfc5234}RFC 5234} and
{{:https://www.rfc-editor.org/rfc/rfc7405}RFC 7405}. A few constraints
are not expressed by the grammar:
{ul
{- Except in the [completion] rule, the byte stream may contain ANSI escape
sequences introduced by the byte [0x1B].}
{- After stripping the ANSI escape sequences, the resulting byte stream must
be valid UTF-8 text.}}
{@abnf[
completions = version nl directives
version = "1"
directives = *(directive nl)
directive = message / group / %s"files" / %s"dirs" / %"restart"
message = %s"message" nl text nl %s"message-end"
group = %s"group" nl group_name nl *item
group_name = *pchar
item = %s"item" nl completion nl item_doc nl %s"item-end"
completion = *pchar
item_doc = text
text = *(pchar / nl)
nl = %0A
pchar = %20-%7E / %8A-%FF
]}
The semantics of directives is as follows:
{ul
{- A [message] directive defines a message to be reported to the user.
It is multi-line ANSI styled text which cannot have a line that is
exactly made of the text [message-end] as it is used to signal the
end of the message. Messages should be reported in the order they
are received.}
{- A [group] directive defines an informational [group_name] followed
by a possibly empty list of completion items that are part of the
group. An item provides a [completion] value, this is a string that
defines what the requested [ARG] value can be replaced with. It is
followed by an [item_doc], multi-line ANSI styled text which cannot
have a line that is exactly made of the text [item-end] as it is
used to signal the end of the item.}
{- A [file] directive indicates that the script should add existing
files staring with [ARG] to completion values.}
{- A [dir] directive indicates that the script should add existing
directories starting with [ARG] to completion values.}
{- A [restart] directive indicates that the script should restart
shell completion as if the command line was starting after the leftmost
[--] disambiguation token. The directive never gets emited if
there is no [--] on the command line.}}
You can easily inspect the completions of any cmdliner based tool by
invoking it like the protocol suggests. For example for the [cmdliner]
tool itself:
{@shell[
cmdliner --__complete --__complete=
]}
{1:error_message_styling Error message ANSI styling}
Since Cmdliner 2.0 error messages printed on [stderr] use styled text
with ANSI escapes unless one of the following conditions is met:
{ul
{- The [NO_COLOR] environment variable is set and different
from the empty string. Yes, even if you have [NO_COLOR=false], that's
what the particularly dumb {:https://no-color.org} standard says.}
{- The [TERM] environment variable is [dumb].}
{- The [TERM] environment variable is unset and {!Sys.backend_type} is
not [Other "js_of_ocaml"]. Yes, browser consoles support
ANSI escapes. Yes, you can run Cmdliner in your browser.}}
{1:legacy_prefix_specification Legacy prefix specification}
Before Cmdliner 2.0, command names, long option names and
{!Cmdliner.Arg.enum} values could be specified by a prefix as long as
the prefix was not ambiguous.
This turned out to be a mistake. It makes the user experience of the
tool unstable as it evolves: former user established shortcuts or
invocations in scripts may be broken by new command, option and
enumerant additions.
Therefore this behaviour was unconditionally removed in Cmdliner
2.0. If you happen to have scripts that rely on it, you can invoke
them with [CMDLINER_LEGACY_PREFIXES=true] set in the environment to
recover the old behaviour. {b However the scripts should be fixed: this
escape hatch will be removed in the future.}
The [CMDLINER_LEGACY_PREFIX=true] escape hatch should not be used for
interactive tool interaction. In particular the behaviour of Cmdliner
completion support under this setting is undefined.