org.jetbrains.kotlin.konan.exec.ExecuteCommand.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.konan.exec
import org.jetbrains.kotlin.konan.KonanExternalToolFailure
import java.io.BufferedReader
import java.io.File
import java.io.InputStreamReader
import java.lang.ProcessBuilder.Redirect
import java.nio.file.Files
open class Command(initialCommand: List, val redirectInputFile: File? = null) {
constructor(tool: String) : this(listOf(tool))
constructor(vararg command: String) : this(command.toList())
protected val command = initialCommand.toMutableList()
val argsWithExecutable: List = command
val args: List
get() = command.drop(1)
operator fun String.unaryPlus(): Command {
command += this
return this@Command
}
operator fun List.unaryPlus(): Command {
command.addAll(this)
return this@Command
}
var logger: ((() -> String)->Unit)? = null
private var stdError: List = emptyList()
fun logWith(newLogger: ((() -> String)->Unit)): Command {
logger = newLogger
return this
}
open fun runProcess(): Int {
stdError = emptyList()
val builder = ProcessBuilder(command)
builder.redirectOutput(Redirect.INHERIT)
if (redirectInputFile == null) {
builder.redirectInput(Redirect.INHERIT)
} else {
builder.redirectInput(redirectInputFile)
}
val process = builder.start()
val reader = BufferedReader(InputStreamReader(process.errorStream))
stdError = reader.readLines()
val exitCode = process.waitFor()
return exitCode
}
open fun execute() {
log()
val code = runProcess()
handleExitCode(code, stdError)
}
/**
* If withErrors is true then output from error stream will be added
*/
fun getOutputLines(withErrors: Boolean = false): List =
getResult(withErrors, handleError = true).outputLines
fun getResult(withErrors: Boolean, handleError: Boolean = false): Result {
log()
val outputFile = Files.createTempFile(null, null).toFile()
outputFile.deleteOnExit()
try {
val builder = ProcessBuilder(command)
if (redirectInputFile == null) {
builder.redirectInput(Redirect.INHERIT)
} else {
builder.redirectInput(redirectInputFile)
}
builder.redirectError(Redirect.INHERIT)
builder.redirectOutput(Redirect.to(outputFile))
.redirectErrorStream(withErrors)
// Note: getting process output could be done without redirecting to temporary file,
// however this would require managing a thread to read `process.inputStream` because
// it may have limited capacity.
val process = builder.start()
val code = process.waitFor()
if (handleError) handleExitCode(code, outputFile.readLines())
return Result(code, outputFile.readLines())
} finally {
outputFile.delete()
}
}
class Result(val exitCode: Int, val outputLines: List)
private fun handleExitCode(code: Int, output: List = emptyList()) {
if (code != 0) throw KonanExternalToolFailure("""
The ${command[0]} command returned non-zero exit code: $code.
output:
""".trimIndent() + "\n${output.joinToString("\n")}", command[0])
// Show warnings in case of success linkage.
if (stdError.isNotEmpty()) {
stdError.joinToString("\n").also { message ->
logger?.let { it { message } } ?: println(message)
}
}
}
private fun log() {
if (logger != null) logger!! { command.joinToString(" ") }
}
}