I'm trying to create a kotlin-jvm project that inc...
# gradle
d
I'm trying to create a kotlin-jvm project that includes a JNI library. Unfortunately, it doesn't look like I can pull in the output files from the JNI model as a dependency to the kotlin module. Any help would be appreciated.
JNI build.gradle.kts:
Copy code
plugins {
    `cpp-library`
}

group = "com.stochastictinkr"
version = "1.0-SNAPSHOT"

val jdkHome = System.getenv("JAVA_HOME") ?: System.getenv("JDK_HOME")

library {
    baseName = "niko-pty"
    targetMachines.addAll(
        machines.macOS.x86_64,
        machines.linux.x86_64,
    )

    binaries.configureEach {
        if (targetPlatform.targetMachine.operatingSystemFamily.isMacOs) {
            // Add JNI library to macOS binaries
            compileTask.get().compilerArgs.addAll(
                "-I$jdkHome/include",
                "-I$jdkHome/include/darwin",
            )
        }
    }
}
kotlin module build.gradle.kts:
Copy code
plugins {
    alias(libs.plugins.kotlin.jvm)
}

group = "com.stochastictinkr"
version = "1.0-SNAPSHOT"

dependencies {
    implementation(project(":niko-pty:native"))
}

repositories {
    mavenCentral()
}
Error message is:
Copy code
No matching variant of project :niko-pty:native was found. The consumer was configured to find a library for use during compile-time, compatible with Java 21, preferably in the form of class files, preferably optimized for standard JVMs, and its dependencies declared externally, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'jvm' but:
with a bunch of variants.
v
Well, you left out the relevant part of the error message. But I guess the problem is, that you say "I want the Java library from `niko ptynative`" while there is only a native library, so you probably have to request some different attributes for your dependency.
👍 1
d
I think I've got it working this way:
Copy code
implementation(project(":niko-pty:native")) {
        attributes {
            attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, Usage.NATIVE_LINK))
            attribute(CppBinary.OPTIMIZED_ATTRIBUTE, false)
        }
    }
Though it doesn't seem to package the lib into the jar file.
I would expect something like this to work:
Copy code
Runtime.getRuntime().load(PseudoTerminal::class.java.classLoader.getResource("libniko-pty.dylib")!!.path)
Oh, interstingly, IntelliJ puts it in the class path (which isn't correct at all).
Copy code
/Users/pittsd/.sdkman/candidates/java/21.0.1-open/bin/java --add-opens=java.base/java.io=ALL-UNNAMED -javaagent:/Users/pittsd/Applications/IntelliJ IDEA <http://Ultimate.app/Contents/lib/idea_rt.jar=64373:/Users/pittsd/Applications/IntelliJ|Ultimate.app/Contents/lib/idea_rt.jar=64373:/Users/pittsd/Applications/IntelliJ> IDEA <http://Ultimate.app/Contents/bin|Ultimate.app/Contents/bin> -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -classpath /Users/pittsd/dev/niko/niko-nikoterm/build/classes/kotlin/main:/Users/pittsd/dev/niko/niko-ecma48/build/classes/kotlin/main:/Users/pittsd/dev/niko/niko-telnet/build/classes/kotlin/main:/Users/pittsd/dev/niko/niko-pty/build/classes/kotlin/main:/Users/pittsd/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/2.0.0/b48df2c4aede9586cc931ead433bc02d6fd7879e/kotlin-stdlib-2.0.0.jar:/Users/pittsd/.gradle/caches/modules-2/files-2.1/net.java.dev.jna/jna-platform/5.14.0/28934d48aed814f11e4c584da55c49fa7032b31b/jna-platform-5.14.0.jar:/Users/pittsd/.gradle/caches/modules-2/files-2.1/net.java.dev.jna/jna/5.14.0/67bf3eaea4f0718cb376a181a629e5f88fa1c9dd/jna-5.14.0.jar:/Users/pittsd/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.8.1/bb0e192bd7c2b6b8217440d36e9758e377e450/kotlinx-coroutines-core-jvm-1.8.1.jar:/Users/pittsd/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/23.0.0/8cc20c07506ec18e0834947b84a864bfc094484e/annotations-23.0.0.jar:/Users/pittsd/dev/niko/niko-io/build/classes/kotlin/main:/Users/pittsd/dev/niko/niko-pty/native/build/lib/main/debug/macos/libniko-pty.dylib com.stochastictinkr.niko.term.NikoTermKt
v
No, not IntelliJ puts it in the classpath, you put it in the classpath
d
Well, yeah, but not intentionally 😉
v
There is no reason this should "magically" be included in the Jar, the Jar just contains the code from that project it is coming from.
d
Right, I guess my question is, how do I make it part of the jar?
v
And if it were in the jar, I don't think
Runtime.getRuntime().load(PseudoTerminal::class.java.classLoader.getResource("libniko-pty.dylib")!!.path)
would work. You would probably need to unpack it to some location and load it from there I guess
d
Hmm, fair enough.
v
Iirc you need to declare a separate resolvable configuration, declare the dependency for that, have a copy task that copies the configuration to some dedicated directory in
layout.buildDirectory
, and then use that task as
srcDir
for the
resources
source directory set of the
main
source set.
d
Okay, that makes sense. It's unfortunate that it takes so many hoops lol.
Seems like JNI support could have been done with a more declarative configuration, but I guess its not common enough for gradle to have anything out-of-the-box.
v
Maybe you should consider using JNA instead of JNI, then you don't even need to build any native code 🙂
d
I tried JNA originally, but unfortunately, I need to use
fork
which doesn't work properly with JNA.
It worked part of the time, but crashed part of the time and just didn't work part of the time.
😞 1