Kotlin native - Execute an executable - kotlin

I am trying to execute a command via bash, for example konanc.
In KotlinJVM this would just be using Runtime.getRuntime().exec("..."), or creating a Process using the ProcessBuilder, however, none of those classes are available in Kotlin-Native, as they are part of the Java libraries.
I tried searching for example code in the documentation and the kotlin-native GitHub repository, but haven't found anything.

tl;dr No, there is no standard process api for kotlin-native
Well the kotlin std for native is still under development, and i don't think process api will be there anytime soon.
However you could use interoperability with some C process library such as https://github.com/eidheim/tiny-process-library
How-to you will find here https://github.com/JetBrains/kotlin-native/blob/master/INTEROP.md
However there are also POSIX's exec/fork calls you could use to spawn and create new process and i think kotlin-native does include POSIX for linux/windows. https://github.com/JetBrains/kotlin-native/tree/master/platformLibs/src/platform see posix.def for platforms.
Example:
import platform.posix.*
fun main(arguments: Array<String>) {
println("${arguments[0]}")
execlp("touch", "touch", "${arguments[0]}")
}
Calling it with ./file <name> will create a file that is named after the parameter name in your current directory.

IMHO, this is the killer app for Kotlin native. And here is part of the solution using the standard Kotlin API; This solution still needs the buffering of the dir or date command output, but generally works on Windows yea!
import kotlin.native.OsFamily.*
import platform.posix.*
fun main(arguments: Array<String>) {
println("running")
if (arguments.size >= 1) {
arguments.forEach { a -> println(a) }
}
val platform = Platform
val os = platform.osFamily
println("os is " + os)
when (os) {
WINDOWS -> runWindows()
else -> runUnix()
}
}
fun runWindows() {
val result = execlp("dir", "","")
println("Ran on windows $result");
}
fun runUnix() {
execlp("date", "","")
println("Ran on UNIX")
}

Finally one that reads from the called process yea!
Only tested on Windows, I will do unix (aka mac :) ) tomorrow.
import kotlin.native.OsFamily.*
import platform.posix.*
import kotlinx.cinterop.refTo
import kotlinx.cinterop.toKString
fun main(arguments: Array<String>) {
println("running")
if (arguments.size >= 1) {
arguments.forEach { a -> println(a) }
}
val platform = Platform
val os = platform.osFamily
println("os is " + os)
when (os) {
WINDOWS -> runWindows()
else -> runUnix()
}
}
fun runWindows() {
val result = execl("dir", "","")
//hmm emulate https://gist.github.com/a-cordier/33211eda92b8084a9e7e
//https://www.youtube.com/watch?v=6xbLgZpOBi8
val fp = _popen("dir", "r") ?: error("Failed to run command: dir")
val buffer = ByteArray(4096)
var counter = 0
println("hmm")
while (true) {
val input = fgets(buffer.refTo(0), buffer.size, fp) ?: break
print(input.toKString())
//println(counter++)
}
println("Ran on windows $result");
}
fun runUnix() {
execlp("date", "","")
println("Ran on UNIX")
}

Related

Why does kotlin/native hello world not output anything inside IntelliJ IDEA on Apple M1?

