Gradle variable scoping - gradle

I my root project I defined a variable and a method using it like
// An immediately executed closure (I hope)
def myvar = ({-> do something})()
def myfun() {
println myvar + ":" + project
}
and called it in a subproject. This lead to an error like
Could not find property 'myvar' on root project 'root'.
I find it strange as myvar is in scope on the function definition (so I'm probably doing something different?). Ii looks like dynamic scoping, but I can't believe it.
I know about project.ext, but I don't want to make myvar available elsewhere.
So I moved the definition into the declaration (it gets evaluated multiple times now, but who cares), but then I found out that project refers to the root project, rather than the subproject calling the function (lexical scoping). Can I get the current project without passing it explicitly?

Local variables declared at the outermost level of a script aren't in scope of methods in the same script. This is due to how Groovy translates a script into a runnable class. One way around this is to use ext.myfun = { ... } instead, which solves the scoping problem and doesn't affect call sites (which can still use myfun()).
Your other problem seems unrelated. project refers to the project that the script is associated with, and isn't determined based on the caller. It isn't possible to get the caller's project without passing it. However, it's possible to declare ext.myfun for every project, e.g. in the root script's allprojects {} block.

Related

What does it mean in Groovy to specify a property followed by a closure?

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.

Declaring gradle dependencies with arguments

I have a multi-moduled Gradle project setup. I would like for a module to be able to accept an argument from a module that specifies it as a dependency. Can this be achieved?
Something like this in the parent module:
dependencies {
implementation project(':innerModule') {
arg "foo"
}
}
And the inner module would also have to declare the variable that gets assigned. def arg = ""? Really not sure on the correct syntax here.
EDIT:
What I am trying to do is create a build type variable, which I can then use for conditional logic in my project code. It is an Android project, but the one module it requires is non-Android. So I cannot use the buildTypes closure in that module, but I was hoping to feed a variable into the non-Android module once the parent Android module determines the build type. I feel like that should be possible
The answer to your specific question is no.
However, if you describe what you are trying to achieve, maybe Gradle will be able to help you.
What is this variable controlling?
What is the effect of the different values?

How Gradle tasks inline configuration works under the hood ?

I'm trying to understand how gradle build script is interpreted under the hood.
I get that the entire build script delegates to the project object, so when we type
task taskName
we are executing a function defined in the project object with task as name and taking a string parameter.
What is weird for me is the inline configuration of the task :
taskName.description = "Description of the task"
taskName.group = "Group of the task"
How this really works in the level of project object ?
Take a look at https://docs.gradle.org/current/dsl/org.gradle.api.Project.html, the first "Properties" section.
Here are the useful bits with some of the details removed:
A project has 5 property 'scopes', which it searches for properties. The scopes are:
The Project object itself.
The extra properties of the project.
The extensions added to the project by the plugins.
The tasks of the project.
The extra properties and convention properties inherited from the project's parent, recursively up to the root project. The properties of this scope are read-only.
When reading a property, the project searches the above scopes in order, and returns the value from the first scope it finds the property in. If not found, an exception is thrown.
In your example, taskName.description = "..." means that Gradle looks up taskName as a method on Project, as an extra property, as an extension and then it finds a task with that name. Then you're just calling setDescription on a regular Task object.
The magic happens in Project.property() using a ExtensibleDynamicObject configured like so:
https://github.com/gradle/gradle/blob/0b9cb4429513297e03965b0578607d10a2a1fcdf/subprojects/core/src/main/java/org/gradle/api/internal/project/DefaultProject.java#L226-L230

How to run the same class multiple times?

I have a puppet module which deploys a JAR file and writes some properties files (by using ERB templates).
Recently we added a "mode" feature to the application, meaning the application can run in different modes depending on the values entered in the manifest.
My hierarchy is as follows:
setup
*config
**files
*install
Meaning setup calls the config class and the install class.
The install class deploys the relevant RPM file according to the mode(s)
The config class checks the modes and for each mode calls the files class with the specific mode and directory parameters, the reason for this structure is that the value of the properties depends on the actual mode.
The technical problem is that if I have multiple modes in the manifest (which is my goal) I need to call the files class twice:
if grep($modesArray, $online_str) == [$online_str] {
class { 'topology::files' :
dir => $code_dir,
mode => $online_str
}
}
$offline_str = "offline"
$offline_suffix = "_$offline_str"
if grep($modesArray, $offline_str) == [$offline_str] {
$dir = "$code_dir$offline_suffix"
class { 'topology::files' :
dir => $dir,
mode => $offline_str
}
However, in puppet you cannot declare the same class twice.
I am trying to figure out how I can call a class twice or perhaps a method which I can access its parameters from my ERB files, but I can't figure this out
The documentation says it's possible but doesn't say how (I checked here https://docs.puppetlabs.com/puppet/latest/reference/lang_classes.html#declaring-classes).
So to summarize is there a way to either:
Call the same class more then once with different parameters
(Some other way to) Create multiple files based on the same ERB file (with different parameters each time)
You can simply declare your class as a define:
define topology::files($dir,$mode){
file{"${dir}/filename":
content=> template("topology/${mode}.erb"),
}
}
That will apply a different template for each mode
And then, instantiate it as many times as you want:
if grep($modesArray, $online_str) == [$online_str] {
topology::files{ "topology_files_${online_str}" :
dir => $code_dir,
mode => $online_str
}
}
$offline_str = "offline"
$offline_suffix = "_$offline_str"
if grep($modesArray, $offline_str) == [$offline_str] {
$dir = "$code_dir$offline_suffix"
topology::files{ "topology_files_${online_str}" :
dir => $dir,
mode => $offline_str
}
Your interpretation of the documentation is off the mark.
Classes in Puppet should be considered singletons. There is exactly one instance of each class. It is part of a node's manifest or it is not. The manifest can declare the class as often as it wants using the include keyword.
Beware of declaration using the resource like syntax.
class { 'classname': }
This can appear at most once in a manifest. Parameter values are now permanently bound to your class. Your node has chosen what specific shape the class should take for it.
Without seeing the code for your class, your question makes me believe that you are trying to use Puppet as a scripting engine. It is not. Puppet only allows you to model a target state. There are some powerful mechanics to implement complex workflows to achieve that state, but you cannot use it to run arbitrary transformations in an arbitrary order.
If you add the class code, we can try and give some advice on how to restructure it to make Puppet do what you need. I'm afraid that may not be possible, though. If it is indeed necessary to sync one or more resources to different states at different times (scripting engine? ;-) during the transaction, you should instead implement that whole workflow as an actual script and have Puppet run that through an exec resource whenever appropriate.

Trying to understand gradle project properties

Clearly I don't understand what's going on here.
I guess prop2 and prop3 can't be accessed because they are variables instead of "project properties".
The question arose because I would like the variables prop2 and prop3 to be visible from within the "doTheThing()" method, but I don't want to have to pass them in. I want the variables to be globally accessible to tasks, methods and classes (but only from within in the build script itself) - and I want them to be typed (which is why the defintion of prop1 is not acceptable).
Really, though - I guess what I'm asking for is some help understanding what a Gradle project property is and what the syntax 'prop1 = "blah"' is actually doing.
I have read the Gradle user guide and also Gradle in Action - if they already explain this concept please point me to the right section (maybe I glossed over it at the time not understanding what is was saying).
prop1 = "blah"
String prop2 = "bleah"
def prop3 = "blargh"
task testPropAccess << {
println "1: $prop1"
println "2: $prop2"
println "3: $prop3"
doTheThing()
}
private void doTheThing(){
println "4: $prop1"
println "5: $prop2" // error: Could not find property 'prop2' on root project 'script'
println "6: $prop3" // error: Could not find property 'prop3' on root project 'script'
}
When you declare a variable at the outermost level (as in your second and third statement), it becomes a local variable of the script's run method. This is really just Groovy behavior, and nothing that Gradle can easily change.
If you want the equivalent of a global variable, just assign a value to an unbound variable (as in your first statement). This adds a dynamic property to Gradle's Project object, which is visible throughout the build script (unless shadowed). In other words, prop1 = "blah" is equivalent to project.prop1 = "blah".
If you want the equivalent of a typed global variable, you'll have to wait until Gradle upgrades to Groovy 1.8, which makes this possible with the #Field annotation. Or you write a plugin that mixes a convention object into the Project object (but that's not suitable for ad-hoc scripting).

Resources