KT-65928 Worker API for Swift Export

This commit is contained in:
Andrey Yastrebov
2024-02-19 14:01:40 +01:00
committed by Space Team
parent 1c1da6bced
commit 28a305a826
12 changed files with 225 additions and 89 deletions
@@ -1,5 +1,4 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import com.github.jengelman.gradle.plugins.shadow.transformers.DontIncludeResourceTransformer
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
@@ -91,10 +90,6 @@ dependencies {
commonRuntimeOnly(project(":kotlin-util-klib"))
commonRuntimeOnly(project(":kotlin-compiler-embeddable"))
if (kotlinBuildProperties.isSwiftExportPluginPublishingEnabled) {
embedded(project(":native:swift:swift-export-embeddable"))
}
embedded(project(":kotlin-gradle-build-metrics"))
embedded(project(":kotlin-gradle-statistics"))
embedded(commonDependency("org.jetbrains.intellij.deps:asm-all")) { isTransitive = false }
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.gradle.internal.KOTLIN_MODULE_GROUP
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.plugin.internal.*
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.swiftexport.initSwiftExportClasspathConfigurations
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSetFactory
import org.jetbrains.kotlin.gradle.plugin.statistics.BuildFusService
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
@@ -254,6 +255,7 @@ abstract class KotlinBasePluginWrapper : DefaultKotlinBasePlugin() {
addGradlePluginMetadataAttributes(project)
}
project.maybeCreateCommonizerClasspathConfiguration()
project.initSwiftExportClasspathConfigurations()
project.createKotlinExtension(projectExtensionClass).apply {
coreLibrariesVersion = pluginVersion
@@ -24,7 +24,7 @@ import java.io.File
import javax.inject.Inject
@DisableCachingByDefault(because = "Swift Export is experimental, so no caching for now")
abstract class BuildSyntheticProjectWithSwiftExportPackage : DefaultTask() {
internal abstract class BuildSyntheticProjectWithSwiftExportPackage : DefaultTask() {
@get:Inject
abstract val providerFactory: ProviderFactory
@@ -18,7 +18,7 @@ import java.io.File
import javax.inject.Inject
@DisableCachingByDefault(because = "This task only copies files")
abstract class CopySwiftExportIntermediatesForConsumer @Inject constructor(
internal abstract class CopySwiftExportIntermediatesForConsumer @Inject constructor(
objectFactory: ObjectFactory,
projectLayout: ProjectLayout,
providerFactory: ProviderFactory,
@@ -17,7 +17,7 @@ import org.jetbrains.kotlin.incremental.createDirectory
import org.jetbrains.kotlin.incremental.deleteRecursivelyOrThrow
@DisableCachingByDefault(because = "Swift Export is experimental, so no caching for now")
abstract class GenerateSPMPackageFromSwiftExport : DefaultTask() {
internal abstract class GenerateSPMPackageFromSwiftExport : DefaultTask() {
@get:Input
abstract val swiftApiModuleName: Property<String>
@@ -11,8 +11,10 @@ import org.gradle.api.file.Directory
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformSourceSetConventionsImpl.commonMain
import org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle
import org.jetbrains.kotlin.gradle.plugin.kotlinPluginLifecycle
import org.jetbrains.kotlin.gradle.plugin.mpp.*
@@ -49,7 +51,6 @@ private fun Project.registerSwiftExportTask(
val swiftExportTask = registerSwiftExportRun(
swiftApiModuleName = swiftApiModuleName,
taskNamePrefix = taskNamePrefix,
mainCompilation = mainCompilation,
)
val staticLibrary = registerSwiftExportCompilationAndGetBinary(
buildType = buildType,
@@ -91,15 +92,19 @@ private fun Project.registerSwiftExportTask(
private fun Project.registerSwiftExportRun(
swiftApiModuleName: Provider<String>,
taskNamePrefix: String,
mainCompilation: KotlinCompilation<*>,
): TaskProvider<SwiftExportTask> {
val swiftExportTaskName = taskNamePrefix + "SwiftExport"
return locateOrRegisterTask<SwiftExportTask>(swiftExportTaskName) { task ->
val directoryProvider = project.future {
kotlinPluginLifecycle.await(KotlinPluginLifecycle.Stage.AfterFinaliseRefinesEdges)
mainCompilation.allKotlinSourceSets.flatMap {
it.kotlin.srcDirs
}
val commonMainProvider = project.future {
project
.multiplatformExtension
.awaitSourceSets()
.commonMain
.get()
.kotlin
.srcDirs
.single()
}
val outputs = layout.buildDirectory.dir(swiftExportTaskName)
@@ -107,15 +112,16 @@ private fun Project.registerSwiftExportRun(
val kotlinIntermediates = outputs.map { it.dir("kotlinIntermediates") }
// Input
task.sourceRoots.from(project.files(directoryProvider.getOrThrow()))
task.bridgeModuleName.set(swiftApiModuleName.map { "${it}Bridge" })
task.debugMode.set(true)
task.konanDistribution.set(konanDistribution.root)
task.swiftExportClasspath.from(project.maybeCreateSwiftExportClasspathResolvableConfiguration())
task.parameters.sourceRoot.set(commonMainProvider.getOrThrow())
task.parameters.bridgeModuleName.set(swiftApiModuleName.map { "${it}Bridge" })
task.parameters.debugMode.set(true)
task.parameters.konanDistribution.set(konanDistribution.root)
// Output
task.swiftApiPath.set(swiftIntermediates.map { it.file("KotlinAPI.swift") })
task.headerBridgePath.set(swiftIntermediates.map { it.file("KotlinBridge.h") })
task.kotlinBridgePath.set(kotlinIntermediates.map { it.file("KotlinBridge.kt") })
task.parameters.swiftApiPath.set(swiftIntermediates.map { it.file("KotlinAPI.swift") })
task.parameters.headerBridgePath.set(swiftIntermediates.map { it.file("KotlinBridge.h") })
task.parameters.kotlinBridgePath.set(kotlinIntermediates.map { it.file("KotlinBridge.kt") })
}
}
@@ -133,7 +139,10 @@ private fun registerSwiftExportCompilationAndGetBinary(
swiftExportCompilationName,
invokeWhenCreated = { swiftExportCompilation ->
swiftExportCompilation.associateWith(mainCompilation)
swiftExportCompilation.defaultSourceSet.kotlin.srcDir(swiftExportTask.map { it.kotlinBridgePath.getFile().parent })
swiftExportCompilation.defaultSourceSet.kotlin.srcDir(swiftExportTask.map {
it.parameters.kotlinBridgePath.getFile().parent
})
swiftExportCompilation.compileTaskProvider.configure {
it.compilerOptions.optIn.add("kotlin.experimental.ExperimentalNativeApi")
}
@@ -165,10 +174,10 @@ private fun Project.registerPackageGeneration(
task.description = "Generates $taskNamePrefix SPM Package"
// Input
task.swiftApiPath.set(swiftExportTask.flatMap { it.swiftApiPath })
task.headerBridgePath.set(swiftExportTask.flatMap { it.headerBridgePath })
task.headerBridgeModuleName.set(swiftExportTask.flatMap { it.bridgeModuleName })
task.libraryPath.set { staticLibrary.linkTaskProvider.flatMap { it.outputFile }.get() }
task.swiftApiPath.set(swiftExportTask.map { it.parameters.swiftApiPath.get() })
task.headerBridgePath.set(swiftExportTask.map { it.parameters.headerBridgePath.get() })
task.headerBridgeModuleName.set(swiftExportTask.map { it.parameters.bridgeModuleName.get() })
task.libraryPath.set { staticLibrary.linkTaskProvider.map { it.outputFile.get() }.get() }
task.swiftLibraryName.set(swiftApiLibraryName)
task.kotlinLibraryName.set(kotlinStaticLibraryName)
task.swiftApiModuleName.set(swiftApiModuleName)
@@ -219,4 +228,5 @@ private fun Project.registerCopyTask(
}
copyTask.dependsOn(syntheticProjectBuild)
return copyTask
}
}
@@ -0,0 +1,52 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.plugin.mpp.apple.swiftexport
import org.gradle.workers.WorkAction
import org.jetbrains.kotlin.gradle.utils.getFile
import org.jetbrains.kotlin.konan.target.Distribution
import org.jetbrains.kotlin.swiftexport.standalone.*
import java.util.logging.Level
import java.util.logging.Logger
internal abstract class SwiftExportAction : WorkAction<SwiftExportParameters> {
companion object : SwiftExportLogger {
private val logger = Logger.getLogger(this::class.java.name)
override fun report(severity: SwiftExportLogger.Severity, message: String) {
logger.log(severity.logType(), message)
}
private fun SwiftExportLogger.Severity.logType(): Level = when (this) {
SwiftExportLogger.Severity.Info -> Level.INFO
SwiftExportLogger.Severity.Warning -> Level.WARNING
SwiftExportLogger.Severity.Error -> Level.SEVERE
else -> Level.INFO
}
}
override fun execute() {
runSwiftExport(
input = SwiftExportInput(
sourceRoot = parameters.sourceRoot.getFile().toPath()
),
config = SwiftExportConfig(
settings = mapOf(
SwiftExportConfig.DEBUG_MODE_ENABLED to parameters.debugMode.getOrElse(false).toString(),
SwiftExportConfig.BRIDGE_MODULE_NAME to parameters.bridgeModuleName.getOrElse(SwiftExportConfig.DEFAULT_BRIDGE_MODULE_NAME),
),
Companion,
distribution = Distribution(parameters.konanDistribution.getFile().absolutePath),
),
output = SwiftExportOutput(
swiftApi = parameters.swiftApiPath.getFile().toPath(),
kotlinBridges = parameters.kotlinBridgePath.getFile().toPath(),
cHeaderBridges = parameters.headerBridgePath.getFile().toPath(),
)
)
}
}
@@ -0,0 +1,52 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.plugin.mpp.apple.swiftexport
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.Category
import org.gradle.api.attributes.LibraryElements
import org.gradle.api.attributes.Usage
import org.jetbrains.kotlin.gradle.internal.KOTLIN_MODULE_GROUP
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
import org.jetbrains.kotlin.gradle.plugin.usageByName
import org.jetbrains.kotlin.gradle.utils.*
private const val SWIFT_EXPORT_CLASSPATH = "swiftExportClasspath"
private const val SWIFT_EXPORT_CLASSPATH_RESOLVABLE = "swiftExportClasspathResolvable"
private const val SWIFT_EXPORT_EMBEDDABLE_MODULE = "swift-export-embeddable"
internal fun Project.initSwiftExportClasspathConfigurations() {
if (project.kotlinPropertiesProvider.swiftExportEnabled) {
maybeCreateSwiftExportClasspathDependenciesConfiguration()
maybeCreateSwiftExportClasspathResolvableConfiguration()
}
}
private fun Project.maybeCreateSwiftExportClasspathDependenciesConfiguration(): Configuration {
return configurations.findDependencyScope(SWIFT_EXPORT_CLASSPATH)
?: project.configurations.createDependencyScope(SWIFT_EXPORT_CLASSPATH) {
description = "Runtime classpath for the SwiftExport worker."
defaultDependencies { dependencies ->
dependencies.add(
project.dependencies.create("$KOTLIN_MODULE_GROUP:$SWIFT_EXPORT_EMBEDDABLE_MODULE:${getKotlinPluginVersion()}")
)
}
}.get()
}
internal fun Project.maybeCreateSwiftExportClasspathResolvableConfiguration(): Configuration {
return configurations.findResolvable(SWIFT_EXPORT_CLASSPATH_RESOLVABLE)
?: project.configurations.createResolvable(SWIFT_EXPORT_CLASSPATH_RESOLVABLE) {
description = "Resolves the runtime classpath for the SwiftExport worker."
attributes.setAttribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
attributes.setAttribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
attributes.setAttribute(Usage.USAGE_ATTRIBUTE, usageByName(Usage.JAVA_RUNTIME))
extendsFrom(maybeCreateSwiftExportClasspathDependenciesConfiguration())
}
}
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.plugin.mpp.apple.swiftexport
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import org.gradle.workers.WorkParameters
internal interface SwiftExportParameters : WorkParameters {
@get:Input
val bridgeModuleName: Property<String>
@get:Input
val debugMode: Property<Boolean>
@get:InputDirectory
@get:PathSensitive(PathSensitivity.RELATIVE)
val konanDistribution: DirectoryProperty
@get:InputDirectory
@get:PathSensitive(PathSensitivity.RELATIVE)
val sourceRoot: DirectoryProperty
@get:OutputFile
val swiftApiPath: RegularFileProperty
@get:OutputFile
val headerBridgePath: RegularFileProperty
@get:OutputFile
val kotlinBridgePath: RegularFileProperty
}
@@ -7,73 +7,40 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.apple.swiftexport
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import org.gradle.work.DisableCachingByDefault
import org.jetbrains.kotlin.gradle.internal.LogType
import org.jetbrains.kotlin.gradle.internal.processLogMessage
import org.jetbrains.kotlin.gradle.utils.getFile
import org.jetbrains.kotlin.konan.target.Distribution
import org.jetbrains.kotlin.swiftexport.standalone.*
import org.gradle.workers.WorkerExecutor
import javax.inject.Inject
@DisableCachingByDefault(because = "Swift Export is experimental, so no caching for now")
abstract class SwiftExportTask : DefaultTask(), SwiftExportLogger {
@get:Input
abstract val bridgeModuleName: Property<String>
@get:Input
abstract val debugMode: Property<Boolean>
@get:InputDirectory
@get:PathSensitive(PathSensitivity.ABSOLUTE)
abstract val konanDistribution: DirectoryProperty
internal abstract class SwiftExportTask : DefaultTask() {
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val sourceRoots: ConfigurableFileCollection
abstract val swiftExportClasspath: ConfigurableFileCollection
@get:OutputFile
abstract val swiftApiPath: RegularFileProperty
@get:Inject
abstract val workerExecutor: WorkerExecutor
@get:OutputFile
abstract val headerBridgePath: RegularFileProperty
@get:OutputFile
abstract val kotlinBridgePath: RegularFileProperty
@get:Nested
abstract val parameters: SwiftExportParameters
@TaskAction
fun run() {
runSwiftExport(
input = SwiftExportInput(
sourceRoot = sourceRoots.filter { it.exists() }.map { it.toPath() }.first()
),
config = SwiftExportConfig(
settings = mapOf(
SwiftExportConfig.DEBUG_MODE_ENABLED to debugMode.getOrElse(false).toString(),
SwiftExportConfig.BRIDGE_MODULE_NAME to bridgeModuleName.getOrElse(SwiftExportConfig.DEFAULT_BRIDGE_MODULE_NAME),
),
this,
distribution = Distribution(konanDistribution.getFile().absolutePath),
),
output = SwiftExportOutput(
swiftApi = swiftApiPath.get().asFile.toPath(),
kotlinBridges = kotlinBridgePath.get().asFile.toPath(),
cHeaderBridges = headerBridgePath.get().asFile.toPath(),
)
)
}
val workQueue = workerExecutor.classLoaderIsolation { workerSpec ->
workerSpec.classpath.from(swiftExportClasspath)
}
override fun report(severity: SwiftExportLogger.Severity, message: String) {
logger.processLogMessage(message, severity.logType())
}
}
workQueue.submit(SwiftExportAction::class.java) { workParameters ->
workParameters.debugMode.set(parameters.debugMode)
workParameters.bridgeModuleName.set(parameters.bridgeModuleName)
workParameters.konanDistribution.set(parameters.konanDistribution)
workParameters.sourceRoot.set(parameters.sourceRoot)
private fun SwiftExportLogger.Severity.logType(): LogType = when (this) {
SwiftExportLogger.Severity.Info -> LogType.INFO
SwiftExportLogger.Severity.Warning -> LogType.WARN
SwiftExportLogger.Severity.Error -> LogType.ERROR
else -> LogType.LOG
workParameters.swiftApiPath.set(parameters.swiftApiPath)
workParameters.headerBridgePath.set(parameters.headerBridgePath)
workParameters.kotlinBridgePath.set(parameters.kotlinBridgePath)
}
}
}
@@ -4,6 +4,10 @@ plugins {
java
}
if (kotlinBuildProperties.isSwiftExportPluginPublishingEnabled) {
publish()
}
dependencies {
embedded(project(":native:swift:sir")) { isTransitive = false }
embedded(project(":native:swift:sir-compiler-bridge")) { isTransitive = false }
@@ -29,6 +29,8 @@ import java.lang.management.ManagementFactory
import java.nio.file.Files
import java.nio.file.Path
private const val SWIFT_EXPORT_EMBEDDABLE = ":native:swift:swift-export-embeddable"
val kotlinGradlePluginAndItsRequired = arrayOf(
":kotlin-assignment",
":kotlin-allopen",
@@ -82,21 +84,35 @@ val kotlinGradlePluginAndItsRequired = arrayOf(
":kotlin-test-js-runner",
":native:kotlin-klib-commonizer-embeddable",
":native:kotlin-klib-commonizer-api",
SWIFT_EXPORT_EMBEDDABLE,
":compiler:build-tools:kotlin-build-statistics",
":compiler:build-tools:kotlin-build-tools-api",
":compiler:build-tools:kotlin-build-tools-impl",
)
private fun Task.processDependent(dependent: String, action: () -> Unit) {
val isSwiftExportEmbeddable = dependent == SWIFT_EXPORT_EMBEDDABLE
val isSwiftExportPluginPublishingEnabled = project.kotlinBuildProperties.isSwiftExportPluginPublishingEnabled
if (!isSwiftExportEmbeddable || isSwiftExportPluginPublishingEnabled) {
action.invoke()
}
}
fun Task.dependsOnKotlinGradlePluginInstall() {
kotlinGradlePluginAndItsRequired.forEach {
dependsOn("${it}:install")
kotlinGradlePluginAndItsRequired.forEach { dependency ->
processDependent(dependency) {
dependsOn("${dependency}:install")
}
}
}
fun Task.dependsOnKotlinGradlePluginPublish() {
kotlinGradlePluginAndItsRequired.forEach {
project.rootProject.tasks.findByPath("${it}:publish")?.let { task ->
dependsOn(task)
kotlinGradlePluginAndItsRequired.forEach { dependency ->
processDependent(dependency) {
project.rootProject.tasks.findByPath("${dependency}:publish")?.let { task ->
dependsOn(task)
}
}
}
}