mirror of
https://github.com/jlengrand/picocli.git
synced 2026-03-10 08:41:17 +00:00
DOC: migrating from Commons CLI updates - various updates
This commit is contained in:
BIN
docs/images/empower_developers.png
Normal file
BIN
docs/images/empower_developers.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 336 KiB |
BIN
docs/images/pexels-photo-1210532.jpeg
Normal file
BIN
docs/images/pexels-photo-1210532.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
BIN
docs/images/processing_results.jpg
Normal file
BIN
docs/images/processing_results.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
BIN
docs/images/seeing-the-future.jpg
Normal file
BIN
docs/images/seeing-the-future.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
@@ -1,28 +1,39 @@
|
||||
= Migrating from Commons CLI to picocli
|
||||
//:author: Remko Popma
|
||||
//:email: rpopma@apache.org
|
||||
:revnumber: 4.0.0-SNAPSHOT
|
||||
:revdate: 2018-11-18
|
||||
//:toc: left
|
||||
//:numbered:
|
||||
//:toclevels: 4
|
||||
:source-highlighter: coderay
|
||||
//:source-highlighter: highlightjs
|
||||
//:highlightjs-theme: darkula
|
||||
:icons: font
|
||||
:imagesdir: images
|
||||
|
||||
Commons CLI, initially released in 2002, is perhaps the most widely used java command line parser, but its API shows its age.
|
||||
Applications looking for a modern approach with a minimum of boilerplate code may be interested in picocli. Picocli offers a fluent API with strong typing, usage help with ANSI colors, autocompletion and a host of other features. Why is it worth the trouble to migrate, and how do you migrate your Commons CLI-based application to picocli? Let's take a look using http://checkstyle.sourceforge.net[Checkstyle] as an example.
|
||||
Apache Commons CLI, initially released in 2002, is perhaps the most widely used java command line parser, but its API shows its age.
|
||||
Applications looking for a modern approach with a minimum of boilerplate code may be interested in https://github.com/remkop/picocli[picocli]. Why is it worth the trouble to migrate, and how do you migrate your Commons CLI-based application to picocli? Picocli offers a fluent API with strong typing, usage help with ANSI colors, autocompletion and a host of other features. Let's take a look using http://checkstyle.sourceforge.net[Checkstyle] as an example.
|
||||
|
||||
|
||||
== Why Migrate?
|
||||
Is migrating from Commons CLI to picocli worth the trouble? What is the benefit of moving from one command line parser to another? Isn't that just redecorating the living room of our application?
|
||||
Is migrating from Commons CLI to picocli worth the trouble? What is the benefit of moving from one command line parser to another? Is this more than just redecorating the living room of our application?
|
||||
|
||||
|
||||
=== End User Experience
|
||||
|
||||
image:https://3g3qeh2v2xw32ifq4r3x9gj7-wpengine.netdna-ssl.com/wp-content/uploads/2018/07/pexels-photo-1210532.jpeg[width=200]
|
||||
image:https://picocli.info/images/pexels-photo-1210532.jpeg[width=200]
|
||||
|
||||
What are the benefits for the end user?
|
||||
What are the benefits for end users?
|
||||
|
||||
First, *@-files*, or https://picocli.info/#AtFiles["argument files"]. Sometimes users need to specify command lines that are longer than supported by the operating system or the shell. When picocli encounters an argument beginning with the character `@`, it expands the contents of that file into the argument list.
|
||||
*Command line completion*. Picocli-based applications can have https://picocli.info/autocomplete.html[command line completion] in bash and zsh shells, as well as in JLine-based https://github.com/remkop/picocli/tree/master/picocli-shell-jline2[interactive shell] applications.
|
||||
|
||||
Nice-looking *usage help* message. The usage help generated by Commons CLI is a bit minimalistic. Out of the box, picocli generates good-looking help with ANSI colors. Additionally, the help message layout is easy to customize. Most of the time the annotations provide enough control over the usage help layout, but there is also a Help API in case you want something different. See the picocli https://github.com/remkop/picocli[README] for some example screenshots.
|
||||
Beautiful, highly readable *usage help* messages. The usage help generated by Commons CLI is a bit minimalistic. Out of the box, picocli generates help that uses https://picocli.info/#_ansi_colors_and_styles[ANSI styles and colors] for contrast to emphasize important information like commands, options, and parameters. The help message layout is easy to customize using the annotations. Additionally, there is a Help API in case you want something different. See the picocli https://github.com/remkop/picocli[README] for some example screenshots.
|
||||
|
||||
*Troubleshooting*. Picocli has a built-in https://picocli.info/#_tracing[tracing] facility to facilitate troubleshooting. End users can use system property `picocli.trace` to control the trace level. Supported levels are `OFF`, `WARN`, `INFO`, and `DEBUG`. The default trace level is `WARN`. Specifying system property `-Dpicocli.trace` without a value will set the trace level to `INFO`.
|
||||
Support for *very large command lines* via @-files, or https://picocli.info/#AtFiles["argument files"]. Sometimes users need to specify command lines that are longer than supported by the operating system or the shell. When picocli encounters an argument beginning with the character `@`, it expands the contents of that file into the argument list. This allows applications to handle command lines of arbitrary length.
|
||||
|
||||
=== Developer Experience
|
||||
image:https://static1.squarespace.com/static/5a75d0d1d74cff3a9efea1c3/t/5a8c4fb153450ac8657fad03/1519144904584/1_ipf30KJppLZ_Godcq82R3Q.png[width=200]
|
||||
_Photo by https://www.pexels.com/u/nurseryart/[Porapak Apichodilok]_
|
||||
image:https://picocli.info/images/empower_developers.png[width=200]
|
||||
|
||||
What are the benefits for you as developer?
|
||||
|
||||
@@ -30,26 +41,23 @@ Generally a picocli application will have a lot *less code* than the Commons CLI
|
||||
|
||||
*Documentation*: picocli has an extensive https://picocli.info[user manual] and detailed https://picocli.info/apidocs/[javadoc].
|
||||
|
||||
Fewer small annoyances:
|
||||
*Troubleshooting*. Picocli has a built-in https://picocli.info/#_tracing[tracing] facility to facilitate troubleshooting. End users can use system property `picocli.trace` to control the trace level. Supported levels are `OFF`, `WARN`, `INFO`, and `DEBUG`. The default trace level is `WARN`.
|
||||
|
||||
* The default parser in Commons CLI recognizes long options with a single `-` hyphen even if its usage help message only shows long options with two `--` dashes. I did not see a way to prevent this. By contrast, picocli gives fine-grained and exact control over the option prefix.
|
||||
* It is surprisingly difficult in Commons CLI to create an application with a required option that also has a `--help` option. Commons CLI has no special treatment for help options and will complain about the missing required option when the user specifies `<command> --help`. Picocli has built-in support for common (and custom) help options.
|
||||
|
||||
=== Future Expansion
|
||||
image:https://cdn.psychologytoday.com/sites/default/files/styles/image-article_inline_full/public/field_blog_entry_images/2017-08/seeing-the-future.jpg[width=200]
|
||||
image:https://picocli.info/images/seeing-the-future.jpg[width=200]
|
||||
|
||||
Finally, other than the immediate pay-off, are there any future benefits to be gained by migrating from Commons CLI to picocli?
|
||||
|
||||
Picocli has a lot of *advanced features*. Your application may not use these features yet, but if you want to expand your application in the future, picocli has support for nested https://picocli.info/#_subcommands[subcommands] (and sub-subcommands to any depth), can easily integrate with Dependency Injection frameworks, and has tools to generate completion scripts to give your application https://picocli.info/autocomplete.html[command line completion] in bash and zsh shells.
|
||||
Picocli has a lot of *advanced features*. Your application may not use these features yet, but if you want to expand your application in the future, picocli has support for nested https://picocli.info/#_subcommands[subcommands] (and sub-subcommands to any depth), https://picocli.info/#_mixins[mixins] for reuse, can easily https://picocli.info/#_dependency_injection[integrate with] Dependency Injection containers, and a growing tool framework to https://github.com/remkop/picocli/tree/master/picocli-codegen[generate] documentation and configuration files from a picocli `CommandSpec` model.
|
||||
|
||||
Finally, picocli is *actively maintained*, whereas Commons CLI seems to be near-dormant with 4 releases in 16 years.
|
||||
|
||||
== An Example Migration: CheckStyle
|
||||
The CheckStyle project was interested in migrating from Commons CLI to get cleaner code, and provide end users with a better experience. The rest of this article uses the CheckStyle command line utility to demonstrate what the code looks like before and after migrating to picocli.
|
||||
|
||||
|
||||
image:https://picocli.info/images/checkstyle-logo-260x50.png[width=200]
|
||||
|
||||
|
||||
//image:https://www.insuremypath.org/sites/default/files/styles/490x350/public/2018-03/pexels-photo-704767.jpeg?itok=lAiP3j-p[width=200]
|
||||
|
||||
|
||||
@@ -57,21 +65,20 @@ A command line application needs to do three things:
|
||||
|
||||
. Define the supported options
|
||||
. Parse the command line arguments
|
||||
. Process the results:
|
||||
- Was version help or usage help requested? If so, print out the requested information and quit.
|
||||
- Validate the user input. If invalid, print an error message and quit.
|
||||
- Finally, run the business logic of the application, based on the values specified by the user.
|
||||
. Process the results
|
||||
|
||||
Let's go over these and compare how this is done in Commond CLI and in picocli, using CheckStyle's command line utility as an example. The resulting source code after the migration can be found here: https://github.com/checkstyle/checkstyle/blob/master/src/main/java/com/puppycrawl/tools/checkstyle/Main.java[`com.puppycrawl.tools.checkstyle.Main`].
|
||||
Let's compare how this is done in Commons CLI and in picocli, using CheckStyle's `com.puppycrawl.tools.checkstyle.Main` command line utility as an example.
|
||||
|
||||
The full source code https://github.com/checkstyle/checkstyle/blob/c998a06ad78213e31b2449e9c9e466c2ff8222f9/src/main/java/com/puppycrawl/tools/checkstyle/Main.java[before] and https://github.com/checkstyle/checkstyle/blob/master/src/main/java/com/puppycrawl/tools/checkstyle/Main.java[after] the migration is on GitHub.
|
||||
|
||||
=== Defining Options and Positional Parameters
|
||||
|
||||
image:https://picocli.info/images/pexels-photo-97077.jpeg[width=200]
|
||||
// image:https://picocli.info/images/pexels-photo-97077.jpeg[width=200]
|
||||
|
||||
// image:https://greenbookblog.org/wp-content/uploads/2018/02/pexels-photo-533189-768x576.jpeg[width=200]
|
||||
|
||||
==== Commons CLI Example
|
||||
Commons CLI has multiple ways to define options: `Options.addOption`, calling `new Options(...)` and invoking setter methods on this object, the deprecated `OptionBuilder` class, and the recommended `Option.Builder` class.
|
||||
==== Defining Options with Commons CLI
|
||||
Commons CLI has multiple ways to define options: `Options.addOption`, constructing a `new Options(...)` and invoking methods on this object, the deprecated `OptionBuilder` class, and the recommended `Option.Builder` class.
|
||||
|
||||
The Checkstyle `Main` class uses the `Options.addOption` method. It starts by defining a number of constants for the option names:
|
||||
|
||||
@@ -104,143 +111,122 @@ private static Options buildOptions() {
|
||||
}
|
||||
----
|
||||
|
||||
==== Picocli Example
|
||||
==== Defining Options with Picocli
|
||||
|
||||
In picocli you can define supported options either programmatically with builders, similar to the Commons CLI approach, or declaratively with annotations.
|
||||
|
||||
The annotations style (example follows below), is likely the preferred option for most applications because it is more declarative and results in less code. Picocli also offers a programmatic API, which may be useful for dynamic applications where not all options are known in advance. If you're interested in the programmatic approach, take a look at the the `CommandSpec`, `OptionSpec` and `PositionalParamSpec` classes. See also https://github.com/remkop/picocli/wiki/Programmatic-API[Programmatic API] for more detail.
|
||||
Picocli’s programmatic API may be useful for dynamic applications where not all options are known in advance. If you're interested in the programmatic approach, take a look at the the `CommandSpec`, `OptionSpec` and `PositionalParamSpec` classes. See also https://github.com/remkop/picocli/wiki/Programmatic-API[Programmatic API] for more detail.
|
||||
|
||||
In this article we will use the picocli annotations. For the CheckStyle example, this would look something like the below:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Option(names = "-c", description = "Sets the check configuration file to use.")
|
||||
File configurationFile;
|
||||
private File configurationFile;
|
||||
|
||||
@Option(names = "-o", description = "Sets the output file. Defaults to stdout")
|
||||
File outputFile;
|
||||
private File outputFile;
|
||||
|
||||
@Option(names = "-v", versionHelp = true, description = "Print product version and exit")
|
||||
boolean versionHelpRequested;
|
||||
private boolean versionHelpRequested;
|
||||
|
||||
@Option(names = {"-t", "--tree"}, description = "Print Abstract Syntax Tree(AST) of the file")
|
||||
boolean printAST;
|
||||
private boolean printAST;
|
||||
----
|
||||
|
||||
==== Declarative
|
||||
==== Comparison
|
||||
|
||||
===== Declarative
|
||||
|
||||
image:https://picocli.info/images/declare.jpg[width=200]
|
||||
|
||||
You immediately note a few things. First, all information is now in one place.
|
||||
With picocli, all information is in one place. With Commons CLI, you build a specification by calling a method with String values. One drawback of an API like this is that good style compels client code to define constants to avoid "magic values", like the Checkstyle `Main` class dutifully does. Annotations only accept String literals, so definition and usage are automatically placed together without the need to declare constants. This results in cleaner and less code.
|
||||
|
||||
With Commons CLI, you build a specification by calling a method with String values. One drawback of a Commons CLI-like API is that good style compels client code to define constants to avoid "magic values", like the Checkstyle `Main` class dutifully does. Annotations only accept String literals, so definition and usage are automatically placed together without the need to declare constants. In this case, that arguably results in cleaner code.
|
||||
|
||||
==== Types
|
||||
===== Strongly Typed
|
||||
|
||||
image::https://picocli.info/images/Type.jpg[width=200]
|
||||
|
||||
Another thing that is interesting is that we went from using `true` or `false` in Commons CLI to denote whether the option takes an argument or not:
|
||||
Commons CLI uses a boolean flag to denote whether the option takes an argument or not. Picocli lets you use types directly. Based on the type, picocli "knows" how many arguments the option needs: `boolean` fields don't have an argument, `Collection`, `Map` and array fields can have zero to any number of arguments, and any other type means the options takes a single argument. This can be customized (see https://picocli.info/#_arity[`arity`]) but most of the time the default is good enough.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
// declaring an option with Commons CLI
|
||||
final Options options = new Options();
|
||||
options.addOption(OPTION_C_NAME, true, "Sets the check configuration file to use.");
|
||||
----
|
||||
You can use `enum` types for options or positional parameters with a limited set of valid values. Not only will picocli validate the input for you, you can also https://picocli.info/#_show_default_values[show all values] in the usage help message with `@Option(description = "Option x. Valid values: ${COMPLETION-CANDIDATES}")`.
|
||||
|
||||
to declaring fields with types in picocli:
|
||||
===== Less Code
|
||||
|
||||
[source,java]
|
||||
----
|
||||
// declaring an option with picocli
|
||||
@Option(names = "-c", description = "Sets the check configuration file to use.")
|
||||
File configurationFile;
|
||||
----
|
||||
image:https://picocli.info/images/convert.png[width=150]
|
||||
|
||||
This has a number of advantages:
|
||||
Picocli https://picocli.info/#_strongly_typed_everything[converts] the option parameter String value to the field type. Not only does it save the application from doing this work, it also provides some minimal validation on the user input. If the conversion fails, a `ParameterException` is thrown with a user-friendly error message.
|
||||
|
||||
* automatic type conversion
|
||||
* no need to specify if an option requires arguments (usually)
|
||||
* assists command line auto-completion
|
||||
|
||||
Picocli automatically converts the command line argument to an object of the specified type. We will talk more about how this works below.
|
||||
|
||||
Based on the type, picocli "knows" how many arguments the option needs: `boolean` fields don't have an argument, `Collection`, `Map` and array fields can have zero to any number of arguments, and any other type means the options takes a single argument. This can be customized (see `arity`) but most of the time the default is good enough.
|
||||
|
||||
Picocli can generate bash completion scripts and JLine Completer candidates, to make your application easier to use for end users. This works best when the options are strongly typed. For example, enums will allow picocli to generate completions with all valid parameters for a given option.
|
||||
|
||||
==== Type Conversion
|
||||
image:https://picocli.info/images/convert.png[width=200]
|
||||
|
||||
Picocli converts the option parameter String value to the field type. This is convenient: not only does it save the application from doing this work, it also provides some minimal validation on the user input. If the conversion fails, a `ParameterException` is thrown with a user-friendly error message. We will take another look at error handling later. Picocli comes with built-in converters for many, many types, but you can easily provide a custom type converter.
|
||||
|
||||
One example: the Checkstyle `Main` class defines a `-x`, `--exclude-regexp` option that allows uses to specify a number of regular expressions for directories to exclude. With Commons CLI, you need to convert the values to `Pattern` objects in the application:
|
||||
For example, the Checkstyle `Main` class defines a `-x`, `--exclude-regexp` option that allows uses to specify a number of regular expressions for directories to exclude. With Commons CLI, you need to convert the String values to `java.util.regex.Pattern` objects in the application:
|
||||
[source,java]
|
||||
----
|
||||
private static List<Pattern> getExclusions(CommandLine commandLine) {
|
||||
final List<Pattern> result = new ArrayList<>();
|
||||
|
||||
// ...
|
||||
|
||||
if (commandLine.hasOption(OPTION_X_NAME)) {
|
||||
for (String value : commandLine.getOptionValues(OPTION_X_NAME)) {
|
||||
result.add(Pattern.compile(value));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
----
|
||||
By contract, in picocli you would simply declare the option on a `List<Pattern>` (or a `Pattern[]` array) field:
|
||||
By contract, in picocli you would simply declare the option on a `List<Pattern>` (or a `Pattern[]` array) field.
|
||||
Since picocli has a built-in converter for `java.util.regex.Pattern`, all that is needed is to declare the option. The conversion code goes away completely. Picocli will instantiate and populate the list if one or more `-x` options are specified on the command line.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
/** Option that allows users to specify a regex of paths to exclude. */
|
||||
@Option(names = {"-x", "--exclude-regexp"},
|
||||
description = "Regular expression of directory to exclude from CheckStyle")
|
||||
private List<Pattern> excludeRegex = new ArrayList<>();
|
||||
private List<Pattern> excludeRegex;
|
||||
----
|
||||
Since picocli has a built-in converter for `java.util.regex.Pattern`, this is all that is needed.
|
||||
Picocli will instantiate and populate the list if one or more `-x` options are specified on the command line.
|
||||
|
||||
==== Option Names
|
||||
===== Option Names
|
||||
|
||||
image:https://picocli.info/images/name.jpg[width=200]
|
||||
|
||||
Finally, Commons CLI supports "short" and "long" options, like `-t` and `--tree`. (Interestingly, the Commons CLI `DefaultParser` will also accept user input where the `-tree` long option is specified with a single hyphen, even though this is not shown in the usage help message.)
|
||||
|
||||
Picocli lets you have any number of options, with any prefix. For example, this is perfectly fine in picocli:
|
||||
Commons CLI supports "short" and "long" options, like `-t` and `--tree`. This is not always what you want. Picocli lets an option have any number of names, with any prefix. For example, this would be perfectly fine in picocli:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Option(names = {"-t", "-tree", "--tree", "/t", "/tree"}) boolean printAST;
|
||||
@Option(names = {"-cp", "-classpath", "--class-path"})
|
||||
----
|
||||
|
||||
==== Positional Parameters
|
||||
===== Positional Parameters
|
||||
image:https://picocli.info/images/location.jpg[width=200]
|
||||
|
||||
A final thing to notice is how positional parameters are handled. In Commons CLI you cannot define positional parameters up front. Instead, its `CommandLine` parse result class has a method `getArgs` that returns the positional parameters as an array of Strings. The Checkstyle `Main` class uses this to create the list of `File` objects to process.
|
||||
In Commons CLI you cannot define positional parameters up front. Instead, its `CommandLine` parse result class has a method `getArgs` that returns the positional parameters as an array of Strings. The Checkstyle `Main` class uses this to create the list of `File` objects to process.
|
||||
|
||||
In picocli, positional parameters are first-class citizens, like named options. Not only can they be strongly typed, parameters at different positions can have different types, and each will have a separate description displayed in the usage help message.
|
||||
In picocli, https://picocli.info/#_positional_parameters[positional parameters] are first-class citizens, like named options. Not only can they be strongly typed, parameters at different positions can have different types, and each will have a separate entry and description listed in the usage help message.
|
||||
|
||||
For example, Checkstyle needs a list of files to process, so we declare a field and annotate it with `@Parameters`. The `arity = "1..*"` attribute means that at least one file must be specified, or picocli will show an error message about the missing argument.
|
||||
|
||||
For the Checkstyle use case, all that is needed is a list or array of `File` objects, so we declare a field and annotate it with `@picocli.CommandLine.Parameters` to capture the files to process:
|
||||
[source,java]
|
||||
----
|
||||
@Parameters(paramLabel = "file", arity = "1..*", description = "The files to process")
|
||||
List<File> filesToProcess;
|
||||
private List<File> filesToProcess;
|
||||
----
|
||||
|
||||
The `arity = "1..*"` here means that at least one file must be specified, or picocli will show an error message to the user that this argument is missing, followed by the usage help message.
|
||||
|
||||
Unline Commons CLI, picocli's usage help message will show an entry for all positional parameters, using the specified parameter label and description. (If the parameter label is omitted picocli will use the field name in `<` and `>` fish brackets.) We will revisit the usage help message later.
|
||||
===== Help Options
|
||||
image:https://picocli.info/images/AskingForHelp.jpg[width=200]
|
||||
|
||||
It is surprisingly difficult in Commons CLI to create an application with a required option that also has a `--help` option. Commons CLI has no special treatment for help options and will complain about the missing required option when the user specifies `<command> --help`. Picocli has built-in support for common (and custom) https://picocli.info/#_help_options[help options].
|
||||
|
||||
|
||||
=== Parsing the Command Line Arguments
|
||||
image:https://picocli.info/images/pipeline.jpg[width=400]
|
||||
|
||||
Commons CLI has a `CommandLineParser` interface with a `parse` method that returns a `CommandLine` representing the parse result. The application then calls `CommandLine.hasOption(String)` to see if a flag was set, or `CommandLine.getOptionValue(String)` to get the option value.
|
||||
|
||||
Picocli populates the annotated fields as it parses the command line arguments. Picocli's `parse...` methods also return a `ParseResult` that can be queried on what options were specified and what value they had, but most applications don't actually need to use the `ParseResult` class since they can simply inspect the value of the annotated fields.
|
||||
Picocli populates the annotated fields as it parses the command line arguments. Picocli's `parse...` methods also return a `ParseResult` that can be queried on what options were specified and what value they had, but most applications don't actually need to use the `ParseResult` class since they can simply inspect the value that were injected into the annotated fields during parsing.
|
||||
|
||||
|
||||
=== Processing the Results
|
||||
|
||||
image:https://picocli.info/images/processing_results.jpg[width=300]
|
||||
|
||||
|
||||
=== Process the Results
|
||||
When the parser is done, the application needs to run its business logic, but first there are some things to check:
|
||||
|
||||
- Was version info or usage help requested? If so, print out the requested information and quit.
|
||||
@@ -276,7 +262,7 @@ System.exit(exitStatus);
|
||||
----
|
||||
|
||||
|
||||
By making your command implement `Runnable` or `Callable`, you can take advantage of picocli's convenience methods that take care of most of the above, so the application can focus on the business logic. At its simplest, this can look something like this:
|
||||
Picocli offers some convenience methods that take care of most of the above. By making your command implement `Runnable` or `Callable`, the application can focus on the business logic. At its simplest, this can look something like this:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -285,7 +271,10 @@ public class Main implements Callable<Integer> {
|
||||
CommandLine.call(new Main(), args);
|
||||
}
|
||||
|
||||
public Integer call() throws CheckstyleException { ...
|
||||
public Integer call() throws CheckstyleException {
|
||||
// business logic here
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Checkstyle needs to control the exit code, and has some strict internal requirements for error handling, so we ended up not using the convenience methods and kept the parse result processing very similar to what it was with Commons CLI.
|
||||
@@ -293,7 +282,9 @@ Checkstyle needs to control the exit code, and has some strict internal requirem
|
||||
|
||||
== Usage Help Message
|
||||
|
||||
A fun bonus is that picocli uses ANSI colors and styles in the usage help message on supported platforms.
|
||||
Picocli uses ANSI colors and styles in the usage help message on supported platforms.
|
||||
This doesn't just look good, it also reduces the cognitive load on the user: the contrast make the important information like commands, options, and parameters stand out from the surrounding text.
|
||||
|
||||
Applications can also use ANSI colors and styles in the description or other sections of the usage help message with a simple markup like `@|bg(red) text with red background|@`. See the https://picocli.info/#_usage_help_with_styles_and_colors[relevant section] of the user manual.
|
||||
|
||||
For CheckStyle, we kept it to the bare minimum, and the resulting output for CheckStyle looks like this:
|
||||
@@ -301,27 +292,20 @@ For CheckStyle, we kept it to the bare minimum, and the resulting output for Che
|
||||
image:https://picocli.info/images/checkstyle-usage.png[]
|
||||
|
||||
|
||||
== Wrapping Up: Quick Tips
|
||||
== Wrapping Up: a Final Tip
|
||||
|
||||
To summarize some of the main points when converting an application from Commons CLI to picocli:
|
||||
|
||||
* Use annotated fields to define options and positional parameters.
|
||||
* You can use https://picocli.info/#option-parameters-methods[annotated methods] for options and positional parameters that require more validation than just type conversion.
|
||||
* Use strong types like `java.nio.file.Path` or `java.util.regex.Pattern` where possible for option and positional parameter fields.
|
||||
* Use `enum` types for options or positional parameters with a limited set of valid values. Not only will picocli validate the input for you, you can also https://picocli.info/#_show_default_values[show all values] in the usage help message with `@Option(description = "Option x. Valid values: ${COMPLETION-CANDIDATES}")`.
|
||||
* Be aware that the Commons CLI default parser will recognize both single hyphen and double hyphen long options, even though the usage help message will only show options with double hyphens. You need to decide whether to continue to support this. Use `@Option(names = "-xxx", hidden = true)` to declare long options with a single hyphen if you want to mimic the exact same behaviour as Commons CLI: hidden options picocli https://picocli.info/#_hidden_options_and_parameters[won't be shown] in the usage help message.
|
||||
Be aware that the Commons CLI default parser will recognize both single hyphen and double hyphen long options, even though the usage help message will only show options with double hyphens. You need to decide whether to continue to support this. Use `@Option(names = "-xxx", hidden = true)` to declare long options with a single hyphen if you want to mimic the exact same behaviour as Commons CLI: hidden options picocli https://picocli.info/#_hidden_options_and_parameters[won't be shown] in the usage help message.
|
||||
|
||||
== Conclusion
|
||||
|
||||
Migrating from Commons CLI to picocli can give end users a more pleasant user experience, and can give developers significant benefits in increased maintainability and potential for future expansion. Migration is a manual process, but is relatively straightforward.
|
||||
Migrating from Commons CLI to picocli can give end users a better user experience, and can give developers significant benefits in increased maintainability and potential for future expansion. Migration is a manual process, but is relatively straightforward.
|
||||
|
||||
Personally, I was glad to see that the CheckStyle maintainers were happy with the result:
|
||||
Update: the CheckStyle project accepted a pull request with the changes in this article. From CheckStyle 8.15 its command line tools will be using picocli. It looks like the CheckStyle maintainers were happy with the result:
|
||||
|
||||
[quote, CheckStyle maintainer Roman Ivanov, (twitter link)]
|
||||
[quote, CheckStyle maintainer Roman Ivanov, https://twitter.com/checkstyle_java/status/1057246772089606144]
|
||||
____
|
||||
Checkstyle migrated from Apache CLI to @picocli (will be released in 8.15), finally documentation of CLI arguments is now well organized in declarative way in code, and checkstyle's CLI is following CLI best practices.
|
||||
____
|
||||
|
||||
|
||||
If you like what you saw, please star https://github.com/remkop/picocli[picocli on GitHub] and tell your friends!
|
||||
|
||||
If you like what you see, please star https://github.com/remkop/picocli[picocli on GitHub] and tell your friends!
|
||||
Reference in New Issue
Block a user