Showing posts with label Let Me Understand Gradle. Show all posts
Showing posts with label Let Me Understand Gradle. Show all posts

2021-10-10

4: Defining and Using Classes in Gradle

<The previous article in this series | The table of contents of this series |

What does a class defined in a Gradle script become? An inner class in the script class, a local inner class in the 'run' method, or what else?

Topics


About: Gradle

The table of contents of this article


Starting Context



Target Context


  • The reader will know what does any class defined in Gradle become and how to define and use any class.

Orientation


There is an article that introduces a better way of writing scripts in Gradle.


Main Body

Stage Direction
Here is Special-Student-7 in a room in an old rather isolated house surrounded by some mountains in Japan.


1: Defining a Class in the Script


Special-Student-7-Hypothesizer
I can define a class in the script, like this.

@Gradle Source Code
class CustumTaskA extends DefaultTask {
	@TaskAction
	void action () {
		println ("### CustumTaskA working!")
	}
}

A reason why I define my class is that I have to define a custom task class, as in the above code.

Another reason is that I want a utility class that is used in multiple tasks.


2: It Does Not Become Any Inner Class or Local Inner Class


Special-Student-7-Rebutter
Anyway, what does such a class become? I mean, where is the class placed in the automatically-generated 'org.gradle.groovy.scripts.BasicScript' sub class?

Special-Student-7-Hypothesizer
I guessed that it would become an inner class in the script class, or maybe a local inner class in the 'run' method, but that has turned out be not the case.

It becomes an outer class.

Special-Student-7-Rebutter
Um? What are the implications of the fact?

Special-Student-7-Hypothesizer
The outer class cannot directly access the script class fields, the script properties, or the project properties.

Special-Student-7-Rebutter
Well, it is rather inconvenient. Can we not define an inner class in the script class?

Special-Student-7-Hypothesizer
As far as I know, no.


3: Defining a Class Outside the Script File


Special-Student-7-Hypothesizer
As the class does not become any inner class of the script class, I do not rather want to write it in the script file, because the class does not become really a part of the script.

Groovy-wise, such a class can be put into another Groovy source file (with the file name extension of 'groovy'), but well, Gradle does not read such a Groovy source file or any 'gradle' extension file, as far as I know. . . . I wonder why Gradle does not use Groovy more straight-forwardly, than badly twisting its expected behavior.

Well, I can put the class in another Gradle file and "apply" the Gradle file into the script, but a problem is that the class is invisible in the script, as this gives an error, "unable to resolve class ClassB".

Classes.gradle

@Gradle Source Code
class ClassB {
	public static void staticMethodB () {
		println ("### staticMethodB!")
	}
	
	public void instanceMethodB () {
		println ("### instanceMethodB!")
	}
}

build.gradle

@Gradle Source Code
apply ("from": "Classes.gradle")

ClassB l_classB = new ClassB ()

Special-Student-7-Rebutter
Why?

Special-Student-7-Hypothesizer
The reason is that a different class loader is used for each Gradle file.

So, what should I do? . . . If a class instance has been put into, for example, a project property, the class instance can be accessed in the script, as this is OK.

Classes.gradle

@Gradle Source Code
class ClassB {
	public static void staticMethodB () {
		println ("### staticMethodB!")
	}
	
	public void instanceMethodB () {
		println ("### instanceMethodB!")
	}
}

ext.classB = new ClassB ()

build.gradle

@Gradle Source Code
classB.instanceMethodB ()

Special-Student-7-Rebutter
That is as expected.

Special-Student-7-Hypothesizer
But what if the class itself, not any class instance, has to be used in the script? For example, a custom task class has to be used in the script in order to create a task from the class.

In fact, I can put the class into a project property, as this is OK.

Classes.gradle

@Gradle Source Code
class ClassB {
	public static void staticMethodB () {
		println ("### staticMethodB!")
	}
	
	public void instanceMethodB () {
		println ("### instanceMethodB!")
	}
}

class CustumTaskA extends DefaultTask {
	@TaskAction
	void action () {
		println ("### CustumTaskA working!")
	}
}

ext.ClassB = ClassB.class
ext.CustumTaskA = CustumTaskA.class

build.gradle

@Gradle Source Code
ClassB.staticMethodB ()

task ("custumTaskA", type: CustumTaskA, {
})

But still, I cannot create any instance from the class with the 'new' operator, in the script.

Although I have not bumped into such necessity, if do, I will be able to have a factory method, like this.

Classes.gradle

