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 01:16:56 UTC

groovy git commit: GROOVY-8577 Migrate org.codehaus.groovy.tools.GrapeMain.groovy to picocli

Repository: groovy
Updated Branches:
  refs/heads/master 25a414f82 -> ff8d74741


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/ff8d7474
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/ff8d7474
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/ff8d7474

Branch: refs/heads/master
Commit: ff8d74741d87133be0a49710470d4b6e23ab1f7e
Parents: 25a414f
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 09:46:15 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/ff8d7474/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)
+        }
+    }
 }