diff --git a/docs/images/WhereIsMyCode.png b/docs/images/WhereIsMyCode.png new file mode 100644 index 00000000..19077dab Binary files /dev/null and b/docs/images/WhereIsMyCode.png differ diff --git a/docs/picocli-v2-groovy-scripts-on-steroids.adoc b/docs/picocli-2.0-groovy-scripts-on-steroids.adoc similarity index 63% rename from docs/picocli-v2-groovy-scripts-on-steroids.adoc rename to docs/picocli-2.0-groovy-scripts-on-steroids.adoc index aee3e0ed..dd5970eb 100644 --- a/docs/picocli-v2-groovy-scripts-on-steroids.adoc +++ b/docs/picocli-2.0-groovy-scripts-on-steroids.adoc @@ -1,13 +1,14 @@ -= Picocli v2: Groovy Scripts on Steroids += Picocli 2.0: Groovy Scripts on Steroids //:author: Remko Popma //:email: rpopma@apache.org //:revnumber: 2.1.0-SNAPSHOT -//:revdate: 2017-11-03 +//:revdate: 2017-11-04 +:prewrap!: :source-highlighter: coderay :icons: font :imagesdir: images -Picocli v2 adds improved support for other JVM languages, especially Groovy. +Picocli 2.0 adds improved support for other JVM languages, especially Groovy. Why use picocli when the Groovy language has built-in CLI support with the http://docs.groovy-lang.org/2.4.7/html/gapi/groovy/util/CliBuilder.html[CliBuilder] class? You may like picocli's usage help, which shows ANSI http://picocli.info/#_ansi_colors_and_styles[colors and styles] @@ -33,13 +34,10 @@ but users may specify a different MessageDigest algorithm. Users can request usa @Grab('info.picocli:picocli:2.0.1') @picocli.groovy.PicocliScript import groovy.transform.Field - -import java.nio.file.Files import java.security.MessageDigest - import static picocli.CommandLine.* -@Parameters(arity = "1", paramLabel = "FILE", description= "The file(s) whose checksum to calculate.") +@Parameters(arity="1", paramLabel="FILE", description="The file(s) whose checksum to calculate.") @Field File[] files @Option(names = ["-a", "--algorithm"], description = [ @@ -47,13 +45,11 @@ import static picocli.CommandLine.* " or any other MessageDigest algorithm."]) @Field String algorithm = "MD5" -@Option(names = ["-h", "--help"], usageHelp = true, description = "Show this help message and exit.") +@Option(names= ["-h", "--help"], usageHelp= true, description= "Show this help message and exit.") @Field boolean helpRequested files.each { - byte[] fileContents = Files.readAllBytes(it.toPath()) - byte[] digest = MessageDigest.getInstance(algorithm).digest(fileContents) - println javax.xml.bind.DatatypeConverter.printHexBinary(digest) + "\t" + it + println MessageDigest.getInstance(algorithm).digest(it.bytes).encodeHex().toString() + "\t" + it } ---- When run in the `$picocli-home/examples/src/main/groovy/picocli/examples` directory, @@ -62,8 +58,9 @@ this example script gives the following results: [source,bash] ---- $ groovy checksum.groovy *.* -4DCE157CA654199494642D49FB67BF11 checksum.groovy -8C7D823F1C9345213977FF6DA964AA02 checksum-with-banner.groovy +4995d24bbb3adf67e2120c36dd3027b7 checksum.groovy +a03c852de017f9303fcc373c7adafac6 checksum-with-banner.groovy +1ee567193bf41cc835ce76b6ca29ed30 checksum-without-base.groovy ---- Invoking the script with the `-h` or `--help` option shows the usage help message @@ -71,8 +68,51 @@ with ANSI colors and styles below: image:GroovyChecksum.png[Usage help with ANSI colors and styles] -You may have noticed that the script does not contain any logic for parsing the command -line arguments or for handling requests for usage help. Let's take a look at how this works. +== Where's the Code? + +You may have noticed that the above script does not contain any logic for parsing the command +line arguments or for handling requests for usage help. + +[.text-center] +image:WhereIsMyCode.png[Dude, where's my code?,width='35%'] + +Without the `@picocli.groovy.PicocliScript` annotation, the script code would look something like this: + +[source,groovy] +---- +class Checksum { + @Parameters(arity = "1", paramLabel = "FILE", description = "...") + File[] files + + @Option(names = ["-a", "--algorithm"], description = ["..."]) + String algorithm = "MD5" + + @Option(names = ["-h", "--help"], usageHelp = true, description = "...") + boolean helpRequested +} +Checksum checksum = new Checksum() +CommandLine commandLine = new CommandLine(checksum) +try { + commandLine.parse(args) + if (commandLine.usageHelpRequested) { + commandLine.usage(System.out) + } else { + checksum.files.each { + byte[] digest = MessageDigest.getInstance(checksum.algorithm).digest(it.bytes) + println digest.encodeHex().toString() + "\t" + it + } + } +} catch (ParameterException ex) { + println ex.message + commandLine.usage(System.out) +} +---- + +The above example has explicit code to parse the command line, deal with invalid user input, +and check for usage help requests. +The first version of the script did not have any of this boilerplate code. + +Let's take a look at how this works. == Basescript @@ -81,7 +121,7 @@ Scripts annotated with `@picocli.groovy.PicocliScript` are automatically transfo This turns a Groovy script into a picocli-based command line application. [.text-center] -image:AllYourBase.png[ALL YOUR BASE ARE BELONG TO US] +image:AllYourBase.png[Alt="ALL YOUR BASE ARE BELONG TO US",width='35%'] When the script is run, Groovy calls the script's `run` method. The `PicocliBaseScript::run` method takes care of parsing the command line and populating the script @@ -95,10 +135,11 @@ fields with the results. The run method does the following: * Otherwise, the script body is executed. -See the http://picocli.info/apidocs/picocli/groovy/PicocliBaseScript.html#run--[PicocliBaseScript javadoc] for more details. +This behavior can be customized, see the http://picocli.info/apidocs/picocli/groovy/PicocliBaseScript.html#run--[PicocliBaseScript javadoc] for more details. In addition to changing the script base class, the `@PicocliScript` annotation also allows Groovy -scripts to use the `@Command` annotation. The picocli parser will look for this annotation on the +scripts to use the `@Command` annotation directly, without introducing a helper class. +The picocli parser will look for this annotation on the class containing the `@Option` and `@Parameters`-annotated fields. The same custom http://picocli.info/apidocs/picocli/groovy/PicocliScriptASTTransformation.html[AST transformation] that changes the script's base class also moves any `@Command` annotation in the script to this @@ -106,9 +147,10 @@ transformed class so the picocli parser can pick it up. == Usage Help With Colors -The `@Command` annotation lets you customize parts of the usage help message like command name, description, headers, footers etc. +The `@Command` annotation lets you customize parts of the http://picocli.info/#_usage_help[usage help] message like command name, description, headers, footers etc. -Let's add some bells and whistles to the above script. +Let's add some bells and whistles to the example script. +(Credit to http://patorjk.com/software/taag/ for the ASCII Art Generator.) [source,groovy] ---- @@ -128,13 +170,10 @@ Let's add some bells and whistles to the above script. ) @picocli.groovy.PicocliScript import groovy.transform.Field - -import java.nio.file.Files import java.security.MessageDigest - import static picocli.CommandLine.* -@Parameters(arity = "1", paramLabel = "FILE", description= "The file(s) whose checksum to calculate.") +@Parameters(arity="1", paramLabel="FILE", description="The file(s) whose checksum to calculate.") @Field private File[] files @Option(names = ["-a", "--algorithm"], description = [ @@ -142,29 +181,29 @@ import static picocli.CommandLine.* " any other MessageDigest algorithm. See [1] for more details."]) @Field private String algorithm = "MD5" -@Option(names = ["-h", "--help"], usageHelp = true, description = "Show this help message and exit.") +@Option(names= ["-h", "--help"], usageHelp=true, description="Show this help message and exit.") @Field private boolean helpRequested -@Option(names = ["-V", "--version"], versionHelp = true, description = "Show version info and exit.") +@Option(names= ["-V", "--version"], versionHelp=true, description="Show version info and exit.") @Field private boolean versionInfoRequested files.each { - byte[] fileContents = Files.readAllBytes(it.toPath()) - byte[] digest = MessageDigest.getInstance(algorithm).digest(fileContents) - println javax.xml.bind.DatatypeConverter.printHexBinary(digest) + "\t" + it + println MessageDigest.getInstance(algorithm).digest(it.bytes).encodeHex().toString() + "\t" + it } ---- The new version of the script adds a header and footer, and the ability to print version information. All text displayed in the usage help message and version information may contain format specifiers -like the '`%n`' line separator. +like the `%n` line separator. The usage help message can also display ANSI colors and styles. Picocli supports a simple markup syntax where `@|` starts an ANSI styled section and `|@` ends it. Immediately following the `@|` is a comma-separated list of colors and styles, -like `@|STYLE1[,STYLE2]… text|@`. +like `@|STYLE1[,STYLE2]... text|@`. See the picocli http://picocli.info/#_usage_help_with_styles_and_colors[user manual] for details on what colors and styles are available. +The usage help message for the new script looks like this: + image:GroovyChecksumWithBanner.png[Customized header and footer with styles and colors] The `@Command` annotation also has a `version = "checksum v1.2.3"` attribute. @@ -181,8 +220,8 @@ For more details, see the http://picocli.info/#_version_help[Version Help] secti == Conclusion -The script above is surprisingly small given all the things it can do. -Most of the code is actually description text for the usage help message. +The `@PicocliScript` annotation allows Groovy scripts to omit boilerplate code and while adding powerful common command line application functionality. +In the final version of our example script, most of the code is actually description text for the usage help message. There is a lot more to picocli, give it a try! diff --git a/docs/picocli-2.0-groovy-scripts-on-steroids.html b/docs/picocli-2.0-groovy-scripts-on-steroids.html new file mode 100644 index 00000000..3244a2ca --- /dev/null +++ b/docs/picocli-2.0-groovy-scripts-on-steroids.html @@ -0,0 +1,796 @@ + + + + + + + +Picocli 2.0: Groovy Scripts on Steroids + + + + + + + +
+
+
+
+

