0
# Groovy CLI Picocli
1
2
Groovy CLI Picocli is a command line interface builder for Groovy applications that integrates with the picocli framework. It provides both dynamic API and annotation-based approaches for parsing command line arguments, handling option validation, generating help messages, and accessing parsed values through a convenient interface.
3
4
## Package Information
5
6
- **Package Name**: org.apache.groovy:groovy-cli-picocli
7
- **Package Type**: maven
8
- **Language**: Groovy/Java
9
- **Installation**: Add dependency to your Gradle build script:
10
11
```groovy
12
dependencies {
13
implementation 'org.apache.groovy:groovy-cli-picocli:5.0.0'
14
}
15
```
16
17
## Core Imports
18
19
```groovy { .api }
20
import groovy.cli.picocli.CliBuilder
21
import groovy.cli.picocli.OptionAccessor
22
import groovy.cli.Option
23
import groovy.cli.Unparsed
24
import groovy.cli.TypedOption
25
import groovy.cli.CliBuilderException
26
```
27
28
## Basic Usage
29
30
### Dynamic API Style
31
32
```groovy
33
import groovy.cli.picocli.CliBuilder
34
35
def cli = new CliBuilder(name: 'myapp')
36
cli.h(longOpt: 'help', 'Show usage information')
37
cli.v(longOpt: 'verbose', 'Enable verbose output')
38
cli.f(longOpt: 'file', args: 1, argName: 'filename', 'Input file to process')
39
40
def options = cli.parse(args)
41
if (!options) {
42
// Parse failed, error message already printed
43
return
44
}
45
46
if (options.h) {
47
cli.usage()
48
return
49
}
50
51
println "Verbose: ${options.v}"
52
println "File: ${options.f}"
53
println "Arguments: ${options.arguments()}"
54
```
55
56
### Annotation-Based Interface Style
57
58
```groovy
59
import groovy.cli.picocli.CliBuilder
60
import groovy.cli.Option
61
import groovy.cli.Unparsed
62
63
interface MyOptions {
64
@Option(shortName='h', description='Show usage information')
65
boolean help()
66
67
@Option(shortName='v', description='Enable verbose output')
68
boolean verbose()
69
70
@Option(shortName='f', description='Input file to process')
71
String file()
72
73
@Unparsed
74
List<String> remaining()
75
}
76
77
def cli = new CliBuilder()
78
def options = cli.parseFromSpec(MyOptions, args)
79
if (options.help()) {
80
cli.usage()
81
}
82
```
83
84
### Annotation-Based Class Style
85
86
```groovy
87
import groovy.cli.picocli.CliBuilder
88
import groovy.cli.Option
89
import groovy.cli.Unparsed
90
91
class MyOptions {
92
@Option(shortName='h', description='Show usage information')
93
boolean help
94
95
@Option(shortName='v', description='Enable verbose output')
96
boolean verbose
97
98
@Option(shortName='f', description='Input file to process')
99
String file
100
101
@Unparsed
102
List<String> remaining
103
}
104
105
def cli = new CliBuilder()
106
def options = new MyOptions()
107
cli.parseFromInstance(options, args)
108
```
109
110
## Architecture
111
112
The Groovy CLI Picocli library is built around several key components:
113
114
- **CliBuilder**: The main builder class providing both dynamic API and annotation-based parsing
115
- **OptionAccessor**: Interface for accessing parsed command line options and arguments
116
- **Annotation System**: `@Option` and `@Unparsed` annotations for declarative option definition
117
- **Picocli Integration**: Leverages the robust picocli library for underlying parsing logic
118
- **Type System**: Support for automatic type conversion and custom converters
119
120
## Capabilities
121
122
### CLI Builder
123
124
The main command line builder class for creating CLI parsers with flexible configuration options.
125
126
```groovy { .api }
127
class CliBuilder {
128
// Constructor
129
CliBuilder()
130
CliBuilder(Map args)
131
132
// Configuration Properties
133
String usage // Command synopsis displayed in usage help
134
String name // Program name displayed in synopsis
135
boolean posix // Enable/disable POSIX short option clustering (default: true)
136
boolean expandArgumentFiles // Whether @-file arguments are expanded (default: true)
137
boolean stopAtNonOption // How to handle non-option arguments (default: true)
138
boolean acceptLongOptionsWithSingleHyphen // Accept long options with single hyphen (default: false)
139
PrintWriter writer // Writer for usage help messages (default: System.out)
140
PrintWriter errorWriter // Writer for error messages (default: System.err)
141
String header // Additional message displayed after usage summary
142
String footer // Additional message displayed after options
143
int width // Usage message width
144
ParserSpec parser // Fine-grained parser behaviour control
145
UsageMessageSpec usageMessage // Fine-grained usage message control
146
147
// Parsing Methods
148
OptionAccessor parse(String[] args)
149
OptionAccessor parse(List args)
150
<T> T parseFromSpec(Class<T> optionsClass, String[] args)
151
<T> T parseFromInstance(T optionInstance, String[] args)
152
153
// Option Definition (Dynamic API)
154
TypedOption option(Map args, Class type, String description)
155
def invokeMethod(String name, Object args) // Dynamic method for option definition
156
157
// Utility Methods
158
void usage() // Print usage message
159
void setUsage(String usage)
160
void setFooter(String footer)
161
void setHeader(String header)
162
void setWidth(int width)
163
}
164
```
165
166
**Dynamic Option Definition Examples:**
167
168
```groovy
169
// Short option with description
170
cli.h('Show help')
171
172
// Short option with long option and description
173
cli.h(longOpt: 'help', 'Show help')
174
175
// Option with argument
176
cli.f(longOpt: 'file', args: 1, argName: 'filename', 'Input file')
177
178
// Option with multiple arguments
179
cli.lib(args: 3, valueSeparator: ',', 'Library paths')
180
181
// Option with type conversion
182
cli.port(type: Integer, 'Port number')
183
184
// Option with Map type for key=value pairs
185
cli.D(type: Map, argName: 'property=value', 'System properties')
186
187
// Long-only option (no short name)
188
cli._(longOpt: 'verbose', 'Enable verbose output')
189
```
190
191
### Option Accessor
192
193
Provides access to parsed command line options and arguments with type-safe value retrieval.
194
195
```groovy { .api }
196
class OptionAccessor {
197
ParseResult parseResult // Underlying picocli parse result
198
199
// Constructor
200
OptionAccessor(ParseResult parseResult)
201
202
// Option Value Access
203
boolean hasOption(TypedOption typedOption)
204
<T> T getOptionValue(TypedOption<T> typedOption)
205
<T> T getOptionValue(TypedOption<T> typedOption, T defaultValue)
206
<T> T getAt(TypedOption<T> typedOption) // Alternative syntax []
207
<T> T getAt(TypedOption<T> typedOption, T defaultValue)
208
Properties getOptionProperties(String name) // For Map-type options
209
<T> T defaultValue(String name) // Get default value for option name
210
211
// Argument Access
212
List<String> arguments() // Get remaining non-option arguments
213
214
// Dynamic Property Access
215
def invokeMethod(String name, Object args) // Dynamic method dispatch
216
def getProperty(String name) // Dynamic property access
217
}
218
```
219
220
**Usage Examples:**
221
222
```groovy
223
def cli = new CliBuilder()
224
def helpOption = cli.h(longOpt: 'help', 'Show help')
225
def fileOption = cli.f(longOpt: 'file', args: 1, 'Input file')
226
227
def options = cli.parse(args)
228
229
// Check if option was specified
230
if (options.hasOption(helpOption)) {
231
cli.usage()
232
}
233
234
// Get option value with type safety
235
String filename = options.getOptionValue(fileOption)
236
String filenameWithDefault = options.getOptionValue(fileOption, "default.txt")
237
238
// Alternative bracket syntax
239
String filename2 = options[fileOption]
240
String filename3 = options[fileOption, "default.txt"]
241
242
// Dynamic property access (for simple cases)
243
boolean help = options.h
244
String file = options.f
245
246
// Get remaining arguments
247
List<String> remaining = options.arguments()
248
249
// Access Map-type option properties (for -D key=value style options)
250
Properties props = options.getOptionProperties("D")
251
String value = props.getProperty("key")
252
253
// Get default value for an option
254
String defaultFile = options.defaultValue("file") // Returns default if specified
255
```
256
257
### Option Annotation
258
259
Marks methods or fields as CLI options in annotation-based usage with comprehensive configuration options.
260
261
```groovy { .api }
262
@interface Option {
263
String description() default "" // Description of the option
264
String shortName() default "" // Short name (single character)
265
String longName() default "" // Long name (multi-character)
266
String valueSeparator() default "" // Value separator for multivalued options
267
boolean optionalArg() default false // Whether option has optional argument
268
int numberOfArguments() default 1 // Number of arguments
269
String numberOfArgumentsString() default "" // Number of arguments as string ('+', '*')
270
String defaultValue() default "" // Default value as string
271
Class convert() default Undefined.class // Custom conversion closure class
272
}
273
```
274
275
**Target**: Methods, Fields
276
277
**Usage Examples:**
278
279
```groovy
280
interface MyOptions {
281
@Option(shortName='h', longName='help', description='Show usage information')
282
boolean help()
283
284
@Option(shortName='v', description='Enable verbose output')
285
boolean verbose()
286
287
@Option(shortName='f', longName='file', description='Input file to process')
288
String file()
289
290
@Option(shortName='p', longName='port', description='Port number', defaultValue='8080')
291
int port()
292
293
@Option(longName='libs', valueSeparator=',', numberOfArgumentsString='+',
294
description='Comma-separated library paths')
295
String[] libraries()
296
297
@Option(shortName='D', numberOfArgumentsString='*',
298
description='System properties as key=value pairs')
299
Map<String, String> properties()
300
}
301
302
class MyOptions {
303
@Option(shortName='h', description='Show help')
304
boolean help
305
306
@Option(shortName='f', description='Input file')
307
String file
308
309
@Option(shortName='c', description='Count', defaultValue='1')
310
int count
311
}
312
```
313
314
### Unparsed Annotation
315
316
Marks methods or fields to contain remaining non-option arguments after parsing.
317
318
```groovy { .api }
319
@interface Unparsed {
320
String description() default "ARGUMENTS" // Description for remaining arguments
321
}
322
```
323
324
**Target**: Methods, Fields
325
326
**Usage Examples:**
327
328
```groovy
329
interface MyOptions {
330
@Option(shortName='v', description='Verbose output')
331
boolean verbose()
332
333
@Unparsed(description='Input files to process')
334
List<String> files()
335
}
336
337
class MyOptions {
338
@Option(shortName='v', description='Verbose output')
339
boolean verbose
340
341
@Unparsed
342
List<String> remaining
343
}
344
```
345
346
### Typed Option
347
348
Container for typed option information that extends HashMap and provides access to default values.
349
350
```groovy { .api }
351
class TypedOption<T> extends HashMap<String, Object> {
352
// Constructor
353
TypedOption()
354
355
// Methods
356
T defaultValue() // Get default value for the option
357
358
// Properties (inherited from HashMap and option definition)
359
String opt // Short option name
360
String longOpt // Long option name
361
Class<T> type // Option value type
362
String description // Option description
363
Object cliOption // Reference to underlying picocli OptionSpec
364
Closure convert // Custom conversion closure if specified
365
366
// Inherits all HashMap methods:
367
// put(String, Object), get(String), containsKey(String), etc.
368
}
369
```
370
371
**Usage Example:**
372
373
```groovy
374
def cli = new CliBuilder()
375
def portOption = cli.port(type: Integer, defaultValue: '8080', 'Port number')
376
377
// Access default value
378
Integer defaultPort = portOption.defaultValue() // Returns 8080
379
380
// Use in parsing
381
def options = cli.parse(args)
382
Integer port = options.getOptionValue(portOption, portOption.defaultValue())
383
```
384
385
### CLI Builder Exception
386
387
Exception thrown for CLI builder configuration errors and validation issues.
388
389
```groovy { .api }
390
class CliBuilderException extends RuntimeException {
391
// Constructor (inherits all RuntimeException constructors)
392
CliBuilderException()
393
CliBuilderException(String message)
394
CliBuilderException(String message, Throwable cause)
395
CliBuilderException(Throwable cause)
396
}
397
```
398
399
## Types
400
401
```groovy { .api }
402
// Import types from picocli for advanced configuration
403
import picocli.CommandLine.Model.ParserSpec
404
import picocli.CommandLine.Model.UsageMessageSpec
405
import picocli.CommandLine.ParseResult
406
407
// Groovy transform for annotation defaults
408
import groovy.transform.Undefined
409
```
410
411
## Error Handling
412
413
- **Parse Errors**: The `parse()` method returns `null` when parsing fails, with error messages written to the error writer (default: System.err)
414
- **Configuration Errors**: `CliBuilderException` is thrown for invalid option configurations or builder setup issues
415
- **Type Conversion Errors**: Automatic type conversion failures are reported as parse errors
416
- **Validation Errors**: Option validation failures (e.g., missing required arguments) are reported as parse errors
417
418
**Example Error Handling:**
419
420
```groovy
421
import groovy.cli.picocli.CliBuilder
422
import groovy.cli.CliBuilderException
423
424
try {
425
def cli = new CliBuilder()
426
cli.h('Help')
427
cli.f(args: 1, 'File name')
428
429
def options = cli.parse(args)
430
if (!options) {
431
// Parse failed, error already printed to System.err
432
System.exit(1)
433
}
434
435
// Safe to use options here
436
if (options.h) {
437
cli.usage()
438
}
439
} catch (CliBuilderException e) {
440
System.err.println("Configuration error: ${e.message}")
441
System.exit(2)
442
}
443
```
444
445
## Advanced Features
446
447
### Custom Type Converters
448
449
```groovy
450
// Using convert closure for custom type conversion
451
cli.date(convert: { String input ->
452
Date.parse('yyyy-MM-dd', input)
453
}, 'Date in yyyy-MM-dd format')
454
```
455
456
### Parser Configuration
457
458
```groovy
459
def cli = new CliBuilder(
460
name: 'myapp',
461
posix: false, // Disable POSIX clustering
462
expandArgumentFiles: true, // Enable @file expansion
463
stopAtNonOption: false, // Continue parsing after non-options
464
acceptLongOptionsWithSingleHyphen: true // Accept --option and -option
465
)
466
```
467
468
### Usage Message Customization
469
470
```groovy
471
def cli = new CliBuilder(
472
usage: 'myapp [options] files...',
473
header: 'Process files with various options:',
474
footer: 'Examples:\n myapp -v file1.txt file2.txt\n myapp --help',
475
width: 120
476
)
477
```