Files
kotlin-fork/native/kotlin-test-native-xctest/build.gradle.kts
T
Pavel Punegov 84e1ab1663 [K/N] XCTest support build script fix
Fix XCTest destination in the cinterop setup.
`-iframework` should point to the directory containing the framework.


Co-authored-by: Cristian Garcia <Cristian.Garcia-Marin@jetbrains.com>

Merge-request: KT-MR-14804
Merged-by: Pavel Punegov <Pavel.Punegov@jetbrains.com>
2024-03-12 13:24:30 +00:00

177 lines
6.0 KiB
Kotlin

import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
import org.jetbrains.kotlin.konan.target.*
import java.io.ByteArrayOutputStream
import java.nio.file.Paths
description = "XCTest wrapper of Native kotlin.test"
plugins {
kotlin("multiplatform")
}
// region XCTest framework location
/**
* Value source for the Xcode developer framework path location.
*/
abstract class DevFrameworkPathValueSource : ValueSource<String, DevFrameworkPathValueSource.Parameters> {
@get:Inject
abstract val execOperations: ExecOperations
interface Parameters : ValueSourceParameters {
val konanTarget: Property<KonanTarget>
}
override fun obtain(): String {
val devFrameworkPath = parameters.konanTarget.get().developerFrameworkPath()
check(File(devFrameworkPath).exists()) {
"Developer frameworks path wasn't found at $devFrameworkPath. Check configuration and Xcode installation"
}
return parameters.konanTarget.get().developerFrameworkPath()
}
/**
* Path to the target SDK platform.
*
* By default, K/N includes only SDK frameworks as platform libs.
* It's required to get the Library frameworks path where the `XCTest.framework` is located.
* Consider making XCTest a platform lib with KT-61709.
*/
private fun targetPlatform(target: String): String {
val out = ByteArrayOutputStream()
val result = execOperations.exec {
executable = "/usr/bin/xcrun"
args = listOf("--sdk", target, "--show-sdk-platform-path")
standardOutput = out
}
check(result.exitValue == 0) {
"xcrun failed with ${result.exitValue}. See the output: $out"
}
return out.toString().trim()
}
/**
* Returns a path to the Xcode developer frameworks location based on the specified [KonanTarget].
*/
private fun KonanTarget.developerFrameworkPath(): String {
val platform = when (this) {
KonanTarget.MACOS_ARM64, KonanTarget.MACOS_X64 -> "macosx"
KonanTarget.IOS_SIMULATOR_ARM64, KonanTarget.IOS_X64 -> "iphonesimulator"
KonanTarget.IOS_ARM64 -> "iphoneos"
else -> error("Target $this is not supported here")
}
return targetPlatform(platform) + "/Developer/Library/Frameworks/"
}
}
/**
* Registers a task to copy the XCTest framework to the build directory for the specified [KonanTarget].
*
* @param target The [KonanTarget] for which the copy framework task should be registered.
* @return The [TaskProvider] representing the registered copy framework task.
*/
fun registerCopyFrameworkTask(target: KonanTarget): TaskProvider<Sync> =
tasks.register<Sync>("${target}FrameworkCopy") {
into(layout.buildDirectory.dir("$target/Frameworks"))
from(
providers.of(DevFrameworkPathValueSource::class) {
parameters {
konanTarget = target
}
}
) {
include("XCTest.framework/**")
}
}
// endregion
// region Kotlin Multiplatform build configuration
val nativeTargets = mutableListOf<KotlinNativeTarget>()
if (HostManager.hostIsMac) {
kotlin {
with(nativeTargets) {
add(macosX64())
add(macosArm64())
add(iosX64())
add(iosArm64())
add(iosSimulatorArm64())
forEach {
val copyTask = registerCopyFrameworkTask(it.konanTarget)
it.compilations.all {
cinterops {
register("XCTest") {
compilerOpts("-iframework", copyTask.map { it.destinationDir }.get().absolutePath)
// cinterop task should depend on the framework copy task
tasks.named(interopProcessingTaskName).configure {
dependsOn(copyTask)
}
}
}
}
}
}
sourceSets.all {
languageSettings.apply {
// Oh, yeah! So much experimental, so wow!
optIn("kotlinx.cinterop.BetaInteropApi")
optIn("kotlinx.cinterop.ExperimentalForeignApi")
optIn("kotlin.experimental.ExperimentalNativeApi")
}
}
}
}
// endregion
// region Artifact collection for consumers
val kotlinTestNativeXCTest by configurations.creating {
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_API))
attribute(KotlinPlatformType.attribute, KotlinPlatformType.native)
}
}
nativeTargets.forEach { target ->
val targetName = target.konanTarget.name
val mainCompilation = target.compilations.getByName("main")
val outputKlibTask = mainCompilation.compileTaskProvider
@Suppress("UNCHECKED_CAST")
val cinteropKlibTask = tasks.named(
mainCompilation.cinterops
.getByName("XCTest")
.interopProcessingTaskName
) as? TaskProvider<CInteropProcess> ?: error("Unable to get CInteropProcess task provider")
val frameworkCopyTask = tasks.named<Sync>("${targetName}FrameworkCopy")
artifacts {
add(kotlinTestNativeXCTest.name, outputKlibTask.flatMap { it.outputFile }) {
classifier = targetName
builtBy(outputKlibTask)
}
add(kotlinTestNativeXCTest.name, cinteropKlibTask.flatMap { it.outputFileProvider }) {
classifier = targetName
builtBy(cinteropKlibTask)
}
// Add a path to a directory that contains copied framework to share it with test infrastructure
add(kotlinTestNativeXCTest.name, frameworkCopyTask.map { it.destinationDir }) {
classifier = "${targetName}Frameworks"
builtBy(frameworkCopyTask)
}
}
}
// endregion