Picocli 2.0 adds improved support for other JVM languages, especially Groovy. +Why use picocli when the Groovy language has built-in CLI support with the CliBuilder class?

+
+
+

You may like picocli’s usage help, which shows ANSI colors and styles +by default. Another feature you may fancy is the command line +TAB autocompletion. Finally, there is a slew of smaller features, +like the fact that your script needs zero lines of command line parsing code, +picocli’s subcommand support, +type conversion for both options and positional parameters, +and parser tracing, to name a few.

+
+
+

cli

+
+
+
+
+

Example

+
+
+

Let’s take a look at an example. The checksum.groovy script below takes one or more file parameters, +and for each file prints out a checksum and the file name. The "checksum" algorithm is MD5 by default, +but users may specify a different MessageDigest algorithm. Users can request usage help with the +-h or --help option.

+
+
+
+
@Grab('info.picocli:picocli:2.0.1')
+@picocli.groovy.PicocliScript
+import groovy.transform.Field
+import java.security.MessageDigest
+import static picocli.CommandLine.*
+
+@Parameters(arity="1", paramLabel="FILE", description="The file(s) whose checksum to calculate.")
+@Field File[] files
+
+@Option(names = ["-a", "--algorithm"], description = [
+        "MD2, MD5, SHA-1, SHA-256, SHA-384, SHA-512,",
+        "  or any other MessageDigest algorithm."])
+@Field String algorithm = "MD5"
+
+@Option(names= ["-h", "--help"], usageHelp= true, description= "Show this help message and exit.")
+@Field boolean helpRequested
+
+files.each {
+  println MessageDigest.getInstance(algorithm).digest(it.bytes).encodeHex().toString() + "\t" + it
+}
+
+
+
+

