Code Monkey home page Code Monkey logo

kommand's Introduction

Kommand Publish Kommand Test Maven Central

ko-fi

logo

Kommand

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

v2.0.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.0, 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
  • Kotlin Multiplatform 1.9.21

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)
    • cross (install with cargo install cross)
    • 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

kommand's People

Contributors

bppleman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

colinhebert

kommand's Issues

Pipe stdout of first command to a second command

Thanks for creating this project.

I have question, maybe it's out of scope. Is it possible to use the stdout of first command for a second command? Like when streaming data of video data and use it as input for ffmpeg to encode it.

So a command like this:

dvd_copy -o - | ffmpeg -i - -codec copy dvd_video.mkv

Unable to add as dependency?

Hello

Newbie in Kotlin/Native here, so please excuse my ignorance.

I've created a sample Kotlin/Native application using IntelliJ's template, and I tried to run a demo with your library. The build.,gradle.kts file is a follows:

build.,gradle.kts
plugins {
    kotlin("multiplatform") version "1.9.0"
}

group = "com.panayotis"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

kotlin {
    val hostOs = System.getProperty("os.name")
    val isArm64 = System.getProperty("os.arch") == "aarch64"
    val isMingwX64 = hostOs.startsWith("Windows")
    val nativeTarget = when {
        hostOs == "Mac OS X" && isArm64 -> macosArm64("native")
        hostOs == "Mac OS X" && !isArm64 -> macosX64("native")
        hostOs == "Linux" && isArm64 -> linuxArm64("native")
        hostOs == "Linux" && !isArm64 -> linuxX64("native")
        isMingwX64 -> mingwX64("native")
        else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
    }

    nativeTarget.apply {
        binaries {
            executable {
                entryPoint = "main"
            }
        }
    }
    sourceSets {
        val nativeMain by getting
        val nativeTest by getting
        commonMain {
            dependencies{

                implementation("com.kgit2:kommand:1.0.1")
            }
        }
    }

}

The actual code is very simple, just a "hello world" under src/nativeMain/kotlin/Main.kt

The problem is that, build system failed with error:

> Task :linkDebugExecutableNative FAILED
2 actionable tasks: 2 executed
e: Could not find 'main' in '<root>' package.

FAILURE: Build failed with an exception.

The full error log is here (the remaining lines)
* What went wrong:
Execution failed for task ':linkDebugExecutableNative'.
> Compilation finished with errors

* Try:
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':linkDebugExecutableNative'.
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:142)
	at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:282)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:140)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:128)
	at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:77)
	at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
	at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:57)
	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
	at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:69)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:322)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:309)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:302)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:288)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:462)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:379)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:49)