I've downloaded a fresh IntelliJ IDEA with the Kotlin multiplatform plugin and created a project using the Native application project template. This template creates a Main.kt file with the content:
fun main() {
println("Hello, Kotlin/Native!")
}
As well as many other gradle files referencing kotlin("multiplatform") version "1.7.20". I can build and run the project from inside IntelliJ IDEA, but I see no hello world:
I can only see the gradle output and a success result, but no Hello Kotlin/Native! message anywhere. I've tried changing the runDebugExecutableNative settings and checked the options related to show the console/output when stdout messages are printed:
I can see no other output window/pane, so… where can I see the output? The project builds a debug or release Kasha.kexe which I can run from the command line manually, but I'm guessing an IDE shouldn't require me to run commands from the command line every time?
$ ./build/bin/native/debugExecutable/Kasha.kexe
Hello, Kotlin/Native!
I can see a greyed out symbol in the run pane, which corresponds to the output > Task :runDebugExecutableNative SKIPPED. Does that mean the IDE can only build but not run the executable? I'm using IntelliJ IDEA 2022.2.3 (Community Edition) and kotlin("multiplatform") version "1.7.20".
UPDATE As bppleman correctly guesses the problem is I'm trying to run this on an Apple M1 machine. The gradle that comes out from the template is:
kotlin {
val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when {
hostOs == "Mac OS X" -> macosX64("native")
hostOs == "Linux" -> linuxX64("native")
isMingwX64 -> mingwX64("native")
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}
…
Replacing the macosX64 call with macosArm64 makes everything work as expected. But I guess now intel based apple machines will be on the wrong side of the fence?
I see a new edit to the question
Replacing the macosX64 call with macosArm64 makes everything work as expected. But I guess now intel based apple machines will be on the wrong side of the fence?
So I'll submit a new answer to that question
Indeed, as you said, there will still be problems after switching to intel mac, so my approach is to judge the current cpu platform at the same time, just like this
val hostOs = System.getProperty("os.name")
val isMingw = hostOs.startsWith("Windows")
val nativeTarget = when {
hostOs == "Mac OS X" -> {
if (System.getProperty("os.arch").contains("aarch64")) {
macosArm64("native")
} else {
macosX64("native")
}
}
hostOs == "Linux" -> linuxX64("native")
isMingw -> {
if (System.getenv("ProgramFiles(x86)") != null) {
mingwX86("native")
} else {
mingwX64("native")
}
}
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}
Of course you can also do this, it depends on your needs
kotlin {
val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows")
val nativeTargets = when {
hostOs == "Mac OS X" -> listOf(macosArm64("native"), macosX64("macosX64"))
hostOs == "Linux" -> listOf(linuxX64("native"))
isMingwX64 -> listOf(mingwX64("native"))
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}
nativeTargets.forEach { target ->
target.apply {
binaries {
executable {
entryPoint = "main"
}
}
}
}
sourceSets {
val nativeMain by getting
val nativeTest by getting
val macosX64Main by getting {
dependsOn(nativeMain)
}
val macosX64Test by getting {
dependsOn(nativeTest)
}
}
}
Use dependsOn to share sourceSet.
It is important to note that there can only be one executeabl entry between two macOS sourceSets
You should not add another entry for macosX64 unless you really want to, then you have to remove the dependsOn dependency
u'll got:

Using Kotlin/Native, how can I execute an external OS executable or $PATH command and interact with its stdin, stdout, and stderr streams?

I'm working on a proof of concept for a command line tool written in Kotlin/Native, as it seems to be an ideal language and runtime for cross-platform binaries.
This command line tool needs to regularly interact with operating system executables, commands and/or shell functions in the os user $PATH or shell environment. However, I don't see any examples in kotlin-native samples or documentation anywhere that might indicate how to:
execute an operating system executable or $PATH command
obtain the execution's return code (integer)
ensure the executed process's stdin, stdout and stderr file descriptor streams can be represented as OutputStream and InputStreams respectively
In JVM-land, we'd use java.lang.ProcessBuilder for all of this, but that's apparently not available in Kotlin/Native.
I found the cinterop/posix platform.posix.system function, but that doesn't give you access to the process's streams.
In my web research, I found a really nice C tutorial that indicates the only clean way to do this is with fork and dup2 and such, but it's not clear to me if or how that would translate to Kotlin/Native code.
I did some play around with kotlin native and succeded to do a posix exec command for jvm, mac, unix and (untested) windows...
https://github.com/hoffipublic/minimal_kotlin_multiplatform
target common
fun String.executeCommand(
redirectStderr: Boolean = true
): String? = MppProcess.executeCommand(this, redirectStderr)
interface IMppProcess {
fun executeCommand(
command: String,
redirectStderr: Boolean = true
): String?
}
expect object MppProcess : IMppProcess {
override fun executeCommand(
command: String,
redirectStderr: Boolean
): String?
}
target jvm
actual object MppProcess : IMppProcess {
actual override fun executeCommand(
command: String,
redirectStderr: Boolean
): String? {
return runCatching {
ProcessBuilder(command.split(Regex("(?<!(\"|').{0,255}) | (?!.*\\1.*)")))
//.directory(workingDir)
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.apply { if (redirectStderr) this.redirectError(ProcessBuilder.Redirect.PIPE) }
.start().apply { waitFor(60L, TimeUnit.SECONDS) }
.inputStream.bufferedReader().readText()
}.onFailure { it.printStackTrace() }.getOrNull()
}
}
target mac/unix/windows
import kotlinx.cinterop.refTo
import kotlinx.cinterop.toKString
import platform.posix.fgets
import platform.posix.pclose
import platform.posix.popen
actual object MppProcess : IMppProcess {
actual override fun executeCommand(
command: String,
redirectStderr: Boolean
): String? {
val commandToExecute = if (redirectStderr) "$command 2>&1" else command
val fp = popen(commandToExecute, "r") ?: error("Failed to run command: $command")
val stdout = buildString {
val buffer = ByteArray(4096)
while (true) {
val input = fgets(buffer.refTo(0), buffer.size, fp) ?: break
append(input.toKString())
}
}
val status = pclose(fp)
if (status != 0) {
error("Command `$command` failed with status $status${if (redirectStderr) ": $stdout" else ""}")
}
return stdout
}
}
only did execute ls by now, but it works.

Creating an IntelliJ Module Programmatically

I'm trying to make a proper project, like the one setup when you go through the Kotlin wizard or something, but I can't figure it out.
class SpigotKtWizard : ModuleBuilder() {
override fun setupRootModel(modifiableRootModel: ModifiableRootModel?) {}
override fun getModuleType(): ModuleType<*> {
return SpigotKtModuleType.instance
}
override fun createWizardSteps(wizardContext: WizardContext, modulesProvider: ModulesProvider): Array<ModuleWizardStep> {
return arrayOf(BuildOptionsStep())
}
override fun createProject(name: String?, path: String?): Project? {
val project = super.createProject(name, path) ?: return null
val d = project.baseDir.createChildData(this, "Test")
File(d.path).writeText("Testing boyyyy")
return project
}
}
This is what I have currently, and I'm getting this:
But other projects (and specifically, the same design I'm trying to achieve), look more like this:
Is there a page in the docs that I missed?
In pic 2 you're displaying a "project" instead of a "module", see the offical doc.
You may want to implement a DirectoryProjectGeneratorBase<YourSettingsBean> and register a (as an example you may refer to this file my julia plugin) directoryProjectGenerator in plugin.xml.
In your implementation of generateProject, you can create files, set files as source root/test root/excluded root by using:
ApplicationManager.getApplication().runWriteAction {
val modifiableModel: ModifiableRootModel = ModifiableModelsProvider.SERVICE.getInstance().getModuleModifiableModel(module)
module.rootManager.modifiableModel.apply {
inheritSdk()
contentEntries.firstOrNull()?.apply {
addExcludeFolder(findOrCreate(baseDir, "out", module))
addSourceFolder(findOrCreate(baseDir, "src", module), false)
}
commit()
}
ModifiableModelsProvider.SERVICE.getInstance().commitModuleModifiableModel(modifiableModel)
}
This should be missing in the doc, BTW. So it's not your fault. But I recommend you to take a look at the existing plugin projects (like the julia plugin mentioned above, the mathemetica plugin or the covscript plugin), which are extremely helpful for new comers.

Why the producer with unlimited buffered channel doesn't return or return invalid data from channel?

Look at this adopted example from official Kotlin documentation:
package com.example
import kotlinx.coroutines.experimental.channels.produce
import kotlinx.coroutines.experimental.runBlocking
fun main(args: Array<String>) = runBlocking {
val producer = produce {
for (x in 1..5) send(x)
}
for (i in producer) {
println(i)
}
println("Done!")
}
If I run it will print:
1
2
3
4
5
Done!
As you can see here is using unbuffered channel by default. Let's change it to buffered channel:
package com.example
import kotlinx.coroutines.experimental.channels.Channel
import kotlinx.coroutines.experimental.channels.produce
import kotlinx.coroutines.experimental.runBlocking
fun main(args: Array<String>) = runBlocking {
val producer = produce(capacity = Channel.UNLIMITED) {
for (x in 1..5) send(x)
}
for (i in producer) {
println(i)
}
println("Done!")
}
If I run it several times it will print:
1
Done!
or
2
Done!
or just
Done!
I suppose that the producer will put data to the buffered channel and the for loop will read data from it until it can do it (i.e. it will read if data is exists in channel OR channel is not closed). Thus I think the for loop should read all data from buffered channel even it was closed. Am I right?
Can anybody explain me why buffered channel cause such strange behavior of producer? Is it bug?
P.S.
kotlin-stdlib v.1.2.21 (it is last version today)
kotlinx-coroutines-core v.0.22.2 (it is last version today)
The Kotlin team has confirmed that this is a bug and opened an issue for it in response to this question.
The description of the issue mentions that it only appears with the produce builder and provides a workaround, basically the inlined code of that convenience construct:
val producer = Channel<Int>(1)
launch(coroutineContext) {
for(i in 1..2) {
producer.send(i)
println("Sent: $i")
}
producer.close()
}

Corda WireTransaction type mismatch - TransactionSignature inferred but DigitalSignature.WithKey expected

I am building a flow modelling the Corda IRS RatesFixFlow (I'm guessing M15 compatible.) But my app is working against the M14 build.
This is the source code:
override fun call(): TransactionSignature {
val resp = sendAndReceive<TransactionSignature>(oracle, SignRequest(partialMerkleTx))
return resp.unwrap { sig ->
check(oracle.owningKey.isFulfilledBy(listOf(sig.by)))
tx.toWireTransaction().checkSignature(sig)
sig
}
}
My code:
override fun call(): TransactionSignature {
val resp = sendAndReceive<TransactionSignature>(agency, SignRequest(partialMerkleTx))
return resp.unwrap { sig ->
check(agency.owningKey.isFulfilledBy( listOf(sig.by)))
tx.toWireTransaction().checkSignature(sig)
sig
}
}
The IRS checkSignature is pointing back to the WireTransaction.kt implementation that uses TransactionSignature as return rather than (I assume) deprecated M14 WireTransaction.checkSignature() that uses DigitalKey.WithKey
Are there any quick workarounds or hacks to reconcile the type update?
If you're on M14, checkSignature should take a DigitalSignature.WithKey.
Try clearing out your local maven repository and cleaning out your caches if you're using IntelliJ: https://www.jetbrains.com/help/idea/cleaning-system-cache.html.