1
0
Fork 0
mirror of https://github.com/ethauvin/kobalt.git synced 2025-04-27 08:38:13 -07:00

Checkpoint.

This commit is contained in:
Cedric Beust 2015-12-06 10:37:53 -08:00
parent e93f4ba85f
commit 8882c1cae5
4 changed files with 143 additions and 75 deletions

View file

@ -6,65 +6,28 @@ import java.io.InputStream
import java.io.InputStreamReader
import java.util.concurrent.TimeUnit
open class RunCommand(val command: String) {
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> -> error(output.joinToString("\n "))
}
var directory = File(".")
var env = hashMapOf<String, String>()
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 = true
var useInputStreamAsErrorIndicator = false
var useErrorStreamAsErrorIndicator : Boolean = true
var useInputStreamAsErrorIndicator : Boolean = false
fun useErrorStreamAsErrorIndicator(f: Boolean) : RunCommand {
useErrorStreamAsErrorIndicator = f
return this
}
var errorCallback: Function1<List<String>, Unit> = RunCommand.DEFAULT_ERROR
var successCallback: Function1<List<String>, Unit> = RunCommand.DEFAULT_SUCCESS
open fun run(args: List<String>,
errorCallback: Function1<List<String>, Unit> = DEFAULT_ERROR,
successCallback: Function1<List<String>, Unit> = defaultSuccess) : Int {
val allArgs = arrayListOf<String>()
allArgs.add(command)
allArgs.addAll(args)
val pb = ProcessBuilder(allArgs)
pb.directory(directory)
log(2, "Running command in directory ${directory.absolutePath}" +
"\n " + allArgs.joinToString(" ").replace("\\", "/"))
val process = pb.start()
pb.environment().let { pbEnv ->
env.forEach {
pbEnv.put(it.key, it.value)
}
}
val callSucceeded = 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()
val isSuccess = isSuccess(callSucceeded, input, error)
if (isSuccess) {
successCallback(fromStream(process.inputStream))
} else {
errorCallback(error + input)
}
return if (isSuccess) 0 else 1
}
open protected fun isSuccess(callSucceeded: Boolean, input: List<String>, error: List<String>) : Boolean {
var hasErrors = ! callSucceeded
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
}
@ -72,9 +35,83 @@ open class RunCommand(val command: String) {
hasErrors = hasErrors || input.size > 0
}
! hasErrors
}
}
fun runCommand(init: RunCommandInfo.() -> Unit) = RunCommand(RunCommandInfo().apply { init() }).invoke()
open class RunCommand(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> ->
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("\\", "/"))
val process = pb.start()
pb.environment().let { pbEnv ->
info.env.forEach {
pbEnv.put(it.key, it.value)
}
}
// 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))

View file

@ -1,24 +1,54 @@
package com.beust.kobalt.plugin.android
import com.beust.kobalt.api.Project
import com.beust.kobalt.misc.RunCommand
import com.beust.kobalt.misc.log
import com.beust.kobalt.misc.runCommand
import java.io.File
open class AndroidCommand(project: Project, androidHome: String, command: String, cwd: File = File(project.directory))
: RunCommand(command) {
init {
env.put("ANDROID_HOME", androidHome)
directory = cwd
}
open class AndroidCommand(project: Project, val androidHome: String, val command: String,
val directory: File = File(project.directory),
val useErrorStreamAsErrorIndicator : Boolean = true,
val args: List<String>)
// : RunCommand(command, directory = cwd, args = args
// ,
// successCallback = { output ->
// log(1, "$command succeeded:")
// output.forEach {
// log(1, " $it")
// }
// }
{
open fun call(args: List<String>) = run(args,
// val SUCCESS_CALLBACK : (List<String>) -> Unit = { output ->
// log(1, "$command succeeded:")
// output.forEach {
// log(1, " $it")
// }
// }
//
// val ERROR_CALLBACK : (List<String>) -> Unit = { output ->
// with(StringBuilder()) {
// append("Error running $command:")
// output.forEach {
// append(" $it")
// }
// error(this.toString())
// }
// }nComman
open fun call(theseArgs: List<String>) : Int {
val rc = runCommand {
args = theseArgs
useErrorStreamAsErrorIndicator = useErrorStreamAsErrorIndicator
directory = directory
env = hashMapOf("ANDROID_HOME" to androidHome)
successCallback = { output ->
log(1, "$command succeeded:")
output.forEach {
log(1, " $it")
}
},
}
errorCallback = { output ->
with(StringBuilder()) {
append("Error running $command:")
@ -27,7 +57,10 @@ open class AndroidCommand(project: Project, androidHome: String, command: String
}
error(this.toString())
}
})
}
}
return rc
}
}

View file

@ -107,15 +107,12 @@ public class AndroidPlugin @Inject constructor(val javaCompiler: JavaCompiler, v
* aapt returns 0 even if it fails, so in order to detect whether it failed, we are checking
* if its error stream contains anything.
*/
inner class AaptCommand(project: Project, aapt: String, val aaptCommand: String,
cwd: File = File(".")) : AndroidCommand(project, androidHome(project), aapt) {
init {
directory = cwd
useErrorStreamAsErrorIndicator = true
}
override fun call(args: List<String>) = super.run(arrayListOf(aaptCommand) + args)
}
inner class AaptCommand(project: Project, aapt: String, val aaptCommand: String, cwd: File = File("."),
args: List<String>)
: AndroidCommand(project, androidHome(project), aapt,
directory = cwd,
useErrorStreamAsErrorIndicator = true,
args = arrayListOf(aaptCommand) + args)
private fun generateR(project: Project, generated: String, aapt: String) : Boolean {
val compileSdkVersion = compileSdkVersion(project)
@ -132,7 +129,7 @@ public class AndroidPlugin @Inject constructor(val javaCompiler: JavaCompiler, v
val variantDir = context.variant.toIntermediateDir()
val rDirectory = KFiles.joinAndMakeDir(generated, "source", "r", variantDir).toString()
val result = AaptCommand(project, aapt, "package").call(listOf(
val result = AaptCommand(project, aapt, "package", args = listOf(
"-f",
"--no-crunch",
"-I", androidJar.toString(),
@ -358,7 +355,7 @@ public class AndroidPlugin @Inject constructor(val javaCompiler: JavaCompiler, v
* adb has weird ways of signaling errors, that's the best I've found so far.
*/
class AdbInstall : RunCommand(adb(project)) {
override fun isSuccess(callSucceeded: Boolean, input: List<String>, error: List<String>)
override fun isSuccess(isSuccess: Boolean, input: List<String>, error: List<String>)
= input.filter { it.contains("Success")}.size > 0
}

View file

@ -55,9 +55,10 @@ class RetrolambdaPlugin @Inject constructor(val dependencyManager: DependencyMan
"-Dretrolambda.bytecodeVersion=${config.byteCodeVersion}",
"-jar", JAR.jarFile.get().path)
val result = RunCommand("java").apply {
directory = File(project.directory)
}.run(args)
val result = RunCommand("java",
directory = File(project.directory),
args = args)
.run()
TaskResult(result == 0)
} else {
TaskResult()