Caused by: org.jetbrains.kotlin.backend.konan.KonanCompilationException: Compilation finished with errors
	at org.jetbrains.kotlin.cli.bc.K2Native$Companion$mainNoExitWithRenderer$1.invoke(K2Native.kt:174)
	at org.jetbrains.kotlin.cli.bc.K2Native$Companion$mainNoExitWithRenderer$1.invoke(K2Native.kt:172)
	at org.jetbrains.kotlin.util.UtilKt.profileIf(Util.kt:22)
	at org.jetbrains.kotlin.util.UtilKt.profile(Util.kt:16)
	at org.jetbrains.kotlin.cli.bc.K2Native$Companion.mainNoExitWithRenderer(K2Native.kt:172)
	at org.jetbrains.kotlin.cli.bc.K2NativeKt.mainNoExitWithGradleRenderer(K2Native.kt:190)
	at org.jetbrains.kotlin.cli.utilities.MainKt$daemonMain$1.invoke(main.kt:51)
	at org.jetbrains.kotlin.cli.utilities.MainKt$daemonMain$1.invoke(main.kt:51)
	at org.jetbrains.kotlin.cli.utilities.MainKt.mainImpl(main.kt:20)
	at org.jetbrains.kotlin.cli.utilities.MainKt.inProcessMain(main.kt:58)
	at org.jetbrains.kotlin.cli.utilities.MainKt.daemonMain(main.kt:51)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at org.jetbrains.kotlin.compilerRunner.KotlinToolRunner.runInProcess(KotlinToolRunner.kt:189)
	at org.jetbrains.kotlin.compilerRunner.KotlinToolRunner.run(KotlinToolRunner.kt:132)
	at org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink.compile(KotlinNativeLink.kt:364)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:125)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:58)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:51)
	at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:29)
	at org.gradle.api.internal.tasks.execution.TaskExecution$3.run(TaskExecution.java:236)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
	at org.gradle.api.internal.tasks.execution.TaskExecution.executeAction(TaskExecution.java:221)
	at org.gradle.api.internal.tasks.execution.TaskExecution.executeActions(TaskExecution.java:204)
	at org.gradle.api.internal.tasks.execution.TaskExecution.executeWithPreviousOutputFiles(TaskExecution.java:187)
	at org.gradle.api.internal.tasks.execution.TaskExecution.execute(TaskExecution.java:165)
	at org.gradle.internal.execution.steps.ExecuteStep.executeInternal(ExecuteStep.java:89)
	at org.gradle.internal.execution.steps.ExecuteStep.access$000(ExecuteStep.java:40)
	at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:53)
	at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:50)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
	at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:50)
	at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:40)
	at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:68)
	at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:38)
	at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:41)
	at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:74)
	at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:55)
	at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:51)
	at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:29)
	at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.executeDelegateBroadcastingChanges(CaptureStateAfterExecutionStep.java:124)
	at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:80)
	at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:58)
	at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:48)
	at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:36)
	at org.gradle.internal.execution.steps.BuildCacheStep.executeWithoutCache(BuildCacheStep.java:181)
	at org.gradle.internal.execution.steps.BuildCacheStep.lambda$execute$1(BuildCacheStep.java:71)
	at org.gradle.internal.Either$Right.fold(Either.java:175)
	at org.gradle.internal.execution.caching.CachingState.fold(CachingState.java:59)
	at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:69)
	at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:47)
	at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:36)
	at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:25)
	at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:36)
	at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:22)
	at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:110)
	at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$2(SkipUpToDateStep.java:56)
	at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:56)
	at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:38)
	at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:73)
	at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:44)
	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:37)
	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:27)
	at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:89)
	at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:50)
	at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:102)
	at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:57)
	at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:76)
	at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:50)
	at org.gradle.internal.execution.steps.SkipEmptyWorkStep.executeWithNoEmptySources(SkipEmptyWorkStep.java:254)
	at org.gradle.internal.execution.steps.SkipEmptyWorkStep.executeWithNoEmptySources(SkipEmptyWorkStep.java:209)
	at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:88)
	at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:56)
	at org.gradle.internal.execution.steps.RemoveUntrackedExecutionStateStep.execute(RemoveUntrackedExecutionStateStep.java:32)
	at org.gradle.internal.execution.steps.RemoveUntrackedExecutionStateStep.execute(RemoveUntrackedExecutionStateStep.java:21)
	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
	at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:43)
	at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:31)
	at org.gradle.internal.execution.steps.AssignWorkspaceStep.lambda$execute$0(AssignWorkspaceStep.java:40)
	at org.gradle.api.internal.tasks.execution.TaskExecution$4.withWorkspace(TaskExecution.java:281)
	at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:40)
	at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:30)
	at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:37)
	at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:27)
	at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:44)
	at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:33)
	at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:76)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:139)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:128)
	at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:77)
	at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
	at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:57)
	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
	at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:69)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:322)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:309)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:302)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:288)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:462)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:379)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:49)

The strange thing is, if I add another (random) dependency, like implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.5") I don't have any problems,

Any idea why this is happening?

mingwX64 not wrking

this work well in mac os but not in windows

fun main(args: Array) {
Command(command)
.arg(arg)
.stdout(Stdio.Pipe)
.stderr(Stdio.Pipe)
.spawn()

    val stdoutReader: com.kgit2.io.Reader? = child.getChildStdout()
    val lines: Sequence<String>? = stdoutReader?.lines()

    val stderrReader: com.kgit2.io.Reader? = child.getChildStdout()
    val err: Sequence<String>? = stderrReader?.lines()

    lines?.forEach {
        println(it)
    }

    err?.forEach {
        println(it)
    }

}

Captura de pantalla 2023-05-08 114302

Q: LICENSE

Currently, there is no LICENSE for this repository. Would love to utilize it.

Looks as if it went from MIT -> APACHE -> nothing

Any plans to license the code?

Add support for varargs command arguments

Given the nature of command arguments, it would be great if the API supported varargs.

Currently I'm supplementing the API with:

fun Command.args(vararg args: String) = args(args.toList())

Ideally rather than doing this:

Command("ping") 
     .args(listOf("-c", "5", "localhost")) 

One should be able to do this:

Command("ping") 
     .args("-c", "5", "localhost")

The advantage of varargs is that it allows to also do fancy things with spread, such as:

Command("ping") 
     .args(getPingCount()*, "localhost")

