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 therun
method, and aCallable
to thecall
method. Theexecute
method does not have this type safety, since theCommandLine
constructor allows anyObject
as a parameter. - Return Value - The
call
andinvoke
static methods allow commands to return any value, while theexecute
method only returns anint
exit code. From 4.0 the result object will be available from theCommandLine.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 forpicocli.CommandLine$Model$ObjectScope
. - [#674] JPMS module: move module-info.class to root of jar.
- [#676] Bugfix: non-defined variables in
defaultValue
now correctly resolve tonull
, and options and positional parameters are now correctly consideredrequired
only if their default value isnull
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 newIExecutionStrategy
interface.IExceptionHandler2
is deprecated in favor of the newIParameterExceptionHandler
IExecutionExceptionHandler
interfaces.- The
AbstractHandler
andAbstractParseResultHandler
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.