dokka
dokka copied to clipboard
Gradle plugin initializes the output directory too eagerly
Describe the bug
Any changes of the Project.outputDir
property by another plugin (loaded after the Dokka plugin) or by my own build configuration are not reflected in the default dokka
task configuration, resulting in pollution of the project work tree.
Expected behaviour
If I change the output directory for my build, the dokka
task should output its results there.
To Reproduce
- Add the Dokka build plugin to any Gradle/Kotlin project.
- Write something like
project.buildDir = "$projectDir/output"
after application of the Dokka plugin. - Run
./gradlew dokka
and observe that the output is produced not to$projectDir/output
but to$projectDir/build
, which is the default build directory in Gradle.
Dokka configuration
Dokka configuration does not matter, and this can be reproduced without any configuration.
Installation
- Operating system: macOS
- Build tool: Gradle v5.6.4
- Dokka version: 0.10.0
Additional context
At the moment, the output directory of the dokka
task is initialized by directly assigning a String
property to a value derived from project.buildDir
within the plugin application code. This interacts poorly with build setups where the build directory differs from the default one: if I have a plugin which is applied after Dokka and which modifies the build directory, then the dokka
task will still output to the default directory, polluting the project work tree. Same if I modify the build directory in my script - the most important thing is that this modification happens after the Dokka plugin application.
I believe that the proper solution here is to rely on the lazy configuration mechanisms in Gradle, and e.g. change the DokkaTask.outputDirectory
type to DirectoryProperty
, with the default value derived from project.layout.buildDirectory
.
I believe that 1.4.10 migrated to Gradle properties, which should fix this issue
@kamildoleglo I have yet to test it, but looking at the implementation, this does not seem to be the case.
In AbstractDokkaTask
, the output directory's convention is set directly to the result of the the defaultDokkaOutputDirectory()
function:
https://github.com/Kotlin/dokka/blob/4ddaafb8ec1a45d2696833d1a441f7dbdbb319dd/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaTask.kt#L35-L36
which is defined to return a simple File
value:
https://github.com/Kotlin/dokka/blob/49d4757acd8e6ef974baccbb228a76ceb74b59b0/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokkaDefaultOutputDirectory.kt#L6-L13
Ergo, at the point when the task is configured, the value of this property will be set to the value of buildDir
at the point when task configuration is invoked. Sure, since the plugin uses lazy task instantiation rather than direct configuration, this may happen after the buildDir
is overridden, but there is no hard guarantee.
Instead, defaultDokkaOutputDirectory()
should return a Provider<File>
or something like it, derived from project.layout.buildDirectory
property. This will ensure that the build directory will be resolved to the correct value at the point where the value of the outputDirectory
property is used, which would happen at the task execution time.
convention()
method returns a Property<>
though. Provider<File>
is read-only AFAIK so it won't do.
I'm fairly confident this will work as expected, Gradle properties are lazily evaluated, so at any point in the configuration phase you can set a value, regardless of the one set via convention
@kamildoleglo it does not matter what the convention()
method returns, matters what is passed to it instead, and when convention()
is called. In the current implementation, the value passed to it is computed at the same time it is called, that is, when the extension object is created. This means that project.buildDir
value used for its computation may not be up to date.
Compare:
val property1 = objects.property<File>()
.convention(project.buildDir)
val property2 = objects.property<File>()
.convention(objects.provider { project.buildDir })
the first case is what essentially implemented now. The second is what I essentially proposed. The second variant handles changes to buildDir
regardless of when it is changed; the first one will only set the correct convention value if the order of the plugins application is correct (dokka plugin is applied last).
Also, the fact that the user can override the convention value is kind of irrelevant, because my point is that the convention value itself is not entirely correct)
Note that you can pass a provider into the convention()
method; that’s what I suggested, not replacing Property
with Provider
, of course)
So the idea is to make defaultDokkaOutputDirectory()
return a Provider <File>
and pass it to the convention()
method.
btw, you might consider using RegularFileProperty
instead of Property<File>
, I believe it is recommended instead of the generic property.