Difference between extra properties and variables in Gradle tasks? - variables

What exactly is difference between these two tasks in Gradle:
task sampleTask {
String myFile = "sample.txt"
delete myFile
}
task sampleTask {
ext.myFile = "sample.txt"
delete myFile
}
Are they basically the same or do they differ somehow?

The first snippet declares a local variable which is only visible within the enclosing block. The second snippet adds an extra property that extends Gradle's object model and is visible everywhere the task is visible. Unless you have a reason to extend the object model, use a local variable.

Related

How do you set up a property in a custom gradle task?

I want to write a task that takes a directory from , does something with the files in it and writes the result into some other directory to.
I've been led to believe this was the way to define such a task (kotlin dsl):
package my.app
abstract class FooBarTask : DefaultTask() {
#get:InputDirectory
abstract val from: Property<Directory>
#get:OutputDirectory
abstract val to: Property<Directory>
#TaskAction
fun doSomething() {
println("Hakuna Matata")
}
}
now how do I set the from and to value in a groovy-based build.gradle?
def myTask = tasks.register('myTask', FooBarTask) {
from = layout.projectDirectory.dir("foo")
to = layout.buildDirectory.dir("bar")
}
this results in
Could not create task ':my-subproject:myTask'.
> Please use the ObjectFactory.directoryProperty() method to create a property of type Directory.
and it shouldn't.
How do you correctly define a directory property in a custom task?
Gradle has the specialized DirectoryProperty, that offers some additional functionality, compared to the plain Property<Directory> which is one of the implemented interfaces. So this specialized type should be used when declaring directory inputs/outputs.
I'm actually not a 100% sure what caused the error you saw.

can I create top level instance in kotlin?

If I have app.kt file in kotlin, can I create instance like appKt()? Thanks.
Kotlin has top level function. For example I can write in app.kt:
val a = 123
fun abc() {}
appKt.abc()
my question is if I can create appKt instance and call instance method
Only classes can be instanced.
Instead of loose function fun abc() {}, this should be the method of a class:
class appKt() {
// private var a: Integer = 123
fun abc() {}
}
No, you can't put arbitrary code at the top level.
You can put only definitions of classes (just as in Java), objects, functions, and properties.
It wouldn't make much sense to put loose code there, anyway: when would it run?
It's not clear what you're trying to achieve with this. If you want some code that gets run when your program starts up, then you could put it into a top-level function — but you'd then have to call that function (e.g. from your main() method). Or you could put it in the init block of the companion object to a class that you know will be loaded. Or if you're using a framework such as Android or Spring, then that will probably provide ways to specify code to be run at start-up.

Get gradle property from settings

So i am trying to transition my app to Kotlin DSL the issue i am facing is with accessing gradle.properties the way i was doing with groovy was like that, i am trying to access gradle.properties props from my settings.gradle.kts file
def propName = 'prop.name.something'
def propDisabled = Boolean.valueOf(properties[propName])
I tried several ways of accessing it with settings.extra[propName].toBoolean. It just seems there should be more straight way to access those properties?
The proper way to access a property declared in gradle.properties in settings.gradle.kts is by delegating it to the settings object:
val myProperty: String by settings
Note that it is mandatory to specify the property type String explicitly here.
This will get the property myProperty from the gradle.properties file. Note that if you use it in the pluginManagement { ... } block, then the property declaration needs to be placed inside pluginManagement { ... }, too, as this block is evaluated before everything else in the script.
However, if a property name contains symbols that are illegal in Kotlin identifiers, such as ., which is not allowed even in backticked identifiers, then you can't access it as a delegated property. There's no way to access such a property from the Gradle model as of Gradle 6.7, but, given that gradle.properties is just a .properties file, you can read it into a Java Properties instance:
val properties = File(rootDir, "gradle.properties").inputStream().use {
java.util.Properties().apply { load(it) }
}
val propNameSomething = properties.getValue("prop.name.something") as String

Companion object with extension function in kotlin?