@Gradle Source Code
class ClassB {
	public static void staticMethodB () {
		println ("### staticMethodB!")
	}
	
	public static ClassB createInstance () {
		return new ClassB ()
	}
	
	public void instanceMethodB () {
		println ("### instanceMethodB!")
	}
}

build.gradle

@Gradle Source Code
def l_classB = ClassB.createInstance ()
l_classB.instanceMethodB ()

I cannot specify the "l_classB" variable type as 'ClassB', though; a shame!


4: Injecting the Script Object or the Project Object into the Class


Special-Student-7-Hypothesizer
As I said, the inconvenience of being an outer class is that the class cannot access the script fields, the script properties, or the project properties, at least directly.

If the class needs to access some of them, they have to be made visible to the class instance somehow.

I usually inject the project object into the class instance.

If the class is something I instantiate with the 'new' operator, I can pass the project object as a constructor argument, like this.

Classes.gradle

@Gradle Source Code
class ClassB {
	private Project i_project
	
	public static void staticMethodB () {
		println ("### staticMethodB!")
	}
	
	public static ClassB createInstance (Project a_project) {
		return new ClassB (a_project)
	}
	
	public ClassB (Project a_project) {
		i_project = a_project
	}
	
	public void instanceMethodB () {
		println ("### instanceMethodB!")
	}
}

If the class is a task class, which is instantiated by the 'task' method, I can set the project object in the configuration closure, like this.

Classes.gradle

@Gradle Source Code
class CustumTaskA extends DefaultTask {
	protected Project i_project
	
	@TaskAction
	void action () {
		println ("### CustumTaskA working!")
	}
}

ext.CustumTaskA = CustumTaskA.class

build.gradle

@Gradle Source Code
task ("custumTaskA", type: CustumTaskA, {
	i_project = project
})


5: A Project Extension: a Strategy I Employ


Special-Student-7-Hypothesizer
A strategy I employ is to have a project extension.

Special-Student-7-Rebutter
What is that?

Special-Student-7-Hypothesizer
Project extension is a project property that is an instance of a class that has some fields and some methods that are used commonly in tasks.

For example, I define a project extension class and set its instance as a project property, like this.

Classes.gradle

@Gradle Source Code
@groovy.transform.CompileStatic
class ProjectExtension {
	private Project i_project
	public String i_parameterA
	public String i_parameterB
	
	public ProjectExtension (Project a_project) {
		i_project = a_project
		i_parameterA = "parameter A"
		i_parameterB = "parameter B"
	}
	
	public void methodA () {
		println ("### methoidA!")
	}
	
	public void methodB () {
		println ("### methoidB!")
	}
}

ext.extension = new ProjectExtension (project)

Why do I not set such data and functions directly as project properties, like this?

build.gradle

@Gradle Source Code
ext.i_parameterA = "parameter A"
ext.i_parameterB = "parameter B"
ext.methodA = {
	println ("### methoidA!")
}
ext.methodB = {
	println ("### methoidB!")
}

The reason is that such functions are closures, and closures are certainly handy, but cannot be staticly compiled, being a performance liability.

Why does the class instance have to be set as a project property?

Well, it does not particularly have to, but as the project object is meant to be injected around into class instances and of course is visible in the script, the class instance will become visible anywhere, that way.


References


<The previous article in this series | The table of contents of this series |

3: Variables and Properties and Variable Scopes, in Gradle

<The previous article in this series | The table of contents of this series | The next article in this series>

"local" and "script wide" scopes? Does not make sense to me. The script class must be put into view, and properties must be discerned from variables.

Topics


About: Gradle
About: Groovy

The table of contents of this article


Starting Context



Target Context


  • The reader will understand what "local scope variable" and "script wide scope variable" really are, discern between variables and properties, and know how variables and properties should be defined and used.

Orientation


There are some articles that help you to make heads and tails of Groovy Gradle scripts (here and here).


Main Body

Stage Direction
Here is Special-Student-7 in a room in an old rather isolated house surrounded by some mountains in Japan.


1: "local scope variable" and "script wide scope variable"??


Special-Student-7-Hypothesizer
This page of the official document does not make sense to me. "local scope variable" and "script wide scope variable"? . . .

"local" means 'script-local', does it not?, because I do not see anything else to which those "local" variables could be local.

Special-Student-7-Rebutter
It is high time for people to understand that saying just "local" is fundamentally meaningless; 'local to WHERE' is the issue.

Special-Student-7-Hypothesizer
Being 'block-local', 'function-local', or 'source-file-local' makes sense, but being vaguely just local is meaningless.