fun getPingCount(): Array<String> =
    if(config.pingCount >= 1) arrayOf("-c", config.pingCount.toString()) else emptyArray()

FR: Support LInuxArm(64) target?

Hello.
Is there any reason why linux/arm is not supported? In theory the native API should be the same and no difference should be present.

I tried to hack the build script myself, but my still inexperience with kotlin/native, plus the added complexity of publishing options made the project uncompilable on my system.

Ktor IO dependency should be an `api` dependency

As far as I see, ktor-io should be an api dependency rather than implementation, as it is present in library's public API, e.g. in Writer or Reader classes. So, if anybody wants to just use the library without knowing anything about ktor and stuff, they'll be in a bit of trouble, as they'll end up with, plainly speaking, broken compilation classpath.

Another way to go would be to fully encapsulate IO API, leaving ktor an implementation detail indeed. This way is preferable, imo, though requires more work.

FR: Add timeout option

It'd be nice if I could add a timeout to a command that if it didn't finish by a certain time, the child process was killed.

Piped I/O example leaves process as zombie

Let's use your example, plus some code to keep the original task alive:

    val child = Command("ping")
        .args("-c", "5", "localhost")
        .stdout(Stdio.Pipe)
        .spawn()
    val stdoutReader: com.kgit2.io.Reader? = child.getChildStdout()
    val lines: Sequence<String>? = stdoutReader?.lines()
    lines?.forEach {
        println(it)
    }
    while (true) {
        sleep(1.toUInt())
        println("TICK")
    }

If you run this code and check for the ping process it is still in the zombie state. This is expected ( see here and here ).

Of course, in the demo code I could solve this by adding a "wait" command just after the lines.foreach loop.
But let's discuss the possibilities, since there is an issue.

The elegant way of doing this is to add this wait command inside the lines wrapper, so when the function returns, wait will have been called already, and no zombie process.
BUT if the app closed the stream before exiting, then the loop will exit , the wait will be called and the application will stay there forever, until the child application exits. The problem here, although it seems natural, is that the user never asked to "wait", and he probably knows that the child process has closed already the file handlers.
On the other hand, leaving this unhandled, leads to zombie processes which is even worse.

There is also the solution, this "wait" command to be added to the demo code you present. But then, it doesn't make any sense why it should be there, except if it is properly documented.

What are your thoughts?

lines() doesn't properly handle stream

I've found that, if the stream contains Unicode characters, the line isn't properly working.
I believe this is a kommand bug, since I created a ktor-only example that seems to work as expected.

To demonstrate the problem, let's use this data and save this in a file named "test"

PREV — DATA
NEXT

Note: the dash is not a usual dash, it's the Unicode sequence 0xE28094 .

Let's read it:

    val cmd = Command("cat").args("test").stdout(Stdio.Pipe).spawn()
    cmd.getChildStdout()?.lines()?.forEach { println("### $it") }

This unfortunately types ### PREV — DATNEXT
It looks like it gets confused and there's an offset by 2 characters, which is actually the difference of the Unicode character (since it's the only 3-byte character as opposed to the other characters who are 1-byte).

To be on the safe side, I created a test for ktor.

class TestInput(val data: ByteArray) : Input() {

    override fun closeSource() {}

    private var pointer: Int = 0
    override fun fill(destination: Memory, offset: Int, length: Int): Int {
        val hm = min(data.size - pointer, length)
        for (i in 0..<hm)
            destination[offset + i] = data[pointer++]
        return hm
    }
}

fun main() {
    val t = TestInput("PREV — DATA\nNEXT".toByteArray())
    while (!t.endOfInput)
        println("*** ${t.readUTF8Line()}")
}

which (fortunately) it gives

*** PREV — DATA
*** NEXT

so, it seems like ktor properly handle the stream.

Is this a kommand bug? Any idea why this is happening?

Remove Main.kt files

Libraries should not include entrypoints like main files, that should be left to the user to implement.

Weird currentReadLine usage in `lines` method of `Reader`

Here are readLine and lines methods of Reader class:

fun readLine(): String? {
    this.currentReadLine = true
    val result = readUTF8Line()
    this.currentReadLine = false
    return result
}

fun lines(): Sequence<String> {
    return sequence {
        this@Reader.currentReadLine = true
        while (endOfInput.not()) {
            readLine()?.let { yield(it) }
        }
    }
}

If I got it right, currentReadLine works like mutex - it should be set to true if line is being read. In this case, currentReadLine should be true while lines is being executed, but it is true only while readLine is executed and until the first readLine execution is finished.

Sorry for bothering if I misunderstood something🙂

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.