aboutsummaryrefslogtreecommitdiff
path: root/flow-main/src/main/scala/com/github/jodersky/flow/internal
diff options
context:
space:
mode:
Diffstat (limited to 'flow-main/src/main/scala/com/github/jodersky/flow/internal')
-rw-r--r--flow-main/src/main/scala/com/github/jodersky/flow/internal/NativeLoader.scala81
1 files changed, 65 insertions, 16 deletions
diff --git a/flow-main/src/main/scala/com/github/jodersky/flow/internal/NativeLoader.scala b/flow-main/src/main/scala/com/github/jodersky/flow/internal/NativeLoader.scala
index a5fdc40..4254c5c 100644
--- a/flow-main/src/main/scala/com/github/jodersky/flow/internal/NativeLoader.scala
+++ b/flow-main/src/main/scala/com/github/jodersky/flow/internal/NativeLoader.scala
@@ -1,19 +1,48 @@
package com.github.jodersky.flow
package internal
-import java.io.{ File, FileOutputStream, InputStream, OutputStream }
+import java.io.{File, FileOutputStream, InputStream, OutputStream}
+import scala.io.Source
+import scala.sys.process.Process
+import scala.util.Try
/** Handles loading of the current platform's native library for flow. */
object NativeLoader {
- private final val BufferSize = 4096
+ /** A platform is the representation of an os-architecture combination */
+ case class Platform(kernel: String, arch: String) {
+ val id = kernel + "-" + arch
+ }
+
+ object Platform {
+
+ /** Create a platform with spaces stripped and case normalized. */
+ def normalize(kernel: String, arch: String) = Platform(
+ kernel.toLowerCase.filter(!_.isWhitespace),
+ arch
+ )
+
+ /** Run 'uname' to determine current platform. Returns None if uname does not exist. */
+ lazy val uname: Option[Platform] = {
+ val lines = Try { Process("uname -sm").lineStream.head }.toOption
+ lines.map { line =>
+ val parts = line.split(" ")
+ if (parts.length != 2) {
+ sys.error("Could not determine platform: 'uname -sm' returned unexpected string: " + line)
+ } else {
+ Platform.normalize(parts(0), parts(1))
+ }
+ }
+ }
+
+ }
- private def os = System.getProperty("os.name").toLowerCase.replaceAll("\\s", "")
+ private final val BufferSize = 4096
- private def arch = System.getProperty("os.arch").toLowerCase
+ private final val LibraryManifest = "library"
/** Extract a resource from this class loader to a temporary file. */
- private def extract(path: String, prefix: String): Option[File] = {
+ private def extract(path: String): Option[File] = {
var in: Option[InputStream] = None
var out: Option[OutputStream] = None
@@ -21,7 +50,7 @@ object NativeLoader {
in = Option(NativeLoader.getClass.getResourceAsStream(path))
if (in.isEmpty) return None
- val file = File.createTempFile(prefix, "")
+ val file = File.createTempFile(path, "")
out = Some(new FileOutputStream(file))
val buffer = new Array[Byte](BufferSize)
@@ -38,21 +67,41 @@ object NativeLoader {
}
}
- private def loadFromJar(library: String) = {
- val fqlib = System.mapLibraryName(library) //fully qualified library name
- val path = s"/native/${os}-${arch}/${fqlib}"
- extract(path, fqlib) match {
- case Some(file) => System.load(file.getAbsolutePath)
- case None => throw new UnsatisfiedLinkError("Cannot extract flow's native library, " +
- "the native library does not exist for your specific architecture/OS combination." +
- "Could not find " + path + ".")
+ private def loadError(msg: String): Nothing = throw new UnsatisfiedLinkError(
+ "Error during native library extraction " +
+ "(this can happen if your platform is not supported by flow): " +
+ msg
+ )
+
+ private def loadFromJar(libraryPrefix: String): Unit = {
+ val platformDir: String = libraryPrefix + "/" + Platform.uname.map(_.id).getOrElse {
+ loadError("Cannot determine current platform.")
+ }
+
+ val manifest: File = {
+ val path = platformDir + "/" + LibraryManifest
+ extract(path) getOrElse {
+ loadError(s"Manifest file $path does not exist.")
+ }
+ }
+
+ Source.fromFile(manifest, "utf-8").getLines foreach { libname =>
+ val path = platformDir + "/" + libname
+ val lib = extract(path) getOrElse loadError(
+ s"Library $path not found."
+ )
+ System.load(lib.getAbsolutePath())
}
}
- def load(library: String) = try {
+ /**
+ * 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, libraryPrefix: String): Unit = try {
System.loadLibrary(library)
} catch {
- case ex: UnsatisfiedLinkError => loadFromJar(library)
+ case ex: UnsatisfiedLinkError => loadFromJar(libraryPrefix)
}
}