aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Odersky <jakob@odersky.com>2016-05-02 05:19:07 -0700
committerJakob Odersky <jakob@odersky.com>2016-05-11 11:17:09 -0700
commit791cb363b77332e3abdf4039102dfcdb863ce6c3 (patch)
tree09ff5d807a1407abedade57b692204ceac3f3280
parent49563ee13599b0cb1add27b24446677a13b1f563 (diff)
downloadsbt-jni-791cb363b77332e3abdf4039102dfcdb863ce6c3.tar.gz
sbt-jni-791cb363b77332e3abdf4039102dfcdb863ce6c3.tar.bz2
sbt-jni-791cb363b77332e3abdf4039102dfcdb863ce6c3.zip
Use macro annotation to load native library
This also removes the need for third projects to depend on a "loader library".
-rw-r--r--.travis.yml17
-rw-r--r--README.md179
-rw-r--r--jni-library/src/main/scala/ch/jodersky/jni/NativeLoader.scala78
-rw-r--r--jni-library/src/main/scala/ch/jodersky/jni/Platform.scala58
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/Autotools.scala30
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/BuildTool.scala14
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/BuildToolApi.scala27
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/CMake.scala31
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/ConfigureMakeInstall.scala49
-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.scala100
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniPackaging.scala119
-rw-r--r--macros/src/main/scala/ch/jodersky/jni/annotations.scala102
-rw-r--r--macros/src/main/scala/ch/jodersky/jni/util/PlatformMacros.scala31
-rw-r--r--plugin/src/main/resources/ch/jodersky/sbt/jni/templates/CMakeLists.txt61
-rw-r--r--plugin/src/main/scala/ch/jodersky/sbt/jni/build/Autotools.scala.notyet28
-rw-r--r--plugin/src/main/scala/ch/jodersky/sbt/jni/build/BuildTool.scala68
-rw-r--r--plugin/src/main/scala/ch/jodersky/sbt/jni/build/CMake.scala33
-rw-r--r--plugin/src/main/scala/ch/jodersky/sbt/jni/build/ConfigureMakeInstall.scala55
-rw-r--r--plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniJavah.scala (renamed from jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniJavah.scala)21
-rw-r--r--plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniLoad.scala25
-rw-r--r--plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniNative.scala159
-rw-r--r--plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniPackage.scala95
-rw-r--r--plugin/src/main/scala/ch/jodersky/sbt/jni/util/BytecodeUtil.scala (renamed from jni-plugin/src/main/scala/ch/jodersky/sbt/jni/util/ByteCode.scala)17
-rw-r--r--plugin/src/sbt-test/sbt-jni/multiclasses/README.md1
-rw-r--r--plugin/src/sbt-test/sbt-jni/multiclasses/build.sbt16
-rw-r--r--plugin/src/sbt-test/sbt-jni/multiclasses/core/src/main/scala/multiclasses/Adder.scala18
-rw-r--r--plugin/src/sbt-test/sbt-jni/multiclasses/core/src/main/scala/multiclasses/Main.scala30
-rw-r--r--plugin/src/sbt-test/sbt-jni/multiclasses/core/src/main/scala/multiclasses/Multiplier.scala12
-rw-r--r--plugin/src/sbt-test/sbt-jni/multiclasses/native1/src/CMakeLists.txt61
-rw-r--r--plugin/src/sbt-test/sbt-jni/multiclasses/native1/src/library.c28
-rw-r--r--plugin/src/sbt-test/sbt-jni/multiclasses/native2/src/CMakeLists.txt61
-rw-r--r--plugin/src/sbt-test/sbt-jni/multiclasses/native2/src/library.c16
l---------plugin/src/sbt-test/sbt-jni/multiclasses/project/ScriptedHelper.scala1
l---------plugin/src/sbt-test/sbt-jni/multiclasses/project/plugins.sbt1
-rw-r--r--plugin/src/sbt-test/sbt-jni/multiclasses/test2
-rw-r--r--plugin/src/sbt-test/sbt-jni/oneproject/README.md1
-rw-r--r--plugin/src/sbt-test/sbt-jni/oneproject/build.sbt6
l---------plugin/src/sbt-test/sbt-jni/oneproject/project/ScriptedHelper.scala1
l---------plugin/src/sbt-test/sbt-jni/oneproject/project/plugins.sbt1
l---------plugin/src/sbt-test/sbt-jni/oneproject/src/main1
l---------plugin/src/sbt-test/sbt-jni/oneproject/src/native1
-rw-r--r--plugin/src/sbt-test/sbt-jni/oneproject/test4
-rw-r--r--plugin/src/sbt-test/sbt-jni/simple/README.md1
-rw-r--r--plugin/src/sbt-test/sbt-jni/simple/build.sbt12
-rw-r--r--plugin/src/sbt-test/sbt-jni/simple/core/src/main/scala/simple/Library.scala10
-rw-r--r--plugin/src/sbt-test/sbt-jni/simple/core/src/main/scala/simple/Main.scala10
-rw-r--r--plugin/src/sbt-test/sbt-jni/simple/native/src/library.c16
-rw-r--r--plugin/src/sbt-test/sbt-jni/simple/project/ScriptedHelper.scala14
-rw-r--r--plugin/src/sbt-test/sbt-jni/simple/project/plugins.sbt3
-rw-r--r--plugin/src/sbt-test/sbt-jni/simple/test4
-rw-r--r--project/Build.scala64
-rw-r--r--project/SbtJniBuild.scala86
-rw-r--r--project/build.properties2
-rw-r--r--project/plugins.sbt9
-rw-r--r--samples/basic/README.md3
-rw-r--r--samples/basic/basic-core/build.sbt1
-rw-r--r--samples/basic/basic-core/src/main/scala/ch/jodersky/jni/basic/Library.scala8
-rw-r--r--samples/basic/basic-core/src/main/scala/ch/jodersky/jni/basic/Main.scala13
-rw-r--r--samples/basic/basic-native/src/CMakeLists.txt46
-rw-r--r--samples/basic/basic-native/src/include/ch_jodersky_jni_basic_Library__.h21
-rw-r--r--samples/basic/basic-native/src/library.c16
-rw-r--r--samples/basic/build.sbt31
-rw-r--r--samples/basic/project/build.properties1
-rw-r--r--samples/basic/project/plugins.sbt1
65 files changed, 1241 insertions, 818 deletions
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..13a9b43
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,17 @@
+language: scala
+
+jdk:
+ - oraclejdk8
+
+script:
+ - sbt test-plugin
+
+cache:
+ directories:
+ - $HOME/.ivy2/cache
+ - $HOME/.sbt/boot/
+
+before_cache:
+ - find $HOME/.ivy2/cache/ch.jodersky -depth -name "sbt-jni*" -exec rm -r {} \;
+ - find $HOME/.ivy2 -name "ivydata-*.properties" -delete
+ - find $HOME/.sbt -name "*.lock" -delete
diff --git a/README.md b/README.md
index 6eb70bb..57455aa 100644
--- a/README.md
+++ b/README.md
@@ -1,110 +1,171 @@
-# SBT-JNI
+[![Build Status](https://travis-ci.org/jodersky/sbt-jni.svg?branch=master)](https://travis-ci.org/jodersky/sbt-jni)
+[![Download](https://api.bintray.com/packages/jodersky/sbt-plugins/sbt-jni/images/download.svg) ](https://bintray.com/jodersky/sbt-plugins/sbt-jni/_latestVersion)
-A suite of plugins for simplifying creation and distribution of JNI programs.
+# sbt-jni
+
+A suite of sbt plugins for simplifying creation and distribution of JNI programs.
## 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.
+Java Native Interface (JNI), is a framework that enables programs written in a JVM language to interact with native code and vice-versa. Such programs can be divided into two logical parts: the JVM part, consisting of sources that will be compiled to bytecode (e.g. Scala or Java), and the native part, consisting of sources that will be compiled to machine-native code (e.g. C, C++ or assembly).
-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:
+Using native code can be beneficial in some situations: it can, for example, provide raw performance boosts or enable otherwise infeasable features such as interaction with peripherals. However, it also adds a few layers of complexities, most notably:
-- Compilation: the project is divided into two parts, each of which require separate compilation. Furthermore, header generation with `javah` has to be dealt with.
+- Compilation: the project is divided into two parts, each of which require separate compilation.
- 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.
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
-This project consists of four 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). The plugins are listed in the following table:
-
-Functionality | JVM part | Native part
---------------|-----------------|-------------
-Compiling | JniJavah | JniNative
-Packaging | JniLoading | JniPackaging
-
-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.
+## Plugin Summary
-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.
+| Plugin | Description |
+|------------|--------------------------------------------------------------------------------------------------------|
+| JniJavah | Adds support for generating headers from classfiles that have `@native` methods. |
+| JniLoad | Makes `@nativeLoader` annotation available, that injects code to transparently load native libraries. |
+| JniNative | Adds sbt wrapper tasks around native build tools to ease building and integrating native libraries. |
+| JniPackage | Packages native libraries into multi-platform fat jars. No more manual library path configuration! |
-## Usage
-Add jcenter resolver and plugin dependency. In `project/plugins.sbt`:
+All plugins are made available by adding
```scala
resolvers += Resolver.jcenterRepo
-addSbtPlugin("ch.jodersky" % "sbt-jni" % "0.4.4")
+addSbtPlugin("ch.jodersky" % "sbt-jni" % "1.0.0-RC1")
```
+to `project/plugins.sbt`.
-Define sub-projects for JVM and native sources. In `myproject/build.sbt`:
+Note that most plugins are enabled in projects by default. Disabling their functionality can be achieved by adding `disablePlugin(<plugin>)` to the corresponding project definition (for example, to disable packaging native libraries).
+## Plugin Details
+
+### JniJavah
+
+| Enabled | Source |
+|--------------------------------|---------------|
+| automatic, for all projects | [JniJavah.scala](plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniJavah.scala)|
+
+This plugin wraps the JDK `javah` command.
+
+Run `sbt-javah` to generate C header files with prototypes for any methods marked as native.
+E.g. the following scala class
```scala
-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
+package org.example
+class Adder(val base: Int) {
+ @native def plus(term: Int): Int // implemented in a native library
+}
+```
+will yield this prototype
+```c
+/*
+ * Class: org_example_Adder
+ * Method: plus
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_example_Adder_plus
+ (JNIEnv *, jobject, jint);
+
```
-Select plugins to enable on sub-projects:
+The header output directory can be configured
+```
+target in javah := <dir> // defaults to target/native/include
+```
-- In `myproject-core/build.sbt`:
+Note that native methods declared both in Scala and Java are supported. Whereas Scala uses the `@native` annotation, Java uses the
+`native` keyword.
- ```scala
- //enablePlugin(JniJavah) // this plugin is added to all JVM projects by default
- enablePlugin(JniLoading)
- ```
+### JniLoad
+| Enabled | Source |
+|--------------------------------|---------------|
+| automatic, for all projects | [JniLoad.scala](plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniLoad.scala) |
-- In `myproject-native/build.sbt`:
+This plugin enables loading native libraries in a safe and transparent manner to the developer (no more explicit, static `System.load("library")` calls required). It does so by providing a class annotation which injects native loading code to all its annottees. Furthermore, in case a native library is not available on the current `java.library.path`, the code injected by the annotation will fall back to loading native libraries packaged according to the rules of `JniPackage` and available on the classpath.
- ```scala
- enablePlugin(JniNative)
- //enablePlugin(JniPackaging) // this plugin is added to all projects using JniNative by default
- ```
+Example use:
+```scala
+import ch.jodersky.sbt.jni.nativeLoader
+
+// By adding this annotation, there is no need to call
+// System.load("adder0") before accessing native methods.
+@nativeLoader("adder0")
+class Adder(val base: Int) {
+ @native def plus(term: Int): Int // implemented in libadder0.so
+}
+
+// The application feels like a pure Scala app.
+object Main extends App {
+ (new Adder(0)).plus(1)
+}
+```
-See an example [build.sbt](samples/basic/build.sbt).
+Note: this plugin is just shorthand for adding `sbt-jni-macros` and `scala macros paradise` projects as provided dependencies.
+Projects must use Scala versions 2.11 or 2.12.0-M4.
-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.
+See the [annotation's implementation](macros/src/main/scala/ch/jodersky/sbt/jni/annotations.scala) for details about the injected code.
-## Plugin Details
-The following gives a detailed description about the various JNI plugins and the ways to customize their behaviour. More details can be found in their [implementations](jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins).
+### JniNative
+| Enabled | Source |
+|--------------------------------|---------------|
+| manual | [JniNative.scala](plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniNative.scala) |
-### JniJavah
-*Add to sub-projects: JVM*
+TODO: explanation
-Adds `javah` task to generate header files for classes containing `@native` methods.
+### JniPackage
+| Enabled | Source |
+|--------------------------------|---------------|
+| automatic, when JniNative enabled | [JniPackage.scala](plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniPackage.scala) |
-Customization: `target in javah := file("myproject-native") / "src" / "include"`, to point to `include` directory of native sources.
+TODO: explanation
-### JniNative
-*Add to sub-projects: native*
+## Canonical Use
+
+1. Define separate sub-projects for JVM and native sources. In `myproject/build.sbt`:
+
+ ```scala
+ lazy val core = project in file("myproject-core"). // contains regular jvm sources and @native methods
+ dependsOn(native % Runtime) // natives only required for running, compilation can be done without
+
+ lazy val native = project in file("myproject-native"). // contains native sources
+ enablePlugin(JniNative)
+ ```
+ Note that separate projects are not strictly required. They are recommended nevertheless, as a portability-convenience tradeoff: programs
+ written in a jvm language are expected to run anywhere without recompilation, but including native libraries in jars limits this portability
+ to only platforms of the packaged libraries.
-Provides sbt tasks to call into a native build system such as Automake or CMake. Run `nativeCompile` to compile native sources.
+2. Initialize the native build tool from a template: `sbt nativeInit cmake <libname>`
-Customization: `sourceDirectory in nativeCompile := file("src")`, to point to a directory containing the native build definition. Supported build tools are:
+3. Implement core project: *just a regular scala project*
-- Automake
-- CMake
+4. Generate native headers: `sbt javah`
-*Make sure the native build configurations respect the arguments and output directories as specified in the sample build configurations.*
+5. Implement native headers in library: *project specific, see examples*
-### JniLoading
-*Add to sub-projects: JVM*
+6. Build/run/test:
-Enables loading of libraries packaged with `JniPackaging` plugin.
+ - Library: `publish`
+ - Application: `core/run`
-### JniPackaging
-*Add to sub-projects: native*
+7. Develop:
-Packages native libraries in main jar, thus enabling easy publshing and distribution.
+ - Re-run `javah` only if `@native` methods changed
+ - Otherwise, go to 6
## Examples
+The [plugins' unit tests](plugin/src/sbt-test/sbt-jni) offer some simple examples. They can be run individually through these steps:
-See projects in the `samples` directory for some simple use-case examples. Note that in order to run these, sbt-jni will have to published locally first.
+1. Publish the macros library locally `sbt publishLocal`.
+2. Change to the test's directory and run `sbt -Dplugin.version=<version>-SNAPSHOT`.
+3. Follow the instructions in the `test` file (only enter the lines that start with ">" into sbt).
-Another, more involved example is a [serial communication library](https://jodersky.github.io/flow).
+Real-world use-cases of sbt-jni include:
+- [serial communication library for scala](https://jodersky.github.io/flow)
## Building
-Both a library (`jni-library`) and an sbt plugin (`sbt-jni`) are published. Since both the plugin and user code can depend on the library, supporting multiple scala versions is not easy. Hence this project uses sbt-doge to allow cross-building on a per-project basis:
+Both the macro library (`sbt-jni-macros`) and the sbt plugins (`sbt-jni`) are published. This project uses sbt-doge to allow cross-building on a per-project basis:
-- jni-library is built against scala 2.10, 2.11 and 2.12
-- sbt-jni is built against scala 2.10 (the scala version that sbt 0.13 uses)
+- sbt-jni-macros is built against Scala 2.11 and 2.12
+- sbt-jni is built against Scala 2.10 (the Scala version that sbt 0.13 uses)
-This configuration makes it necessary to always cross-compile and cross-publish this project, i.e. append a "+" before every task.
+The differing Scala versions make it necessary to always cross-compile and cross-publish this project, i.e. append a "+" before every task.
Run `+publishLocal` to build and use this plugin locally.
diff --git a/jni-library/src/main/scala/ch/jodersky/jni/NativeLoader.scala b/jni-library/src/main/scala/ch/jodersky/jni/NativeLoader.scala
deleted file mode 100644
index c806464..0000000
--- a/jni-library/src/main/scala/ch/jodersky/jni/NativeLoader.scala
+++ /dev/null
@@ -1,78 +0,0 @@
-package ch.jodersky.jni
-
-import java.io.{File, FileOutputStream, InputStream, OutputStream}
-
-/**
- * Provides enhanced native library loading functionality.
- */
-object NativeLoader {
-
- /** Name of the shared library file that is contained in a jar. */
- final val LibraryName = "library"
-
- final val BufferSize = 4096
-
- /** Extracts a resource from this class loader to a temporary file. */
- private def extract(path: String): Option[File] = {
- var in: Option[InputStream] = None
- var out: Option[OutputStream] = None
-
- try {
- in = Option(NativeLoader.getClass.getResourceAsStream(path))
- if (in.isEmpty) return None
-
- val file = File.createTempFile(path, "")
- out = Some(new FileOutputStream(file))
-
- val buffer = new Array[Byte](BufferSize)
- var length = -1;
- do {
- length = in.get.read(buffer)
- if (length != -1) out.get.write(buffer, 0, length)
- } while (length != -1)
-
- Some(file)
- } finally {
- in.foreach(_.close)
- out.foreach(_.close)
- }
- }
-
- private def loadError(msg: String): Nothing = throw new UnsatisfiedLinkError(
- "Error during native library extraction " +
- "(this can happen if your platform is not supported): " +
- msg
- )
-
- /**
- * Gets the absolute, full path of a native library on the classpath, given a libraryPath
- * and platform.
- */
- def fullLibraryPath(libraryPath: String, platform: Platform) = {
- libraryPath + "/" + platform.id + "/" + LibraryName
- }
-
- private def loadFromJar(libraryPath: String): Unit = {
- val platform = Platform.current.getOrElse {
- loadError("Cannot determine current platform.")
- }
-
- val resource = fullLibraryPath(libraryPath, platform)
-
- val file = extract(resource) getOrElse loadError(
- s"Shared library $resource not found."
- )
- System.load(file.getAbsolutePath())
- }
-
- /**
- * 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 {
- System.loadLibrary(library)
- } catch {
- case ex: UnsatisfiedLinkError => loadFromJar(libraryPath)
- }
-
-}
diff --git a/jni-library/src/main/scala/ch/jodersky/jni/Platform.scala b/jni-library/src/main/scala/ch/jodersky/jni/Platform.scala
deleted file mode 100644
index 1ece9e3..0000000
--- a/jni-library/src/main/scala/ch/jodersky/jni/Platform.scala
+++ /dev/null
@@ -1,58 +0,0 @@
-package ch.jodersky.jni
-
-import java.io.IOException
-import scala.sys.process.Process
-
-/**
- * A platform is the representation of an architecture-kernel combination.
- * It is a somewhat ambigous concept defined as the set of all system configurations
- * capable of running a native binary.
- */
-case class Platform private (arch: String, kernel: String) {
-
- /**
- * String representation of this platform. It is inspired by Autotools' platform
- * triplet, without the vendor field.
- */
- def id = arch + "-" + kernel
-
-}
-
-object Platform {
-
- /** The unknown platform. */
- final val Unknown = Platform("unknown", "unknown")
-
- /** Creates a platform with spaces stripped and case normalized. */
- def normalized(arch: String, kernel: String): Platform = {
- def normalize(str: String) = str.toLowerCase.filter(!_.isWhitespace)
- Platform(normalize(arch), normalize(kernel))
- }
-
- /** Runs 'uname' to determine current platform. Returns None if uname does not exist. */
- def uname: Option[Platform] = {
- val lineOpt = try {
- Some(Process("uname -sm").lines.head)
- } catch {
- case _: IOException => None
- }
- lineOpt.map { line =>
- val parts = line.split(" ")
- if (parts.length != 2) {
- sys.error("Could not determine platform: 'uname -sm' returned unexpected string: " + line)
- } else {
- Platform.normalized(parts(1), parts(0))
- }
- }
- }
-
- /** 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/build/Autotools.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/Autotools.scala
deleted file mode 100644
index 960e66e..0000000
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/Autotools.scala
+++ /dev/null
@@ -1,30 +0,0 @@
-package ch.jodersky.sbt.jni
-package build
-
-import java.io.File
-import sbt._
-
-object Autotools extends BuildTool {
-
- val name = "Autotools"
-
- def detect(baseDirectory: File) = baseDirectory.list().contains("configure")
-
- object api extends ConfigureMakeInstall {
-
- override def configure(base: File, build: File, target: File) = {
- val targetPath = target.getAbsolutePath
-
- Process(
- s"${base.getAbsolutePath}/configure " +
- s"--prefix=$targetPath " +
- s"--libdir=$targetPath " +
- "--disable-versioned-lib",
- build
- )
- }
-
- }
-
-}
-
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/BuildTool.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/BuildTool.scala
deleted file mode 100644
index 23df2b2..0000000
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/BuildTool.scala
+++ /dev/null
@@ -1,14 +0,0 @@
-package ch.jodersky.sbt.jni
-package build
-
-import java.io.File
-
-trait BuildTool {
-
- def name: String
-
- def detect(baseDirectory: File): Boolean
-
- def api: BuildToolApi
-
-}
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/BuildToolApi.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/BuildToolApi.scala
deleted file mode 100644
index 43dd17a..0000000
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/BuildToolApi.scala
+++ /dev/null
@@ -1,27 +0,0 @@
-package ch.jodersky.sbt.jni
-package build
-
-import java.io.File
-import sbt.Logger
-
-trait BuildToolApi {
-
- /** Invokes the native build tool's clean task */
- def clean(baseDirectory: File, log: Logger): Unit
-
- /**
- * Invokes the native build tool's main task, resulting in a single shared
- * library file.
- * @param baseDirectory the directory where the native project is located
- * @param buildDirectory a directory from where the build is called, it may be used to store temporary files
- * @param targetDirectory the directory into which the native library is copied
- * @return the native library file
- */
- def library(
- baseDirectory: File,
- targetDirectory: File,
- buildDirectory: File,
- log: Logger
- ): File
-
-}
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/CMake.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/CMake.scala
deleted file mode 100644
index 3f75585..0000000
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/CMake.scala
+++ /dev/null
@@ -1,31 +0,0 @@
-package ch.jodersky.sbt.jni
-package build
-
-import java.io.File
-import sbt._
-
-object CMake extends BuildTool {
-
- val name = "CMake"
-
- def detect(baseDirectory: File) = baseDirectory.list().contains("CMakeLists.txt")
-
- object api extends ConfigureMakeInstall {
-
- override def configure(base: File, build: File, target: File) = {
- val targetPath = target.getAbsolutePath
- Process(
- //Disable producing versioned library files, not needed for fat jars.
- "cmake " +
- s"-DCMAKE_INSTALL_PREFIX:PATH=$targetPath " +
- s"-DLIB_INSTALL_DIR:PATH=$targetPath " +
- "-DENABLE_VERSIONED_LIB:BOOLEAN=OFF " +
- base.getAbsolutePath,
- build
- )
- }
-
- }
-
-}
-
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/ConfigureMakeInstall.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/ConfigureMakeInstall.scala
deleted file mode 100644
index 97011ed..0000000
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/ConfigureMakeInstall.scala
+++ /dev/null
@@ -1,49 +0,0 @@
-package ch.jodersky.sbt.jni
-package build
-
-import java.io.File
-import sbt._
-
-/** Native build tools relying on a standard 'configure && make && make install' process */
-trait ConfigureMakeInstall extends BuildToolApi {
-
- override def clean(baseDirectory: File, log: Logger) = Process("make clean", baseDirectory) ! log
-
- def configure(baseDirectory: File, buildDirectory: File, targetDirectory: File): ProcessBuilder
-
- def make(baseDirectory: File, buildDirectory: File, targetDirectory: File): ProcessBuilder = Process("make", buildDirectory)
-
- def install(baseDirectory: File, buildDirectory: File, targetDirectory: File): ProcessBuilder = Process("make install", buildDirectory)
-
- override def library(
- baseDirectory: File,
- buildDirectory: File,
- targetDirectory: File,
- log: Logger
- ): File = {
-
- val ev = (
- configure(baseDirectory, buildDirectory, targetDirectory) #&&
- make(baseDirectory, buildDirectory, targetDirectory) #&&
- install(baseDirectory, buildDirectory, targetDirectory)
- ) ! log
-
- if (ev != 0) sys.error(s"Building native library failed. Exit code: ${ev}")
-
- val products: List[File] = (targetDirectory ** ("*" -- "*.la")).get.filter(_.isFile).toList
-
- //only one produced library is expected
- products match {
- case Nil =>
- sys.error("No files were created during compilation, " +
- "something went wrong with the autotools configuration.")
- case head :: Nil =>
- head
- case head :: tail =>
- log.warn("More than one file was created during compilation, " +
- s"only the first one (${head.getAbsolutePath}) will be used.")
- head
- }
- }
-}
-
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
deleted file mode 100644
index 599bfa1..0000000
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniLoading.scala
+++ /dev/null
@@ -1,28 +0,0 @@
-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/plugins/JniNative.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniNative.scala
deleted file mode 100644
index 2ac179d..0000000
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniNative.scala
+++ /dev/null
@@ -1,100 +0,0 @@
-package ch.jodersky.sbt.jni
-package plugins
-
-import build._
-import ch.jodersky.jni.{NativeLoader, Platform}
-import sbt._
-import sbt.Keys._
-
-/** Wraps a native build system in sbt tasks. */
-object JniNative extends AutoPlugin {
-
- 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 settings: 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)(settings)
-}
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
deleted file mode 100644
index cc3a851..0000000
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniPackaging.scala
+++ /dev/null
@@ -1,119 +0,0 @@
-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."
- )
-
- val nativeLibraries = taskKey[Map[Platform, File]](
- "All native libraries, managed and unmanaged."
- )
-
- }
- 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,
-
- // managed native libraries take precedence
- nativeLibraries := unmanagedNativeLibraries.value ++ managedNativeLibraries.value,
-
- resourceGenerators += Def.task {
-
- val libraries: Seq[(Platform, File)] = nativeLibraries.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
-
- )
-
- override lazy val projectSettings = inConfig(Compile)(settings) ++
- Seq(crossPaths := false) //don't add scala version to native jars
-
-
-}
diff --git a/macros/src/main/scala/ch/jodersky/jni/annotations.scala b/macros/src/main/scala/ch/jodersky/jni/annotations.scala
new file mode 100644
index 0000000..c7a6555
--- /dev/null
+++ b/macros/src/main/scala/ch/jodersky/jni/annotations.scala
@@ -0,0 +1,102 @@
+package ch.jodersky.jni
+
+import util.PlatformMacros
+
+import scala.language.experimental.macros
+
+import scala.reflect.macros.whitebox.Context
+import scala.annotation.StaticAnnotation
+import scala.annotation.compileTimeOnly
+
+object nativeLoaderMacro {
+
+ def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
+ import c.universe._
+
+ val nativeLibrary: String = c.prefix.tree match {
+ case Apply(_, List(Literal(Constant(x: String)))) => x
+ case Apply(_, xs :: tail) => c.abort(xs.pos, "Native library must be a constant.")
+ case t => c.abort(t.pos, "Native library not specified.")
+ }
+
+ def inject(annottees: List[Tree]): List[Tree] = annottees match {
+
+ case ClassDef(mods, name, tparams, Template(parents, self, body)) :: tail =>
+ val extra = q"""
+ {
+ ${name.toTermName}
+ }
+ """
+
+ val module: List[Tree] = tail match {
+ case Nil => inject(List(q"""object ${name.toTermName}"""))
+ case other => inject(other)
+ }
+
+ ClassDef(mods, name, tparams, Template(parents, self, body :+ extra)) :: module
+
+ //q"$mods object $name extends ..$parents {$self => ..$body }" :: Nil =>
+ case ModuleDef(mods, name, Template(parents, self, body)) :: Nil =>
+ val loadPackagedDef: DefDef = q"""
+ private 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 = ${PlatformMacros.current(c)}
+
+ val resourcePath: String = "/native/" + plat + "/" + lib
+ val resourceStream = Option($name.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)
+ }
+ """
+ val loadDef: DefDef = q"""
+ private def load(): Unit = try {
+ System.loadLibrary($nativeLibrary)
+ } catch {
+ case ex: UnsatisfiedLinkError => loadPackaged()
+ }
+ """
+
+ val extraBody = q"""
+ $loadPackagedDef
+ $loadDef
+ load()
+ """
+
+ ModuleDef(mods, name, Template(parents, self, body :+ extraBody)) :: Nil
+
+ case _ =>
+ c.abort(c.enclosingPosition, "nativeLoader can only be annotated to classes and singleton objects")
+
+ }
+
+ val outputs = inject(annottees.map(_.tree).toList)
+
+ val result: Tree = Block(outputs, Literal(Constant(())))
+
+ c.Expr[Any](result)
+ }
+
+}
+
+@compileTimeOnly("Macro Paradise must be enabled to apply annotation.")
+class nativeLoader(nativeLibrary: String) extends StaticAnnotation {
+ def macroTransform(annottees: Any*): Any = macro nativeLoaderMacro.impl
+}
diff --git a/macros/src/main/scala/ch/jodersky/jni/util/PlatformMacros.scala b/macros/src/main/scala/ch/jodersky/jni/util/PlatformMacros.scala
new file mode 100644
index 0000000..a4af100
--- /dev/null
+++ b/macros/src/main/scala/ch/jodersky/jni/util/PlatformMacros.scala
@@ -0,0 +1,31 @@
+package ch.jodersky.jni
+package util
+
+import scala.language.experimental.macros
+
+import scala.reflect.macros.whitebox.Context
+
+object PlatformMacros {
+
+ // arch-kernel
+ def current(c: Context): c.Expr[String] = {
+ import c.universe._
+ val result = q"""
+ val line = try {
+ scala.sys.process.Process("uname -sm").lineStream.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
+ }
+ """
+ c.Expr[String](result)
+ }
+
+}
diff --git a/plugin/src/main/resources/ch/jodersky/sbt/jni/templates/CMakeLists.txt b/plugin/src/main/resources/ch/jodersky/sbt/jni/templates/CMakeLists.txt
new file mode 100644
index 0000000..8292064
--- /dev/null
+++ b/plugin/src/main/resources/ch/jodersky/sbt/jni/templates/CMakeLists.txt
@@ -0,0 +1,61 @@
+################################################################
+# A minimal CMake file that is compatible with sbt-jni #
+# #
+# All settings required by sbt-jni have been marked so, please #
+# add/modify/remove settings to build your specific library. #
+################################################################
+
+cmake_minimum_required(VERSION 2.6)
+
+# Define project and related variables
+#
+project ({{project}})
+
+# Set versions and library name
+# (required by sbt-jni) please use semantic versioning
+#
+set (VERSION_MAJOR 0)
+set (VERSION_MINOR 0)
+set (VERSION_PATCH 0)
+# (required by sbt-jni) major version will always be appended to library name
+set (LIB_NAME ${CMAKE_PROJECT_NAME}${VERSION_MAJOR})
+
+# Command-line options
+#
+# (set by sbt-jni)
+set (LIB_INSTALL_DIR lib CACHE PATH "Path in which to install libraries (equivalent to Autoconf --libdir).")
+# (set by sbt-jni)
+set (LIB_ENABLE_MINOR_VERSIONS ON CACHE BOOLEAN "Build libraries with minor and patch versions appended.")
+
+# Setup JNI
+find_package(JNI REQUIRED)
+if (JNI_FOUND)
+ message (STATUS "JNI include directories: ${JNI_INCLUDE_DIRS}")
+endif()
+
+# Include directories
+include_directories(.)
+include_directories(include)
+include_directories(${JNI_INCLUDE_DIRS})
+
+# Setup main shared library
+file(GLOB LIB_SRC
+ "*.c"
+ "*.cpp"
+)
+add_library(${LIB_NAME} SHARED ${LIB_SRC})
+
+# By default, in a regular build, minor and patch versions are added to the generated files.
+# When built through sbt-jni however, LIB_ENABLE_MINOR_VERSIONS is deactivated and only a
+# major-versioned library file is built.
+if (LIB_ENABLE_MINOR_VERSIONS)
+ set_target_properties(
+ ${LIB_NAME}
+ PROPERTIES
+ VERSION 0.${VERSION_MINOR}.${VERSION_PATCH} # major version always 0, it is included in library name
+ SOVERSION 0
+ )
+endif()
+
+# Installation targets
+install(TARGETS ${LIB_NAME} LIBRARY DESTINATION ${LIB_INSTALL_DIR})
diff --git a/plugin/src/main/scala/ch/jodersky/sbt/jni/build/Autotools.scala.notyet b/plugin/src/main/scala/ch/jodersky/sbt/jni/build/Autotools.scala.notyet
new file mode 100644
index 0000000..7d81858
--- /dev/null
+++ b/plugin/src/main/scala/ch/jodersky/sbt/jni/build/Autotools.scala.notyet
@@ -0,0 +1,28 @@
+package ch.jodersky.sbt.jni
+package build
+
+import java.io.File
+import sbt._
+
+object Autotools extends BuildTool with ConfigureMakeInstall {
+
+ val name = "Autotools"
+
+ def detect(baseDirectory: File) = baseDirectory.list().contains("configure")
+
+ override def getInstance(baseDir: File, buildDir: File, logger: Logger) = new Instance {
+
+ override def log = logger
+ override def baseDirectory = baseDir
+ override def buildDirectory = buildDir
+
+ override def configure(target: File) = Process(
+ s"${base.getAbsolutePath}/configure " +
+ s"--prefix=${target.getAbsolutePath} " +
+ s"--libdir=${target.getAbsolutePath} " +
+ "--disable-versioned-lib",
+ build
+ )
+ }
+
+} \ No newline at end of file
diff --git a/plugin/src/main/scala/ch/jodersky/sbt/jni/build/BuildTool.scala b/plugin/src/main/scala/ch/jodersky/sbt/jni/build/BuildTool.scala
new file mode 100644
index 0000000..3d0deb1
--- /dev/null
+++ b/plugin/src/main/scala/ch/jodersky/sbt/jni/build/BuildTool.scala
@@ -0,0 +1,68 @@
+package ch.jodersky.sbt.jni
+package build
+
+import java.io.{ File, InputStream }
+import java.nio.file.Files
+
+import scala.io.Source
+import sbt.Logger
+
+trait BuildTool {
+
+ /** Name of this build tool. */
+ def name: String
+
+ /** Detect if this build tool is configured in the given directory.
+ * E.g. for the Make build tool, this would return true if a Makefile is present
+ * in the given directory.
+ */
+ def detect(baseDirectory: File): Boolean
+
+ protected def templateMappings: Seq[(String, String)]
+
+ /** Initialize the given directory with a minimal, functioning configuration for
+ * this build tool. E.g. for the Make build tool, this would create a Makefile in
+ * the given directory that is compatible with sbt-jni.
+ * @return all created files
+ */
+ def initTemplate(baseDirectory: File, projectName: String): Seq[File] =
+ for ((resource, name) <- templateMappings) yield {
+ val resourceStream = this.getClass.getResourceAsStream(resource)
+
+ if (resourceStream == null) sys.error(s"Template for $name not found.")
+
+ val raw = Source.fromInputStream(resourceStream).mkString("")
+ val replaced = raw.replaceAll("\\{\\{project\\}\\}", projectName)
+
+ baseDirectory.mkdir()
+ val out = baseDirectory.toPath().resolve(name)
+ Files.write(out, replaced.getBytes)
+ out.toFile()
+ }
+
+ /** Actual tasks that can be perfomed on a specific configuration, such as
+ * configured in a Makefile.
+ */
+ trait Instance {
+
+ /** Invokes the native build tool's clean task */
+ def clean(): Unit
+
+ /** Invokes the native build tool's main task, resulting in a single shared
+ * library file.
+ * @param baseDirectory the directory where the native project is located
+ * @param buildDirectory a directory from where the build is called, it may be used
+ * to store temporary files
+ * @param targetDirectory the directory into which the native library is copied
+ * @return the native library file
+ */
+ def library(
+ targetDirectory: File
+ ): File
+
+ }
+
+ /** Get an instance (build configuration) of this tool, in the specified directory. */
+ def getInstance(baseDirectory: File, buildDirectory: File, logger: Logger): Instance
+
+}
diff --git a/plugin/src/main/scala/ch/jodersky/sbt/jni/build/CMake.scala b/plugin/src/main/scala/ch/jodersky/sbt/jni/build/CMake.scala
new file mode 100644
index 0000000..eb7be1e
--- /dev/null
+++ b/plugin/src/main/scala/ch/jodersky/sbt/jni/build/CMake.scala
@@ -0,0 +1,33 @@
+package ch.jodersky.sbt.jni
+package build
+
+import sbt._
+
+object CMake extends BuildTool with ConfigureMakeInstall {
+
+ override val name = "CMake"
+
+ override def detect(baseDirectory: File) = baseDirectory.list().contains("CMakeLists.txt")
+
+ override protected def templateMappings = Seq(
+ "/ch/jodersky/sbt/jni/templates/CMakeLists.txt" -> "CMakeLists.txt"
+ )
+
+ override def getInstance(baseDir: File, buildDir: File, logger: Logger) = new Instance {
+
+ override def log = logger
+ override def baseDirectory = baseDir
+ override def buildDirectory = buildDir
+
+ override def configure(target: File) = Process(
+ // disable producing versioned library files, not needed for fat jars
+ "cmake " +
+ s"-DCMAKE_INSTALL_PREFIX:PATH=${target.getAbsolutePath} " +
+ s"-DLIB_INSTALL_DIR:PATH=${target.getAbsolutePath} " +
+ "-DLIB_ENABLE_MINOR_VERSIONS:BOOLEAN=OFF " +
+ baseDirectory.getAbsolutePath,
+ buildDirectory
+ )
+ }
+
+}
diff --git a/plugin/src/main/scala/ch/jodersky/sbt/jni/build/ConfigureMakeInstall.scala b/plugin/src/main/scala/ch/jodersky/sbt/jni/build/ConfigureMakeInstall.scala
new file mode 100644
index 0000000..7bfedae
--- /dev/null
+++ b/plugin/src/main/scala/ch/jodersky/sbt/jni/build/ConfigureMakeInstall.scala
@@ -0,0 +1,55 @@
+package ch.jodersky.sbt.jni
+package build
+
+import java.io.File
+import sbt._
+
+trait ConfigureMakeInstall { self: BuildTool =>
+
+ /* API for native build tools that use a standard 'configure && make && make install' process,
+ * where the configure step is left ab
+ stract. */
+ trait Instance extends self.Instance {
+
+ def log: Logger
+ def baseDirectory: File
+ def buildDirectory: File
+
+ def clean() = Process("make clean", buildDirectory) ! log
+
+ def configure(targetDirectory: File): ProcessBuilder
+
+ def make(): ProcessBuilder = Process("make", buildDirectory)
+
+ def install(): ProcessBuilder = Process("make install", buildDirectory)
+
+ def library(
+ targetDirectory: File
+ ): File = {
+
+ val ev: Int = (
+ configure(targetDirectory) #&& make() #&& install()
+ ) ! log
+
+ if (ev != 0) sys.error(s"Building native library failed. Exit code: ${ev}")
+
+ val products: List[File] =
+ (targetDirectory ** ("*.so" | "*.dylib")).get.filter(_.isFile).toList
+
+ // only one produced library is expected
+ products match {
+ case Nil =>
+ sys.error(s"No files were created during compilation, " +
+ "something went wrong with the ${name} configuration.")
+ case head :: Nil =>
+ head
+ case head :: tail =>
+ log.warn("More than one file was created during compilation, " +
+ s"only the first one (${head.getAbsolutePath}) will be used.")
+ head
+ }
+ }
+ }
+
+}
+
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniJavah.scala b/plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniJavah.scala
index 63e5b23..edda1fc 100644
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniJavah.scala
+++ b/plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniJavah.scala
@@ -3,7 +3,7 @@ package plugins
import sbt._
import sbt.Keys._
-import util.ByteCode
+import util.BytecodeUtil
/** Adds `javah` header-generation functionality to projects. */
object JniJavah extends AutoPlugin {
@@ -14,7 +14,7 @@ object JniJavah extends AutoPlugin {
object autoImport {
val javahClasses = taskKey[Set[String]](
- "Fully qualified names of classes containing native declarations."
+ "Finds fully qualified names of classes containing native declarations."
)
val javah = taskKey[File](
@@ -30,26 +30,25 @@ object JniJavah extends AutoPlugin {
val compiled: inc.Analysis = (compile in Compile).value
val classFiles: Set[File] = compiled.relations.allProducts.toSet
val nativeClasses = classFiles flatMap { file =>
- ByteCode.natives(file)
+ BytecodeUtil.nativeClasses(file)
}
nativeClasses
},
- target in javah := target.value / "include",
-
- fullClasspath in javah := fullClasspath.value,
+ target in javah := target.value / "native" / "include",
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; Seq((classDirectory in Compile).value) }
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)
+ if (!classes.isEmpty) {
+ log.info("Headers will be generated to " + out.getAbsolutePath)
+ }
for (clazz <- classes) {
- log.info("Generating header for " + clazz + "...")
+ log.info("Generating header for " + clazz)
val parts = Seq(
"javah",
"-d", out.getAbsolutePath,
@@ -64,6 +63,6 @@ object JniJavah extends AutoPlugin {
}
)
- override lazy val projectSettings = inConfig(Compile)(mainSettings)
+ override lazy val projectSettings = mainSettings
}
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
new file mode 100644
index 0000000..51d9c7b
--- /dev/null
+++ b/plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniLoad.scala
@@ -0,0 +1,25 @@
+package ch.jodersky.sbt.jni
+package plugins
+
+import sbt._
+import sbt.Keys._
+
+object JniLoad extends AutoPlugin {
+
+ override def requires = empty
+ override def trigger = allRequirements
+
+ 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).
+ addCompilerPlugin(
+ "org.scalamacros" % "paradise" % ProjectVersion.MacrosParadise cross CrossVersion.full
+ ),
+ libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided,
+ libraryDependencies += "ch.jodersky" %% "sbt-jni-macros" % ProjectVersion.Macros % Provided
+ )
+
+ override def projectSettings = settings
+
+}
diff --git a/plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniNative.scala b/plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniNative.scala
new file mode 100644
index 0000000..6192a08
--- /dev/null
+++ b/plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniNative.scala
@@ -0,0 +1,159 @@
+package ch.jodersky.sbt.jni
+package plugins
+
+import build._
+import sbt._
+import sbt.Keys._
+
+/** Wraps a native build system in sbt tasks. */
+object JniNative extends AutoPlugin {
+
+ object autoImport {
+
+ //Main task, inspect this first
+ val nativeCompile = taskKey[File](
+ "Builds a native library by calling the native build tool."
+ )
+
+ val nativePlatform = settingKey[String](
+ "Platform (architecture-kernel) of the system this build is running on."
+ )
+
+ val nativeBuildTool = taskKey[BuildTool](
+ "The build tool to be used when building a native library."
+ )
+
+ val nativeInit = inputKey[Seq[File]](
+ "Initialize a native build script from a template."
+ )
+
+ }
+ import autoImport._
+
+ val nativeBuildToolInstance = taskKey[BuildTool#Instance]("Get an instance of the current native build tool.")
+
+ lazy val settings: Seq[Setting[_]] = Seq(
+
+ // the value retruned must match that of `ch.jodersky.jni.PlatformMacros#current()` of project `macros`
+ nativePlatform := {
+ try {
+ val lines = Process("uname -sm").lines
+ if (lines.length == 0) {
+ sys.error("Error occured trying to run `uname`")
+ }
+ // uname -sm returns "<kernel> <hardware name>"
+ val parts = lines.head.split(" ")
+ if (parts.length != 2) {
+ sys.error("'uname -sm' returned unexpected string: " + lines.head)
+ } else {
+ val arch = parts(1).toLowerCase.replaceAll("\\s", "")
+ val kernel = parts(0).toLowerCase.replaceAll("\\s", "")
+ arch + "-" + kernel
+ }
+ } catch {
+ case ex: Exception =>
+ sLog.value.error("Error trying to determine platform.")
+ sLog.value.warn("Cannot determine platform! It will be set to 'unknown'.")
+ "unknown-unknown"
+ }
+ },
+
+ sourceDirectory in nativeCompile := sourceDirectory.value / "native",
+
+ target in nativeCompile := target.value / "native" / (nativePlatform).value,
+
+ nativeBuildTool := {
+ val tools = Seq(CMake)
+
+ 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 set to$src) " +
+ "points to a directory containing a supported build script. Supported build tools are: " +
+ tools.map(_.name).mkString(",")
+ )
+
+ },
+
+ nativeBuildToolInstance := {
+ val tool = nativeBuildTool.value
+ val srcDir = (sourceDirectory in nativeCompile).value
+ val buildDir = (target in nativeCompile).value / "build"
+ IO.createDirectory(buildDir)
+ tool.getInstance(
+ baseDirectory = srcDir,
+ buildDirectory = buildDir,
+ logger = streams.value.log
+ )
+ },
+
+ clean in nativeCompile := {
+ val log = streams.value.log
+
+ log.debug("Cleaning native build")
+ try {
+ val toolInstance = nativeBuildToolInstance.value
+ toolInstance.clean()
+ } catch {
+ case ex: Exception =>
+ log.debug(s"Native cleaning failed: $ex")
+ }
+
+ },
+
+ nativeCompile := {
+ val tool = nativeBuildTool.value
+ val toolInstance = nativeBuildToolInstance.value
+ val targetDir = (target in nativeCompile).value / "bin"
+ val log = streams.value.log
+
+ IO.createDirectory(targetDir)
+
+ log.info(s"Building library with native build tool ${tool.name}")
+ val lib = toolInstance.library(targetDir)
+ log.success(s"Library built in ${lib.getAbsolutePath}")
+ lib
+ },
+
+ // also clean native sources
+ clean := {
+ (clean in nativeCompile).value
+ clean.value
+ },
+
+ nativeInit := {
+ import complete.DefaultParsers._
+
+ val log = streams.value.log
+
+ def getTool(toolName: String): BuildTool = toolName.toLowerCase match {
+ case "cmake" => CMake
+ case _ => sys.error("Unsupported build tool: " + toolName)
+ }
+
+ val args = spaceDelimited("<tool> [<libname>]").parsed.toList
+
+ val (tool: BuildTool, lib: String) = args match {
+ case Nil => sys.error("Invalid arguments.")
+ case tool :: Nil => (getTool(tool), name.value)
+ case tool :: lib :: other => (getTool(tool), lib)
+ }
+
+ log.info(s"Initializing native build with ${tool.name} configuration")
+ val files = tool.initTemplate((sourceDirectory in nativeCompile).value, lib)
+ files foreach { file =>
+ log.info("Wrote to " + file.getAbsolutePath)
+ }
+ files
+ }
+
+ )
+
+ override lazy val projectSettings = settings
+
+}
diff --git a/plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniPackage.scala b/plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniPackage.scala
new file mode 100644
index 0000000..b6b5100
--- /dev/null
+++ b/plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniPackage.scala
@@ -0,0 +1,95 @@
+package ch.jodersky.sbt.jni
+package plugins
+
+import sbt._
+import sbt.Keys._
+import java.io.File
+
+/** Packages libraries built with JniNative. */
+object JniPackage extends AutoPlugin {
+
+ // JvmPlugin is required or else it will override resource generators when first included
+ override def requires = JniNative && plugins.JvmPlugin
+ override def trigger = allRequirements
+
+ object autoImport {
+
+ val enableNativeCompilation = settingKey[Boolean](
+ "Determines if native compilation is enabled. If not enabled, 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[Seq[(File, String)]](
+ "Reads `unmanagedNativeDirectories` and maps platforms to library files specified theirin."
+ )
+
+ val managedNativeLibraries = taskKey[Seq[(File, String)]](
+ "Maps locally built, platform-dependant libraries."
+ )
+
+ val nativeLibraries = taskKey[Seq[(File, String)]](
+ "All native libraries, managed and unmanaged."
+ )
+
+ }
+ import autoImport._
+ import JniNative.autoImport._
+
+ lazy val settings: Seq[Setting[_]] = Seq(
+
+ enableNativeCompilation := true,
+
+ unmanagedNativeDirectories := Seq(baseDirectory.value / "lib_native"),
+
+ unmanagedNativeLibraries := {
+ val baseDirs: Seq[File] = unmanagedNativeDirectories.value
+ val mappings: Seq[(File, String)] = unmanagedNativeDirectories.value.flatMap { dir =>
+ val files: Seq[File] = (dir ** "*").get
+ files pair rebase(dir, "/native")
+ }
+ mappings
+ },
+
+ managedNativeLibraries := Def.taskDyn[Seq[(File, String)]] {
+ val enableManaged = (enableNativeCompilation).value
+ if (enableManaged) Def.task {
+ val library: File = nativeCompile.value
+ val platform = nativePlatform.value
+
+ Seq(library -> s"/native/$platform/${library.name}")
+ }
+ else Def.task {
+ Seq.empty
+ }
+ }.value,
+
+ nativeLibraries := unmanagedNativeLibraries.value ++ managedNativeLibraries.value,
+
+ resourceGenerators += Def.task {
+ val libraries: Seq[(File, String)] = nativeLibraries.value
+ val resources: Seq[File] = for ((file, path) <- libraries) yield {
+
+ // native library as a managed resource file
+ val resource = resourceManaged.value / path
+
+ // copy native library to a managed resource, so that it is always available
+ // on the classpath, even when not packaged as a jar
+ IO.copyFile(file, resource)
+ resource
+ }
+ resources
+ }.taskValue
+
+ )
+
+ override lazy val projectSettings = inConfig(Compile)(settings) ++
+ Seq(crossPaths := false) // don't add scala version to native jars
+
+}
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/util/ByteCode.scala b/plugin/src/main/scala/ch/jodersky/sbt/jni/util/BytecodeUtil.scala
index b3c6fa3..fe728a6 100644
--- a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/util/ByteCode.scala
+++ b/plugin/src/main/scala/ch/jodersky/sbt/jni/util/BytecodeUtil.scala
@@ -1,16 +1,16 @@
package ch.jodersky.sbt.jni
package util
-import java.io.{File, FileInputStream, Closeable}
-import scala.collection.mutable.{HashSet}
+import java.io.{ File, FileInputStream, Closeable }
+import scala.collection.mutable.{ HashSet }
-import org.objectweb.asm.{ClassReader, ClassVisitor, MethodVisitor, Opcodes}
+import org.objectweb.asm.{ ClassReader, ClassVisitor, MethodVisitor, Opcodes }
-object ByteCode {
+object BytecodeUtil {
private class NativeFinder extends ClassVisitor(Opcodes.ASM5) {
- //contains any classes found to contain at least one @native def
+ // classes found to contain at least one @native def
val _nativeClasses = new HashSet[String]
def nativeClasses = _nativeClasses.toSet
@@ -47,7 +47,12 @@ object ByteCode {
}
}
- def natives(classFile: File): Set[String] = using(new FileInputStream(classFile)) { in =>
+ /** Finds classes containing native implementations.
+ * @param classFile java class file from which classes are read
+ * @return all fully qualified names of classes that contain at least one member annotated
+ * with @native
+ */
+ def nativeClasses(classFile: File): Set[String] = using(new FileInputStream(classFile)) { in =>
val reader = new ClassReader(in)
val finder = new NativeFinder
reader.accept(finder, 0)
diff --git a/plugin/src/sbt-test/sbt-jni/multiclasses/README.md b/plugin/src/sbt-test/sbt-jni/multiclasses/README.md
new file mode 100644
index 0000000..b039e7a
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/multiclasses/README.md
@@ -0,0 +1 @@
+Tests multiple native projects and native loading for classes.
diff --git a/plugin/src/sbt-test/sbt-jni/multiclasses/build.sbt b/plugin/src/sbt-test/sbt-jni/multiclasses/build.sbt
new file mode 100644
index 0000000..d12a711
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/multiclasses/build.sbt
@@ -0,0 +1,16 @@
+ivyLoggingLevel := UpdateLogging.Quiet
+
+lazy val root = (project in file(".")).
+ aggregate(core, native1, native2)
+
+lazy val core = (project in file("core")).
+ dependsOn(native1 % Runtime).
+ dependsOn(native2 % Runtime)
+
+lazy val native1 = (project in file("native1")).
+ settings(sourceDirectory in nativeCompile := sourceDirectory.value).
+ enablePlugins(JniNative)
+
+lazy val native2 = (project in file("native2")).
+ settings(sourceDirectory in nativeCompile := sourceDirectory.value).
+ enablePlugins(JniNative)
diff --git a/plugin/src/sbt-test/sbt-jni/multiclasses/core/src/main/scala/multiclasses/Adder.scala b/plugin/src/sbt-test/sbt-jni/multiclasses/core/src/main/scala/multiclasses/Adder.scala
new file mode 100644
index 0000000..91d5683
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/multiclasses/core/src/main/scala/multiclasses/Adder.scala
@@ -0,0 +1,18 @@
+package multiclasses
+
+import ch.jodersky.jni.nativeLoader
+
+@nativeLoader("demo0")
+class Adder(base0: Int) {
+
+ final private val base = base0
+
+ @native def plus(term: Int): Int
+
+}
+
+object Adder {
+
+ @native def sum(term1: Int, term2: Int): Int
+
+}
diff --git a/plugin/src/sbt-test/sbt-jni/multiclasses/core/src/main/scala/multiclasses/Main.scala b/plugin/src/sbt-test/sbt-jni/multiclasses/core/src/main/scala/multiclasses/Main.scala
new file mode 100644
index 0000000..6734ae9
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/multiclasses/core/src/main/scala/multiclasses/Main.scala
@@ -0,0 +1,30 @@
+package multiclasses
+
+object Main {
+
+ def addition(): Unit = {
+ val zero = new Adder(0)
+ val one = new Adder(1)
+ assert((zero plus 1) == 1)
+ assert((one plus 1) == 2)
+ assert(Adder.sum(0,1) == 1)
+ }
+
+ def multiplication(): Unit = {
+ val zero = new Multiplier {
+ override def base = 0
+ }
+
+ val one = new Multiplier {
+ override def base = 1
+ }
+ assert((zero times 1) == 0)
+ assert((one times 1) == 1)
+ }
+
+ def main(args: Array[String]): Unit = {
+ addition()
+ multiplication()
+ }
+
+}
diff --git a/plugin/src/sbt-test/sbt-jni/multiclasses/core/src/main/scala/multiclasses/Multiplier.scala b/plugin/src/sbt-test/sbt-jni/multiclasses/core/src/main/scala/multiclasses/Multiplier.scala
new file mode 100644
index 0000000..19cd25f
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/multiclasses/core/src/main/scala/multiclasses/Multiplier.scala
@@ -0,0 +1,12 @@
+package multiclasses
+
+import ch.jodersky.jni.nativeLoader
+
+@nativeLoader("multiplier1")
+abstract class Multiplier {
+
+ def base: Int = 1
+
+ @native def times(factor: Int): Int
+
+}
diff --git a/plugin/src/sbt-test/sbt-jni/multiclasses/native1/src/CMakeLists.txt b/plugin/src/sbt-test/sbt-jni/multiclasses/native1/src/CMakeLists.txt
new file mode 100644
index 0000000..1539ec2
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/multiclasses/native1/src/CMakeLists.txt
@@ -0,0 +1,61 @@
+################################################################
+# A minimal CMake file that is compatible with sbt-jni #
+# #
+# All settings required by sbt-jni have been marked so, please #
+# add/modify/remove settings to build your specific library. #
+################################################################
+
+cmake_minimum_required(VERSION 2.6)
+
+# Define project and related variables
+#
+project (demo)
+
+# Set versions and library name
+# (required by sbt-jni) please use semantic versioning
+#
+set (VERSION_MAJOR 0)
+set (VERSION_MINOR 0)
+set (VERSION_PATCH 0)
+# (required by sbt-jni) major version will always be appended to library name
+set (LIB_NAME ${CMAKE_PROJECT_NAME}${VERSION_MAJOR})
+
+# Command-line options
+#
+# (set by sbt-jni)
+set (LIB_INSTALL_DIR lib CACHE PATH "Path in which to install libraries (equivalent to Autoconf --libdir).")
+# (set by sbt-jni)
+set (LIB_ENABLE_MINOR_VERSIONS ON CACHE BOOLEAN "Build libraries with minor and patch versions appended.")
+
+# Setup JNI
+find_package(JNI REQUIRED)
+if (JNI_FOUND)
+ message (STATUS "JNI include directories: ${JNI_INCLUDE_DIRS}")
+endif()
+
+# Include directories
+include_directories(.)
+include_directories(../../core/target/native/include)
+include_directories(${JNI_INCLUDE_DIRS})
+
+# Setup main shared library
+file(GLOB LIB_SRC
+ "*.c"
+ "*.cpp"
+)
+add_library(${LIB_NAME} SHARED ${LIB_SRC})
+
+# By default, in a regular build, minor and patch versions are added to the generated files.
+# When built through sbt-jni however, LIB_ENABLE_MINOR_VERSIONS is deactivated and only a
+# major-versioned library file is built.
+if (LIB_ENABLE_MINOR_VERSIONS)
+ set_target_properties(
+ ${LIB_NAME}
+ PROPERTIES
+ VERSION 0.${VERSION_MINOR}.${VERSION_PATCH} # major version always 0, it is included in library name
+ SOVERSION 0
+ )
+endif()
+
+# Installation targets
+install(TARGETS ${LIB_NAME} LIBRARY DESTINATION ${LIB_INSTALL_DIR})
diff --git a/plugin/src/sbt-test/sbt-jni/multiclasses/native1/src/library.c b/plugin/src/sbt-test/sbt-jni/multiclasses/native1/src/library.c
new file mode 100644
index 0000000..8b45660
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/multiclasses/native1/src/library.c
@@ -0,0 +1,28 @@
+#include <jni.h>
+#include "multiclasses_Adder.h"
+#include "multiclasses_Adder__.h"
+
+/*
+ * Class: multiclasses_Adder
+ * Method: plus
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_multiclasses_Adder_plus
+ (JNIEnv* env, jobject instance, jint term)
+{
+ jclass clazz = (*env)->GetObjectClass(env, instance);
+ jfieldID field = (*env)->GetFieldID(env, clazz, "base", "I");
+ jint base = (*env)->GetIntField(env, instance, field);
+ return base + term;
+}
+
+/*
+ * Class: multiclasses_Adder__
+ * Method: sum
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL Java_multiclasses_Adder_00024_sum
+ (JNIEnv* env, jobject instance, jint term1, jint term2)
+{
+ return term1 + term2;
+}
diff --git a/plugin/src/sbt-test/sbt-jni/multiclasses/native2/src/CMakeLists.txt b/plugin/src/sbt-test/sbt-jni/multiclasses/native2/src/CMakeLists.txt
new file mode 100644
index 0000000..feeab89
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/multiclasses/native2/src/CMakeLists.txt
@@ -0,0 +1,61 @@
+################################################################
+# A minimal CMake file that is compatible with sbt-jni #
+# #
+# All settings required by sbt-jni have been marked so, please #
+# add/modify/remove settings to build your specific library. #
+################################################################
+
+cmake_minimum_required(VERSION 2.6)
+
+# Define project and related variables
+#
+project (multiplier)
+
+# Set versions and library name
+# (required by sbt-jni) please use semantic versioning
+#
+set (VERSION_MAJOR 1)
+set (VERSION_MINOR 2)
+set (VERSION_PATCH 3)
+# (required by sbt-jni) major version will always be appended to library name
+set (LIB_NAME ${CMAKE_PROJECT_NAME}${VERSION_MAJOR})
+
+# Command-line options
+#
+# (set by sbt-jni)
+set (LIB_INSTALL_DIR lib CACHE PATH "Path in which to install libraries (equivalent to Autoconf --libdir).")
+# (set by sbt-jni)
+set (LIB_ENABLE_MINOR_VERSIONS ON CACHE BOOLEAN "Build libraries with minor and patch versions appended.")
+
+# Setup JNI
+find_package(JNI REQUIRED)
+if (JNI_FOUND)
+ message (STATUS "JNI include directories: ${JNI_INCLUDE_DIRS}")
+endif()
+
+# Include directories
+include_directories(.)
+include_directories(../../core/target/native/include)
+include_directories(${JNI_INCLUDE_DIRS})
+
+# Setup main shared library
+file(GLOB LIB_SRC
+ "*.c"
+ "*.cpp"
+)
+add_library(${LIB_NAME} SHARED ${LIB_SRC})
+
+# By default, in a regular build, minor and patch versions are added to the generated files.
+# When built through sbt-jni however, LIB_ENABLE_MINOR_VERSIONS is deactivated and only a
+# major-versioned library file is built.
+if (LIB_ENABLE_MINOR_VERSIONS)
+ set_target_properties(
+ ${LIB_NAME}
+ PROPERTIES
+ VERSION 0.${VERSION_MINOR}.${VERSION_PATCH} # major version always 0, it is included in library name
+ SOVERSION 0
+ )
+endif()
+
+# Installation targets
+install(TARGETS ${LIB_NAME} LIBRARY DESTINATION ${LIB_INSTALL_DIR})
diff --git a/plugin/src/sbt-test/sbt-jni/multiclasses/native2/src/library.c b/plugin/src/sbt-test/sbt-jni/multiclasses/native2/src/library.c
new file mode 100644
index 0000000..5fe817e
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/multiclasses/native2/src/library.c
@@ -0,0 +1,16 @@
+#include <jni.h>
+#include "multiclasses_Multiplier.h"
+
+/*
+ * Class: multiclasses_Multiplier
+ * Method: times
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_multiclasses_Multiplier_times
+ (JNIEnv* env, jobject instance, jint factor)
+{
+ jclass clazz = (*env)->GetObjectClass(env, instance);
+ jmethodID method = (*env)->GetMethodID(env, clazz, "base", "()I");
+ jint base = (*env)->CallIntMethod(env, instance, method);
+ return base * factor;
+}
diff --git a/plugin/src/sbt-test/sbt-jni/multiclasses/project/ScriptedHelper.scala b/plugin/src/sbt-test/sbt-jni/multiclasses/project/ScriptedHelper.scala
new file mode 120000
index 0000000..aeaba9d
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/multiclasses/project/ScriptedHelper.scala
@@ -0,0 +1 @@
+../../simple/project/ScriptedHelper.scala \ No newline at end of file
diff --git a/plugin/src/sbt-test/sbt-jni/multiclasses/project/plugins.sbt b/plugin/src/sbt-test/sbt-jni/multiclasses/project/plugins.sbt
new file mode 120000
index 0000000..1c60ea7
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/multiclasses/project/plugins.sbt
@@ -0,0 +1 @@
+../../simple/project/plugins.sbt \ No newline at end of file
diff --git a/plugin/src/sbt-test/sbt-jni/multiclasses/test b/plugin/src/sbt-test/sbt-jni/multiclasses/test
new file mode 100644
index 0000000..e9e0d97
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/multiclasses/test
@@ -0,0 +1,2 @@
+> javah
+> core/run \ No newline at end of file
diff --git a/plugin/src/sbt-test/sbt-jni/oneproject/README.md b/plugin/src/sbt-test/sbt-jni/oneproject/README.md
new file mode 100644
index 0000000..9d78fef
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/oneproject/README.md
@@ -0,0 +1 @@
+Tests the combination of native and scala sources in a single project.
diff --git a/plugin/src/sbt-test/sbt-jni/oneproject/build.sbt b/plugin/src/sbt-test/sbt-jni/oneproject/build.sbt
new file mode 100644
index 0000000..3b95d55
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/oneproject/build.sbt
@@ -0,0 +1,6 @@
+ivyLoggingLevel := UpdateLogging.Quiet
+
+enablePlugins(JniNative)
+
+target in javah := (sourceDirectory in nativeCompile).value / "include"
+
diff --git a/plugin/src/sbt-test/sbt-jni/oneproject/project/ScriptedHelper.scala b/plugin/src/sbt-test/sbt-jni/oneproject/project/ScriptedHelper.scala
new file mode 120000
index 0000000..aeaba9d
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/oneproject/project/ScriptedHelper.scala
@@ -0,0 +1 @@
+../../simple/project/ScriptedHelper.scala \ No newline at end of file
diff --git a/plugin/src/sbt-test/sbt-jni/oneproject/project/plugins.sbt b/plugin/src/sbt-test/sbt-jni/oneproject/project/plugins.sbt
new file mode 120000
index 0000000..1c60ea7
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/oneproject/project/plugins.sbt
@@ -0,0 +1 @@
+../../simple/project/plugins.sbt \ No newline at end of file
diff --git a/plugin/src/sbt-test/sbt-jni/oneproject/src/main b/plugin/src/sbt-test/sbt-jni/oneproject/src/main
new file mode 120000
index 0000000..1b483d7
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/oneproject/src/main
@@ -0,0 +1 @@
+../../simple/core/src/main/ \ No newline at end of file
diff --git a/plugin/src/sbt-test/sbt-jni/oneproject/src/native b/plugin/src/sbt-test/sbt-jni/oneproject/src/native
new file mode 120000
index 0000000..ebbac72
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/oneproject/src/native
@@ -0,0 +1 @@
+../../simple/native/src/ \ No newline at end of file
diff --git a/plugin/src/sbt-test/sbt-jni/oneproject/test b/plugin/src/sbt-test/sbt-jni/oneproject/test
new file mode 100644
index 0000000..2c485fa
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/oneproject/test
@@ -0,0 +1,4 @@
+> javah
+$ exists src/native/include/simple_Library__.h
+> nativeInit cmake demo
+> run \ No newline at end of file
diff --git a/plugin/src/sbt-test/sbt-jni/simple/README.md b/plugin/src/sbt-test/sbt-jni/simple/README.md
new file mode 100644
index 0000000..54179b2
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/simple/README.md
@@ -0,0 +1 @@
+Very basic test.
diff --git a/plugin/src/sbt-test/sbt-jni/simple/build.sbt b/plugin/src/sbt-test/sbt-jni/simple/build.sbt
new file mode 100644
index 0000000..e324d5a
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/simple/build.sbt
@@ -0,0 +1,12 @@
+ivyLoggingLevel := UpdateLogging.Quiet
+
+lazy val root = (project in file(".")).
+ aggregate(core, native)
+
+lazy val core = (project in file("core")).
+ settings(target in javah := (sourceDirectory in nativeCompile in native).value / "include").
+ dependsOn(native % Runtime)
+
+lazy val native = (project in file("native")).
+ settings(sourceDirectory in nativeCompile := sourceDirectory.value).
+ enablePlugins(JniNative)
diff --git a/plugin/src/sbt-test/sbt-jni/simple/core/src/main/scala/simple/Library.scala b/plugin/src/sbt-test/sbt-jni/simple/core/src/main/scala/simple/Library.scala
new file mode 100644
index 0000000..785f8dd
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/simple/core/src/main/scala/simple/Library.scala
@@ -0,0 +1,10 @@
+package simple
+
+import ch.jodersky.jni.nativeLoader
+
+@nativeLoader("demo0")
+object Library {
+
+ @native def say(message: String): Int
+
+}
diff --git a/plugin/src/sbt-test/sbt-jni/simple/core/src/main/scala/simple/Main.scala b/plugin/src/sbt-test/sbt-jni/simple/core/src/main/scala/simple/Main.scala
new file mode 100644
index 0000000..c54d6e0
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/simple/core/src/main/scala/simple/Main.scala
@@ -0,0 +1,10 @@
+package simple
+
+object Main {
+
+ def main(args: Array[String]): Unit = {
+ val result = Library.say("hello world")
+ assert(result == 42)
+ }
+
+}
diff --git a/plugin/src/sbt-test/sbt-jni/simple/native/src/library.c b/plugin/src/sbt-test/sbt-jni/simple/native/src/library.c
new file mode 100644
index 0000000..4fa15b1
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/simple/native/src/library.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include "simple_Library__.h"
+
+/*
+ * Class: simple_Library__
+ * Method: say
+ * Signature: (Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_simple_Library_00024_say
+(JNIEnv *env, jobject clazz, jstring message) {
+ const char* msg = (*env)->GetStringUTFChars(env, message, 0);
+ fprintf(stdout, "Printing from native library: %s\n", msg);
+ fflush(stdout);
+ (*env)->ReleaseStringUTFChars(env, message, msg);
+ return 42;
+}
diff --git a/plugin/src/sbt-test/sbt-jni/simple/project/ScriptedHelper.scala b/plugin/src/sbt-test/sbt-jni/simple/project/ScriptedHelper.scala
new file mode 100644
index 0000000..cd63c89
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/simple/project/ScriptedHelper.scala
@@ -0,0 +1,14 @@
+import sbt._
+import sbt.Keys._
+
+object ScriptedHelper extends AutoPlugin {
+
+ override def requires = empty
+ override def trigger = allRequirements
+
+ override def projectSettings = Seq(
+ crossScalaVersions := Seq("2.11.8", "2.12.0-M4"),
+ scalaVersion := crossScalaVersions.value.head
+ )
+
+}
diff --git a/plugin/src/sbt-test/sbt-jni/simple/project/plugins.sbt b/plugin/src/sbt-test/sbt-jni/simple/project/plugins.sbt
new file mode 100644
index 0000000..b8fc7ed
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/simple/project/plugins.sbt
@@ -0,0 +1,3 @@
+ivyLoggingLevel := UpdateLogging.Quiet
+
+addSbtPlugin("ch.jodersky" % "sbt-jni" % System.getProperty("plugin.version"))
diff --git a/plugin/src/sbt-test/sbt-jni/simple/test b/plugin/src/sbt-test/sbt-jni/simple/test
new file mode 100644
index 0000000..dd9e5bf
--- /dev/null
+++ b/plugin/src/sbt-test/sbt-jni/simple/test
@@ -0,0 +1,4 @@
+> javah
+$ exists native/src/include/simple_Library__.h
+> nativeInit cmake demo
+> core/run \ No newline at end of file
diff --git a/project/Build.scala b/project/Build.scala
deleted file mode 100644
index 377730d..0000000
--- a/project/Build.scala
+++ /dev/null
@@ -1,64 +0,0 @@
-import sbt._
-import sbt.Keys._
-
-import bintray.BintrayPlugin.autoImport._
-import sbtdoge.CrossPerProjectPlugin
-
-object JniBuild extends Build {
-
- val scalaVersions = List("2.11.8", "2.12.0-M4", "2.12.0-M3", "2.10.6")
-
- val commonSettings = Seq(
- version := "0.4.5-SNAPSHOT",
- organization := "ch.jodersky",
- licenses := Seq(("BSD New", url("http://opensource.org/licenses/BSD-3-Clause"))),
- scalacOptions ++= Seq("-deprecation", "-feature")
- )
-
- lazy val root = Project(
- id = "root",
- base = file("."),
- aggregate = Seq(
- library, plugin
- ),
- settings = Seq(
- publish := {},
- publishLocal := {},
- publishTo := Some(Resolver.file("Unused transient repository", target.value / "unusedrepo")) // make sbt-pgp happy
- )
- ).enablePlugins(CrossPerProjectPlugin)
-
- lazy val library = Project(
- id = "jni-library",
- base = file("jni-library"),
- settings = commonSettings ++ Seq(
- scalaVersion := scalaVersions.head,
- crossScalaVersions := scalaVersions
- )
- )
-
- lazy val plugin = Project(
- id = "sbt-jni",
- base = file("jni-plugin"),
- dependencies = Seq(library),
- settings = commonSettings ++ Seq(
- sbtPlugin := true,
- sourceGenerators in Compile += Def.task{
- val src = s"""|package ch.jodersky.sbt.jni
- |
- |private object Version {
- | final val PluginVersion = "${version.value}"
- |}
- |""".stripMargin
- val file = sourceManaged.value / "ch" / "jodersky" / "sbt" / "jni" / "Version.scala"
- IO.write(file, src)
- Seq(file)
- }.taskValue,
- libraryDependencies += "org.ow2.asm" % "asm" % "5.0.4",
- publishMavenStyle := false,
- bintrayRepository := "sbt-plugins",
- bintrayOrganization in bintray := None
- )
- )
-
-}
diff --git a/project/SbtJniBuild.scala b/project/SbtJniBuild.scala
new file mode 100644
index 0000000..3961eda
--- /dev/null
+++ b/project/SbtJniBuild.scala
@@ -0,0 +1,86 @@
+import sbt._
+import sbt.Keys._
+import sbt.ScriptedPlugin._
+
+import sbtdoge.CrossPerProjectPlugin
+
+import scalariform.formatter.preferences._
+import com.typesafe.sbt.SbtScalariform
+import com.typesafe.sbt.SbtScalariform.ScalariformKeys
+
+object SbtJniBuild extends Build {
+
+ val scalaVersions: Seq[String] = List("2.11.8", "2.12.0-M4")
+ val macrosParadiseVersion = "2.1.0"
+
+ val commonSettings = Seq(
+ version := "1.0.0-SNAPSHOT",
+ organization := "ch.jodersky",
+ licenses := Seq(("BSD New", url("http://opensource.org/licenses/BSD-3-Clause"))),
+ scalacOptions ++= Seq("-deprecation", "-feature"),
+ resolvers += Resolver.sonatypeRepo("releases"),
+ ScalariformKeys.preferences := ScalariformKeys.preferences.value
+ .setPreference(DoubleIndentClassDeclaration, true)
+ .setPreference(DanglingCloseParenthesis, Preserve)
+ .setPreference(MultilineScaladocCommentsStartOnFirstLine, true)
+ .setPreference(PlaceScaladocAsterisksBeneathSecondAsterisk, true)
+ )
+
+ lazy val root = Project(
+ id = "root",
+ base = file("."),
+ aggregate = Seq(
+ macros, plugin
+ ),
+ settings = commonSettings ++ Seq(
+ publish := {},
+ publishLocal := {},
+ // make sbt-pgp happy
+ publishTo := Some(Resolver.file("Unused transient repository", target.value / "unusedrepo"))
+ ) ++ addCommandAlias("test-plugin", ";+sbt-jni-macros/publishLocal;scripted")
+ ).enablePlugins(CrossPerProjectPlugin)
+
+ lazy val macros = Project(
+ id = "sbt-jni-macros",
+ base = file("macros"),
+ settings = commonSettings ++ Seq(
+ scalaVersion := scalaVersions.head,
+ crossScalaVersions := scalaVersions,
+ addCompilerPlugin("org.scalamacros" % "paradise" % macrosParadiseVersion cross CrossVersion.full),
+ libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value
+ )
+ )
+
+ lazy val plugin = Project(
+ id = "sbt-jni",
+ base = file("plugin"),
+ settings = commonSettings ++ scriptedSettings ++ Seq(
+ sbtPlugin := true,
+ publishMavenStyle := false,
+ scalaVersion := "2.10.6",
+ crossScalaVersions := Seq(scalaVersion.value),
+
+ libraryDependencies += "org.ow2.asm" % "asm" % "5.0.4",
+
+ // make project settings available to source
+ sourceGenerators in Compile += Def.task{
+ val src = s"""|/* Generated by sbt */
+ |package ch.jodersky.sbt.jni
+ |
+ |private[jni] object ProjectVersion {
+ | final val MacrosParadise = "${macrosParadiseVersion}"
+ | final val Macros = "${version.value}"
+ |}
+ |""".stripMargin
+ val file = sourceManaged.value / "ch" / "jodersky" / "sbt" / "jni" / "ProjectVersion.scala"
+ IO.write(file, src)
+ Seq(file)
+ }.taskValue,
+ scriptedLaunchOpts := Seq(
+ "-Dplugin.version=" + version.value,
+ "-XX:MaxPermSize=256m", "-Xmx2g", "-Xss2m"
+ )
+ )
+ )
+
+}
diff --git a/project/build.properties b/project/build.properties
index 817bc38..43b8278 100644
--- a/project/build.properties
+++ b/project/build.properties
@@ -1 +1 @@
-sbt.version=0.13.9
+sbt.version=0.13.11
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 391c9ab..19a16f4 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -1,3 +1,8 @@
-addSbtPlugin("me.lessis" % "bintray-sbt" % "0.3.0")
-
+// cross-compile subprojects with differing scala versions
addSbtPlugin("com.eed3si9n" % "sbt-doge" % "0.1.5")
+
+// testing for sbt plugins
+libraryDependencies += "org.scala-sbt" % "scripted-plugin" % sbtVersion.value
+
+// formatting
+addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.6.0")
diff --git a/samples/basic/README.md b/samples/basic/README.md
deleted file mode 100644
index 0f29336..0000000
--- a/samples/basic/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Basic demo application using sbt-jni
-
-Publish plugin before running.
diff --git a/samples/basic/basic-core/build.sbt b/samples/basic/basic-core/build.sbt
deleted file mode 100644
index cd51bb3..0000000
--- a/samples/basic/basic-core/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-//enablePlugins(JniLoader)
diff --git a/samples/basic/basic-core/src/main/scala/ch/jodersky/jni/basic/Library.scala b/samples/basic/basic-core/src/main/scala/ch/jodersky/jni/basic/Library.scala
deleted file mode 100644
index efe3018..0000000
--- a/samples/basic/basic-core/src/main/scala/ch/jodersky/jni/basic/Library.scala
+++ /dev/null
@@ -1,8 +0,0 @@
-package ch.jodersky.jni.basic
-
-/** A demo object, mapping to a native library. */
-object Library {
-
- @native def print(message: String): Int
-
-}
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
deleted file mode 100644
index 2aab1ed..0000000
--- a/samples/basic/basic-core/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/native", "demo1")
- val result: Int = Library.print("Hello world!\n")
- println("Returned: " + result)
- }
-
-}
diff --git a/samples/basic/basic-native/src/CMakeLists.txt b/samples/basic/basic-native/src/CMakeLists.txt
deleted file mode 100644
index ec112b3..0000000
--- a/samples/basic/basic-native/src/CMakeLists.txt
+++ /dev/null
@@ -1,46 +0,0 @@
-cmake_minimum_required(VERSION 2.6)
-
-# Define project and related variables
-#
-project (demo)
-
-# Set versions and library name
-# Note: major version will be appended to library name
-#
-set (VERSION_MAJOR 1)
-set (VERSION_MINOR 2)
-set (VERSION_PATCH 3)
-set (LIB_NAME demo${VERSION_MAJOR})
-
-# Command-line options
-#
-# required by sbt-jni to install binaries to correct places
-set (LIB_INSTALL_DIR lib CACHE PATH "Path in which to install libraries (Autoconf equivalent to --libdir).")
-# required by sbt-jni to disable versioned libraries
-set (ENABLE_VERSIONED_LIB ON CACHE BOOLEAN "Generate versioned library files and symlinks.")
-
-# Setup JNI
-find_package(JNI REQUIRED)
-if (JNI_FOUND)
- message (STATUS "JNI include directories: ${JNI_INCLUDE_DIRS}")
-endif()
-
-# Include directories
-include_directories(.)
-include_directories(include)
-include_directories(${JNI_INCLUDE_DIRS})
-
-# Setup main shared library
-# Note: major version is appended to library name
-add_library(${LIB_NAME} SHARED library.c)
-if (ENABLE_VERSIONED_LIB)
- set_target_properties(
- ${LIB_NAME}
- PROPERTIES
- VERSION 0.${VERSION_MINOR}.${VERSION_PATCH} # major version always 0, it is included in name
- SOVERSION 0
- )
-endif()
-
-# Installation targets
-install(TARGETS ${LIB_NAME} LIBRARY DESTINATION ${LIB_INSTALL_DIR})
diff --git a/samples/basic/basic-native/src/include/ch_jodersky_jni_basic_Library__.h b/samples/basic/basic-native/src/include/ch_jodersky_jni_basic_Library__.h
deleted file mode 100644
index 60821e4..0000000
--- a/samples/basic/basic-native/src/include/ch_jodersky_jni_basic_Library__.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-/* Header for class ch_jodersky_jni_basic_Library__ */
-
-#ifndef _Included_ch_jodersky_jni_basic_Library__
-#define _Included_ch_jodersky_jni_basic_Library__
-#ifdef __cplusplus
-extern "C" {
-#endif
-/*
- * Class: ch_jodersky_jni_basic_Library__
- * Method: print
- * Signature: (Ljava/lang/String;)I
- */
-JNIEXPORT jint JNICALL Java_ch_jodersky_jni_basic_Library_00024_print
- (JNIEnv *, jobject, jstring);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/samples/basic/basic-native/src/library.c b/samples/basic/basic-native/src/library.c
deleted file mode 100644
index eec05e5..0000000
--- a/samples/basic/basic-native/src/library.c
+++ /dev/null
@@ -1,16 +0,0 @@
-#include <stdio.h>
-#include "ch_jodersky_jni_basic_Library__.h"
-
-/*
- * Class: ch_jodersky_jni_basic_Library__
- * Method: print
- * Signature: (Ljava/lang/String;)I
- */
-JNIEXPORT jint JNICALL Java_ch_jodersky_jni_basic_Library_00024_print
-(JNIEnv *env, jobject clazz, jstring message) {
- const char* msg = (*env)->GetStringUTFChars(env, message, 0);
- fprintf(stdout, "Printing from native library: %s", msg);
- fflush(stdout);
- (*env)->ReleaseStringUTFChars(env, message, msg);
- return 0;
-}
diff --git a/samples/basic/build.sbt b/samples/basic/build.sbt
deleted file mode 100644
index 7f5bedd..0000000
--- a/samples/basic/build.sbt
+++ /dev/null
@@ -1,31 +0,0 @@
-val commonSettings = Seq(
- scalaVersion := "2.11.7",
- organization := "ch.jodersky"
-)
-
-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 javah in Compile := (sourceDirectory in native).value / "include"
- ),
- dependencies = Seq(
- native % Runtime
- )
-).enablePlugins(JniLoading)
-
-lazy val native = Project(
- id = "basic-native",
- base = file("basic-native"),
- settings = commonSettings ++ Seq(
- //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/build.properties b/samples/basic/project/build.properties
deleted file mode 100644
index 817bc38..0000000
--- a/samples/basic/project/build.properties
+++ /dev/null
@@ -1 +0,0 @@
-sbt.version=0.13.9
diff --git a/samples/basic/project/plugins.sbt b/samples/basic/project/plugins.sbt
deleted file mode 100644
index ec8b3e3..0000000
--- a/samples/basic/project/plugins.sbt
+++ /dev/null
@@ -1 +0,0 @@
-addSbtPlugin("ch.jodersky" % "sbt-jni" % "0.4.0-SNAPSHOT")