Anyway, supposing that it is 'script-local', I do not understand the difference between being "script-local" and being "script wide", at least if the variable is defined at the top of the script. I mean, according to the common sense of programming in general as I understand, if a variable is defined at the top of a scope, the variable should be visible throughout the scope.

For example, in the following Java or Python code piece, 'l_string' (which is function-local) is function wide, being visible even in the inner class or the inner function.

@Java Source Code
	private static void test () throws Exception {
		String l_string = "a string";
		class InMethodClass {
			public void print () {
				System.out.println (String.format ("### %s", l_string));
			}
		}
		InMethodClass l_inMethodClass  = new InMethodClass ();
		l_inMethodClass.print ();
		System.exit (0);
	}

@Python Source Code
	@staticmethod
	def test () -> None:
		l_string: str = "a string"
		def inMethodFunction () -> None:
			sys.stdout.write ("### {0:s}\n".format (l_string))
		inMethodFunction ()

An explanation like "'localScope1' is not script wide (invisible in 'method ()') because it is script-local" does not make sense to me.

Special-Student-7-Rebutter
Do not expect me to make sense of it.

Special-Student-7-Hypothesizer
As for the definition of "scriptScope", it does not even work in Gradle, right? At least, my Gradle flags that definition with an error message: "Could not set unknown property 'scriptScope' for root project 'gradleTests' of type org.gradle.api.Project." . . .

Special-Student-7-Rebutter
Is it just a mistake? We have to admit that mistakes can sometimes happen.

Special-Student-7-Hypothesizer
It is not more "a mistake" than just showing a piece of Groovy code without being able to be bothered to explain the fact that Gradle scripts are not really Groovy code and how so.

Special-Student-7-Rebutter
It is not any "script wide variable" in Gradle, but a "script wide variable" in Groovy; why did the author not show a "script wide variable" in Gradle, in a Gradle document?

Special-Student-7-Hypothesizer
He or she could not be bothered, I guess.

Besides, the statement, "Groovy has two types of script variables", is not correct: there is one more type of script variables.


2: Necessary Understanding of the Underlying Mechanism


Special-Student-7-Hypothesizer
The explanation of the document page does not make sense because it did not make the necessary explanation of the underlying mechanism.

Actually, the whole official document is scheming to do without explaining the mechanism, but this is one more example of such a scheme does not usually work.

Whether the author kindly tried to spare readers some details or spared himself or herself, it is not working, I say.

Well, as is explained in more detail in a previous article, the Gradle script is not executed as it is, but is converted to a 'org.gradle.groovy.scripts.BasicScript' sub class, whose 'run' method is executed.

At the conversion, where does each fragment in the script go in the class?

This is not accurate at all, but conceptually, that Gradle script (it is not even a Gradle script, in fact) in the document page becomes like the following class (after "scriptScope"s are changed to 'binding.scriptScope').

@Java Source Code
~

class Main extends org.gradle.groovy.scripts.BasicScript {
	public groovy.lang.Binding binding = new groovy.lang.Binding ();
	
	static void main (String[] args) {
		// calls the 'run ()' method somehow
		~
	}
	
	public Object run() {
		Object localScope1 = "localScope1"; // specifying 'String' does not seem to make it 'String'.
		Object localScope2 = "localScope2";
		binding.scriptScope = "binding.scriptScope";
		
		System.out.println (localScope1);
		System.out.println (localScope2);
		System.out.println (binding.scriptScope);
		
		groovy.lang.Closure closure = new groovy.lang.Closure (); // the code, "System.out.println (localScope1); System.out.println (localScope2); System.out.println (binding.scriptScope);",  is injected somehow
		
		closure.call ();
		method ();
	}
	
	private void method() {
		try {
			localScope1;
		} catch (MissingPropertyException e) {
			System.out.println ("localScope1NotAvailable");
		}
		try {
			localScope2;
		} catch(MissingPropertyException e) {
			System.out.println ("localScope2NotAvailable");
		}
		System.out.println (binding.scriptScope);
	}
}

I repeat that the code is not accurate in many aspects, but that does not matter for this discussion.

The function, 'method ()', becomes a class method, and the rest go into the 'run ()' method.

What is "binding"? It is a 'groovy.lang.Binding' instance that the 'Script' instance is automatically equipped with. And 'scriptScope' becomes a property (rather than a variable) of the 'Binding' instance, which is a container of properties.

It is important to discern properties from variables.

Special-Student-7-Rebutter
Why is it important?

