Add artifacts collection inside KotlinArtifacts extension.

And make KotlinArtifact instances expandable via groovy machinery.
This commit is contained in:
konstantin.tskhovrebov
2022-03-29 10:17:03 +02:00
committed by Space
parent aafab7800b
commit 9430f0bafb
8 changed files with 157 additions and 137 deletions
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.native
import org.jetbrains.kotlin.gradle.BaseGradleIT import org.jetbrains.kotlin.gradle.BaseGradleIT
import org.jetbrains.kotlin.gradle.GradleVersionRequired import org.jetbrains.kotlin.gradle.GradleVersionRequired
import org.jetbrains.kotlin.gradle.native.GeneralNativeIT.Companion.withNativeCommandLineArguments import org.jetbrains.kotlin.gradle.native.GeneralNativeIT.Companion.withNativeCommandLineArguments
import org.jetbrains.kotlin.gradle.transformProjectWithPluginsDsl
import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.HostManager
import org.junit.Assume import org.junit.Assume
import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertFalse
@@ -19,14 +20,14 @@ class NativeLibraryDslIT : BaseGradleIT() {
@Test @Test
fun `check registered gradle tasks`() { fun `check registered gradle tasks`() {
with(Project("new-kn-library-dsl")) { with(transformProjectWithPluginsDsl("new-kn-library-dsl")) {
build(":shared:tasks") { build(":shared:tasks") {
assertSuccessful() assertSuccessful()
assertTasksRegistered( assertTasksRegistered(
":shared:assembleMyfatframeFatFramework", ":shared:assembleMyfatframeFatFramework",
":shared:assembleMyframeFramework", ":shared:assembleMyframeFrameworkIosArm64",
":shared:assembleMylibSharedLibrary", ":shared:assembleMylibSharedLibraryLinuxX64",
":shared:assembleMyslibSharedLibrary", ":shared:assembleMyslibSharedLibraryLinuxX64",
":shared:assembleSharedXCFramework" ":shared:assembleSharedXCFramework"
) )
assertTasksNotRegistered( assertTasksNotRegistered(
@@ -37,8 +38,8 @@ class NativeLibraryDslIT : BaseGradleIT() {
assertSuccessful() assertSuccessful()
assertTasksRegistered( assertTasksRegistered(
":lib:assembleGroofatframeFatFramework", ":lib:assembleGroofatframeFatFramework",
":lib:assembleGrooframeFramework", ":lib:assembleGrooframeFrameworkIosArm64",
":lib:assembleGroolibSharedLibrary", ":lib:assembleGroolibSharedLibraryIosX64",
":lib:assembleLibXCFramework" ":lib:assembleLibXCFramework"
) )
} }
@@ -47,7 +48,7 @@ class NativeLibraryDslIT : BaseGradleIT() {
@Test @Test
fun `link shared library from two gradle modules`() { fun `link shared library from two gradle modules`() {
with(Project("new-kn-library-dsl")) { with(transformProjectWithPluginsDsl("new-kn-library-dsl")) {
build(":shared:assembleMyslibDebugSharedLibraryLinuxX64") { build(":shared:assembleMyslibDebugSharedLibraryLinuxX64") {
assertSuccessful() assertSuccessful()
assertTasksExecuted( assertTasksExecuted(
@@ -63,7 +64,7 @@ class NativeLibraryDslIT : BaseGradleIT() {
@Test @Test
fun `link shared library from single gradle module`() { fun `link shared library from single gradle module`() {
with(Project("new-kn-library-dsl")) { with(transformProjectWithPluginsDsl("new-kn-library-dsl")) {
build(":shared:assembleMylibDebugSharedLibraryLinuxX64") { build(":shared:assembleMylibDebugSharedLibraryLinuxX64") {
assertSuccessful() assertSuccessful()
assertTasksExecuted( assertTasksExecuted(
@@ -86,8 +87,7 @@ class NativeLibraryDslIT : BaseGradleIT() {
@Test @Test
fun `link shared library from single gradle module with additional link args`() { fun `link shared library from single gradle module with additional link args`() {
with(Project("new-kn-library-dsl")) { with(transformProjectWithPluginsDsl("new-kn-library-dsl")) {
setupWorkingDir()
gradleProperties().appendText("\nkotlin.native.linkArgs=-Xfoo=bar -Xbaz=qux") gradleProperties().appendText("\nkotlin.native.linkArgs=-Xfoo=bar -Xbaz=qux")
build(":shared:assembleMylibDebugSharedLibraryLinuxX64") { build(":shared:assembleMylibDebugSharedLibraryLinuxX64") {
assertSuccessful() assertSuccessful()
@@ -112,7 +112,7 @@ class NativeLibraryDslIT : BaseGradleIT() {
@Test @Test
fun `link release XCFramework from two gradle modules`() { fun `link release XCFramework from two gradle modules`() {
Assume.assumeTrue(HostManager.hostIsMac) Assume.assumeTrue(HostManager.hostIsMac)
with(Project("new-kn-library-dsl")) { with(transformProjectWithPluginsDsl("new-kn-library-dsl")) {
build(":shared:assembleSharedReleaseXCFramework") { build(":shared:assembleSharedReleaseXCFramework") {
assertSuccessful() assertSuccessful()
assertTasksExecuted( assertTasksExecuted(
@@ -1,3 +1,7 @@
plugins {
kotlin("multiplatform").apply(false)
}
allprojects { allprojects {
repositories { repositories {
mavenLocal() mavenLocal()
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.gradle.targets.native.tasks.artifact package org.jetbrains.kotlin.gradle.targets.native.tasks.artifact
import groovy.lang.Closure
import org.gradle.api.Action import org.gradle.api.Action
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.attributes.Usage import org.gradle.api.attributes.Usage
@@ -18,7 +17,12 @@ import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.presetName import org.jetbrains.kotlin.konan.target.presetName
abstract class KotlinArtifact { abstract class KotlinArtifact(
val project: Project,
val artifactName: String
) {
internal abstract val taskName: String
internal val modules = mutableSetOf<Any>() internal val modules = mutableSetOf<Any>()
fun setModules(vararg project: Any) { fun setModules(vararg project: Any) {
modules.clear() modules.clear()
@@ -29,11 +33,11 @@ abstract class KotlinArtifact {
modules.add(project) modules.add(project)
} }
internal abstract fun validate(project: Project, name: String): Boolean internal abstract fun validate()
internal abstract fun registerAssembleTask(project: Project, name: String) internal abstract fun registerAssembleTask()
} }
abstract class KotlinNativeArtifact : KotlinArtifact() { abstract class KotlinNativeArtifact(project: Project, artifactName: String) : KotlinArtifact(project, artifactName) {
var modes: Set<NativeBuildType> = NativeBuildType.DEFAULT_BUILD_TYPES var modes: Set<NativeBuildType> = NativeBuildType.DEFAULT_BUILD_TYPES
fun modes(vararg modes: NativeBuildType) { fun modes(vararg modes: NativeBuildType) {
this.modes = modes.toSet() this.modes = modes.toSet()
@@ -52,22 +56,16 @@ abstract class KotlinNativeArtifact : KotlinArtifact() {
binaryOptions[name] = value binaryOptions[name] = value
} }
override fun validate(project: Project, name: String): Boolean { override fun validate() {
val logger = project.logger check(modules.isNotEmpty()) {
if (modules.isEmpty()) { "Native artifact '$artifactName' wasn't configured because it requires at least one module for linking"
logger.error("Native library '${name}' wasn't configured because it requires at least one module for linking")
return false
} }
check(modes.isNotEmpty()) {
if (modes.isEmpty()) { "Native artifact '$artifactName' wasn't configured because it requires at least one build type in modes"
logger.error("Native library '${name}' wasn't configured because it requires at least one build type in modes")
return false
} }
return true
} }
fun Project.registerLibsDependencies(target: KonanTarget, artifactName: String, deps: Set<Any>): String { protected fun Project.registerLibsDependencies(target: KonanTarget, artifactName: String, deps: Set<Any>): String {
val librariesConfigurationName = lowerCamelCaseName(target.presetName, artifactName, "linkLibrary") val librariesConfigurationName = lowerCamelCaseName(target.presetName, artifactName, "linkLibrary")
configurations.maybeCreate(librariesConfigurationName).apply { configurations.maybeCreate(librariesConfigurationName).apply {
isVisible = false isVisible = false
@@ -82,7 +80,7 @@ abstract class KotlinNativeArtifact : KotlinArtifact() {
return librariesConfigurationName return librariesConfigurationName
} }
fun Project.registerExportDependencies(target: KonanTarget, artifactName: String, deps: Set<Any>): String { protected fun Project.registerExportDependencies(target: KonanTarget, artifactName: String, deps: Set<Any>): String {
val exportConfigurationName = lowerCamelCaseName(target.presetName, artifactName, "linkExport") val exportConfigurationName = lowerCamelCaseName(target.presetName, artifactName, "linkExport")
configurations.maybeCreate(exportConfigurationName).apply { configurations.maybeCreate(exportConfigurationName).apply {
isVisible = false isVisible = false
@@ -8,25 +8,16 @@ package org.jetbrains.kotlin.gradle.targets.native.tasks.artifact
import org.gradle.api.Action import org.gradle.api.Action
import org.gradle.api.Project import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType
import org.jetbrains.kotlin.gradle.utils.castIsolatedKotlinPluginClassLoaderAware
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
import javax.inject.Inject import javax.inject.Inject
private val SAFE_NAME_PATTERN = """\W""".toRegex()
private inline fun <reified T : KotlinArtifact> Project.kotlinArtifact(configure: Action<T>) {
kotlinArtifact(name.replace(SAFE_NAME_PATTERN, "_"), configure)
}
private inline fun <reified T : KotlinArtifact> Project.kotlinArtifact(name: String, configure: Action<T>) {
val artifact = objects.newInstance(T::class.java)
artifact.addModule(this)
configure.execute(artifact)
if (artifact.validate(this, name)) {
artifact.registerAssembleTask(this, name)
}
}
//Groovy script DSL //Groovy script DSL
abstract class NativeArtifacts @Inject constructor(private val project: Project) { abstract class NativeArtifacts @Inject constructor(private val project: Project) {
companion object {
private val UNSAFE_NAME_SYMBOLS = """\W""".toRegex()
}
@RequiresOptIn( @RequiresOptIn(
message = "This API is experimental. It may be changed in the future.", message = "This API is experimental. It may be changed in the future.",
level = RequiresOptIn.Level.WARNING level = RequiresOptIn.Level.WARNING
@@ -37,51 +28,74 @@ abstract class NativeArtifacts @Inject constructor(private val project: Project)
@ExperimentalArtifactDsl @ExperimentalArtifactDsl
fun Library(name: String, configure: Action<KotlinNativeLibrary>) { fun Library(name: String, configure: Action<KotlinNativeLibrary>) {
project.kotlinArtifact(name, configure) addKotlinArtifact(name, configure)
} }
@ExperimentalArtifactDsl @ExperimentalArtifactDsl
fun Library(configure: Action<KotlinNativeLibrary>) { fun Library(configure: Action<KotlinNativeLibrary>) {
project.kotlinArtifact(configure) addKotlinArtifact(configure)
} }
@ExperimentalArtifactDsl @ExperimentalArtifactDsl
fun Framework(name: String, configure: Action<KotlinNativeFramework>) { fun Framework(name: String, configure: Action<KotlinNativeFramework>) {
project.kotlinArtifact(name, configure) addKotlinArtifact(name, configure)
} }
@ExperimentalArtifactDsl @ExperimentalArtifactDsl
fun Framework(configure: Action<KotlinNativeFramework>) { fun Framework(configure: Action<KotlinNativeFramework>) {
project.kotlinArtifact(configure) addKotlinArtifact(configure)
} }
@ExperimentalArtifactDsl @ExperimentalArtifactDsl
fun FatFramework(name: String, configure: Action<KotlinNativeFatFramework>) { fun FatFramework(name: String, configure: Action<KotlinNativeFatFramework>) {
project.kotlinArtifact(name, configure) addKotlinArtifact(name, configure)
} }
@ExperimentalArtifactDsl @ExperimentalArtifactDsl
fun FatFramework(configure: Action<KotlinNativeFatFramework>) { fun FatFramework(configure: Action<KotlinNativeFatFramework>) {
project.kotlinArtifact(configure) addKotlinArtifact(configure)
} }
@ExperimentalArtifactDsl @ExperimentalArtifactDsl
fun XCFramework(name: String, configure: Action<KotlinNativeXCFramework>) { fun XCFramework(name: String, configure: Action<KotlinNativeXCFramework>) {
project.kotlinArtifact(name, configure) addKotlinArtifact(name, configure)
} }
@ExperimentalArtifactDsl @ExperimentalArtifactDsl
fun XCFramework(configure: Action<KotlinNativeXCFramework>) { fun XCFramework(configure: Action<KotlinNativeXCFramework>) {
project.kotlinArtifact(configure) addKotlinArtifact(configure)
}
private inline fun <reified T : KotlinArtifact> addKotlinArtifact(configure: Action<T>) {
addKotlinArtifact(project.name.replace(UNSAFE_NAME_SYMBOLS, "_"), configure)
}
private inline fun <reified T : KotlinArtifact> addKotlinArtifact(name: String, configure: Action<T>) {
//create via newInstance for extensibility
val artifact = project.objects.newInstance(T::class.java, project, name)
//we should add artifact to collection BEFORE configuration
//because other plugins can add extensions for artifacts which will be used in configuration block
project.kotlinArtifactsExtension.artifacts.add(artifact)
artifact.addModule(project)
configure.execute(artifact)
artifact.registerAssembleTask()
} }
} }
//Groovy script DSL //Groovy script DSL
private const val KOTLIN_ARTIFACTS_EXTENSION_NAME = "kotlinArtifacts"
internal fun Project.registerKotlinArtifactsExtension() { internal fun Project.registerKotlinArtifactsExtension() {
extensions.create("kotlinArtifacts", KotlinArtifactsExtension::class.java, this) extensions.create(KOTLIN_ARTIFACTS_EXTENSION_NAME, KotlinArtifactsExtension::class.java, this)
} }
internal val Project.kotlinArtifactsExtension: KotlinArtifactsExtension
get() = extensions.getByName(KOTLIN_ARTIFACTS_EXTENSION_NAME).castIsolatedKotlinPluginClassLoaderAware()
abstract class KotlinArtifactsExtension @Inject constructor(project: Project) { abstract class KotlinArtifactsExtension @Inject constructor(project: Project) {
val artifacts = project.objects.domainObjectSet(KotlinArtifact::class.java)
val DEBUG = NativeBuildType.DEBUG val DEBUG = NativeBuildType.DEBUG
val RELEASE = NativeBuildType.RELEASE val RELEASE = NativeBuildType.RELEASE
@@ -17,8 +17,14 @@ import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.util.visibleName import org.jetbrains.kotlin.konan.util.visibleName
import javax.inject.Inject
open class KotlinNativeFatFramework @Inject constructor(
project: Project,
artifactName: String
) : KotlinNativeArtifact(project, artifactName) {
override val taskName get() = lowerCamelCaseName("assemble", artifactName, "FatFramework")
open class KotlinNativeFatFramework : KotlinNativeArtifact() {
var targets: Set<KonanTarget> = emptySet() var targets: Set<KonanTarget> = emptySet()
fun targets(vararg targets: KonanTarget) { fun targets(vararg targets: KonanTarget) {
this.targets = targets.toSet() this.targets = targets.toSet()
@@ -28,50 +34,47 @@ open class KotlinNativeFatFramework : KotlinNativeArtifact() {
private val kind = NativeOutputKind.FRAMEWORK private val kind = NativeOutputKind.FRAMEWORK
override fun validate(project: Project, name: String): Boolean { override fun validate() {
val logger = project.logger super.validate()
if (!super.validate(project, name)) return false check(targets.isNotEmpty()) {
if (targets.isEmpty()) { "Native artifact '$artifactName' wasn't configured because it requires at least one target"
logger.error("Native library '${name}' wasn't configured because it requires at least one target")
return false
} }
targets.firstOrNull { !kind.availableFor(it) }?.let { target -> val wrongTarget = targets.firstOrNull { !kind.availableFor(it) }
logger.error("Native library '${name}' wasn't configured because ${kind.description} is not available for ${target.visibleName}") check(wrongTarget == null) {
return false "Native artifact '$artifactName' wasn't configured because ${kind.description} is not available for ${wrongTarget!!.visibleName}"
} }
return true
} }
override fun registerAssembleTask(project: Project, name: String) { override fun registerAssembleTask() {
val parentTask = project.registerTask<Task>(lowerCamelCaseName("assemble", name, "FatFramework")) { validate()
val parentTask = project.registerTask<Task>(taskName) {
it.group = "build" it.group = "build"
it.description = "Assemble all types of registered '$name' FatFramework" it.description = "Assemble all types of registered '$artifactName' FatFramework"
} }
project.tasks.named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).dependsOn(parentTask) project.tasks.named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).dependsOn(parentTask)
modes.forEach { buildType -> modes.forEach { buildType ->
val fatTask = project.registerTask<FatFrameworkTask>( val fatTask = project.registerTask<FatFrameworkTask>(
lowerCamelCaseName("assemble", name, buildType.visibleName, "FatFramework") lowerCamelCaseName("assemble", artifactName, buildType.visibleName, "FatFramework")
) { ) {
it.baseName = name it.baseName = artifactName
it.destinationDir = project.buildDir.resolve("out/fatframework/${buildType.getName()}") it.destinationDir = project.buildDir.resolve("out/fatframework/${buildType.getName()}")
} }
parentTask.dependsOn(fatTask) parentTask.dependsOn(fatTask)
val nameSuffix = "ForFat" val nameSuffix = "ForFat"
val frameworkDescriptors: List<FrameworkDescriptor> = targets.map { target -> val frameworkDescriptors: List<FrameworkDescriptor> = targets.map { target ->
val librariesConfigurationName = project.registerLibsDependencies(target, name + nameSuffix, modules) val librariesConfigurationName = project.registerLibsDependencies(target, artifactName + nameSuffix, modules)
val exportConfigurationName = project.registerExportDependencies(target, name + nameSuffix, modules) val exportConfigurationName = project.registerExportDependencies(target, artifactName + nameSuffix, modules)
val targetTask = registerLinkFrameworkTask( val targetTask = registerLinkFrameworkTask(
project, project,
name, artifactName,
target, target,
buildType, buildType,
librariesConfigurationName, librariesConfigurationName,
exportConfigurationName, exportConfigurationName,
embedBitcode, embedBitcode,
"${name}FatFrameworkTemp", "${artifactName}FatFrameworkTemp",
nameSuffix nameSuffix
) )
fatTask.dependsOn(targetTask) fatTask.dependsOn(targetTask)
@@ -20,41 +20,40 @@ import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.presetName import org.jetbrains.kotlin.konan.target.presetName
import org.jetbrains.kotlin.konan.util.visibleName import org.jetbrains.kotlin.konan.util.visibleName
import javax.inject.Inject
open class KotlinNativeFramework : KotlinNativeArtifact() { open class KotlinNativeFramework @Inject constructor(project: Project, artifactName: String) : KotlinNativeArtifact(project, artifactName) {
lateinit var target: KonanTarget lateinit var target: KonanTarget
var embedBitcode: BitcodeEmbeddingMode? = null var embedBitcode: BitcodeEmbeddingMode? = null
private val kind = NativeOutputKind.FRAMEWORK private val kind = NativeOutputKind.FRAMEWORK
override fun validate(project: Project, name: String): Boolean { override val taskName get() = lowerCamelCaseName("assemble", artifactName, kind.taskNameClassifier, target.presetName)
val logger = project.logger
if (!super.validate(project, name)) return false
if (!this::target.isInitialized) {
logger.error("Native library '${name}' wasn't configured because it requires target")
return false
}
if (!kind.availableFor(target)) {
logger.error("Native library '${name}' wasn't configured because ${kind.description} is not available for ${target.visibleName}")
return false
}
return true override fun validate() {
super.validate()
check(this::target.isInitialized) {
"Native artifact '$artifactName' wasn't configured because it requires target"
}
check(kind.availableFor(target)) {
"Native artifact '$artifactName' wasn't configured because ${kind.description} is not available for ${target.visibleName}"
}
} }
override fun registerAssembleTask(project: Project, name: String) { override fun registerAssembleTask() {
val resultTask = project.registerTask<Task>(lowerCamelCaseName("assemble", name, kind.taskNameClassifier)) { task -> validate()
val resultTask = project.registerTask<Task>(taskName) { task ->
task.group = BasePlugin.BUILD_GROUP task.group = BasePlugin.BUILD_GROUP
task.description = "Assemble ${kind.description} '$name'." task.description = "Assemble ${kind.description} '$artifactName' for ${target.visibleName}."
task.enabled = target.enabledOnCurrentHost task.enabled = target.enabledOnCurrentHost
} }
val librariesConfigurationName = project.registerLibsDependencies(target, name, modules) val librariesConfigurationName = project.registerLibsDependencies(target, artifactName, modules)
val exportConfigurationName = project.registerExportDependencies(target, name, modules) val exportConfigurationName = project.registerExportDependencies(target, artifactName, modules)
modes.forEach { buildType -> modes.forEach { buildType ->
val targetTask = registerLinkFrameworkTask( val targetTask = registerLinkFrameworkTask(
project, project,
name, artifactName,
target, target,
buildType, buildType,
librariesConfigurationName, librariesConfigurationName,
@@ -17,49 +17,48 @@ import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.presetName import org.jetbrains.kotlin.konan.target.presetName
import org.jetbrains.kotlin.konan.util.visibleName import org.jetbrains.kotlin.konan.util.visibleName
import javax.inject.Inject
open class KotlinNativeLibrary : KotlinNativeArtifact() { open class KotlinNativeLibrary @Inject constructor(
project: Project,
artifactName: String
) : KotlinNativeArtifact(project, artifactName) {
lateinit var target: KonanTarget lateinit var target: KonanTarget
private val kind: NativeOutputKind private val kind: NativeOutputKind
get() = if (isStatic) NativeOutputKind.STATIC else NativeOutputKind.DYNAMIC get() = if (isStatic) NativeOutputKind.STATIC else NativeOutputKind.DYNAMIC
override fun validate(project: Project, name: String): Boolean { override val taskName get() = lowerCamelCaseName("assemble", artifactName, kind.taskNameClassifier, "Library", target.presetName)
val logger = project.logger
if (!super.validate(project, name)) return false
if (!this::target.isInitialized) {
logger.error("Native library '${name}' wasn't configured because it requires target")
return false
}
if (!kind.availableFor(target)) {
logger.error("Native library '${name}' wasn't configured because ${kind.description} is not available for ${target.visibleName}")
return false
}
return true override fun validate() {
super.validate()
check(this::target.isInitialized) {
"Native artifact '$artifactName' wasn't configured because it requires target"
}
check(kind.availableFor(target)) {
"Native artifact '$artifactName' wasn't configured because ${kind.description} is not available for ${target.visibleName}"
}
} }
override fun registerAssembleTask(project: Project, name: String) { override fun registerAssembleTask() {
validate()
val resultTask = project.registerTask<Task>( val resultTask = project.registerTask<Task>(taskName) { task ->
lowerCamelCaseName("assemble", name, kind.taskNameClassifier, "Library")
) { task ->
task.group = BasePlugin.BUILD_GROUP task.group = BasePlugin.BUILD_GROUP
task.description = "Assemble all types of registered '$name' ${kind.description}." task.description = "Assemble all types of registered '$artifactName' ${kind.description} for ${target.visibleName}."
task.enabled = target.enabledOnCurrentHost task.enabled = target.enabledOnCurrentHost
} }
project.tasks.named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).dependsOn(resultTask) project.tasks.named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).dependsOn(resultTask)
val librariesConfigurationName = project.registerLibsDependencies(target, name, modules) val librariesConfigurationName = project.registerLibsDependencies(target, artifactName, modules)
val exportConfigurationName = project.registerExportDependencies(target, name, modules) val exportConfigurationName = project.registerExportDependencies(target, artifactName, modules)
modes.forEach { buildType -> modes.forEach { buildType ->
val targetTask = project.registerTask<KotlinNativeLinkArtifactTask>( val targetTask = project.registerTask<KotlinNativeLinkArtifactTask>(
lowerCamelCaseName("assemble", name, buildType.visibleName, kind.taskNameClassifier, "Library", target.presetName), lowerCamelCaseName("assemble", artifactName, buildType.visibleName, kind.taskNameClassifier, "Library", target.presetName),
listOf(target, kind.compilerOutputKind) listOf(target, kind.compilerOutputKind)
) { task -> ) { task ->
task.description = "Assemble ${kind.description} '$name' for a target '${target.name}'." task.description = "Assemble ${kind.description} '$artifactName' for a target '${target.name}'."
task.enabled = target.enabledOnCurrentHost task.enabled = target.enabledOnCurrentHost
task.baseName = name task.baseName = artifactName
task.optimized = buildType.optimized task.optimized = buildType.optimized
task.debuggable = buildType.debuggable task.debuggable = buildType.debuggable
task.linkerOptions = linkerOptions task.linkerOptions = linkerOptions
@@ -17,8 +17,14 @@ import org.jetbrains.kotlin.gradle.tasks.dependsOn
import org.jetbrains.kotlin.gradle.tasks.registerTask import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
import javax.inject.Inject
open class KotlinNativeXCFramework @Inject constructor(
project: Project,
artifactName: String
) : KotlinNativeArtifact(project, artifactName) {
override val taskName get() = lowerCamelCaseName("assemble", artifactName, "XCFramework")
open class KotlinNativeXCFramework : KotlinNativeArtifact() {
var targets: Set<KonanTarget> = emptySet() var targets: Set<KonanTarget> = emptySet()
fun targets(vararg targets: KonanTarget) { fun targets(vararg targets: KonanTarget) {
this.targets = targets.toSet() this.targets = targets.toSet()
@@ -28,46 +34,43 @@ open class KotlinNativeXCFramework : KotlinNativeArtifact() {
private val kind = NativeOutputKind.FRAMEWORK private val kind = NativeOutputKind.FRAMEWORK
override fun validate(project: Project, name: String): Boolean { override fun validate() {
val logger = project.logger super.validate()
if (!super.validate(project, name)) return false check(targets.isNotEmpty()) {
if (targets.isEmpty()) { "Native artifact '$artifactName' wasn't configured because it requires at least one target"
logger.error("Native library '${name}' wasn't configured because it requires at least one target")
return false
} }
targets.firstOrNull { !kind.availableFor(it) }?.let { target -> val wrongTarget = targets.firstOrNull { !kind.availableFor(it) }
logger.error("Native library '${name}' wasn't configured because ${kind.description} is not available for ${target.visibleName}") check(wrongTarget == null) {
return false "Native artifact '$artifactName' wasn't configured because ${kind.description} is not available for ${wrongTarget!!.visibleName}"
} }
return true
} }
override fun registerAssembleTask(project: Project, name: String) { override fun registerAssembleTask() {
val parentTask = project.registerTask<Task>(lowerCamelCaseName("assemble", name, "XCFramework")) { validate()
val parentTask = project.registerTask<Task>(taskName) {
it.group = "build" it.group = "build"
it.description = "Assemble all types of registered '$name' XCFramework" it.description = "Assemble all types of registered '$artifactName' XCFramework"
} }
project.tasks.named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).dependsOn(parentTask) project.tasks.named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).dependsOn(parentTask)
modes.forEach { buildType -> modes.forEach { buildType ->
val holder = XCFrameworkTaskHolder.create(project, name, buildType).also { val holder = XCFrameworkTaskHolder.create(project, artifactName, buildType).also {
parentTask.dependsOn(it.task) parentTask.dependsOn(it.task)
} }
val nameSuffix = "ForXCF" val nameSuffix = "ForXCF"
val frameworkDescriptors: List<FrameworkDescriptor> = targets.map { target -> val frameworkDescriptors: List<FrameworkDescriptor> = targets.map { target ->
val librariesConfigurationName = project.registerLibsDependencies(target, name + nameSuffix, modules) val librariesConfigurationName = project.registerLibsDependencies(target, artifactName + nameSuffix, modules)
val exportConfigurationName = project.registerExportDependencies(target, name + nameSuffix, modules) val exportConfigurationName = project.registerExportDependencies(target, artifactName + nameSuffix, modules)
val targetTask = registerLinkFrameworkTask( val targetTask = registerLinkFrameworkTask(
project, project,
name, artifactName,
target, target,
buildType, buildType,
librariesConfigurationName, librariesConfigurationName,
exportConfigurationName, exportConfigurationName,
embedBitcode, embedBitcode,
"${name}XCFrameworkTemp", "${artifactName}XCFrameworkTemp",
nameSuffix nameSuffix
) )
holder.task.dependsOn(targetTask) holder.task.dependsOn(targetTask)
@@ -76,7 +79,7 @@ open class KotlinNativeXCFramework : KotlinNativeArtifact() {
val group = AppleTarget.values().firstOrNull { it.targets.contains(target) } val group = AppleTarget.values().firstOrNull { it.targets.contains(target) }
holder.fatTasks[group]?.configure { fatTask -> holder.fatTasks[group]?.configure { fatTask ->
fatTask.baseName = name fatTask.baseName = artifactName
fatTask.fromFrameworkDescriptors(listOf(descriptor)) fatTask.fromFrameworkDescriptors(listOf(descriptor))
fatTask.dependsOn(targetTask) fatTask.dependsOn(targetTask)
} }