Hi everyone! :wave: I'm migrating to Dokka Gradle...
# dokka
r
Hi everyone! πŸ‘‹ I'm migrating to Dokka Gradle Plugin (DGP) version 2.0.0 in a multimodule project and need some help replicating the configuration I had with version 1.x. Previously, I could just apply the plugin to the modules I wanted documentation for and configure this in the root `build.gradle.kts`:
Copy code
dokkaHtmlMultiModule {
    outputDirectory.set(rootDir.resolve("docs/dokka"))
}
Everything worked seamlessly! πŸš€ With version 2.x, it seems I now have to explicitly declare all the Dokka dependencies for the modules in the root `build.gradle.kts`:
Copy code
dependencies {
    dokka(project(":childProjectA"))
    dokka(project(":childProjectB"))
}
This manual step is tricky for me since my project has both dynamic modules (loaded via a JSON config) and static modules. In version 1.x, I could rely on Gradle's build conventions for automation, but now I feel like I have two sources of truth: 1. The standard Gradle configuration for modules. 2. The new Dokka dependencies I need to define manually. Is there an alternative approach for multimodule projects that would let me mark a project as a Dokka dependency directly in the module’s
build.gradle.kts
? Something that works well with Gradle build conventions and can be automated? Thanks in advance for your insights!
m
You need to do the aggregating configuration + lenient view dance
I swear I saw a nice article about this not too long ago, let me dig
πŸ™ 1
Heads up it's involved and I'm still undecided whether it's worth it πŸ˜„
r
damn, with version 1.x was literally 4 lines of code to properly support multimodules in my project 🫣
m
I know πŸ˜…
So you would declare a configuration in your root build script and add all projects to it:
Copy code
allprojects {
  configuration.dependencies.add("myConfig", project(":${it.name}")
}
And then add that as a a dependency to the "dokka" configuration
r
I'll give it a try and report back
thanks πŸ™‚
m
Maybe there's a better way but I haven't found yet
r
unfortunately this approach seems quite complex and require some Gradle knowledge that goes above my current level. It shouldn't be so difficult to configure Dokka for a multimodule project and version 2.x should not drop features from version 1.x. I've opened a bug report to hopefully get this fixed: https://github.com/Kotlin/dokka/issues/3991
πŸ‘ 2
m
TBH, I think this is mostly in Gradle hands: β€’ https://github.com/gradle/gradle/issues/29403 β€’ https://github.com/gradle/gradle/issues/29037 Maybe Dokka can workaround though...
πŸ‘ 2
c
> Is there an alternative approach for multimodule projects that would let me mark a project as a Dokka dependency directly in the module’s
build.gradle.kts
? The Gradle team is actively fighting against this. They are experimenting with lazy configuration (see "configuration on demand"), with which only projects explicitly requested are configured. With what you're trying to do, when it becomes stable, any module not referenced in the root module just won't be executed and won't appear in the documentation.
It shouldn't be so difficult to configure Dokka for a multimodule project and version 2.x should not drop features from version 1.x.
The fact that the old Dokka plugin worked like that was one of the main causes of stability and performance problems, it's almost the primary reason for why Dokkatoo was created, and thus we get Dokka 2
πŸ‘ 1
a
Hey, I agree that it's not convenient to automatically aggregate modules. We had a lot of discussions about it, and it's a missing feature in Gradle. When it comes to developing Gradle plugins, they always face a "pick two out of three" compromise: A. Compatible with Gradle best practices. B. Easy to understand and convenient to configure in projects. C. Compatible with projects of all types. The main motivation of DGPv2 was compatibility with the Gradle API, so we decided to pick A. Because Dokka is used in a wide variety of projects, we had to choose C. And while, yes, having to explicitly define the dependencies can be verbose and annoying when there are many subprojects, it is at least possible and easy to understand.
πŸ’― 1
m
@Adam Semenenko could DGP add code do the lenient artifact aggregation? That'd be one step closer to an easier solution
a
One more robust way of automatic aggregation can be done by manually defining a shared BuildService that collects each subproject, and then in the aggregating project get the list of subprojects to add dependencies. The Kotlin JS wrappers uses this BuildService approach (for not just Dokka, but also publishing and creating a BOM). β€’ SubprojectService collects the path and type of each subproject https://github.com/JetBrains/kotlin-wrappers/blob/pre.860/buildSrc/src/main/kotlin/SubprojectService.kt β€’ SubprojectService is registered in every subproject in a 'base' convention plugin https://github.com/JetBrains/kotlin-wrappers/blob/pre.860/buildSrc/src/main/kotlin/subproject-conventions.gradle.kts β€’ The
:docs
subproject then collects all documentable subprojects https://github.com/JetBrains/kotlin-wrappers/blob/pre.860/docs/build.gradle.kts#L24-L30 (DGP might be able to do this automatically, but shared BuildServices break when the applied plugins aren't identical in every subproject https://github.com/gradle/gradle/issues/17559 gradle intensifies)
πŸ‘€ 1
m
Looks like it's either flawed BuildService or lenient configuration 😞 Both of them having issues...
So forget what I said, manual list of projects is the way to go until there is a sane way to do this in Gradle-land
shared BuildServices break when the applied plugins aren't identical in every subproject
Missed that one: that's a cool idea https://github.com/gradle/gradle/issues/31846
Still feels fragile. What makes sure that every required subproject is configured before the
addAllLater
πŸ€”
a
> could DGP add code do the lenient artifact aggregation? I think this is already the case... (my uncertainty is because I haven't looked at this in a while, and I can't remember if
dependencies { allprojects { dokka(project) } }
works). β€’ When aggregating, the subprojects are set here https://github.com/Kotlin/dokka/blob/0c8711917c367340368d17ee11c6a0867d61c655/dokka-runners/dokka-gradle-plugin/src/main/kotlin/formats/DokkaFormatPlugin.kt#L100-L102 β€’
incomingArtifactFiles
does some complicated matching/filtering, and sets
lenient(true)
https://github.com/Kotlin/dokka/blob/623ee350ad054f7bd893cf03348fd10781fe0903/dokka-runners/dokka-gradle-plugin/src/main/kotlin/dependencies/ModuleComponentDependencies.kt#L62-L75
org.gradle.strictbuildclasspath=true
would be cool There's like 10 different ways to add Gradle plugins, it's very confusing
nod 1
m
> I think this is already the case... So then adding multi-module support could be done as easily as that in the root build script?
Copy code
allprojects {
  dokka(project(":$path")) // non-dokka projects will be ignored
}
Which would effectively solve @Roberto Leinardi issue? Not the cleanest thing but should do?
😯 1
maybe 1
c
but then you can't have a "test" module or any other internal module that you don't want to publish
m
Yea. Ideally this is configured in each module
Copy code
dokka {
  aggregate = false // defaults to true
}
c
in each project? But then you can't run Gradle without initializing all projects first, which is what the Gradle team wants to avoid
m
πŸ™ˆ
It's still isolated and runs in parallel
But you're right that you have to configure everything first
Same goes with the
BuildService
solution as far as I understand?
Which is why we need something like https://github.com/gradle/gradle/issues/29037
⭐ 1
a
I've had a quick test and this works, and ignores subprojects that don't have the Dokka plugin.
Copy code
dependencies {
//    dokka(project)
//    dokka(project(":childProjectA"))
//    dokka(project(":childProjectB"))
    allprojects { dokka(project) }
}
But I would really recommend explicitly listing the dependencies, because
allprojects {}
is a landmine and can cause issues.
πŸ‘ 1
πŸ‘€ 1
m
I would really recommend explicitly listing the dependencies
Agreed
because
allprojects {}
is a landmine and can cause issues.
Using
allprojects {}
is fine as long as you're only using
Project.name
and
Project.path
inside the lambda.
The real problem with the
allprojects {}
solution is the lenient dependency resolution. I've been bitten by this already and it's a massive pain to debug
(But also one of the only 2 flawed solutions we have right now 😞 )