aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Odersky <jodersky@gmail.com>2016-01-17 17:18:13 -0800
committerJakob Odersky <jodersky@gmail.com>2016-01-17 17:18:13 -0800
commit2b343f73fc5b7605b22af41c675c2afc86d2a447 (patch)
tree51087a72109c927c7be245367458bffb3ec48ae7
parent1cdbc2d2e04c7ab34d2e5af07567b37c60be571f (diff)
downloadsbt-jni-2b343f73fc5b7605b22af41c675c2afc86d2a447.tar.gz
sbt-jni-2b343f73fc5b7605b22af41c675c2afc86d2a447.tar.bz2
sbt-jni-2b343f73fc5b7605b22af41c675c2afc86d2a447.zip
Refactor to multi-plugin build
-rw-r--r--README.md113
-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/build/Autotools.scala5
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniJavah.scala (renamed from jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniJvm.scala)26
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniLoading.scala28
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniNative.scala (renamed from jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniPlugin.scala)19
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniNative.scala.old (renamed from jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniNative.scala)0
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniPackage.scala.old183
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniPackaging.scala113
-rw-r--r--samples/basic/basic-core/build.sbt1
-rw-r--r--samples/basic/basic-core/src/main/scala/ch/jodersky/jni/basic/Library.scala (renamed from samples/basic/basic-main/src/main/scala/ch/jodersky/jni/basic/Library.scala)0
-rw-r--r--samples/basic/basic-core/src/main/scala/ch/jodersky/jni/basic/Main.scala13
-rw-r--r--samples/basic/basic-main/src/main/scala/ch/jodersky/jni/basic/Main.scala13
-rw-r--r--samples/basic/build.sbt23
-rw-r--r--samples/basic/project/plugins.sbt2
15 files changed, 450 insertions, 199 deletions
diff --git a/README.md b/README.md
index 7f3c6c3..502dfa7 100644
--- a/README.md
+++ b/README.md
@@ -1,62 +1,97 @@
-# SBT-JNI
-Making JNI tolerable.
-
-Work in progress...
+[Work in progress]
-## Overview of JNI
-Java Native Interface (JNI), is a standard interface to allow programs written in a JVM language to interact with native code. Creating a program that uses JNI is usually a multi-step process:
-
-1. The JVM side of the program is written (in Java or Scala for example) and interfacing methods declared as native.
-2. JVM sources are compiled.
-3. The program "javah" is run to generate C header files for classes containing native declarations.
-4. These header files may be included from actual native source files that implement the native methods.
-5. Native sources are compiled into a native library.
+# SBT-JNI
-Running the program also requires special steps:
+A suite of plugins for simplifying creation and distribution of JNI programs.
-1. Supply the native library to the program's library path.
-2. Run the program, making sure to load the native library (through `System.load()`) before calling any native methods.
+## Motivation
+Java Native Interface (JNI), is a standard interface to allow programs written in a JVM language to interact with native code. Such programs can be divided into two logical parts: the JVM part, consisting of source files that will generate java bytecode (e.g. Scala or Java), and the native part, consisting of source files that will be compiled down to machine-native code (e.g. C, C++ or assembly). Interaction between the two parts is managed by header files generated through the `javah` utility.
-Finally, since platform independence is lost with native libraries, publishing an application or library becomes harder:
+Using native code can be beneficial in some situations, it can for example provide performance gains or otherwise infeasable features such as system-level interaction with peripherals. However, it also adds a few complexities, notably:
-1. Native libraries must be compiled and made available for all supported platforms.
-2. Loading the correct library on a given platform has to be managed somehow.
+- Compilation: the project is divided into two parts, each of which require separate compilation. Furthermore, header generation with `javah` has to be dealt with.
+- Portability: native binaries only run on the platform on which they were compiled.
+- Distribution: native binaries must be made available and packaged for every supported platform.
-All in all, using JNI impairs platform-independence and thus a considerable amount of the JVM's advantages regarding ease of development and distribution.
+The second point, portability, is inherent to JNI and thus unavoidable. However the first and last points can be greatly simplified with the help of build tools.
## Plugin Overview
-As described in the previous paragraph, using JNI adds a number of burdens to developing applications. The problems can be divided into two groups, both of which this plugin attempts to address:
+This project consists of 4 autoplugins that aim to solve the difficulties in compiling and distributing JNI programs. These plugins provide two functionalities (compiling and distributing) for two parts of a project (JVM and native):
-- Build problems, related to the manual work involved with synchronizing JVM and native sources and running applications locally.
-- Distribution problems, related to the way libraries and applications can be deployed.
+Plugins
+--------------|-----------------|-------------
+Functionality | JVM part | Native part
+--------------|-----------------|-------------
+Compiling | JniJavah | JniNative
+Packaging | JniLoading | JniPackaging
-[how the plugin addresses issues]
-[sbt wrapper for native build tools]
+A project using this suite of plugins must be divided into two sub-projects, corresponding to the two parts: one that will contain JVM sources and one that will contain native sources.
-## Usage
+The reason for dividing a project into two subprojects is two-fold: it enables flexible plugging of native sources and also integrates easily into the existing maven ecosystem. Adding the native binaries as additional artifacts has issues with scala versioning.
-Depend on plugin `project/plugins.sbt`:
+## Usage
+Add plugin dependency. In `project/plugins.sbt`:
```scala
-addSbtPlugin("ch.jodersky" % "sbt-jni" % "0.2")
+addSbtPlugin("ch.jodersky" % "sbt-jni" % "0.3")
```
-Create sbt project containing JVM sources:
-```scala
-lazy val main = Project("main", file("main)).enablePlugins(JniJvm).dependsOn(native % Runtime)
-```
+Define sub-projects for JVM and native sources. In `myproject/build.sbt`:
-Create sbt project containing native sources:
```scala
-lazy val native = Project("native", file("native")).dependsOn(JniNative)
+lazy val core = project in file("myproject-core") // contains regular jvm sources and @native methods
+lazy val native = project in file("myproject-native") // contains native sources
```
-### Keys
+Select plugins to enable on sub-projects:
-```
-javah
-jni
-```
+- In `myproject-core/build.sbt`:
+
+ ```scala
+ //enablePlugin(JniJavah) // this plugin is added to all JVM projects by default
+ enablePlugin(JniLoading)
+ ```
+
+- In `myproject-native/build.sbt`:
+
+ ```scala
+ enablePlugin(JniNative)
+ //enablePlugin(JniPackaging) // this plugin is added to all projects using JniNative by default
+ ```
+
+Note that some plugins are added by default. To disable their functionality (for example if you don't wish to package native libraries), add `disablePlugin(<plugin>)` to the corresponding build definition.
+
+
+## Plugin Details
+The following gives a detailed description about the various JNI plugins and the ways to customize their behaviour.
+
+### Javah
+*JVM sub-projects*
+
+#### Functionality
+Adds a `javah` task to generate header files for classes containing `@native` methods.
+
+#### Customization
+Change target to point to `include` directory of native sources: `target in javah := file("myproject-native") / "src" / "include"`
+
+
+### JniNative
+*Native sub-projects*
+
+#### Functionality
+Provides sbt tasks to call into a native build system such as Automake or CMake. Run `sbt nativeCompile` to compile native sources.
+
+#### Customization
+Change source directory to point to a directory containing the native build definition. Supported build tools are:
+
+- Automake
+- CMake
+
+*Make sure the native build configurations respect the arguments and output directories as specified in the [native build templates](templates)*
+
+### JniLoading
+*JVM sub-projects*
-### Common settings
+### JniPackaging
+*Native sub-projects*
## Examples
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
deleted file mode 100644
index 56322ad..0000000
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniPackager.scala
+++ /dev/null
@@ -1,110 +0,0 @@
-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/build/Autotools.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/Autotools.scala
index c1de1ea..960e66e 100644
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/Autotools.scala
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/Autotools.scala
@@ -16,11 +16,12 @@ object Autotools extends BuildTool {
val targetPath = target.getAbsolutePath
Process(
- s"${base.getAbsolutePath}/configure " +
+ s"${base.getAbsolutePath}/configure " +
s"--prefix=$targetPath " +
s"--libdir=$targetPath " +
"--disable-versioned-lib",
- build)
+ build
+ )
}
}
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniJvm.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniJavah.scala
index 789eabf..63e5b23 100644
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniJvm.scala
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniJavah.scala
@@ -1,12 +1,15 @@
package ch.jodersky.sbt.jni
+package plugins
import sbt._
import sbt.Keys._
import util.ByteCode
-object JniJvm extends AutoPlugin {
+/** Adds `javah` header-generation functionality to projects. */
+object JniJavah extends AutoPlugin {
override def requires = plugins.JvmPlugin
+ override def trigger = allRequirements
object autoImport {
@@ -34,22 +37,17 @@ object JniJvm extends AutoPlugin {
target in javah := target.value / "include",
+ fullClasspath in javah := fullClasspath.value,
+
javah := {
val out = (target in javah).value
- 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 jcp: Seq[File] = (fullClasspath in javah).value.map(_.data)
val cp = jcp.mkString(sys.props("path.separator"))
val log = streams.value.log
val classes = (javahClasses in javah).value
+ log.info("Headers will be generated to " + out.getAbsolutePath)
for (clazz <- classes) {
log.info("Generating header for " + clazz + "...")
val parts = Seq(
@@ -66,12 +64,6 @@ 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, since native libraries can only be loaded once
- )
-
- override lazy val projectSettings = mainSettings ++ clientSettings
+ override lazy val projectSettings = inConfig(Compile)(mainSettings)
}
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniLoading.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniLoading.scala
new file mode 100644
index 0000000..599bfa1
--- /dev/null
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniLoading.scala
@@ -0,0 +1,28 @@
+package ch.jodersky.sbt.jni
+package plugins
+
+import sbt._
+import sbt.Keys._
+import util.ByteCode
+
+/**
+ * Enables loading native libraries from the classpath, typically created
+ * from a project using JniPackaging.
+ */
+object JniLoading extends AutoPlugin {
+
+ override def requires = plugins.JvmPlugin
+
+ lazy val settings = Seq(
+
+ //enable enhanced native library extraction
+ libraryDependencies += "ch.jodersky" %% "jni-library" % Version.PluginVersion,
+
+ //fork new JVM, since native libraries can only be loaded once
+ fork in run := true
+
+ )
+
+ 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/plugins/JniNative.scala
index 2b76b7a..2ac179d 100644
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniPlugin.scala
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniNative.scala
@@ -1,13 +1,13 @@
package ch.jodersky.sbt.jni
+package plugins
import build._
import ch.jodersky.jni.{NativeLoader, Platform}
import sbt._
import sbt.Keys._
-object Jni extends AutoPlugin {
-
- override def requires = plugins.JvmPlugin
+/** Wraps a native build system in sbt tasks. */
+object JniNative extends AutoPlugin {
object autoImport {
@@ -17,17 +17,18 @@ object Jni extends AutoPlugin {
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")
+ "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.")
-
+ "The build tool to be used when building a native library."
+ )
}
import autoImport._
- lazy val mainSettings: Seq[Setting[_]] = Seq(
+ lazy val settings: Seq[Setting[_]] = Seq(
- nativePlatform := Platform.current.getOrElse{
+ nativePlatform := Platform.current.getOrElse {
sLog.value.warn("Warning: cannot determine platform! It will be set to 'unknown'.")
Platform.Unknown
},
@@ -95,5 +96,5 @@ object Jni extends AutoPlugin {
)
- override lazy val projectSettings = inConfig(Compile)(mainSettings)
+ override lazy val projectSettings = inConfig(Compile)(settings)
}
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniNative.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniNative.scala.old
index 9cfa2af..9cfa2af 100644
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniNative.scala
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniNative.scala.old
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniPackage.scala.old b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniPackage.scala.old
new file mode 100644
index 0000000..60980f5
--- /dev/null
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniPackage.scala.old
@@ -0,0 +1,183 @@
+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 packageNativeMappings = taskKey[Seq[(File, String)]]("")
+
+ 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]]("")
+
+ val managedNativeLibraries = 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 := 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
+ },
+
+ managedNativeLibraries := Def.taskDyn[Map[Platform, File]]{
+ val enableManaged = (enableNativeCompilation).value
+ if (enableManaged) Def.task {
+ val library: File = (nativeCompile in Compile).value
+ val platform = (nativePlatform in Compile).value
+ Map(platform -> library)
+ } else Def.task{
+ Map()
+ }
+ }.value,
+
+ resourceGenerators in Compile += Def.task {
+
+ val libraries: Seq[(Patform, File)] = (managedNativeLibraries.value ++ unmanagedNativeLibraries.value).toSeq
+
+ val resources: Seq[File] = for ((plat, file) <- libraries) yield {
+
+ //native library as a managed resource file
+ val resource = (resourceManaged in Compile).value /
+ NativeLoader.fullLibraryPath(
+ (nativeLibraryPath).value,
+ plat)
+
+ //copy native library to a managed resource (so it can also be loaded when not packaged as a jar)
+ IO.copyFile(file, resource)
+ resource
+ }
+ resources
+ }.taskValue,
+
+ //don't add scala version to native jars
+ crossPaths := false
+
+ )
+
+ override lazy val projectSettings = settings
+ /*
+ name in packageNative := name,
+
+
+ //this is file name of the artifact
+ artifactName in packageNative := { (sv: ScalaVersion, module: ModuleID, artifact: Artifact) =>
+ artifact.name + "-native." + module.revision + "." + artifact.extension
+ }
+
+ packageNative := {
+ val log = streams.value.log
+
+ val mappings: Seq[(File, String)] = {
+ val libs = unmanagedNativeLibraries.value.toSeq ++ managedNativeLibraries.value.toSeq
+ libs.map{ case (platform, file) =>
+ file -> NativeLoader.fullLibraryPath(
+ (nativeLibraryPath in Compile).value,
+ platform)
+ }
+ }
+
+ val manifest = new java.util.jar.Manifest
+ val namer = (artifactName in packageNative).value
+ val jar: File = (target in nativeCompile).value / namer(
+ scalaVersion,
+
+ (artifactName 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 new JVM when running locally, since native libraries can only be loaded once
+ //fork in run := true
+
+ /*
+ override lazy val projectSettings =
+ Defaults.packageTaskSettings(packageNative, packageNativeMappings) ++
+ addArtifact(artifact in packageNative, packageNative) ++
+ inConfig(Compile)(settings) ++ Seq(
+
+ packageNativeMappings := {
+ val libs = (unmanagedNativeLibraries in Compile).value.toSeq ++
+ (managedNativeLibraries in Compile).value.toSeq
+ libs.map{ case (platform, file) =>
+ file -> NativeLoader.fullLibraryPath(
+ (nativeLibraryPath in Compile).value,
+ platform)
+ }
+ },
+
+ artifactClassifier in packageNative := Some("native"),
+
+ //this is file name of the artifact
+ artifactName := { (sv: ScalaVersion, module: ModuleID, artifact: Artifact) =>
+ artifact.name + "-foso" + module.revision + "." + artifact.extension
+ },
+
+ artifact in packageNative := {
+ Artifact("foo", "jar", "jar", Some("native"), Nil, None, Map("platform" -> "all"))
+ },
+
+ crossVersion in packageNative := CrossVersion.Disabled
+// projectID in packageNative := { projectID.value.copy(crossVersion = CrossVersion.Disabled) }
+
+
+ )
+ */
+
+}
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniPackaging.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniPackaging.scala
new file mode 100644
index 0000000..6014086
--- /dev/null
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniPackaging.scala
@@ -0,0 +1,113 @@
+package ch.jodersky.sbt.jni
+package plugins
+
+import ch.jodersky.jni._
+import sbt._
+import sbt.Keys._
+
+/** Packages libraries built with JniNative. */
+object JniPackaging extends AutoPlugin {
+
+ //JvmPlugin is required or else resource generators will be overriden
+ override def requires = JniNative && plugins.JvmPlugin
+ override def trigger = allRequirements
+
+ object autoImport {
+
+ val nativeLibraryPath = settingKey[String](
+ "String that is prepended to the path of a native library when packaged. This value should be " +
+ "passed to `NativeLoader.load()`"
+ )
+
+ val enableNativeCompilation = settingKey[Boolean](
+ "Determines if native compilation is enabled. If disabled, only pre-compiled libraries in " +
+ "`unmanagedNativeDirectories` will be packaged."
+ )
+
+ val unmanagedNativeDirectories = settingKey[Seq[File]](
+ "Unmanaged directories containing native libraries. The libraries must be regular files " +
+ "contained in a subdirectory corresponding to a platform. For example " +
+ "`<unamagedNativeDirectory>/x86_64-linux/libfoo.so` is an unmanaged library for machines having " +
+ "the x86_64 architecture and running the Linux kernel."
+ )
+
+ val unmanagedNativeLibraries = taskKey[Map[Platform, File]](
+ "Reads `unmanagedNativeDirectories` and maps platforms to library files specified theirin."
+ )
+
+ val managedNativeLibraries = taskKey[Map[Platform, File]](
+ "Maps locally built, platform-dependant libraries."
+ )
+
+ }
+ import autoImport._
+ import JniNative.autoImport._
+
+ lazy val settings: Seq[Setting[_]] = Seq(
+
+ nativeLibraryPath := {
+ val orgPath = organization.value.replaceAll("\\.", "/")
+ s"/${orgPath}/${name.value}/native"
+ },
+
+ enableNativeCompilation := true,
+
+ unmanagedNativeDirectories := Seq(baseDirectory.value / "lib_native"),
+
+ unmanagedNativeLibraries := {
+ val dirs: Seq[File] = unmanagedNativeDirectories.value
+ val seq: Seq[(Platform, File)] = for (
+ dir <- dirs;
+ if (dir.exists && dir.isDirectory);
+ platformDir <- dir.listFiles();
+ if platformDir.isDirectory;
+ platform = Platform.fromId(platformDir.name);
+ library <- platformDir.listFiles();
+ if library.isFile
+ ) yield {
+ platform -> library.getCanonicalFile()
+ }
+
+ seq.toMap
+ },
+
+ managedNativeLibraries := Def.taskDyn[Map[Platform, File]] {
+ val enableManaged = (enableNativeCompilation).value
+ if (enableManaged) Def.task {
+ val library: File = nativeCompile.value
+ val platform = nativePlatform.value
+ Map(platform -> library)
+ }
+ else Def.task {
+ Map()
+ }
+ }.value,
+
+ resourceGenerators += Def.task {
+
+ val libraries: Seq[(Platform, File)] = (managedNativeLibraries.value ++ unmanagedNativeLibraries.value).toSeq
+
+ val resources: Seq[File] = for ((plat, file) <- libraries) yield {
+
+ //native library as a managed resource file
+ val resource = resourceManaged.value /
+ NativeLoader.fullLibraryPath(
+ (nativeLibraryPath).value,
+ plat
+ )
+
+ //copy native library to a managed resource (so it can also be loaded when not packaged as a jar)
+ IO.copyFile(file, resource)
+ resource
+ }
+ resources
+ }.taskValue,
+
+ //don't add scala version to native jars
+ crossPaths in Global := false
+
+ )
+
+ override lazy val projectSettings = inConfig(Compile)(settings)
+
+}
diff --git a/samples/basic/basic-core/build.sbt b/samples/basic/basic-core/build.sbt
new file mode 100644
index 0000000..cd51bb3
--- /dev/null
+++ b/samples/basic/basic-core/build.sbt
@@ -0,0 +1 @@
+//enablePlugins(JniLoader)
diff --git a/samples/basic/basic-main/src/main/scala/ch/jodersky/jni/basic/Library.scala b/samples/basic/basic-core/src/main/scala/ch/jodersky/jni/basic/Library.scala
index efe3018..efe3018 100644
--- a/samples/basic/basic-main/src/main/scala/ch/jodersky/jni/basic/Library.scala
+++ b/samples/basic/basic-core/src/main/scala/ch/jodersky/jni/basic/Library.scala
diff --git a/samples/basic/basic-core/src/main/scala/ch/jodersky/jni/basic/Main.scala b/samples/basic/basic-core/src/main/scala/ch/jodersky/jni/basic/Main.scala
new file mode 100644
index 0000000..2aab1ed
--- /dev/null
+++ b/samples/basic/basic-core/src/main/scala/ch/jodersky/jni/basic/Main.scala
@@ -0,0 +1,13 @@
+package ch.jodersky.jni.basic
+
+import ch.jodersky.jni.NativeLoader
+
+object Main {
+
+ def main(args: Array[String]): Unit = {
+ NativeLoader.load("/ch/jodersky/jni/basic/native", "demo1")
+ val result: Int = Library.print("Hello world!\n")
+ println("Returned: " + result)
+ }
+
+}
diff --git a/samples/basic/basic-main/src/main/scala/ch/jodersky/jni/basic/Main.scala b/samples/basic/basic-main/src/main/scala/ch/jodersky/jni/basic/Main.scala
deleted file mode 100644
index b46f0d1..0000000
--- a/samples/basic/basic-main/src/main/scala/ch/jodersky/jni/basic/Main.scala
+++ /dev/null
@@ -1,13 +0,0 @@
-package ch.jodersky.jni.basic
-
-import ch.jodersky.jni.NativeLoader
-
-object Main {
-
- def main(args: Array[String]): Unit = {
- NativeLoader.load("/ch/jodersky/jni/basic", "demo1")
- val r = Library.print("Hello world!\n")
- println("Returned: " + r)
- }
-
-}
diff --git a/samples/basic/build.sbt b/samples/basic/build.sbt
index cceeb16..7f5bedd 100644
--- a/samples/basic/build.sbt
+++ b/samples/basic/build.sbt
@@ -3,22 +3,29 @@ val commonSettings = Seq(
organization := "ch.jodersky"
)
-lazy val main = Project(
- id = "sample-basic-main",
- base = file("basic-main"),
+lazy val root = Project(
+ id = "root",
+ base = file("."),
+ aggregate = Seq(core, native)
+)
+
+lazy val core = Project(
+ id = "basic-core",
+ base = file("basic-core"),
settings = commonSettings ++ Seq(
- target in (Compile, javah) :=
- (sourceDirectory in native).value / "include"
+ target in javah in Compile := (sourceDirectory in native).value / "include"
),
dependencies = Seq(
native % Runtime
)
-).enablePlugins(JniJvm)
+).enablePlugins(JniLoading)
lazy val native = Project(
- id = "sample-basic-native",
+ id = "basic-native",
base = file("basic-native"),
settings = commonSettings ++ Seq(
- jniLibraryPath in (Compile, jni) := "/ch/jodersky/jni/basic"
+ //enableNativeCompilation in Compile := false,
+ sourceDirectory in nativeCompile in Compile := sourceDirectory.value,
+ nativeLibraryPath in Compile := "/ch/jodersky/jni/basic/native"
)
).enablePlugins(JniNative)
diff --git a/samples/basic/project/plugins.sbt b/samples/basic/project/plugins.sbt
index 08f838a..c683b30 100644
--- a/samples/basic/project/plugins.sbt
+++ b/samples/basic/project/plugins.sbt
@@ -1 +1 @@
-addSbtPlugin("ch.jodersky" % "sbt-jni" % "0.2-SNAPSHOT")
+addSbtPlugin("ch.jodersky" % "sbt-jni" % "0.3-SNAPSHOT")