Skip to content

picocli 2.0

Compare
Choose a tag to compare
@remkop remkop released this 21 Oct 11:04
· 3960 commits to main since this release

Picocli 2.0

The picocli community is pleased to announce picocli 2.0.

First, picocli now provides tight integration for Groovy scripts. The new @picocli.groovy.PicocliScript annotation allows Groovy scripts to use the @Command annotation, and turns a Groovy script into a picocli-based command line application. When the script is run, @Field variables annotated with @Option or @Parameters are initialized from the command line arguments before the script body is executed.

Applications with subcommands will also benefit from upgrading to picocli 2.0. The CommandLine.run and CommandLine.call methods now work for subcommands, and if more flexibility is needed, take a look at the new parseWithHandler methods. Error handling for subcommands has been improved in this release with the new ParseException.getCommandLine method.

Improved ease of use: Usage help is now printed automatically from the CommandLine.run and CommandLine.call methods and from the built-in handlers used with the parseWithHandler method.

The parser has been improved and users can now mix positional arguments with options on the command line. Previously, positional parameter had to follow the options. Important notice: To make this feature possible, the default parsing behaviour of multi-value options and parameters changed to be non-greedy by default.

Finally, this release includes many bug fixes.

This is the twelfth public release.
Picocli follows semantic versioning.

Table of Contents

New and noteworthy

Groovy Script Support

Picocli 2.0 introduces special support for Groovy scripts.

Scripts annotated with @picocli.groovy.PicocliScript are automatically transformed to use picocli.groovy.PicocliBaseScript as their base class and can also use the @Command annotation to customize parts of the usage message like command name, description, headers, footers etc.

Before the script body is executed, the PicocliBaseScript base class parses the command line and initializes @Field variables annotated with @Option or @Parameters. The script body is executed if the user input was valid and did not request usage help or version information.

// exampleScript.groovy
@Grab('info.picocli:picocli:2.0.0')
@Command(name = "myCommand", description = "does something special")
@PicocliScript
import picocli.groovy.PicocliScript
import picocli.CommandLine.Command
import picocli.CommandLine.Option
import groovy.transform.Field

@Option(names = "-x", description = "number of repetitions")
@Field int count;

@Option(names = ["-h", "--help"], usageHelp = true, description = "print this help message and exit")
@Field boolean helpRequested;

//if (helpRequested) { // not necessary: PicocliBaseScript takes care of this
//    CommandLine.usage(this, System.err); return 0;
//}
count.times {
   println "hi"
}
// the CommandLine that parsed the args is available as a property
assert this.commandLine.commandName == "myCommand"

Better Subcommand Support

New methods CommandLine::parseWithHandler were added. These methods intend to offer the same ease of use as the run and call methods, but with more flexibility and better support for nested subcommands.

The CommandLine::call and CommandLine::run now support subcommands and will execute the last subcommand specified by the user. (Previously subcommands were ignored and only the top-level command was executed.)

From this release, picocli exceptions provide a getCommandLine method that returns the command or subcommand where parsing or execution failed. Previously, if the user provided invalid input for applications with subcommands, it was difficult to pinpoint exactly which subcommand failed to parse the input.

Easier To Use

The convenience methods will automatically print usage help and version information when the user specifies options annotated with the versionHelp or usageHelp attributes.

Methods that automatically print help:

  • CommandLine::call
  • CommandLine::run
  • CommandLine::parseWithHandler (with the built-in Run…​ handlers)
  • CommandLine::parseWithHandlers (with the built-in Run…​ handlers)

Methods that do not automatically print help:

  • CommandLine::parse
  • CommandLine::populateCommand

Also notable: Collection and Map fields no longer require the type annotation attribute: picocli now infers the conversion target type from the generic type parameters where possible.

Parser Improvements

Positional parameters can now be mixed with options on the command line. Previously, positional parameter had to follow the options.

To make this feature possible, the default parsing behaviour of multi-value options and parameters changed to be non-greedy by default.

Usage Help Format Improvements

This release contains various bugfixes related to improving the usage help format for multi-value options and collections. For example, for Maps that don't have a paramLabel, picocli now shows key type and value type instead of the internal Java field name.

Promoted features

Promoted features are features that were incubating in previous versions of picocli but are now supported and subject to backwards compatibility.

The following are the features that have been promoted in this picocli release.

  • Autocomplete - as of picocli 2.0, autocompletion is no longer beta.