Special-Student-7-Hypothesizer
The properties container is a map (roughly speaking), and the property name, "scriptScope", becomes a key of the map; whether the key is available or not depends on the timing: the key is sometimes available and sometimes unavailable from the same code point. So, it is fundamentally unsuitable to talk about the availability of the property in terms of scope.

Special-Student-7-Rebutter
So, the argument by the document page is fundamentally off the mark . . .


3: "local" Is Really 'run'-Method-Local


Special-Student-7-Hypothesizer
Ah-ha, I understand now that "local" does not mean "script-local", but ''run'-method-local'.

And it now makes sense that 'localScope1' is invisible in the 'method ()' function but is visible in the closure.


4: "script wide" Is Not Really Script Wide


Special-Student-7-Hypothesizer
As has been explained in a previous section, what is being called "script wide variable" in the document page is not really any variable.

And it is not really script wide, either.

For example, in this code, "binding.string" is not visible at the call of "printData ()".

@Gradle Source Code
void printData () {
	println (String.format ("### binding.string: %s", binding.string))
}

printData ()
binding.string = "string 3"

. . . Is that a mater-of-course because the function is defined before "binding.string" is defined (in fact, the term, "define", is not appropriate, because it is not any variable)?

No, the function definition is fine, as can be seen by the fact that this is OK.

@Gradle Source Code
void printData () {
	println (String.format ("### binding.string: %s", binding.string))
}

binding.string = "string 3"
printData ()

In fact, as "binding.string" is a key of a map, whether it can be accessed or not is about whether the key exists at that moment, not about scope.

Now, I understand why this type of "variable" cannot be specified with the variable type: because it is not any variable, but a key-value pair of a map. The variable type (note that I did not say 'datum type') of values has been already determined by the definition of the map, right?


5: In Fact, There Is Another Type of Variables, Which Is Really Script Wide: Script Class Field


Special-Student-7-Hypothesizer
In fact, I can define a variable like this.

@Gradle Source Code
@groovy.transform.Field Object i_object = "string 4"

That becomes a script class field.

As can be guessed (if the underlying mechanism is understood), it is really script wide.

For example, this is fine.

@Gradle Source Code
void printData2 () {
	println (String.format ("### i_object: %s", i_object))
}

printData2 ()
println (String.format ("### i_object: %s", i_object))

@groovy.transform.Field Object i_object = "string 4"

Well, if "i_string" looks as though it is being used before it is defined, it does not matter, because the definition is not inside the flow of the 'run' method.


6: There Are Also Project Extra Properties


Special-Student-7-Hypothesizer
As what can be used for similar purpose with those variables and properties, also project extra properties should not be left out.

Any project extra property is a property of the project instance, and it is called "extra" because there are also non-extra properties, which are preset properties.

A project extra property can be set and used like this.

@Gradle Source Code
void printData3 () {
	println (String.format ("### ext.string2: %s", ext.string2))
	println (String.format ("### ext.string2: %s", string2)) // "ext." can be ommited
}

ext.string2 = "string 5"
println (String.format ("### ext.string2: %s", ext.string2))
println (String.format ("### ext.string2: %s", string2)) // "ext." can be ommited
printData3 ()

The reason why "ext" (which is a property of the project instance, not of the script instance) can be accessed in the script class is that the 'org.gradle.groovy.scripts.BasicScript' sub class secretly interferes to send the access to the project instance (as has been explained in a previous article).

Special-Student-7-Rebutter
Why should we use a project extra property instead of a script property?

Special-Student-7-Hypothesizer
Well, if the script instance corresponds to the project instance one-to-one (it does, as far as I am concerned), I do not see any particular reason, practically speaking.

Special-Student-7-Rebutter
Then, can project extra properties be "left out", after all?

Special-Student-7-Hypothesizer
But usually, rather script-binded properties are left out, because 'Project' is the main entity for Gradle.


7: Is That of a Variant Type or of an Automatically-Deduced Type?


Special-Student-7-Hypothesizer
I really do not approve omitting specifying the variable type in variable definition.

For example, I do not write like this.

@Gradle Source Code
def l_string1 = "string 1"

That is because the code becomes more difficult to understand: it is far better to know that the variable points to only a string than to have to expect whatever object, if the variable is meant to point to only a string. The immediate little labor-saving is not worth harming the long-time maintainability.

Anyway, what does that definition mean? Does that make it a variant type or an automatically deduced type?

What is the difference? Variant type means that it can point to any datum of any type, while automatically deduced type means that it is a specific type determined by the initial value.

