Skip to content

Commit

Permalink
Add support for Kotlin Artifact DSL.
Browse files Browse the repository at this point in the history
  • Loading branch information
TadeasKriz committed Nov 2, 2023
1 parent 5fb685e commit b6408a7
Show file tree
Hide file tree
Showing 19 changed files with 529 additions and 186 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,27 @@ import co.touchlab.skie.plugin.coroutines.registerConfigureMinOsVersionTaskIfNee
import co.touchlab.skie.plugin.defaultarguments.disableCachingIfNeeded
import co.touchlab.skie.plugin.dependencies.SkieCompilerPluginDependencyProvider
import co.touchlab.skie.plugin.directory.SkieDirectoriesManager
import co.touchlab.skie.plugin.directory.skieDirectories
import co.touchlab.skie.plugin.fatframework.FatFrameworkConfigurator
import co.touchlab.skie.plugin.subplugin.SkieSubPluginManager
import co.touchlab.skie.plugin.switflink.SwiftLinkingConfigurator
import co.touchlab.skie.plugin.util.appleTargets
import co.touchlab.skie.plugin.util.frameworks
import co.touchlab.skie.plugin.util.subpluginOption
import co.touchlab.skie.plugin.util.*
import co.touchlab.skie.util.plugin.SkiePlugin
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
import org.jetbrains.kotlin.gradle.dsl.KotlinNativeArtifact
import org.jetbrains.kotlin.gradle.targets.native.tasks.artifact.kotlinArtifactsExtension
import org.jetbrains.kotlin.konan.target.HostManager

