Slackbot
05/31/2023, 11:59 AMJavi
05/31/2023, 12:07 PMJavi
05/31/2023, 12:08 PMJavi
05/31/2023, 12:08 PMVampire
05/31/2023, 12:29 PMExtensionAware
even if it does not declare it, so if the extension in question does not declare it, just cast it to ExtensionAware
.
Regarding order Javi already said it, plugins should not depend on application order or something is done badly. Instead either apply the plugin you need in your plugin if it must always be there, or react to the other plugin being applied using pluginManager.withPlugin("...") { ... }
Javi
05/31/2023, 12:42 PMVampire
05/31/2023, 12:44 PMExtensionAware
if it does not declare it, add the extension like you would also to the project directly.Javi
05/31/2023, 12:47 PMExtensionAware
, how can I add a new property to it? Would it be accessed under the same name?
For example, if the plugin declares FooExtension { val foo: Property<String> }
as foo
. How can I add something after using val foo = extensions.getByType<FooExtension>() as ExtensionAware
?Javi
05/31/2023, 12:51 PMExtensionAware
// Extensions are just plain objects, there is no interface/type
class MyExtension {
String foo
MyExtension(String foo) {
this.foo = foo
}
}
// Add new extensions via the extension container
project.extensions.create('custom', MyExtension, "bar")
// («name», «type», «constructor args», …)
// extensions appear as properties on the target object by the given name
assert project.custom instanceof MyExtension
assert project.custom.foo == "bar"
// also via a namespace method
project.custom {
assert foo == "bar"
foo = "other"
}
assert project.custom.foo == "other"
// Extensions added with the extension container's create method are themselves extensible
assert project.custom instanceof ExtensionAware
project.custom.extensions.create("nested", MyExtension, "baz")
assert project.custom.nested.foo == "baz"
// All extension aware objects have a special "ext" extension of type ExtraPropertiesExtension
assert project.hasProperty("myProperty") == false
project.ext.myProperty = "myValue"
// Properties added to the "ext" extension are promoted to the owning object
assert project.myProperty == "myValue"
Daniele Segato
05/31/2023, 1:09 PMinterface EnvironmentPluginExtension {
val flavorName: Property<String>
}
and inside my plugin:
val extension = extensions.getByType<ApplicationExtension>()
val envExtension = (extension as ExtensionAware).extensions.create<EnvironmentPluginExtension>("environments", EnvironmentPluginExtension::class.java)
envExtension.flavorName.convention("environment")
I now have environments
inside android
BUT
from the build.gradle.kts
script I need to do
android {
environments {
flavorName.set("test")
}
}
instead of flavorName = "test"
and inside the plugin if I try to read it:
envExtension.flavorName.get()
I get the "convention": environment
do I have to use doLast
?Daniele Segato
05/31/2023, 1:10 PMJavi
05/31/2023, 1:11 PMProperty<*>
is the way to configure things, and it is used with set
, in the future it will be syntax sugar so you can set properties via =
Javi
05/31/2023, 1:11 PMProperty
, only on onVariants
and so on lambdas if I remember correctly.Daniele Segato
05/31/2023, 1:13 PM=
tho'Javi
05/31/2023, 1:14 PMProperty<*>
, and you want to avoid ordering issues due using get()
in the configuration phase, you would need to use variants lambdas
Check this: https://developer.android.com/build/extend-agpJavi
05/31/2023, 1:15 PMProperty
, but if you check those variants lambdas you would see that they use properties so you can use something like
foo.set(bar)
, without calling bar.get()
Javi
05/31/2023, 1:16 PMVampire
05/31/2023, 1:18 PMI now haveinsideenvironments
android
BUT
from theAs I said, you want ascript I need to dobuild.gradle.kts
NamedDomainObjectContainer
Daniele Segato
05/31/2023, 1:40 PMAs I said, you want aYes I got that, I'm going through steps, starting simple with a string property to see how i can write it and read it and at present I'm not able to read itNamedDomainObjectContainer
Vampire
05/31/2023, 1:46 PMget()
before the applying project had a chance to configure it.
Don't do that with the lazy properties, but use them lazy, that's all their purpose.Daniele Segato
05/31/2023, 1:47 PMJavi
05/31/2023, 1:48 PMotherPluginExtension.someProperty.set(yourProperty)
Javi
05/31/2023, 1:48 PMDaniele Segato
05/31/2023, 1:49 PMJavi
05/31/2023, 1:50 PMotherPluginExtension.someProperty.set(yourProperty.map { ... })
otherPluginExtension.someProperty.set(
provider {
computeValue(propertyOne.get(), propertyTwo.get())
}
)
Vampire
05/31/2023, 1:58 PMVampire
05/31/2023, 1:59 PMinterface Environment : Named {
val propertiesFile: RegularFileProperty
val disableReleaseBuild: Property<Boolean>
}
pluginManager.withPlugin("idea") {
val idea = project.extensions.getByType<IdeaModel>() as ExtensionAware
val environments = objects.domainObjectContainer(Environment::class)
idea.extensions.add("environments", environments)
environments.whenObjectAdded {
// ...
}
// or
environments.configureEach {
// ...
}
// or whatever
}
allows you to do in the consumer
idea {
environments {
create("dev") {
propertiesFile.set(file("dev.properties"))
disableReleaseBuild.set(true)
}
}
}
Daniele Segato
05/31/2023, 2:05 PMenvironment.$env.properties
, parse it and set buildConfig
for each property I find in there
Instead of doing this automatically I'd like to give configurability for
• enable the buildConfig feature only if there are environment configured
• the name of the flavor dimension (default to environment)
• which environment to use, which file to look for for each environment (with default again, if not specified the file name is the one above)Vampire
05/31/2023, 2:12 PMVampire
05/31/2023, 2:23 PMabstract class Environment @Inject constructor(val name: String) {
var propertiesFile: File? = null
var disableReleaseBuild: Boolean? = null
}
abstract class EnvironmentsExtension {
@get:Inject
abstract val objects: ObjectFactory
fun create(name: String, configuration: Action<Environment>) {
val environment = objects.newInstance<Environment>(name)
.also(configuration::execute)
// ...
}
}
pluginManager.withPlugin("idea") {
val idea = project.extensions.getByType<IdeaModel>() as ExtensionAware
idea.extensions.create("environments", EnvironmentsExtension::class)
}
and then
idea {
environments {
create("dev") {
propertiesFile = file("dev.properties")
disableReleaseBuild = true
}
}
}
Daniele Segato
05/31/2023, 2:23 PMDaniele Segato
05/31/2023, 3:09 PMDaniele Segato
05/31/2023, 3:10 PMDaniele Segato
06/06/2023, 1:11 PMNamed
one. Works great. But I also want to explore the second with the Action. And understand both better.
I think I need to read
https://docs.gradle.org/current/userguide/custom_gradle_types.html
and you previously pointed me to https://docs.gradle.org/current/userguide/custom_gradle_types.html
I’ve looked at the code for the Named interface but I don’t understand how all that is generated automatically hopefully those docs will help me out figure it out.
Does the injection part works out of the box without any dependency?Vampire
06/06/2023, 1:16 PMobjects.newInstance
, extensions.create
, ...) Gradle automatically decorates the class to make it ExtensionAware
(even if that interface is not declared, hence you can cast almost all objects to it and use it), to implement Named
if declared and not done manually, to implement `@Inject`ed services, to implement abstract `Property`s and so on. That way you can save quite some boilerplate code by just let Gradle do its magic.