From bc55eccf28053c3fa35974be1c179ec5e236d5ca Mon Sep 17 00:00:00 2001 From: Pavel Punegov Date: Tue, 27 Feb 2024 16:05:12 +0100 Subject: [PATCH] [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. --- .../build.gradle.kts | 127 +++++++++++------- 1 file changed, 79 insertions(+), 48 deletions(-) diff --git a/native/kotlin-test-native-xctest/build.gradle.kts b/native/kotlin-test-native-xctest/build.gradle.kts index a48c8e85cc3..52358c2b3df 100644 --- a/native/kotlin-test-native-xctest/build.gradle.kts +++ b/native/kotlin-test-native-xctest/build.gradle.kts @@ -12,58 +12,89 @@ plugins { kotlin("multiplatform") } -/** - * 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. - */ -fun targetPlatform(target: String): String { - val out = ByteArrayOutputStream() - val result = project.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() -} +// region XCTest framework location /** - * Returns a path to the Xcode developer frameworks location based on the specified KonanTarget. + * Value source for the Xcode developer framework path location. */ -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") +abstract class DevFrameworkPathValueSource : ValueSource { + @get:Inject + abstract val execOperations: ExecOperations + + interface Parameters : ValueSourceParameters { + val konanTarget: Property } - return targetPlatform(platform) + "/Developer/Library/Frameworks/" -} + override fun obtain(): String { + val devFrameworkPath = parameters.konanTarget.get().developerFrameworkPath() -/** - * 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 = - tasks.register("${target}FrameworkCopy") { - val devFrameworkPath = Paths.get(target.developerFrameworkPath()) - check(devFrameworkPath.toFile().exists()) { + check(File(devFrameworkPath).exists()) { "Developer frameworks path wasn't found at $devFrameworkPath. Check configuration and Xcode installation" } - from(devFrameworkPath.resolve("XCTest.framework")) + 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 = + tasks.register("${target}FrameworkCopy") { + from( + providers.of(DevFrameworkPathValueSource::class) { + parameters { + konanTarget = target + } + }.map { + Paths.get(it).resolve("XCTest.framework") + } + ) into(layout.buildDirectory.dir("$target/Frameworks/XCTest.framework")) } +// endregion + +// region Kotlin Multiplatform build configuration + val nativeTargets = mutableListOf() if (HostManager.hostIsMac) { @@ -80,13 +111,7 @@ if (HostManager.hostIsMac) { it.compilations.all { cinterops { register("XCTest") { - compilerOpts( - "-iframework", project.layout.buildDirectory - .dir("$konanTarget/Frameworks") - .get() - .asFile - .absolutePath - ) + compilerOpts("-iframework", copyTask.map { it.destinationDir }.get().absolutePath) // cinterop task should depend on the framework copy task tasks.named(interopProcessingTaskName).configure { dependsOn(copyTask) @@ -107,6 +132,10 @@ if (HostManager.hostIsMac) { } } +// endregion + +// region Artifact collection for consumers + val kotlinTestNativeXCTest by configurations.creating { attributes { attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_API)) @@ -143,4 +172,6 @@ nativeTargets.forEach { target -> builtBy(frameworkCopyTask) } } -} \ No newline at end of file +} + +// endregion \ No newline at end of file