Slackbot
11/02/2022, 7:31 PMZak Taccardi
11/02/2022, 7:32 PMconfig {
// configure my convention plugin
}
// it's fine if this throws - and my convention plugin logic runs immediately after the `config` lambda above ends
Chris Lee
11/02/2022, 7:33 PMChris Lee
11/02/2022, 7:34 PMfun Project.config(block : (MyExtension).() -> Unit ) {
val ext = <lookup extension>
ext.apply(block)
// do magic here
}
Zak Taccardi
11/02/2022, 7:35 PMplugins {
id("me.conventions.library.android")
}
config { // emits `MyAndroidLibraryConventionExtension`
}
plugins {
id("me.conventions.app.android")
}
config { // emits `MyAndroidAppConventionExtension`
}
where the same wording of config { }
lambda emits a different value based on the convention plugin appliedZak Taccardi
11/02/2022, 7:36 PMplugins {
id("me.conventions.library.android")
}
configLibrary { // emits `MyAndroidLibraryConventionExtension`
}
plugins {
id("me.conventions.app.android")
}
configApp { // emits `MyAndroidAppConventionExtension`
}
Javi
11/02/2022, 7:38 PMZak Taccardi
11/02/2022, 7:38 PMZak Taccardi
11/02/2022, 7:39 PMpreAfterEvaluate { }
lolJavi
11/02/2022, 7:39 PMJavi
11/02/2022, 7:39 PMZak Taccardi
11/02/2022, 7:40 PMJavi
11/02/2022, 7:40 PMplugins {
id("me.convention")
}
config {
android {
library { ... }
// app { ... }
}
}
Javi
11/02/2022, 7:41 PMJavi
11/02/2022, 7:42 PMplugins {
id("me.convention")
}
config {
android {
features {
dagger()
}
library()
}
}
Zak Taccardi
11/02/2022, 7:44 PMplugins {
id("me.convention")
}
config {
}
Zak Taccardi
11/02/2022, 7:45 PMChris Lee
11/02/2022, 7:46 PMproject.extensions.create<Something>("foo")
creates foo
accessor.Zak Taccardi
11/02/2022, 7:47 PMplugins {
id("me.convention")
}
config {
}
// the problem is that I need a hook to run here, immediately before `afterEvaluate { }` - which gradle does not seem to provide
Chris Lee
11/02/2022, 7:49 PMfun Project.config(block : (MyExtension).() -> Unit ) {
val ext = <lookup extension>
ext.apply(block)
// do magic here
}
Chris Lee
11/02/2022, 7:49 PMZak Taccardi
11/02/2022, 7:50 PMZak Taccardi
11/02/2022, 7:50 PMZak Taccardi
11/02/2022, 7:51 PMconfig { }
- not configLibrary { }
/ configApp { }
Chris Lee
11/02/2022, 7:51 PMconfig
, such that the DSL inside config is extensible.Chris Lee
11/02/2022, 7:51 PMconfig {
library {
}
}
Chris Lee
11/02/2022, 7:52 PMZak Taccardi
11/02/2022, 7:53 PMconfig {
library {
}
}
I’d want library
to fail compilation (as opposed to a runtime error) if we are working within the <http://convention.app|convention.app>
pluginZak Taccardi
11/02/2022, 7:53 PMChris Lee
11/02/2022, 7:54 PMplugins {
id("my.rust.plugin")
}
cloudshift {
golang {
}
rust {
}
}
…this would fail to compile as my.golang.plugin
hasn’t been applied (which would register the golang extension).Zak Taccardi
11/02/2022, 7:55 PMChris Lee
11/02/2022, 7:55 PMChris Lee
11/02/2022, 7:55 PMextensionAware.extensions.create<GoLangExtension>("golang")
Zak Taccardi
11/02/2022, 7:57 PMplugins {
id("my.rust.plugin")
}
golang {
}
rust {
}
cloudshift {
}
which isn’t idealChris Lee
11/02/2022, 7:57 PMcloudshift
.Javi
11/02/2022, 7:58 PMlibrary
+ app
, you can know that state and emit an error in compile time with a custom message indicating that library
with app
can't workJavi
11/02/2022, 7:59 PMlibrary { }
app { }
Zak Taccardi
11/02/2022, 7:59 PMcloudshift.goLang {
}
Javi
11/02/2022, 7:59 PMChris Lee
11/02/2022, 8:00 PMDoes that mean you can do the following then:
```cloudshift.goLang {
}```…only if you also register an extension val on cloudshift for golang.
Zak Taccardi
11/02/2022, 8:00 PMyou can know that state and emit an error in compile time with a custom message indicating thatWell it would mean compilation has to run and fail first. both are still on the compile classpath, and I would want to use generated extensions to avoid themwithlibrary
can’t workapp
Chris Lee
11/02/2022, 8:00 PMpublic val CloudShiftExtension.golang: GoLangExtension
get() = extensions.getByType()
Chris Lee
11/02/2022, 8:01 PMcloudshift.golang
to work. separate from
public fun CloudShiftExtension.golang(action: GoLangExtension.() -> Unit): Unit
Chris Lee
11/02/2022, 8:01 PMVampire
11/02/2022, 8:01 PMZak Taccardi
11/02/2022, 8:02 PMChris Lee
11/02/2022, 8:02 PMVampire
11/02/2022, 8:02 PMZak Taccardi
11/02/2022, 8:02 PMZak Taccardi
11/02/2022, 8:03 PMVampire
11/02/2022, 8:04 PMconfig {
whatever {
// actual configuration here
}
}
and then you can do your post-configure actions in the whatever
function after executing the given action block and throw if the method is called a second time.Zak Taccardi
11/02/2022, 8:08 PMdata class
which is nasty
// setting `moduleConfig` executes our convention plugin configuration logic immediately
// and can only happen once per project
moduleConfig = me.Module(
gradlePath = project.path, // silly
convention = LIBRARY_JAVA, // silly - should know automatically the library convention based on the applied plugin
isPublished = true, // silly, bc our app can't be published
)
I want to change it to:
config {
isPublished = true // field would not exist in an Android app project
}
Zak Taccardi
11/02/2022, 8:09 PMZak Taccardi
11/02/2022, 8:15 PMmoduleConfig = config {
}
and evaluate immediately once moduleConfig
is setVampire
11/02/2022, 8:36 PMinterface AppConfigValues {
val setting: Property<String>
}
abstract class AppConfig {
@get:Inject
abstract val objects: ObjectFactory
infix fun ig(action: Action<AppConfigValues>) {
val setting = objects.newInstance<AppConfigValues>().also(action::execute).setting.get()
println("configured $setting")
}
}
project.extensions.create<AppConfig>("conf")
and then
conf ig {
setting.set("YAY!")
}
Vampire
11/02/2022, 8:37 PMinterface AppConfigValues {
val setting: Property<String>
}
abstract class AppConfig {
@get:Inject
abstract val objects: ObjectFactory
operator fun rem(action: Action<AppConfigValues>) {
val setting = objects.newInstance<AppConfigValues>().also(action::execute).setting.get()
println("configured $setting")
}
}
project.extensions.create<AppConfig>("config")
and then
config % {
setting.set("YAY!")
}
Vampire
11/02/2022, 8:37 PMVampire
11/02/2022, 8:40 PMconfig .. {
setting.set("YAY!")
}
or any other, just pick oneVampire
11/02/2022, 8:40 PMinvoke
as the type-safe accessor with action argument will win thenVampire
11/02/2022, 8:44 PMinvoke
that is.
Two argument invoke
would work too:
interface AppConfigValues {
val setting: Property<String>
}
abstract class AppConfig {
@get:Inject
abstract val objects: ObjectFactory
operator fun invoke(nothing: Nothing?, action: Action<AppConfigValues>) {
val setting = objects.newInstance<AppConfigValues>().also(action::execute).setting.get()
println("configured $setting")
}
}
project.extensions.create<AppConfig>("config")
and then
config(null) {
setting.set("YAY!")
}
Zak Taccardi
11/03/2022, 1:59 AMZak Taccardi
11/03/2022, 2:00 AMProperty<String>
has to have:
setting.set("YAY!")
instead of:
setting = "YAY!"
bc the latter is so much nicer. I wish kotlin could support both options somehowVampire
11/03/2022, 2:05 AMChris Lee
11/03/2022, 2:37 AMpublic var <T> Property<T>.finalValue: T
get() = throw UnsupportedOperationException("Do not use property.value; call property.get() if that is what is intended, or use .from(property) to chain properties together")
set(value) = value(value).finalizeValue()
Chris Lee
11/03/2022, 2:37 AMpublic var <T> Property<T>.value: T
get() = throw UnsupportedOperationException("Do not use property.value; call property.get() if that is what is intended, or use .from(property) to chain properties together")
set(value) {
value(value)
}
Zak Taccardi
11/03/2022, 9:58 PMZak Taccardi
11/03/2022, 9:58 PMChris Lee
11/03/2022, 10:08 PM