In fact, that sloppy definition makes it a variant type.

And the variable can be used without being cast to the type of the pointed datum, although I strongly disapprove such code.

However, sadly, specifications of variable types in Gradle do not seem so meaningful, because any variable becomes variant, as can be seen in this code.

@Gradle Source Code
String l_string2 = "string 2"
l_string2 = 2 // allowed, so the variable is variant anyway


8: What About Variables in Other Locations?


Special-Student-7-Hypothesizer
What I wondered when I first read the document page was "Well, what happen to variables in other locations?".

For example, what does the variable, 'l_string', in this function become?

@Gradle Source Code
void functionA () {
    String l_string
}

As I naturally understand now (only because I have distinguished between variables and properties and have made sense of variable scopes in Gradle), the discussion of the document page does not apply here: the variable obviously does not become 'run'-method-local or become any script property.

Any variable in any function, any class, any closure, or whatever is just a variable in the entity in the usual Java sense.


References


<The previous article in this series | The table of contents of this series | The next article in this series>

2021-10-03

2: Trying to Make Sense of Gradle Scripts as Groovy Code

<The previous article in this series | The table of contents of this series | The next article in this series>

Gradle scripts are based on Groovy (forget about Kotlin here), but are not exactly Groovy code. Here is an outline of what Gradle is doing secretly.

Topics


About: Gradle
About: Groovy

The table of contents of this article


Starting Context


  • The reader has a basic knowledge on Groovy or Java.

Target Context


  • The reader will know how Groovy Gradle scripts are not really Groovy scripts and an outline of what Gradle is doing secretly..

Orientation


Here is the 1st step toward beginning to fully exploit Gradle.


Main Body

Stage Direction
Here is Special-Student-7 in a room in an old rather isolated house surrounded by some mountains in Japan.


1: We Have Rectified That Unreasonable "Hello World" Script to Be More Reasonable as Groovy Code, but Still It Does Not Make Sense . . .


Special-Student-7-Hypothesizer
We have rectified that unreasonable "Hello World" script to be more reasonable in the previous article.

@Gradle Source Code
task ("hello", {
	doLast ({
		println ('Hello world!')
	})
})

But, still, it does not make sense . . .

'task' is a function and seems to be a method of the 'org.gradle.api.Project' interface (in fact, implemented by 'org.gradle.api.internal.project.DefaultProject', etc.), apparently.

However, how is that unqualified 'task' call in the script delivered to the project instance? Does the script really run inside the project class? . . . No, it runs inside a 'groovy.lang.Script' sub class.

What is going on there? Well, I need to learn some basics of Groovy in order to understand that.


2: Some Basics of Groovy


Special-Student-7-Hypothesizer
In the 1st place, how does any Groovy script become to be executed?

Groovy is based on Java, right? As I understand that Java does not let any logic sit outside any class, is the script incorporated into a Java class, I guess?

. . . Yes, it is; more precisely, it is incorporated into an automatically-generated 'groovy.lang.Script' sub class as described in the official document of Groovy.

Although the main logic goes into the 'run' method of the 'groovy.lang.Script' sub class, it is not as simple as that the whole script code just goes into the 'run' method. That is because some statements like 'import' directives and function definitions cannot go inside any method, Java-wise.

An important fact to be aware of is that Groovy method calls do not become simple Java method calls, which is the reason why the "dynamic method" functionality is possible in Groovy. . . . So, Groovy has indeed a mechanism that allows non-existent methods to be called or non-existent properties to be accessed.

Special-Student-7-Rebutter
Well, the official Groovy document looks to be fairly well-written, compared with, for example, Gradle's; it is understandable.


3: The Reality of Gradle Scripts


Special-Student-7-Hypothesizer
However, in order for the mechanism to be put in effect, of course, some arrangements are required to have been made, which I do not see inside the Gradle script. Namely, a 'methodMissing' method has to be in place.

The fact is that the Gradle script is not passed into Groovy even after it has been preprocessed.

In fact, the Gradle script is incorporated into an automatically-generated 'org.gradle.groovy.scripts.BasicScript' sub class (note that 'org.gradle.groovy.scripts.BasicScript' is a Gradle class).

Special-Student-7-Rebutter
So, the Gradle script is not really a Groovy script . . .

Special-Student-7-Hypothesizer
I can guess now that 'org.gradle.groovy.scripts.BasicScript' is in charge of delivering undefined-in-the-script-class property or method calls to the project instance.

