I am experimenting with some gradle at a new project, and in its settings.gradle, file I see these few lines that I am unable to make sense of as to what groovy structure or a language feature it is and what it does and how it works:
plugins {
id "com.gradle.build-scan" version "1.12.1"
id "cz.malohlava" version "1.0.3"
}
buildScan {
server = "some.host.com"
publishAlways()
}
I was suspecting it was either a a closure or an interface of some sort, but could not make head or tail of it.
Any help in understanding following will be a great help:
what it does?
How plugins and buildScan works here from the language's perspective?
From the language perspective, the closures are executed in the context of another objects than the build script. This is called delegation in Groovy.
http://groovy-lang.org/closures.html#_delegation_strategy
plugin delegates to https://docs.gradle.org/current/dsl/org.gradle.plugin.use.PluginDependenciesSpec.html
buildScan delegates to Build Scan Plugin's extension object which configures the plugin.
There may be some trickery here that I don't understand, particularly as I can't find either plugins() or buildScan() in the API docs. Nonetheless, the following is a reasonable reading of what the syntax means.
plugins {} and buildScan {} are both methods that take a closure (see other answers for explanation of this) as an argument.
Each closure has a delegate object of a particular type that's different depending on the method using the closure, i.e. the delegate of plugins() will be of a different type to the delegate of buildScan()
Within the closure, unqualified methods and properties will be executed against the delegate object. So for the plugins {} block, id(...).version(...) will be called against its delegate. For buildScan {}, you're setting the property server on the delegate and calling its publishAlways() method.
Honestly, I don't know how useful the above information is for using and understanding Gradle, but I think it's what you're asking for. Hope it helps!
Related
In the gradle doc: https://docs.gradle.org/current/userguide/custom_plugins.html#sec:custom_plugins_standalone_project
The code block is:
gradlePlugin {
plugins {
create("simplePlugin") {
id = "org.example.greeting"
implementationClass = "org.example.GreetingPlugin"
}
}
}
I noticed it calls create method. And I looked the source code. It says:
Creates a new item with the given name, adding it to this container,
then configuring it with the given action.
What does it mean? Is it actually used anywhere? Or it can be any name does not really matter?
gradlePlugin.plugins is a NamedDomainObjectContainer - which are used by Gradle to hold multiple objects, each with a name.
The documentation on plugin development goes into more detail on the usage of NamedDomainObjectContainers.
Sometimes you might want to expose a way for users to define multiple, named data objects of the same type.
[...]
It’s very common for a plugin to post-process the captured values within the plugin implementation e.g. to configure tasks.
Since the elements of a NamedDomainObjectContainer can be any type, there's no specific usage for one. Generally they are used to configure the Gradle project, for example creating specific tasks, configuring source code locations.
I am completely new to Groovy, trying to learn it, but stymied because I can't parse the syntax well enough to even know where to look in the documentation. I am using Groovy in Gradle. There are many places where examples are given, but no explanation on what it means, so I just need a few pointers.
publishing {
publications {
mavenJava(MavenPublication) {
groupId = 'com.xxx.yyy'
artifactId = 'zzz'
from components.java
}
}
repositories {
mavenLocal();
}
}
The main build code is referring to things on the project class. On that class, I can find a property called publishing, and it is a class PublishingExtension. It appears then that the curly brace starts a closure with code in it. The documentation says this syntax:
publishing { }
configures the PublishingExtension. What I want to understand is what it means (i.e. what is actually happening) when I specify what looks like a property and follow that with a Closure. In the Groovy documentation I could not find any syntax like this, nor explanation. I sure it is something simple but I don't know enough to even know what to look for.
If I visit the Project Class API Docs there is no method there named publishing. Nor is there a property defined by the method getPublishing. Apparently this magic capability is enabled by the publishing plugin. If I visit the Publishing Plugin API Doc there is no description of this publishing property either or how it modifies the base project.
Similarly, drilling down a little more, that closure starts with a symbol publications and in the documentation for the PublishingExtension I find a property which is of type PublicationContainer which is read only. I also find a method named publications which does not accept a closure, but instead a configuration of type Action<? super PublicationContainer>. Again, I don't know how the contents of the curly braces are converted to an Action class instance. How does this object get constructed? Action is an interface, and the only method is execute however it is completely unclear how this action gets constructed.
The block that defines the Action starts with symbol mavenJava that looks like a method, but actually that first symbol is declaring a name of a new object of type MavenPublication named mavenJava. Either this is magically constructed (I don't know the rules) or there is a method called, but which method? What is it about PublicationContainer that allows it to know that an arbitrary mavenJava command is supposed to create an object instance. Then again, the curly braces that follow this, is that a closure, a configuration, or something even more exotic?
So as you can see, I am missing a little info on how Groovy works. So far I can't find documentation that explains this syntax, however it might be there if I knew what to look for. Can anyone explain what the syntax is really doing, or refer me to a site that can explain it?
publishing is called to configure the PublishingExtension.
In PublishingExtension there is a publications method accepting an Action which is usually coerced from a Closure. In Groovy a Closure is automatically converted to an interface with a single method.
mavenJava is a non-existent DSL method, which is passed by Gradle DSL builder to the create method of PublicationContainer:
publishing.publications.create('mavenJava', MavenPublication) {
// Configure the maven publication here
}
groupId and artifactId are properties of MavenPublication and are being set here.
from is the from(component) of MavenPublication and is written using Groovy simplified method call literal without brackets.
In general Gradle uses a root DSL builder which calls the nested DSL builders provided by plugins. Hence sometimes it's difficuilt (also for the IDE) to find proper references of all parts of the build.gradle file.
I'm trying to understand deeply how gradle is working.
Let's take the case of a basic dependencies {} declaration in a build.gradle script.
dependencies is a method on Project object.
DSL Project object = org.gradle.api.Project interface:
void dependencies(Closure configureClosure)
Configures the dependencies for this project.
This method executes the given closure against the DependencyHandler for this project. The
DependencyHandler is passed to the closure as the closure's delegate.
So: method receives as parameter a configuration closure and establish that closure's delegate object is DependencyHandler class, than execute the closure against it's delegate object.
Having this example:
dependencies {
// configurationName dependencyNotation
implementation 'commons-lang:commons-lang:2.6'
}
This is translated in a call of method add of DependencyHandler class:
Dependency add(String configurationName, Object dependencyNotation) Adds a dependency to the given configuration.
And now the question:
How exactly is done the translation of the line
implementation 'commons-lang:commons-lang:2.6'
into a class method call (e.g. DependencyHandler.add()) and who is responsable ?
My impression is that there is a missing explanation in the documentation, something like: default method on this delegate object DependencyHandler is add(...), so each configClosure's line, if matching notation configurationName dependencyNotation, will be translate into delegate's object default method.
But this is just a possible interpretation.
Ideea is: I give a closure with multiple lines, this is executed agains a delegation object which happens to have methods add(), and magically for each line this method is called ... how is this happening, based on what mechanism ? Is the Project.dependencies() method doing this, is the delegate object itself, or some other groovy specific mechanisms, etc.
Thank you.
If you want to get deeper insight on how Gradle (or its DSL) work under the hood, you can always check the actual source code. However, since this is not required to understand how to write build scripts, it is not included in the documentation.
Regarding your specific example, I have to admit that I do not exactly know how it is done, but I have a guess. If someone else has better insights, feel free to prove me wrong.
While Gradle indeed uses AST transformations to extend the regular Groovy syntax in some cases (e.g. the task definition syntax), I think they just rely on dynamic methods for dependency definitions.
Groovy is a dynamic language. This includes a method called methodMissing that may be defined by any class and will be called whenever a missing method is called on an object of that class:
class Example {
def methodMissing(String name, args) {
println name
}
}
def example = new Example()
example.method1()
You can find a more detailed example in Mr. Hakis blog.
Since Groovy allows omitting parentheses for method calls with arguments, your example implementation 'commons-lang:commons-lang:2.6' is basically nothing else but calling the method implementation with the dependency notation string as its argument.
Now Gradle could catch these calls via methodMissing and then call DependencyHandler.add() if the configuration actually exists. This allows you to dynamically add configurations in your build script:
configurations {
myConfig
}
dependencies {
myConfig 'commons-lang:commons-lang:2.6'
}
One way of doing seemed to be to use the java.lang.Compiler
I tried to use the java.lang.Compiler inside Eclipse anddid not understand the Object any parameters for the methods of that class? And putting in a class did not seem to work either.
Compiler.command(any) // what is meant by any? What are valid objects to put there?
Compiler.compileClass(clazz) // Nothing happens when I out a class in there?
Compiler.compileClasses(string) // hm?
How to can I print a hello message with a Compiler inside Eclipse...?
Reading the documentation is a very important skill you need to learn.
Whenever you come across a class or a method that you don't know the functionality of, simply look at the documentation first.
Here is the docs for java.lang.Compiler: https://docs.oracle.com/javase/7/docs/api/java/lang/Compiler.html
This is the first sentence of the document:
The Compiler class is provided to support Java-to-native-code compilers and related services. By design, the Compiler class does nothing; it serves as a placeholder for a JIT compiler implementation.
So, the answer to your question is, it does nothing. According to the documentation, it does nothing. It is used to start up the Java compiler when the JVM starts. You are not meant to use this.
I've been told not to use implementation details. A dependency seems like an implementation detail. However I could phrase it also as a behavior.
Example: A LinkList depends on a storage engine to store its links (eg LinkStorageInterface). The constructor needs to be passed an instance of an implemented LinkStorageInterface to do its job.
I can't say 'shouldUseLinkStorage'. But maybe I can say 'shouldStoreLinksInStorage'.
What is correct to 'test' in this case? Should I test that it stores links in a store (behavior) or don't test this at all?
The dependency itself is not an expected behavior, but the actions called on the dependency most certainly are. You should test the stuff you (the caller) know about, and avoid testing the stuff that requires you to have intimate knowledge of the inner workings of the SUT.
Expanding your example a little, lets imagine that our LinkStorageInterface has the following definition (Pseudo-Code):
Interface LinkStorageInterface
void WriteListToPersistentMedium(LinkList list)
End Interface
Now, since you (the caller) are providing the concrete implementation for that interface it is perfectly reasonable for you to test that WriteListToPersistentMedium() gets called when you invoke the Save() method on your LinkList.
A test might look like this, again using pseudo-code:
void ShouldSaveLinkListToPersistentMedium()
define da = new MockLinkListStorage()
define list = new LinkList(da)
list.Save()
Assert.Method(da.WriteListToPersistentMedium).WasCalledWith(list)
end method
You have tested expected behavior without testing implementation specific details of either your SUT, or your mock. What you want to avoid testing (mostly) are things like:
Order in which methods were called
Making a method, or property public just so you can check it
Anything that does not directly involve the expected behavior you are testing
Again, a dependency is something that you as the consumer of the class are providing, so you expect it to be used. Otherwise there is no point in having that dependency in the first place.
LinkStorageInterface is not an implementation detail - its name suggests an interface to to an engine. In which case the name shouldUseLinkStorage has more value than shouldStoreLinksInStorage.
That's my 2 pennies worth!