abstract class SkieGradlePlugin : Plugin<Project> {

override fun apply(project: Project) {
project.extensions.create("skieInternal", SkieInternalExtension::class.java)

project.configureSkieGradlePlugin()

project.afterEvaluate {
project.configureRuntimeVariantFallback()
project.configureSkieCompilerPlugin()
}
}
Expand All @@ -38,6 +37,15 @@ abstract class SkieGradlePlugin : Plugin<Project> {
SkieSubPluginManager.configureDependenciesForSubPlugins(project)
}

private fun Project.configureRuntimeVariantFallback() {
if (!skieInternal.runtimeVariantFallback.isPresent) {
val extraPropertiesKey = "skieRuntimeVariantFallback"
skieInternal.runtimeVariantFallback.set(
project.properties[extraPropertiesKey]?.toString().toBoolean()
)
}
}

private fun Project.configureSkieCompilerPlugin() {
if (!isSkieEnabled) {
return
Expand All @@ -47,69 +55,74 @@ abstract class SkieGradlePlugin : Plugin<Project> {

FatFrameworkConfigurator.configureSkieForFatFrameworks(project)

configureEachKotlinFrameworkLinkTask {
configureSkieForLinkTask()
kotlinMultiplatformExtension?.appleTargets?.all {
val target = this
binaries.all {
val binary = this
skieInternal.targets.add(
SkieTarget.TargetBinary(
project = project,
target = target,
binary = binary,
)
)
}
}

kotlinArtifactsExtension.artifacts.withType<KotlinNativeArtifact>().all {
skieInternal.targets.addAll(skieTargetsOf(this))
}

skieInternal.targets.all {
configureSkie()
}
}

private fun KotlinNativeLink.configureSkieForLinkTask() {
private fun SkieTarget.configureSkie() {
SkieDirectoriesManager.configureCreateSkieBuildDirectoryTask(this)

GradleAnalyticsManager(project).configureAnalytics(this)

disableCachingIfNeeded()
binary.target.addDependencyOnSkieRuntime()
binary.registerConfigureMinOsVersionTaskIfNeeded()
registerConfigureMinOsVersionTaskIfNeeded()

CreateSkieConfigurationTask.registerTask(this)

SwiftLinkingConfigurator.configureCustomSwiftLinking(this)

disableCachingIfNeeded()

addDependencyOnSkieRuntime()

SkieSubPluginManager.registerSubPlugins(this)

configureKotlinCompiler()
}

private fun KotlinNativeLink.configureKotlinCompiler() {
compilerPluginClasspath = listOfNotNull(
compilerPluginClasspath,
SkieCompilerPluginDependencyProvider.getOrCreateDependencyConfiguration(project),
).reduce(FileCollection::plus)

compilerPluginOptions.addPluginArgument(
private fun SkieTarget.configureKotlinCompiler() {
addPluginArgument(
SkiePlugin.id,
SkiePlugin.Options.skieDirectories.subpluginOption(skieDirectories),
SkiePlugin.Options.skieDirectories.subpluginOption(skieDirectories.get()),
)
}
}

internal fun Project.warnOnEmptyFrameworks() {
val hasFrameworks = extensions.findByType(KotlinMultiplatformExtension::class.java)?.appleTargets?.any { it.frameworks.isNotEmpty() } ?: false
if (!hasFrameworks) {
logger.warn("w: No Apple frameworks configured. Make sure you applied SKIE plugin in the correct module.")
addToCompilerClasspath(
SkieCompilerPluginDependencyProvider.getOrCreateDependencyConfiguration(project)
)
}
}

internal fun Project.configureEachKotlinFrameworkLinkTask(
configure: KotlinNativeLink.() -> Unit,
) {
configureEachKotlinAppleTarget {
frameworks.forEach { framework ->
// Cannot use configure on linkTaskProvider because it's not possible to register new tasks in configure block of another task
configure(framework.linkTask)
internal fun Project.warnOnEmptyFrameworks() {
gradle.taskGraph.whenReady {
if (skieInternal.targets.isEmpty()) {
logger.warn("w: No Apple frameworks configured in module ${this@warnOnEmptyFrameworks.path}. Make sure you applied SKIE plugin in the correct module.")
}
}
}

internal fun Project.configureEachKotlinAppleTarget(
configure: KotlinNativeTarget.() -> Unit,
) {
val kotlinExtension = extensions.findByType(KotlinMultiplatformExtension::class.java) ?: return

kotlinExtension.appleTargets.forEach {
configure(it)
}
}

private val Project.isSkieEnabled: Boolean
get() = project.skieExtension.isEnabled.get() && HostManager.hostIsMac

internal val Project.kotlinMultiplatformExtension: KotlinMultiplatformExtension?
get() = project.extensions.findByType(KotlinMultiplatformExtension::class.java)

internal val Project.skieInternal: SkieInternalExtension
get() = project.extensions.getByType(SkieInternalExtension::class.java)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package co.touchlab.skie.plugin

import co.touchlab.skie.plugin.util.SkieTarget
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import javax.inject.Inject

internal interface SkieInternalExtension {
val targets: NamedDomainObjectContainer<SkieTarget>

val runtimeVariantFallback: Property<Boolean>
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package co.touchlab.skie.plugin.analytics

import co.touchlab.skie.plugin.util.SkieTarget
import co.touchlab.skie.plugin.analytics.environment.GradleEnvironmentAnalytics
import co.touchlab.skie.plugin.analytics.git.GitAnalytics
import co.touchlab.skie.plugin.analytics.hardware.HardwareAnalytics
Expand All @@ -8,58 +9,53 @@ import co.touchlab.skie.plugin.analytics.project.ProjectAnalytics
import co.touchlab.skie.plugin.configuration.SkieExtension.Companion.buildConfiguration
import co.touchlab.skie.plugin.configuration.skieExtension
import co.touchlab.skie.plugin.directory.createSkieBuildDirectoryTask
import co.touchlab.skie.plugin.directory.skieDirectories
import co.touchlab.skie.plugin.util.doFirstOptimized
import co.touchlab.skie.plugin.util.doLastOptimized
import co.touchlab.skie.plugin.util.registerSkieLinkBasedTask
import co.touchlab.skie.plugin.util.*
import co.touchlab.skie.plugin.util.registerSkieTargetBasedTask
import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
import java.time.Duration

internal class GradleAnalyticsManager(
private val project: Project,
) {

fun configureAnalytics(linkTask: KotlinNativeLink) {
val analyticsCollectorProvider = project.provider {
fun configureAnalytics(target: SkieTarget) {
val analyticsCollectorProvider = target.skieDirectories.map { skieDirectories ->
AnalyticsCollector(
skieBuildDirectory = linkTask.skieDirectories.buildDirectory,
skieBuildDirectory = skieDirectories.buildDirectory,
skieConfiguration = project.skieExtension.buildConfiguration(),
)
}

configureUploadAnalyticsTask(linkTask)
configureUploadAnalyticsTask(target)

registerAnalyticsProducers(linkTask, analyticsCollectorProvider)
registerAnalyticsProducers(target, analyticsCollectorProvider)
}

private fun configureUploadAnalyticsTask(linkTask: KotlinNativeLink) {
val uploadTask = linkTask.registerSkieLinkBasedTask<SkieUploadAnalyticsTask>("uploadAnalytics") {
this.analyticsDirectory.set(linkTask.skieDirectories.buildDirectory.analytics.directory)
this.applicationSupportDirectory.set(linkTask.skieDirectories.applicationSupport)
private fun configureUploadAnalyticsTask(target: SkieTarget) {
val uploadTask = target.registerSkieTargetBasedTask<SkieUploadAnalyticsTask>("uploadAnalytics") {
this.analyticsDirectory.set(target.skieDirectories.map { it.buildDirectory.analytics.directory })
this.applicationSupportDirectory.set(target.skieDirectories.map { it.applicationSupport })

dependsOn(linkTask.createSkieBuildDirectoryTask)
}

linkTask.finalizedBy(uploadTask)
dependsOn(target.createSkieBuildDirectoryTask)

linkTask.project.afterEvaluate {
uploadTask.configure {
onlyIf {
val analyticsConfiguration = project.skieExtension.analytics
onlyIf {
val analyticsConfiguration = project.skieExtension.analytics

analyticsConfiguration.enabled.get() && !analyticsConfiguration.disableUpload.get()
}
analyticsConfiguration.enabled.get() && !analyticsConfiguration.disableUpload.get()
}
}

target.task.configure {
finalizedBy(uploadTask)
}
}

private fun registerAnalyticsProducers(
linkTask: KotlinNativeLink,
target: SkieTarget,
analyticsCollectorProvider: Provider<AnalyticsCollector>,
) {
linkTask.doFirstOptimized {
target.task.configureDoFirstOptimized {
analyticsCollectorProvider.get().collectAsync(
GradleEnvironmentAnalytics.Producer(project),

Expand All @@ -71,20 +67,20 @@ internal class GradleAnalyticsManager(
)
}

registerPerformanceAnalyticsProducer(linkTask, analyticsCollectorProvider)
registerPerformanceAnalyticsProducer(target, analyticsCollectorProvider)
}

private fun registerPerformanceAnalyticsProducer(
linkTask: KotlinNativeLink,
target: SkieTarget,
analyticsCollectorProvider: Provider<AnalyticsCollector>,
) {
var start: Long = 0

linkTask.doFirstOptimized {
target.task.configureDoFirstOptimized {
start = System.currentTimeMillis()
}

linkTask.doLastOptimized {
target.task.configureDoLastOptimized {
val linkTaskDuration = Duration.ofMillis(System.currentTimeMillis() - start)

analyticsCollectorProvider.get().collectSynchronously(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package co.touchlab.skie.plugin.configuration

import co.touchlab.skie.plugin.util.SkieTarget
import co.touchlab.skie.plugin.configuration.SkieExtension.Companion.buildConfiguration
import co.touchlab.skie.plugin.directory.createSkieBuildDirectoryTask
import co.touchlab.skie.plugin.directory.skieBuildDirectory
import co.touchlab.skie.plugin.util.registerSkieLinkBasedTask
import co.touchlab.skie.plugin.util.registerSkieTargetBasedTask
import co.touchlab.skie.plugin.util.skieBuildDirectory
import co.touchlab.skie.plugin.util.skieConfiguration
import groovy.json.JsonOutput
import org.gradle.api.DefaultTask
import org.gradle.api.provider.Property
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
import java.io.File

internal abstract class CreateSkieConfigurationTask : DefaultTask() {
Expand All @@ -35,14 +36,16 @@ internal abstract class CreateSkieConfigurationTask : DefaultTask() {

companion object {

fun registerTask(linkTask: KotlinNativeLink) {
val createConfiguration = linkTask.registerSkieLinkBasedTask<CreateSkieConfigurationTask>("createConfiguration") {
configurationFile.set(linkTask.skieBuildDirectory.skieConfiguration)
fun registerTask(target: SkieTarget) {
val createConfiguration = target.registerSkieTargetBasedTask<CreateSkieConfigurationTask>("createConfiguration") {
configurationFile.set(target.skieBuildDirectory.skieConfiguration)

dependsOn(linkTask.createSkieBuildDirectoryTask)
dependsOn(target.createSkieBuildDirectoryTask)
}

linkTask.inputs.files(createConfiguration.map { it.outputs })
target.task.configure {
inputs.files(createConfiguration.map { it.outputs })
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,39 @@
package co.touchlab.skie.plugin.coroutines

import co.touchlab.skie.gradle.KotlinCompilerVersion
import co.touchlab.skie.gradle_plugin.BuildConfig
import co.touchlab.skie.plugin.util.named
import co.touchlab.skie.plugin.util.withType
import co.touchlab.skie.plugin.util.SkieTarget
import co.touchlab.skie.plugin.skieInternal
import co.touchlab.skie.plugin.util.lowerCamelCaseName
import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractNativeLibrary
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.presetName

internal fun KotlinNativeTarget.addDependencyOnSkieRuntime() {
if (!project.isCoroutinesInteropEnabled) {
return
internal fun SkieTarget.addDependencyOnSkieRuntime() {
val configurationNames = when (this) {
is SkieTarget.TargetBinary -> listOfNotNull(
binary.compilation.apiConfigurationName,
(binary as? AbstractNativeLibrary)?.exportConfigurationName,
)
is SkieTarget.Artifact -> listOf(
lowerCamelCaseName(konanTarget.presetName, artifact.artifactName, "linkLibrary"),
lowerCamelCaseName(konanTarget.presetName, artifact.artifactName, "linkExport"),
)
}

compilations.named("main") {
defaultSourceSet.dependencies {
api(BuildConfig.RUNTIME_DEPENDENCY(konanTarget, BuildConfig.KOTLIN_TOOLING_VERSION))
}
val dependency = if (project.skieInternal.runtimeVariantFallback.get()) {
BuildConfig.DEFAULT_RUNTIME_DEPENDENCY
} else {
BuildConfig.SPECIFIC_RUNTIME_DEPENDENCY(konanTarget, BuildConfig.KOTLIN_TOOLING_VERSION)
}

binaries.withType<AbstractNativeLibrary>().configureEach {
export(BuildConfig.RUNTIME_DEPENDENCY(konanTarget, BuildConfig.KOTLIN_TOOLING_VERSION))
configurationNames.forEach { configurationName ->
project.dependencies.add(configurationName, dependency)
}
}

private fun BuildConfig.RUNTIME_DEPENDENCY(konanTarget: KonanTarget, kotlinVersion: String): String {
private val BuildConfig.DEFAULT_RUNTIME_DEPENDENCY: String
get() = "$RUNTIME_DEPENDENCY_GROUP:$RUNTIME_DEPENDENCY_NAME:$RUNTIME_DEPENDENCY_VERSION"

private fun BuildConfig.SPECIFIC_RUNTIME_DEPENDENCY(konanTarget: KonanTarget, kotlinVersion: String): String {
return "$RUNTIME_DEPENDENCY_GROUP:$RUNTIME_DEPENDENCY_NAME-${konanTarget.presetName}__kgp_${kotlinVersion}:$RUNTIME_DEPENDENCY_VERSION"
}

Expand Down
Loading

0 comments on commit b6408a7

Please sign in to comment.