lyng CLI: support for shebang, started shell KMP code
This commit is contained in:
		
							parent
							
								
									b961296425
								
							
						
					
					
						commit
						83e79f47c7
					
				
							
								
								
									
										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, 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