I would like to have extension function and use logger from kotlin-logging and have constants inside companion object.
My function:
fun String.toFoo(): Foo {
logger.debug { "Mapping [$this] to Foo" }
if(MY_CONST.equals(this) {
...
}
Question is where I should put val logger = KotlinLogging.logger {} and MY_CONST since I cannot use companion object with an extension function?
If you just want you logger to be a singleton you can make an object that contains and instance of the logger and reach it from there.
Object LoggerSingleton( val logger = KotlinLogging.logger{})
Then in your extension function
fun String.toFoo(): Foo {
LoggerSingleton.logger.debug { "Mapping [$this] to Foo" }
if(MY_CONST.equals(this) {
}
Since an Object in Kotlin is guaranteed to have only one instance you won't have a different logger for each use of toFoo.
EDIT
To keep the desired class name
Use this signature
Like so:
Object StringLoggerSingleton( val logger = KotlinLogging.logger("String"))
I do not know what you want to accomplish with your logger, but I show you what I did already ;-)
Usually I put extension functions in its own file named similar to what the function is actually extending (e.g. either StringExtensionFunction or if is more related to its purpose and maybe only available if certain dependencies are available, I also did something like, e.g. JsoupExtensionFunctions (where there was a String.toJsoupHtml(), File.toJsoupXml(), etc.)).
If I then need constants I just place them within that file, e.g. by just writing something like:
private const val MY_CONST = "my_const_value"
No surrounding class, no surrounding object.
Regarding the logger... as loggers are usually tied to a certain name/class, I usually put a logger inside every (important) class or associate some logger to specific names... So I am not completely sure what your intent is here... If it's ok for you that the logger is returning the container of your extension function (maybe StringExtensionFunction.kt), then you can also put a logger-val inside that file similar to what I showed with MY_CONST.
If your intention was rather to reuse the callers logger, that might not work so easily... (the easiest would then probably be to pass it to the function, but usually you do not want that)... and other mechanisms may not really be worth it ;-)

filter jython-generated property fields for getters/setters from dir() result

I ran into a problem using java objects in jython today because jython is trying to be intelligent and automatically creates properties for (simple) getter/setter methods - For each method a field with the leading get/set removed and the next letter converted to lowercase is created:
//java code
class MyClass {
public List<Thing> getAllThings() { ... }
public List<Thing> getSpecificThings(String filter) { ... }
public void setSomeThing(SomeThing x) { ... }
[...]
}
#jython code
obj = MyClass()
hasattr(obj, "allThings") #-> True
hasattr(obj, "specificThings") #-> False because getSpecificThings has a param
hasattr(obj, "someThing") #-> False BUT
"someThing" in dir(obj) #-> True
The last line summarizes my problem here - the result of dir contains these fields (even when executed on obj.class instead of obj). I need a list of all methods callable on the object, which for my objects basically is the result of dir without these properties and filtered to exclude everything inherited from java.lang.Object and things starting with an underscore (the purpose of this is to automagically convert some python classes to java equivalents, e.g. dicts to Maps). In theory I could use __dict__ which doesn't contain them, but this would mean I'd have to recursively evaluate the base classes' __dict__s too, which I would like to avoid. What I am currently doing is seeing if the attribute actually exists and then check if it has an argslist attribute (meaning it is a method), which is true for every dir entry except for the generated properties:
for entry in dir(obj):
#skip things starting with an underscore or inherited from Object
if entry.startswith("_") or entry in dir(java.lang.Object): continue
#check if the dir entry is a fake setter property
if not hasattr(obj, entry): continue
#check if the dir entry has an argslist attribute (false for getter props)
e = getattr(obj, entry)
if not hasattr(e, "argslist"): continue
#start actual processing of the entry...
The problem with this approach is that the objects in question are interfaces to beans and a getSomething method typically fetches data from a database, so the getattr call for a property makes a roundtrip to the DB which can take multiple seconds and waste tons of memory.
Can I stop jython from generating these properties? If not, does anybody have an idea how I can filter out the properties without accessing them first? The only thing I could think of was checking if dir contains a method named get/set<property>, but this seems hackish and could generate false positives, which must be avoided.
The answer was easier than anticipated. While a hasattr of the property is True for an object instance, it is False for the objects class if the get-method in question is not static - the class doesn't have the property as you can't execute the method on it. The updated loop now looks like this:
for entry in dir(obj):
#skip things starting with an underscore or inherited from Object
if entry.startswith("_") or entry in dir(java.lang.Object): continue
#check if the dir entry is a fake property
if not hasattr(obj.class, entry): continue
#check if the dir entry has an argslist attribute (false if not a method)
e = getattr(obj, entry)
if not hasattr(e, "argslist"): continue
#start actual processing of the entry...