I will not dig into how exactly 'org.gradle.groovy.scripts.BasicScript' does so, but I need to know that the Gradle script becomes a 'org.gradle.groovy.scripts.BasicScript' sub class, which is a sub class of the Groovy-standard 'groovy.lang.Script' class, and the sub class delivers undefined-in-it property or method calls to the project instance.

That level of clarification is due to users, in my opinion.

Special-Student-7-Rebutter
I agree. As the Gradle official document does not make that point clear, its discourse does not make sense; it is not understandable, really.

Special-Student-7-Hypothesizer
What I sense in the Gradle official document is a disgustingly patronizing attitude to say "You don't need to understand.". I need to understand!


References


<The previous article in this series | The table of contents of this series | The next article in this series>

2021-09-26

1: Opting to Write More Reasonable Groovy-Based Gradle Scripts

<The previous article in this series | The table of contents of this series | The next article in this series>

The pervasively-promoted way of writing Groovy-based Gradle scripts is not advantageous for fully utilizing the full potential of Gradle.

Topics


About: Gradle
About: Groovy

The table of contents of this article


Starting Context


  • The reader has a basic knowledge on Groovy or Java.

Target Context


  • The reader will know how the pervasively-promoted way of writing Groovy-based Gradle scripts is a bad idea and know the preferable way.

Orientation


This is the 1st step toward fully utilizing Gradle; further steps will be taken in following articles of this series.


Main Body

Stage Direction
Here is Special-Student-7 in a room in an old rather isolated house surrounded by some mountains in Japan.


1: Gradle Is Great, Because It Is Groovy-Based


Special-Student-7-Hypothesizer
Gradle is my primary choice as build tool; I deem it great.

Special-Student-7-Rebutter
Are you saying so based on some experience with some other build tools?

Special-Student-7-Hypothesizer
Yes, I used to use Apache Ant, but gave up on it.

Special-Student-7-Rebutter
Why?

Special-Student-7-Hypothesizer
Because it is restrictive: it has a set of things I am supposed to do, and it would be fine if I wanted to do only those things, but I am sorry, I do not want to do only what are supposed by it.

Special-Student-7-Rebutter
That seems a usual thing for any tool. You are not supposed to cut paper with a hammer.

Special-Student-7-Hypothesizer
Certainly, but I am not happy with a pair of scissors that supposes me to cut only white paper.

Special-Student-7-Rebutter
So, what are not supposed by the tool but are wanted by you?

Special-Student-7-Hypothesizer
I do programming not only in Java, but also in C++, C#, Python, and even in UNOIDL, and in Linux and in Microsoft Windows, and have to dictate what commands should be executed with what arguments on what conditions, and sometimes, some derived files have to be tweaked.

Special-Student-7-Rebutter
Does the tool not allow such things?

Special-Student-7-Hypothesizer
I am not sure whether it does not allow them absolutely, but things become very cumbersome when I try to do such sort of things: the tool is XML-based, and dictating somehow intricate logic in a XML form is irksome, if not impossible. Sometimes, I had to be resigned to put such logic into an operating system script, but that is unsatisfactory, because such a script is operating system dependent, but I do not want different versions of my build scripts.

I really do not see any merit in build tools' being XML-based.

Special-Student-7-Rebutter
Some people seem to be claiming its merit to be programming-less.

Special-Student-7-Hypothesizer
But their users are programmers! What kind of programmers are they who love programming-less-ness?

Special-Student-7-Rebutter
Reluctant programmers, I guess: not every programmer is enthusiastic on programming.

Special-Student-7-Hypothesizer
Ah, so XML-based build tools are for reluctant programmers, I understand.

Anyway, I began to think "Is there not a better built tool for my usage?".

Special-Student-7-Rebutter
And you found Gradle . . .

Special-Student-7-Hypothesizer
I found also Apache Maven, but reading some introductions for Apache Maven, I was sure I did not like it.

Special-Student-7-Rebutter
Why?

Special-Student-7-Hypothesizer
For one thing, it is XML-based, which implies its usual restrictiveness.

Special-Student-7-Rebutter
That seems to be a sufficient reason for you to turn down the tool.

Special-Student-7-Hypothesizer
For another, it promotes "Convention over Configuration".

Special-Student-7-Rebutter
Is that a problem?

Special-Student-7-Hypothesizer
Having conventions itself is not particularly any problem if I have complete freedom to do unconventionally, but I have to reasonably expect an annoying barrage of recommendations to obey conventions and hampering scarcity of information for doing unconventionally, which is the problem.

Special-Student-7-Rebutter
Is that the case for Apache Maven?

