问题
I am trying to statically compile a groovy script to speed up it's execution, but am not able to get it to work if command line arguments are used. My actual script is much longer, but the one-line script I use for this question perfectly reproduces my error.
Using the following script (test.groovy)
println(args.length)
This can be compiled with the command groovyc test.groovy
and ran by the java command java -cp .;%GROOVY_HOME%\lib\* test
and will simply print the number of command line arguments used.
Now, if I provide the script (config.groovy)
withConfig(configuration) {
ast(groovy.transform.CompileStatic)
}
and compile with groovyc -configscript config.groovy test.groovy
, I get an error
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
This error only occurs when I attempt to compile statically. I can get it to work by wrapping the script in a class and putting my code in a main method (which, of course, is what the compiler does with a script), but not when I try to just use the script (which is what I prefer to do). For some reason, the variable args is unknown when compiled statically. I've tried this.args
but still receive the error. If I try to declare a type for args (String[] args
), it no longer receives the command line arguments.
Is there a way to still get the command line arguments when a script is compiled statically this way?
I am using Groovy version 2.4.10 on Windows 7 with Java 8.
回答1:
There's difference in executing Groovy class and running simple script. It's not correct that compiler simply wraps your script in main method, the body of the script will be copied into a run
method.
Example:
println(args.length)
will be converted to
import org.codehaus.groovy.runtime.InvokerHelper
class Main extends Script {
def run() {
println(args.length)
}
static void main(String[] args) {
InvokerHelper.runScript(Main, args)
}
}
This compiles fine due to dynamic types.
Now, if we add @CompileStatic
annotation to that class, we'll get the error of undeclared variable.
So, you have to wrap your code in class in order to use static compiling.
You can read more about Scripts versus classes in documentation.
回答2:
The Script works via dynamic evaluation of the bindings object. If you want to use static compilation, you need to use the explicit form, changing your test.groovy script into the following:
String[] args = (String[])binding.getVariable('args')
println args.length
Using your already provided configuration script you do get a static compiled Script. I tested running it this way:
groovyc --configscript config.groovy test.groovy
java -cp .;%GROOVY_HOME%\lib\groovy-2.5.3.jar test 1 2 3
This prints 3
.
If you want to not modify test.groovy at all, you can create a new base class:
import groovy.transform.CompileStatic
@CompileStatic
abstract class StaticBase extends Script {
StaticBase() {
}
StaticBase(Binding binding) {
super(binding)
}
String[] getArgs() {
(String[]) getBinding().getVariable("args")
}
}
Since the base class has a method getArgs
, then when the test.groovy refers to args, the static compiler picks up the call to that method.
groovyc --configscript config.groovy -b StaticBase test.groovy
java -cp .;%GROOVY_HOME%\lib\groovy-2.5.3.jar test 1 2
The code in test.class has a run method whose code represents this.println(this.getArgs().length)
来源:https://stackoverflow.com/questions/45054961/compile-groovy-script-statically-with-command-line-arguments