-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from Animeshz/node-js
Add NodeJS support!
- Loading branch information
Showing
62 changed files
with
2,259 additions
and
771 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
plugins { | ||
`kotlin-dsl` | ||
`java-gradle-plugin` | ||
} | ||
|
||
repositories { | ||
jcenter() | ||
} | ||
|
||
dependencies { | ||
implementation(kotlin("gradle-plugin")) | ||
} | ||
|
||
gradlePlugin { | ||
plugins.register("keyboard-mouse-publishing") { | ||
id = "keyboard-mouse-publishing" | ||
implementationClass = "com.github.animeshz.keyboard_mouse.publishing.PublishingPlugin" | ||
} | ||
|
||
plugins.register("keyboard-mouse-native-compile") { | ||
id = "keyboard-mouse-native-compile" | ||
implementationClass = "com.github.animeshz.keyboard_mouse.native_compile.NativeCompilationPlugin" | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
...d-src/src/main/kotlin/com/github/animeshz/keyboard_mouse/native_compile/Configurations.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package com.github.animeshz.keyboard_mouse.native_compile | ||
|
||
class Target( | ||
val os: String, | ||
val arch: String, | ||
val dockerImage: String | ||
) | ||
|
||
open class NativeConfiguration { | ||
val jni = JniConfiguration() | ||
val napi = JsCompilationConfiguration() | ||
|
||
fun jni(configuration: JniConfiguration.() -> Unit) { | ||
jni.apply(configuration) | ||
} | ||
|
||
fun napi(configuration: JsCompilationConfiguration.() -> Unit) { | ||
napi.apply(configuration) | ||
} | ||
} | ||
|
||
open class JniConfiguration { | ||
val headers: JniHeaderConfiguration = JniHeaderConfiguration() | ||
val compilation: JniCompilationConfiguration = JniCompilationConfiguration() | ||
|
||
fun headers(configuration: JniHeaderConfiguration.() -> Unit) { | ||
headers.apply(configuration) | ||
} | ||
|
||
fun compilation(configuration: JniCompilationConfiguration.() -> Unit) { | ||
compilation.apply(configuration) | ||
} | ||
} | ||
|
||
open class JniHeaderConfiguration { | ||
var inputDir: String = "" | ||
var outputDir: String = "" | ||
} | ||
|
||
open class JniCompilationConfiguration { | ||
var baseInputPaths: List<String> = emptyList() | ||
var outputDir: String = "" | ||
var targets: List<Target> = emptyList() | ||
} | ||
|
||
open class JsCompilationConfiguration { | ||
var baseInputPaths: List<String> = emptyList() | ||
var outputDir: String = "" | ||
var targets: List<Target> = emptyList() | ||
} |
82 changes: 82 additions & 0 deletions
82
...c/src/main/kotlin/com/github/animeshz/keyboard_mouse/native_compile/JniCompilationTask.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package com.github.animeshz.keyboard_mouse.native_compile | ||
|
||
import org.apache.tools.ant.taskdefs.condition.Os | ||
import org.gradle.api.DefaultTask | ||
import org.gradle.api.GradleException | ||
import org.gradle.api.tasks.Input | ||
import org.gradle.api.tasks.TaskAction | ||
import org.gradle.api.tasks.options.Option | ||
import java.io.ByteArrayOutputStream | ||
import javax.inject.Inject | ||
|
||
/** | ||
* For building shared libraries out of C/C++ sources for JVM | ||
*/ | ||
open class JniCompilationTask @Inject constructor( | ||
private val target: Target | ||
) : DefaultTask() { | ||
init { | ||
group = "nativeCompilation" | ||
} | ||
|
||
@get:Input | ||
@set:Option(option = "verbose", description = "Sets verbosity of output.") | ||
var isVerbose: Boolean = false | ||
|
||
@TaskAction | ||
fun run() { | ||
project.checkDockerInstallation() | ||
|
||
val tmpVar = project.file(".").absolutePath | ||
val path = if (Os.isFamily(Os.FAMILY_WINDOWS)) { | ||
"/run/desktop/mnt/host/${tmpVar[0].toLowerCase()}${tmpVar.substring(2 until tmpVar.length).replace('\\', '/')}" | ||
} else { | ||
tmpVar | ||
} | ||
|
||
val work: () -> Pair<Int, String> = { | ||
ByteArrayOutputStream().use { | ||
project.exec { | ||
commandLine( | ||
"docker", | ||
"run", | ||
"--rm", | ||
"-v", | ||
"$path:/work/project", | ||
target.dockerImage, | ||
"bash", | ||
"-c", | ||
"mkdir -p \$WORK_DIR/project/build/jni && " + | ||
"mkdir -p \$WORK_DIR/project/build/tmp/compile-jni-${target.os}-${target.arch} && " + | ||
"cd \$WORK_DIR/project/build/tmp/compile-jni-${target.os}-${target.arch} && " + | ||
"cmake -DARCH=${target.arch} \$WORK_DIR/project/src/jvmMain/cpp/${target.os} && " + | ||
"cmake --build . ${if (isVerbose) "--verbose " else ""}--config Release && " + | ||
"cp -rf libKeyboardKt${target.arch}.{dll,so,dylib} \$WORK_DIR/project/build/jni 2>/dev/null || : && " + | ||
"cd .. && rm -rf compile-jni-${target.os}-${target.arch}" | ||
) | ||
|
||
isIgnoreExitValue = true | ||
standardOutput = System.out | ||
errorOutput = it | ||
}.exitValue to it.toString() | ||
} | ||
} | ||
var (exit, error) = work() | ||
|
||
// Fix non-daemon docker on Docker for Windows | ||
val nonDaemonError = "docker: error during connect: This error may indicate that the docker daemon is not running." | ||
if (Os.isFamily(Os.FAMILY_WINDOWS) && error.startsWith(nonDaemonError)) { | ||
project.exec { commandLine("C:\\Program Files\\Docker\\Docker\\DockerCli.exe", "-SwitchDaemon") }.assertNormalExitValue() | ||
|
||
do { | ||
Thread.sleep(500) | ||
val result = work() | ||
exit = result.first | ||
error = result.second | ||
} while (error.startsWith(nonDaemonError)) | ||
} | ||
|
||
System.err.println(error) | ||
if (exit != 0) throw GradleException("An error occured while running the command, see the stderr for more details.") | ||
} | ||
} |
81 changes: 81 additions & 0 deletions
81
.../main/kotlin/com/github/animeshz/keyboard_mouse/native_compile/JniHeaderGenerationTask.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package com.github.animeshz.keyboard_mouse.native_compile | ||
|
||
import org.apache.tools.ant.taskdefs.condition.Os | ||
import org.gradle.api.DefaultTask | ||
import org.gradle.api.tasks.TaskAction | ||
import org.gradle.internal.jvm.Jvm | ||
import org.gradle.kotlin.dsl.configure | ||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension | ||
import java.io.ByteArrayOutputStream | ||
import javax.inject.Inject | ||
import kotlin.system.exitProcess | ||
|
||
open class JniHeaderGenerationTask @Inject constructor( | ||
private val configuration: JniHeaderConfiguration | ||
) : DefaultTask() { | ||
val metaRegex = """public \w*\s*class (.+)\.(\w+) (?:implements|extends).*\{\R([^\}]*)\}""".toRegex() | ||
val methodRegex = """.*\bnative\b.*""".toRegex() | ||
|
||
init { | ||
group = "nativeCompilation" | ||
|
||
inputs.dir(configuration.inputDir) | ||
outputs.dir(configuration.outputDir) | ||
|
||
dependsOn(project.tasks.getByName("compileKotlinJvm")) | ||
} | ||
|
||
@TaskAction | ||
fun run() { | ||
val javaHome = Jvm.current().javaHome | ||
val javap = javaHome.resolve("bin").walk().firstOrNull { it.name.startsWith("javap") }?.absolutePath ?: error("javap not found") | ||
val javac = javaHome.resolve("bin").walk().firstOrNull { it.name.startsWith("javac") }?.absolutePath ?: error("javac not found") | ||
val buildDir = project.file("build/classes/kotlin/jvm/main") | ||
val tmpDir = project.file("build/tmp/jvmJni").apply { mkdirs() } | ||
|
||
buildDir.walkTopDown() | ||
.filter { "META" !in it.absolutePath } | ||
.forEach { file -> | ||
if (!file.isFile) return@forEach | ||
|
||
val output = ByteArrayOutputStream().use { | ||
project.exec { | ||
commandLine(javap, "-private", "-cp", buildDir.absolutePath, file.absolutePath) | ||
standardOutput = it | ||
}.assertNormalExitValue() | ||
it.toString() | ||
} | ||
|
||
val (packageName, className, methodInfo) = metaRegex.find(output)?.destructured ?: return@forEach | ||
val nativeMethods = methodRegex.findAll(methodInfo).mapNotNull { it.groups }.flatMap { it.asSequence().mapNotNull { group -> group?.value } }.toList() | ||
if (nativeMethods.isEmpty()) return@forEach | ||
|
||
val source = buildString { | ||
appendln("package $packageName;") | ||
appendln("public class $className {") | ||
for (method in nativeMethods) { | ||
if ("()" in method) appendln(method) | ||
else { | ||
val updatedMethod = StringBuilder(method).apply { | ||
var count = 0 | ||
var i = 0 | ||
while (i < length) { | ||
if (this[i] == ',' || this[i] == ')') insert(i, " arg${count++}".also { i += it.length + 1 }) | ||
else i++ | ||
} | ||
} | ||
appendln(updatedMethod) | ||
} | ||
} | ||
appendln("}") | ||
} | ||
|
||
val outputFile = tmpDir.resolve(packageName.replace(".", "/")).apply { mkdirs() }.resolve("$className.java").apply { delete() }.apply { createNewFile() } | ||
outputFile.writeText(source) | ||
|
||
project.exec { | ||
commandLine(javac, "-h", project.file(configuration.outputDir).absolutePath, outputFile.absolutePath) | ||
}.assertNormalExitValue() | ||
} | ||
} | ||
} |
79 changes: 79 additions & 0 deletions
79
.../src/main/kotlin/com/github/animeshz/keyboard_mouse/native_compile/NapiCompilationTask.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package com.github.animeshz.keyboard_mouse.native_compile | ||
|
||
import org.apache.tools.ant.taskdefs.condition.Os | ||
import org.gradle.api.DefaultTask | ||
import org.gradle.api.GradleException | ||
import org.gradle.api.tasks.Input | ||
import org.gradle.api.tasks.TaskAction | ||
import org.gradle.api.tasks.options.Option | ||
import java.io.ByteArrayOutputStream | ||
import javax.inject.Inject | ||
|
||
/** | ||
* For building shared libraries out of C/C++ sources for NodeJS | ||
*/ | ||
open class NapiCompilationTask @Inject constructor( | ||
private val target: Target | ||
) : DefaultTask() { | ||
init { | ||
group = "nativeCompilation" | ||
} | ||
|
||
@get:Input | ||
@set:Option(option = "verbose", description = "Sets verbosity of output.") | ||
var isVerbose: Boolean = false | ||
|
||
@TaskAction | ||
fun run() { | ||
project.checkDockerInstallation() | ||
|
||
val tmpVar = project.file(".").absolutePath | ||
val path = if (Os.isFamily(Os.FAMILY_WINDOWS)) { | ||
"/run/desktop/mnt/host/${tmpVar[0].toLowerCase()}${tmpVar.substring(2 until tmpVar.length).replace('\\', '/')}" | ||
} else { | ||
tmpVar | ||
} | ||
|
||
val work: () -> Pair<Int, String> = { | ||
ByteArrayOutputStream().use { | ||
project.exec { | ||
commandLine( | ||
"docker", | ||
"run", | ||
"--rm", | ||
"-v", | ||
"$path:/work/project", | ||
target.dockerImage, | ||
"bash", | ||
"-c", | ||
"mkdir -p \$WORK_DIR/project/build/napi && " + | ||
"cmake-js compile --CDARCH=${target.arch} --arch=${target.arch} ${if (isVerbose) "-l=verbose " else ""} -d=\$WORK_DIR/project/src/jsMain/cpp/${target.os} && " + | ||
"cp -rf \$WORK_DIR/project/src/jsMain/cpp/${target.os}/build/{,Release/}KeyboardKt${target.os.capitalize()}${target.arch}.node \$WORK_DIR/project/build/napi 2>/dev/null || : && " + | ||
"rm -rf \$WORK_DIR/project/src/jsMain/cpp/${target.os}/build" | ||
) | ||
|
||
isIgnoreExitValue = true | ||
standardOutput = System.out | ||
errorOutput = it | ||
}.exitValue to it.toString() | ||
} | ||
} | ||
var (exit, error) = work() | ||
|
||
// Fix non-daemon docker on Docker for Windows | ||
val nonDaemonError = "docker: error during connect: This error may indicate that the docker daemon is not running." | ||
if (Os.isFamily(Os.FAMILY_WINDOWS) && error.startsWith(nonDaemonError)) { | ||
project.exec { commandLine("C:\\Program Files\\Docker\\Docker\\DockerCli.exe", "-SwitchDaemon") }.assertNormalExitValue() | ||
|
||
do { | ||
Thread.sleep(500) | ||
val result = work() | ||
exit = result.first | ||
error = result.second | ||
} while (error.startsWith(nonDaemonError)) | ||
} | ||
|
||
System.err.println(error) | ||
if (exit != 0) throw GradleException("An error occured while running the command, see the stderr for more details.") | ||
} | ||
} |
Oops, something went wrong.