aboutsummaryrefslogtreecommitdiff
path: root/jni-library/src/main/scala/ch/jodersky/jni
diff options
context:
space:
mode:
Diffstat (limited to 'jni-library/src/main/scala/ch/jodersky/jni')
-rw-r--r--jni-library/src/main/scala/ch/jodersky/jni/NativeLoader.scala75
-rw-r--r--jni-library/src/main/scala/ch/jodersky/jni/Platform.scala50
2 files changed, 125 insertions, 0 deletions
diff --git a/jni-library/src/main/scala/ch/jodersky/jni/NativeLoader.scala b/jni-library/src/main/scala/ch/jodersky/jni/NativeLoader.scala
new file mode 100644
index 0000000..46d4b2d
--- /dev/null
+++ b/jni-library/src/main/scala/ch/jodersky/jni/NativeLoader.scala
@@ -0,0 +1,75 @@
+package ch.jodersky.jni
+
+import java.io.{File, FileOutputStream, InputStream, OutputStream}
+import scala.io.Source
+
+/**
+ * Provides enhanced native library loading functionality.
+ */
+object NativeLoader {
+
+ /** Name of the shared library file that is contained in a jar. */
+ final val LibraryName = "library"
+
+ final val BufferSize = 4096
+
+ /** Extract a resource from this class loader to a temporary file. */
+ private def extract(path: String): Option[File] = {
+ var in: Option[InputStream] = None
+ var out: Option[OutputStream] = None
+
+ try {
+ in = Option(NativeLoader.getClass.getResourceAsStream(path))
+ if (in.isEmpty) return None
+
+ val file = File.createTempFile(path, "")
+ out = Some(new FileOutputStream(file))
+
+ val buffer = new Array[Byte](BufferSize)
+ var length = -1;
+ do {
+ length = in.get.read(buffer)
+ if (length != -1) out.get.write(buffer, 0, length)
+ } while (length != -1)
+
+ Some(file)
+ } finally {
+ in.foreach(_.close)
+ out.foreach(_.close)
+ }
+ }
+
+ private def loadError(msg: String): Nothing = throw new UnsatisfiedLinkError(
+ "Error during native library extraction " +
+ "(this can happen if your platform is not supported): " +
+ msg
+ )
+
+ def fullLibraryPath(libraryPath: String, platform: Platform) = {
+ libraryPath + "/native/" + platform.id + "/" + LibraryName
+ }
+
+ private def loadFromJar(libraryPath: String): Unit = {
+ val platform = Platform.current.getOrElse{
+ loadError("Cannot determine current platform.")
+ }
+
+ val resource = fullLibraryPath(libraryPath, platform)
+
+ val file = extract(resource) getOrElse loadError(
+ s"Shared library $resource not found."
+ )
+ System.load(file.getAbsolutePath())
+ }
+
+ /**
+ * Load a native library from the available library path or fall back
+ * to extracting and loading a native library from available resources.
+ */
+ def load(library: String, libraryPath: String): Unit = try {
+ System.loadLibrary(library)
+ } catch {
+ case ex: UnsatisfiedLinkError => loadFromJar(libraryPath)
+ }
+
+}
diff --git a/jni-library/src/main/scala/ch/jodersky/jni/Platform.scala b/jni-library/src/main/scala/ch/jodersky/jni/Platform.scala
new file mode 100644
index 0000000..db1662d
--- /dev/null
+++ b/jni-library/src/main/scala/ch/jodersky/jni/Platform.scala
@@ -0,0 +1,50 @@
+package ch.jodersky.jni
+
+import java.io.IOException
+import scala.sys.process.Process
+
+/**
+ * A platform is the representation of an architecture-kernel combination.
+ * It is a somewhat ambigous concept defined as the set of all system configurations
+ * capable of running a native binary.
+ */
+case class Platform private (arch: String, kernel: String) {
+
+ /**
+ * String representation of this platform. It is inspired by Autotools' platform
+ * triplet, without the vendor field.
+ */
+ def id = arch + "-" + kernel
+
+}
+
+object Platform {
+
+ final val Unknown = Platform("unknown", "unknown")
+
+ /** Create a platform with spaces stripped and case normalized. */
+ def normalized(arch: String, kernel: String): Platform = {
+ def normalize(str: String) = str.toLowerCase.filter(!_.isWhitespace)
+ Platform(normalize(arch), normalize(kernel))
+ }
+
+ /** Run 'uname' to determine current platform. Returns None if uname does not exist. */
+ def uname: Option[Platform] = {
+ val lineOpt = try {
+ Some(Process("uname -sm").lines.head)
+ } catch {
+ case _: IOException => None
+ }
+ lineOpt.map { line =>
+ val parts = line.split(" ")
+ if (parts.length != 2) {
+ sys.error("Could not determine platform: 'uname -sm' returned unexpected string: " + line)
+ } else {
+ Platform.normalized(parts(1), parts(0))
+ }
+ }
+ }
+
+ def current = uname
+
+}