Skip to content
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

Add option to toggle Dokka Generator Worker API isolation #136

Merged
merged 14 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions modules/dokkatoo-plugin/api/dokkatoo-plugin.api
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ public final class dev/adamko/dokkatoo/DokkatooBasePlugin$inlined$sam$i$org_grad
}

public abstract class dev/adamko/dokkatoo/DokkatooExtension : java/io/Serializable, org/gradle/api/plugins/ExtensionAware {
public final fun ClassLoaderIsolation (Lkotlin/jvm/functions/Function1;)Ldev/adamko/dokkatoo/workers/ClassLoaderIsolation;
public static synthetic fun ClassLoaderIsolation$default (Ldev/adamko/dokkatoo/DokkatooExtension;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/adamko/dokkatoo/workers/ClassLoaderIsolation;
public final fun ProcessIsolation (Lkotlin/jvm/functions/Function1;)Ldev/adamko/dokkatoo/workers/ProcessIsolation;
public static synthetic fun ProcessIsolation$default (Ldev/adamko/dokkatoo/DokkatooExtension;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/adamko/dokkatoo/workers/ProcessIsolation;
public abstract fun getDokkaGeneratorIsolation ()Lorg/gradle/api/provider/Property;
public abstract fun getDokkatooCacheDirectory ()Lorg/gradle/api/file/DirectoryProperty;
public abstract fun getDokkatooConfigurationsDirectory ()Lorg/gradle/api/file/DirectoryProperty;
public abstract fun getDokkatooModuleDirectory ()Lorg/gradle/api/file/DirectoryProperty;
Expand Down Expand Up @@ -364,6 +369,7 @@ public abstract class dev/adamko/dokkatoo/tasks/DokkatooGenerateTask : dev/adamk
public abstract fun getPublicationEnabled ()Lorg/gradle/api/provider/Property;
public abstract fun getRuntimeClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection;
public abstract fun getWorkerDebugEnabled ()Lorg/gradle/api/provider/Property;
public abstract fun getWorkerIsolation ()Lorg/gradle/api/provider/Property;
public abstract fun getWorkerJvmArgs ()Lorg/gradle/api/provider/ListProperty;
public abstract fun getWorkerLogFile ()Lorg/gradle/api/file/RegularFileProperty;
public abstract fun getWorkerMaxHeapSize ()Lorg/gradle/api/provider/Property;
Expand Down Expand Up @@ -400,3 +406,20 @@ public abstract class dev/adamko/dokkatoo/tasks/LogHtmlPublicationLinkTask : dev
public final class dev/adamko/dokkatoo/tasks/LogHtmlPublicationLinkTask$Companion {
}

public abstract interface class dev/adamko/dokkatoo/workers/ClassLoaderIsolation : dev/adamko/dokkatoo/workers/WorkerIsolation {
}

public abstract interface class dev/adamko/dokkatoo/workers/ProcessIsolation : dev/adamko/dokkatoo/workers/WorkerIsolation {
public abstract fun getAllJvmArgs ()Lorg/gradle/api/provider/ListProperty;
public abstract fun getDebug ()Lorg/gradle/api/provider/Property;
public abstract fun getDefaultCharacterEncoding ()Lorg/gradle/api/provider/Property;
public abstract fun getEnableAssertions ()Lorg/gradle/api/provider/Property;
public abstract fun getJvmArgs ()Lorg/gradle/api/provider/ListProperty;
public abstract fun getMaxHeapSize ()Lorg/gradle/api/provider/Property;
public abstract fun getMinHeapSize ()Lorg/gradle/api/provider/Property;
public abstract fun getSystemProperties ()Lorg/gradle/api/provider/MapProperty;
}

public abstract interface class dev/adamko/dokkatoo/workers/WorkerIsolation {
}

59 changes: 43 additions & 16 deletions modules/dokkatoo-plugin/src/main/kotlin/DokkatooBasePlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import dev.adamko.dokkatoo.internal.*
import dev.adamko.dokkatoo.tasks.DokkatooGenerateTask
import dev.adamko.dokkatoo.tasks.DokkatooPrepareModuleDescriptorTask
import dev.adamko.dokkatoo.tasks.DokkatooTask
import dev.adamko.dokkatoo.workers.ClassLoaderIsolation
import dev.adamko.dokkatoo.workers.ProcessIsolation
import java.io.File
import javax.inject.Inject
import kotlinx.serialization.ExperimentalSerializationApi
Expand Down Expand Up @@ -72,29 +74,39 @@ constructor(
sourceSetScopeConvention = dokkatooExtension.sourceSetScopeDefault
)

target.tasks.withType<DokkatooGenerateTask>().configureEach {
cacheDirectory.convention(dokkatooExtension.dokkatooCacheDirectory)
workerDebugEnabled.convention(false)
workerLogFile.convention(temporaryDir.resolve("dokka-worker.log"))
workerJvmArgs.set(
listOf(
//"-XX:MaxMetaspaceSize=512m",
"-XX:+HeapDumpOnOutOfMemoryError",
"-XX:+AlwaysPreTouch", // https://github.com/gradle/gradle/issues/3093#issuecomment-387259298
//"-XX:StartFlightRecording=disk=true,name={path.drop(1).map { if (it.isLetterOrDigit()) it else '-' }.joinToString("")},dumponexit=true,duration=30s",
//"-XX:FlightRecorderOptions=repository=$baseDir/jfr,stackdepth=512",
)
)
dokkaConfigurationJsonFile.convention(temporaryDir.resolve("dokka-configuration.json"))
}

target.tasks.withType<DokkatooPrepareModuleDescriptorTask>().configureEach {
moduleName.convention(dokkatooExtension.moduleName)
includes.from(providers.provider { dokkatooExtension.dokkatooSourceSets.flatMap { it.includes } })
modulePath.convention(dokkatooExtension.modulePath)
}

target.tasks.withType<DokkatooGenerateTask>().configureEach {
cacheDirectory.convention(dokkatooExtension.dokkatooCacheDirectory)
workerLogFile.convention(temporaryDir.resolve("dokka-worker.log"))
dokkaConfigurationJsonFile.convention(temporaryDir.resolve("dokka-configuration.json"))

workerIsolation.convention(dokkatooExtension.dokkaGeneratorIsolation.map { src ->
when (src) {
is ClassLoaderIsolation -> src
is ProcessIsolation -> {
// Complicated workaround to copy old properties, to maintain backwards compatibility.
// Remove when the deprecated task properties are deleted.
dokkatooExtension.ProcessIsolation {
@Suppress("DEPRECATION")
run {
debug.convention(workerDebugEnabled.orElse(src.debug))
enableAssertions.convention(src.enableAssertions)
minHeapSize.convention(workerMinHeapSize.orElse(src.minHeapSize))
maxHeapSize.convention(workerMaxHeapSize.orElse(src.maxHeapSize))
jvmArgs.convention(workerJvmArgs.orElse(src.jvmArgs))
allJvmArgs.convention(src.allJvmArgs)
defaultCharacterEncoding.convention(src.defaultCharacterEncoding)
systemProperties.convention(src.systemProperties)
}
}
}
}
})

publicationEnabled.convention(true)
onlyIf("publication must be enabled") { publicationEnabled.getOrElse(true) }
Expand Down Expand Up @@ -145,6 +157,21 @@ constructor(
kotlinxCoroutines.convention("1.6.4")
}

dokkatooExtension.dokkaGeneratorIsolation.convention(
dokkatooExtension.ProcessIsolation {
debug.convention(false)
jvmArgs.convention(
listOf(
//"-XX:MaxMetaspaceSize=512m",
"-XX:+HeapDumpOnOutOfMemoryError",
"-XX:+AlwaysPreTouch", // https://github.com/gradle/gradle/issues/3093#issuecomment-387259298
//"-XX:StartFlightRecording=disk=true,name={path.drop(1).map { if (it.isLetterOrDigit()) it else '-' }.joinToString("")},dumponexit=true,duration=30s",
//"-XX:FlightRecorderOptions=repository=$baseDir/jfr,stackdepth=512",
)
)
}
)

return dokkatooExtension
}

Expand Down
55 changes: 54 additions & 1 deletion modules/dokkatoo-plugin/src/main/kotlin/DokkatooExtension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,27 @@ package dev.adamko.dokkatoo
import dev.adamko.dokkatoo.dokka.DokkaPublication
import dev.adamko.dokkatoo.dokka.parameters.DokkaSourceSetSpec
import dev.adamko.dokkatoo.internal.*
import dev.adamko.dokkatoo.workers.ClassLoaderIsolation
import dev.adamko.dokkatoo.workers.ProcessIsolation
import dev.adamko.dokkatoo.workers.WorkerIsolation
import java.io.Serializable
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.model.ObjectFactory
import org.gradle.api.plugins.ExtensionAware
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Nested
import org.gradle.kotlin.dsl.*
import org.gradle.workers.WorkerExecutor

/**
* Configure the behaviour of the [DokkatooBasePlugin].
*/
abstract class DokkatooExtension
@DokkatooInternalApi
constructor(
objects: ObjectFactory,
private val objects: ObjectFactory,
) : ExtensionAware, Serializable {

/** Directory into which [DokkaPublication]s will be produced */
Expand Down Expand Up @@ -127,4 +132,52 @@ constructor(

companion object
}

/**
* Dokkatoo runs Dokka Generator in a separate
* [Gradle Worker](https://docs.gradle.org/8.5/userguide/worker_api.html).
*
* You can control whether Dokkatoo launches Dokka Generator in
* * a new process, using [ProcessIsolation],
* * or the current process with an isolated classpath, using [ClassLoaderIsolation].
*
* _Aside: Launching [without isolation][WorkerExecutor.noIsolation] is not an option, because
* running Dokka Generator **requires** an isolated classpath._
*
* ```kotlin
* dokkatoo {
* // use the current Gradle process, but with an isolated classpath
* workerIsolation = ClassLoaderIsolation()
*
* // launch a new process, optionally controlling the standard JVM options
* workerIsolation = ProcessIsolation {
* minHeapSize = "2g" // increase minimum heap size
* systemProperties.add("someCustomProperty", 123)
* }
* }
* ```
*
* @see WorkerIsolation
* @see dev.adamko.dokkatoo.workers.ProcessIsolation
* @see dev.adamko.dokkatoo.workers.ClassLoaderIsolation
*
*/
@get:Nested
abstract val dokkaGeneratorIsolation: Property<WorkerIsolation>

/**
* Create a new [ClassLoaderIsolation] options instance.
*
* The resulting options must be set into [dokkaGeneratorIsolation].
*/
fun ClassLoaderIsolation(configure: ClassLoaderIsolation.() -> Unit = {}): ClassLoaderIsolation =
objects.newInstance<ClassLoaderIsolation>().apply(configure)

/**
* Create a new [ProcessIsolation] options.
*
* The resulting options instance must be set into [dokkaGeneratorIsolation].
*/
fun ProcessIsolation(configure: ProcessIsolation.() -> Unit = {}): ProcessIsolation =
objects.newInstance<ProcessIsolation>().apply(configure)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import dev.adamko.dokkatoo.dokka.parameters.DokkaModuleDescriptionKxs
import dev.adamko.dokkatoo.dokka.parameters.builders.DokkaParametersBuilder
import dev.adamko.dokkatoo.internal.DokkaPluginParametersContainer
import dev.adamko.dokkatoo.internal.DokkatooInternalApi
import dev.adamko.dokkatoo.workers.ClassLoaderIsolation
import dev.adamko.dokkatoo.workers.DokkaGeneratorWorker
import dev.adamko.dokkatoo.workers.ProcessIsolation
import dev.adamko.dokkatoo.workers.WorkerIsolation
import java.io.IOException
import javax.inject.Inject
import kotlinx.serialization.json.JsonElement
Expand Down Expand Up @@ -72,20 +75,17 @@ constructor(
@get:Nested
val generator: DokkaGeneratorParametersSpec = objects.newInstance(pluginsConfiguration)

/** @see JavaForkOptions.getDebug */
@get:Input
abstract val workerDebugEnabled: Property<Boolean>
/** @see JavaForkOptions.getMinHeapSize */
@get:Input
@get:Optional
abstract val workerMinHeapSize: Property<String>
/** @see JavaForkOptions.getMaxHeapSize */
@get:Input
@get:Optional
abstract val workerMaxHeapSize: Property<String>
/** @see JavaForkOptions.jvmArgs */
@get:Input
abstract val workerJvmArgs: ListProperty<String>
/**
* Control whether Dokkatoo launches Dokka Generator.
*
* Defaults to [dev.adamko.dokkatoo.DokkatooExtension.dokkaGeneratorIsolation].
*
* @see dev.adamko.dokkatoo.DokkatooExtension.dokkaGeneratorIsolation
* @see dev.adamko.dokkatoo.workers.ProcessIsolation
*/
@get:Nested
abstract val workerIsolation: Property<WorkerIsolation>

@get:Internal
abstract val workerLogFile: RegularFileProperty

Expand All @@ -110,16 +110,28 @@ constructor(

logger.info("DokkaGeneratorWorker runtimeClasspath: ${runtimeClasspath.asPath}")

val workQueue = workers.processIsolation {
classpath.from(runtimeClasspath)
forkOptions {
defaultCharacterEncoding = "UTF-8"
minHeapSize = workerMinHeapSize.orNull
maxHeapSize = workerMaxHeapSize.orNull
enableAssertions = true
debug = workerDebugEnabled.get()
jvmArgs = workerJvmArgs.get()
}
val isolation = workerIsolation.get()
logger.info("[$path] running with workerIsolation $isolation")
val workQueue = when (isolation) {
is ClassLoaderIsolation ->
workers.classLoaderIsolation {
classpath.from(runtimeClasspath)
}

is ProcessIsolation ->
workers.processIsolation {
classpath.from(runtimeClasspath)
forkOptions {
isolation.defaultCharacterEncoding.orNull?.let(this::setDefaultCharacterEncoding)
isolation.debug.orNull?.let(this::setDebug)
isolation.enableAssertions.orNull?.let(this::setEnableAssertions)
isolation.maxHeapSize.orNull?.let(this::setMaxHeapSize)
isolation.minHeapSize.orNull?.let(this::setMinHeapSize)
isolation.jvmArgs.orNull?.let(this::setJvmArgs)
isolation.systemProperties.orNull?.let(this::systemProperties)
isolation.allJvmArgs.orNull?.let(this::setAllJvmArgs)
}
}
}

workQueue.submit(DokkaGeneratorWorker::class) {
Expand Down Expand Up @@ -183,4 +195,23 @@ constructor(
}
}
}

//region Deprecated Properties
/** @see JavaForkOptions.getDebug */
@get:Internal
@Deprecated("Please move worker options to `DokkatooExtension.dokkaGeneratorIsolation`. Worker options were moved to allow for configuring worker isolation")
abstract val workerDebugEnabled: Property<Boolean>
/** @see JavaForkOptions.getMinHeapSize */
@get:Internal
@Deprecated("Please move worker options to `DokkatooExtension.dokkaGeneratorIsolation`. Worker options were moved to allow for configuring worker isolation")
abstract val workerMinHeapSize: Property<String>
/** @see JavaForkOptions.getMaxHeapSize */
@get:Internal
@Deprecated("Please move worker options to `DokkatooExtension.dokkaGeneratorIsolation`. Worker options were moved to allow for configuring worker isolation")
abstract val workerMaxHeapSize: Property<String>
/** @see JavaForkOptions.jvmArgs */
@get:Internal
@Deprecated("Please move worker options to `DokkatooExtension.dokkaGeneratorIsolation`. Worker options were moved to allow for configuring worker isolation")
abstract val workerJvmArgs: ListProperty<String>
//endregion
}
Loading
Loading