1
0
Fork 0
mirror of https://github.com/ethauvin/kobalt.git synced 2025-04-26 16:28:12 -07:00

NewRunCommand.

This commit is contained in:
Cedric Beust 2016-02-12 01:29:40 +04:00
parent bf4b6144c3
commit 64bfcbf12a

View file

@ -0,0 +1,127 @@
package com.beust.kobalt.misc
import java.io.BufferedReader
import java.io.File
import java.io.InputStream
import java.io.InputStreamReader
import java.util.concurrent.TimeUnit
class RunCommandInfo {
lateinit var command: String
var args : List<String> = arrayListOf()
var directory : File = File("")
var env : Map<String, String> = hashMapOf()
/**
* Some commands fail but return 0, so the only way to find out if they failed is to look
* at the error stream. However, some commands succeed but output text on the error stream.
* This field is used to specify how errors are caught.
*/
var useErrorStreamAsErrorIndicator : Boolean = true
var useInputStreamAsErrorIndicator : Boolean = false
var errorCallback: Function1<List<String>, Unit> = NewRunCommand.DEFAULT_ERROR
var successCallback: Function1<List<String>, Unit> = NewRunCommand.DEFAULT_SUCCESS
var isSuccess: (Boolean, List<String>, List<String>) -> Boolean = {
isSuccess: Boolean,
input: List<String>,
error: List<String> ->
var hasErrors = ! isSuccess
if (useErrorStreamAsErrorIndicator && ! hasErrors) {
hasErrors = hasErrors || error.size > 0
}
if (useInputStreamAsErrorIndicator && ! hasErrors) {
hasErrors = hasErrors || input.size > 0
}
! hasErrors
}
}
fun runCommand(init: RunCommandInfo.() -> Unit) = NewRunCommand(RunCommandInfo().apply { init() }).invoke()
open class NewRunCommand(val info: RunCommandInfo) {
companion object {
val DEFAULT_SUCCESS = { output: List<String> -> }
// val DEFAULT_SUCCESS_VERBOSE = { output: List<String> -> log(2, "Success:\n " + output.joinToString("\n"))}
// val defaultSuccess = DEFAULT_SUCCESS
val DEFAULT_ERROR = {
output: List<String> ->
kotlin.error(output.joinToString("\n "))
}
}
// fun useErrorStreamAsErrorIndicator(f: Boolean) : RunCommand {
// useErrorStreamAsErrorIndicator = f
// return this
// }
fun invoke() : Int {
val allArgs = arrayListOf<String>()
allArgs.add(info.command)
allArgs.addAll(info.args)
val pb = ProcessBuilder(allArgs)
pb.directory(info.directory)
log(2, "Running command in directory ${info.directory.absolutePath}" +
"\n " + allArgs.joinToString(" ").replace("\\", "/"))
pb.environment().let { pbEnv ->
info.env.forEach {
pbEnv.put(it.key, it.value)
}
}
val process = pb.start()
// Run the command and collect the return code and streams
val returnCode = process.waitFor(30, TimeUnit.SECONDS)
val input = if (process.inputStream.available() > 0) fromStream(process.inputStream)
else listOf()
val error = if (process.errorStream.available() > 0) fromStream(process.errorStream)
else listOf()
// Check to see if the command succeeded
val isSuccess = isSuccess(returnCode, input, error)
if (isSuccess) {
info.successCallback(input)
} else {
info.errorCallback(error + input)
}
return if (isSuccess) 0 else 1
}
/**
* Subclasses can override this method to do their own error handling, since commands can
* have various ways to signal errors.
*/
open protected fun isSuccess(isSuccess: Boolean, input: List<String>, error: List<String>) : Boolean {
var hasErrors = ! isSuccess
if (info.useErrorStreamAsErrorIndicator && ! hasErrors) {
hasErrors = hasErrors || error.size > 0
}
if (info.useInputStreamAsErrorIndicator && ! hasErrors) {
hasErrors = hasErrors || input.size > 0
}
return ! hasErrors
}
/**
* Turn the given InputStream into a list of strings.
*/
private fun fromStream(ins: InputStream) : List<String> {
val result = arrayListOf<String>()
val br = BufferedReader(InputStreamReader(ins))
var line = br.readLine()
while (line != null) {
result.add(line)
line = br.readLine()
}
return result
}
}