From c821c8fe6e67020641c2ff778281acad0166391f Mon Sep 17 00:00:00 2001 From: Alexander Shabalin Date: Thu, 25 Jan 2024 13:42:00 +0100 Subject: [PATCH] [K/N] Add runProcess convenience function to executors --- .../kotlin/native/executors/RunProcess.kt | 121 ++++++++++++++++++ .../executors/XcodeSimulatorExecutor.kt | 16 +-- 2 files changed, 124 insertions(+), 13 deletions(-) create mode 100644 native/executors/src/main/kotlin/org/jetbrains/kotlin/native/executors/RunProcess.kt diff --git a/native/executors/src/main/kotlin/org/jetbrains/kotlin/native/executors/RunProcess.kt b/native/executors/src/main/kotlin/org/jetbrains/kotlin/native/executors/RunProcess.kt new file mode 100644 index 00000000000..daba68db7f8 --- /dev/null +++ b/native/executors/src/main/kotlin/org/jetbrains/kotlin/native/executors/RunProcess.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2010-2024 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 java.io.ByteArrayOutputStream +import java.io.File +import kotlin.time.Duration + +data class RunProcessResult( + /** + * Execution time of the process. Can be a bit bigger than [ExecuteRequest.timeout]. + */ + val executionTime: Duration, + /** + * Captured standard output of the process. + */ + val stdout: String, + /** + * Captured standard error of the process. + */ + val stderr: String, +) { + /** + * Full output of the process: [stdout] followed by [stderr]. + */ + val output: String + get() = stdout + stderr +} + +class RunProcessException( + message: String, + private val result: RunProcessResult, + /** + * Process exit code if it exited by itself, or `null` if it was killed by a timeout. + */ + val exitCode: Int?, +) : IllegalStateException( + """ + |$message + |stdout: ${result.stdout} + |stderr: ${result.stderr} + """.trimMargin() +) { + /** + * Execution time of the process. Can be a bit bigger than [ExecuteRequest.timeout]. + */ + val executionTime by result::executionTime + + /** + * Captured standard output of the process. + */ + val stdout by result::stdout + + /** + * Captured standard error of the process. + */ + val stderr by result::stderr + + /** + * Full output of the process: [stdout] followed by [stderr]. + */ + val output by result::output +} + +/** + * Run [executableAbsolutePath] with [Executor] using current process' working directory with [args] and capture the full output. + * + * A simplified version of [Executor.execute]. + * + * @param executableAbsolutePath Path to the executable + * @param args Command line args + * @param block optional block to additionally customize [ExecuteRequest] + * + * @throws RunProcessException if the process has failed or timed out. + */ +inline fun Executor.runProcess( + executableAbsolutePath: String, + vararg args: String, + block: ExecuteRequest.() -> Unit = {}, +): RunProcessResult { + ByteArrayOutputStream().use { stdout -> + ByteArrayOutputStream().use { stderr -> + val request = ExecuteRequest(executableAbsolutePath).apply { + this.args.addAll(args) + this.stdout = stdout + this.stderr = stderr + this.workingDirectory = File("").absoluteFile + this.block() + } + val response = this.execute(request) + val result = RunProcessResult( + executionTime = response.executionTime, + stdout = stdout.toString("UTF-8").trim(), + stderr = stderr.toString("UTF-8").trim(), + ) + try { + response.assertSuccess() + } catch (e: IllegalStateException) { + throw RunProcessException(e.message!!, result, response.exitCode) + } + return result + } + } +} + +/** + * Run [executableAbsolutePath] on host using current process' working directory with [args] and capture the full output. + * + * A simplified version of [HostExecutor.execute]. + * + * @param executableAbsolutePath Path to the executable + * @param args Command line args + * @param block optional block to additionally customize [ExecuteRequest] + * + * @throws RunProcessException if the process has failed or timed out. + */ +inline fun runProcess(executableAbsolutePath: String, vararg args: String, block: ExecuteRequest.() -> Unit = {}) = + HostExecutor().runProcess(executableAbsolutePath, *args, block = block) \ No newline at end of file diff --git a/native/executors/src/main/kotlin/org/jetbrains/kotlin/native/executors/XcodeSimulatorExecutor.kt b/native/executors/src/main/kotlin/org/jetbrains/kotlin/native/executors/XcodeSimulatorExecutor.kt index 7247b339131..7d25c6c5eed 100644 --- a/native/executors/src/main/kotlin/org/jetbrains/kotlin/native/executors/XcodeSimulatorExecutor.kt +++ b/native/executors/src/main/kotlin/org/jetbrains/kotlin/native/executors/XcodeSimulatorExecutor.kt @@ -18,15 +18,6 @@ private fun defaultDeviceId(target: KonanTarget) = when (target.family) { else -> error("Unexpected simulation target: $target") } -private fun Executor.run(executableAbsolutePath: String, vararg args: String) = ByteArrayOutputStream().let { - this.execute(ExecuteRequest(executableAbsolutePath).apply { - this.args.addAll(args) - stdout = it - workingDirectory = File("").absoluteFile - }).assertSuccess() - it -} - /** * [Executor] that runs the process in an Xcode simulator. * @@ -67,8 +58,7 @@ class XcodeSimulatorExecutor( }.toTypedArray() private fun simctl(vararg args: String): String { - val out = hostExecutor.run("/usr/bin/xcrun", *arrayOf("simctl", *args)) - return out.toString("UTF-8").trim() + return hostExecutor.runProcess("/usr/bin/xcrun", "simctl", *args).stdout } private var deviceChecked: SimulatorDeviceDescriptor? = null @@ -148,10 +138,10 @@ class XcodeSimulatorExecutor( } if (version.minor >= 1) { // Option -downloadPlatform NAME available only since 14.1 - hostExecutor.run("/usr/bin/xcrun", "xcodebuild", "-downloadPlatform", osName) + hostExecutor.runProcess("/usr/bin/xcrun", "xcodebuild", "-downloadPlatform", osName) } else { // Have to download all platforms :( - hostExecutor.run("/usr/bin/xcrun", "xcodebuild", "-downloadAllPlatforms") + hostExecutor.runProcess("/usr/bin/xcrun", "xcodebuild", "-downloadAllPlatforms") } }