Gradle plugin development
Gradle is an open-source build automation system that builds upon the concepts of Apache Ant and Apache Maven and introduces a Groovy-based domain-specific language (DSL) instead of the XML.
Demonstrating gradle plugin development to generate customized sourceSet.
Writing Custom Plugin
Gradle makes it very easy to build custom binary plugins. You simply need to create a class that implements the org.gradle.api.Plugin<T> interface.
The plugin class and its code can be reside in one of the following three locations:
-
Build script
: Can be directly embedded into the build script. This approach limits the reuse value of the plugin -
buildSrc project
: can reside under the buildSrc project is automatically compiled and is made available in the build scripts classpath. -
Stand-alone project
: can be bundled as a JAR file that can then be included in the build script’s classpath.
Creating a Java Plugin (buildSrc project)
Create Gradle plugin by creating folder structure as shown below
|my-gradle-plugin | |__buildSrc | |__src |__main |__java | |--groovy |__com.tvajjala.plugin | |__com.tvajjala.task
Understanding Gradle Build System
Gradle built system has two major building blocks
-
project
-
task
A Gradle build system is made up of one or more projects
and each project contains one or more tasks
.
Project
A project
in Gradle is an abstract concept that represents an artifact that needs to be built.
For each project in the build, Gradle creates an instance of org.gradle.api.Project
and associates it with the build script.
This allows the build scripts to use Project’s API to access properties and customize build behavior at runtime
(for example, by creating new tasks or skipping existing tasks).
add wrapper task to build.gradle to generate wrapper for your project
|
package com.tvajjala.plugins
import com.tvajjala.actions.SrcSetAction
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.SourceSetContainer
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import static com.tvajjala.constants.Constants.SOURCE_SET
import static com.tvajjala.constants.TestStrategy.*
/**
* @author ThirupathiReddy Vajjala
*
* Plugin used to generate custom sourceSet for different types of tests execution
* this allows you to write test cases in a better way
*
*/
class TestStrategyPlugin implements Plugin<Project> {
Logger LOG= LoggerFactory.getLogger(TestStrategyPlugin.class)
@Override
void apply(Project project) {
LOG.debug("Generating sourceSet and tasks for testing")
SourceSetContainer container = (SourceSetContainer) project.getProperties().get(SOURCE_SET)
container.create(UNIT.sourceSetName, new SrcSetAction(UNIT, project))
container.create(LAYER.sourceSetName, new SrcSetAction(LAYER, project))
container.create(CONTRACT.sourceSetName, new SrcSetAction(CONTRACT, project))
container.create(INTEGRATION.sourceSetName, new SrcSetAction(INTEGRATION, project))
}
}
Read SourceSetContainer object from project reference.
|
package com.tvajjala.actions
import com.tvajjala.actions.internal.ResourceDirectoryAction
import com.tvajjala.actions.internal.SrcDirectoryAction
import com.tvajjala.constants.TestStrategy
import org.gradle.api.Action
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.SourceSetContainer
import org.gradle.api.tasks.TaskContainer
import org.gradle.api.tasks.testing.Test
import static com.tvajjala.constants.Constants.SOURCE_SET
/**
* @author ThirupathiReddy Vajjala
*
* Creates SourceSet and add required classpath to that directory
*
*/
class SrcSetAction implements Action<SourceSet> {
TestStrategy testStrategy
Project project
SourceSetContainer container
SrcSetAction(TestStrategy testStrategy, Project project) {
this.testStrategy = testStrategy
this.project = project
this.container = (SourceSetContainer) project.getProperties().get(SOURCE_SET)
}
@Override
void execute(SourceSet sourceSet) {
def javaSrcSet = sourceSet.java(new SrcDirectoryAction(testStrategy.taskName))
/** Setting classpath to avoid compilation errors */
javaSrcSet.setCompileClasspath(javaSrcSet.compileClasspath + container.getByName("main").output.classesDirs+
container.getByName("main").compileClasspath)
/** Setting runtime classpath to execute tests */
javaSrcSet.setRuntimeClasspath(javaSrcSet.runtimeClasspath + container.getByName("main").output.classesDirs+
container.getByName("main").runtimeClasspath)
/** Defining resource folder for yml or properties */
sourceSet.resources(new ResourceDirectoryAction(testStrategy.taskName))
TaskContainer taskContainer = project.getTasks()
/** Define task to execute specific testCases */
taskContainer.create(testStrategy.taskName, Test.class, new TaskAction(testStrategy, project))
taskContainer.getByName("clean", new CleanupAction(testStrategy.taskName))
}
}
Commonly used Project API Properties and methods
Property/Method | Description |
---|---|
name |
Name of the project and can be changed using the settings.gradle file. By default, the project directory name. |
defaultTasks |
Configures the names of the default tasks to run for a project |
parent |
Returns the parent project |
task |
Overloaded method to create new task |
INFO: SourceSetContainer , add new actions to create/customize default folder structure.
Tasks
Gradle projects are made up of one or more tasks that perform build steps.
Tasks execute actions such as compile Java source code and generate classes or clean
target folders. For each task in build file, Gradle creates an instance of
org.gradle.api.Task
. By default it would be org.gradle.api.DefaultTask
.
Property/Method | Description |
---|---|
name |
Name of the task |
enabled |
Decides if a task is enabled or disabled |
doFirst |
Adds an action to the beginning of the task’s action list |
doLast |
Adds an action to the end of tasks action list |
onlyIf |
Runs a task only if the passed in closure returns true |
dependson |
Configures task dependencies |
package com.tvajjala.actions
import com.tvajjala.constants.TestStrategy
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.tasks.SourceSetContainer
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import static com.tvajjala.constants.Constants.GROUP_NAME
import static com.tvajjala.constants.Constants.SOURCE_SET
/**
* @author ThirupathiReddy Vajjala
*
* Registers tasks to project
*
*/
class TaskAction implements Action<Task> {
static Logger LOG= LoggerFactory.getLogger(TaskAction.class)
TestStrategy testStrategy
Project project
SourceSetContainer container
TaskAction(TestStrategy testStrategy, Project project) {
this.testStrategy = testStrategy
this.project = project
this.container = (SourceSetContainer) project.getProperties().get(SOURCE_SET)
}
@Override
void execute(Task task) {
/** Group name where task is defined */
task.setGroup(GROUP_NAME)
LOG.info("Adding $testStrategy.taskName to Group $GROUP_NAME")
/** Description of the task */
task.setDescription(testStrategy.description)
/** Specify test classes location to run */
task.setTestClassesDirs(container.getByName(testStrategy.sourceSetName).output.classesDirs)
/** Specify classpath for runtime dependencies */
task.setClasspath(container.getByName(testStrategy.sourceSetName).runtimeClasspath)
LOG.info("Setting classpath to task")
}
}
Short Plugin Name
Plugins by default are referred to by their fully qualified class names. it is however possible to give your plugins short, easy-to-use names.
This can be achieved by creating file in the src/main/resources/META-INF/gradle-plugin folder.
The name of the properties file becomes the plugin’s short name.
|
Since this plugin deals with build numbers, we can call the plugin build-number-plugin.
to accomplish this, create a build-number-plugin.properties
file under the gradle-plugins
folder.
Inside the file, you simply create a property with the key implementation-class
and the fully qualified class as its value:
com.tvajjala.test-strategy
.propertiesimplementation-class=com.tvajjala.plugins.TestStrategyPlugin
use below line to register plugin to your project
apply plugin: 'com.tvajjala.test-strategy'
Plugin Source code dependencies
Plugin source code dependencies can be defined under buildscript
section.
buildscript { ext { springBootVersion = '2.1.0.RELEASE' } repositories { mavenLocal() mavenCentral() jcenter() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } }
Semantic versioning 2.0.0 guidelines
Software projects typically have version numbers that are expressed using three numbers separated by periods:
<major-version>.<minor-version>.<patch/incremental-version>.<build-number>
Ex: 1.0.0.56
-
major feature changes that are usually not backward compatible.
-
minor versions are incremented when implementing minor features or major bugs.
-
patch/incremental versions are incremented for minor bugs, text changes , etc.
-
build number for incremental builds from development teams. build numbers can be obtained
from source code check-ins or build timestamps or continuous integration (CI) server-generated numbers.
Complete source available at https://github.com/tvajjala/check-address/tree/master/buildSrc |
Comments
Post a Comment