_posts/2018-05-30-groovy-2-5-clibuilder-renewal.html (843 lines of code) (raw):
---
layout: post
status: PUBLISHED
published: true
title: Groovy 2.5 CliBuilder Renewal
excerpt: "<p>The <code>CliBuilder</code> class for quickly and concisely building
command line applications has been renewed in Apache Groovy 2.5.\r\nThis article
highlights what is new.</p>\r\n<div class=\"content\">\r\n<img src=\"http://picocli.info/images/CliBuilder2.5-cygwin.png\"
alt=\"CliBuilder2.5 cygwin\">\r\n</div>\r\n"
id: 5682a883-2a4f-4c03-868f-d5a8c7f81a81
date: '2018-05-30 11:39:52 -0400'
categories: logging
tags:
- clibuilder
- picocli
- groovy
- cli
- commons-cli
permalink: logging/entry/groovy-2-5-clibuilder-renewal
---
<div id="header">
<h1>Apache Groovy 2.5 CliBuilder Renewal</h1>
<div class="details">
<span id="revdate">2018-05-30</span>
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>The <code>CliBuilder</code> class for quickly and concisely building command line applications has been renewed in Apache Groovy 2.5.<br />
This article highlights what is new.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="http://picocli.info/images/CliBuilder2.5-cygwin.png" alt="CliBuilder2.5 cygwin">
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_code_groovy_util_clibuilder_code_class_is_deprecated">The <code>groovy.util.CliBuilder</code> Class is Deprecated</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Previous versions of CliBuilder used Apache <a href="https://commons.apache.org/proper/commons-cli/index.html">Commons CLI</a> as the underlying parser library.<br />
From Groovy 2.5, there is an alternative version of CliBuilder based on the <a href="https://github.com/remkop/picocli">picocli</a> parser.</p>
</div>
<div class="paragraph">
<p>It is recommended that applications explicitly import either <code>groovy.cli.picocli.CliBuilder</code> or <code>groovy.cli.commons.CliBuilder</code>. The <code>groovy.util.CliBuilder</code> class is deprecated and delegates to the Commons CLI version for backwards compatibility.</p>
</div>
<div class="paragraph">
<p>New features will likely only be added to the picocli version, and <code>groovy.util.CliBuilder</code> may be removed in a future version of Groovy.<br />
The Commons CLI version is intended for applications that rely on the internals of the Commons CLI implementation of CliBuilder and cannot easily migrate to the picocli version.</p>
</div>
<div class="paragraph">
<p>Next, let’s look at some new features in Groovy 2.5 CliBuilder.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_typed_options">Typed Options</h2>
<div class="sectionbody">
<div class="imageblock">
<div class="content">
<img src="http://picocli.info/images/Type.jpg" alt="Type">
</div>
</div>
<div class="paragraph">
<p>Options can be boolean flags or they can take one or more option parameters.<br />
In previous versions of CliBuilder, you would have to specify <code>args: 1</code> for options that need a parameter, or<br />
<code>args: '+'</code> for options that accept multiple parameters.</p>
</div>
<div class="paragraph">
<p>This version of CliBuilder adds support for typed options. This is convenient when processing parse results,<br />
but additionally, the number of arguments is inferred from the type,<br />
so if the <code>type</code> is specified, <code>args</code> can be omitted.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">def cli = new CliBuilder()
cli.a(type: String, 'a-arg')
cli.b(type: boolean, 'b-arg')
cli.c(type: Boolean, 'c-arg')
cli.d(type: int, 'd-arg')
cli.e(type: Long, 'e-arg')
cli.f(type: Float, 'f-arg')
cli.g(type: BigDecimal, 'g-arg')
cli.h(type: File, 'h-arg')
cli.i(type: RoundingMode, 'i-arg')
def argz = '''-a John -b -d 21 -e 1980 -f 3.5 -g 3.14159
-h cv.txt -i DOWN and some more'''.split()
def options = cli.parse(argz)
assert options.a == 'John'
assert options.b
assert !options.c
assert options.d == 21
assert options.e == 1980L
assert options.f == 3.5f
assert options.g == 3.14159
assert options.h == new File('cv.txt')
assert options.i == RoundingMode.DOWN
assert options.arguments() == ['and', 'some', 'more']
</code></pre>
</div>
</div>
<div class="sect2">
<h3 id="_supported_types">Supported Types</h3>
<div class="paragraph">
<p>The Commons CLI-based CliBuilder supports primitives, numeric types, files, enums and arrays thereof<br />
(using <a href="http://docs.groovy-lang.org/2.5.0-SNAPSHOT/html/gapi/index.html?org/codehaus/groovy/runtime/StringGroovyMethods.html#asType">StringGroovyMethods#asType(String, Class)</a>).<br />
The picocli-based CliBuilder supports those <a href="http://picocli.info/#_built_in_types">and more</a>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_adding_more_types">Adding More Types</h3>
<div class="paragraph">
<p>If the built-in types don’t meet your needs, it is easy to register a custom converter. Specify a <code>convert</code> Closure to convert the String argument to any other type. For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">import java.nio.file.Paths
import java.time.LocalTime
def cli = new CliBuilder()
cli.a(convert: { it.toUpperCase() }, 'a-arg') // (1)
cli.p(convert: { Paths.get(it) }, 'p-arg') // (2)
cli.t(convert: { LocalTime.parse(it) }, 't-arg') // (3)
def options = cli.parse('-a abc -p /usr/home -t 15:31:59'.split())
assert options.a == 'ABC'
assert options.p.absolute && options.p.parent == Paths.get('/usr')
assert options.t.hour == 15 && options.t.minute == 31
</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Convert one String to another</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Option value is converted to a <code>java.nio.file.Path</code></td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Option value is converted to a <code>java.time.LocalTime</code></td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_annotations">Annotations</h2>
<div class="sectionbody">
<div class="imageblock">
<div class="content">
<img src="http://picocli.info/images/a-annotations.png" alt="Annotations">
</div>
</div>
<div class="paragraph">
<p>From this release, Groovy offers an annotation API for processing command line arguments.</p>
</div>
<div class="paragraph">
<p>Applications can annotate fields or methods with <code>@groovy.cli.Option</code> for named options<br />
or <code>@groovy.cli.Unparsed</code> for positional parameters.<br />
When the parser matches a command line argument with an option name or positional parameter, the value is converted<br />
to the correct type and injected into the field or method.</p>
</div>
<div class="sect2">
<h3 id="_annotating_methods_of_an_interface">Annotating Methods of an Interface</h3>
<div class="paragraph">
<p>One way to use the annotations is on "getter-like" methods (methods that return a value) of an interface. For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">import groovy.cli.*
interface IHello {
@Option(shortName='h', description='display usage') Boolean help() // (1)
@Option(shortName='u', description='user name') String user() // (2)
@Unparsed(description = 'positional parameters') List remaining() // (3)
}
</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Method returns <code>true</code> if <code>-h</code> or <code>--help</code> was specified on the command line.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Method returns the parameter value that was specified for the <code>-u</code> or <code>--user</code> option.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Any remaining parameters will be returned as a list from this method.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>How to use this interface (using the picocli version to demonstrate its usage help):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">import groovy.cli.picocli.CliBuilder
def cli = new CliBuilder(name: 'groovy Greeter')
def argz = '--user abc'.split()
IHello hello = cli.parseFromSpec(IHello, argz)
assert hello.user() == 'abc'
hello = cli.parseFromSpec(GreeterI, ['--help', 'Some', 'Other', 'Args'] as String[])
assert hello.help()
cli.usage()
assert hello.remaining() == ['Some', 'Other', 'Args']
</code></pre>
</div>
</div>
<div class="paragraph">
<p>This prints the following usage help message:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code>Usage: groovy Greeter [-h] [-u=<user>] [<remaining>...]
[<remaining>...] positional parameters
-u, --user=<user> user name
-h, --help display usage</code></pre>
</div>
</div>
<div class="paragraph">
<p>When <code>parseFromSpec</code> is called, <code>CliBuilder</code> reads the annotations, parses the command line arguments<br />
and returns an instance of the interface.<br />
The interface methods return the option values matched on the command line.</p>
</div>
</div>
<div class="sect2">
<h3 id="_annotating_properties_or_setter_methods_of_a_class">Annotating Properties or Setter Methods of a Class</h3>
<div class="paragraph">
<p>Another way to use the annotations is on the properties or "setter-like" methods (<code>void</code> methods with a single parameter) of a class. For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">class Hello {
@Option(shortName='h', description='display usage') // (1)
Boolean help
private String user
@Option(shortName='u', description='user name') // (2)
void setUser(String user) {
this.user = user
}
String getUser() { user }
@Unparsed(description = 'positional parameters') // (3)
List remaining
}
</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The <code>help</code> Boolean property is set to <code>true</code> if <code>-h</code> or <code>--help</code> was specified on the command line.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>The <code>setUser</code> property setter method is invoked with the <code>-u</code> or <code>--user</code> option parameter value.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>The <code>remaining</code> property is set to a new <code>List</code> containing the remaining args, if any.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The annotated class can be used as follows:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">String[] argz = ['--user', 'abc', 'foo']
def cli = new CliBuilder(usage: 'groovy Greeter [option]') // (1)
Hello greeter = cli.parseFromInstance(new Hello(), argz) // (2)
assert greeter.user == 'abc' // (3)
assert greeter.remaining == ['foo'] // (4)
</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Create a <code>CliBuilder</code> instance.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Extract options from the annotated instance, parse arguments, and populate and return the supplied instance.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Verify that the String option value has been assigned to the property.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Verify the remaining arguments property.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>When <code>parseFromInstance</code> is called, <code>CliBuilder</code> again reads the annotations, parses the command line<br />
arguments and finally returns the instance. The annotated fields and setter methods are initialized with the values<br />
matched for the associated option.</p>
</div>
</div>
<div class="sect2">
<h3 id="_script_annotations">Script Annotations</h3>
<div class="imageblock">
<div class="content">
<img src="http://picocli.info/images/GroovyScriptAnnotations.png" alt="Script">
</div>
</div>
<div class="paragraph">
<p>Groovy 2.5 also offers new annotations for Groovy scripts.</p>
</div>
<div class="paragraph">
<p><code>@OptionField</code> is equivalent to combining <code>@groovy.transform.Field</code> and <code>@Option</code>, whereas <code>@UnparsedField</code> is equivalent to combining <code>@Field</code> and <code>@Unparsed</code>.</p>
</div>
<div class="paragraph">
<p>Use these annotations to turn script variables into fields so that the variables can be populated by CliBuilder. For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">import groovy.cli.OptionField
import groovy.cli.UnparsedField
@OptionField String user
@OptionField Boolean help
@UnparsedField List remaining
String[] argz = ['--user', 'abc', 'foo']
new CliBuilder().parseFromInstance(this, argz)
assert user == 'abc'
assert remaining == ['foo']
</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_typed_positional_parameters">Typed Positional Parameters</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This version of CliBuilder offers some limited support for strongly typed positional parameters.</p>
</div>
<div class="paragraph">
<p>If all positional parameters have the same type, the <code>@Unparsed</code> annotation can be used with an array type other than <code>String[]</code>.<br />
Again, the type conversion is done using <a href="http://docs.groovy-lang.org/2.5.0-SNAPSHOT/html/gapi/index.html?org/codehaus/groovy/runtime/StringGroovyMethods.html#asType">StringGroovyMethods#asType(String, Class)</a><br />
in the Commons CLI version, while the picocli version of CliBuilder supports a <a href="http://picocli.info/#_built_in_types">superset</a> of those types.</p>
</div>
<div class="paragraph">
<p>This functionality is only available for the annotations API, not for the dynamic API.<br />
Here is an example of an interface that can capture strongly typed positional parameters:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">interface TypedPositionals {
@Unparsed Integer[] nums()
}
</code></pre>
</div>
</div>
<div class="paragraph">
<p>The code below demonstrates the type conversion:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">def argz = '12 34 56'.split()
def cli = new CliBuilder()
def options = cli.parseFromSpec(TypedPositionals, argz)
assert options.nums() == [12, 34, 56]
</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_apache_commons_cli_features">Apache Commons CLI Features</h2>
<div class="sectionbody">
<div class="imageblock">
<div class="content">
<img src="http://picocli.info/images/FeatureIconAdvancedOptions.png" alt="FeatureIconAdvancedOptions">
</div>
</div>
<div class="paragraph">
<p>Sometimes you may want to use advanced features of the underlying parsing library.<br />
For example, you may have a command line application with mutually exclusive options.<br />
The below code shows how to achieve this using the Apache Commons CLI <code>OptionGroup</code> API:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">import groovy.cli.commons.CliBuilder
import org.apache.commons.cli.*
def cli = new CliBuilder()
def optionGroup = new OptionGroup()
optionGroup.with {
addOption cli.option('s', [longOpt: 'silent'], 's option')
addOption cli.option('v', [longOpt: 'verbose'], 'v option')
}
cli.options.addOptionGroup optionGroup
assert !cli.parse('--silent --verbose'.split()) (1)
</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Parsing this input will fail because two mutually exclusive options were specified.</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_picocli_clibuilder_features">Picocli CliBuilder Features</h2>
<div class="sectionbody">
<div class="imageblock">
<div class="content">
<img src="http://picocli.info/images/FeatureIconAdvancedOptions.png" alt="FeatureIconAdvancedOptions">
</div>
</div>
<div class="sect2">
<h3 id="_strongly_typed_lists">Strongly Typed Lists</h3>
<div class="imageblock">
<div class="content">
<img src="http://picocli.info/images/list.png" alt="list">
</div>
</div>
<div class="paragraph">
<p>Options with multiple values often use an array or a List to capture the values.<br />
Arrays can be strongly typed, that is, contain elements other than String.<br />
The picocli version of CliBuilder lets you do the same with Lists.<br />
The <code>auxiliaryType</code> specifies the type that the elements should be converted to.<br />
For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">import groovy.cli.picocli.CliBuilder
def cli = new CliBuilder()
cli.T(type: List, auxiliaryTypes: Long, 'typed list') // (1)
def options = cli.parse('-T 1 -T 2 -T 3'.split()) // (2)
assert options.Ts == [ 1L, 2L, 3L ] // (3)
</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Define an option that can have multiple integer values.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>An example command line.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>The option values as a <code>List<Integer></code>.</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_strongly_typed_maps">Strongly Typed Maps</h3>
<div class="imageblock">
<div class="content">
<img src="http://picocli.info/images/map.png" alt="map">
</div>
</div>
<div class="paragraph">
<p>The picocli version of CliBuilder offers native support for Map options.<br />
This is as simple as specifying Map as the option type.<br />
By default, both keys and values are stored as Strings in the Map,<br />
but it’s possible to use <code>auxiliaryType</code> to specify the types that the keys and values should be converted to.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">import groovy.cli.picocli.CliBuilder
def cli = new CliBuilder()
cli.D(args: 2, valueSeparator: '=', 'Commons CLI style map') // (1)
cli.X(type: Map, 'picocli style map support') // (2)
cli.Z(type: Map, auxiliaryTypes: [TimeUnit, Integer].toArray(), 'typed map') // (3)
def options = cli.parse('-Da=b -Dc=d -Xx=y -Xi=j -ZDAYS=2 -ZHOURS=23'.split()) // (4)
assert options.Ds == ['a', 'b', 'c', 'd'] // (5)
assert options.Xs == [ 'x':'y', 'i':'j' ] // (6)
assert options.Zs == [ (DAYS as TimeUnit):2, (HOURS as TimeUnit):23 ] // (7)
</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Commons CLI has map-like options by specifying that each option must have two parameters, with some separator.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>The picocli version of CliBuilder has native support for Map options.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>The key type and value type can be specified for strongly-typed maps.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>An example command line.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>The Commons CLI style option gives a list of [key, value, key, value, …​] objects.</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>The picocli style option gives the result as a <code>Map<String, String></code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>When <code>auxiliaryTypes</code> are specified, the keys and values of the map are converted to the specified types, giving you a <code>Map<TimeUnit, Integer></code>.</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_usage_help_with_detailed_synopsis">Usage Help with Detailed Synopsis</h3>
<div class="imageblock">
<div class="content">
<img src="http://picocli.info/images/iceberg.png" alt="iceberg">
</div>
</div>
<div class="paragraph">
<p>CliBuilder has always supported a <code>usage</code> property to display the usage help synopsis of a command:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">// the old way
new CliBuilder(usage: 'myapp [options]').usage()
</code></pre>
</div>
</div>
<div class="paragraph">
<p>The above program prints:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>Usage: myapp [options]</pre>
</div>
</div>
<div class="paragraph">
<p>This still works, but the picocli version has a better alternative with the <code>name</code> property.<br />
If you specify <code>name</code> instead of <code>usage</code>, picocli will show all options in a succinct synopsis with square brackets <code>[</code> and <code>]</code> for optional elements and ellipsis <code>…​</code> for elements that can be repeated one or more times. For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">// the new way
def cli = new CliBuilder(name: 'myapp') // detailed synopsis
cli.a('option a description')
cli.b('option b description')
cli.c(type: List, 'option c description')
cli.usage()
</code></pre>
</div>
</div>
<div class="paragraph">
<p>The above program prints:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>Usage: myapp [-ab] [-c=PARAM]...
-a option a description
-b option b description
-c= PARAM option c description</pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_use_any_option_names">Use Any Option Names</h3>
<div class="imageblock">
<div class="content">
<img src="http://picocli.info/images/freedom-c-PsychoShadow-www.bigstockphoto.com.jpg" alt="freedom c PsychoShadow www.bigstockphoto.com">
</div>
</div>
<div class="paragraph">
<p><em>Image credit: (c) PsychoShadow - www.bigstockphoto.com</em></p>
</div>
<div class="paragraph">
<p>Before, if an option had multiple names with a single hyphen, you had no choice but to declare the option multiple times:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">// before: split -cp, -classpath into two options
def cli = new CliBuilder(usage: 'groovyConsole [options] [filename]')
cli.classpath('Where to find the class files')
cli.cp(longOpt: 'classpath', 'Aliases for '-classpath')
</code></pre>
</div>
</div>
<div class="paragraph">
<p>The picocli version of CliBuilder supports a <code>names</code> property that can have any number of option names that can take any prefix. For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">// after: an option can have many names with any prefix
def cli = new CliBuilder(usage: 'groovyConsole [options] [filename]')
cli._(names: ['-cp', '-classpath', '--classpath'], 'Where to find the class files')
</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_fine_grained_usage_help_message">Fine-grained Usage Help Message</h3>
<div class="imageblock">
<div class="content">
<img src="http://picocli.info/images/sift.png" alt="sift">
</div>
</div>
<div class="paragraph">
<p>Picocli offers fine-grained control over the usage help message format and this functionality is exposed via the <code>usageMessage</code> CliBuilder property.</p>
</div>
<div class="paragraph">
<p>The usage message has a number of sections: header, synopsis, description, parameters, options and finally the footer. Each section has a heading, that precedes the first line of its section. For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">import groovy.cli.picocli.CliBuilder
def cli = new CliBuilder()
cli.name = "groovy clidemo"
cli.usageMessage.with { // (1)
headerHeading("Header heading:%n") // (2)
header("header 1", "header 2") // (3)
synopsisHeading("%nUSAGE: ")
descriptionHeading("%nDescription heading:%n")
description("description 1", "description 2")
optionListHeading("%nOPTIONS:%n")
footerHeading("%nFooter heading:%n")
footer("footer 1", "footer 2")
}
cli.a(longOpt: 'aaa', 'a-arg') // (4)
cli.b(longOpt: 'bbb', 'b-arg')
cli.usage()
</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Use the <code>usageMessage</code> CliBuilder property to customize the usage help message.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Headings can contain string format specifiers like the <code>%n</code> newline.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Sections are multi-line: each string will be rendered on a separate line.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Define some options.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>This prints the following output:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>Header heading:
header 1
header 2
USAGE: groovy clidemo [-ab]
Description heading:
description 1
description 2
OPTIONS:
-a, --aaa a-arg
-b, --bbb b-arg
Footer heading:
footer 1
footer 2</pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_usage_help_with_ansi_colors">Usage Help with ANSI Colors</h3>
<div class="paragraph">
<p>Out of the box, the command name, option names and parameter labels in the usage help message are rendered with <a href="http://picocli.info/#_ansi_colors_and_styles">ANSI styles and colors</a>.<br />
The color scheme for these elements can be <a href="http://picocli.info/#_configuring_fixed_elements">configured</a> with system properties.</p>
</div>
<div class="paragraph">
<p>Other than that, you can use colors and styles in the descriptions and other sections of the usage help message,<br />
using a <a href="http://picocli.info/#_usage_help_with_styles_and_colors">simple markup notation</a>. The example below demonstrates:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="groovy">def cli = new groovy.cli.picocli.CliBuilder(name: 'myapp')
cli.usageMessage.with {
headerHeading("@|bold,red,underline Header heading|@:%n")
header($/@|bold,green \
___ _ _ ___ _ _ _
/ __| (_) _ )_ _(_) |__| |___ _ _
| (__| | | _ \ || | | / _` / -_) '_|
\___|_|_|___/\_,_|_|_\__,_\___|_|
|@/$)
synopsisHeading("@|bold,underline Usage|@: ")
descriptionHeading("%n@|bold,underline Description heading|@:%n")
description("Description 1", "Description 2") // after the synopsis
optionListHeading("%n@|bold,underline Options heading|@:%n")
footerHeading("%n@|bold,underline Footer heading|@:%n")
footer($/@|bold,blue \
___ ___ ___
/ __|_ _ ___ _____ ___ _ |_ ) | __|
| (_ | '_/ _ \/ _ \ V / || | / / _|__ \
\___|_| \___/\___/\_/ \_, | /___(_)___/
|__/ |@/$)
}
cli.a('option a description')
cli.b('option b description')
cli.c(type: List, 'option c description')
cli.usage()
</code></pre>
</div>
</div>
<div class="paragraph">
<p>The code above gives the following output:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="http://picocli.info/images/CliBuilder2.5-cygwin.png" alt="CliBuilder2.5 cygwin">
</div>
</div>
<div class="paragraph">
<p>(Credit to <a href="http://patorjk.com/software/taag/">http://patorjk.com/software/taag/</a> for the ASCII art.)</p>
</div>
</div>
<div class="sect2">
<h3 id="_new_code_errorwriter_code_property">New <code>errorWriter</code> Property</h3>
<div class="imageblock">
<div class="content">
<img src="http://picocli.info/images/error.png" alt="error">
</div>
</div>
<div class="paragraph">
<p>When the user provided invalid input, the picocli version of CliBuilder writes an error message and the usage help message to the new <code>errorWriter</code> property (set to <code>System.err</code> by default).<br />
When the user requests help, and the application calls <code>CliBuilder.usage()</code>, the usage help message is printed to the <code>writer</code> property (<code>System.out</code> by default).</p>
</div>
<div class="paragraph">
<p>Previous versions of CliBuilder used the <code>writer</code> property for both invalid input and user-requested help.</p>
</div>
<div class="paragraph">
<p>Why this change? This helps command line application authors to follow standard practice and separate diagnostic output from the program output: If the output of a Groovy program is piped to another program,<br />
sending error messages to STDERR prevents the downstream program from inadvertently trying to parse error output.<br />
On the other hand, when users request help with <code>--help</code> or <code>--version</code>, the output should be sent to STDOUT,<br />
because the user may want to pipe the output to a utility like <code>less</code> or <code>grep</code>.</p>
</div>
<div class="paragraph">
<p>For backwards compatibility, setting the <code>writer</code> property to another value will also set the <code>errorWriter</code> to the same value.<br />
(You can still set the <code>errorWriter</code> to another value afterwards if desired.)</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_gotchas_incompatibilities">Gotchas/Incompatibilities</h2>
<div class="sectionbody">
<div class="imageblock">
<div class="content">
<img src="http://picocli.info/images/incompatible.jpg" alt="incompatible">
</div>
</div>
<div class="paragraph">
<p>There are a few areas where the new versions of <code>CliBuilder</code> are not compatible with previous versions or with each other.</p>
</div>
<div class="sect2">
<h3 id="_properties_code_options_code_and_code_formatter_code_unavailable_in_picocli_version">Properties <code>options</code> and <code>formatter</code> Unavailable in Picocli Version</h3>
<div class="paragraph">
<p>The Commons CLI version of CliBuilder, and previous versions of CliBuilder, expose an <code>options</code> property of type <code>org.apache.commons.cli.Options</code>, that can be used to configure the underlying Commons CLI parser without going through the CliBuilder API. This property is not available in the picocli version of CliBuilder.<br />
Applications that read or write this property must import <code>groovy.cli.commons.CliBuilder</code><br />
or modify the application.</p>
</div>
<div class="paragraph">
<p>Additionally, the <code>formatter</code> property of type <code>org.apache.commons.cli.HelpFormatter</code> is not available in the picocli version of CliBuilder. If your application uses this property, consider using the <code>usageMessage</code> property instead, or import <code>groovy.cli.commons.CliBuilder</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_property_code_parser_code_differs_in_picocli_and_commons_cli_versions">Property <code>parser</code> Differs in Picocli and Commons CLI Versions</h3>
<div class="paragraph">
<p>The picocli version of CliBuilder has a <code>parser</code> property that exposes a <code>picocli.CommandLine.Model.ParserSpec</code> object<br />
that can be used to configure the parser behavior.</p>
</div>
<div class="paragraph">
<p>The Commons CLI version of CliBuilder, and previous versions of CliBuilder, expose a <code>parser</code> property of type <code>org.apache.commons.cli.CommandLineParser</code>. This functionality is not available in the picocli version of CliBuilder.</p>
</div>
<div class="paragraph">
<p>If your application uses the <code>parser</code> property to set a different Commons CLI parser, consider using the <code>posix</code> property instead, or import <code>groovy.cli.commons.CliBuilder</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_different_parser_behavior_for_code_longoption_code">Different Parser Behavior for <code>longOption</code></h3>
<div class="paragraph">
<p>The Commons CLI <code>DefaultParser</code> recognizes <code>longOption</code> option names prefixed with a single hypen (e.g., <code>-option</code>)<br />
as well as options prefixed with a double hyphen (e.g., <code>--option</code>).<br />
This is not always obvious since the usage help message only shows the double hyphen prefix for <code>longOption</code> option names.</p>
</div>
<div class="paragraph">
<p>For backwards compatibility, the picocli version of CliBuilder has an <code>acceptLongOptionsWithSingleHyphen</code> property:<br />
set this property to <code>true</code> if the parser should recognize long option names with both<br />
a single hyphen and a double hyphen prefix. The default is <code>false</code>,<br />
so only long option names with a double hypen prefix (<code>--option</code>) are recognized.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Groovy 2.5 CliBuilder offers a host of exciting new features. Try it out and let us know what you think!</p>
</div>
<div class="paragraph">
<p>For reference: Groovy <a href="http://groovy-lang.org/">site</a> and GitHub <a href="https://github.com/apache/groovy/">project</a>,<br />
picocli <a href="http://picocli.info/">site</a> and <a href="https://github.com/remkop/picocli">picocli GitHub project</a>. Please star the projects if you like what you see!</p>
<p>A copy of this article was previously published on the picocli web site.<br />
<a href="http://picocli.info/groovy-2.5-clibuilder-renewal.html">See the original article here.</a></p>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2018-05-29 22:09:03 JST
</div>
</div>