Skip to content

Picocli 4.0.0

Compare
Choose a tag to compare
@remkop remkop released this 17 Jul 04:20

Picocli 4.0.0 GA

The picocli community is pleased to announce picocli 4.0. This is a big release.

First, the picocli-codegen module now includes an annotation processor that instantly enables your JAR for GraalVM native images. It also gives compile-time errors for invalid annotations and attributes. We recommend that all projects using picocli enable this annotation processor.

The execute API is an easy way to execute your command with almost no code. It allows for more flexible configuration than previous APIs, and introduces much improved exit code support. This replaces the static methods call and run, as well as the parseWithHandlers methods, which are now deprecated.

Improved Spring support: the new picocli-spring-boot-starter module includes a PicocliSpringFactory and auto-configuration to use Spring dependency injection in your picocli command line application. This is especially useful if your application contains subcommands.

The parser has been enhanced to handle argument groups: mutually exclusive options, mutually dependent options, and option sections in the usage help. What makes the picocli design unique and extremely powerful is that argument groups can be nested, so applications can define repeating composite groups of mutually exclusive or co-dependent options.

Annotation attributes can now contain variables that can be resolved as system properties, environment variables and resource bundle keys.

The picocli JAR is now an explicit JPMS module, as well as an OSGi bundle. As part of this change, the Groovy support classes and annotations have been moved to a separate picocli-groovy artifact.

Boolean options can now easily be made negatable, which adds a "no-" version of the option. This is a common feature in command line parser libraries for Perl, PHP, Ruby, Lisp, Dart and Go, but we are not aware of any other Java libraries that support this.

All in all, this release contains 96 bugfixes and improvements over picocli 3.9.6.

Many thanks to the following community contributors to this release of picocli:

AkosCz, AlcaYezz, Andreas Deininger, andrewbleonard, Arturo Alonso, Bob Tiernay, Devin Smith, feinstein, Garret Wilson, Gerard Bosch, gitfineon, jrevault, Judd Gaddie, Liam Esteban Prince, marinier, Michael D. Adams, Mikaël Barbero, Mikusch, Nicolas Mingo, Paolo Di Tommaso, Philipp Hanslovsky, Radu Cotescu, Reinhard Pointner, Sebastian Thomschke, Shane Rowatt, shanetreacy, Steffen Rehberg, Sualeh Fatehi, Takuya Ishibashi, Thibaud Lepretre and Warkdev.

This is the fifty-seventh public release.
Picocli follows semantic versioning.

Table of Contents

New and Noteworthy

Annotation processor

GraalVM

The picocli-codegen module now includes an annotation processor that instantly enables your JAR for GraalVM native images. The annotation processor can build a model from the picocli annotations at compile time rather than at runtime.

Use this if you’re interested in:

  • Compile time error checking. The annotation processor shows errors for invalid annotations and attributes immediately when you compile, instead of during testing at runtime, resulting in shorter feedback cycles.
  • Graal native images. The annotation processor generates and updates Graal configuration files under
    META-INF/native-image/picocli-generated/$project during compilation, to be included in the application jar.
    This includes configuration files for reflection, resources and dynamic proxies.
    By embedding these configuration files, your jar is instantly Graal-enabled.
    The $project location is configurable, see processor options below.
    In most cases no further configuration is needed when generating a native image.

Processor option: project

The picocli annotation processor supports a number of options, most important of which is the project option to control the output subdirectory: the generated files are written to META-INF/native-image/picocli-generated/${project}. A good convention is to use the Maven ${groupId}/${artifactId} as the value; a unique subdirectory ensures your jar can be shaded with other jars that may also contain generated configuration files.

To configure this option, pass the -Aproject=<some value> to the javac compiler. The examples below show how to do this for Maven and Gradle.

Enabling the Annotation Processor

Since Java 6, annotation processing is part of the standard javac compiler, but many IDEs and build tools require something extra to enable annotation processing.

IDE

This page shows the steps to configure Eclipse and IntelliJ IDEA to enable annotation processing.

Maven

In Maven, use annotationProcessorPaths in the configuration of the maven-compiler-plugin. This requires maven-compiler-plugin plugin version 3.5 or higher.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <!-- annotationProcessorPaths requires maven-compiler-plugin version 3.5 or higher -->
  <version>${maven-compiler-plugin-version}</version>
  <configuration>
    <annotationProcessorPaths>
      <path>
        <groupId>info.picocli</groupId>
        <artifactId>picocli-codegen</artifactId>
        <version>4.0.0</version>
      </path>
    </annotationProcessorPaths>
    <compilerArgs>
      <arg>-Aproject=${groupId}/${artifactId}</arg>
    </compilerArgs>
  </configuration>
</plugin>

See the picocli-codegen README for more details.

Gradle

Use the annotationProcessor path in Gradle 4.6 and higher:

