aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Odersky <jodersky@gmail.com>2016-01-11 23:11:28 -0800
committerJakob Odersky <jodersky@gmail.com>2016-01-11 23:11:28 -0800
commit1cdbc2d2e04c7ab34d2e5af07567b37c60be571f (patch)
tree6692fcaf7ec11cbcf1a80b941b9b40094e83db3b
parentc85463d30f73a43eb5275c504657a5117c126ca4 (diff)
downloadsbt-jni-1cdbc2d2e04c7ab34d2e5af07567b37c60be571f.tar.gz
sbt-jni-1cdbc2d2e04c7ab34d2e5af07567b37c60be571f.tar.bz2
sbt-jni-1cdbc2d2e04c7ab34d2e5af07567b37c60be571f.zip
WIP divide plugin
-rw-r--r--jni-library/src/main/scala/ch/jodersky/jni/NativeLoader.scala6
-rw-r--r--jni-library/src/main/scala/ch/jodersky/jni/Platform.scala6
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniJvm.scala21
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniNative.scala18
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniPackager.scala110
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniPlugin.scala99
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/util/ByteCode.scala4
-rw-r--r--project/Build.scala2
8 files changed, 247 insertions, 19 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
index 261e52b..c806464 100644
--- a/jni-library/src/main/scala/ch/jodersky/jni/NativeLoader.scala
+++ b/jni-library/src/main/scala/ch/jodersky/jni/NativeLoader.scala
@@ -45,11 +45,11 @@ object NativeLoader {
)
/**
- * Gets the absolute, full path of a resource on the classpath, given a libraryPath
+ * Gets the absolute, full path of a native library on the classpath, given a libraryPath
* and platform.
*/
def fullLibraryPath(libraryPath: String, platform: Platform) = {
- libraryPath + "/native/" + platform.id + "/" + LibraryName
+ libraryPath + "/" + platform.id + "/" + LibraryName
}
private def loadFromJar(libraryPath: String): Unit = {
@@ -66,7 +66,7 @@ object NativeLoader {
}
/**
- * Loads a native library from the available library path or fall back
+ * Loads a native library from the available library path or falls back
* to extracting and loading a native library from available resources.
*/
def load(libraryPath: String, library: String): Unit = try {
diff --git a/jni-library/src/main/scala/ch/jodersky/jni/Platform.scala b/jni-library/src/main/scala/ch/jodersky/jni/Platform.scala
index cff9e95..1ece9e3 100644
--- a/jni-library/src/main/scala/ch/jodersky/jni/Platform.scala
+++ b/jni-library/src/main/scala/ch/jodersky/jni/Platform.scala
@@ -49,4 +49,10 @@ object Platform {
/** Determines platform the current JVM is running on. */
def current = uname
+ /** Parse an id to a platform. */
+ def fromId(id: String) = {
+ val (arch, dashKernel) = id.span(_ != '-')
+ Platform(arch, dashKernel.drop(1))
+ }
+
}
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniJvm.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniJvm.scala
index 292e1fa..789eabf 100644
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniJvm.scala
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniJvm.scala
@@ -24,8 +24,9 @@ object JniJvm extends AutoPlugin {
lazy val mainSettings: Seq[Setting[_]] = Seq(
javahClasses in javah := {
- val classFiles: Set[File] = compile.value.relations.allProducts.toSet
- val nativeClasses = classFiles.flatMap { file =>
+ val compiled: inc.Analysis = (compile in Compile).value
+ val classFiles: Set[File] = compiled.relations.allProducts.toSet
+ val nativeClasses = classFiles flatMap { file =>
ByteCode.natives(file)
}
nativeClasses
@@ -33,11 +34,17 @@ object JniJvm extends AutoPlugin {
target in javah := target.value / "include",
- fullClasspath in javah := (fullClasspath in Compile).value,
-
javah := {
val out = (target in javah).value
- val jcp: Seq[File] = (fullClasspath in javah).value.map(_.data)
+ val jcp: Seq[File] = {
+ (compile in Compile).value
+ //FIXME: a cleaner approach that would also call compile would
+ //be to use `fullClasspath`, this however results in generating resources
+ //which in turn might require header files generated by javah. Hence
+ //to avoid a cyclic dependency `compile` is called first and the
+ //class directory returned.
+ Seq((classDirectory in Compile).value)
+ }
val cp = jcp.mkString(sys.props("path.separator"))
val log = streams.value.log
@@ -62,9 +69,9 @@ object JniJvm extends AutoPlugin {
lazy val clientSettings = Seq(
//enable enhanced native library extraction
libraryDependencies += "ch.jodersky" %% "jni-library" % Version.PluginVersion,
- fork in run := true //fork new JVM as native libraries can only be loaded once
+ fork in run := true //fork new JVM, since native libraries can only be loaded once
)
- override lazy val projectSettings = inConfig(Compile)(mainSettings) ++ clientSettings
+ override lazy val projectSettings = mainSettings ++ clientSettings
}
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniNative.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniNative.scala
index 90f7cc8..9cfa2af 100644
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniNative.scala
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniNative.scala
@@ -21,7 +21,7 @@ object JniNative extends AutoPlugin {
}
import autoImport._
- lazy val settings: Seq[Setting[_]] = Seq(
+ lazy val mainSettings: Seq[Setting[_]] = Seq(
sourceDirectory in jni := baseDirectory.value / "src",
@@ -86,11 +86,15 @@ object JniNative extends AutoPlugin {
jniLibraryPath in jni := {
"/" + organization.value.replaceAll("\\.", "/") + "/" + name.value
- },
+ }
+
+ )
- unmanagedResourceDirectories += baseDirectory.value / "lib_native",
+ lazy val resourceSettings: Seq[Setting[_]] = Seq(
- resourceGenerators += Def.task {
+ unmanagedResourceDirectories in Compile += baseDirectory.value / "lib_native",
+
+ resourceGenerators in Compile += Def.task {
//build native library
val library = jni.value
@@ -109,8 +113,10 @@ object JniNative extends AutoPlugin {
)
- override lazy val projectSettings = inConfig(Compile)(settings) ++ Seq(
- //don't scala version to native jars
+
+
+ override lazy val projectSettings = mainSettings ++ resourceSettings ++ Seq(
+ //don't add scala version to native jars
crossPaths := false
)
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniPackager.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniPackager.scala
new file mode 100644
index 0000000..56322ad
--- /dev/null
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniPackager.scala
@@ -0,0 +1,110 @@
+package ch.jodersky.sbt.jni
+
+import build._
+import ch.jodersky.jni.{NativeLoader, Platform}
+import sbt._
+import sbt.Keys._
+
+object JniPackager extends AutoPlugin {
+
+ override def requires = Jni
+ override def trigger = allRequirements
+
+ object autoImport {
+
+ //Main task, inspect this first
+ val packageNative = taskKey[File]("Packages native libraries in a jar.")
+
+ val nativeLibraryPath = settingKey[String](
+ "String that is prepended to the path of a native library when packaged.")
+
+ val enableNativeCompilation = settingKey[Boolean](
+ "Determines if native compilation is enabled in a scoped key (typically for packaging).")
+
+ val unmanagedNativeDirectories = settingKey[Seq[File]](
+ """|Unmanaged directories containing native libraries. The libraries must be regular files
+ |contained in a subdirectory corresponding to a platform.""".stripMargin)
+
+ val unmanagedNativeLibraries = taskKey[Map[Platform, File]]("")
+
+ }
+ import autoImport._
+ import Jni.autoImport._
+
+ lazy val settings: Seq[Setting[_]] = Seq(
+
+ nativeLibraryPath := {
+ val orgPath = organization.value.replaceAll("\\.", "/")
+ s"/${orgPath}/${name.value}/native"
+ },
+
+ //Make NativeLoader available to project
+ libraryDependencies += "ch.jodersky" %% "jni-library" % Version.PluginVersion,
+
+ name in packageNative := {
+ name.value + "-native-" + version.value + ".jar"
+ },
+
+ enableNativeCompilation in packageNative := true,
+
+ unmanagedNativeDirectories := Seq(baseDirectory.value / "lib_native"),
+
+ unmanagedNativeLibraries := {
+ val dirs: Seq[File] = unmanagedNativeDirectories.value
+ val seq: Seq[(Platform, File)] = for (
+ dir <- dirs;
+ platformDir <- dir.listFiles();
+ if platformDir.isDirectory;
+ platform = Platform.fromId(platformDir.name);
+ library <- platformDir.listFiles();
+ if library.isFile
+ ) yield {
+ platform -> library.getCanonicalFile()
+ }
+
+ seq.toMap
+ },
+
+ packageNative := {
+ val log = streams.value.log
+
+ val unmanagedMappings: Seq[(File, String)] = {
+ unmanagedNativeLibraries.value.toSeq.map{ case (platform, file) =>
+ file -> NativeLoader.fullLibraryPath(
+ (nativeLibraryPath in Compile).value,
+ platform)
+ }
+ }
+
+ val enableManaged = (enableNativeCompilation in packageNative).value
+
+ val managedMappings: Seq[(File, String)] = if (enableManaged) {
+ val library: File = (nativeCompile in Compile).value
+ val path: String = NativeLoader.fullLibraryPath(
+ (nativeLibraryPath in Compile).value,
+ (nativePlatform in Compile).value
+ )
+ Seq(library -> path)
+ } else {
+ Seq()
+ }
+
+ val mappings = unmanagedMappings ++ managedMappings
+ val manifest = new java.util.jar.Manifest
+ val jar: File = (target in nativeCompile).value / (name in packageNative).value
+
+ Package.makeJar(mappings, jar, manifest, log)
+ jar
+ },
+
+ //Add native jar to runtime classpath. Note that it is not added as a resource (this would cause
+ //the native library to included in the main jar).
+ unmanagedClasspath in Runtime += Attributed.blank(packageNative.value),
+
+ fork in run := true //fork new JVM, since native libraries can only be loaded once
+
+ )
+ override lazy val projectSettings = (settings)
+
+
+}
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniPlugin.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniPlugin.scala
new file mode 100644
index 0000000..2b76b7a
--- /dev/null
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniPlugin.scala
@@ -0,0 +1,99 @@
+package ch.jodersky.sbt.jni
+
+import build._
+import ch.jodersky.jni.{NativeLoader, Platform}
+import sbt._
+import sbt.Keys._
+
+object Jni extends AutoPlugin {
+
+ override def requires = plugins.JvmPlugin
+
+ object autoImport {
+
+ //Main task, inspect this first
+ val nativeCompile = taskKey[File]("Builds a native library (by calling the native build tool).")
+
+ val nativePlatform = settingKey[Platform]("Platform of the system this build is running on.")
+
+ val nativeBuildTools = taskKey[Seq[BuildTool]](
+ "A collection of build tools that are tested to determine the current build environment")
+ val nativeBuildTool = taskKey[BuildTool](
+ "The build tool to be used when building a native library.")
+
+
+ }
+ import autoImport._
+
+ lazy val mainSettings: Seq[Setting[_]] = Seq(
+
+ nativePlatform := Platform.current.getOrElse{
+ sLog.value.warn("Warning: cannot determine platform! It will be set to 'unknown'.")
+ Platform.Unknown
+ },
+
+ sourceDirectory in nativeCompile := sourceDirectory.value / "native",
+
+ target in nativeCompile := target.value / "native" / (nativePlatform).value.id,
+
+ nativeBuildTools := Seq(CMake, Autotools),
+
+ nativeBuildTool := {
+ val tools = nativeBuildTools.value
+
+ val src = (sourceDirectory in nativeCompile).value
+
+ val tool = if (src.exists && src.isDirectory) {
+ tools.find(t => t detect src)
+ } else {
+ None
+ }
+ tool.getOrElse(
+ sys.error("No supported native build tool detected. " +
+ s"Check that the setting 'sourceDirectory in nativeCompile' (currently $src) " +
+ "points to a valid directory. Supported build tools are: " +
+ tools.map(_.name).mkString(","))
+ )
+
+ },
+
+ clean in nativeCompile := {
+ val log = streams.value.log
+
+ log.debug("Cleaning native build")
+ try {
+ val tool = nativeBuildTool.value
+ tool.api.clean(
+ (sourceDirectory in nativeCompile).value,
+ log
+ )
+ } catch {
+ case ex: Exception =>
+ log.debug(s"Native cleaning failed: $ex")
+ }
+
+ },
+
+ nativeCompile := {
+ val tool = nativeBuildTool.value
+ val src = (sourceDirectory in nativeCompile).value
+ val buildDir = (target in nativeCompile).value / "build"
+ val targetDir = (target in nativeCompile).value / "lib"
+ val log = streams.value.log
+
+ IO.createDirectory(buildDir)
+ IO.createDirectory(targetDir)
+
+ tool.api.library(src, buildDir, targetDir, log)
+ },
+
+ // change clean task to also clean native sources
+ clean := {
+ (clean in nativeCompile).value
+ clean.value
+ }
+
+ )
+
+ override lazy val projectSettings = inConfig(Compile)(mainSettings)
+}
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/util/ByteCode.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/util/ByteCode.scala
index 308a077..b3c6fa3 100644
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/util/ByteCode.scala
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/util/ByteCode.scala
@@ -14,7 +14,7 @@ object ByteCode {
val _nativeClasses = new HashSet[String]
def nativeClasses = _nativeClasses.toSet
- private var fullyQualifiedName: String = ""
+ private var fullyQualifiedName: String = ""
override def visit(version: Int, access: Int, name: String, signature: String,
superName: String, interfaces: Array[String]): Unit = {
@@ -30,7 +30,7 @@ object ByteCode {
_nativeClasses += fullyQualifiedName
}
- null //do not visit method further
+ null //return null, do not visit method further
}
}
diff --git a/project/Build.scala b/project/Build.scala
index 4991d9e..9c9b917 100644
--- a/project/Build.scala
+++ b/project/Build.scala
@@ -6,7 +6,7 @@ object JniBuild extends Build {
val scalaVersions = List("2.11.7", "2.12.0-M3", "2.10.5")
val commonSettings = Seq(
- version := "0.2-SNAPSHOT",
+ version := "0.3-SNAPSHOT",
organization := "ch.jodersky",
scalacOptions ++= Seq("-deprecation", "-feature")
)