refactor(build): Centralize Dokka configuration into convention plugin (#4173)

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
James Rich 2026-01-09 09:40:00 -06:00 committed by GitHub
parent 02f99bd7bb
commit 731430d7d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 283 additions and 149 deletions

View file

@ -50,9 +50,11 @@ dependencies {
compileOnly(libs.compose.multiplatform.gradlePlugin)
compileOnly(libs.datadog.gradlePlugin)
compileOnly(libs.detekt.gradlePlugin)
compileOnly(libs.dokka.gradlePlugin)
compileOnly(libs.firebase.crashlytics.gradlePlugin)
compileOnly(libs.google.services.gradlePlugin)
compileOnly(libs.hilt.gradlePlugin)
implementation(libs.kover.gradlePlugin)
compileOnly(libs.kotlin.gradlePlugin)
compileOnly(libs.ksp.gradlePlugin)
compileOnly(libs.androidx.room.gradlePlugin)
@ -165,6 +167,16 @@ gradlePlugin {
id = "meshtastic.kmp.library.compose"
implementationClass = "KmpLibraryComposeConventionPlugin"
}
register("dokka") {
id = "meshtastic.dokka"
implementationClass = "DokkaConventionPlugin"
}
register("kover") {
id = "meshtastic.kover"
implementationClass = "KoverConventionPlugin"
}
register("root") {
id = "meshtastic.root"

View file

@ -31,6 +31,8 @@ class AndroidApplicationConventionPlugin : Plugin<Project> {
apply(plugin = "meshtastic.detekt")
apply(plugin = "meshtastic.spotless")
apply(plugin = "meshtastic.analytics")
apply(plugin = "meshtastic.kover")
apply(plugin = "meshtastic.dokka")
extensions.configure<ApplicationExtension> {
configureKotlinAndroid(this)

View file

@ -32,7 +32,8 @@ class AndroidLibraryConventionPlugin : Plugin<Project> {
apply(plugin = "meshtastic.android.lint")
apply(plugin = "meshtastic.detekt")
apply(plugin = "meshtastic.spotless")
apply(plugin = "org.jetbrains.dokka")
apply(plugin = "meshtastic.dokka")
apply(plugin = "meshtastic.kover")
extensions.configure<LibraryExtension> {
configureKotlinAndroid(this)

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.dependencies
import org.meshtastic.buildlogic.configureDokka
import org.meshtastic.buildlogic.library
import org.meshtastic.buildlogic.libs
class DokkaConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
apply(plugin = "org.jetbrains.dokka")
// Ensure the Android documentation plugin is available in all modules for better Android support
dependencies {
add("dokkaPlugin", libs.library("dokka-android-documentation-plugin"))
}
configureDokka()
}
}
}

View file

@ -30,6 +30,8 @@ class KmpLibraryConventionPlugin : Plugin<Project> {
apply(plugin = "meshtastic.android.lint")
apply(plugin = "meshtastic.detekt")
apply(plugin = "meshtastic.spotless")
apply(plugin = "meshtastic.dokka")
apply(plugin = "meshtastic.kover")
configureKotlinMultiplatform()
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2025 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.meshtastic.buildlogic.configureKover
class KoverConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
apply(plugin = "org.jetbrains.kotlinx.kover")
configureKover()
}
}
}

View file

@ -17,11 +17,24 @@
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.meshtastic.buildlogic.configureDokkaAggregation
import org.meshtastic.buildlogic.configureGraphTasks
import org.meshtastic.buildlogic.configureKover
import org.meshtastic.buildlogic.configureKoverAggregation
class RootConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
require(target.path == ":")
target.subprojects { configureGraphTasks() }
with(target) {
apply(plugin = "org.jetbrains.dokka")
configureDokkaAggregation()
apply(plugin = "org.jetbrains.kotlinx.kover")
configureKover()
configureKoverAggregation()
subprojects { configureGraphTasks() }
}
}
}

View file

@ -0,0 +1,102 @@
/*
* Copyright (c) 2025 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.meshtastic.buildlogic
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.jetbrains.dokka.gradle.DokkaExtension
import java.io.File
import java.net.URI
fun Project.configureDokka() {
extensions.configure<DokkaExtension> {
// Use the full project path as the module name to ensure uniqueness
moduleName.set(project.path.removePrefix(":").replace(":", "-").ifEmpty { project.name })
// Discover and register Android source sets (main + flavors)
val registerAndroidSourceSets = {
project.file("src").listFiles()
?.filter { it.isDirectory && !it.name.contains("test", ignoreCase = true) }
?.forEach { sourceDir ->
val sourceSetName = sourceDir.name
val ktDir = File(sourceDir, "kotlin")
val javaDir = File(sourceDir, "java")
if (ktDir.exists() || javaDir.exists()) {
dokkaSourceSets.maybeCreate(sourceSetName).apply {
if (ktDir.exists()) sourceRoots.from(ktDir)
if (javaDir.exists()) sourceRoots.from(javaDir)
suppress.set(false)
}
}
}
}
pluginManager.withPlugin("com.android.library") {
if (!plugins.hasPlugin("org.jetbrains.kotlin.multiplatform")) {
registerAndroidSourceSets()
}
}
pluginManager.withPlugin("com.android.application") {
registerAndroidSourceSets()
}
dokkaSourceSets.configureEach {
perPackageOption {
matchingRegex.set("hilt_aggregated_deps")
suppress.set(true)
}
perPackageOption {
matchingRegex.set("org.meshtastic.core.strings.*")
suppress.set(true)
}
// Ensure source sets containing core logic are not suppressed
val isCoreSourceSet = name in listOf("main", "commonMain", "androidMain", "fdroid", "google")
if (isCoreSourceSet) {
suppress.set(false)
}
sourceLink {
enableJdkDocumentationLink.set(true)
enableKotlinStdLibDocumentationLink.set(true)
reportUndocumented.set(true)
// Standardized repo-root based source links
localDirectory.set(project.projectDir)
val relativePath = project.projectDir.relativeTo(rootProject.projectDir).path.replace("\\", "/")
remoteUrl.set(URI("https://github.com/meshtastic/Meshtastic-Android/blob/main/$relativePath"))
remoteLineSuffix.set("#L")
}
}
}
}
fun Project.configureDokkaAggregation() {
extensions.configure<DokkaExtension> {
moduleName.set("Meshtastic App")
dokkaPublications.configureEach {
suppressInheritedMembers.set(true)
}
}
subprojects.forEach { subproject ->
subproject.pluginManager.withPlugin("org.jetbrains.dokka") {
dependencies.add("dokka", subproject)
}
}
}

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2025 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.meshtastic.buildlogic
import kotlinx.kover.gradle.plugin.dsl.KoverProjectExtension
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
fun Project.configureKover() {
extensions.configure<KoverProjectExtension> {
reports {
filters {
excludes {
// Exclude generated classes
classes("*_Impl")
classes("*Binding")
classes("*Factory")
classes("*.BuildConfig")
classes("*.R")
classes("*.R$*")
// Exclude UI components
annotatedBy("*Preview")
// Exclude declarations
annotatedBy(
"*.HiltAndroidApp",
"*.AndroidEntryPoint",
"*.Module",
"*.Provides",
"*.Binds",
"*.Composable",
)
// Suppress generated code
packages("hilt_aggregated_deps")
packages("org.meshtastic.core.strings")
}
}
}
}
}
fun Project.configureKoverAggregation() {
subprojects.forEach { subproject ->
subproject.plugins.withId("org.jetbrains.kotlinx.kover") {
dependencies.add("kover", subproject)
}
}
}