When run in the $picocli-home/examples/src/main/groovy/picocli/examples directory, +this example script gives the following results:

+
+
+
+
$ groovy checksum.groovy *.*
+4995d24bbb3adf67e2120c36dd3027b7        checksum.groovy
+a03c852de017f9303fcc373c7adafac6        checksum-with-banner.groovy
+1ee567193bf41cc835ce76b6ca29ed30        checksum-without-base.groovy
+
+
+
+

Invoking the script with the -h or --help option shows the usage help message +with ANSI colors and styles below:

+
+
+

Usage help with ANSI colors and styles

+
+
+
+
+

Where’s the Code?

+
+
+

You may have noticed that the above script does not contain any logic for parsing the command +line arguments or for handling requests for usage help.

+
+
+

Dude

+
+
+

Without the @picocli.groovy.PicocliScript annotation, the script code would look something like this:

+
+
+
+
class Checksum {
+    @Parameters(arity = "1", paramLabel = "FILE", description = "...")
+    File[] files
+
+    @Option(names = ["-a", "--algorithm"], description = ["..."])
+    String algorithm = "MD5"
+
+    @Option(names = ["-h", "--help"], usageHelp = true, description = "...")
+    boolean helpRequested
+}
+Checksum checksum = new Checksum()
+CommandLine commandLine = new CommandLine(checksum)
+try {
+    commandLine.parse(args)
+    if (commandLine.usageHelpRequested) {
+        commandLine.usage(System.out)
+    } else {
+        checksum.files.each {
+            byte[] digest = MessageDigest.getInstance(checksum.algorithm).digest(it.bytes)
+            println digest.encodeHex().toString() + "\t" + it
+        }
+    }
+} catch (ParameterException ex) {
+    println ex.message
+    commandLine.usage(System.out)
+}
+
+
+
+

The above example has explicit code to parse the command line, deal with invalid user input, +and check for usage help requests. +The first version of the script did not have any of this boilerplate code.

+
+
+

Let’s take a look at how this works.

+
+
+
+
+

Basescript

+
+
+

Scripts annotated with @picocli.groovy.PicocliScript are automatically transformed to use +picocli.groovy.PicocliBaseScript as their base class. +This turns a Groovy script into a picocli-based command line application.

