使用命令行参数静态编译 groovy 脚本

Compile groovy script statically with command line arguments

我正在尝试静态编译 groovy 脚本以加快其执行速度,但如果使用命令行参数,我将无法使其正常工作。我的实际脚本要长得多,但我用于此问题的单行脚本完美地重现了我的错误。

使用以下脚本 (test.groovy)

println(args.length)

这可以通过java命令java -cp .;%GROOVY_HOME%\lib\* test使用命令groovyc test.groovy和运行编译,并且将简单地打印使用的命令行参数的数量。

现在,如果我提供脚本 (config.groovy)

withConfig(configuration) {
    ast(groovy.transform.CompileStatic)
}

并用groovyc -configscript config.groovy test.groovy编译,我得到一个错误

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
testing.groovy: 1: [Static type checking] - The variable [args] is undeclared.
 @ line 1, column 9.
   println(args.length)
           ^

1 error

此错误仅在我尝试静态编译时发生。我可以通过将脚本包装在 class 中并将我的代码放入 main 方法中(当然,这是编译器对脚本所做的)来让它工作,但是当我尝试只使用脚本(这是我喜欢做的)。由于某种原因,静态编译时变量 args 是未知的。我试过 this.args 但仍然收到错误。如果我尝试为 args (String[] args) 声明类型,它不再接收命令行参数。

当脚本以这种方式静态编译时,是否仍然可以获取命令行参数?

我在 Windows 7 和 Java 8.

上使用 Groovy 版本 2.4.10

执行 Groovy class 和 运行 简单脚本有区别。编译器简单地将脚本包装在 main 方法中是不正确的,脚本的主体将被复制到 run 方法中。

示例:

println(args.length)

将转换为

import org.codehaus.groovy.runtime.InvokerHelper
class Main extends Script {                     
    def run() {                                 
        println(args.length)              
    }
    static void main(String[] args) {           
        InvokerHelper.runScript(Main, args)     
    }
}

由于是动态类型,因此可以正常编译。 现在,如果我们向 class 添加 @CompileStatic 注释,我们将得到未声明变量的错误。

因此,您必须将代码包装在 class 中才能使用静态编译。

您可以在 documentation 中阅读有关脚本与 classes 的更多信息。

脚本通过绑定对象的动态评估工作。如果要使用静态编译,则需要使用显式形式,将 test.groovy 脚本更改为以下内容:

String[] args = (String[])binding.getVariable('args')
println args.length

使用您已经提供的配置脚本,您可以获得静态编译脚本。我以这种方式测试了 运行:

groovyc --configscript config.groovy test.groovy
java -cp .;%GROOVY_HOME%\lib\groovy-2.5.3.jar test 1 2 3

这会打印 3.

如果你不想修改test.groovy,你可以创建一个新的基础class:

import groovy.transform.CompileStatic

@CompileStatic
abstract class StaticBase extends Script {
    StaticBase() {
    }

    StaticBase(Binding binding) {
        super(binding)
    }

    String[] getArgs() {
        (String[]) getBinding().getVariable("args")
    }
}

由于基础 class 有一个方法 getArgs,那么当 test.groovy 引用 args 时,静态编译器会调用对该方法的调用。

groovyc --configscript config.groovy -b StaticBase test.groovy
java -cp .;%GROOVY_HOME%\lib\groovy-2.5.3.jar test 1 2

test.class中的代码有一个运行方法,其代码代表this.println(this.getArgs().length)