[K/N] Add RosettaExecutor

Merge-request: KT-MR-9986
Merged-by: Alexander Shabalin <Alexander.Shabalin@jetbrains.com>
This commit is contained in:
Alexander Shabalin
2023-05-06 13:44:25 +00:00
committed by Space Team
parent 917f64c893
commit 142e2b4b2a
6 changed files with 68 additions and 12 deletions
@@ -53,7 +53,7 @@ private fun Executor.service(project: Project) = object: ExecutorService {
val execSpec = project.objects.newInstance<DefaultExecSpec>().apply {
action.execute(this)
}
val request = executeRequest(
val request = ExecuteRequest(
executableAbsolutePath = execSpec.executable,
args = execSpec.args,
).apply {
@@ -105,6 +105,7 @@ fun create(project: Project): ExecutorService {
}
}.service(project)
supportsRunningTestsOnDevice(testTarget) -> deviceLauncher(project)
configurables is AppleConfigurables && RosettaExecutor.availableFor(configurables) -> RosettaExecutor(configurables).service(project)
else -> HostExecutor().service(project)
}
}
@@ -40,6 +40,7 @@ private abstract class RunGTestJob : WorkAction<RunGTestJob.Parameters> {
when {
target == hostTarget -> HostExecutor()
configurables is AppleConfigurables && configurables.targetTriple.isSimulator -> XcodeSimulatorExecutor(configurables)
configurables is AppleConfigurables && RosettaExecutor.availableFor(configurables) -> RosettaExecutor(configurables)
else -> error("Cannot run for target $target")
}
}
@@ -50,7 +51,7 @@ private abstract class RunGTestJob : WorkAction<RunGTestJob.Parameters> {
with(parameters) {
reportFileUnprocessed.asFile.get().parentFile.mkdirs()
executor.execute(executeRequest(this@with.executable.asFile.get().absolutePath).apply {
executor.execute(ExecuteRequest(this@with.executable.asFile.get().absolutePath).apply {
this.args.add("--gtest_output=xml:${reportFileUnprocessed.asFile.get().absolutePath}")
filter.orNull?.also {
this.args.add("--gtest_filter=${it}")
@@ -3,8 +3,6 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:OptIn(ExperimentalTime::class)
package org.jetbrains.kotlin.native.executors
import java.io.*
@@ -62,13 +60,6 @@ data class ExecuteRequest(
inline fun copying(block: ExecuteRequest.() -> Unit): ExecuteRequest = copy().apply(block)
}
/**
* Helper for [ExecuteRequest] creation.
* Allows to create it without opting in [kotlin.time.ExperimentalTime] for API < 1.6.
*/
fun executeRequest(executableAbsolutePath: String, args: MutableList<String> = mutableListOf()): ExecuteRequest =
ExecuteRequest(executableAbsolutePath, args)
data class ExecuteResponse(
/**
* Process exit code if it exited by itself, or `null` if it was killed by a timeout.
@@ -0,0 +1,61 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.native.executors
import org.jetbrains.kotlin.konan.target.AppleConfigurables
import org.jetbrains.kotlin.konan.target.Configurables
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
/**
* [Executor] that runs the process using Apple's Rosetta 2.
*
* @param configurables [Configurables] for target
*/
class RosettaExecutor(
private val configurables: AppleConfigurables,
) : Executor {
companion object {
/**
* Returns `true` if running via Rosetta 2 can be made available for given [configurables].
*
* This does not check that Rosetta 2 is installed.
*/
fun availableFor(configurables: AppleConfigurables): Boolean {
return HostManager.host is KonanTarget.MACOS_ARM64 && configurables.target is KonanTarget.MACOS_X64
}
/**
* Return `true` if Rosetta 2 is installed.
*
* @param [hostExecutor] executor in which to run the check. By default [HostExecutor].
*/
fun checkIsInstalled(hostExecutor: Executor = HostExecutor()): Boolean {
if (HostManager.host !is KonanTarget.MACOS_ARM64) {
return false
}
return hostExecutor.execute(ExecuteRequest("/usr/bin/arch").apply {
this.args.addAll(listOf("-x86_64", "/usr/bin/true"))
}).exitCode == 0
}
}
private val hostExecutor: Executor = HostExecutor()
private val target by configurables::target
init {
require(availableFor(configurables)) {
"$target cannot be run under Rosetta 2 from host ${HostManager.host}"
}
require(checkIsInstalled(hostExecutor)) {
"Rosetta 2 is not installed on host ${HostManager.host}"
}
}
// When Rosetta 2 is installed, all x64 binaries are automatically run through it.
override fun execute(request: ExecuteRequest): ExecuteResponse = hostExecutor.execute(request)
}
@@ -19,7 +19,7 @@ private fun defaultDeviceId(target: KonanTarget) = when (target.family) {
}
private fun Executor.run(executableAbsolutePath: String, vararg args: String) = ByteArrayOutputStream().let {
this.execute(executeRequest(executableAbsolutePath).apply {
this.execute(ExecuteRequest(executableAbsolutePath).apply {
this.args.addAll(args)
stdout = it
workingDirectory = File("").absoluteFile
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.native.executors.Executor
import org.jetbrains.kotlin.native.executors.EmulatorExecutor
import org.jetbrains.kotlin.native.executors.XcodeSimulatorExecutor
import org.jetbrains.kotlin.konan.target.*
import org.jetbrains.kotlin.native.executors.RosettaExecutor
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
import java.util.concurrent.ConcurrentHashMap
@@ -35,6 +36,7 @@ internal object TestRunners {
configurables is ConfigurablesWithEmulator -> EmulatorExecutor(configurables)
configurables is AppleConfigurables && configurables.targetTriple.isSimulator ->
XcodeSimulatorExecutor(configurables)
configurables is AppleConfigurables && RosettaExecutor.availableFor(configurables) -> RosettaExecutor(configurables)
else -> runningOnUnsupportedTarget()
}
)