Fixed issues

  • [#207] API Change: Provide ability to find which subcommand threw a ParameterException API enhancement. Thanks to velit and AshwinJay.
  • [#179] API Change to remove full JRE dependency and require only Compact Profile. Replace use of java.awt.Point with picocli.CommandLine.Help.TextTable.Cell. Thanks to webfolderio.
  • [#205] API Change: CommandLine::getCommand now returns a generic type instead of Object so client code can avoid type casting.
  • [#196] API Change: Option::type() and Parameters::type() now return empty array by default (was {String.class}).
  • [#210] API Change: Deprecated the option help attribute in favour of the usageHelp and versionHelp attributes.
  • [#115] New feature: Added new CommandLine::parseWithHandler convenience methods for commands with subcommands.
  • [#130] New feature: Options and positional parameters can now be mixed on the command line.
  • [#196] New feature: Infer type from collections and maps when type annotation not specified. Thanks to ddimtirov for the suggestion.
  • [#197] New feature: Use type attribute to determine conversion target type instead of field type. This allows fields to be declared as interfaces or abstract types (or arrays/collections/maps of these) and via the type attribute picocli will be able to convert String arguments to concrete implementation objects.
  • [#187] New feature: Added methods to programmatically get and set the command name.
  • [#192] Bugfix: Default arity should be 1, not *, for array and collection options. Thanks to RobertZenz.
  • [#193] Bugfix: Splitting an argument should not cause max arity to be exceeded.
  • [#191] Bugfix: Arity should not limit the total number of values put in an array or collection. Thanks to RobertZenz.
  • [#186] Bugfix: Confusing usage message for collection options. Thanks to AlexFalappa.
  • [#181] Bugfix: Incorrect help message was displayed for short options with paramLabel when arity > 1.
  • [#184] Bugfix/Enhancement: Programmatically setting the separator is now reflected in the usage help message. Thanks to defnull.
  • [#200] Bugfix: Prevent NPE when command name is set to empty string or spaces. Thanks to jansohn.
  • [#203] Bugfix: Empty string parameter parsed incorrectly. Thanks to AshwinJay.
  • [#194] Enhancement: Usage help should show split regex for option/parameters.
  • [#198] Enhancement: Usage help parameter list details should indicate arity for positional parameters.
  • [#195] Enhancement: Usage help should show Map types if paramLabel not specified.
  • [#183] Enhancement: Add examples to user manual for using picocli in other JVM languages. Thanks to binkley for pointing out that Kotlin may support array literals in annotations from 1.2.
  • [#185] Enhancement: Exception message text for missing options should not use field names but be more descriptive and consistent with usage help. Thanks to AlexFalappa.
  • [#201] Enhancement: Usage help should not show null default values. Thanks to jansohn.
  • [#202] Enhancement: Java 9: add Automatic-Module-Name: info.picocli to MANIFEST.MF to make picocli play nice in the Java 9 module system.
  • [#204] Enhancement: instantiate LinkedHashSet instead of HashSet for Set fields to preserve input ordering.
  • [#208] Enhancement: Remove pom.xml, which was not being maintained. Picocli can only be built with gradle going forward.
  • [#212] Enhancement: Improve javadoc for picocli.AutoComplete.

Deprecations

The help attribute for options is now deprecated. Please change to use usageHelp and versionHelp attributes instead. From picocli v2.0, the convenience methods will automatically print usage help and version information when requested with the versionHelp and usageHelp option attributes (but not for the help attribute).

Potential breaking changes

This release has a number of incompatible changes:

  • Multi-value options (array, list and map fields) are not greedy by default any more.
  • Arity is not max values: end users may specify multi-value options (array, list and map fields) an unlimited number of times.
  • A single argument that is split into parts with a regex now counts as a single argument (so arity="1" won't prevent all parts from being added to the field)
  • API change: replaced java.awt.Point with custom Cell class as return value type for public method Help.TextTable.putValue().
  • API change: @Option.type() and @Parameters.type() now return an empty array by default (was {String.class}).
  • API change: ParameterException and all subclasses now require a CommandLine object indicating the command or subcommand that the user provided invalid input for.
  • The CommandLine::call and CommandLine::run now support subcommands and will execute the last subcommand specified by the user. Previously subcommands were ignored and the top-level command was executed unconditionally.

I am not happy about the disruption these changes may cause, but I felt they were needed for three reasons: the old picocli v1.0 behaviour caused ambiguity in common use cases, was inconsistent with most Unix tools, and prevented supporting mixing options with positional arguments on the command line.

To illustrate the new non-greedy behaviour, consider this example program:

class MixDemo {
  @Option(names="-o") List<String> options;
  @Parameters         List<String> positionalParams;

  public void run() {
    System.out.println("positional: " + positionalParams);
    System.out.println("options   : " + options);
  }

  public static void main(String[] args) {
    CommandLine.run(new MixDemo(), System.err, args);
  }
}

We run this program as below, where the option is followed by multiple values:

$ java MixDemo -o 1 2 3

Previously, the arguments following -o would all end up in the options list. Running the above command with picocli 1.0 would print out the following:

# (Previously, in picocli-1.0.1)
$ java MixDemo -o 1 2 3

positional: null
options   : [1, 2, 3]

From picocli 2.0, only the first argument following -o is added to the options list, the remainder is parsed as positional parameters:

# (Currently, in picocli-2.0)
$ java MixDemo -o 1 2 3

positional: [2, 3]
options   : [1]

To put multiple values in the options list in picocli 2.0, users can specify the -o option multiple times:

$ java MixDemo -o 1 -o 2 -o 3

positional: null
options   : [1, 2, 3]

Alternatively, application authors can make a multi-value option greedy in picocli v2.0 by explicitly setting a variable arity:

class Args {
    @Option(names = "-o", arity = "1..*") List<String> options;
}

(... "greedy" means consume until the next option, so not necessarily all remaining command line arguments.)