How can I create my own checkstyle plugin that preconfigures the settings?

Hi,

I just migrated several projects from maven to gradle and it’s working fine for now :slight_smile: So currently I am trying to improve some things and therefore I am now blocked by the following issue: I have a jar files which contains a checkstyle.xml file. I am using this strategy because we have several repos and I don’t want to copy the checkstyle config to each repo. This is working fine with the following code:

apply plugin: 'code-quality'
def checkstyleDir = 'checkstyle/config'
def checkstyleFile = 'checkStyle.xml'
    configurations { checkstyle }
dependencies{
    checkstyle group: 'de.xplosion', name: 'checkstyle', version: '1.0'
}
  // copy checkstyle config from checkstyle.jar into checkstyle/config folder
task getCheckstyleConfig {
    dependsOn configurations.checkstyle
      configurations.checkstyle.files.each { details ->
        zipTree(details).each{ file ->
              if(!(new File(checkstyleDir + '/' + checkstyleFile).exists())){
                  if(file.name.contains(checkstyleFile)){
                    copy{
                        println file.toString() + " -> " + checkstyleDir
                        from file.toString()
                        into checkstyleDir
                    }
                }
            }
        }
    }
}
  task aggregateCheckstyle(type: Checkstyle) {
    dependsOn getCheckstyleConfig
    source subprojects.collect { project ->
        project.sourceSets.main.allJava
        project.sourceSets.test.allJava
    }
    classpath = files(subprojects.collect { project ->
        project.sourceSets.main.compileClasspath
    })
      configFile = new File(checkstyleDir + '/' + checkstyleFile)
    println 'Running checkstyle with config ' + configFile.toString()
    resultFile = new File(checkstyleDir + '/checkstyleResult.xml')
    ignoreFailures = true
}

What I am trying now is to create a plugin-wrapper around the code-quality plugin. I wnt to move the above code into a plugin to push the config file directly into the code quality plugin with the result that I only have to include

apply plugin: 'myOwnCheckstylePlugin'

So I moved the code into an own project:

package de.xplosion.gradle
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.quality.CodeQualityPlugin
import org.gradle.api.plugins.quality.JavaCodeQualityPluginConvention
import org.gradle.api.plugins.quality.Checkstyle
  class CheckstylePlugin implements Plugin<Project> {
    def void apply(Project project) {
        project.plugins.apply(CodeQualityPlugin)
        def map = [type: Checkstyle]
        project.task(map, 'myOwnCheckStylePlugin') << {
                          source project.subprojects.collect {
                project.sourceSets.main.allJava
                project.sourceSets.test.allJava
            }
            classpath = files(project.subprojects.collect {
                project.sourceSets.main.compileClasspath
            })
                      configFile = new File('checkstyle.xml')
            resultFile = new File(project.buildDir.toString() + '/checkstyleResult.xml')
            ignoreFailures = true
                                    }
    }
}

This compiles and I can install the jar file into the repo. I can also use

apply plugin: 'myOwnCheckstylePlugin'

into another project, but the config file won’t be loaded. The code-quality plugin is triggered but the redefinition of the config file location is not used.

Anybody an idea what I have to do? Another question would be: how do I test it correctly? How can I simulate the activation of my plugin in a test class?

I tried

def project = org.gradle.testfixtures.ProjectBuilder.builder().build()
           project.plugins.apply(JavaBasePlugin)
project.plugins.apply(CheckstylePlugin)

but I cannot see that the checkstyleMain task is called. How would I do that?

Thank you in advice!

Martin

Do you have an example of a build file that is using your ‘myOwnCheckstylePlugin’?

A couple potential issues: * rather than using new File() (which will resolve relative to the current working directory), use project.file() which will always resolve relative to the project dir * You can also use project.fileResolver.withBaseDir(project.buildDir).resolve(filename) for resolving to subdirectories of the project

As far as testing once you apply the plugins you can verify that the plugins have been applied and that the task is configured the way that you expect.

assert project.plugins.hasPlugin(CodeQualityPlugin)
def task = project.tasks.aggregateCheckstyle
assert task != null
assert task.configFile = ... whatever you expect it to be...

Looking at the code for the plugin, aren’t you missing the getCheckstyleConfig task in your plugin together with the configuration?

Hi,

thanks for your answers. I think I found a solution. I missed the ‘dependsOn’ for my task. Complete apply function looks now like that:

def void apply(Project project) {
          project.plugins.apply(CodeQualityPlugin)
          project.task([type: Checkstyle], 'myCheckstyle')
{
              // create directories
            setupPlugin(project)
              // add all java files
                        source = project.files(project.subprojects.collect { p ->
                p.sourceSets*.allJava
            })
                          classpath = project.files(project.subprojects.collect { p ->
                p.sourceSets*.compileClasspath
            })
                          if(project.plugins.hasPlugin(JavaBasePlugin)){
                source += project.files(project.sourceSets*.allJava)
                                  classpath += project.files(project.sourceSets*.compileClasspath)
            }
              // create temporary checkstyle config file from jar (from resource folder)
            InputStream exportTemplateStream =
                    getClass().getClassLoader().getResourceAsStream(CHECKSTYLE_CONFIG_FILE)
              def configFileName = project.buildDir.toString() + CHECKSTYLE_CONFIG_DIR + '/' + CHECKSTYLE_CONFIG_FILE
            def myConfigFile = project.file(configFileName)
            myConfigFile << exportTemplateStream.getText('utf-8')
            configFile = myConfigFile
              // set result
            resultFile = project.file(project.buildDir.toString() + CHECKSTYLE_RESULT_DIR +
'/' + CHECKSTYLE_RESULT_FILE)
              // don't break the build
            ignoreFailures = true
        }
          // link in our task
        project.subprojects.each{ sub ->
            def task = sub.tasks[JavaBasePlugin.CHECK_TASK_NAME]
            project.logger.info task.toString() + ' dependsOn ' + project.tasks.withType(Checkstyle)
            task.dependsOn project.tasks.withType(Checkstyle)
        }
                  def task = project.tasks[JavaBasePlugin.CHECK_TASK_NAME]
        task.dependsOn project.tasks.withType(Checkstyle)
        project.logger.info task.toString() + ' dependsOn ' + project.tasks.withType(Checkstyle)
              }

The main build file now only must use

buildscript{
          repositories { maven{ url "http://nexus" } }
    dependencies { classpath 'org.example:myCheckstyle:1.0' }
}
  apply plugin: 'checkstyle'

I have only tested it with a multi-project, so maybe there are still some open points. By the way, any code improvements welcome. :slight_smile:

Did you package the config file in your plugin jar?

Yes

This is what I wanted, because we want to use one single checkstyle config for different projects in different repos

Small update: The code block from above must have

if(project.plugins.hasPlugin(JavaBasePlugin)){
            def task = project.tasks[JavaBasePlugin.CHECK_TASK_NAME]
            task.dependsOn project.tasks.withType(Checkstyle)
            project.logger.info task.toString() + ' dependsOn ' + project.tasks.withType(Checkstyle)
        }

instead of

def task = project.tasks[JavaBasePlugin.CHECK_TASK_NAME]
task.dependsOn project.tasks.withType(Checkstyle)
project.logger.info task.toString() + ' dependsOn ' + project.tasks.withType(Checkstyle)