Special-Student-7-Hypothesizer
It seems so more or less; it is usually inevitable, because the tool's community is consisted of people who have adopted the tool knowing the policy; expecting otherwise is unreasonable.

On the other hand, what is great about Gradle is that not only it is scripts-based, but also its scripts are Groovy-based (or Kotlin-based, but I will omit mentioning Kotlin, because I do not use it, at least for now).

Special-Student-7-Rebutter
Is that because you are familiar with Groovy?

Special-Student-7-Hypothesizer
In fact, I was not familiar with Groovy at all, but Groovy is Java-based and allows any Java code in it, and I am familiar with Java.

Special-Student-7-Rebutter
Good for you.

Special-Student-7-Hypothesizer
And should be good for any Java programmer.

It is important that Gradle scripts are not based on a Gradle-original, probably meager and restrictive, unfamiliar language.

Special-Student-7-Rebutter
Instead on the great Java language.

Special-Student-7-Hypothesizer
Java is a very versatile programming language, and what Java can do, we can do in Gradle scripts.

Special-Student-7-Rebutter
So, is it happy ever after?

Special-Student-7-Hypothesizer
The only drawback is the heaviness, which may not be much nuisance if the environment is high-performance.


2: The Pervasively-Promoted Way of Writing Groovy-Based Gradle Scripts Is Unwise, Though


Special-Student-7-Hypothesizer
However, the pervasively-promoted way of writing Groovy-based Gradle scripts is very unwise, in my opinion.

Special-Student-7-Rebutter
"the pervasively-promoted way"? What way?

Special-Student-7-Hypothesizer
The "Hello World" script in this official document page is representing the way.

Special-Student-7-Rebutter
Well . . .

Special-Student-7-Hypothesizer
Let me cite it here.

@Gradle Source Code
task hello {
	doLast {
		println 'Hello world!'
	}
}

Special-Student-7-Rebutter
How is that very simple code "very unwise"?

Special-Student-7-Hypothesizer
The problem is that it does not make sense as Groovy code.

Special-Student-7-Rebutter
Well, what is that "task" there? Does Groovy have such a keyword?

Special-Student-7-Hypothesizer
No, it does not. "task" is a method, actually.

Special-Student-7-Rebutter
Huh? Then, is that naked (without quoted) "hello" an argument? Is that a variable? But where has it been defined?

Special-Student-7-Hypothesizer
You see that it does not make sense.

On the other hand, let us look at this code cited from the same official page.

@Gradle Source Code
4.times { counter ->
    task "task$counter" {
        doLast {
            println "I'm task number $counter"
        }
    }
}

Special-Student-7-Rebutter
Huh? Why is the task name specification suddenly a string this time?

Special-Student-7-Hypothesizer
The page offers no explanation; the 2nd code, which is incoherent with the 1st one, suddenly appears from the blue.

Special-Student-7-Rebutter
I wonder who thought that that kind of document was OK . . .

Special-Student-7-Hypothesizer
In fact, the task name specification should really be always a string, at least as Groovy code.

Special-Student-7-Rebutter
Then, why is the 1st code OK?

Special-Student-7-Hypothesizer
Gradle is surreptitiously making it OK.

Special-Student-7-Rebutter
Why is it making defective code OK?

Special-Student-7-Hypothesizer
They are promoting such defective code.

Special-Student-7-Rebutter
Why?

Special-Student-7-Hypothesizer
Because they think that such defective code is neat, I guess.

Special-Student-7-Rebutter
"neat"? . . . I do not understand the concept of "neat"ness.

Special-Student-7-Hypothesizer
It is a Biasian concept, which I myself do not really understand well.

Anyway, obviously, the 1st code is trying hard to hide the fact that "task" is a method and in fact, the fact that the script is Groovy-based.

Special-Student-7-Rebutter
Why would it want to do such a thing?

Special-Student-7-Hypothesizer
I can only guess that it feels neat for some people.

But it feels as unwise to me as neat to them, because for one, it is hiding the tool's strongest point (at least for me) of being Groovy-based (I said that the tool is great because it is Groovy-based), for another, the tactics is not working.

Special-Student-7-Rebutter
When you say "not working" . . .

Special-Student-7-Hypothesizer
The 2nd code requires "task" to be a method, otherwise that "task" in that loop would not make sense. So, the fact cannot be hidden, unless they prohibit the 2nd code, making the tool a restrictive one that has no merit over XML-based ones.

Special-Student-7-Rebutter
. . . Is there not another reason for their promoting the way, a reason even we can understand, I mean, other than "neat"ness?

