Skip to content

Simplify Gradle Plugin #1194

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Aug 14, 2020
Merged

Simplify Gradle Plugin #1194

merged 21 commits into from
Aug 14, 2020

Conversation

sellmair
Copy link
Member

@sellmair sellmair commented Jul 21, 2020

Closes #1108
Closes #1107
Closes #995
Closes #1113
Closes #1114
Closes #1285
Closes #1286
Closes #1287
Closes #1288
Closes #1289
Closes #1290
Closes #1291
Closes #1292
Closes #1274
Closes #1106

@sellmair sellmair added the runner: Gradle plugin An issue/PR related to Dokka's Gradle plugin label Jul 21, 2020
@sellmair sellmair added this to the 1.4.0 milestone Jul 21, 2020
@sellmair sellmair force-pushed the gradle-plugin-maintenance branch from 377ac8b to c86185e Compare July 21, 2020 12:02
@sellmair sellmair force-pushed the gradle-plugin-maintenance branch from a87c0bf to dc6d1ac Compare July 22, 2020 07:32
@sellmair sellmair force-pushed the gradle-plugin-maintenance branch 3 times, most recently from ac54b16 to 7e5d83f Compare July 22, 2020 13:55
@sellmair sellmair force-pushed the gradle-plugin-maintenance branch 16 times, most recently from 72dd286 to 6abbd43 Compare August 6, 2020 12:28
@sellmair sellmair force-pushed the gradle-plugin-maintenance branch 4 times, most recently from 529200f to 6821309 Compare August 10, 2020 09:06
Comment on lines 7 to 8
fun ExternalDocumentationLink.Companion.jdk(jdkVersion: Int): ExternalDocumentationLinkImpl {
return ExternalDocumentationLink(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

=

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return ExternalDocumentationLink("https://kotlinlang.org/api/latest/jvm/stdlib/")
}

fun ExternalDocumentationLink.Companion.androidSdk(): ExternalDocumentationLinkImpl {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

=

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

)
}

private fun GradleDokkaSourceSetBuilder.moduleNameOrDefault(): String {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

=

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return name
}

private fun GradleDokkaSourceSetBuilder.externalDocumentationLinksWithDefaults(): Set<ExternalDocumentationLinkImpl> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

=

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sellmair sellmair force-pushed the gradle-plugin-maintenance branch from cf26c3e to 689a528 Compare August 14, 2020 13:11
@sellmair sellmair force-pushed the gradle-plugin-maintenance branch from 3959b47 to 69a6ac7 Compare August 14, 2020 15:14
@sellmair sellmair merged commit 613d2e4 into master Aug 14, 2020
private set

@get:Nested
internal val childDokkaTasks: Set<AbstractDokkaTask>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A task inside a task does not seem right. Why is this needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This kind of "parent" tasks are managing multiple "child" tasks. Accessing them like this is not just there for convenience. While not all parent tasks do have task dependencies on its children, they should still re-run if any of the child's configuration changes. This happens to work pretty nicely with this @Nested pointing to the actual tasks ☺️

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mh. Usually. This is not a pattern that should be required. I haven't seen this before. :)

While not all parent tasks do have task dependencies on its children, they should still re-run if any of the child's configuration changes.

Maybe you can show a concrete use case? I don't quite understand what that means.


open class DokkaCollectorTask : AbstractDokkaTask() {
open class DokkaCollectorTask : AbstractDokkaParentTask() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably just 💄 , but I would recommend making all task classes abstract instead of open.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the hint! ☺️
Pushed a commit to change this ✅

import org.jetbrains.dokka.utilities.cast
import kotlin.reflect.typeOf

internal inline fun <reified T : Any> ObjectFactory.safeProperty() = property<T?>()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need these extension methods and can not use property<T>() directly? It's already <T> not <T?>.

Copy link
Member Author

@sellmair sellmair Aug 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an interesting question:
So the issue that I have with properties is, that it was really hard for me to track whether or not I expect some value being present in a Property, or if it is actually, truly "optional". The current implementation of Property also does not care. It is like "can be there, might not".

Therefore, I started tracking my expectations of any Property myself in the Type-System:
The convention internally here is:

  • Property<T?> -> No value required
  • Property<T> -> Fail if value is missing

See this getSafe method, that I am using everywhere. This will use orNull if the type T is optional and get otherwise.

@OptIn(ExperimentalStdlibApi::class)
internal inline fun <reified T> Provider<T>.getSafe(): T =
    if (typeOf<T>().isMarkedNullable) orNull as T
    else get()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exactly do you mean by "tracking expectations"? Is there any tooling that helps you here to ensure that the value is set?

Gradle should already do this for you. If you mark a property that you use somewhere as input with @Optional keeping it unset is allows (basically it is nullable) otherwise, you will get an error if it is not set.


@Input
@Optional
val displayName: Property<String?> = project.objects.safeProperty()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use Property here. A property can always be unset and usually that should be enough and there shouldn't be the need to set it to null. You can still use @Optional to state that unset is a valid state.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean I should use a Kotlin property? var displayName: String? = null

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Term property is quite overloaded. :)

This is what I mean:

val displayName: Property<String> = project.objects.property()

or, if you can make the class abstract, just:

abstract val displayName: Property<String>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I saw the other comment, which answers why you use <String?>. I'll answer there later.

val displayName: Property<String?> = project.objects.safeProperty()

@InputFiles
val sourceRoots: ConfigurableFileCollection = project.files()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of objects.fileCollection() would be the better API if you just need an empty collection (If it is available in the Gradle version you want to support).

Even better would be to make the class and all the properties abstract. Like:

    @get:InputFiles
    abstract val sourceRoots: ConfigurableFileCollection

Then Gradle will inject everything and you have much less boilerplate. And you probably won't have to pass project and/or objects around.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot, I did not know about objects.fileCollection and I also did not know about the fact, that Gradle will inject into Abstract classes here as well, which is awesome!! 🤪

Pushed a commit for objects.fileCollection. Making this object abstract will require some tests to be adapted. I will wait for another PR to be merged first to tackle this! :)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically everywhere where you pass a class to Gradle so that Gradle does the instantiation, you can use an abstract class and will get an object of an instrumented subclass that does all these things automatically.

@IgnatBeresnev IgnatBeresnev deleted the gradle-plugin-maintenance branch October 12, 2022 20:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment