diff options
author | Jakob Odersky <jodersky@gmail.com> | 2015-11-16 21:24:45 -0800 |
---|---|---|
committer | Jakob Odersky <jodersky@gmail.com> | 2015-11-16 23:02:14 -0800 |
commit | 9328cb2b61751472b6abe9c57502ecef79d3d069 (patch) | |
tree | def4f3f3cf6b652b035d5d7b3c08f72caccaddbd /flow-main/src/main/scala/com/github/jodersky/flow/internal | |
parent | c3b825b45753d8a82930d85cfc2abd81f30cb54a (diff) | |
download | akka-serial-9328cb2b61751472b6abe9c57502ecef79d3d069.tar.gz akka-serial-9328cb2b61751472b6abe9c57502ecef79d3d069.tar.bz2 akka-serial-9328cb2b61751472b6abe9c57502ecef79d3d069.zip |
Improved determination of platform and loading of native libraries.
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.scala | 81 |
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) } } |