Rule based model configuration in Gradle allows us to have a graph of objects with dependencies that are resolved by Gradle. To make this work Gradle needs to know about the object in this model space. The model space is populated with objects of our own and with objects from Gradle. At time of writing this blog post we can not interact with the Gradle Project
object in our rule based model configuration. It is not officially part of the model space. Probably in the future this might change and will the Project
object managed by Gradle be part of the model space. Which means we can use then a Project
object as input parameter for any rule methods we have. For now the official way to pass project information to the rule based model space is via the model
configuration block in our build script. The model
configuration block can be used to set properties on objects with values from our Project
object.
In the following example we have VersionFile
object that is part of the model space.
Continue reading →
Rule based model configuration gives Gradle more knowledge about the objects and their dependencies. This information can be used by Gradle to optimise the build process. We define rules on how we want Gradle to create objects and how we want to mutate objects in a class that extends RuleSource
. We can also add rules to validate objects available in the Gradle model space. We use the @Validate
annotation on methods that have validation logic. The first argument of the method is of the type of the object we want to validate. This type must be managed by Gradle.
In the following example we use the sample from a previous post. In this sample we have a VersionFile
class that is managed by Gradle. The class has a version
and outputFile
property. The version
must be set and must start with a v
. The outputFile
property is also required.
Continue reading →
Gradle 3.2 deprecates the <<
operator to add actions to a task. The <<
operator maps to the leftShift
method of a task. This operator confuses a lot people that are new to Gradle. Because without the operator we are configuring a task instead of adding actions. I can tell from experience the mistake is easily made. If we use the <<
in our build script with Gradle 3.2 we get a warning on the console. The warning message already mentions a solution: use the doLast
method to add actions.
In the following example build script we define the task deprecatedSample
using the <<
operator. The other task newSample
uses the doLast
method to add an action:
Continue reading →
When we use Rule based model configuration in our Gradle project we can give Gradle rules on how to manage objects from the model space. These rules are defined in a class that extends RuleSource
. When we want to set some default values for properties of a model object (in Gradle terms this is a subject) we can use the @Defaults
annotation. Rules annotated with @Defaults
are invoked right after the object is created and before any other methods that can mutate the state of the object.
The method, to set the default values, must have the type of the object as first parameter. Other parameters are considered input parameters and can be used to set a default value based on other model objects.
Continue reading →
We use the model
task to see which objects are available in the Gradle model space. The model spaced is managed by Rule based model configuration. Objects can be defined as hidden by the object author(s). By default a hidden object is not shown in the model report. We must use the task option --showHidden
to show also the hidden objects in the model report.
$ gradle -q model --showHidden
------------------------------------------------------------
Root project
------------------------------------------------------------
+ buildDir
| Type: java.io.File
| Value: /Users/mrhaki/Projects/sample/build
| Creator: Project..buildDir()
+ extensionContainer
| Type: org.gradle.api.plugins.ExtensionContainer
| Creator: Project..extensionContainer()
+ fileOperations
| Type: org.gradle.api.internal.file.FileOperations
| Creator: DefaultProject.BasicServicesRules#fileOperations(ServiceRegistry)
+ instantiator
| Type: org.gradle.internal.reflect.Instantiator
| Creator: DefaultProject.BasicServicesRules#instantiator(ServiceRegistry)
+ nodeInitializerRegistry
| Type: org.gradle.model.internal.core.NodeInitializerRegistry
| Creator: DefaultProject.BasicServicesRules#nodeInitializerRegistry(ModelSchemaStore, StructBindingsStore)
+ projectIdentifier
| Type: org.gradle.api.internal.project.ProjectIdentifier
| Value: root project 'versionrule'
| Creator: Project..projectIdentifier()
+ proxyFactory
| Type: org.gradle.model.internal.manage.instance.ManagedProxyFactory
| Creator: DefaultProject.BasicServicesRules#proxyFactory(ServiceRegistry)
+ schemaStore
| Type: org.gradle.model.internal.manage.schema.ModelSchemaStore
| Creator: DefaultProject.BasicServicesRules#schemaStore(ServiceRegistry)
+ serviceRegistry
| Type: org.gradle.internal.service.ServiceRegistry
| Value: ProjectScopeServices
| Creator: Project..serviceRegistry()
+ sourceDirectorySetFactory
| Type: org.gradle.api.internal.file.SourceDirectorySetFactory
| Creator: DefaultProject.BasicServicesRules#sourceDirectorySetFactory(ServiceRegistry)
+ structBindingsStore
| Type: org.gradle.model.internal.manage.binding.StructBindingsStore
| Creator: DefaultProject.BasicServicesRules#structBindingsStore(ServiceRegistry)
+ taskFactory
| Type: org.gradle.api.internal.project.taskfactory.ITaskFactory
| Creator: DefaultProject.BasicServicesRules#taskFactory(ServiceRegistry)
+ tasks
| Type: org.gradle.model.ModelMap | Creator: Project..tasks()
| Rules:
⤷ VersionFileTaskRules#createVersionFileTask(ModelMap, VersionFile)
+ buildEnvironment
| Type: org.gradle.api.tasks.diagnostics.BuildEnvironmentReportTask
| Value: task ':buildEnvironment'
| Creator: tasks.addPlaceholderAction(buildEnvironment)
| Rules:
⤷ copyToTaskContainer
+ components
| Type: org.gradle.api.reporting.components.ComponentReport
| Value: task ':components'
| Creator: tasks.addPlaceholderAction(components)
| Rules:
⤷ copyToTaskContainer
+ dependencies
| Type: org.gradle.api.tasks.diagnostics.DependencyReportTask
| Value: task ':dependencies'
| Creator: tasks.addPlaceholderAction(dependencies)
| Rules:
⤷ copyToTaskContainer
+ dependencyInsight
| Type: org.gradle.api.tasks.diagnostics.DependencyInsightReportTask
| Value: task ':dependencyInsight'
| Creator: tasks.addPlaceholderAction(dependencyInsight)
| Rules:
⤷ HelpTasksPlugin.Rules#addDefaultDependenciesReportConfiguration(DependencyInsightReportTask, ServiceRegistry)
⤷ copyToTaskContainer
+ dependentComponents
| Type: org.gradle.api.reporting.dependents.DependentComponentsReport
| Value: task ':dependentComponents'
| Creator: tasks.addPlaceholderAction(dependentComponents)
| Rules:
⤷ copyToTaskContainer
+ generateVersionFile
| Type: mrhaki.gradle.VersionFileTask
| Value: task ':generateVersionFile'
| Creator: VersionFileTaskRules#createVersionFileTask(ModelMap, VersionFile) > create(generateVersionFile)
| Rules:
⤷ copyToTaskContainer
+ help
| Type: org.gradle.configuration.Help
| Value: task ':help'
| Creator: tasks.addPlaceholderAction(help)
| Rules:
⤷ copyToTaskContainer
+ init
| Type: org.gradle.buildinit.tasks.InitBuild
| Value: task ':init'
| Creator: tasks.addPlaceholderAction(init)
| Rules:
⤷ copyToTaskContainer
+ model
| Type: org.gradle.api.reporting.model.ModelReport
| Value: task ':model'
| Creator: tasks.addPlaceholderAction(model)
| Rules:
⤷ copyToTaskContainer
+ projects
| Type: org.gradle.api.tasks.diagnostics.ProjectReportTask
| Value: task ':projects'
| Creator: tasks.addPlaceholderAction(projects)
| Rules:
⤷ copyToTaskContainer
+ properties
| Type: org.gradle.api.tasks.diagnostics.PropertyReportTask
| Value: task ':properties'
| Creator: tasks.addPlaceholderAction(properties)
| Rules:
⤷ copyToTaskContainer
+ tasks
| Type: org.gradle.api.tasks.diagnostics.TaskReportTask
| Value: task ':tasks'
| Creator: tasks.addPlaceholderAction(tasks)
| Rules:
⤷ copyToTaskContainer
+ wrapper
| Type: org.gradle.api.tasks.wrapper.Wrapper
| Value: task ':wrapper'
| Creator: tasks.addPlaceholderAction(wrapper)
| Rules:
⤷ copyToTaskContainer
+ typeConverter
| Type: org.gradle.internal.typeconversion.TypeConverter
| Creator: DefaultProject.BasicServicesRules#typeConverter(ServiceRegistry)
+ versionFile
| Type: mrhaki.gradle.VersionFile
| Creator: VersionFileTaskRules#versionFile(VersionFile)
| Rules:
⤷ versionFile { ... } @ build.gradle line 8, column 5
+ outputFile
| Type: java.io.File
| Value: /Users/mrhaki/Projects/sample/build/version.txt
| Creator: VersionFileTaskRules#versionFile(VersionFile)
+ version
| Type: java.lang.String
| Value: 1.0.1.RELEASE
| Creator: VersionFileTaskRules#versionFile(VersionFile)
$
Continue reading →
The Gradle model
task shows the objects in the model space of Gradle. The output shows the object hierarchy. By default a full report is shown, with a lot of information. We can customize the output format with the --format
task argument. The default value is full
, but we can also use the value short
. With the value short
a lot less information is shown.
Let's see the output of the model
task for a sample project:
Continue reading →
Gradle has an incubating feature Rule based model configuration.
This is a new way to configure Gradle projects where Gradle has more control of the configuration and the dependencies between configuration objects.
This allows Gradle to resolve configuration values before they are used, because Gradle knows there is a dependency.
With this new model we don't need any lazy evaluation "tricks" we had to use.
For example there was an internal convention mapping mechanism for tasks to assign values to a task configuration after the task was already created.
Also the project.afterEvalute
is a mechanism to have late binding for task properties.
With the new rule based model Gradle can do without these options, we can rely on Gradle resolving all dependent configuration values when we create a task.
In Gradle we already know about the "project space" where the Project
object is the root of the object graph.
For example repositories
are part of the project space. Gradle can get some useful information from the project space, but it is mostly a graph of objects that Gradle only partially can reason about.
Then we have the "model space".
This is part of a project and we can use it in our build script with the model
configuration block.
The model space is separate from the project space and contains objects that are managed by Gradle.
Gradle knows dependencies between the objects and how to create and change them.
This helps Gradle to optimise build logic.
To help Gradle we must define rules to work with objects in the model space.
Each rule is like a recipe for Gradle on how to work with the model.
Gradle can build a graph of models and know about dependencies between models.
This way Gradle guarantees that model objects are completely configured before being used.
For example if a rule needs a VersionFile
model configuration object then Gradle makes sure that the VersionFile
is created and all properties are set.
So we don't need any lazy or late binding anymore, because the properties will be set (Gradle makes sure) when we want to use them.
The rules are defined a class that extends RuleSource
.
Such a class is stateless and only contains methods to work with the model objects.
Gradle has some specific annotations that can be used on methods to indicate what a method should do.
Continue reading →
Asciidoctor is a Ruby tool, but luckily we can use AsciidoctorJ to use Asciidoctor in Java code.
The Asciidoctor Gradle plugin relies on AsciidoctorJ to run.
AsciidoctorJ allows us to write custom extensions in Java (or Groovy), but we can still use Asciidoctor extensions written in Ruby with the Gradle plugin.
In the following example we use the emoji-inline-macro from Asciidoctor extensions lab.
This is an extension written in Ruby. We create a new directory for our sample and create a lib
folder.
Inside the lib
directory we copy the file emoji-inline-macro.rb
and the supporting directory emoji-inline-macro
.
These files are all in the Asciidoctor extensions lab repository.
After we have copied the files we should have the following structure:
Continue reading →
To apply a plugin in our Gradle build script we can use the plugins DSL.
The plugins DSL is very concise and allows Gradle to be more efficient and more in control when loading the plugin.
Normally the plugin we define is fetched from the Gradle plugin portal.
If we have our own repository, for example on the intranet of our company, we have to define that extra repository with a pluginRepositories
configuration block in the settings.gradle
file of our project.
In the following sample we have a plugin mrhaki.gradle.version-file
that is stored in the company intranet repository with the URL http://intranet/artifactory/libs-release/
.
Continue reading →
The Pair
class in Ratpack is an easy way to create a growing data structure, passed on via Promise
methods.
A Pair
object has a left and right part containing data.
These parts can even be other Pair
objects.
Since Ratpack 1.4.0 the Promise
class has methods to set the right or left part of a Pair
: left
, flatLeft
, right
and flatRight
.
The result of these methods is a Promise<Pair>
object.
The input can be Promise
type or a Function
that can use a previous Promise
.
In the following example specification we use the different new methods to create a Pair
.
We also create a simple Ratpack server with a asynchronous HTTP client implementation to simulate remote calls returning a Promise
:
Continue reading →
In functional programming we have the concept of an identity function.
An identity function returns the same result as the input of the function.
Groovy has a lot of functional paradigms including a identity function.
Of course in Groovy's case it is an identity closure.
It is defined as a constant in the Closure
class: Closure.IDENTITY
.
If we use this closure we get the same result as the argument we provide.
In the following example we first create our own identity closure.
Next we use the built-in Closure.IDENTITY
closure:
Continue reading →
Groovy adds a lot of useful methods to the Java JDK classes.
One of them is the sleep
method that is added to all objects.
With the sleep
method we can add a pause to our code.
The sleep
method accepts a sleep time in milli seconds.
The implementation of the method will always wait for he given amount of milli seconds even if interrupted.
But we can add a closure as extra argument, which is invoked when the sleep
method is interrupted.
We should return true
for the closure to really interrupt, otherwise we use false
.
In the following example we use the sleep
method to pause the bedtime
method of the User
class.
We run the bedtime
method in a thread and after 2000 milli seconds we intercept the thread.
The sleep
method still wait for 5 seconds, before ending:
Continue reading →