Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Groovy Goodness — Customizing The Groovy Compiler

DZone's Guide to

Groovy Goodness — Customizing The Groovy Compiler

With Groovy we can configure the compiler for our source files just like we can configure the Groovy compilation unit when we use GroovyShell to execute scripts. Read on to learn more.

· Web Dev Zone
Free Resource

Discover how to focus on operators for Reactive Programming and how they are essential to react to data in your application.  Brought to you in partnership with Wakanda

With Groovy we can configure the compiler for our source files just like we can configure the Groovy compilation unit when we use GroovyShell to execute scripts. For example, we can add annotations to source files before they are compiled without adding them to the actual source code ourselves. Suppose we want to apply the TypeChecked or CompileStatic AST transformation annotation to all source files in our project. We only have to write a configuration file and specify the name of the configuration file with the --configscript option of the Groovy compiler. If we use Gradle to build our Groovy project, we can also customize the GroovyCompile task to set the configuration file.

The configuration file has an implicit object with the name configuration of type CompilerConfiguration. Also, there is a builder syntax available via the CompilerCustomizationBuilder class. Let's look at both ways to define our custom configuration. We want to add theCompileStatic annotation to all classes, together with the ToString AST transformation annotation. Next, we also want to add the packagejava.time as implicit import for our source files. This means we don't have to write an import statement in our code to include classes from this package. Finally, we add an ExpressionChecker that will fail the compilation of our project if a variable name is only 1 character. We assume we use Gradle to build our project and we place the file groovycConfig.groovy in the directory src/groovyCompile. We must not name the fileconfiguration.groovy, because there is already a variable with the name configuration in the script and this will confuse the compiler.

// File: src/groovyCompile/groovycConfig.groovy
import groovy.transform.CompileStatic
import groovy.transform.ToString
import org.codehaus.groovy.ast.expr.VariableExpression
import org.codehaus.groovy.control.customizers.SecureASTCustomizer.ExpressionChecker
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
import org.codehaus.groovy.control.customizers.ImportCustomizer
import org.codehaus.groovy.control.customizers.SecureASTCustomizer

// Add the AST annotation @CompileStatic
// and @ToString to all classes.
configuration.addCompilationCustomizers(
        new ASTTransformationCustomizer(CompileStatic))
configuration.addCompilationCustomizers(
        new ASTTransformationCustomizer(ToString))

// Add implicit import for all classes
// for the package java.time.
def imports = new ImportCustomizer()
imports.addStarImports('java.time')
configuration.addCompilationCustomizers(imports)

// Define expression checker to deny 
// usage of variable names with length of 1.
def smallVariableNames = { expr ->
    if (expr instanceof VariableExpression) {
        expr.variable.size() > 1
    } else {
        true
    }
} as ExpressionChecker

def secureAstCustomizer = new SecureASTCustomizer()
secureAstCustomizer.addExpressionCheckers(smallVariableNames)
configuration.addCompilationCustomizers(secureAstCustomizer)

With the builder syntax of CompilerCustomizatioBuilder we also have the flexibility to pass parameters to the AST transformations we want to apply. We configure the ToString annotation to include the names of the properties in the generated toString method implementation:

// File: src/groovyCompile/groovycConfig.groovy
import org.codehaus.groovy.ast.expr.VariableExpression
import org.codehaus.groovy.control.customizers.SecureASTCustomizer.ExpressionChecker

// Using CompilerCustomizationBuilder.withConfig 
// method, where the class
// CompilerCustomizationBuilder is implicitly 
// imported for this script.
withConfig(configuration)  {

    ast(groovy.transform.CompileStatic)

    // Define includeNames parameter for ToString
    // AST annotation.
    ast(includeNames: true, groovy.transform.ToString)

    imports {
        star('java.time')
    }

    // Define expression checker to deny 
    // usage of variable names with length of 1.
    def smallVariableNames = { expr ->
        if (expr instanceof VariableExpression) {
            expr.variable.size() > 1
        } else {
            true
        }
    } as ExpressionChecker
    secureAst {
        addExpressionCheckers smallVariableNames
    }
}

Next, we configure the GroovyCompile task compileGroovy in our Gradle project to use our configuration file when the code is compiled. We do this via the property groovyOptions of the compile task:

// File: build.gradle
plugins {
    id "groovy"
}

repositories {
    jcenter()
}

dependencies {
    compile "org.codehaus.groovy:groovy-all:2.4.5"
    testCompile "org.spockframework:spock-core:1.0-groovy-2.4"
}

// Add the configuration script file
// to the compiler options.
compileGroovy.groovyOptions.configurationScript = file('src/groovyCompile/groovycConfig.groovy')

Written with Groovy 2.4.5.

Learn how divergent branches can appear in your repository and how to better understand why they are called “branches".  Brought to you in partnership with Wakanda

Topics:
groovy ,compiler

Published at DZone with permission of Hubert Klein Ikkink, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}