dependencies {
    compile 'info.picocli:picocli:4.0.0'
    annotationProcessor 'info.picocli:picocli-codegen:4.0.0'
}

To set an annotation processor option in Gradle, add these options to the options.compilerArgs list in the compileJava block.

compileJava {
    options.compilerArgs += ["-Aproject=${project.group}/${project.name}"]
}

See the picocli-codegen README for more details.

Execution API

executable commands

Picocli 4.0 introduces new API to execute commands. Let’s take a quick look at what changed.

Exit Code

Many command line applications return an exit code to signify success or failure. Zero often means success, a non-zero exit code is often used for errors, but other than that, meanings differ per application.

The new CommandLine.execute method introduced in picocli 4.0 returns an int, and applications can use this return value to call System.exit if desired. For example:

public static void main(String... args) {
  CommandLine cmd = new CommandLine(new App());
  int exitCode = cmd.execute(args);
  System.exit(exitCode);
}

Older versions of picocli had some limited exit code support where picocli would call System.exit, but this is now deprecated.

Generating an Exit Code

@Command-annotated classes that implement Callable and @Command-annotated methods can simply return an int or Integer, and this value will be returned from CommandLine.execute. For example:

@Command(name = "greet")
class Greet implements Callable<Integer> {
  public Integer call() {
    System.out.println("hi");
    return 1;
  }

  @Command
  int shout() {
    System.out.println("HI!");
    return 2;
  }
}

assert 1 == new CommandLine(new Greet()).execute();
assert 2 == new CommandLine(new Greet()).execute("shout");

Commands with a user object that implements Runnable can implement the IExitCodeGenerator interface to generate an exit code. For example:

@Command(name = "wave")
class Gesture implements Runnable, IExitCodeGenerator {
  public void run() {
    System.out.println("wave");
  }
  public int getExitCode() {
    return 3;
  }
}

assert 3 == new CommandLine(new Gesture()).execute();

Exception Exit Codes

By default, the execute method returns CommandLine.ExitCode.USAGE (64) for invalid input, and CommandLine.ExitCode.SOFTWARE (70) when an exception occurred in the Runnable, Callable or command method. (For reference, these values are EX_USAGE and EX_SOFTWARE, respectively, from Unix and Linux sysexits.h). This can be customized with the @Command annotation. For example:

@Command(exitCodeOnInvalidInput = 123,
   exitCodeOnExecutionException = 456)

Additionally, applications can configure a IExitCodeExceptionMapper to map a specific exception to an exit code:

class MyMapper implements IExitCodeExceptionMapper {
  public int getExitCode(Throwable t) {
    if (t instanceof FileNotFoundException) {
      return 74;
    }
    return 1;
  }
}

When the end user specified invalid input, the execute method prints an error message followed by the usage help message of the command, and returns an exit code. This can be customized by configuring a IParameterExceptionHandler.

If the business logic of the command throws an exception, the execute method prints the stack trace of the exception and returns an exit code. This can be customized by configuring a IExecutionExceptionHandler.

Configuration

The new CommandLine.execute method is an instance method. The older run, call and invoke methods are static methods. Static methods don’t allow configuration. The new API lets applications configure the parser or other aspects before execution. For example:

public static void main(String... args) {
  CommandLine cmd = new CommandLine(new App());
  cmd.setCaseInsensitiveEnumValuesAllowed(true);
  cmd.setUnmatchedArgumentsAllowed(true);
  cmd.setStopAtPositional(true);
  cmd.setExpandAtFiles(false);
  cmd.execute(args);
}

Spring support

spring and spring boot logos

Thibaud Lepretre, the author of kakawait/picocli-spring-boot-starter has graciously contributed a pull request to the picocli project with a new picocli-spring-boot-starter module. This includes a PicocliSpringFactory and auto-configuration and makes it extremely easy to use Spring dependency injection in your picocli command line application.

Spring Boot example

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.Bean;
import picocli.CommandLine;
import picocli.CommandLine.IFactory;

@SpringBootApplication
public class MySpringApp implements CommandLineRunner, ExitCodeGenerator {
    private int exitCode;

    @Autowired
    IFactory factory; // auto-configured to inject PicocliSpringFactory

    @Autowired
    MyCommand myCommand; // your @picocli.CommandLine.Command-annotated class

    @Override
    public void run(String... args) {
        // let picocli parse command line args and run the business logic
        exitCode = new CommandLine(myCommand, factory).execute(args);
    }

    @Override
    public int getExitCode() {
        return exitCode;
    }

    public static void main(String[] args) {
        // let Spring instantiate and inject dependencies
        System.exit(SpringApplication.exit(SpringApplication.run(MySpringApp.class, args)));
    }
}

When your command is annotated with @Component, Spring can autodetect it for dependency injection.
The business logic of your command looks like any other picocli command with options and parameters.

