You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2018/05/18 02:24:58 UTC
[2/3] groovy git commit: GROOVY-8577 Migrate
org.codehaus.groovy.tools.GrapeMain.groovy to picocli
GROOVY-8577 Migrate org.codehaus.groovy.tools.GrapeMain.groovy to picocli
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/892d8abb
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/892d8abb
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/892d8abb
Branch: refs/heads/GROOVY_2_5_X
Commit: 892d8abb11b826f376a5c9c615d8a09dd0879ccc
Parents: 45c507c
Author: Remko Popma <re...@yahoo.com>
Authored: Fri May 11 01:27:36 2018 +0900
Committer: Paul King <pa...@asert.com.au>
Committed: Fri May 18 12:24:47 2018 +1000
----------------------------------------------------------------------
.../org/codehaus/groovy/tools/GrapeMain.groovy | 533 ++++++++++---------
1 file changed, 283 insertions(+), 250 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/groovy/blob/892d8abb/src/main/groovy/org/codehaus/groovy/tools/GrapeMain.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/org/codehaus/groovy/tools/GrapeMain.groovy b/src/main/groovy/org/codehaus/groovy/tools/GrapeMain.groovy
index dd330c3..705cd26 100644
--- a/src/main/groovy/org/codehaus/groovy/tools/GrapeMain.groovy
+++ b/src/main/groovy/org/codehaus/groovy/tools/GrapeMain.groovy
@@ -19,290 +19,323 @@
package org.codehaus.groovy.tools
import groovy.grape.Grape
-import groovy.transform.Field
-import org.apache.commons.cli.CommandLine
-import org.apache.commons.cli.DefaultParser
-import org.apache.commons.cli.HelpFormatter
-import org.apache.commons.cli.Option
-import org.apache.commons.cli.OptionGroup
-import org.apache.commons.cli.Options
import org.apache.ivy.util.DefaultMessageLogger
import org.apache.ivy.util.Message
-
-//commands
-
-@Field install = {arg, cmd ->
- if (arg.size() > 5 || arg.size() < 3) {
- println 'install requires two to four arguments: <group> <module> [<version> [<classifier>]]'
- return
- }
- def ver = '*'
- if (arg.size() >= 4) {
- ver = arg[3]
- }
- def classifier = null
- if (arg.size() >= 5) {
- classifier = arg[4]
+import picocli.CommandLine
+import picocli.CommandLine.Command
+import picocli.CommandLine.Option
+import picocli.CommandLine.Parameters
+import picocli.CommandLine.ParentCommand
+import picocli.CommandLine.RunLast
+import picocli.CommandLine.Unmatched
+
+@Command(name = "grape", description = "Allows for the inspection and management of the local grape cache.",
+ subcommands = [
+ Install.class,
+ Uninstall.class,
+ ListCommand.class,
+ Resolve.class,
+ picocli.CommandLine.HelpCommand.class])
+class GrapeMain implements Runnable {
+ @Option(names = ["-D", "--define"], description = "define a system property", paramLabel = "<name=value>")
+ private Map<String, String> properties = new LinkedHashMap<String, String>()
+
+ @Option(names = ["-r", "--resolver"], description = "define a grab resolver (for install)", paramLabel = "<url>")
+ private List<String> resolvers = new ArrayList<String>()
+
+ @Option(names = ["-q", "--quiet"], description = "Log level 0 - only errors")
+ private boolean quiet
+
+ @Option(names = ["-w", "--warn"], description = "Log level 1 - errors and warnings")
+ private boolean warn
+
+ @Option(names = ["-i", "--info"], description = "Log level 2 - info")
+ private boolean info
+
+ @Option(names = ["-V", "--verbose"], description = "Log level 3 - verbose")
+ private boolean verbose
+
+ @Option(names = ["-d", "--debug"], description = "Log level 4 - debug")
+ private boolean debug
+
+ @Unmatched List<String> unmatched = new ArrayList<String>()
+
+ private CommandLine parser
+
+ public static void main(String[] args) {
+ GrapeMain grape = new GrapeMain()
+ def parser = new CommandLine(grape)
+ parser.addMixin("helpOptions", new HelpOptionsMixin())
+ parser.subcommands.findAll { k, v -> k != 'help' }.each { k, v -> v.addMixin("helpOptions", new HelpOptionsMixin()) }
+
+ grape.parser = parser
+ parser.parseWithHandler(new RunLast(), args)
}
- // set the instance so we can re-set the logger
- Grape.getInstance()
- setupLogging()
+ void run() {
+ if (unmatched) {
+ System.err.println "grape: '${unmatched[0]}' is not a grape command. See 'grape --help'"
+ } else {
+ parser.usage(System.out) // if no subcommand was specified
+ }
+ }
- cmd.getOptionValues('r')?.each { String url ->
- Grape.addResolver(name:url, root:url)
+ private void init() {
+ properties.each { k, v ->
+ System.setProperty(k, v)
+ }
}
- try {
- Grape.grab(autoDownload: true, group: arg[1], module: arg[2], version: ver, classifier: classifier, noExceptions: true)
- } catch (Exception e) {
- println "An error occured : $ex"
+ private void setupLogging(int defaultLevel = 2) { // = Message.MSG_INFO -> some parsing error :(
+ if (quiet) {
+ Message.setDefaultLogger(new DefaultMessageLogger(Message.MSG_ERR))
+ } else if (warn) {
+ Message.setDefaultLogger(new DefaultMessageLogger(Message.MSG_WARN))
+ } else if (info) {
+ Message.setDefaultLogger(new DefaultMessageLogger(Message.MSG_INFO))
+ } else if (verbose) {
+ Message.setDefaultLogger(new DefaultMessageLogger(Message.MSG_VERBOSE))
+ } else if (debug) {
+ Message.setDefaultLogger(new DefaultMessageLogger(Message.MSG_DEBUG))
+ } else {
+ Message.setDefaultLogger(new DefaultMessageLogger(defaultLevel))
+ }
}
-}
-@Field uninstall = {arg, cmd ->
- if (arg.size() != 4) {
- println 'uninstall requires three arguments: <group> <module> <version>'
- // TODO make version optional? support classifier?
-// println 'uninstall requires two to four arguments, <group> <module> [<version>] [<classifier>]'
- return
+ /**
+ * Defines help options (--help and --version) and a version provider used by the top-level command and all subcommands.
+ * Intended to be installed as a picocli mixin.
+ */
+ // IMPLEMENTATION NOTE:
+ // The @Command(mixinStandardHelpOptions = true) attribute cannot be used because
+ // the unix standard short option for version help is uppercase -V, while previous versions
+ // of this class use lowercase -v. This custom mixin preserves option compatibility.
+ @Command(versionProvider = VersionProvider.class, sortOptions = false,
+ parameterListHeading = "%nParameters:%n",
+ optionListHeading = "%nOptions:%n",
+ descriptionHeading = "%n")
+ private static class HelpOptionsMixin {
+ @Option(names = ["-h", "--help"], description = "usage information") boolean isHelpRequested
+ @Option(names = ["-v", "--version"], description = "display the Groovy and JVM versions") boolean isVersionRequested
}
- String group = arg[1]
- String module = arg[2]
- String ver = arg[3]
-// def classifier = null
-
- // set the instance so we can re-set the logger
- Grape.getInstance()
- setupLogging()
-
- if (!Grape.enumerateGrapes().find {String groupName, Map g ->
- g.any {String moduleName, List<String> versions ->
- group == groupName && module == moduleName && ver in versions
- }
- }) {
- println "uninstall did not find grape matching: $group $module $ver"
- def fuzzyMatches = Grape.enumerateGrapes().findAll { String groupName, Map g ->
- g.any {String moduleName, List<String> versions ->
- groupName.contains(group) || moduleName.contains(module) ||
- group.contains(groupName) || module.contains(moduleName)
- }
- }
- if (fuzzyMatches) {
- println 'possible matches:'
- fuzzyMatches.each { String groupName, Map g -> println " $groupName: $g" }
+
+ private static class VersionProvider implements CommandLine.IVersionProvider {
+ String[] getVersion() {
+ String version = GroovySystem.getVersion()
+ return "Groovy Version: $version JVM: ${System.getProperty('java.version')}"
}
- return
}
- Grape.instance.uninstallArtifact(group, module, ver)
-}
-@Field list = {arg, cmd ->
- println ""
+ @Command(name = 'install', header = 'Installs a particular grape',
+ synopsisHeading = 'Usage: grape ',
+ description = 'Installs the specified groovy module or maven artifact. If a version is specified that specific version will be installed, otherwise the most recent version will be used (as if `*` was passed in).')
+ private static class Install implements Runnable {
+ @Parameters(index = '0', arity = '1', description = 'Which module group the module comes from. Translates directly to a Maven groupId or an Ivy Organization. Any group matching /groovy[x][\\..*]^/ is reserved and may have special meaning to the groovy endorsed modules.')
+ String group
- int moduleCount = 0
- int versionCount = 0
+ @Parameters(index = '1', arity = '1', description = 'The name of the module to load. Translated directly to a Maven artifactId or an Ivy artifact.')
+ String module
- // set the instance so we can re-set the logger
- Grape.getInstance()
- setupLogging()
+ @Parameters(index = '2', arity = '0..1', description = 'The version of the module to use. Either a literal version `1.1-RC3` or an Ivy Range `[2.2.1,)` meaning 2.2.1 or any greater version).')
+ String version = '*'
- Grape.enumerateGrapes().each {String groupName, Map group ->
- group.each {String moduleName, List<String> versions ->
- println "$groupName $moduleName $versions"
- moduleCount++
- versionCount += versions.size()
- }
- }
- println ""
- println "$moduleCount Grape modules cached"
- println "$versionCount Grape module versions cached"
-}
+ @Parameters(index = '3', arity = '0..1', description = 'The optional classifier to use (for example, jdk15).')
+ String classifier
-@Field resolve = {arg, cmd ->
- Options options = new Options();
- options.addOption(Option.builder("a").hasArg(false).longOpt("ant").build());
- options.addOption(Option.builder("d").hasArg(false).longOpt("dos").build());
- options.addOption(Option.builder("s").hasArg(false).longOpt("shell").build());
- options.addOption(Option.builder("i").hasArg(false).longOpt("ivy").build());
- CommandLine cmd2 = new DefaultParser().parse(options, arg[1..-1] as String[], true);
- arg = cmd2.args
-
- // set the instance so we can re-set the logger
- Grape.getInstance()
- setupLogging(Message.MSG_ERR)
-
- if ((arg.size() % 3) != 0) {
- println 'There needs to be a multiple of three arguments: (group module version)+'
- return
- }
- if (args.size() < 3) {
- println 'At least one Grape reference is required'
- return
- }
- def before, between, after
- def ivyFormatRequested = false
-
- if (cmd2.hasOption('a')) {
- before = '<pathelement location="'
- between = '">\n<pathelement location="'
- after = '">'
- } else if (cmd2.hasOption('d')) {
- before = 'set CLASSPATH='
- between = ';'
- after = ''
- } else if (cmd2.hasOption('s')) {
- before = 'export CLASSPATH='
- between = ':'
- after = ''
- } else if (cmd2.hasOption('i')) {
- ivyFormatRequested = true
- before = '<dependency '
- between = '">\n<dependency '
- after = '">'
- } else {
- before = ''
- between = '\n'
- after = '\n'
- }
+ @ParentCommand GrapeMain parentCommand
- iter = arg.iterator()
- def params = [[:]]
- def depsInfo = [] // this list will contain the module/group/version info of all resolved dependencies
- if(ivyFormatRequested) {
- params << depsInfo
- }
- while (iter.hasNext()) {
- params.add([group: iter.next(), module: iter.next(), version: iter.next()])
- }
- try {
- def results = []
- def uris = Grape.resolve(* params)
- if(!ivyFormatRequested) {
- for (URI uri: uris) {
- if (uri.scheme == 'file') {
- results += new File(uri).path
- } else {
- results += uri.toASCIIString()
- }
+ void run() {
+ parentCommand.init()
+
+ // set the instance so we can re-set the logger
+ Grape.getInstance()
+ parentCommand.setupLogging()
+
+ parentCommand.resolvers.each { String url ->
+ Grape.addResolver(name:url, root:url)
}
- } else {
- depsInfo.each { dep ->
- results += ('org="' + dep.group + '" name="' + dep.module + '" revision="' + dep.revision)
+
+ try {
+ Grape.grab(autoDownload: true, group: group, module: module, version: version, classifier: classifier, noExceptions: true)
+ } catch (Exception ex) {
+ println "An error occured : $ex"
}
}
+ }
- if (results) {
- println "${before}${results.join(between)}${after}"
- } else {
- println 'Nothing was resolved'
+ @Command(name = 'list', header = 'Lists all installed grapes',
+ synopsisHeading = 'Usage: grape ',
+ description = 'Lists locally installed modules (with their full maven name in the case of groovy modules) and versions.')
+ private static class ListCommand implements Runnable {
+
+ @ParentCommand GrapeMain parentCommand
+
+ void run() {
+ parentCommand.init()
+
+ println ""
+
+ int moduleCount = 0
+ int versionCount = 0
+
+ // set the instance so we can re-set the logger
+ Grape.getInstance()
+ parentCommand.setupLogging()
+
+ Grape.enumerateGrapes().each {String groupName, Map group ->
+ group.each {String moduleName, List<String> versions ->
+ println "$groupName $moduleName $versions"
+ moduleCount++
+ versionCount += versions.size()
+ }
+ }
+ println ""
+ println "$moduleCount Grape modules cached"
+ println "$versionCount Grape module versions cached"
}
- } catch (Exception e) {
- println "Error in resolve:\n\t$e.message"
- if (e.message =~ /unresolved dependency/) println "Perhaps the grape is not installed?"
}
-}
-@Field help = { arg, cmd -> grapeHelp() }
-
-@Field commands = [
- 'install': [closure: install,
- shortHelp: 'Installs a particular grape'],
- 'uninstall': [closure: uninstall,
- shortHelp: 'Uninstalls a particular grape (non-transitively removes the respective jar file from the grape cache)'],
- 'list': [closure: list,
- shortHelp: 'Lists all installed grapes'],
- 'resolve': [closure: resolve,
- shortHelp: 'Enumerates the jars used by a grape'],
- 'help': [closure: help,
- shortHelp: 'Usage information']
-]
-
-@Field grapeHelp = {
- int spacesLen = commands.keySet().max {it.length()}.length() + 3
- String spaces = ' ' * spacesLen
-
- PrintWriter pw = new PrintWriter(binding.variables.out ?: System.out)
- new HelpFormatter().printHelp(
- pw,
- 80,
- "grape [options] <command> [args]\n",
- "options:",
- options,
- 2,
- 4,
- null, // footer
- true);
- pw.flush()
-
- println ""
- println "commands:"
- commands.each {String k, v ->
- println " ${(k + spaces).substring(0, spacesLen)} $v.shortHelp"
- }
- println ""
-}
+ @Command(name = 'resolve', header = 'Enumerates the jars used by a grape',
+ synopsisHeading = 'Usage: grape ',
+ description = 'Prints the file locations of the jars representing the artifcats for the specified module(s) and the respective transitive dependencies.')
+ private static class Resolve implements Runnable {
-@Field setupLogging = {int defaultLevel = 2 -> // = Message.MSG_INFO -> some parsing error :(
- if (cmd.hasOption('q')) {
- Message.setDefaultLogger(new DefaultMessageLogger(Message.MSG_ERR))
- } else if (cmd.hasOption('w')) {
- Message.setDefaultLogger(new DefaultMessageLogger(Message.MSG_WARN))
- } else if (cmd.hasOption('i')) {
- Message.setDefaultLogger(new DefaultMessageLogger(Message.MSG_INFO))
- } else if (cmd.hasOption('V')) {
- Message.setDefaultLogger(new DefaultMessageLogger(Message.MSG_VERBOSE))
- } else if (cmd.hasOption('d')) {
- Message.setDefaultLogger(new DefaultMessageLogger(Message.MSG_DEBUG))
- } else {
- Message.setDefaultLogger(new DefaultMessageLogger(defaultLevel))
- }
-}
+ @Option(names = ['-a', '--ant'], description = 'Express dependencies in a format applicable for an ant script')
+ private boolean ant
-// command line parsing
-@Field Options options = new Options();
+ @Option(names = ['-d', '--dos'], description = 'Express dependencies in a format applicable for a windows batch file')
+ private boolean dos
-options.addOption(Option.builder("D").longOpt("define").desc("define a system property").numberOfArgs(2).valueSeparator().argName("name=value").build());
-options.addOption(Option.builder("r").longOpt("resolver").desc("define a grab resolver (for install)").hasArg(true).argName("url").build());
-options.addOption(Option.builder("h").hasArg(false).desc("usage information").longOpt("help").build());
+ @Option(names = ['-s', '--shell'], description = 'Express dependencies in a format applicable for a unix shell script')
+ private boolean shell
-// Logging Level Options
-options.addOptionGroup(
- new OptionGroup()
- .addOption(Option.builder("q").hasArg(false).desc("Log level 0 - only errors").longOpt("quiet").build())
- .addOption(Option.builder("w").hasArg(false).desc("Log level 1 - errors and warnings").longOpt("warn").build())
- .addOption(Option.builder("i").hasArg(false).desc("Log level 2 - info").longOpt("info").build())
- .addOption(Option.builder("V").hasArg(false).desc("Log level 3 - verbose").longOpt("verbose").build())
- .addOption(Option.builder("d").hasArg(false).desc("Log level 4 - debug").longOpt("debug").build())
-)
-options.addOption(Option.builder("v").hasArg(false).desc("display the Groovy and JVM versions").longOpt("version").build());
+ @Option(names = ['-i', '--ivy'], description = 'Express dependencies in an ivy-like format')
+ private boolean ivyFormatRequested
-@Field CommandLine cmd
+ @ParentCommand GrapeMain parentCommand
-cmd = new DefaultParser().parse(options, args, true);
+ void run() {
+ parentCommand.init()
-if (cmd.hasOption('h')) {
- grapeHelp()
- return
-}
+ // set the instance so we can re-set the logger
+ Grape.getInstance()
+ parentCommand.setupLogging(Message.MSG_ERR)
-if (cmd.hasOption('v')) {
- String version = GroovySystem.getVersion();
- println "Groovy Version: $version JVM: ${System.getProperty('java.version')}"
- return
-}
+ if ((arg.size() % 3) != 0) {
+ println 'There needs to be a multiple of three arguments: (group module version)+'
+ return
+ }
+ if (args.size() < 3) {
+ println 'At least one Grape reference is required'
+ return
+ }
+ def before, between, after
+
+ if (ant) {
+ before = '<pathelement location="'
+ between = '">\n<pathelement location="'
+ after = '">'
+ } else if (dos) {
+ before = 'set CLASSPATH='
+ between = ';'
+ after = ''
+ } else if (shell) {
+ before = 'export CLASSPATH='
+ between = ':'
+ after = ''
+ } else if (ivy) {
+ before = '<dependency '
+ between = '">\n<dependency '
+ after = '">'
+ } else {
+ before = ''
+ between = '\n'
+ after = '\n'
+ }
-if (options.hasOption('D')) {
- cmd.getOptionProperties('D')?.each { k, v ->
- System.setProperty(k, v)
+ iter = arg.iterator()
+ def params = [[:]]
+ def depsInfo = [] // this list will contain the module/group/version info of all resolved dependencies
+ if (ivyFormatRequested) {
+ params << depsInfo
+ }
+ while (iter.hasNext()) {
+ params.add([group: iter.next(), module: iter.next(), version: iter.next()])
+ }
+ try {
+ def results = []
+ def uris = Grape.resolve(* params)
+ if(!ivyFormatRequested) {
+ for (URI uri: uris) {
+ if (uri.scheme == 'file') {
+ results += new File(uri).path
+ } else {
+ results += uri.toASCIIString()
+ }
+ }
+ } else {
+ depsInfo.each { dep ->
+ results += ('org="' + dep.group + '" name="' + dep.module + '" revision="' + dep.revision)
+ }
+ }
+
+ if (results) {
+ println "${before}${results.join(between)}${after}"
+ } else {
+ println 'Nothing was resolved'
+ }
+ } catch (Exception e) {
+ println "Error in resolve:\n\t$e.message"
+ if (e.message =~ /unresolved dependency/) println "Perhaps the grape is not installed?"
+ }
+ }
}
-}
-String[] arg = cmd.args
-if (arg?.length == 0) {
- grapeHelp()
-} else if (commands.containsKey(arg[0])) {
- commands[arg[0]].closure(arg, cmd)
-} else {
- println "grape: '${arg[0]}' is not a grape command. See 'grape --help'"
+ @Command(name = 'uninstall',
+ synopsisHeading = 'Usage: grape ',
+ description = 'Uninstalls a particular grape (non-transitively removes the respective jar file from the grape cache).')
+ private static class Uninstall implements Runnable {
+ @Parameters(index = '0', arity = '1', description = 'Which module group the module comes from. Translates directly to a Maven groupId or an Ivy Organization. Any group matching /groovy[x][\\..*]^/ is reserved and may have special meaning to the groovy endorsed modules.')
+ String group
+
+ @Parameters(index = '1', arity = '1', description = 'The name of the module to load. Translated directly to a Maven artifactId or an Ivy artifact.')
+ String module
+
+ @Parameters(index = '2', arity = '1', description = 'The version of the module to use. Either a literal version `1.1-RC3` or an Ivy Range `[2.2.1,)` meaning 2.2.1 or any greater version).')
+ String version
+
+ // TODO make version optional? support classifier?
+ //@Parameters(index = '3', arity = '0..1', description = 'The optional classifier to use (for example, jdk15).')
+ //String classifier;
+
+ @ParentCommand GrapeMain parentCommand
+
+ void run() {
+ parentCommand.init()
+
+ // set the instance so we can re-set the logger
+ Grape.getInstance()
+ parentCommand.setupLogging()
+
+ if (!Grape.enumerateGrapes().find {String groupName, Map g ->
+ g.any {String moduleName, List<String> versions ->
+ group == groupName && module == moduleName && version in versions
+ }
+ }) {
+ println "uninstall did not find grape matching: $group $module $version"
+ def fuzzyMatches = Grape.enumerateGrapes().findAll { String groupName, Map g ->
+ g.any {String moduleName, List<String> versions ->
+ groupName.contains(group) || moduleName.contains(module) ||
+ group.contains(groupName) || module.contains(moduleName)
+ }
+ }
+ if (fuzzyMatches) {
+ println 'possible matches:'
+ fuzzyMatches.each { String groupName, Map g -> println " $groupName: $g" }
+ }
+ return
+ }
+ Grape.instance.uninstallArtifact(group, module, version)
+ }
+ }
}