[K/N] Make XCTest runner project configuration cache friendly

Use ValueSource with ExecOperations to obtain developer framework
location where the XCTest.framework is located.
This is necessary for Gradle configuration cache.
This commit is contained in:
Pavel Punegov
2024-02-27 16:05:12 +01:00
committed by Space Team
parent 808d4353e5
commit bc55eccf28
@@ -12,6 +12,29 @@ plugins {
kotlin("multiplatform") 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. * Path to the target SDK platform.
* *
@@ -19,9 +42,9 @@ plugins {
* It's required to get the Library frameworks path where the `XCTest.framework` is located. * It's required to get the Library frameworks path where the `XCTest.framework` is located.
* Consider making XCTest a platform lib with KT-61709. * Consider making XCTest a platform lib with KT-61709.
*/ */
fun targetPlatform(target: String): String { private fun targetPlatform(target: String): String {
val out = ByteArrayOutputStream() val out = ByteArrayOutputStream()
val result = project.exec { val result = execOperations.exec {
executable = "/usr/bin/xcrun" executable = "/usr/bin/xcrun"
args = listOf("--sdk", target, "--show-sdk-platform-path") args = listOf("--sdk", target, "--show-sdk-platform-path")
standardOutput = out standardOutput = out
@@ -34,9 +57,9 @@ fun targetPlatform(target: String): String {
} }
/** /**
* Returns a path to the Xcode developer frameworks location based on the specified KonanTarget. * Returns a path to the Xcode developer frameworks location based on the specified [KonanTarget].
*/ */
fun KonanTarget.developerFrameworkPath(): String { private fun KonanTarget.developerFrameworkPath(): String {
val platform = when (this) { val platform = when (this) {
KonanTarget.MACOS_ARM64, KonanTarget.MACOS_X64 -> "macosx" KonanTarget.MACOS_ARM64, KonanTarget.MACOS_X64 -> "macosx"
KonanTarget.IOS_SIMULATOR_ARM64, KonanTarget.IOS_X64 -> "iphonesimulator" KonanTarget.IOS_SIMULATOR_ARM64, KonanTarget.IOS_X64 -> "iphonesimulator"
@@ -46,24 +69,32 @@ fun KonanTarget.developerFrameworkPath(): String {
return targetPlatform(platform) + "/Developer/Library/Frameworks/" return targetPlatform(platform) + "/Developer/Library/Frameworks/"
} }
}
/** /**
* Registers a task to copy the XCTest framework to the build directory for the specified KonanTarget. * 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. * @param target The [KonanTarget] for which the copy framework task should be registered.
* @return The TaskProvider representing the registered copy framework task. * @return The [TaskProvider] representing the registered copy framework task.
*/ */
fun registerCopyFrameworkTask(target: KonanTarget): TaskProvider<Sync> = fun registerCopyFrameworkTask(target: KonanTarget): TaskProvider<Sync> =
tasks.register<Sync>("${target}FrameworkCopy") { tasks.register<Sync>("${target}FrameworkCopy") {
val devFrameworkPath = Paths.get(target.developerFrameworkPath()) from(
check(devFrameworkPath.toFile().exists()) { providers.of(DevFrameworkPathValueSource::class) {
"Developer frameworks path wasn't found at $devFrameworkPath. Check configuration and Xcode installation" parameters {
konanTarget = target
} }
}.map {
from(devFrameworkPath.resolve("XCTest.framework")) Paths.get(it).resolve("XCTest.framework")
}
)
into(layout.buildDirectory.dir("$target/Frameworks/XCTest.framework")) into(layout.buildDirectory.dir("$target/Frameworks/XCTest.framework"))
} }
// endregion
// region Kotlin Multiplatform build configuration
val nativeTargets = mutableListOf<KotlinNativeTarget>() val nativeTargets = mutableListOf<KotlinNativeTarget>()
if (HostManager.hostIsMac) { if (HostManager.hostIsMac) {
@@ -80,13 +111,7 @@ if (HostManager.hostIsMac) {
it.compilations.all { it.compilations.all {
cinterops { cinterops {
register("XCTest") { register("XCTest") {
compilerOpts( compilerOpts("-iframework", copyTask.map { it.destinationDir }.get().absolutePath)
"-iframework", project.layout.buildDirectory
.dir("$konanTarget/Frameworks")
.get()
.asFile
.absolutePath
)
// cinterop task should depend on the framework copy task // cinterop task should depend on the framework copy task
tasks.named(interopProcessingTaskName).configure { tasks.named(interopProcessingTaskName).configure {
dependsOn(copyTask) dependsOn(copyTask)
@@ -107,6 +132,10 @@ if (HostManager.hostIsMac) {
} }
} }
// endregion
// region Artifact collection for consumers
val kotlinTestNativeXCTest by configurations.creating { val kotlinTestNativeXCTest by configurations.creating {
attributes { attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_API)) attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_API))
@@ -144,3 +173,5 @@ nativeTargets.forEach { target ->
} }
} }
} }
// endregion