/kommand

Kotlin Native library for run child process or external command.

Primary LanguageRustApache License 2.0Apache-2.0

Kommand Publish Kommand Test Maven Central

ko-fi

logo

Kommand

Kotlin Native library for create sub-process and redirect their I/O.

v2.2.0

Rust is an excellent language that takes into account both performance and engineering.

In version 1.x, we use the following API to provide the function of creating child processes

  • fork of [POSIX api]
  • CreateChildProcess of [win32 api]
  • java.lang.ProcessBuilder of JVM

In version 2.x, we use the Rust standard library to provide the function of creating child processes.

  • std::process::Command of Rust
  • java.lang.ProcessBuilder of JVM

It will bring

  • More unified API
  • Easier to use API
  • Performance is still excellent
  • Easier to maintain
  • Code structure is clearer

Supported Platforms

  • x86_64-apple-darwin
  • aarch64-apple-darwin
  • x86_64-unknown-linux-gnu
  • aarch64-unknown-linux-gnu
  • x86_64-pc-windows-gnu (mingw-w64)
  • jvm

Dependent

  • Rust Standard Library 1.69.0 (No support for later versions)
  • Kotlin Multiplatform 2.0.10

Usage

Dependency

build.gradle.kts:

// ……
repositories {
    mavenCentral()
}
// ……

dependencies {
    // should replace with the latest version
    implementation("com.kgit2:kommand:2.x")
}

Quick Start

Inherit Standard I/O

package com.kgit2.kommand
import com.kgit2.kommand.process.Command
import com.kgit2.kommand.process.Stdio
fun main() {
Command("ping")
.args(listOf("-c", "5", "localhost"))
.stdout(Stdio.Inherit)
.spawn()
.wait()
}

Piped I/O

package com.kgit2.kommand
import com.kgit2.kommand.process.Command
import com.kgit2.kommand.process.Stdio
fun main() {
val child = Command("ping")
.args(listOf("-c", "5", "localhost"))
.stdout(Stdio.Pipe)
.spawn()
child.bufferedStdout()?.lines()?.forEach { line ->
println(line)
}
child.wait()
}

Null I/O

package com.kgit2.kommand
import com.kgit2.kommand.process.Command
import com.kgit2.kommand.process.Stdio
fun main() {
Command("echo")
.arg("nothing")
.stdout(Stdio.Null)
.spawn()
.wait()
}

Timeout Detection

package com.kgit2.kommand
import com.kgit2.kommand.process.Command
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import kotlinx.datetime.Clock
fun main() = runBlocking(Dispatchers.Default) {
// Sleep with regular
val start = Clock.System.now()
val status = Command("sleep").arg("5").status()
println("status: $status elapsed: ${Clock.System.now() - start}")
// Sleep with timeout detection and timeout determination
val start2 = Clock.System.now()
val child = Command("sleep").arg("5").spawn()
val childJob = async(Dispatchers.IO) {
runCatching {
child.wait()
}.onFailure {
println("child result: $it")
}.getOrNull()
}
runCatching {
withTimeout(3000) {
childJob.await()
}
}.onSuccess {
println("status: $it elapsed: ${Clock.System.now() - start2}")
}.onFailure {
child.kill()
println("status: $it elapsed: ${Clock.System.now() - start2}")
}
// Sleep with timeout detection and determination that it will not timeout
val start3 = Clock.System.now()
val child2 = Command("sleep").arg("2").spawn()
val childJob2 = async(Dispatchers.IO) {
runCatching {
child2.wait()
}.onFailure {
println("child result: $it")
}.getOrNull()
}
runCatching {
withTimeout(3000) {
childJob2.await()
}
}.onSuccess {
println("status: $it elapsed: ${Clock.System.now() - start3}")
}.onFailure {
child2.kill()
println("status: $it elapsed: ${Clock.System.now() - start3}")
}
Unit
}

Full example check kommand-examples/timeout.

Dependency:

Build by yourself

1. Dependencies

  • rust toolchain - <= 1.69.0 (https://rustup.rs) (recommend)
    • just (install with cargo install just)
  • cross-compile toolchain
    • x86_64-apple-darwin
    • aarch64-apple-darwin
    • x86_64-unknown-linux-gnu
    • aarch64-unknown-linux-gnu
    • x86_64-pc-windows-gnu (mingw-w64)
  • docker (optional)

Recommend build all platforms in macOS.

Kotlin Multiplatform gets the most complete support on macOS.

If you are using macOS, you can install the cross-compile toolchain with

just prepare

Otherwise, you need to install the cross-compile toolchain yourself.

2. Clone this repo

git clone https://github.com/kgit2/kommand.git

3. Build kommand-core

cd kommand-core
just all

4. Build kommand

./gradlew build

5. cross-platform test

Only linux support cross-platform test.

  • install docker

Install Docker Engine

  • test it
# for x86_64
just linuxX64Test
# for aarch64
just linuxArm64Test

Maintainers

@BppleMan.

@XJMiada.(Original Picture)

License

Apache2.0 © BppleMan

Credits

  • JetBrains Logo (Main) logo

Star History

Star History Chart