import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import java.util.concurrent.Callable;

@Component
@Command(name = "myCommand")
public class MyCommand implements Callable<Integer> {

    @Autowired
    private SomeService someService;

    // Prevent "Unknown option" error when users use
    // the Spring Boot parameter 'spring.config.location' to specify
    // an alternative location for the application.properties file.
    @Option(names = "--spring.config.location", hidden = true)
    private String springConfigLocation;

    @Option(names = { "-x", "--option" }, description = "example option")
    private boolean flag;

    public Integer call() throws Exception {
        // business logic here
        return 0;
    }
}

Argument groups

argument groups

This release introduces a new @ArgGroup annotation and its ArgGroupSpec programmatic equivalent.

Argument Groups can be used to define:

  • mutually exclusive options
  • options that must co-occur (dependent options)
  • option sections in the usage help message
  • repeating composite arguments

To create a group using the annotations API, annotate a field or method with @ArgGroup.
The field's type refers to the class containing the options and positional parameters in the group.
(For annotated interface methods this would be the return type, for annotated setter methods in a concrete class this would be the setter's parameter type.)

Picocli will instantiate this class as necessary to capture command line argument values in the @Option and @Parameters-annotated fields and methods of this class.

Mutually Exclusive Options

Annotate a field or method with @ArgGroup(exclusive = true) to create a group of mutually exclusive options and positional parameters. For example:

@Command(name = "exclusivedemo")
public class MutuallyExclusiveOptionsDemo {

    @ArgGroup(exclusive = true, multiplicity = "1")
    Exclusive exclusive;

    static class Exclusive {
        @Option(names = "-a", required = true) int a;
        @Option(names = "-b", required = true) int b;
        @Option(names = "-c", required = true) int c;
    }
}

The above example defines a command with mutually exclusive options -a, -b and -c.

The group itself has a multiplicity attribute that defines how many times the group may be specified within the command.
The default is multiplicity = "0..1", meaning that by default a group may be omitted or specified once.
In this example the group has multiplicity = "1", so the group must occur once: one of the exclusive options must occur on the command line.

The synopsis of this command is exclusivedemo (-a=<a> | -b=<b> | -c=<c>).

Note that the options are defined as required = true; this means required within the group, not required within the command.

Picocli will validate the arguments and throw a MutuallyExclusiveArgsException if multiple mutually exclusive arguments were specified. For example:

MutuallyExclusiveOptionsDemo example = new MutuallyExclusiveOptionsDemo();
CommandLine cmd = new CommandLine(example);

try {
    cmd.parseArgs("-a=1", "-b=2");
} catch (MutuallyExclusiveArgsException ex) {
    assert "Error: -a=<a>, -b=<b> are mutually exclusive (specify only one)"
            .equals(ex.getMessage());
}

For the above group, only one of the options can be specified. Any other combination of options, or the absence of options, is invalid.

Mutually Dependent Options

Annotate a field or method with @ArgGroup(exclusive = false) to create a group of dependent options and positional parameters that must co-occur. For example:

@Command(name = "co-occur")
public class DependentOptionsDemo {

    @ArgGroup(exclusive = false)
    Dependent dependent;

    static class Dependent {
        @Option(names = "-a", required = true) int a;
        @Option(names = "-b", required = true) int b;
        @Option(names = "-c", required = true) int c;
    }
}

The above example defines a command with dependent options -a, -b and -c that must co-occur.

The group itself has a multiplicity attribute that defines how many times the group may be specified within the command.
In this example the group uses the default multiplicity, multiplicity = "0..1", meaning that the group may be omitted or specified once.

The synopsis of this command is co-occur [-a=<a> -b=<b> -c=<c>].

Note that the options are defined as required = true; this means required within the group, not required within the command.

Picocli will validate the arguments and throw a MissingParameterException if not all dependent arguments were specified. For example:

DependentOptionsDemo example = new DependentOptionsDemo();
CommandLine cmd = new CommandLine(example);

try {
    cmd.parseArgs("-a=1", "-b=2");
} catch (MissingParameterException ex) {
    assert "Error: Missing required argument(s): -c=<c>".equals(ex.getMessage());
}

Option Sections in Usage Help

The example below uses groups to define options sections in the usage help.
When a group has a non-null heading (or headingKey), the options in the group are given the specified heading in the usage help message.
The headingKey attribute can be used to get the heading text from the command's resource bundle.

This works for mutually exclusive or co-occurring groups, but it is also possible to define a group that does no validation but only creates an option section in the usage help.

Annotate a field or method with @ArgGroup(validate = false) to create a group for display purposes only. For example:

@Command(name = "sectiondemo", description = "Section demo")
public class OptionSectionDemo {

    @ArgGroup(validate = false, heading = "This is the first section%n")
    Section1 section1;

    static class Section1 {
        @Option(names = "-a", description = "Option A") int a;
        @Option(names = "-b", description = "Option B") int b;
        @Option(names = "-c", description = "Option C") int c;
    }

    @ArgGroup(validate = false, heading = "This is the second section%n")
    Section2 section2;

    static class Section2 {
        @Option(names = "-x", description = "Option X") int x;
        @Option(names = "-y", description = "Option Y") int y;
        @Option(names = "-z", description = "Option X") int z;
    }

    public static void main(String[] args) {
        new CommandLine(new OptionSectionDemo()).usage(System.out);
    }
}

This prints the following usage help message:

Usage: sectiondemo [-a=<a>] [-b=<b>] [-c=<c>] [-x=<x>] [-y=<y>] [-z=<z>]
Section demo
This is the first section
  -a=<a>    Option A
  -b=<b>    Option B
  -c=<c>    Option C
This is the second section
  -x=<x>    Option X
  -y=<y>    Option Y
  -z=<z>    Option X

Note that the heading text must end with %n to insert a newline between the heading text and the first option.
This is for consistency with other headings in the usage help, like @Command(headerHeading = "Usage:%n", optionListHeading = "%nOptions:%n").

Repeating Composite Argument Groups

The below example shows how groups can be composed of other groups, and how arrays and collections can be used to capture repeating groups (with a multiplicity greater than one):

@Command(name = "repeating-composite-demo")
public class CompositeGroupDemo {

    @ArgGroup(exclusive = false, multiplicity = "1..*")
    List<Composite> composites;

    static class Composite {
        @ArgGroup(exclusive = false, multiplicity = "0..1")
        Dependent dependent;

        @ArgGroup(exclusive = true, multiplicity = "1")
        Exclusive exclusive;
    }

    static class Dependent {
        @Option(names = "-a", required = true) int a;
        @Option(names = "-b", required = true) int b;
        @Option(names = "-c", required = true) int c;
    }

    static class Exclusive {
        @Option(names = "-x", required = true) boolean x;
        @Option(names = "-y", required = true) boolean y;
        @Option(names = "-z", required = true) boolean z;
    }
}

In the above example, the annotated composites field defines a composite group that must be specified at least once, and may be specified many times (multiplicity = "1..*"), on the command line.

The synopsis of this command is:

Usage: repeating-composite-demo ([-a=<a> -b=<b> -c=<c>] (-x | -y | -z))...

Each time the group is matched, picocli creates an instance of the Composite class and adds it to the composites list.

The Composite class itself contains two groups: an optional (multiplicity = "0..1") group of dependent options that must co-occur, and another group of mutually exclusive options, which is mandatory (multiplicity = "1").

The below example illustrates:

CompositeGroupDemo example = new CompositeGroupDemo();
CommandLine cmd = new CommandLine(example);

cmd.parseArgs("-x", "-a=1", "-b=1", "-c=1", "-a=2", "-b=2", "-c=2", "-y");
assert example.composites.size() == 2;

Composite c1 = example.composites.get(0);
assert c1.exclusive.x;
assert c1.dependent.a == 1;
assert c1.dependent.b == 1;
assert c1.dependent.c == 1;

Composite c2 = example.composites.get(1);
assert c2.exclusive.y;
assert c2.dependent.a == 2;
assert c2.dependent.b == 2;
assert c2.dependent.c == 2;

Positional Parameters

When a @Parameters positional parameter is part of a group, its index is the index within the group, not within the command.

Variable expansion

From this release, picocli supports variable interpolation (variable expansion) in annotation attributes as well as in text attributes of the programmatic API.

Variable Interpolation Example

@Command(name = "status", description = "This command logs the status for ${PARENT-COMMAND-NAME}.")
class Status {
    @Option(names = {"${dirOptionName1:--d}", "${dirOptionName2:---directories}"}, // -d or --directories
            description = {"Specify one or more directories, separated by '${sys:path.separator}'.",
                           "The default is the user home directory (${DEFAULT-VALUE})."},  
            arity = "${sys:dirOptionArity:-1..*}",
            defaultValue = "${sys:user.home}",
            split = "${sys:path.separator}")
    String[] directories;
}

Predefined Variables

See the user manual for the list of predefined variables.

Custom Variables

In addition, you can define your own variables. Currently the following syntaxes are supported:

  • ${sys:key}: system property lookup, replaced by the value of System.getProperty("key")
  • ${env:key}: environment variable lookup, replaced by the value of System.getEnv("key")
  • ${bundle:key}: look up the value of key in the resource bundle of the command
  • ${key}: search all of the above, first system properties, then environment variables, and finally the resource bundle of the command

Default Values for Custom Variables

You can specify a default value to use when no value is found for a custom variable. The syntax for specifying a default is ${a:-b}, where a is the variable name and b is the default value to use if a is not found.

Explicit JPMS module

modules

The main picocli-4.0.0.jar is a JPMS module named info.picocli.

Starting from picocli 4.0, this jar will be an explicit module instead of an automatic module, so the jlink tool can be used to provide a trimmed binary image that has only the required modules.

Typically, a modular jar includes the module-info.class file in its root directory. This causes problems for some older tools, which incorrectly process the module descriptor as if it were a normal Java class. To provide the best backward compatibility, the main picocli artifact is a modular multi-release jar with the module-info.class file located in META-INF/versions/9.

Separate picocli-groovy module

Groovy logo

Also, from this release the main picocli-4.x artifact no longer contains the picocli.groovy classes: these have been split off into a separate picocli-groovy-4.x artifact. This was necessary to make the main picocli-4.x.jar an explicit JPMS module.

Negatable options

From picocli 4.0, options can be negatable.

class App {
    @Option(names = "--verbose",           negatable = true) boolean verbose;
    @Option(names = "-XX:+PrintGCDetails", negatable = true) boolean printGCDetails;
    @Option(names = "-XX:-UseG1GC",        negatable = true) boolean useG1GC = true;
}

When an option is negatable, picocli will recognize negative aliases of the option on the command line.

For *nix-style long options, aliases have the prefix 'no-' to the given names.
For Java JVM-style options, the :+ is turned into :- and vice versa. (This can be changed by customizing the INegatableOptionTransformer.)

If the negated form of the option is found, for example --no-verbose, the value is set to the provided default. Otherwise, with a regular call, for example --verbose, it is set to the opposite of the default.

Fallback value for options

This release introduces a new attribute on the Option annotation: fallbackValue for options with optional parameter: assign this value when the option was specified on the command line without parameter.

This is different from the defaultValue, which is assigned if the option is not specified at all on the command line.

Using a fallbackValue allows applications to distinguish between cases where

  • the option was not specified on the command line (default value assigned)
  • the option was specified without parameter on the command line (fallback value assigned)
  • the option was specified with parameter on the command line (command line argument value assigned)

This is useful to define options that can function as a boolean "switch" and optionally allow users to provide a (strongly typed) extra parameter value.

The option description may contain the ${FALLBACK-VALUE} variable which will be replaced with the actual fallback value when the usage help is shown.

Custom parameter processing

Options or positional parameters can be assigned a IParameterConsumer that implements custom logic to process the parameters for this option or this position. When an option or positional parameter with a custom IParameterConsumer is matched on the command line, picocli's internal parser is temporarily suspended, and the custom parameter consumer becomes responsible for consuming and processing as many command line arguments as needed.

This can be useful when passing options through to another command.

For example, the unix find command has a -exec option to execute some action for each file found. Any arguments following the -exec option until a ; or + argument are not options for the find command itself, but are interpreted as a separate command and its options.

The example below demonstrates how to implement find -exec using this API:

@Command(name = "find")
class Find {
    @Option(names = "-exec", parameterConsumer = ExecParameterConsumer.class)
    List<String> list = new ArrayList<String>();
}

class ExecParameterConsumer implements IParameterConsumer {
    public void consumeParameters(Stack<String> args, ArgSpec argSpec, CommandSpec commandSpec) {
        List<String> list = argSpec.getValue();
        while (!args.isEmpty()) {
            String arg = args.pop();
            list.add(arg);

            // `find -exec` semantics: stop processing after a ';' or '+' argument
            if (";".equals(arg) || "+".equals(arg)) {
                break;
            }
        }
    }
}

Improved parsing of quoted parameters

Also, from this release, support for quoted parameter values has been improved. Quoted parameter values can now contain nested quoted substrings to give end users fine-grained control over how values are split. See the user manual for details.

Auto-detect terminal width for usage help

From this release, commands defined with @Command(usageHelpAutoWidth = true) will try to adjust the usage message help layout to the terminal width.
There is also programmatic API to control this via the CommandLine::setUsageHelpAutoWidth and UsageMessageSpec::autoWidth methods.

End users may enable this by setting system property picocli.usage.width to AUTO, and may disable this by setting this system property to a numeric value.

This feature requires Java 7.

Improved support for Chinese, Japanese and Korean usage help

Picocli will align the usage help message to fit within some user-defined width (80 columns by default).
A number of characters in Chinese, Japanese and Korean (CJK) are wider than others.
If those characters are treated to have the same width as other characters, the usage help message may extend past the right margin.

From this release, picocli will use 2 columns for these wide characters when calculating where to put line breaks, resulting in better usage help message text.

This can be switched off with CommandLine.setAdjustLineBreaksForWideCJKCharacters(false).

Fixed issues

4.0.0-GA

  • [#752][#658][#496] Add picocli-spring-boot-starter module including a PicocliSpringFactory and auto-configuration. Thanks to Thibaud Lepretre for the pull request.
  • [#736] API: Allow removal of ArgSpec from CommandSpec. Thanks to AkosCz for the feature request.
  • [#756] API: Make synopsis indent for multi-line synopsis configurable (related to #739).
  • [#761] API: Add ParseResult.matchedArgs() method to return all matched arguments in order; change ParseResult.matchedOptions() and ParseResult.matchedPositionals() to return the full list of matched options and positional parameters, including duplicates if the option or positional parameter was matched multiple times. Thanks to Michael D. Adams for the feature request.
  • [#760] API: Deprecate CommandLine.setSplitQuotedStrings: the vast majority of applications want to split while respecting quotes.
  • [#754] API/Enhancement: Allow boolean options to get value from fallback instead of defaultProvider. Thanks to Michael D. Adams for the feature request.
  • [#696][#741] Enhancement: Automatically split lines in TextTable. Thanks to Sualeh Fatehi for the pull request.
  • [#744] Enhancement: Composite Argument Groups: more informative error messages. Thanks to Andreas Deininger for raising this.
  • [#745] Enhancement: Picocli should disallow split regex for single-value type options. Thanks to Andreas Deininger for raising this.
  • [#748] Enhancement: Provide API to use a custom Layout in usage help message: ensure Help.createDefaultLayout() is used internally so that subclasses overriding this method can control the Layout that is used.
  • [#595] Enhancement: Support for quoted arguments containing nested quoted substrings, allowing end-users to control how values are split in parts when a split regex is defined.
  • [#739] Bugfix: infinite loop or exception when command name plus synopsis heading length equals or exceeds usage help message width. Thanks to Arturo Alonso for raising this.
  • [#746] Bugfix: Apply default values to options and positional parameters in argument groups. Thanks to Andreas Deininger for raising this.
  • [#742] Bugfix: Default values prevent correct parsing in argument groups. Thanks to Andreas Deininger for raising this.
  • [#759] Bugfix: Correct tracing when custom end-of-option delimiter is matched on the command line.
  • [#738] Bugfix: setTrimQuotes does not trim quotes from option names. Thanks to Judd Gaddie for raising this.
  • [#758] Bugfix: Duplicate name exception in argument group: better / more concise error message. Thanks to Andreas Deininger for raising this.
  • [#751] Build: Make build more portable.
  • [#753] Doc: Improve documentation for multi-value fields: mention the split attribute. Thanks to feinstein.
  • [#740] Doc: Update user manual to replace parse examples with parseArgs.
  • [#713] Doc: Update UML class diagrams for picocli 4.0.

4.0.0-beta-2

  • [#280] API: @Option(fallbackValue = "...") for options with optional parameter: assign this value when the option was specified on the command line without parameter. Thanks to Paolo Di Tommaso and marinier for the suggestion and in-depth discussion.
  • [#625] API: @Command(synopsisSubcommandLabel = "...") to allow customization of the subcommands part of the synopsis: by default this is [COMMAND]. Thanks to Sebastian Thomschke and AlcaYezz for the feature request and subsequent discussion.
  • [#718] API: Add IParameterConsumer and @Option(parameterConsumer = Xxx.class) for passing arguments through to another command, like find -exec. Thanks to Reinhard Pointner for the suggestion.
  • [#721] API: Add public method Text.getCJKAdjustedLength().
  • [#634] API: Dynamically detect terminal size. Requires Java 7. Thanks to my colleague Takuya Ishibashi for the suggestion.
  • [#737] Deprecate the parse method in favor of parseArgs.
  • [#717] Negatable options change: avoid unmappable character ± for synopsis: it renders as scrambled characters in encoding ASCII and in some terminals.
  • [#734][#735] Make the picocli jar OSGi friendly. Thanks to Radu Cotescu for the pull request.
  • [#733] Improve error message for unmatched arguments. Thanks to my colleague Takuya Ishibashi for raising this.
  • [#719] Bugfix: options with variable arity should stop consuming arguments on custom end-of-options delimiter.
  • [#720] Bugfix: @Unmatched list should be cleared prior to subsequent invocations.
  • [#723] Bugfix: variables in defaultValue were not expanded in usage help option description line for showDefaultValues = true. Thanks to Mikaël Barbero for raising this.
  • [#722] Bugfix: synopsis of deeply nested @ArgGroup shows @Options duplicate on outer level of command. Thanks to Shane Rowatt for raising this.
  • [#724] Bugfix: Usage message exceeds width.
  • [#731] Doc: Add Zero Bugs Commitment to README.

4.0.0-beta-1b

  • [#500] Add a generic and extensible picocli annotation processor
  • [#699] Add annotation processor that generates reflect-config.json during build
  • [#703] Add annotation processor that generates resource-config.json during build
  • [#704] Add annotation processor that generates proxy-config.json during build
  • [#707] Add example maven/gradle projects that demonstrate using the annotation processor
  • [#711] API: Create separate picocli-groovy module, make picocli an explicit module (a modular multiversion jar)
  • [#694] API: negatable boolean options. Thanks to Michael D. Adams for the feature request.
  • [#712] Boolean options should not toggle by default, to be consistent with negatable options
  • [#709] Fix scrambled characters for the ± character when running on system with non-UTF8 encoding
  • [#717] Fix unmappable character for encoding ASCII by setting compiler encoding to UTF8 explicitly. Thanks to Liam Esteban Prince for raising this.
  • [#697] Option sort in usage help should ignore option name prefix; long options without short name should be inserted alphabetically, instead of always appear at the top.
  • [#695] Fix runtime warnings about illegal reflective access to field java.io.FilterOutputStream.out. Thanks to gitfineon for reporting this issue.
  • [#698] Reduce reflect-config.json used by GraalVM native-image generation
  • [#700] Change default exit codes to 1 for Exceptions in client code, 2 for invalid usage. Add links to ExitCode javadoc.
  • [#715] processor tests should not fail when running in different locale
  • [#710] Let annotation processor validate negatable options, usageHelp options
  • [#716] Revert @Inherited annotation for @Command. Thanks to Mikusch for raising this.

4.0.0-alpha-3

  • [#516] API: Add support for color schemes in the convenience methods and associated classes and interfaces. Thanks to Bob Tiernay for the suggestion.
  • [#561] API: Parser configuration for convenience methods.
  • [#650] API: Global parser configuration if using Runnable. Thanks to gitfineon for raising this.
  • [#424] API: Exit on help, version or invalid arguments. Thanks to Gerard Bosch for raising this.
  • [#541] API: Improved exception handling for Runnable/Callable.
  • [#680] API: Add annotation API for exitCodeList and exitCodeListHeading.
  • [#611] API: Add CommandLine.addSubcommand overloaded method without name or alias. Thanks to andrewbleonard for the request.
  • [#684] API: Make CommandLine.defaultFactory method public.
  • [#675] API: Make Help.ColorScheme immutable. This is a breaking API change.
  • [#673] API: Deprecate CommandLine.Range public fields, add accessor methods to use instead.
  • [#663] How to remove stacktraces on error. Thanks to Nicolas Mingo and jrevault for raising this and subsequent discussion.
  • [#672] Need way to send errors back from subcommand. Thanks to Garret Wilson for raising this.
  • [#678] Exit Status section in usage help message.
  • [#683] Ensure exitCodeList implementation is consistent with other usage message attributes.
  • [#575] Codegen: Use mixinStandardHelpOptions in AutoComplete$App (add support for the --version option)
  • [#645] Codegen: Exclude Jansi Console from generated GraalVM reflection configuration. Thanks to shanetreacy for raising this.
  • [#686] Codegen: Add support for @Command interfaces (dynamic proxies) in GraalVM native image.
  • [#669] Codegen: Add support for resource bundles in GraalVM native image.
  • [#691] Codegen bugfix: ReflectionConfigGenerator should not generate config for picocli.CommandLine$Model$ObjectScope.
  • [#674] JPMS module: move module-info.class to root of jar.
  • [#676] Bugfix: non-defined variables in defaultValue now correctly resolve to null, and options and positional parameters are now correctly considered required only if their default value is null after variable interpolation. Thanks to ifedorenko for raising this.
  • [#682] Bugfix: incorrect evaluation for multiple occurrences of a variable.
  • [#689] NPE in codegen OutputFileMixin.
  • [#679] Documentation: Update examples for new execute API. Add examples for exit code control and custom exception handlers.
  • [#681] Documentation: Add exit code section to Internationalization example in user manual.

4.0.0-alpha-2

  • [#495] Publish picocli as a JPMS module in a new artifact picocli-core-module. Thanks to Warkdev for the pull request.
  • [#21] Count double-width Asian characters as two characters for line-breaking purposes.
  • [#526] Add support for variable interpolation in message strings. Thanks to Bob Tiernay for the suggestion.
  • [#660] Added @java.lang.annotation.Inherited to the @picocli.CommandLine.Command annotation. Thanks to Devin Smith for the suggestion.
  • [#661] Bugfix for stack overflow when option in an argument group had a default value. Thanks to Andreas Deininger for reporting this.
  • [#656] Bugfix for issue where synopsis for composite argument groups did not expand for n..* (n > 1). Thanks to Arno Tuomainen for finding this issue.
  • [#654] Bugfix: argument group heading text was not retrieved from ResourceBundle. Thanks to Andreas Deininger for raising this.
  • [#635] Bugfix in argument group validation: did not show an error if some but not all parts of a co-occurring group were specified. Thanks to Philipp Hanslovsky for the pull request.
  • [#653] Bugfix: argument group validation should be skipped if help was requested. Thanks to Andreas Deininger for raising this.
  • [#655] Bugfix: argument group validation silently accepts missing subgroup with multiplicity=1.
  • [#652] Documentation: fixes in user manual. Thanks to Andreas Deininger for the pull request.
  • [#651] Documentation: fixes in user manual. Thanks to Andreas Deininger for the pull request.

4.0.0-alpha-1

  • [#643] Change % to %% when using ${DEFAULT-VALUE} in option description. Thanks to Steffen Rehberg for the pull request.
  • [#638] Document fallback descriptionKey for options and parameters in user manual. Thanks to Mikusch for the suggestion.
  • [#199] mutually exclusive options
  • [#295] options that must co-occur (dependent options)
  • [#450] option grouping in the usage help message
  • [#358] (also [#635]) repeating composite arguments (this should also cover the use cases presented in #454 and #434 requests for repeatable subcommands)

Deprecations

run, call, invoke, and parseWithHandlers methods replaced by execute

All variants of the run, call, invoke, and parseWithHandlers methods are deprecated from this release, in favor of the new execute method.

Similarly, the following classes and interfaces are deprecated:

  • IParseResultHandler2 is deprecated in favor of the new IExecutionStrategy interface.
  • IExceptionHandler2 is deprecated in favor of the new IParameterExceptionHandler IExecutionExceptionHandler interfaces.
  • The AbstractHandler and AbstractParseResultHandler classes are deprecated with no replacement.

CommandLine.setSplitQuotedStrings deprecated

The CommandLine.setSplitQuotedStrings (and isSplitQuotedStrings) methods have been deprecated:
Most applications should not change the default. The rare application that does need to split parameter values without respecting quotes should use ParserSpec.splitQuotedStrings(boolean).

parse deprecated in favor of parseArgs

From this release, the parse method is deprecated in favor of parseArgs.

Range public fields

The public fields of the Range class have been deprecated and public methods min(), max(), isVariable() have been added that should be used instead.

Potential breaking changes

picocli.groovy classes moved to separate artifact

From this release the main picocli-4.x artifact no longer contains the picocli.groovy classes: these have been split off into a separate picocli-groovy-4.x artifact.

Scripts upgrading to picocli 4.0 must change more than just the version number!
Scripts should use @Grab('info.picocli:picocli-groovy:4.x') from version 4.0, @Grab('info.picocli:picocli:4.x') will not work.

Split regex on single-value options is now disallowed

Picocli now throws an InitializationException when a single-value type option or positional parameter has a split regex.
Only multi-value options or positional parameters should have a split regex. The runtime check can be disabled by setting system property picocli.ignore.invalid.split to any value.
(The annotation processor also checks this at compile time; this check cannot be disabled.)

ColorScheme is now immutable

The Help.ColorScheme class has been made immutable. Its public fields are no longer public.
A new Help.ColorScheme.Builder class has been introduced to create ColorScheme instances.

This is a breaking API change: I could not think of a way to do this without breaking backwards compatibility.

Boolean options do not toggle by default

From this release, when a flag option is specified on the command line picocli will set its value to the opposite of its default value.

Prior to 4.0, the default was to "toggle" boolean flags to the opposite of their current value:
if the previous value was true it is set to false, and when the value was false it is set to true.

Applications can call CommandLine.setToggleBooleanFlags(true) to enable toggling.
Note that when toggling is enabled, specifying a flag option twice on the command line will have no effect because they cancel each other out.

ParseResult matchedOptions now returns full list

ParseResult.matchedOptions() and ParseResult.matchedPositionals() now return the full list of matched options and positional parameters, including duplicates if the option or positional parameter was matched multiple times.
Prior to this release, these methods would return a list that did not contain duplicates.
Applications interested in the old behavior should use the new matchedOptionSet() and matchedPositionalSet() methods that return a Set.

Error message for unmatched arguments changed

The error message for unmatched arguments now shows the index in the command line arguments where the unmatched argument was found,
and shows the unmatched value in single quotes. This is useful when the unmatched value is whitespace or an empty String.

For example:

Previously:  Unmatched arguments: B, C
New       :  Unmatched arguments from index 1: 'B', 'C'

This may break tests that rely on the exact error message.

Option order changed

Previously, options that only have a long name (and do not have a short name) were always shown before options with a short name.
From this release, they are inserted in the option list by their first non-prefix letter.
This may break tests that expect a specific help message.

Factory

From version 4.0, picocli delegates all object creation to the factory, including creating Collection instances to capture multi-value @Option values. Previously, Collection objects were instantiated separately without involving the factory.

It is recommended that custom factories should fall back to the default factory. Something like this:

@Override
public <K> K create(Class<K> clazz) throws Exception {
    try {
        return doCreate(clazz); // custom factory lookup or instantiation
    } catch (Exception e) {
        return CommandLine.defaultFactory().create(clazz); // fallback if missing
    }
}