From f58311e23153fdba2b950af42bf78fdb9719dc4f Mon Sep 17 00:00:00 2001 From: Jakob Odersky Date: Thu, 1 Dec 2016 00:04:50 -0800 Subject: Implement basic build with scala meta --- .../main/scala/ch/jodersky/jni/annotations.scala | 103 ++++++++++++++++++++- 1 file changed, 99 insertions(+), 4 deletions(-) (limited to 'macros/src/main/scala/ch/jodersky/jni/annotations.scala') diff --git a/macros/src/main/scala/ch/jodersky/jni/annotations.scala b/macros/src/main/scala/ch/jodersky/jni/annotations.scala index f2ace96..6805ebf 100644 --- a/macros/src/main/scala/ch/jodersky/jni/annotations.scala +++ b/macros/src/main/scala/ch/jodersky/jni/annotations.scala @@ -1,12 +1,14 @@ package ch.jodersky.jni -import macrocompat.bundle +//import macrocompat.bundle -import scala.language.experimental.macros -import scala.reflect.macros.whitebox.Context +//import scala.language.experimental.macros +//import scala.reflect.macros.whitebox.Context +import scala.collection.immutable.Seq import scala.annotation.StaticAnnotation import scala.annotation.compileTimeOnly +/* @bundle class nativeLoaderMacro(val c: Context) { @@ -106,8 +108,101 @@ class nativeLoaderMacro(val c: Context) { } } + */ + +import scala.meta._ @compileTimeOnly("Macro Paradise must be enabled to apply annotation.") class nativeLoader(nativeLibrary: String) extends StaticAnnotation { - def macroTransform(annottees: Any*): Any = macro nativeLoaderMacro.impl + + inline def apply(defn: Any): Any = meta { + val nativeLibrary = this match { + case q"new $_(${Lit(arg: String)})" => arg + case _ => throw new IllegalArgumentException("Native library must be a constant") + } + + defn match { + + case Term.Block(Seq( + cls: Defn.Class, + companion: Defn.Object)) => + + val newStats: Seq[Stat] = MacroUtil.staticInitializer(nativeLibrary) +: + companion.templ.stats.getOrElse(Nil) + + val newCompanion = companion.copy( + templ = companion.templ.copy(stats = Some(newStats))) + + Term.Block(Seq(cls, newCompanion)) + + case companion: Defn.Object => + val newStats: Seq[Stat] = MacroUtil.staticInitializer(nativeLibrary) +: + companion.templ.stats.getOrElse(Nil) + + val newCompanion = companion.copy( + templ = companion.templ.copy(stats = Some(newStats))) + + Term.Block(Seq(newCompanion)) + + case _ => + throw new IllegalArgumentException( + "nativeLoader can only be annotated to classes and singleton objects") + } + } +} + +object MacroUtil { + + def staticInitializer(nativeLibrary: String) = q"""{ + def loadPackaged(): Unit = { + import java.io.File + import java.nio.file.{Files, Path} + + val lib: String = System.mapLibraryName($nativeLibrary) + + val tmp: Path = Files.createTempDirectory("jni-") + val plat: String = { + val line = try { + scala.sys.process.Process("uname -sm").lines.head + } catch { + case ex: Exception => sys.error("Error running `uname` command") + } + val parts = line.split(" ") + if (parts.length != 2) { + sys.error("Could not determine platform: 'uname -sm' returned unexpected string: " + line) + } else { + val arch = parts(1).toLowerCase.replaceAll("\\s", "") + val kernel = parts(0).toLowerCase.replaceAll("\\s", "") + arch + "-" + kernel + } + } + + val resourcePath: String = "/native/" + plat + "/" + lib + val resourceStream = Option(this.getClass.getResourceAsStream(resourcePath)) match { + case Some(s) => s + case None => throw new UnsatisfiedLinkError( + "Native library " + lib + " (" + resourcePath + ") cannot be found on the classpath.") + } + + val extractedPath = tmp.resolve(lib) + + try { + Files.copy(resourceStream, extractedPath) + } catch { + case ex: Exception => throw new UnsatisfiedLinkError( + "Error while extracting native library: " + ex) + } + + System.load(extractedPath.toAbsolutePath.toString) + } + + def load(): Unit = try { + System.loadLibrary($nativeLibrary) + } catch { + case ex: UnsatisfiedLinkError => loadPackaged() + } + + load() + }""" + } -- cgit v1.2.3