github remkop/picocli v4.0.0-alpha-3
Picocli 4.0.0-alpha-3

latest releases: v4.7.6, v4.7.5, v4.7.4...
5 years ago

Picocli 4.0.0-alpha-3

The picocli community is pleased to announce picocli 4.0.0-alpha-3.

This release adds improved support for command execution via the new execute method.
This method returns an exit code that applications can use to call System.exit.

The older run, call, invoke and parseWithHandlers convenience methods that were similar to execute but had limited support for parser configuration and and limited support for exit codes are deprecated from this release.

This release also improves the picocli tools for configuring GraalVM native image builds: there is now support for commands with resource bundles and jewelcli-style @Command-annotated interfaces for which picocli generates a dynamic proxy.

Please try this and provide feedback. We can still make changes.

What do you think of the @ArgGroup annotations API? What about the programmatic API? Does it work as expected? Are the input validation error messages correct and clear? Is the documentation clear and complete? Anything you want to change or improve? Any other feedback?

Many thanks to the picocli community for the contributions!

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

Table of Contents

  • New and noteworthy
  • Fixed issues
  • Deprecations
  • Potential breaking changes

New and Noteworthy

Executing 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 instance of 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.setUnmarchedArgumentsAllowed(true);
  cmd.setStopAtPositional(true);
  cmd.setExpandAtFiles(false);
  cmd.execute(args);
}

Execution Configuration

The following configuration methods are new and are only applicable with the execute method (and executeHelpRequest):

  • get/setOut
  • get/setErr
  • get/setColorScheme
  • get/setExecutionStrategy
  • get/setParameterExceptionHandler
  • get/setExecutionExceptionHandler
  • get/setExitCodeExceptionMapper

The above methods are not applicable (and ignored) with other entry points like parse, parseArgs, populateCommand, run, call, invoke, parseWithHandler and parseWithHandlers.

API Evolution and Trade-offs

Previous versions of picocli offered the run, call and invoke methods to execute a Runnable, Callable or Method command. Here are some trade-offs versus the new execute method:

  • Static - These are static methods, with the drawback that they don't allow configuration, as mentioned above.
  • Type Safety - It is a compile-time error when an application tries to pass anything else than a Runnable to the run method, and a Callable to the call method. The execute method does not have this type safety, since the CommandLine constructor allows any Object as a parameter.
  • Return Value - The call and invoke static methods allow commands to return any value, while the execute method only returns an int exit code. From 4.0 the result object will be available from the CommandLine.getExecutionResult method.

Feedback Requested

With the new execute API the ColorScheme class will start to play a more central role. I decided to make the ColorScheme class immutable from this release. This is a breaking API change.
Should it be deprecated first, or not changed at all, or is the 4.0 release a good time to make breaking changes? Your feedback is very welcome on #675.

Tools for Configuring GraalVM Native Image Builds

The picocli-codegen module now has two new tools, in addition to the existing ReflectionConfigGenerator:

  • ResourceConfigGenerator
  • DynamicProxyConfigGenerator

ResourceConfigGenerator

The GraalVM native-image builder by default will not integrate any of the
classpath resources into the image it creates.

ResourceConfigGenerator generates a JSON String with the resource bundles and other classpath resources
that should be included in the Substrate VM native image.

The output of ResourceConfigGenerator is intended to be passed to the -H:ResourceConfigurationFiles=/path/to/reflect-config.json option of the native-image GraalVM utility,
or placed in a META-INF/native-image/ subdirectory of the JAR.

This allows picocli-based native image applications to access these resources.

DynamicProxyConfigGenerator

Substrate VM doesn't provide machinery for generating and interpreting bytecodes at run time. Therefore all dynamic proxy classes
need to be generated at native image build time.

DynamicProxyConfigGenerator generates a JSON String with the fully qualified interface names for which
dynamic proxy classes should be generated at native image build time.

The output of DynamicProxyConfigGenerator is intended to be passed to the -H:DynamicProxyConfigurationFiles=/path/to/proxy-config.json option of the native-image GraalVM utility,
or placed in a META-INF/native-image/ subdirectory of the JAR.

This allows picocli-based native image applications that use @Command-annotated interfaces with
@Option and @Parameters-annotated methods.

Fixed issues

  • [#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.

Deprecations

Convenience 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.

Range

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

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.

Don't miss a new picocli release

NewReleases is sending notifications on new releases.