aboutsummaryrefslogtreecommitdiff
path: root/runners/gradle-plugin
diff options
context:
space:
mode:
authorPaweł Marks <pmarks@virtuslab.com>2020-07-17 16:36:09 +0200
committerPaweł Marks <pmarks@virtuslab.com>2020-07-17 16:36:09 +0200
commit6996b1135f61c7d2cb60b0652c6a2691dda31990 (patch)
treed568096c25e31c28d14d518a63458b5a7526b896 /runners/gradle-plugin
parentde56cab76f556e5b4af0b8c8cb08d8b482b86d0a (diff)
parent1c3530dcbb50c347f80bef694829dbefe89eca77 (diff)
downloaddokka-6996b1135f61c7d2cb60b0652c6a2691dda31990.tar.gz
dokka-6996b1135f61c7d2cb60b0652c6a2691dda31990.tar.bz2
dokka-6996b1135f61c7d2cb60b0652c6a2691dda31990.zip
Merge branch 'dev-0.11.0'
Diffstat (limited to 'runners/gradle-plugin')
-rw-r--r--runners/gradle-plugin/build.gradle108
-rw-r--r--runners/gradle-plugin/build.gradle.kts63
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaTask.kt45
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ConfigurationExtractor.kt222
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaArtifacts.kt17
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaBootstrapFactory.kt18
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaCollectorTask.kt58
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaMultimoduleTask.kt68
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaSourceSetIDFactory.kt10
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt330
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ProxyUtils.kt23
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ReflectDsl.kt72
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/configurationImplementations.kt234
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/defaultDokkaOutputDirectory.kt13
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokkaConfigurations.kt41
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/main.kt72
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/utils.kt24
-rw-r--r--runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/AutomagicProxyTest.kt48
-rw-r--r--runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaTasksTest.kt65
-rw-r--r--runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/KotlinDslDokkaTaskConfigurationTest.kt97
20 files changed, 1072 insertions, 556 deletions
diff --git a/runners/gradle-plugin/build.gradle b/runners/gradle-plugin/build.gradle
deleted file mode 100644
index ceb03bae..00000000
--- a/runners/gradle-plugin/build.gradle
+++ /dev/null
@@ -1,108 +0,0 @@
-import com.gradle.publish.DependenciesBuilder
-
-apply plugin: 'java'
-apply plugin: 'kotlin'
-
-
-apply plugin: 'com.github.johnrengelman.shadow'
-apply plugin: "com.gradle.plugin-publish"
-
-sourceCompatibility = 1.8
-
-tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
- kotlinOptions {
- freeCompilerArgs += "-Xjsr305=strict"
- languageVersion = language_version
- apiVersion = language_version
- jvmTarget = "1.8"
- }
-}
-
-repositories {
- jcenter()
- google()
-}
-
-dependencies {
- testCompile group: 'junit', name: 'junit', version: '4.12'
-
- shadow group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlin_for_gradle_runtime_version
- shadow group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_for_gradle_runtime_version
-
- compile project(":integration")
-
- compileOnly "org.jetbrains.kotlin:kotlin-gradle-plugin"
- compileOnly("com.android.tools.build:gradle:3.0.0")
- compileOnly("com.android.tools.build:gradle-core:3.0.0")
- compileOnly("com.android.tools.build:builder-model:3.0.0")
- compileOnly gradleApi()
- compileOnly localGroovy()
- implementation "com.google.code.gson:gson:$gson_version"
-}
-
-task sourceJar(type: Jar) {
- from sourceSets.main.allSource
-}
-
-processResources {
- eachFile {
- if (it.name == "org.jetbrains.dokka.properties") {
- it.filter { line ->
- line.replace("<version>", dokka_version)
- }
- }
- }
-}
-
-shadowJar {
- baseName = 'dokka-gradle-plugin'
- classifier = ''
-}
-
-apply plugin: 'maven-publish'
-
-publishing {
- publications {
- dokkaGradlePlugin(MavenPublication) { publication ->
- artifactId = 'dokka-gradle-plugin'
-
- artifact sourceJar {
- classifier "sources"
- }
-
- project.shadow.component(publication)
- }
- }
-}
-
-bintrayPublication(project, ['dokkaGradlePlugin'])
-
-configurations.archives.artifacts.clear()
-artifacts {
- archives shadowJar
-}
-
-pluginBundle {
- website = 'https://www.kotlinlang.org/'
- vcsUrl = 'https://github.com/kotlin/dokka.git'
- description = 'Dokka, the Kotlin documentation tool'
- tags = ['dokka', 'kotlin', 'kdoc', 'android']
-
- plugins {
- dokkaGradlePlugin {
- id = 'org.jetbrains.dokka'
- displayName = 'Dokka plugin'
- }
- }
-
- withDependencies { List<Dependency> list ->
- list.clear()
- def builder = new DependenciesBuilder()
- builder.addUniqueScopedDependencies(list, configurations.shadow, "compile")
- }
-
- mavenCoordinates {
- groupId = "org.jetbrains.dokka"
- artifactId = "dokka-gradle-plugin"
- }
-} \ No newline at end of file
diff --git a/runners/gradle-plugin/build.gradle.kts b/runners/gradle-plugin/build.gradle.kts
new file mode 100644
index 00000000..0222f5e0
--- /dev/null
+++ b/runners/gradle-plugin/build.gradle.kts
@@ -0,0 +1,63 @@
+import org.jetbrains.configureBintrayPublication
+import org.jetbrains.dokkaVersion
+
+plugins {
+ `java-gradle-plugin`
+}
+
+repositories {
+ google()
+}
+
+dependencies {
+ implementation(project(":core"))
+ compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin")
+ compileOnly("com.android.tools.build:gradle:3.0.0")
+ compileOnly("com.android.tools.build:gradle-core:3.0.0")
+ compileOnly("com.android.tools.build:builder-model:3.0.0")
+ compileOnly(gradleApi())
+ compileOnly(gradleKotlinDsl())
+ testImplementation(gradleApi())
+ testImplementation(gradleKotlinDsl())
+ testImplementation(kotlin("test-junit"))
+ testImplementation("org.jetbrains.kotlin:kotlin-gradle-plugin")
+
+ constraints {
+ val kotlin_version: String by project
+ compileOnly("org.jetbrains.kotlin:kotlin-reflect:${kotlin_version}") {
+ because("kotlin-gradle-plugin and :core both depend on this")
+ }
+ }
+}
+
+val sourceJar by tasks.registering(Jar::class) {
+ archiveClassifier.set("sources")
+ from(sourceSets["main"].allSource)
+}
+
+gradlePlugin {
+ plugins {
+ create("dokkaGradlePlugin") {
+ id = "org.jetbrains.dokka"
+ implementationClass = "org.jetbrains.dokka.gradle.DokkaPlugin"
+ version = dokkaVersion
+ }
+ }
+}
+
+publishing {
+ publications {
+ register<MavenPublication>("pluginMaven") {
+ artifactId = "dokka-gradle-plugin"
+ }
+
+ register<MavenPublication>("dokkaGradlePluginForIntegrationTests") {
+ artifactId = "dokka-gradle-plugin"
+ from(components["java"])
+ version = "for-integration-tests-SNAPSHOT"
+ }
+ }
+}
+
+
+configureBintrayPublication("dokkaGradlePluginPluginMarkerMaven", "pluginMaven")
diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaTask.kt
new file mode 100644
index 00000000..846f021c
--- /dev/null
+++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaTask.kt
@@ -0,0 +1,45 @@
+package org.jetbrains.dokka.gradle
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.Project
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.attributes.Usage
+import org.gradle.api.tasks.Classpath
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.TaskAction
+import org.gradle.kotlin.dsl.dependencies
+import org.jetbrains.dokka.DokkaBootstrap
+import org.jetbrains.dokka.plugability.Configurable
+
+
+abstract class AbstractDokkaTask : DefaultTask(), Configurable {
+ @Input
+ var outputDirectory: String = defaultDokkaOutputDirectory().absolutePath
+
+ @Input
+ override val pluginsConfiguration: Map<String, String> = mutableMapOf()
+
+ @Classpath
+ val plugins: Configuration = project.maybeCreateDokkaPluginConfiguration(name)
+
+ @Classpath
+ val runtime: Configuration = project.maybeCreateDokkaRuntimeConfiguration(name)
+
+ @TaskAction
+ protected fun run() {
+ val kotlinColorsEnabledBefore = System.getProperty(DokkaTask.COLORS_ENABLED_PROPERTY) ?: "false"
+ System.setProperty(DokkaTask.COLORS_ENABLED_PROPERTY, "false")
+ try {
+ generate()
+ } finally {
+ System.setProperty(DokkaTask.COLORS_ENABLED_PROPERTY, kotlinColorsEnabledBefore)
+ }
+ }
+
+ protected abstract fun generate()
+
+ protected fun DokkaBootstrap(bootstrapClassFQName: String): DokkaBootstrap {
+ return DokkaBootstrap(runtime, bootstrapClassFQName)
+ }
+}
diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ConfigurationExtractor.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ConfigurationExtractor.kt
index 39672b9a..1bfd2c78 100644
--- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ConfigurationExtractor.kt
+++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ConfigurationExtractor.kt
@@ -1,9 +1,5 @@
package org.jetbrains.dokka.gradle
-import com.android.build.gradle.*
-import com.android.build.gradle.api.BaseVariant
-import com.android.builder.core.BuilderConstants
-import org.gradle.api.NamedDomainObjectCollection
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.UnknownDomainObjectException
@@ -13,90 +9,71 @@ import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.dokka.ReflectDsl
-import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
+import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
-import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
-import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
import java.io.File
import java.io.Serializable
class ConfigurationExtractor(private val project: Project) {
-
- fun extractConfiguration(targetName: String, variantNames: List<String>) =
- if (project.isMultiplatformProject()) {
- extractFromMultiPlatform(targetName, variantNames)
- } else {
- extractFromSinglePlatform(variantNames)
+ fun extractConfiguration(sourceSetName: String): PlatformData? {
+ val projectExtension = project.extensions.findByType(KotlinProjectExtension::class.java) ?: run {
+ project.logger.error("Missing kotlin project extension")
+ return null
}
- private fun extractFromSinglePlatform(variantNames: List<String>): PlatformData? {
- val target: KotlinTarget
- try {
- target = project.extensions.getByType(KotlinSingleTargetExtension::class.java).target
- } catch (e: Throwable) {
- when (e) {
- is UnknownDomainObjectException, is NoClassDefFoundError, is ClassNotFoundException ->
- return null
- else -> throw e
- }
+ val sourceSet = projectExtension.sourceSets.findByName(sourceSetName) ?: run {
+ project.logger.error("No source set with name '$sourceSetName' found")
+ return null
}
- return try {
- PlatformData(
- null,
- accumulateClassPaths(variantNames, target),
- accumulateSourceSets(variantNames, target),
- getPlatformName(target.platformType)
- )
- } catch (e: NoSuchMethodError) {
+ val compilation = try {
+ when (projectExtension) {
+ is KotlinMultiplatformExtension -> {
+ val targets = projectExtension.targets.flatMap { it.compilations }
+ targets.find { it.name == sourceSetName }
+ ?: targets.find { it.kotlinSourceSets.contains(sourceSet) }
+ }
+ is KotlinSingleTargetExtension -> projectExtension.target.compilations.find {
+ it.kotlinSourceSets.contains(sourceSet)
+ }
+ else -> null
+ }
+ } catch (e: NoClassDefFoundError) { // Old Kotlin plugin versions
null
}
+
+ val sourceRoots = sourceSet.sourceFiles
+ val classpath = compilation?.classpath
+ ?: sourceRoots + sourceSet.allParentSourceFiles()
+
+ return PlatformData(
+ sourceSetName,
+ classpath.filter { it.exists() },
+ sourceRoots,
+ sourceSet.dependsOn.map { it.name },
+ compilation?.target?.platformType?.name ?: "common"
+ )
}
- private fun extractFromMultiPlatform(targetName: String, variantNames: List<String>): PlatformData? =
- try {
- project.extensions.getByType(KotlinMultiplatformExtension::class.java).targets
- } catch (e: Throwable) {
- when (e) {
- is UnknownDomainObjectException, is NoClassDefFoundError, is ClassNotFoundException ->
- null
- else -> throw e
- }
- }?.let {
- val fixedName = if (targetName.toLowerCase() == "common") "metadata" else targetName.toLowerCase()
- it.find { target -> target.name.toLowerCase() == fixedName }?.let { target ->
- PlatformData(
- fixedName,
- accumulateClassPaths(variantNames, target),
- accumulateSourceSets(variantNames, target),
- target.platformType.toString()
- )
- }
- }
+ private fun KotlinSourceSet.allParentSourceFiles(): List<File> =
+ sourceFiles + dependsOn.flatMap { it.allParentSourceFiles() }
fun extractFromJavaPlugin(): PlatformData? =
project.convention.findPlugin(JavaPluginConvention::class.java)
?.run { sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME)?.allSource?.srcDirs }
- ?.let { PlatformData(null, emptyList(), it.toList(), "") }
+ ?.let { PlatformData(null, emptyList(), it.toList(), emptyList(), "") }
- fun extractFromKotlinTasks(kotlinTasks: List<Task>): PlatformData? =
+ fun extractFromKotlinTasks(kotlinTasks: List<Task>): List<PlatformData> =
try {
- kotlinTasks.map { extractFromKotlinTask(it) }.let { platformDataList ->
- PlatformData(
- null,
- platformDataList.flatMap { it.classpath },
- platformDataList.flatMap { it.sourceRoots },
- ""
- )
- }
+ kotlinTasks.map { extractFromKotlinTask(it) }
} catch (e: Throwable) {
when (e) {
is UnknownDomainObjectException, is NoClassDefFoundError, is ClassNotFoundException ->
- extractFromKotlinTasksTheHardWay(kotlinTasks)
+ listOfNotNull(extractFromKotlinTasksTheHardWay(kotlinTasks))
else -> throw e
}
}
@@ -110,10 +87,18 @@ class ConfigurationExtractor(private val project: Project) {
when (e) {
is UnknownDomainObjectException, is NoClassDefFoundError, is ClassNotFoundException ->
project.extensions.getByType(KotlinMultiplatformExtension::class.java).targets
- .firstNotNullResult { target -> target.compilations.find { it.compileKotlinTask == task } }
+ .flatMap { it.compilations }.firstOrNull { it.compileKotlinTask == task }
else -> throw e
}
- }.let { PlatformData(task.name, getClasspath(it), getSourceSet(it), it?.platformType?.toString() ?: "") }
+ }.let { compilation ->
+ PlatformData(
+ task.name,
+ compilation?.classpath.orEmpty(),
+ compilation?.sourceFiles.orEmpty(),
+ compilation?.dependentSourceSets?.map { it.name }.orEmpty(),
+ compilation?.platformType?.toString() ?: ""
+ )
+ }
private fun extractFromKotlinTasksTheHardWay(kotlinTasks: List<Task>): PlatformData? {
val allClasspath = mutableSetOf<File>()
@@ -134,8 +119,8 @@ class ConfigurationExtractor(private val project: Project) {
val taskClasspath: Iterable<File> =
(it["getClasspath", AbstractCompile::class].takeIfIsFunc()?.invoke()
- ?: it["compileClasspath", abstractKotlinCompileClz].takeIfIsProp()?.v()
- ?: it["getClasspath", abstractKotlinCompileClz]())
+ ?: it["compileClasspath", abstractKotlinCompileClz].takeIfIsProp()?.v()
+ ?: it["getClasspath", abstractKotlinCompileClz]())
if (taskClasspath is FileCollection) {
allClasspathFileCollection += taskClasspath
@@ -152,97 +137,42 @@ class ConfigurationExtractor(private val project: Project) {
}
classpath.addAll(project.files(allClasspath).toList())
- return PlatformData(null, classpath, allSourceRoots.toList(), "")
+ return PlatformData(null, classpath, allSourceRoots.toList(), emptyList(), "")
}
- private fun getSourceSet(target: KotlinTarget, variantName: String): List<File> =
- if (target.isAndroidTarget())
- getSourceSet(getCompilation(target, variantName))
- else
- getSourceSet(getMainCompilation(target))
-
- private fun getClasspath(target: KotlinTarget, variantName: String): List<File> =
- if (target.isAndroidTarget())
- getClasspathFromAndroidTask(getCompilation(target, variantName))
- else
- getClasspath(getMainCompilation(target))
-
- private fun getSourceSet(compilation: KotlinCompilation<*>?): List<File> = compilation
- ?.allKotlinSourceSets
- ?.flatMap { it.kotlin.sourceDirectories }
- ?.filter { it.exists() }
- .orEmpty()
-
- private fun getClasspath(compilation: KotlinCompilation<*>?): List<File> = compilation
- ?.compileDependencyFiles
- ?.files
- ?.toList()
- ?.filter { it.exists() }
- .orEmpty()
-
- // This is a workaround for KT-33893
- private fun getClasspathFromAndroidTask(compilation: KotlinCompilation<*>): List<File> = (compilation
- .compileKotlinTask as? KotlinCompile)
- ?.classpath?.files?.toList() ?: getClasspath(compilation)
-
- private fun getMainCompilation(target: KotlinTarget) =
- getCompilation(target, getMainCompilationName(target))
-
- private fun getCompilation(target: KotlinTarget, name: String) =
- target.compilations.getByName(name)
-
- private fun getMainCompilationName(target: KotlinTarget) = if (target.isAndroidTarget())
- getVariants(project).filter { it.buildType.name == BuilderConstants.RELEASE }.map { it.name }.first()
- else
- KotlinCompilation.MAIN_COMPILATION_NAME
-
- private fun getVariants(project: Project): Set<BaseVariant> {
- val androidExtension = project.extensions.getByName("android")
- val baseVariants = when (androidExtension) {
- is AppExtension -> androidExtension.applicationVariants.toSet()
- is LibraryExtension -> {
- androidExtension.libraryVariants.toSet() +
- if (androidExtension is FeatureExtension) {
- androidExtension.featureVariants.toSet()
- } else {
- emptySet<BaseVariant>()
- }
- }
- is TestExtension -> androidExtension.applicationVariants.toSet()
- else -> emptySet()
- }
- val testVariants = if (androidExtension is TestedExtension) {
- androidExtension.testVariants.toSet() + androidExtension.unitTestVariants.toSet()
- } else {
- emptySet<BaseVariant>()
- }
+ private val KotlinCompilation<*>.sourceFiles: List<File>
+ get() = kotlinSourceSets.flatMap { it.sourceFiles }
- return baseVariants + testVariants
- }
+ private val KotlinSourceSet.sourceFiles: List<File>
+ get() = kotlin.sourceDirectories.filter { it.exists() }.toList()
- private fun getPlatformName(platform: KotlinPlatformType): String =
- if (platform == KotlinPlatformType.androidJvm) KotlinPlatformType.jvm.toString() else platform.toString()
+ private val KotlinCompilation<*>.dependentSourceSets: Set<KotlinSourceSet>
+ get() = (allKotlinSourceSets - kotlinSourceSets)
- private fun accumulateClassPaths(variantNames: List<String>, target: KotlinTarget) =
- if (variantNames.isNotEmpty()) {
- variantNames.flatMap { getClasspath(target, it) }.distinct()
+ private val KotlinCompilation<*>.classpath: List<File>
+ get() = if (target.isAndroidTarget()) {
+ getClasspathFromAndroidTask(this)
} else {
- if (target.isAndroidTarget())
- getClasspathFromAndroidTask(getMainCompilation(target))
- else
- getClasspath(getMainCompilation(target))
+ getClasspathFromRegularTask(this)
}
- private fun accumulateSourceSets(variantNames: List<String>, target: KotlinTarget) =
- if (variantNames.isNotEmpty())
- variantNames.flatMap { getSourceSet(target, it) }.distinct()
- else
- getSourceSet(getMainCompilation(target))
+ // This is a workaround for KT-33893
+ private fun getClasspathFromAndroidTask(compilation: KotlinCompilation<*>): List<File> = (compilation
+ .compileKotlinTask as? KotlinCompile)
+ ?.classpath?.files?.toList() ?: getClasspathFromRegularTask(compilation)
+
+ private fun getClasspathFromRegularTask(compilation: KotlinCompilation<*>): List<File> =
+ compilation
+ .compileDependencyFiles
+ .files
+ .toList()
+ .filter { it.exists() }
data class PlatformData(
val name: String?,
val classpath: List<File>,
val sourceRoots: List<File>,
+ val dependentSourceSets: List<String>,
val platform: String
) : Serializable
-} \ No newline at end of file
+}
diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaArtifacts.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaArtifacts.kt
new file mode 100644
index 00000000..90d51015
--- /dev/null
+++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaArtifacts.kt
@@ -0,0 +1,17 @@
+package org.jetbrains.dokka.gradle
+
+import org.gradle.api.Project
+import org.jetbrains.dokka.DokkaVersion
+
+internal val Project.dokkaArtifacts get() = DokkaArtifacts(this)
+
+internal class DokkaArtifacts(private val project: Project) {
+ private fun fromModuleName(name: String) =
+ project.dependencies.create("org.jetbrains.dokka:$name:${DokkaVersion.version}")
+
+ val dokkaCore get() = fromModuleName("dokka-core")
+ val dokkaBase get() = fromModuleName("dokka-base")
+ val javadocPlugin get() = fromModuleName("javadoc-plugin")
+ val gfmPlugin get() = fromModuleName("gfm-plugin")
+ val jekyllPlugin get() = fromModuleName("jekyll-plugin")
+}
diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaBootstrapFactory.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaBootstrapFactory.kt
new file mode 100644
index 00000000..df29c19b
--- /dev/null
+++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaBootstrapFactory.kt
@@ -0,0 +1,18 @@
+package org.jetbrains.dokka.gradle
+
+import org.gradle.api.artifacts.Configuration
+import org.jetbrains.dokka.DokkaBootstrap
+import java.net.URLClassLoader
+
+
+fun DokkaBootstrap(configuration: Configuration, bootstrapClassFQName: String): DokkaBootstrap {
+ val runtimeJars = configuration.resolve()
+ val runtimeClassLoader = URLClassLoader(
+ runtimeJars.map { it.toURI().toURL() }.toTypedArray(),
+ ClassLoader.getSystemClassLoader().parent
+ )
+
+ val bootstrapClass = runtimeClassLoader.loadClass(bootstrapClassFQName)
+ val bootstrapInstance = bootstrapClass.constructors.first().newInstance()
+ return automagicTypedProxy(DokkaPlugin::class.java.classLoader, bootstrapInstance)
+}
diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaCollectorTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaCollectorTask.kt
new file mode 100644
index 00000000..37952ea8
--- /dev/null
+++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaCollectorTask.kt
@@ -0,0 +1,58 @@
+package org.jetbrains.dokka.gradle
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.plugins.JavaBasePlugin
+import org.gradle.api.plugins.JavaBasePlugin.DOCUMENTATION_GROUP
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.TaskAction
+import java.lang.IllegalStateException
+
+open class DokkaCollectorTask : DefaultTask() {
+
+ @Input
+ var modules: List<String> = emptyList()
+
+ @Input
+ var outputDirectory: String = defaultDokkaOutputDirectory().absolutePath
+
+ private lateinit var configuration: GradleDokkaConfigurationImpl
+
+ @Input
+ var dokkaTaskNames: Set<String> = setOf()
+
+ @TaskAction
+ fun collect() {
+ val configurations = project.subprojects
+ .filter { subProject -> subProject.name in modules }
+ .flatMap { subProject -> dokkaTaskNames.mapNotNull(subProject.tasks::findByName) }
+ .filterIsInstance<DokkaTask>()
+ .mapNotNull { dokkaTask -> dokkaTask.getConfigurationOrNull() }
+
+
+ val initial = GradleDokkaConfigurationImpl().apply {
+ outputDir = outputDirectory
+ cacheRoot = configurations.first().cacheRoot
+ }
+
+ // TODO this certainly not the ideal solution
+ configuration = configurations.fold(initial) { acc, it: GradleDokkaConfigurationImpl ->
+ if (acc.cacheRoot != it.cacheRoot)
+ throw IllegalStateException("Dokka task configurations differ on core argument cacheRoot")
+ acc.sourceSets = acc.sourceSets + it.sourceSets
+ acc.pluginsClasspath = (acc.pluginsClasspath + it.pluginsClasspath).distinct()
+ acc
+ }
+ project.tasks.withType(DokkaTask::class.java).configureEach { it.enforcedConfiguration = configuration }
+ }
+
+ init {
+ // TODO: This this certainly not the ideal solution
+ dokkaTaskNames.forEach { dokkaTaskName ->
+ finalizedBy(dokkaTaskName)
+ }
+
+ group = DOCUMENTATION_GROUP
+ }
+
+
+}
diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaMultimoduleTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaMultimoduleTask.kt
new file mode 100644
index 00000000..6fd58afe
--- /dev/null
+++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaMultimoduleTask.kt
@@ -0,0 +1,68 @@
+package org.jetbrains.dokka.gradle
+
+import com.google.gson.GsonBuilder
+import org.gradle.api.plugins.JavaBasePlugin
+import org.gradle.api.plugins.JavaBasePlugin.DOCUMENTATION_GROUP
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.Internal
+import org.jetbrains.dokka.plugability.Configurable
+
+open class DokkaMultimoduleTask : AbstractDokkaTask(), Configurable {
+
+ @Input
+ var documentationFileName: String = "README.md"
+
+
+ @Input
+ var dokkaTaskNames: Set<String> = setOf()
+ set(value) {
+ field = value.toSet()
+ setDependsOn(getSubprojectDokkaTasks(value))
+ }
+
+
+ override fun generate() {
+ val bootstrap = DokkaBootstrap("org.jetbrains.dokka.DokkaMultimoduleBootstrapImpl")
+ val gson = GsonBuilder().setPrettyPrinting().create()
+ val configuration = getConfiguration()
+ bootstrap.configure(gson.toJson(configuration)) { level, message ->
+ when (level) {
+ "debug" -> logger.debug(message)
+ "info" -> logger.info(message)
+ "progress" -> logger.lifecycle(message)
+ "warn" -> logger.warn(message)
+ "error" -> logger.error(message)
+ }
+ }
+ bootstrap.generate()
+ }
+
+ @Internal
+ internal fun getConfiguration(): GradleDokkaConfigurationImpl =
+ GradleDokkaConfigurationImpl().apply {
+ outputDir = project.file(outputDirectory).absolutePath
+ pluginsClasspath = plugins.resolve().toList()
+ pluginsConfiguration = this@DokkaMultimoduleTask.pluginsConfiguration
+ modules = getSubprojectDokkaTasks(dokkaTaskNames).map { dokkaTask ->
+ GradleDokkaModuleDescription().apply {
+ name = dokkaTask.project.name
+ path = dokkaTask.project.projectDir.resolve(dokkaTask.outputDirectory)
+ .toRelativeString(project.file(outputDirectory))
+ docFile = dokkaTask.project.projectDir.resolve(documentationFileName).absolutePath
+ }
+ }
+ }
+
+ private fun getSubprojectDokkaTasks(dokkaTaskName: String): List<DokkaTask> {
+ return project.subprojects
+ .mapNotNull { subproject -> subproject.tasks.findByName(dokkaTaskName) as? DokkaTask }
+ }
+
+ private fun getSubprojectDokkaTasks(dokkaTaskNames: Set<String>): List<DokkaTask> {
+ return dokkaTaskNames.flatMap { dokkaTaskName -> getSubprojectDokkaTasks(dokkaTaskName) }
+ }
+
+ init {
+ group = DOCUMENTATION_GROUP
+ }
+}
diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaSourceSetIDFactory.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaSourceSetIDFactory.kt
new file mode 100644
index 00000000..3fadb4fd
--- /dev/null
+++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaSourceSetIDFactory.kt
@@ -0,0 +1,10 @@
+@file:Suppress("FunctionName")
+
+package org.jetbrains.dokka.gradle
+
+import org.gradle.api.Project
+import org.jetbrains.dokka.DokkaSourceSetID
+
+internal fun DokkaSourceSetID(project: Project, sourceSetName: String): DokkaSourceSetID {
+ return DokkaSourceSetID(moduleName = project.path, sourceSetName = sourceSetName)
+}
diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt
index bafe657e..0d7e74a3 100644
--- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt
+++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt
@@ -1,28 +1,26 @@
package org.jetbrains.dokka.gradle
import com.google.gson.GsonBuilder
-import org.gradle.api.*
-import org.gradle.api.artifacts.Configuration
+import org.gradle.api.NamedDomainObjectContainer
+import org.gradle.api.Project
+import org.gradle.api.Task