Special-Student-7-Hypothesizer
Like labor-saving? But it saves only 2 characters (2 double quotes). Going that way for saving just 2 characters would be too foolish, even for Biasians.

Special-Student-7-Rebutter
So, they are promoting the way because it is just "neat". . . . This "neat"ness is very enigmatic to me.


3: A More Reasonable "Hello World" Script


Hypothesizer 7
This is a more reasonable "Hello World" script.

@Gradle Source Code
task ("hello", {
	doLast ({
		println ('Hello world!')
	})
})

"task" is really a method of 'org.gradle.api.Project' (an interface) implemented by 'org.gradle.api.internal.project.DefaultProject', and "hello" (which has to be a string) and the "{~}" block (which is a closure) are the arguments passed into the method.

Special-Student-7-Rebutter
Do you mean that the script becomes a 'DefaultProject' instance?

Special-Student-7-Hypothesizer
Actually no.

Special-Student-7-Rebutter
Huh? Then, why can the method be called from the script without any qualification?

Special-Student-7-Hypothesizer
A good point; we will see it in the next article.

Special-Student-7-Rebutter
Well, otherwise, the code is very clear that way.

Special-Student-7-Hypothesizer
The '(' and ')' pairs can be omitted according to the Groovy specifications, but that is not any good idea, in my opinion.

The reason is the entailed inconsistency: the pair is required anyway if there is no argument or if ambiguity would sneak in otherwise. So, sometimes, the pair appears, and sometimes, not, which I call inconsistency. Besides, I have to ask myself "Isn't this ambiguous?" every time. Omitting the 2 characters is not much labor-saving that is worth such inconsistency and required extra care.

Special-Student-7-Rebutter
Having those '(' and ')' pairs makes it clear that they are function calls.

Special-Student-7-Hypothesizer
Other than that, the official "Hello World" code omits just the """ and """ pair and a single ",".

As a conclusion, the pervasively-promoted way is barely labor-saving.


4: The Pervasively-Promoted Way of Writing Groovy-Based Gradle Scripts Is Disgusting, in Fact


Special-Student-7-Hypothesizer
In fact, I deem the pervasively-promoted way of writing Groovy-based Gradle scripts not only unwise, but also disgusting.

It reeks of an attitude I am really repulsed by.

Special-Student-7-Rebutter
What attitude?

Special-Student-7-Hypothesizer
The attitude that tells you "You don't need to understand; just do as I tell you!".

Special-Student-7-Rebutter
Where do you sense the attitude?

Special-Student-7-Hypothesizer
The way is not just not helping us to understand what is really going on, but intentionally trying to prevent us from understanding it, which is nothing but telling you "You don't need to understand.".

Special-Student-7-Rebutter
I cannot deny that.

Special-Student-7-Hypothesizer
And it is evidently also telling you to do as it tells you.

Special-Student-7-Rebutter
A tutorial is usually so.

Special-Student-7-Hypothesizer
So, what else can it be?

Special-Student-7-Rebutter
Such an attitude is certainly not nice; it is an attitude toward slaves: slaves are supposed to blindly obey without any understanding, but free men are supposed to judge themselves based on what? of course on understandings.

Special-Student-7-Hypothesizer
I wonder why many Biasians are happy with such an attitude.

Special-Student-7-Rebutter
They may not be particularly "happy".


5: We Can Do This Because We Understand


Special-Student-7-Hypothesizer
As now we understand, we can do like this.

@Gradle Source Code
def closureA = {int a_index ->
	println String.format ('Hello world, %s!', a_index)
}

for (int l_index = 0; l_index < 10; l_index ++) {
	def int l_indexCopied = l_index
	task (String.format ("hello%d", l_index), {
		doLast ({
			closureA (l_indexCopied)
		})
	})
}

1st, as '{~}' block is a closure, we can define a closure only once and reuse it, possibly customized via some arguments (note that passing different variables as 'l_indexCopied's (a new variable per iteration) is important, because the closure encloses the passed variable, not the passed value).

2nd, tasks can be dynamically created with the task names dynamically determined.

Special-Student-7-Rebutter
Certainly.

Special-Student-7-Hypothesizer
That kind of scripts can be written only because we understand Gradle scripts as Groovy code.


References


  • Gradle Inc.. (2020). Build Script Basics. Retrieved from https://docs.gradle.org/6.7.1/userguide/tutorial_using_tasks.html
<The previous article in this series | The table of contents of this series | The next article in this series>