lyng CLI: support for shebang, started shell KMP code
This commit is contained in:
parent
b961296425
commit
e0bb183929
3
bin/lyng_test
Executable file
3
bin/lyng_test
Executable file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env lyng
|
||||
|
||||
println("Hello from lyng!")
|
@ -29,11 +29,11 @@ kotlin {
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-common"))
|
||||
implementation(project(":lynglib"))
|
||||
implementation(libs.okio)
|
||||
|
||||
implementation(libs.clikt)
|
||||
|
||||
implementation(kotlin("stdlib-common"))
|
||||
// optional support for rendering markdown in help messages
|
||||
// implementation(libs.clikt.markdown)
|
||||
}
|
||||
@ -42,9 +42,15 @@ kotlin {
|
||||
dependencies {
|
||||
implementation(kotlin("test-common"))
|
||||
implementation(kotlin("test-annotations-common"))
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
implementation(libs.okio.fakefilesystem)
|
||||
}
|
||||
}
|
||||
// val nativeMain by getting {
|
||||
// dependencies {
|
||||
// implementation(kotlin("stdlib-common"))
|
||||
// }
|
||||
// }
|
||||
val linuxX64Main by getting {
|
||||
|
||||
}
|
||||
|
@ -17,11 +17,28 @@ import okio.use
|
||||
|
||||
expect fun exit(code: Int)
|
||||
|
||||
expect class ShellCommandExecutor {
|
||||
fun executeCommand(command: String): CommandResult
|
||||
|
||||
companion object {
|
||||
fun create(): ShellCommandExecutor
|
||||
}
|
||||
}
|
||||
|
||||
data class CommandResult(
|
||||
val exitCode: Int,
|
||||
val output: String,
|
||||
val error: String
|
||||
)
|
||||
|
||||
val baseContext = Context().apply {
|
||||
addFn("exit") {
|
||||
exit(requireOnlyArg<ObjInt>().toInt())
|
||||
ObjVoid
|
||||
}
|
||||
// ObjString.type.addFn("shell") {
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand() {
|
||||
@ -90,11 +107,16 @@ class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand() {
|
||||
}
|
||||
|
||||
suspend fun executeFile(fileName: String) {
|
||||
val text = FileSystem.SYSTEM.source(fileName.toPath()).use { fileSource ->
|
||||
var text = FileSystem.SYSTEM.source(fileName.toPath()).use { fileSource ->
|
||||
fileSource.buffer().use { bs ->
|
||||
bs.readUtf8()
|
||||
}
|
||||
}
|
||||
if( text.startsWith("#!") ) {
|
||||
// skip shebang
|
||||
val pos = text.indexOf('\n')
|
||||
text = text.substring(pos + 1)
|
||||
}
|
||||
processErrors {
|
||||
Compiler().compile(Source(fileName, text)).execute(baseContext)
|
||||
}
|
||||
|
21
lyng/src/jvmMain/kotlin/Shell.jvm.kt
Normal file
21
lyng/src/jvmMain/kotlin/Shell.jvm.kt
Normal file
@ -0,0 +1,21 @@
|
||||
package net.sergeych
|
||||
|
||||
// Alternative implementation for native targets
|
||||
actual class ShellCommandExecutor() {
|
||||
actual fun executeCommand(command: String): CommandResult {
|
||||
val process = ProcessBuilder("/bin/sh", "-c", command).start()
|
||||
val exitCode = process.waitFor()
|
||||
val output = process.inputStream.bufferedReader().readText()
|
||||
val error = process.errorStream.bufferedReader().readText()
|
||||
|
||||
return CommandResult(
|
||||
exitCode = exitCode,
|
||||
output = output.trim(),
|
||||
error = error.trim()
|
||||
)
|
||||
}
|
||||
|
||||
actual companion object {
|
||||
actual fun create(): ShellCommandExecutor = ShellCommandExecutor()
|
||||
}
|
||||
}
|
@ -1,7 +1,48 @@
|
||||
@file:OptIn(ExperimentalForeignApi::class, ExperimentalForeignApi::class)
|
||||
|
||||
package net.sergeych
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import platform.posix.fgets
|
||||
import platform.posix.pclose
|
||||
import platform.posix.popen
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
actual class ShellCommandExecutor() {
|
||||
actual fun executeCommand(command: String): CommandResult {
|
||||
val outputBuilder = StringBuilder()
|
||||
val errorBuilder = StringBuilder()
|
||||
|
||||
val fp = popen(command, "r") ?: return CommandResult(
|
||||
exitCode = -1,
|
||||
output = "",
|
||||
error = "Failed to execute command"
|
||||
)
|
||||
|
||||
val buffer = ByteArray(4096)
|
||||
while (true) {
|
||||
val bytesRead = buffer.usePinned { pinned ->
|
||||
fgets(pinned.addressOf(0), buffer.size.convert(), fp)
|
||||
}
|
||||
if (bytesRead == null) break
|
||||
outputBuilder.append(bytesRead.toKString())
|
||||
}
|
||||
|
||||
val status = pclose(fp)
|
||||
val exitCode = if (status == 0) 0 else 1
|
||||
|
||||
return CommandResult(
|
||||
exitCode = exitCode,
|
||||
output = outputBuilder.toString().trim(),
|
||||
error = errorBuilder.toString().trim()
|
||||
)
|
||||
}
|
||||
|
||||
actual companion object {
|
||||
actual fun create(): ShellCommandExecutor = ShellCommandExecutor()
|
||||
}
|
||||
}
|
||||
|
||||
actual fun exit(code: Int) {
|
||||
exitProcess(code)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user