aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Odersky <jakob@odersky.com>2016-12-01 00:04:50 -0800
committerJakob Odersky <jakob@odersky.com>2016-12-01 00:04:50 -0800
commitf58311e23153fdba2b950af42bf78fdb9719dc4f (patch)
tree17e7acab070df5385eb2ff8f94e29ebb4e83ca79
parentbbf3b0364a8a62cd2c5feb20728e0f7f0bd3f776 (diff)
downloadsbt-jni-meta.tar.gz
sbt-jni-meta.tar.bz2
sbt-jni-meta.zip
Implement basic build with scala metameta
-rw-r--r--build.sbt15
-rw-r--r--macros/src/main/scala/ch/jodersky/jni/annotations.scala103
-rw-r--r--plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniLoad.scala8
3 files changed, 112 insertions, 14 deletions
diff --git a/build.sbt b/build.sbt
index 30fed7c..43e5de2 100644
--- a/build.sbt
+++ b/build.sbt
@@ -1,8 +1,8 @@
-val scalaVersions = Seq("2.12.0", "2.11.8", "2.10.6")
-val macrosParadiseVersion = "2.1.0"
+val scalaVersions = Seq("2.11.8", "2.12.0", "2.10.6")
+val macrosParadiseVersion = "3.0.0.132"
// version is derived from latest git tag
-version in ThisBuild := ("git describe --always --dirty --match v[0-9].*" !!).tail.trim
+version in ThisBuild := ("git describe --always --dirty=-SNAPSHOT --match v[0-9].*" !!).tail.trim
organization in ThisBuild := "ch.jodersky"
scalacOptions in ThisBuild ++= Seq(
"-deprecation",
@@ -28,10 +28,11 @@ lazy val macros = (project in file("macros"))
name := "sbt-jni-macros",
scalaVersion := scalaVersions.head,
crossScalaVersions := scalaVersions,
- addCompilerPlugin("org.scalamacros" % "paradise" % macrosParadiseVersion cross CrossVersion.full),
- libraryDependencies += "org.typelevel" %% "macro-compat" % "1.1.1",
- libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value % Provided,
- libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value
+ resolvers += Resolver.url(
+ "scalameta",
+ url("http://dl.bintray.com/scalameta/maven"))(Resolver.ivyStylePatterns),
+ addCompilerPlugin("org.scalameta" % "paradise" % macrosParadiseVersion cross CrossVersion.full),
+ libraryDependencies += "org.scalameta" %% "scalameta" % "1.3.0"
)
lazy val plugin = (project in file("plugin"))
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()
+ }"""
+
}
diff --git a/plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniLoad.scala b/plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniLoad.scala
index 9f88dbb..47bf9be 100644
--- a/plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniLoad.scala
+++ b/plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniLoad.scala
@@ -12,12 +12,14 @@ object JniLoad extends AutoPlugin {
lazy val settings: Seq[Setting[_]] = Seq(
// Macro Paradise plugin and dependencies are needed to expand annotation macros.
// Once expanded however, downstream projects don't need these dependencies anymore
- // (hence the "Provided" configuration).
+ // (hence the "Provided" configurations).
+ resolvers += Resolver.url("scalameta",
+ url("http://dl.bintray.com/scalameta/maven"))(Resolver.ivyStylePatterns),
addCompilerPlugin(
- "org.scalamacros" % "paradise" % ProjectVersion.MacrosParadise cross CrossVersion.full
+ "org.scalameta" % "paradise" % ProjectVersion.MacrosParadise cross CrossVersion.full
),
resolvers += Resolver.jcenterRepo,
- libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided,
+ //libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided,
libraryDependencies += "ch.jodersky" %% "sbt-jni-macros" % ProjectVersion.Macros % Provided
)