+
+
+

AllYourBase

+
+
+

When the script is run, Groovy calls the script’s run method. +The PicocliBaseScript::run method takes care of parsing the command line and populating the script +fields with the results. The run method does the following:

+
+
+
    +
  • +

    First, @Field variables annotated with @Option or @Parameters are initialized from the command line arguments.

    +
  • +
  • +

    If the user input was invalid, an error message is printed followed by the usage help message.

    +
  • +
  • +

    If the user requested usage help or version information, this is printed to the console and the script exits.

    +
  • +
  • +

    Otherwise, the script body is executed.

    +
  • +
+
+
+

This behavior can be customized, see the PicocliBaseScript javadoc for more details.

+
+
+

In addition to changing the script base class, the @PicocliScript annotation also allows Groovy +scripts to use the @Command annotation directly, without introducing a helper class. +The picocli parser will look for this annotation on the +class containing the @Option and @Parameters-annotated fields. The same custom +AST transformation +that changes the script’s base class also moves any @Command annotation in the script to this +transformed class so the picocli parser can pick it up.

+
+
+
+
+

Usage Help With Colors

+
+
+

The @Command annotation lets you customize parts of the usage help message like command name, description, headers, footers etc.

+
+
+

Let’s add some bells and whistles to the example script. +(Credit to http://patorjk.com/software/taag/ for the ASCII Art Generator.)

+
+
+
+
@Grab('info.picocli:picocli:2.0.1')
+@Command(header = [
+    "@|bold,green    ___                            ___ _           _                  |@",
+    "@|bold,green   / __|_ _ ___  _____ ___  _     / __| |_  ___ __| |__ ____  _ _ __  |@",
+    "@|bold,green  | (_ | '_/ _ \\/ _ \\ V / || |   | (__| ' \\/ -_) _| / /(_-< || | '  \\ |@",
+    "@|bold,green   \\___|_| \\___/\\___/\\_/ \\_, |    \\___|_||_\\___\\__|_\\_\\/__/\\_,_|_|_|_||@",
+    "@|bold,green                         |__/                                         |@"
+        ],
+        description = "Print a checksum of each specified FILE.",
+        version = 'checksum v1.2.3', showDefaultValues = true,
+        footerHeading = "%nFor more details, see:%n",
+        footer = ["[1] https://docs.oracle.com/javase/9/docs/specs/security/standard-names.html",
+                "ASCII Art thanks to http://patorjk.com/software/taag/"]
+)
+@picocli.groovy.PicocliScript
+import groovy.transform.Field
+import java.security.MessageDigest
+import static picocli.CommandLine.*
+
+@Parameters(arity="1", paramLabel="FILE", description="The file(s) whose checksum to calculate.")
+@Field private File[] files
+
+@Option(names = ["-a", "--algorithm"], description = [
+        "MD2, MD5, SHA-1, SHA-256, SHA-384, SHA-512, or",
+        "  any other MessageDigest algorithm. See [1] for more details."])
+@Field private String algorithm = "MD5"
+
+@Option(names= ["-h", "--help"], usageHelp=true, description="Show this help message and exit.")
+@Field private boolean helpRequested
+
+@Option(names= ["-V", "--version"], versionHelp=true, description="Show version info and exit.")
+@Field private boolean versionInfoRequested
+
+files.each {
+  println MessageDigest.getInstance(algorithm).digest(it.bytes).encodeHex().toString() + "\t" + it
+}
+
+
+
+

The new version of the script adds a header and footer, and the ability to print version information. +All text displayed in the usage help message and version information may contain format specifiers +like the %n line separator.

+
+
+

The usage help message can also display ANSI colors and styles. +Picocli supports a simple markup syntax where @| starts an ANSI styled section and |@ ends it. +Immediately following the @| is a comma-separated list of colors and styles, +like @|STYLE1[,STYLE2]…​ text|@. +See the picocli user manual for details on what colors and styles are available.

+
+
+

The usage help message for the new script looks like this:

+
+
+

Customized header and footer with styles and colors

+
+
+

The @Command annotation also has a version = "checksum v1.2.3" attribute. +This version string is printed when the user specifies --version on the command line because +we declared an @Option with that name with attribute versionHelp = true.

+
+
+
+
$ groovy checksum-with-banner.groovy --version
+checksum v1.2.3
+
+
+
+

For more details, see the Version Help section of the user manual.

+
+
+
+
+

Conclusion

+
+
+

The @PicocliScript annotation allows Groovy scripts to omit boilerplate code and while adding powerful common command line application functionality. +In the final version of our example script, most of the code is actually description text for the usage help message.

+
+
+

There is a lot more to picocli, give it a try!

+
+
+

Please star the project on GitHub if you like it and tell your friends!

+
+
+
+
+ + + \ No newline at end of file