Skip to content
Commits on Source (2)
  • Tony Mancill's avatar
    Import Upstream version 3.9.1 · 4bfd326e
    Tony Mancill authored
    4bfd326e
  • Miroslav Kravec's avatar
    Import Debian changes 3.9.1-1 · dde711c0
    Miroslav Kravec authored and Tony Mancill's avatar Tony Mancill committed
    picocli (3.9.1-1) unstable; urgency=medium
    
      * New upstream release
      * debian/control: add libjline2-java dependency
      * debian/libpicocli-java.poms: add new picocli modules / JARs
      * debian/maven.rules: keep jline version (debian version link doesn't exist)
      * debian/patches: make new release buildable under debian
    dde711c0
......@@ -20,6 +20,7 @@ any option prefix style, POSIX-style [grouped short options](https://picocli.inf
custom [type converters](https://picocli.info/#_custom_type_converters),
[password options](http://picocli.info/#_interactive_password_options) and more.
Parser [tracing](https://picocli.info/#_tracing) facilitates troubleshooting.
Command-line [argument files](https://picocli.info/#AtFiles) (@-files) allow applications to handle very long command lines.
Distinguishes between [named options](https://picocli.info/#_options) and
[positional parameters](https://picocli.info/#_positional_parameters) and allows _both_ to be
......@@ -36,7 +37,7 @@ Works with Java 5 or higher (but is designed to facilitate the use of Java 8 lam
Picocli-based command line applications can have [TAB autocompletion](https://picocli.info/autocomplete.html),
interactively showing users what options and subcommands are available.
When an option has [`completionCandidates`](https://picocli.info/#__code_completion_candidates_code_variable) or has an `enum` type, autocompletion can also suggest option values.
Picocli can generate completion scripts for bash and zsh, and offers an API to easily [create a JLine `Completer`](https://github.com/remkop/picocli/releases#3.2.0-jline) for your application.
Picocli can generate completion scripts for bash and zsh, and offers [`picocli-shell-jline2`](picocli-shell-jline2/README.md) and [`picocli-shell-jline3`](picocli-shell-jline3/README.md) modules with JLine `Completer` implementations for building interactive shell applications.
Picocli-based applications can easily [integrate](https://picocli.info/#_dependency_injection) with Dependency Injection containers.
......@@ -44,9 +45,9 @@ Picocli-based applications can easily [integrate](https://picocli.info/#_depende
![Picocli Demo help message with ANSI colors](docs/images/picocli.Demo.png?raw=true)
### Releases
* [Releases](https://github.com/remkop/picocli/releases) - latest: 3.6.1
* [Picocli 3.0.0 Release Notes](https://github.com/remkop/picocli/releases/tag/v3.0.0) - note there are some [potential breaking changes](https://github.com/remkop/picocli/releases/tag/v3.0.0#3.0.0-breaking-changes) from prior versions
* [Picocli 2.0 Release Notes](https://github.com/remkop/picocli/releases/tag/v2.0.0) - note there are some [potential breaking changes](https://github.com/remkop/picocli/releases/tag/v2.0.0#2.0-breaking-changes) from prior versions
* [Releases](https://github.com/remkop/picocli/releases) - Latest: 3.9.1 [Release Notes](https://github.com/remkop/picocli/releases/tag/v3.9.1)
* Older: Picocli 3.0.0 [Release Notes](https://github.com/remkop/picocli/releases/tag/v3.0.0)
* Older: Picocli 2.0 [Release Notes](https://github.com/remkop/picocli/releases/tag/v2.0.0)
### Documentation
* [3.x User manual: https://picocli.info](https://picocli.info)
......@@ -56,8 +57,10 @@ Picocli-based applications can easily [integrate](https://picocli.info/#_depende
* [API Javadoc](https://picocli.info/apidocs/)
* [3.0 Programmatic API](https://picocli.info/picocli-3.0-programmatic-api.html)
* [FAQ](https://github.com/remkop/picocli/wiki/FAQ)
* [GraalVM AOT Compilation to Native Image](https://picocli.info/picocli-on-graalvm.html) <img src="https://www.graalvm.org/resources/img/logo-colored.svg" >
### Articles
* [Migrating from Commons CLI to picocli](https://picocli.info/migrating-from-commons-cli.html). You won't regret it! :-) (also on: [DZone](https://dzone.com/articles/migrating-from-commons-cli-to-picocli) and [Java Code Geeks](https://www.javacodegeeks.com/2018/11/migrating-commons-cli-picocli.html)).
* [Groovy 2.5 CliBuilder Renewal](https://picocli.info/groovy-2.5-clibuilder-renewal.html) (also on [blogs.apache.org](https://blogs.apache.org/logging/entry/groovy-2-5-clibuilder-renewal)). In two parts: [Part 1](https://picocli.info/groovy-2.5-clibuilder-renewal-part1.html) (also on: [DZone](https://dzone.com/articles/groovy-25-clibuilder-renewal), [Java Code Geeks](https://www.javacodegeeks.com/2018/06/groovy-clibuilder-renewal-part-1.html)), [Part 2](https://picocli.info/groovy-2.5-clibuilder-renewal-part2.html) (also on: [DZone](https://dzone.com/articles/groovy-25-clibuilder-renewal-part-2), [Java Code Geeks](https://www.javacodegeeks.com/2018/06/groovy-clibuilder-renewal-part-2.html)).
* Micronaut user manual for running microservices [standalone with picocli](https://docs.micronaut.io/snapshot/guide/index.html#commandLineApps).
* [Java Command-Line Interfaces (Part 30): Observations](http://marxsoftware.blogspot.jp/2017/11/java-cmd-line-observations.html) by Dustin Marx about picocli 2.0.1 (also on: [DZone](https://dzone.com/articles/java-command-line-interfaces-part-30-finale-observations), [Java Code Geeks](https://www.javacodegeeks.com/2017/11/java-command-line-interfaces-part-30-observations.html))
......@@ -66,6 +69,10 @@ Picocli-based applications can easily [integrate](https://picocli.info/#_depende
* [Picocli 2.0: Do More With Less](https://picocli.info/picocli-2.0-do-more-with-less.html) (also on: [DZone](https://dzone.com/articles/whats-new-in-picocli-20), [Java Code Geeks](https://www.javacodegeeks.com/2018/01/picocli-2-0-less.html))
* [Announcing picocli 1.0](https://picocli.info/announcing-picocli-1.0.html) (also on: [DZone](https://dzone.com/articles/announcing-picocli-10))
### 中文
* [Picocli 2.0: Steroids上的Groovy脚本](https://picocli.info/zh/picocli-2.0-groovy-scripts-on-steroids.html)
* [Picocli 2.0: 以少求多](https://picocli.info/zh/picocli-2.0-do-more-with-less.html)
### Related
* Check out Thibaud Lepretre's [picocli Spring boot starter](https://github.com/kakawait/picocli-spring-boot-starter)!
......@@ -76,15 +83,17 @@ Picocli-based applications can easily [integrate](https://picocli.info/#_depende
## Adoption
<img src="https://picocli.info/images/groovy-logo.png" height="50"> <img src="http://micronaut.io/images/micronaut_mini_copy_tm.svg" height="50"> <img src="https://picocli.info/images/junit5logo-172x50.png" height="50"> <img src="https://picocli.info/images/debian-logo-50.png" height="50"> <img src="https://picocli.info/images/karate-logo.svg" height="50" width="50"/>
<img src="https://picocli.info/images/groovy-logo.png" height="50"> <img src="https://picocli.info/images/1x1.png" width="10"> <img src="http://micronaut.io/images/micronaut_mini_copy_tm.svg" height="50"> <img src="https://picocli.info/images/1x1.png" width="10"><img src="https://picocli.info/images/junit5logo-172x50.png" height="50"> <img src="https://picocli.info/images/1x1.png" width="10"> <img src="https://picocli.info/images/debian-logo-192x50.png" height="50"> <img src="https://picocli.info/images/1x1.png" width="10"> <img src="https://picocli.info/images/karate-logo.png" height="50" width="50"/> <img src="https://picocli.info/images/checkstyle-logo-260x50.png" height="50"><img src="https://picocli.info/images/1x1.png" width="10"> <img src="https://picocli.info/images/ballerina-logo.png" height="40">
* Picocli is now part of Groovy. From Groovy 2.5, all Groovy command line tools are picocli-based, and picocli is the underlying parser for Groovy's [CliBuilder DSL](http://groovy-lang.org/dsls.html#_clibuilder).
* Picocli is now part of Micronaut. The Micronaut CLI has been rewritten with picocli, and Micronaut has dedicated support for running microservices [standalone with picocli](https://docs.micronaut.io/snapshot/guide/index.html#commandLineApps).
* Picocli is now part of JUnit 5. JUnit 5.3 migrated its `ConsoleLauncher` from jopt-simple to picocli to support @-files (argument files); this helps users who need to specify many tests on the command line and run into system limitations.
* Debian now offers a [libpicocli-java package](https://packages.debian.org/unstable/java/libpicocli-java). Thanks to [Miroslav Kravec](https://salsa.debian.org/kravemir-guest).
* Debian now offers a [libpicocli-java package](https://tracker.debian.org/pkg/picocli). Thanks to [Miroslav Kravec](https://udd.debian.org/dmd/?kravec.miroslav%40gmail.com).
* Picocli is used in the Intuit [Karate](https://github.com/intuit/karate) standalone JAR / executable.
* Picocli is part of [Ballerina](https://ballerina.io/). Ballerina uses picocli for all its command line utilities.
* Picocli is used in the [CheckStyle](https://checkstyle.org/cmdline.html) standalone JAR / executable from Checkstyle 8.15.
<img src="https://picocli.info/images/downloads-201808.png">
<img src="https://picocli.info/images/downloads-201812.png">
Glad to see more people are using picocli. We must be doing something right. :-)
......@@ -121,12 +130,12 @@ public class Example implements Runnable {
}
public static void main(String[] args) {
CommandLine.run(new Example(), System.out, args);
CommandLine.run(new Example(), args);
}
}
```
If your command implements `Runnable`, all the code that is necessary to parse the command line and execute the command is a call to `CommandLine.run` with the command line parameters and the `Runnable` command. When the program is run on the command line, the command line arguments are converted to Java objects and assigned to the annotated fields. After the arguments are successfully parsed, picocli calls the command's `run` method.
If your command implements `Runnable`, all that is necessary to parse the command line and execute the command is a call to `CommandLine.run` with the command line parameters and the `Runnable` command. When the program is run on the command line, the command line arguments are converted to Java objects and assigned to the annotated fields. After the arguments are successfully parsed, picocli calls the command's `run` method.
```bash
$ java Example -v inputFile1 inputFile2
......@@ -168,35 +177,35 @@ See the [source code](https://github.com/remkop/picocli/blob/master/src/main/jav
### Gradle
```
compile 'info.picocli:picocli:3.6.1'
compile 'info.picocli:picocli:3.9.1'
```
### Maven
```
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
<version>3.6.1</version>
<version>3.9.1</version>
</dependency>
```
### Scala SBT
```
libraryDependencies += "info.picocli" % "picocli" % "3.6.1"
libraryDependencies += "info.picocli" % "picocli" % "3.9.1"
```
### Ivy
```
<dependency org="info.picocli" name="picocli" rev="3.6.1" />
<dependency org="info.picocli" name="picocli" rev="3.9.1" />
```
### Grape
```groovy
@Grapes(
@Grab(group='info.picocli', module='picocli', version='3.6.1')
@Grab(group='info.picocli', module='picocli', version='3.9.1')
)
```
### Leiningen
```
[info.picocli/picocli "3.6.1"]
[info.picocli/picocli "3.9.1"]
```
### Buildr
```
'info.picocli:picocli:jar:3.6.1'
'info.picocli:picocli:jar:3.9.1'
```
This diff is collapsed.
group 'info.picocli'
description 'Java command line parser with both an annotations API and a programmatic API. Usage help with ANSI styles and colors. Autocomplete. Nested subcommands. Easily included as source to avoid adding a dependency.'
version '3.6.1'
// for bumpVersion task
def oldVersion = '3\\.6\\.1-SNAPSHOT'
def oldRevdate = '2018\\-09\\-12'
def revDate = '2018-09-28'
// for bumpReadmeVersion task
def previousReleaseVersion = '3.6.0'
version "$projectVersion"
buildscript {
repositories {
......@@ -17,12 +9,20 @@ buildscript {
dependencies {
classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.3'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.+'
}
}
apply plugin: 'org.asciidoctor.convert'
apply plugin: 'jacoco'
apply plugin: 'distribution'
apply plugin: 'maven-publish'
apply plugin: 'com.jfrog.bintray'
allprojects {
apply plugin: 'groovy'
apply plugin: 'java'
apply plugin: 'java-library' // to avoid https://github.com/gradle/gradle/issues/1118
sourceCompatibility = 1.5
targetCompatibility = 1.5
......@@ -30,17 +30,23 @@ allprojects {
jcenter()
}
configurations.all {
resolutionStrategy {
// avoid "Could not resolve junit:junit-dep:[4.9,)" caused by stefanbirkner:system-rules when building offline
force "junit:junit-dep:$junitDepVersion"
}
}
configurations {
ivy
}
dependencies {
compileOnly 'org.codehaus.groovy:groovy-all:2.4.10'
ivy 'org.apache.ivy:ivy:2.4.0' // for Gradle
testCompile 'junit:junit:4.12',
'org.hamcrest:hamcrest-core:1.3',
'org.fusesource.jansi:jansi:1.15',
'org.codehaus.groovy:groovy-all:2.4.10',
'com.github.stefanbirkner:system-rules:1.17.1'
compileOnly "org.codehaus.groovy:groovy-all:$groovyVersion"
ivy "org.apache.ivy:ivy:$ivyVersion" // for Gradle
testCompile "junit:junit:$junitVersion",
"org.hamcrest:hamcrest-core:$hamcrestCoreVersion",
"org.fusesource.jansi:jansi:$jansiVersion",
"org.codehaus.groovy:groovy-all:$groovyVersion",
"com.github.stefanbirkner:system-rules:$systemRulesVersion"
}
tasks.withType(GroovyCompile) {
......@@ -53,58 +59,12 @@ allprojects {
tasks.withType(JavaCompile) {
options.compilerArgs << '-Xlint:unchecked' << '-Xlint:deprecation'
}
}
project('picocli-examples') {
dependencies {
compile rootProject
compile 'org.apache.ivy:ivy:2.4.0', // for Intelli/J
'org.codehaus.groovy:groovy-all:2.4.10'
}
def generatedResources = "$buildDir/generated-resources/main"
sourceSets {
main {
//register an output folder on the main SourceSet:
output.dir(generatedResources, builtBy: 'generateVersionTxt')
//it is now a part of the 'main' classpath and will be a part of the jar
}
}
//a task that generates the resources for the example VersionProviderDemo1:
task generateVersionTxt {
description 'Creates a version.txt file with build info that is added to the root of the picocli-examples jar'
doLast {
new File(generatedResources).mkdirs()
def generated = new File(generatedResources, "version.txt")
generated.text = """
Version: $rootProject.version
Buildtime: ${new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())}
Application-name: $rootProject.name $project.name
"""
}
}
}
apply plugin: 'org.asciidoctor.convert'
apply plugin: 'jacoco'
apply plugin: 'distribution'
apply plugin: 'maven-publish'
jar {
manifest {
attributes 'Specification-Title' : 'picocli',
'Specification-Vendor' : 'Remko Popma',
'Specification-Version' : version,
'Implementation-Title' : 'picocli',
'Implementation-Vendor' : 'Remko Popma',
'Implementation-Version': version,
'Main-Class' : 'picocli.AutoComplete',
'Automatic-Module-Name' : 'info.picocli'
}
}
javadoc {
options.overview = "src/main/java/overview.html"
destinationDir = file("build/docs/apidocs")
}
javadoc.options.addStringOption('Xdoclint:none', '-quiet')
// work around https://github.com/gradle/gradle/issues/4046
javadoc.dependsOn('copyJavadocDocFiles')
task copyJavadocDocFiles(type: Copy) {
......@@ -113,11 +73,9 @@ task copyJavadocDocFiles(type: Copy) {
include '**/doc-files/*.*'
}
jacocoTestReport {
reports {
xml.enabled true
html.enabled false
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
task testJar(type: Jar, dependsOn: compileTestJava) {
from sourceSets.test.output
......@@ -131,64 +89,143 @@ task testSourcesJar(type: Jar) {
from sourceSets.test.java.srcDirs
classifier = 'test-sources'
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
artifacts {
archives javadocJar
archives sourcesJar
archives testSourcesJar
archives testJar
archives jar
}
distributions {
main {
baseName = "$archivesBaseName-all"
contents {
from jar
from sourcesJar
from testJar
from testSourcesJar
from javadocJar
from ('LICENSE')
from ("$rootDir/RELEASE-NOTES.md")
}
}
}
ext {
bintrayUsername = System.getenv('BINTRAY_USER')
bintrayApiKey = System.getenv('BINTRAY_KEY')
bintrayDryRun = false
mavenOssUser = System.getenv('MAVEN_OSS_USER')
mavenOssPassword = System.getenv('MAVEN_OSS_PASSWORD')
mavenOssSync = false
// pom configuration for MavenPublication
pomConfig = {
licenses {
license {
name "The Apache Software License, version 2.0"
url "http://www.apache.org/licenses/LICENSE-2.0.txt"
distribution "repo"
}
}
developers {
developer {
id "rpopma"
name "Remko Popma"
email "rpopma@apache.org"
}
}
scm {
url "https://github.com/remkop/picocli/tree/master"
connection 'scm:git:https://github.com/remkop/picocli.git'
developerConnection 'scm:git:ssh://github.com:remkop/picocli.git'
}
}
}
}
jar {
manifest {
attributes 'Specification-Title' : 'picocli',
'Specification-Vendor' : 'Remko Popma',
'Specification-Version' : version,
'Implementation-Title' : 'picocli',
'Implementation-Vendor' : 'Remko Popma',
'Implementation-Version': version,
'Main-Class' : 'picocli.AutoComplete',
'Automatic-Module-Name' : 'info.picocli'
}
}
javadoc.options.overview = "src/main/java/overview.html"
javadoc.dependsOn('asciidoctor')
asciidoctor {
sourceDir = file('docs')
outputDir = file('build/docs')
logDocuments = true
}
artifacts {
archives javadocJar
archives sourcesJar
archives testSourcesJar
archives testJar
archives jar
jacocoTestReport {
reports {
xml.enabled true
html.enabled false
}
}
jacocoTestCoverageVerification {
violationRules {
rule {
limit {
minimum = 0.98
}
}
}
}
// run coverage verification during the build (and fail when appropriate)
check.dependsOn jacocoTestCoverageVerification
task bumpReadmeVersion {
doLast {
// README.md
ant.replaceregexp(match: "$previousReleaseVersion", replace: "$version", flags: 'g', byline: true, encoding: 'UTF8') {
ant.replaceregexp(match: "$projectPreviousReleaseVersion", replace: "$version", flags: 'g', byline: true, encoding: 'UTF8') {
fileset(dir: '.', includes: 'README.md')
fileset(dir: './picocli-codegen/', includes: 'README.md')
fileset(dir: './picocli-shell-jline2/', includes: 'README.md')
fileset(dir: './picocli-shell-jline3/', includes: 'README.md')
}
}
}
task bumpVersion {
doLast {
ant.replaceregexp(match: "\"$oldVersion\"", replace: "\"$version\"", flags: 'g', byline: true, encoding: 'UTF8') {
ant.replaceregexp(match: "\"$projectPreviousVersionRegex\"", replace: "\"$version\"", flags: 'g', byline: true, encoding: 'UTF8') {
fileset(dir: 'src/main/java/picocli', includes: 'CommandLine.java')
fileset(dir: 'src/test/java/picocli', includes: 'CommandLineTest.java')
}
ant.replaceregexp(match: "version $oldVersion", replace: "version $version", flags: 'g', byline: true, encoding: 'UTF8') {
ant.replaceregexp(match: "version $projectPreviousVersionRegex", replace: "version $version", flags: 'g', byline: true, encoding: 'UTF8') {
fileset(dir: 'src/test/java/picocli', includes: 'AutoCompleteTest.java')
}
// Doc header
ant.replaceregexp(match: ":revnumber: $oldVersion", replace: ":revnumber: $version", flags: 'g', byline: true, encoding: 'UTF8') {
ant.replaceregexp(match: ":revnumber: $projectPreviousVersionRegex", replace: ":revnumber: $version", flags: 'g', byline: true, encoding: 'UTF8') {
fileset(dir: 'docs', includes: 'index.adoc')
fileset(dir: 'docs', includes: 'quick-guide.adoc')
fileset(dir: 'docs', includes: 'autocomplete.adoc')
fileset(dir: 'docs', includes: 'picocli-3.0-programmatic-api.adoc')
}
// Downloads section, Gradle
ant.replaceregexp(match: ":picocli:$oldVersion", replace: ":picocli:$version", flags: 'g', byline: true, encoding: 'UTF8') {
ant.replaceregexp(match: ":picocli:$projectPreviousVersionRegex", replace: ":picocli:$version", flags: 'g', byline: true, encoding: 'UTF8') {
fileset(dir: 'docs', includes: 'index.adoc')
}
// Downloads section, Maven
ant.replaceregexp(match: "<version>$oldVersion</version>", replace: "<version>$version</version>", flags: 'g', byline: true, encoding: 'UTF8') {
ant.replaceregexp(match: "<version>$projectPreviousVersionRegex</version>", replace: "<version>$version</version>", flags: 'g', byline: true, encoding: 'UTF8') {
fileset(dir: 'docs', includes: 'index.adoc')
}
// Downloads section, SBT
ant.replaceregexp(match: "\"picocli\" % \"$oldVersion\"", replace: "\\\"picocli\\\" % \\\"$version\\\"", flags: 'g', byline: true, encoding: 'UTF8') {
ant.replaceregexp(match: "\"picocli\" % \"$projectPreviousVersionRegex\"", replace: "\\\"picocli\\\" % \\\"$version\\\"", flags: 'g', byline: true, encoding: 'UTF8') {
fileset(dir: 'docs', includes: 'index.adoc')
}
// Downloads section, Ivy
ant.replaceregexp(match: "rev=\"$oldVersion\"", replace: "rev=\\\"$version\\\"", flags: 'g', byline: true, encoding: 'UTF8') {
ant.replaceregexp(match: "rev=\"$projectPreviousVersionRegex\"", replace: "rev=\\\"$version\\\"", flags: 'g', byline: true, encoding: 'UTF8') {
fileset(dir: 'docs', includes: 'index.adoc')
}
ant.replaceregexp(match: oldRevdate, replace: revDate, flags: 'g', byline: true, encoding: 'UTF8') {
ant.replaceregexp(match: releaseDatePreviousRegex, replace: releaseDate, flags: 'g', byline: true, encoding: 'UTF8') {
fileset(dir: 'docs', includes: 'index.adoc')
fileset(dir: 'docs', includes: 'quick-guide.adoc')
fileset(dir: 'docs', includes: 'autocomplete.adoc')
......@@ -202,35 +239,57 @@ task copyDocs(type: Copy) {
into 'docs'
}
distributions {
main {
baseName = "$archivesBaseName-all"
contents {
from jar
from sourcesJar
from testJar
from testSourcesJar
from javadocJar
from ('LICENSE')
from ('RELEASE-NOTES.md')
ext {
bintrayPackage = 'picocli'
bintrayWebsiteUrl = 'http://picocli.info'
bintrayLabels = ['cli', 'cli framework', 'java', 'command line', 'ergonomic', 'library', 'parser', 'ansi', 'colors', 'annotations', 'reflection', 'usage', 'help', 'customizable', 'stand-alone application', 'main method', 'picocli']
}
bintray {
user = bintrayUsername
key = bintrayApiKey
publications = ['MyPublication']
dryRun = bintrayDryRun //[Default: false] Whether to run this as dry-run, without deploying
publish = false //[Default: false] Whether version should be auto published after an upload
override = false //[Default: false] Whether to override version artifacts already published
//Package configuration. The plugin will use the repo and name properties to check if the package already exists. In that case, there's no need to configure the other package properties (like userOrg, desc, etc).
pkg {
repo = 'picocli'
name = bintrayPackage
userOrg = 'remkop'
licenses = ['Apache-2.0']
desc = description
websiteUrl = bintrayWebsiteUrl
issueTrackerUrl = 'https://github.com/remkop/picocli/issues'
vcsUrl = 'https://github.com/remkop/picocli.git'
labels = bintrayLabels
publicDownloadNumbers = false
githubRepo = 'remkop/picocli' //Optional Github repository
githubReleaseNotesFile = 'RELEASE-NOTES.md' //Optional Github readme file
version {
name = "$projectVersion"
desc = description
released = new Date()
vcsTag = "v$projectVersion"
mavenCentralSync {
sync = mavenOssSync //[Default: true] Determines whether to sync the version to Maven Central.
user = mavenOssUser //OSS user token: mandatory
password = mavenOssPassword //OSS user password: mandatory
close = '1' //Optional property. By default the staging repository is closed and artifacts are released to Maven Central. You can optionally turn this behaviour off (by puting 0 as value) and release the version manually.
}
}
}
ext {
bintrayBaseUrl = 'https://api.bintray.com/maven'
bintrayRepository = 'picocli'
bintrayPackage = 'picocli'
bintrayUsername = System.getenv('BINTRAY_USER')
bintrayApiKey = System.getenv('BINTRAY_KEY')
}
publishing {
publications {
plugin(MavenPublication) {
MyPublication(MavenPublication) {
from components.java
artifact sourcesJar
artifact testJar
artifact testSourcesJar
artifact javadocJar
groupId 'info.picocli'
artifactId bintrayPackage
version "$projectVersion"
pom.withXml {
def root = asNode()
root.appendNode('packaging', 'jar')
......@@ -238,35 +297,7 @@ publishing {
root.appendNode('description', description)
root.appendNode('url', 'http://picocli.info')
root.appendNode('inceptionYear', '2017')
def license = root.appendNode('licenses').appendNode('license')
license.appendNode('name', 'The Apache Software License, version 2.0')
license.appendNode('url', 'http://www.apache.org/licenses/LICENSE-2.0.txt')
license.appendNode('distribution', 'repo')
def developer = root.appendNode('developers').appendNode('developer')
developer.appendNode('id', 'rpopma')
developer.appendNode('name', 'Remko Popma')
developer.appendNode('email', 'rpopma@apache.org')
def scm = root.appendNode('scm')
scm.appendNode('connection', 'scm:git:https://github.com/remkop/picocli.git')
scm.appendNode('developerConnection', 'scm:git:ssh://github.com:remkop/picocli.git')
scm.appendNode('url', 'https://github.com/remkop/picocli/tree/master')
}
}
}
repositories {
maven {
name 'myLocal'
url "file://$projectDir/../repo/$bintrayUsername"
}
maven {
name 'Bintray'
url "$bintrayBaseUrl/$bintrayUsername/$bintrayRepository/$bintrayPackage"
credentials {
username = bintrayUsername
password = bintrayApiKey
root.children().last() + pomConfig
}
}
}
......@@ -283,7 +314,8 @@ Release procedure:
7a update README.md (latest version, release notes)
8. commit -m "Release picocli version ..."
9. tag v$version
10. gradlew publishPluginPublicationToBintrayRepository
10.a gradlew bintrayUpload - to publish to bintray.com
10.b gradlew bintrayPublish - to sign, publish and Maven central sync task)
11. edit version numbers: increase minor version and add -SNAPSHOT classifier
12. gradlew bumpVersion
......@@ -306,7 +338,7 @@ Release procedure:
25. Switch to master
26. Update RELEASE-NOTES.md (insert changes from branch)
27. Update last release version in README
28. Update `previousReleaseVersion` in build.gradle
28. Update `projectPreviousVersion` in build.gradle
29. gradlew copyDocs
30. commit -m "Update master for release x.x (from branch x.x)"
*/
picocli (3.9.1-1) unstable; urgency=medium
* New upstream release
* debian/control: add libjline2-java dependency
* debian/libpicocli-java.poms: add new picocli modules / JARs
* debian/maven.rules: keep jline version (debian version link doesn't exist)
* debian/patches: make new release buildable under debian
-- Miroslav Kravec <kravec.miroslav@gmail.com> Sun, 13 Jan 2019 10:26:29 +0100
picocli (3.6.1-1) unstable; urgency=medium
* New upstream release
......
......@@ -8,6 +8,7 @@ Build-Depends:
default-jdk,
gradle-debian-helper (>= 1.4),
groovy (>= 2.4.10),
libjline2-java,
maven-repo-helper
Standards-Version: 4.2.1
Vcs-Git: https://salsa.debian.org/java-team/picocli.git
......
......@@ -26,3 +26,5 @@
# Empty by default. [mh_install]
#
build/debian/picocli.pom --artifact=build/libs/picocli*.jar --java-lib
picocli-codegen/build/debian/picocli-codegen.pom --artifact=picocli-shell-jline2/build/libs/picocli*.jar --java-lib
picocli-shell-jline2/build/debian/picocli-shell-jline2.pom --artifact=picocli-shell-jline2/build/libs/picocli*.jar --java-lib
jline jline jar * * *
From: Miroslav Kravec <kravec.miroslav@gmail.com>
Date: Sat, 8 Sep 2018 13:37:14 +0200
Subject: Adjust build to work without asciidoctor, and with default java
Date: Sun, 13 Jan 2019 10:19:34 +0100
Subject: Adjust build to work without asciidoctor and bintray,
and with default java
---
build.gradle | 29 ++---------------------------
1 file changed, 2 insertions(+), 27 deletions(-)
build.gradle | 61 ++-------------------------------------
picocli-codegen/build.gradle | 36 +----------------------
picocli-shell-jline2/build.gradle | 36 +----------------------
picocli-shell-jline3/build.gradle | 34 ----------------------
4 files changed, 4 insertions(+), 163 deletions(-)
diff --git a/build.gradle b/build.gradle
index 66bce6c..0822354 100644
index 73543c8..0b34d03 100644
--- a/build.gradle
+++ b/build.gradle
@@ -10,21 +10,11 @@ def revDate = '2018-09-28'
// for bumpReadmeVersion task
def previousReleaseVersion = '3.6.0'
@@ -2,29 +2,16 @@ group 'info.picocli'
description 'Java command line parser with both an annotations API and a programmatic API. Usage help with ANSI styles and colors. Autocomplete. Nested subcommands. Easily included as source to avoid adding a dependency.'
version "$projectVersion"
-buildscript {
- repositories {
......@@ -21,12 +25,20 @@ index 66bce6c..0822354 100644
-
- dependencies {
- classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.3'
- classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.+'
- }
-}
-
-apply plugin: 'org.asciidoctor.convert'
apply plugin: 'jacoco'
apply plugin: 'distribution'
apply plugin: 'maven-publish'
-apply plugin: 'com.jfrog.bintray'
allprojects {
apply plugin: 'groovy'
apply plugin: 'java'
apply plugin: 'java-library' // to avoid https://github.com/gradle/gradle/issues/1118
- sourceCompatibility = 1.5
- targetCompatibility = 1.5
+ sourceCompatibility = 1.6
......@@ -34,39 +46,238 @@ index 66bce6c..0822354 100644
repositories {
jcenter()
@@ -83,7 +73,6 @@ Application-name: $rootProject.name $project.name
}
}
@@ -112,9 +99,6 @@ allprojects {
}
-apply plugin: 'org.asciidoctor.convert'
apply plugin: 'jacoco'
apply plugin: 'distribution'
apply plugin: 'maven-publish'
@@ -105,14 +94,6 @@ javadoc {
options.overview = "src/main/java/overview.html"
destinationDir = file("build/docs/apidocs")
}
-// work around https://github.com/gradle/gradle/issues/4046
-javadoc.dependsOn('copyJavadocDocFiles')
-task copyJavadocDocFiles(type: Copy) {
- from('src/main/java')
- into 'build/docs/apidocs'
- include '**/doc-files/*.*'
-}
-
jacocoTestReport {
reports {
xml.enabled true
@@ -135,12 +116,6 @@ task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
ext {
- bintrayUsername = System.getenv('BINTRAY_USER')
- bintrayApiKey = System.getenv('BINTRAY_KEY')
- bintrayDryRun = false
mavenOssUser = System.getenv('MAVEN_OSS_USER')
mavenOssPassword = System.getenv('MAVEN_OSS_PASSWORD')
mavenOssSync = false
@@ -158,12 +142,6 @@ jar {
}
javadoc.options.overview = "src/main/java/overview.html"
-javadoc.dependsOn('asciidoctor')
-asciidoctor {
- sourceDir = file('docs')
- outputDir = file('build/docs')
- logDocuments = true
-}
artifacts {
archives javadocJar
archives sourcesJar
jacocoTestReport {
reports {
xml.enabled true
@@ -244,41 +222,6 @@ ext {
bintrayWebsiteUrl = 'http://picocli.info'
bintrayLabels = ['cli', 'cli framework', 'java', 'command line', 'ergonomic', 'library', 'parser', 'ansi', 'colors', 'annotations', 'reflection', 'usage', 'help', 'customizable', 'stand-alone application', 'main method', 'picocli']
}
-bintray {
- user = bintrayUsername
- key = bintrayApiKey
- publications = ['MyPublication']
- dryRun = bintrayDryRun //[Default: false] Whether to run this as dry-run, without deploying
- publish = false //[Default: false] Whether version should be auto published after an upload
- override = false //[Default: false] Whether to override version artifacts already published
- //Package configuration. The plugin will use the repo and name properties to check if the package already exists. In that case, there's no need to configure the other package properties (like userOrg, desc, etc).
- pkg {
- repo = 'picocli'
- name = bintrayPackage
- userOrg = 'remkop'
- licenses = ['Apache-2.0']
- desc = description
- websiteUrl = bintrayWebsiteUrl
- issueTrackerUrl = 'https://github.com/remkop/picocli/issues'
- vcsUrl = 'https://github.com/remkop/picocli.git'
- labels = bintrayLabels
- publicDownloadNumbers = false
- githubRepo = 'remkop/picocli' //Optional Github repository
- githubReleaseNotesFile = 'RELEASE-NOTES.md' //Optional Github readme file
- version {
- name = "$projectVersion"
- desc = description
- released = new Date()
- vcsTag = "v$projectVersion"
- mavenCentralSync {
- sync = mavenOssSync //[Default: true] Determines whether to sync the version to Maven Central.
- user = mavenOssUser //OSS user token: mandatory
- password = mavenOssPassword //OSS user password: mandatory
- close = '1' //Optional property. By default the staging repository is closed and artifacts are released to Maven Central. You can optionally turn this behaviour off (by puting 0 as value) and release the version manually.
- }
- }
- }
-}
publishing {
publications {
MyPublication(MavenPublication) {
diff --git a/picocli-codegen/build.gradle b/picocli-codegen/build.gradle
index 889264e..8cd29fb 100644
--- a/picocli-codegen/build.gradle
+++ b/picocli-codegen/build.gradle
@@ -2,13 +2,12 @@ plugins {
id 'java'
id 'distribution'
id 'maven-publish'
- id 'com.jfrog.bintray'
}
group 'info.picocli'
description 'Picocli Code Generation - Tools to generate documentation, configuration, source code and other files from a picocli model.'
version "$projectVersion"
-sourceCompatibility = 1.5
+sourceCompatibility = 1.6
dependencies {
compile rootProject
@@ -31,39 +30,6 @@ ext {
bintrayWebsiteUrl = 'https://github.com/remkop/picocli/tree/master/picocli-codegen'
bintrayLabels = ['cli', 'cli framework', 'command line', 'codegen', 'picocli']
}
-bintray {
- user = bintrayUsername
- key = bintrayApiKey
- publications = ['MyPublication']
- dryRun = bintrayDryRun //[Default: false] Whether to run this as dry-run, without deploying
- publish = false //[Default: false] Whether version should be auto published after an upload
- override = false //[Default: false] Whether to override version artifacts already published
- //Package configuration. The plugin will use the repo and name properties to check if the package already exists. In that case, there's no need to configure the other package properties (like userOrg, desc, etc).
- pkg {
- repo = 'picocli'
- name = bintrayPackage
- userOrg = 'remkop'
- licenses = ['Apache-2.0']
- desc = description
- websiteUrl = bintrayWebsiteUrl
- issueTrackerUrl = 'https://github.com/remkop/picocli/issues'
- vcsUrl = 'https://github.com/remkop/picocli.git'
- labels = bintrayLabels
- publicDownloadNumbers = false
- version {
- name = "$projectVersion"
- desc = description
- released = new Date()
- vcsTag = "v$projectVersion"
- mavenCentralSync {
- sync = mavenOssSync //[Default: true] Determines whether to sync the version to Maven Central.
- user = mavenOssUser //OSS user token: mandatory
- password = mavenOssPassword //OSS user password: mandatory
- close = '1' //Optional property. By default the staging repository is closed and artifacts are released to Maven Central. You can optionally turn this behaviour off (by puting 0 as value) and release the version manually.
- }
- }
- }
-}
publishing {
publications {
MyPublication(MavenPublication) {
diff --git a/picocli-shell-jline2/build.gradle b/picocli-shell-jline2/build.gradle
index bc8023d..68dac91 100644
--- a/picocli-shell-jline2/build.gradle
+++ b/picocli-shell-jline2/build.gradle
@@ -2,13 +2,12 @@ plugins {
id 'java'
id 'distribution'
id 'maven-publish'
- id 'com.jfrog.bintray'
}
group 'info.picocli'
description 'Picocli Shell JLine2 - easily build interactive shell applications with JLine 2 and picocli.'
version "$projectVersion"
-sourceCompatibility = 1.5
+sourceCompatibility = 1.6
dependencies {
compile rootProject
@@ -33,39 +32,6 @@ ext {
bintrayWebsiteUrl = 'https://github.com/remkop/picocli/tree/master/picocli-shell-jline2'
bintrayLabels = ['cli', 'interactive', 'commandline', 'shell', 'picocli', 'jline']
}
-bintray {
- user = bintrayUsername
- key = bintrayApiKey
- publications = ['MyPublication']
- dryRun = bintrayDryRun //[Default: false] Whether to run this as dry-run, without deploying
- publish = false //[Default: false] Whether version should be auto published after an upload
- override = false //[Default: false] Whether to override version artifacts already published
- //Package configuration. The plugin will use the repo and name properties to check if the package already exists. In that case, there's no need to configure the other package properties (like userOrg, desc, etc).
- pkg {
- repo = 'picocli'
- name = bintrayPackage
- userOrg = 'remkop'
- licenses = ['Apache-2.0']
- desc = description
- websiteUrl = bintrayWebsiteUrl
- issueTrackerUrl = 'https://github.com/remkop/picocli/issues'
- vcsUrl = 'https://github.com/remkop/picocli.git'
- labels = bintrayLabels
- publicDownloadNumbers = false
- version {
- name = "$projectVersion"
- desc = description
- released = new Date()
- vcsTag = "v$projectVersion"
- mavenCentralSync {
- sync = mavenOssSync //[Default: true] Determines whether to sync the version to Maven Central.
- user = mavenOssUser //OSS user token: mandatory
- password = mavenOssPassword //OSS user password: mandatory
- close = '1' //Optional property. By default the staging repository is closed and artifacts are released to Maven Central. You can optionally turn this behaviour off (by puting 0 as value) and release the version manually.
- }
- }
- }
-}
publishing {
publications {
MyPublication(MavenPublication) {
diff --git a/picocli-shell-jline3/build.gradle b/picocli-shell-jline3/build.gradle
index 2eb0d77..35c009f 100644
--- a/picocli-shell-jline3/build.gradle
+++ b/picocli-shell-jline3/build.gradle
@@ -2,7 +2,6 @@ plugins {
id 'java'
id 'distribution'
id 'maven-publish'
- id 'com.jfrog.bintray'
}
group 'info.picocli'
@@ -33,39 +32,6 @@ ext {
bintrayWebsiteUrl = 'https://github.com/remkop/picocli/tree/master/picocli-shell-jline3'
bintrayLabels = ['cli', 'interactive', 'commandline', 'shell', 'picocli', 'jline']
}
-bintray {
- user = bintrayUsername
- key = bintrayApiKey
- publications = ['MyPublication']
- dryRun = bintrayDryRun //[Default: false] Whether to run this as dry-run, without deploying
- publish = false //[Default: false] Whether version should be auto published after an upload
- override = false //[Default: false] Whether to override version artifacts already published
- //Package configuration. The plugin will use the repo and name properties to check if the package already exists. In that case, there's no need to configure the other package properties (like userOrg, desc, etc).
- pkg {
- repo = 'picocli'
- name = bintrayPackage
- userOrg = 'remkop'
- licenses = ['Apache-2.0']
- desc = description
- websiteUrl = bintrayWebsiteUrl
- issueTrackerUrl = 'https://github.com/remkop/picocli/issues'
- vcsUrl = 'https://github.com/remkop/picocli.git'
- labels = bintrayLabels
- publicDownloadNumbers = false
- version {
- name = "$projectVersion"
- desc = description
- released = new Date()
- vcsTag = "v$projectVersion"
- mavenCentralSync {
- sync = mavenOssSync //[Default: true] Determines whether to sync the version to Maven Central.
- user = mavenOssUser //OSS user token: mandatory
- password = mavenOssPassword //OSS user password: mandatory
- close = '1' //Optional property. By default the staging repository is closed and artifacts are released to Maven Central. You can optionally turn this behaviour off (by puting 0 as value) and release the version manually.
- }
- }
- }
-}
publishing {
publications {
MyPublication(MavenPublication) {
......@@ -4,4 +4,4 @@
dh $@ --buildsystem=gradle --with maven-repo-helper
override_dh_auto_build:
dh_auto_build -- jar -x :picocli-examples:compileGroovy
dh_auto_build -- jar -x :picocli-examples:compileGroovy -x :picocli-shell-jline3:jar
groovyVersion = 2.4.10
hamcrestCoreVersion = 1.3
ivyVersion = 2.4.0
jansiVersion = 1.15
jlineVersion = 2.14.6
jline3Version = 3.9.0
junitDepVersion = 4.11
junitVersion = 4.12
# projectPreviousReleaseVersion is non-SNAPSHOT, only published releases
projectPreviousReleaseVersion = 3\\.9\\.0
# projectPreviousVersionRegex may be a SNAPSHOT
projectPreviousVersionRegex = 3\\.9\\.1-SNAPSHOT
projectVersion = 3.9.1
releaseDate = 2019-01-10
releaseDatePreviousRegex = 2019\\-01\\-04
systemRulesVersion = 1.17.1
\ No newline at end of file
<p align="center"><img src="https://picocli.info/images/logo/horizontal-400x150.png" alt="picocli" height="150px"></p>
# Picocli Code Generation
Picocli Code Generation contains tools for generating source code, documentation and configuration files
for picocli-based applications.
## ReflectionConfigGenerator Tool for AOT Compilation to Native Image on GraalVM
`ReflectionConfigGenerator` generates a JSON String with the program elements that will be accessed reflectively in a picocli-based application, in order to compile this application ahead-of-time into a native executable with GraalVM.
The output of `ReflectionConfigGenerator` is intended to be passed to the `-H:ReflectionConfigurationFiles=/path/to/reflectconfig` option of the `native-image` GraalVM utility. This allows picocli-based applications to be compiled to a native image.
See [Picocli on GraalVM: Blazingly Fast Command Line Apps](https://github.com/remkop/picocli/wiki/Picocli-on-GraalVM:-Blazingly-Fast-Command-Line-Apps) for details.
### Generating GraalVM Reflection Configuration During the Build
Below shows some examples of configuring your build to generate a GraalVM reflection configuration file with the `ReflectionConfigGenerator` tool during the build.
Note that the `--output` option allows you to specify the path to the file to write the configuration to.
When this option is omitted, the output is sent to standard out.
The `ReflectionConfigGenerator` tool accepts any number of fully qualified class names of command classes
(classes with picocli annotations like `@Command`, `@Option` and `@Parameters`).
The resulting configuration file will contain entries for the reflected elements of all specified classes.
#### Maven
For Maven, add an `exec:java` goal to generate a Graal reflection configuration file with the `ReflectionConfigGenerator` tool.
This example uses the `process-classes` phase of the build, there are [alternatives](http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html).
Note that the `picocli-codegen` module is only added as a dependency for the `exec` plugin, so it does not need to be added to the project dependencies.
```xml
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>generateGraalReflectionConfig</id>
<phase>process-classes</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<includeProjectDependencies>true</includeProjectDependencies>
<includePluginDependencies>true</includePluginDependencies>
<mainClass>picocli.codegen.aot.graalvm.ReflectionConfigGenerator</mainClass>
<arguments>
<argument>--output=target/cli-reflect.json</argument>
<argument>com.your.package.YourCommand1</argument>
<argument>com.your.package.YourCommand2</argument>
</arguments>
</configuration>
<dependencies>
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli-codegen</artifactId>
<version>3.9.1</version>
<type>jar</type>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
```
#### Gradle
For Gradle, add a custom configuration for the `picocli-codegen` module to your `gradle.build`.
This allows us to add this module to the classpath of our custom task without adding it as a dependency to the "standard" build.
```gradle
configurations {
generateConfig
}
dependencies {
compile 'info.picocli:picocli:3.9.1'
generateConfig 'info.picocli:picocli-codegen:3.9.1'
}
```
Then, add a custom task to run the `ReflectionConfigGenerator` tool.
This example generates the file during the `assemble` lifecycle task, there are [alternatives](https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_tasks).
```gradle
task(generateGraalReflectionConfig, dependsOn: 'classes', type: JavaExec) {
main = 'picocli.codegen.aot.graalvm.ReflectionConfigGenerator'
classpath = configurations.generateConfig + sourceSets.main.runtimeClasspath
def outputFile = new File(project.buildDir, 'cli-reflect.json')
args = ["--output=$outputFile", 'com.your.package.YourCommand1', 'com.your.package.YourCommand2']
}
assemble.dependsOn generateGraalReflectionConfig
```
\ No newline at end of file
plugins {
id 'java'
id 'distribution'
id 'maven-publish'
id 'com.jfrog.bintray'
}
group 'info.picocli'
description 'Picocli Code Generation - Tools to generate documentation, configuration, source code and other files from a picocli model.'
version "$projectVersion"
sourceCompatibility = 1.5
dependencies {
compile rootProject
testCompile "junit:junit:$junitVersion"
}
jar {
manifest {
attributes 'Specification-Title' : 'Picocli Code Generation',
'Specification-Vendor' : 'Remko Popma',
'Specification-Version' : version,
'Implementation-Title' : 'Picocli Code Generation',
'Implementation-Vendor' : 'Remko Popma',
'Implementation-Version': version,
'Automatic-Module-Name' : 'info.picocli.codegen'
}
}
ext {
bintrayPackage = 'picocli-codegen'
bintrayWebsiteUrl = 'https://github.com/remkop/picocli/tree/master/picocli-codegen'
bintrayLabels = ['cli', 'cli framework', 'command line', 'codegen', 'picocli']
}
bintray {
user = bintrayUsername
key = bintrayApiKey
publications = ['MyPublication']
dryRun = bintrayDryRun //[Default: false] Whether to run this as dry-run, without deploying
publish = false //[Default: false] Whether version should be auto published after an upload
override = false //[Default: false] Whether to override version artifacts already published
//Package configuration. The plugin will use the repo and name properties to check if the package already exists. In that case, there's no need to configure the other package properties (like userOrg, desc, etc).
pkg {
repo = 'picocli'
name = bintrayPackage
userOrg = 'remkop'
licenses = ['Apache-2.0']
desc = description
websiteUrl = bintrayWebsiteUrl
issueTrackerUrl = 'https://github.com/remkop/picocli/issues'
vcsUrl = 'https://github.com/remkop/picocli.git'
labels = bintrayLabels
publicDownloadNumbers = false
version {
name = "$projectVersion"
desc = description
released = new Date()
vcsTag = "v$projectVersion"
mavenCentralSync {
sync = mavenOssSync //[Default: true] Determines whether to sync the version to Maven Central.
user = mavenOssUser //OSS user token: mandatory
password = mavenOssPassword //OSS user password: mandatory
close = '1' //Optional property. By default the staging repository is closed and artifacts are released to Maven Central. You can optionally turn this behaviour off (by puting 0 as value) and release the version manually.
}
}
}
}
publishing {
publications {
MyPublication(MavenPublication) {
from components.java
artifact sourcesJar
artifact testJar
artifact testSourcesJar
artifact javadocJar
groupId 'info.picocli'
artifactId bintrayPackage
version "$projectVersion"
pom.withXml {
def root = asNode()
root.appendNode('packaging', 'jar')
root.appendNode('name', bintrayPackage)
root.appendNode('description', description)
root.appendNode('url', 'http://picocli.info')
root.appendNode('inceptionYear', '2018')
root.children().last() + pomConfig
}
}
}
}
package picocli.codegen.aot.graalvm;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Model.ArgSpec;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Model.IGetter;
import picocli.CommandLine.Model.ISetter;
import picocli.CommandLine.Model.OptionSpec;
import picocli.CommandLine.Model.PositionalParamSpec;
import picocli.CommandLine.Model.UnmatchedArgsBinding;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
/**
* {@code ReflectionConfigGenerator} generates a JSON String with the program elements that will be accessed
* reflectively in a picocli-based application, in order to compile this application ahead-of-time into a native
* executable with GraalVM.
* <p>
* GraalVM has <a href="https://github.com/oracle/graal/blob/master/substratevm/REFLECTION.md">limited support for Java
* reflection</a> and it needs to know ahead of time the reflectively accessed program elements.
* </p><p>
* The output of {@code ReflectionConfigGenerator} is intended to be passed to the {@code -H:ReflectionConfigurationFiles=/path/to/reflectconfig}
* option of the {@code native-image} <a href="https://www.graalvm.org/docs/reference-manual/aot-compilation/">GraalVM utility</a>.
* This allows picocli-based applications to be compiled to a native image.
* </p><p>
* If necessary, it is possible to exclude classes with system property {@code picocli.codegen.excludes},
* which accepts a comma-separated list of regular expressions of the fully qualified class names that should
* <em>not</em> be included in the resulting JSON String.
* </p>
*
* @since 3.7.0
*/
public class ReflectionConfigGenerator {
private static final String SYSPROP_CODEGEN_EXCLUDES = "picocli.codegen.excludes";
private static final String REFLECTED_FIELD_BINDING_CLASS = "picocli.CommandLine$Model$FieldBinding";
private static final String REFLECTED_METHOD_BINDING_CLASS = "picocli.CommandLine$Model$MethodBinding";
private static final String REFLECTED_FIELD_BINDING_FIELD = "field";
private static final String REFLECTED_METHOD_BINDING_METHOD = "method";
private static final String REFLECTED_BINDING_FIELD_SCOPE = "scope";
@Command(name = "ReflectionConfigGenerator",
description = {"Generates a JSON file with the program elements that will be " +
"accessed reflectively for the specified @Command classes. " +
"The generated JSON file can be passed to the -H:ReflectionConfigurationFiles=/path/to/reflectconfig " +
"option of the `native-image` GraalVM utility.",
"See https://github.com/oracle/graal/blob/master/substratevm/REFLECTION.md"},
mixinStandardHelpOptions = true, version = "picocli-codegen ReflectionConfigGenerator 3.7.0")
private static class App implements Callable<Void> {
@Parameters(arity = "1..*", description = "One or more classes to generate a GraalVM ReflectionConfiguration for.")
Class<?>[] classes = new Class<?>[0];
@Option(names = {"-o", "--output"}, description = "Output file to write the configuration to. " +
"If not specified, the configuration is written to the standard output stream.")
File outputFile;
public Void call() throws NoSuchFieldException, IllegalAccessException, IOException {
List<CommandSpec> specs = new ArrayList<CommandSpec>();
for (Class<?> cls : classes) {
specs.add(new CommandLine(cls).getCommandSpec());
}
String result = new ReflectionConfigGenerator().generateReflectionConfig(specs.toArray(new CommandSpec[0]));
if (result != null) {
if (outputFile == null) {
System.out.print(result);
} else {
writeToFile(result);
}
}
return null;
}
private void writeToFile(String result) throws IOException {
FileWriter writer = null;
try {
writer = new FileWriter(outputFile);
writer.write(result);
} finally {
if (writer != null) {
writer.close();
}
}
}
}
/**
* Runs this class as a standalone application, printing the resulting JSON String to a file or to {@code System.out}.
* @param args one or more fully qualified class names of {@code @Command}-annotated classes.
*/
public static void main(String... args) {
CommandLine.call(new App(), args);
}
/**
* Returns a JSON String with the program elements that will be accessed reflectively for the specified
* {@code CommandSpec} objects.
*
* @param specs one or more {@code CommandSpec} objects to inspect
* @return a JSON String in the <a href="https://github.com/oracle/graal/blob/master/substratevm/REFLECTION.md#manual-configuration">format</a>
* required by the {@code -H:ReflectionConfigurationFiles=/path/to/reflectconfig} option of the GraalVM {@code native-image} utility.
* @throws NoSuchFieldException if a problem occurs while processing the specified specs
* @throws IllegalAccessException if a problem occurs while processing the specified specs
*/
public String generateReflectionConfig(CommandSpec... specs) throws NoSuchFieldException, IllegalAccessException {
Visitor visitor = new Visitor();
for (CommandSpec spec : specs) {
visitor.visitCommandSpec(spec);
}
return generateReflectionConfig(visitor).toString();
}
StringBuilder generateReflectionConfig(Visitor visited) {
StringBuilder result = new StringBuilder(1024);
String prefix = String.format("[%n");
String suffix = String.format("%n]%n");
for (ReflectedClass cls : visited.visited.values()) {
result.append(prefix).append(cls);
prefix = String.format(",%n");
}
return result.append(suffix);
}
static final class Visitor {
Map<String, ReflectedClass> visited = new LinkedHashMap<String, ReflectedClass>();
Visitor() {
getOrCreateClass(Method.class);
getOrCreateClassName("java.lang.reflect.Executable").addMethod("getParameters");
getOrCreateClassName("java.lang.reflect.Parameter").addMethod("getName");
// type converters registered with reflection
getOrCreateClassName("java.time.Duration").addMethod("parse", CharSequence.class);
getOrCreateClassName("java.time.Instant").addMethod("parse", CharSequence.class);
getOrCreateClassName("java.time.LocalDate").addMethod("parse", CharSequence.class);
getOrCreateClassName("java.time.LocalDateTime").addMethod("parse", CharSequence.class);
getOrCreateClassName("java.time.LocalTime").addMethod("parse", CharSequence.class);
getOrCreateClassName("java.time.MonthDay").addMethod("parse", CharSequence.class);
getOrCreateClassName("java.time.OffsetDateTime").addMethod("parse", CharSequence.class);
getOrCreateClassName("java.time.OffsetTime").addMethod("parse", CharSequence.class);
getOrCreateClassName("java.time.Period").addMethod("parse", CharSequence.class);
getOrCreateClassName("java.time.Year").addMethod("parse", CharSequence.class);
getOrCreateClassName("java.time.YearMonth").addMethod("parse", CharSequence.class);
getOrCreateClassName("java.time.ZonedDateTime").addMethod("parse", CharSequence.class);
getOrCreateClassName("java.time.ZoneId").addMethod("of", String.class);
getOrCreateClassName("java.time.ZoneOffset").addMethod("of", String.class);
getOrCreateClassName("java.nio.file.Path");
getOrCreateClassName("java.nio.file.Paths").addMethod("get", String.class, String[].class);
getOrCreateClassName("java.sql.Connection");
getOrCreateClassName("java.sql.Driver");
getOrCreateClassName("java.sql.DriverManager")
.addMethod("getConnection", String.class)
.addMethod("getDriver", String.class);
getOrCreateClassName("java.sql.Timestamp").addMethod("valueOf", String.class);
}
void visitCommandSpec(CommandSpec spec) throws NoSuchFieldException, IllegalAccessException {
if (spec.userObject() != null) {
if (spec.userObject() instanceof Method) {
Method method = (Method) spec.userObject();
ReflectedClass cls = getOrCreateClass(method.getDeclaringClass());
cls.addMethod(method.getName(), method.getParameterTypes());
} else if (Proxy.isProxyClass(spec.userObject().getClass())) {
// do nothing
} else {
visitAnnotatedFields(spec.userObject().getClass());
}
}
visitObjectType(spec.versionProvider());
visitObjectType(spec.defaultValueProvider());
for (UnmatchedArgsBinding binding : spec.unmatchedArgsBindings()) {
visitGetter(binding.getter());
visitSetter(binding.setter());
}
for (OptionSpec option : spec.options()) {
visitArgSpec(option);
}
for (PositionalParamSpec positional : spec.positionalParameters()) {
visitArgSpec(positional);
}
for (CommandSpec mixin : spec.mixins().values()) {
visitCommandSpec(mixin);
}
for (CommandLine sub : spec.subcommands().values()) {
visitCommandSpec(sub.getCommandSpec());
}
}
private void visitAnnotatedFields(Class<?> cls) {
if (cls == null) {
return;
}
ReflectedClass reflectedClass = getOrCreateClass(cls);
Field[] declaredFields = cls.getDeclaredFields();
for (Field f : declaredFields) {
if (f.isAnnotationPresent(CommandLine.Spec.class)) {
reflectedClass.addField(f.getName());
}
if (f.isAnnotationPresent(CommandLine.ParentCommand.class)) {
reflectedClass.addField(f.getName());
}
if (f.isAnnotationPresent(CommandLine.Mixin.class)) {
reflectedClass.addField(f.getName());
}
if (f.isAnnotationPresent(CommandLine.Unmatched.class)) {
reflectedClass.addField(f.getName());
}
}
visitAnnotatedFields(cls.getSuperclass());
}
private void visitArgSpec(ArgSpec argSpec) throws NoSuchFieldException, IllegalAccessException {
visitGetter(argSpec.getter());
visitSetter(argSpec.setter());
visitType(argSpec.type());
visitTypes(argSpec.auxiliaryTypes());
visitObjectType(argSpec.completionCandidates());
visitObjectTypes(argSpec.converters());
}
private void visitTypes(Class<?>[] classes) {
for (Class<?> cls : classes) { visitType(cls); }
}
private void visitType(Class<?> type) {
if (type != null) { getOrCreateClass(type); }
}
private void visitObjectType(Object object) {
if (object != null) { visitType(object.getClass()); }
}
private void visitObjectTypes(Object[] array) {
if (array != null) {
for (Object element : array) { visitObjectType(element); }
}
}
private void visitGetter(IGetter getter) throws NoSuchFieldException, IllegalAccessException {
if (getter == null) {
return;
}
if (REFLECTED_FIELD_BINDING_CLASS.equals(getter.getClass().getName())) {
visitFieldBinding(getter);
}
if (REFLECTED_METHOD_BINDING_CLASS.equals(getter.getClass().getName())) {
visitMethodBinding(getter);
}
}
private void visitSetter(ISetter setter) throws NoSuchFieldException, IllegalAccessException {
if (setter == null) {
return;
}
if (REFLECTED_FIELD_BINDING_CLASS.equals(setter.getClass().getName())) {
visitFieldBinding(setter);
}
if (REFLECTED_METHOD_BINDING_CLASS.equals(setter.getClass().getName())) {
visitMethodBinding(setter);
}
}
private void visitFieldBinding(Object fieldBinding) throws IllegalAccessException, NoSuchFieldException {
Field field = (Field) accessibleField(fieldBinding.getClass(), REFLECTED_FIELD_BINDING_FIELD).get(fieldBinding);
Object scope = accessibleField(fieldBinding.getClass(), REFLECTED_BINDING_FIELD_SCOPE).get(fieldBinding);
getOrCreateClass(scope.getClass()).addField(field.getName());
}
private void visitMethodBinding(Object methodBinding) throws IllegalAccessException, NoSuchFieldException {
Method method = (Method) accessibleField(methodBinding.getClass(), REFLECTED_METHOD_BINDING_METHOD).get(methodBinding);
ReflectedClass cls = getOrCreateClass(method.getDeclaringClass());
cls.addMethod(method.getName(), method.getParameterTypes());
Object scope = accessibleField(methodBinding.getClass(), REFLECTED_BINDING_FIELD_SCOPE).get(methodBinding);
ReflectedClass scopeClass = getOrCreateClass(scope.getClass());
if (!scope.getClass().equals(method.getDeclaringClass())) {
scopeClass.addMethod(method.getName(), method.getParameterTypes());
}
}
private static Field accessibleField(Class<?> cls, String fieldName) throws NoSuchFieldException {
Field field = cls.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
}
ReflectedClass getOrCreateClass(Class<?> cls) {
if (cls.isPrimitive()) {
return new ReflectedClass(cls.getName()); // don't store
}
return getOrCreateClassName(cls.getName());
}
private ReflectedClass getOrCreateClassName(String name) {
ReflectedClass result = visited.get(name);
if (result == null) {
result = new ReflectedClass(name);
if (!excluded(name)) {
visited.put(name, result);
}
}
return result;
}
static boolean excluded(String fqcn) {
String[] excludes = System.getProperty(SYSPROP_CODEGEN_EXCLUDES, "").split(",");
for (String regex : excludes) {
if (fqcn.matches(regex)) {
System.err.printf("Class %s is excluded: (%s=%s)%n", fqcn, SYSPROP_CODEGEN_EXCLUDES, System.getProperty(SYSPROP_CODEGEN_EXCLUDES));
return true;
}
}
return false;
}
}
static class ReflectedClass {
private final String name;
private final Set<ReflectedField> fields = new LinkedHashSet<ReflectedField>();
private final Set<ReflectedMethod> methods = new LinkedHashSet<ReflectedMethod>();
ReflectedClass(String name) {
this.name = name;
}
ReflectedClass addField(String fieldName) {
fields.add(new ReflectedField(fieldName));
return this;
}
ReflectedClass addMethod0(String methodName, String... paramTypes) {
methods.add(new ReflectedMethod(methodName, paramTypes));
return this;
}
ReflectedClass addMethod(String methodName, Class... paramClasses) {
String[] paramTypes = new String[paramClasses.length];
for (int i = 0; i < paramClasses.length; i++) {
paramTypes[i] = paramClasses[i].getName();
}
return addMethod0(methodName, paramTypes);
}
@Override
public String toString() {
String result = String.format("" +
" {%n" +
" \"name\" : \"%s\",%n" +
" \"allDeclaredConstructors\" : true,%n" +
" \"allPublicConstructors\" : true,%n" +
" \"allDeclaredMethods\" : true,%n" +
" \"allPublicMethods\" : true", name);
if (!fields.isEmpty()) {
result += String.format(",%n \"fields\" : ");
String prefix = String.format("[%n"); // start JSON array
for (ReflectedField field : fields) {
result += String.format("%s %s", prefix, field);
prefix = String.format(",%n");
}
result += String.format("%n ]"); // end JSON array
}
if (!methods.isEmpty()) {
result += String.format(",%n \"methods\" : ");
String prefix = String.format("[%n"); // start JSON array
for (ReflectedMethod method : methods) {
result += String.format("%s %s", prefix, method);
prefix = String.format(",%n");
}
result += String.format("%n ]"); // end JSON array
}
result += String.format("%n }");
return result;
}
}
static class ReflectedMethod {
private final String name;
private final String[] paramTypes;
ReflectedMethod(String name, String... paramTypes) {
this.name = name;
this.paramTypes = paramTypes.clone();
}
@Override public int hashCode() { return name.hashCode() * Arrays.hashCode(paramTypes); }
@Override public boolean equals(Object o) {
return o instanceof ReflectedMethod
&& ((ReflectedMethod) o).name.equals(name)
&& Arrays.equals(((ReflectedMethod) o).paramTypes, paramTypes);
}
@Override
public String toString() {
return String.format("{ \"name\" : \"%s\", \"parameterTypes\" : [%s] }", name, formatParamTypes());
}
private String formatParamTypes() {
StringBuilder result = new StringBuilder();
for (String type : paramTypes) {
if (result.length() > 0) {
result.append(", ");
}
result.append('"').append(type).append('"');
}
return result.toString();
}
}
static class ReflectedField {
private final String name;
ReflectedField(String name) {
this.name = name;
}
@Override public int hashCode() { return name.hashCode(); }
@Override public boolean equals(Object o) {
return o instanceof ReflectedField && ((ReflectedField) o).name.equals(name);
}
@Override
public String toString() {
return String.format("{ \"name\" : \"%s\" }", name);
}
}
}
package picocli.codegen.aot.graalvm;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Mixin;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Option;
import picocli.CommandLine.ParameterException;
import picocli.CommandLine.Parameters;
import picocli.CommandLine.Spec;
import picocli.CommandLine.Unmatched;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Command(name = "example", version = "3.7.0",
mixinStandardHelpOptions = true, subcommands = CommandLine.HelpCommand.class)
public class Example implements Runnable {
@Command public static class ExampleMixin {
@Option(names = "-l")
int length;
}
@Option(names = "-t")
TimeUnit timeUnit;
@Parameters(index = "0")
File file;
@Spec
CommandSpec spec;
@Mixin
ExampleMixin mixin;
@Unmatched
List<String> unmatched;
private int minimum;
private File[] otherFiles;
@Command
int multiply(@Option(names = "--count") int count,
@Parameters int multiplier) {
System.out.println("Result is " + count * multiplier);
return count * multiplier;
}
@Option(names = "--minimum")
public void setMinimum(int min) {
if (min < 0) {
throw new ParameterException(spec.commandLine(), "Minimum must be a positive integer");
}
minimum = min;
}
@Parameters(index = "1..*")
public void setOtherFiles(File[] otherFiles) {
for (File f : otherFiles) {
if (!f.exists()) {
throw new ParameterException(spec.commandLine(), "File " + f.getAbsolutePath() + " must exist");
}
}
this.otherFiles = otherFiles;
}
public void run() {
System.out.printf("timeUnit=%s, length=%s, file=%s, unmatched=%s, minimum=%s, otherFiles=%s%n",
timeUnit, mixin.length, file, unmatched, minimum, Arrays.toString(otherFiles));
}
public static void main(String[] args) {
CommandLine.run(new Example(), args);
}
}
package picocli.codegen.aot.graalvm;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import static org.junit.Assert.assertEquals;
public class ReflectionConfigGeneratorTest {
@Test
public void testMainStdOut() throws IOException {
PrintStream old = System.out;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
System.setOut(new PrintStream(baos));
try {
ReflectionConfigGenerator.main(Example.class.getName());
} finally {
System.setOut(old);
}
String expected = read("/example-reflect.json");
expected = expected.replace("\r\n", "\n");
expected = expected.replace("\n", System.getProperty("line.separator"));
assertEquals(expected, baos.toString());
}
@Test
public void testMainOutputFile() throws IOException {
File file = File.createTempFile("picocli-codegen", ".json");
ReflectionConfigGenerator.main("--output", file.getAbsolutePath(), Example.class.getName());
String expected = read("/example-reflect.json");
expected = expected.replace("\r\n", "\n");
expected = expected.replace("\n", System.getProperty("line.separator"));
String actual = readAndClose(new FileInputStream(file));
assertEquals(expected, actual);
}
private String read(String resource) throws IOException {
return readAndClose(getClass().getResourceAsStream(resource));
}
private String readAndClose(InputStream in) throws IOException {
try {
byte[] buff = new byte[15000];
int size = in.read(buff);
return new String(buff, 0, size);
} finally {
in.close();
}
}
}
[
{
"name" : "java.lang.reflect.Method",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true
},
{
"name" : "java.lang.reflect.Executable",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "getParameters", "parameterTypes" : [] }
]
},
{
"name" : "java.lang.reflect.Parameter",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "getName", "parameterTypes" : [] }
]
},
{
"name" : "java.time.Duration",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "parse", "parameterTypes" : ["java.lang.CharSequence"] }
]
},
{
"name" : "java.time.Instant",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "parse", "parameterTypes" : ["java.lang.CharSequence"] }
]
},
{
"name" : "java.time.LocalDate",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "parse", "parameterTypes" : ["java.lang.CharSequence"] }
]
},
{
"name" : "java.time.LocalDateTime",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "parse", "parameterTypes" : ["java.lang.CharSequence"] }
]
},
{
"name" : "java.time.LocalTime",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "parse", "parameterTypes" : ["java.lang.CharSequence"] }
]
},
{
"name" : "java.time.MonthDay",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "parse", "parameterTypes" : ["java.lang.CharSequence"] }
]
},
{
"name" : "java.time.OffsetDateTime",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "parse", "parameterTypes" : ["java.lang.CharSequence"] }
]
},
{
"name" : "java.time.OffsetTime",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "parse", "parameterTypes" : ["java.lang.CharSequence"] }
]
},
{
"name" : "java.time.Period",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "parse", "parameterTypes" : ["java.lang.CharSequence"] }
]
},
{
"name" : "java.time.Year",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "parse", "parameterTypes" : ["java.lang.CharSequence"] }
]
},
{
"name" : "java.time.YearMonth",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "parse", "parameterTypes" : ["java.lang.CharSequence"] }
]
},
{
"name" : "java.time.ZonedDateTime",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "parse", "parameterTypes" : ["java.lang.CharSequence"] }
]
},
{
"name" : "java.time.ZoneId",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "of", "parameterTypes" : ["java.lang.String"] }
]
},
{
"name" : "java.time.ZoneOffset",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "of", "parameterTypes" : ["java.lang.String"] }
]
},
{
"name" : "java.nio.file.Path",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true
},
{
"name" : "java.nio.file.Paths",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "get", "parameterTypes" : ["java.lang.String", "[Ljava.lang.String;"] }
]
},
{
"name" : "java.sql.Connection",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true
},
{
"name" : "java.sql.Driver",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true
},
{
"name" : "java.sql.DriverManager",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "getConnection", "parameterTypes" : ["java.lang.String"] },
{ "name" : "getDriver", "parameterTypes" : ["java.lang.String"] }
]
},
{
"name" : "java.sql.Timestamp",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "valueOf", "parameterTypes" : ["java.lang.String"] }
]
},
{
"name" : "picocli.codegen.aot.graalvm.Example",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"fields" : [
{ "name" : "spec" },
{ "name" : "mixin" },
{ "name" : "unmatched" },
{ "name" : "timeUnit" },
{ "name" : "file" }
],
"methods" : [
{ "name" : "setMinimum", "parameterTypes" : ["int"] },
{ "name" : "setOtherFiles", "parameterTypes" : ["[Ljava.io.File;"] },
{ "name" : "multiply", "parameterTypes" : ["int", "int"] }
]
},
{
"name" : "java.lang.Object",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true
},
{
"name" : "java.util.concurrent.TimeUnit",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true
},
{
"name" : "java.util.Collections$UnmodifiableRandomAccessList",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true
},
{
"name" : "picocli.codegen.aot.graalvm.Example$ExampleMixin",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"fields" : [
{ "name" : "length" }
]
},
{
"name" : "picocli.CommandLine$AutoHelpMixin",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"fields" : [
{ "name" : "helpRequested" },
{ "name" : "versionRequested" }
]
},
{
"name" : "java.io.File",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true
},
{
"name" : "[Ljava.io.File;",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true
},
{
"name" : "picocli.CommandLine$HelpCommand",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"fields" : [
{ "name" : "helpRequested" },
{ "name" : "commands" }
]
},
{
"name" : "[Ljava.lang.String;",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true
},
{
"name" : "java.lang.String",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true
}
]
description 'Java and Groovy examples for the picocli project.'
dependencies {
compile rootProject
compile "org.apache.ivy:ivy:$ivyVersion", // for Intelli/J
"org.codehaus.groovy:groovy-all:$groovyVersion"
}
def generatedResources = "$buildDir/generated-resources/main"
sourceSets {
main {
//register an output folder on the main SourceSet:
output.dir(generatedResources, builtBy: 'generateVersionTxt')
//it is now a part of the 'main' classpath and will be a part of the jar
}
}
//a task that generates the resources for the example VersionProviderDemo1:
task generateVersionTxt {
description 'Creates a version.txt file with build info that is added to the root of the picocli-examples jar'
doLast {
new File(generatedResources).mkdirs()
def generated = new File(generatedResources, "version.txt")
generated.text = """
Version: $rootProject.version
Buildtime: ${new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())}
Application-name: $rootProject.name $project.name
"""
}
}
package picocli.examples.customhelp;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Help;
import picocli.CommandLine.Help.Column;
import picocli.CommandLine.Help.Column.Overflow;
import picocli.CommandLine.Help.TextTable;
import picocli.CommandLine.IHelpSectionRenderer;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Model.UsageMessageSpec;
import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_COMMAND_LIST;
/**
* This example demonstrates how to customize a section of the usage help message.
* It replaces the standard command list with a custom list that displays
* not just the immediate subcommands but the full hierarchy of subcommands:
* <pre>
* Usage: showall [-hV] [COMMAND]
* Demonstrates a usage help message that shows not just the subcommands of this
* command, but also the nested sub-subcommands.
* -h, --help Show this help message and exit.
* -V, --version Print version information and exit.
* Commands:
* sub1 subcommand1 of showall
* sub1sub1 subcommand1 of subcommand1 of showall
* sub1sub2 subcommand2 of subcommand1 of showall
* sub2 subcommand2 of showall
* sub2sub1 subcommand1 of subcommand2 of showall
* </pre>
*
* As requested in https://github.com/remkop/picocli/issues/566
*/
@Command(name = "showall", mixinStandardHelpOptions = true,
version = "from picocli 3.9",
description = "Demonstrates a usage help message that shows " +
"not just the subcommands of this command, " +
"but also the nested sub-subcommands.",
subcommands = {Subcommand1.class, Subcommand2.class} )
public class ShowAll {
public static void main(String[] args) {
CommandLine cmd = new CommandLine(new ShowAll());
cmd.getHelpSectionMap().put(SECTION_KEY_COMMAND_LIST, new MyCommandListRenderer());
cmd.usage(System.out);
}
}
class MyCommandListRenderer implements IHelpSectionRenderer {
//@Override
public String render(Help help) {
CommandSpec spec = help.commandSpec();
if (spec.subcommands().isEmpty()) { return ""; }
// prepare layout: two columns
// the left column overflows, the right column wraps if text is too long
TextTable textTable = TextTable.forColumns(help.ansi(),
new Column(15, 2, Overflow.SPAN),
new Column(spec.usageMessage().width() - 15, 2, Overflow.WRAP));
for (CommandLine subcommand : spec.subcommands().values()) {
addHierarchy(subcommand, textTable, "");
}
return textTable.toString();
}
private void addHierarchy(CommandLine cmd, TextTable textTable, String indent) {
// create comma-separated list of command name and aliases
String names = cmd.getCommandSpec().names().toString();
names = names.substring(1, names.length() - 1); // remove leading '[' and trailing ']'
// command description is taken from header or description
String description = description(cmd.getCommandSpec().usageMessage());
// add a line for this command to the layout
textTable.addRowValues(indent + names, description);
// add its subcommands (if any)
for (CommandLine sub : cmd.getSubcommands().values()) {
addHierarchy(sub, textTable, indent + " ");
}
}
private String description(UsageMessageSpec usageMessage) {
if (usageMessage.header().length > 0) {
return usageMessage.header()[0];
}
if (usageMessage.description().length > 0) {
return usageMessage.description()[0];
}
return "";
}
}
@Command(name = "sub1", description = "subcommand1 of showall",
subcommands = {Subcommand1Sub1.class, Subcommand1Sub2.class})
class Subcommand1 {}
@Command(name = "sub2", description = "subcommand2 of showall",
subcommands = {Subcommand2Sub1.class})
class Subcommand2 {}
@Command(name = "sub1sub1", description = "subcommand1 of subcommand1 of showall")
class Subcommand1Sub1 {}
@Command(name = "sub1sub2", description = "subcommand2 of subcommand1 of showall")
class Subcommand1Sub2 {}
@Command(name = "sub2sub1", description = "subcommand1 of subcommand2 of showall")
class Subcommand2Sub1 {}
package picocli.examples.mixin;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Mixin;
import picocli.CommandLine.Option;
@Command(name = "mixee", description = "This command has a footer and an option mixed in")
public class CommandWithMixin {
@Mixin
CommonOption commonOption = new CommonOption();
@Option(names = "-y", description = "command option")
int y;
public static void main(String[] args) {
CommandWithMixin cmd = new CommandWithMixin();
new CommandLine(cmd).parseArgs("-x", "3", "-y", "4");
System.out.printf("x=%s, y=%s%n", cmd.commonOption.x, cmd.y);
}
}
package picocli.examples.mixin;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
@Command(footer = "Common footer")
public class CommonOption {
@Option(names = "-x", description = "reusable option you want in many commands")
int x;
}