标签:
在本文中将会涉及到:
也许您写了一些有趣或实用的 Groovy 脚本并希望与您的朋友分享,可是您并不想强迫他们安装 Groovy,所以您也许想要做一个小工具让您的朋友们能够用它编译您的 Groovy 脚本并且生成一个可执行的 Jar 包。本文将介绍如何制作一个小工具让您的 Groovy 脚本能够在没有安装 Groovy 的环境下也能被即时编译和打包成为可执行的 Jar,并通过此过程介绍 Groovy 的一些有趣的特性。
在本文中,我们将设计并实现一个命令行工具,并通过它来完成对 Groovy 脚本的即时编译和打包。我们将使用 CliBuilder 来实现程序对命令行的处理;使用 AntBuilder 来处理打包的问题;使用 FileSystemCompiler 类来实现对 Groovy 脚本文件的编译。
通过本示例了解 Groovy 中 CliBuilder 的使用方法,以及 AntBuilder 在 Groovy 中的应用。
由于 Eclipse IDE 和 Groovy 语言都是跨平台的,所以您可以在任何平台上编写本示例中的程序,并将它运行在任何平台上。
用 Groovy 来编写脚本是开发跨平台工具的一个不错的途径。随着脚本复杂程度的不断增长,您可能需要在您的脚本中处理命令行选项。而处理这些选项或参数并且根据情况显示相应的帮助信息可能会是件麻烦事。 Groovy 捆绑了 Apache Commons CLI 库作为它的一部分,然而它同时也提供了一个使用起来简单得多的 CliBuilder。那么,接下来我们看看该如何使用它。
// 创建 CliBuilder 实例,并定义命令行选项 def cmdline = new CliBuilder(usage: ‘GroovyPackman -[chflms] [date] [prefix]‘) cmdline.h( longOpt: ‘help‘, required: false, ‘show usage information‘ ) cmdline.d( longOpt: ‘destfile‘, argName: ‘destfile‘, required: false, args: 1, ‘jar destintation filename‘ ) cmdline.m( longOpt: ‘mainclass‘, argName: ‘mainclass‘, required: true, args: 1, ‘fully qualified main class‘ ) cmdline.c( longOpt: ‘groovyc‘, required: false, ‘Run groovyc‘ ) cmdline.s( longOpt: ‘sourcepath‘, argName: ‘sourcepath‘, required: true, args: 1, ‘fully path to the mainclass. Must be specified with .groovy file path when -c option used.‘)
在以上代码段中,我们首先创建 CliBuilder 的实例。CliBuilder 拥有一个叫做 Usage 的属性,可以用它来显示程序的使用说明等信息。在这里,它作为参数传递给构造函数将可以告诉程序的使用者有哪些选项可以使用。接着,我们使用名字为一个字母的方法来定义命令行选项,longOpt 属性允许选项被指定为 -h 或 --help 均可。argName 属性用来指定在使用说明中显示的选项的参数名。而 args 属性用来指定选项所需的参数个数。required 属性用来告诉命令行解析器当前选项是否是必须的。函数中最后一个参数用来指定该选项的描述信息。
Groovy 语言中闭包(closure)是一个非常重要的概念,它更象是一个“代码块”或者方法指针,代码在某处被定义然后在其后的调用处执行。关于闭包本文不多做介绍,更多详细内容请参考以下文章:
实战 Groovy: 使用闭包、ExpandoMetaClass 和类别进行元编程
GroovyDoc 中对 with(Object self, Closure closure) 方法的描述
Groovy 为我们提供了 with 方法,它允许闭包被对象的引用本身所调用,这是通过把对象赋值给闭包的 delegate 属性并且作为参数传递给闭包来实现的。使用 with 方法有时可以帮助我们减少代码量,在此我们看看使用 with() 方法定义各项参数与代码清单 1 所使用的方法有何不同。
def cmdline = new CliBuilder(usage: ‘GroovyPackman -[chflms] [date] [prefix]‘) // 使用 Object 上的 with 方法省去 cmdline 对象的限定 cmdline.with { h longOpt: ‘help‘, required: false, ‘show usage information‘; d longOpt: ‘destfile‘, argName: ‘destfile‘, optionalArg: true, args: 1, ‘jar destintation filename‘; m longOpt: ‘mainclass‘, argName: ‘mainclass‘, required: true, args: 1, ‘fully name of main class‘; c longOpt: ‘groovyc‘, required: false, ‘Run groovyc‘; s longOpt: ‘sourcepath‘, argName: ‘sourcepath‘, required: true, args: 1, ‘fully path to the mainclass. Must be specified with .groovy file path when -c option used.‘; }
这样,我们为程序设置了命令行入口。其中:
在本文的最后一章中您将看到通过这个命令行工具(在本文中被命名为:GroovyPackman)编译打包脚本的实例。
def opt = cmdline.parse(args) if (!opt) { return } if (opt.h) { cmdline.usage() return }
Parse() 方法用来解析本程序执行时命令行输入的参数。通过 Parser 解析命令行参数后,我们可以得到 OptionAccessor 对象的一个实例,当输入的参数不合法时,这个对象为空。
通过 opt 对象我们可以轻松的获取到所有已经在命令行中指定的选项,我们将处理各个选项的值,使其符合我们的要求。
// 将命令行选项赋值给变量 def mainClass = opt.m def sourcePath = opt.s def destFile = mainClass + ‘.jar‘ if (!(sourcePath.endsWith("\\"))||!(sourcePath.endsWith("/")))( sourcePath = sourcePath + "/" ) if (opt.d) { destFile = opt.d }
在处理完程序的命令行选项之后,我们将进入本示例另一个重点:使用 AntBuilder 实现脚本的编译和打包。首先,我们来认识一下生成器。
生成器 (Builder) 是 Groovy 中的一个特性,它可以很方便地在 Groovy 中构造如 XML 文档一般的树形数据结构。而 AntBuilder 就是众多生成器中的一员,通过它您可以毫不费力地构造 Ant 基于 XML 结构的构建文件 (build.xml),不需要处理 XML 就可以执行构建。更加令人兴奋的是,与以往费力地用复制粘贴来创建 build.xml 文件不同,您不但可以编写循环、条件语句,甚至可以利用面向对象语言的优势。
以下代码段展示了使用 AntBuilder 的一个非常简单的例子。在本示例中,当用户在命令行指定了 -c 选项时将在命令行窗口输出其指定需要编译的 Groovy 脚本文件。
def ant = new AntBuilder() if (opt.c) { // 检查脚本文件是否存在 def scriptBase = mainClass.replace( ‘.‘, ‘/‘ ) def scriptFile = new File(sourcePath + scriptBase +‘.groovy‘ ) if (!scriptFile.canRead()) { println "Cannot read script file: ‘${scriptFile}‘" return } ant.echo( "Compiling ${scriptFile}" ) }
在本示例中,用户在命令行中使用 -c 选项指定了有效的 Groovy 脚本文件之后,程序将对其进行编译。我们将使用 FileSystemCompiler 类来实现脚本文件的编译,代码段 6 展示了这一过程。
try{ FileSystemCompiler fc= new FileSystemCompiler() fc.commandLineCompile( [ scriptFile ] as String[] ) } catch (org.codehaus.groovy.control.MultipleCompilationErrorsException e){ println e.getMessage() println "*** Possible solution: Please copy GroovyPackman.jar to dir \"${sourcePath}\" and try again. ***" return }
FileSystemCompiler 类的实例相当于是 Groovy 的命令行编译器 (groovyc)。在 Groovy 中,您可以直接调用 Groovy 编译器来编译脚本文件,其命令一般为 groovyc *.groovy。这个过程将生成一个或多个 *.class 文件,这些文件可以使用 java 命令执行(但在执行 Groovy 生成的 .class 文件时,需保证 ClassPath 中指向了 Groovy 的库文件,例如:goovy-1.x.x.jar 等)。
在安装有 Groovy 的系统中您也可使用命令 groovy *.groovy,同时完成编译和运行 Groovy 脚本。与 groovyc 命令不同的是,groovy 命令不是在文件系统而是在内存中生成 *.class 文件并立即执行。
在实现了对 Groovy 脚本文件编译的功能之后,接下来将利用 Groovy 中的 Ant 任务进行打包工作。
def GROOVY_SUPPORT = (System.getenv(‘GROOVY_SUPPORT‘))? (new File( System.getenv(‘GROOVY_SUPPORT‘))) : "" // 如果系统中安装了 Groovy,打包时我们将从系统中获取程序运行所必须的 Jar 包 if (GROOVY_SUPPORT!=""){ if (GROOVY_SUPPORT.canRead()) { // 构建可执行的 Jar 包并指定 mainclass ant.echo("Processing *.class files in ${sourcePath}...") ant.jar( destfile: destFile, compress: true ) { fileset( dir: sourcePath, includes: ‘**/*.class‘ ) zipgroupfileset( dir: GROOVY_SUPPORT, includes: ‘groovy-all-*.jar‘ ) zipgroupfileset( dir: GROOVY_SUPPORT, includes: ‘commons*.jar‘ ) zipgroupfileset( dir: GROOVY_SUPPORT, includes: ‘ant*.jar‘ ) // 您可以在此添加更多的 Jar 包,这取决于您的需求 manifest { attribute( name: ‘Main-Class‘, value: mainClass ) attribute( name: ‘Class-Path‘, value: ‘.‘ ) } } } } // 如果系统未检测到 Groovy 环境,打包时将使用 GroovyPackman.jar 中获取程序运行所必须的 Jar 包 else { ant.echo( "Missing environment variable GROOVY_SUPPORT: ‘${GROOVY_SUPPORT}‘" ) def PACKMAN = URLDecoder.decode( GroovyPackman.class.getProtectionDomain().getCodeSource().getLocation().getFile(), "UTF-8") PACKMAN = PACKMAN.toString().replaceFirst("/", "") ant.echo( "Using Packman: ‘${PACKMAN}‘" ) ant.jar( destfile: destFile, compress: true) { fileset( dir: sourcePath, includes: ‘**/*.class‘ ) zipfileset( excludes: ‘GroovyPackman*.*,org/apache/tools/**,images/**‘, src: PACKMAN) // 您可以根据具体需要增加更多的 Jar manifest { attribute( name: ‘Main-Class‘, value: mainClass ) attribute( name: ‘Class-Path‘, value: ‘.‘ ) } } }
在代码清单 7 中,首先检查环境变量 GROOVY_SUPPORT 是否存在,该环境变量通常在安装 Groovy Eclipse Plugin 之后会被设置,指向包含 Groovy 运行库文件所在的目录。接着再分别针对 GROOVY_SUPPORT 存在与否来完成不同的打包过程。当 GROOVY_SUPPORT 可用时我们将从 GROOVY_SUPPORT 指向的目录中拾取相关的运行库(如:groovy-all-*.jar,commons*.jar,ant*.jar 等);而当 GROOVY_SUPPORT 不可用即系统中未安装 Groovy 时,程序将从自身拾取相应的运行库并将其与编译后的脚本一同打包。
第一步,我们需要在 Eclipse 中编译本脚本文件,生成 *.class。要在 Eclipse 中编译 Groovy 脚本,可以通过点击菜单 Run -> Run As -> Groovy Script。
为了便于分享同时执行起来更加方便,第二步,我们将用 Java 命令执行生成的 .class 文件并将其自身打包成可执行的 Jar 包。
C:\groovy\GroovyPackman\bin>set GROOVY_SUPPORT= d:\eclipse\plugins\org.codehaus.groovy_1.7.5.20101020-2100-e35-release\lib C:\groovy\GroovyPackman\bin>java -cp .\;%groovy_support%\;%groovy_support%\ groovy-all-1.7.5.jar;%groovy_support%\commons-cli-1.2.jar;%groovy_support%\ant.jar; %groovy_support%\ant-launcher.jar GroovyPackman -m GroovyPackman -s c:\Groovy\GroovyPackman\bin
在这个例子中,我们使用一个非常简单的 helloworld 程序来做演示,看看如何用我们刚才制作的小工具来进行编译和打包。这个 helloworld 程序包含两个 *.groovy 脚本文件,本身为三层目录结构的包,如图 3 所示:
在这里,我们在命令行中执行以下命令:
java -jar GroovyPackman.jar
测试一下之前打包好的小工具能否正常运行。如图 4 所示:
如上图命令行中的输出结果,GroovyPackman.jar 运行正常。接下来开始编译和打包 hellowworld 示例程序。
将 GroovyPackman.jar 拷贝到 com.test.helloworld 包所在的根目录。在命令行中执行以下命令:
java -jar GroovyPackman.jar -m com.test,helloworld.helloworld -s c:\groovy -c
helloworld 将被自动编译并打包,如图 5 所示:
从图 6 中可以看到,*.groovy 脚本文件已经被编译成多个 *.class 文件。
从图 7 中可以看到已经打包好的 helloworld 程序。
接下来我们在命令行中运行刚刚生成的 helloworld 程序,结果如图 8 所示:
http://www.ibm.com/developerworks/cn/java/j-lo-groovypackage/index.html
http://www.ibm.com/developerworks/cn/java/j-pg06239.html
标签:
原文地址:http://www.cnblogs.com/softidea/p/4479242.html