aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--README.md14
-rw-r--r--examples/demo/build.sbt16
-rw-r--r--examples/demo/native/CMakeLists.txt45
-rw-r--r--examples/demo/native/main.c16
-rw-r--r--examples/demo/native/org_example_jni_demo_Library__.h21
-rw-r--r--examples/demo/project/plugins.sbt1
-rw-r--r--examples/demo/src/main/scala/org/example/jni/demo/Library.scala8
-rw-r--r--examples/demo/src/main/scala/org/example/jni/demo/Main.scala12
-rw-r--r--jni-library/src/main/scala/ch/jodersky/jni/NativeLoader.scala75
-rw-r--r--jni-library/src/main/scala/ch/jodersky/jni/Platform.scala50
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/Defaults.scala192
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniPlugin.scala19
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/Keys.scala53
-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.scala23
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/CMake.scala60
-rw-r--r--jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/NoTool.scala9
-rw-r--r--project/Build.scala36
-rw-r--r--project/build.properties1
20 files changed, 670 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7c124b5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+.ensime_cache/
+.ensime
+target/
+*.class
+*~ \ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d861fec
--- /dev/null
+++ b/README.md
@@ -0,0 +1,14 @@
+# Definitions
+- Native binaries: artifacts such as libraries and executables that are platform dependent, typically C/C++ projects
+
+# Goals:
+
+## Wrapper
+- Native binaries are compiled with another build tool, such as Make, Autotools or CMake
+- Provides a "Native" configuration with most standard sbt tasks.
+Why not provide separate tasks, like nativeCompile and nativeTest? individual tasks don't map, that's the whole idea to use a wrapper
+
+## JNI
+- Generate JNI headers
+
+
diff --git a/examples/demo/build.sbt b/examples/demo/build.sbt
new file mode 100644
index 0000000..8cd1419
--- /dev/null
+++ b/examples/demo/build.sbt
@@ -0,0 +1,16 @@
+enablePlugins(JniPlugin)
+
+name := "jni-demo"
+
+organization := "org.example"
+
+baseDirectory in jni := (baseDirectory in ThisBuild).value / "native"
+
+javahObjects in javah += "org.example.jni.demo.Library"
+
+libraryDependencies += "ch.jodersky" %% "jni-library" % "0.1-SNAPSHOT"
+
+//exportJars in run := true
+
+//librearDependencies += "org.example" %% "demo" % "0.1"
+//librearDependencies += "org.example" % "demo" % "0.1" % "runtime" classifier "native"
diff --git a/examples/demo/native/CMakeLists.txt b/examples/demo/native/CMakeLists.txt
new file mode 100644
index 0000000..059a0dd
--- /dev/null
+++ b/examples/demo/native/CMakeLists.txt
@@ -0,0 +1,45 @@
+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(${JNI_INCLUDE_DIRS})
+
+# Setup main shared library
+# Note: major version is appended to library name
+add_library(${LIB_NAME} SHARED main.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/examples/demo/native/main.c b/examples/demo/native/main.c
new file mode 100644
index 0000000..5916c48
--- /dev/null
+++ b/examples/demo/native/main.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include "org_example_jni_demo_Library__.h"
+
+/*
+ * Class: org_example_jni_demo_Library__
+ * Method: print
+ * Signature: (Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_example_jni_demo_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/examples/demo/native/org_example_jni_demo_Library__.h b/examples/demo/native/org_example_jni_demo_Library__.h
new file mode 100644
index 0000000..06a7fa4
--- /dev/null
+++ b/examples/demo/native/org_example_jni_demo_Library__.h
@@ -0,0 +1,21 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_example_jni_demo_Library__ */
+
+#ifndef _Included_org_example_jni_demo_Library__
+#define _Included_org_example_jni_demo_Library__
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_example_jni_demo_Library__
+ * Method: print
+ * Signature: (Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_example_jni_demo_Library_00024_print
+ (JNIEnv *, jobject, jstring);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/examples/demo/project/plugins.sbt b/examples/demo/project/plugins.sbt
new file mode 100644
index 0000000..ab5617e
--- /dev/null
+++ b/examples/demo/project/plugins.sbt
@@ -0,0 +1 @@
+addSbtPlugin("ch.jodersky" % "sbt-jni" % "0.1-SNAPSHOT")
diff --git a/examples/demo/src/main/scala/org/example/jni/demo/Library.scala b/examples/demo/src/main/scala/org/example/jni/demo/Library.scala
new file mode 100644
index 0000000..c157b5d
--- /dev/null
+++ b/examples/demo/src/main/scala/org/example/jni/demo/Library.scala
@@ -0,0 +1,8 @@
+package org.example.jni.demo
+
+/** A demo object, mapping to a native library. */
+object Library {
+
+ @native def print(message: String): Int
+
+}
diff --git a/examples/demo/src/main/scala/org/example/jni/demo/Main.scala b/examples/demo/src/main/scala/org/example/jni/demo/Main.scala
new file mode 100644
index 0000000..7306104
--- /dev/null
+++ b/examples/demo/src/main/scala/org/example/jni/demo/Main.scala
@@ -0,0 +1,12 @@
+package org.example.jni.demo
+
+import ch.jodersky.jni.NativeLoader
+
+object Main {
+
+ def main(args: Array[String]): Unit = {
+ NativeLoader.load("demo1", "/org/example/jni/demo")
+ Library.print("Hello world!")
+ }
+
+}
diff --git a/jni-library/src/main/scala/ch/jodersky/jni/NativeLoader.scala b/jni-library/src/main/scala/ch/jodersky/jni/NativeLoader.scala
new file mode 100644
index 0000000..46d4b2d
--- /dev/null
+++ b/jni-library/src/main/scala/ch/jodersky/jni/NativeLoader.scala
@@ -0,0 +1,75 @@
+package ch.jodersky.jni
+
+import java.io.{File, FileOutputStream, InputStream, OutputStream}
+import scala.io.Source
+
+/**
+ * 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
+
+ /** Extract 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
+ )
+
+ def fullLibraryPath(libraryPath: String, platform: Platform) = {
+ libraryPath + "/native/" + 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())
+ }
+
+ /**
+ * Load a native library from the available library path or fall back
+ * to extracting and loading a native library from available resources.
+ */
+ def load(library: String, libraryPath: 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
new file mode 100644
index 0000000..db1662d
--- /dev/null
+++ b/jni-library/src/main/scala/ch/jodersky/jni/Platform.scala
@@ -0,0 +1,50 @@
+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 {
+
+ final val Unknown = Platform("unknown", "unknown")
+
+ /** Create 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))
+ }
+
+ /** Run '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))
+ }
+ }
+ }
+
+ def current = uname
+
+}
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/Defaults.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/Defaults.scala
new file mode 100644
index 0000000..49f4a49
--- /dev/null
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/Defaults.scala
@@ -0,0 +1,192 @@
+package ch.jodersky.sbt.jni
+
+import sbt._
+import sbt.Keys._
+import Keys._
+
+import build.CMake
+import build.BuildTool
+
+import ch.jodersky.jni.Platform
+import ch.jodersky.jni.NativeLoader
+
+object Defaults {
+
+ lazy val buildSettings: Seq[Setting[_]] = Seq(
+
+ baseDirectory in jni := (baseDirectory in ThisBuild).value / "native",
+
+ target in jni := target.value / "native" / (jniPlatform in jni).value.id,
+
+ jniPlatform in jni := Platform.current.getOrElse{
+ sLog.value.warn("Warning: cannot determine platform! It will be set to 'unknown'.")
+ Platform.Unknown
+ },
+
+ jniBuildTool in jni := {
+ val tools = Seq(CMake)
+
+ val base = (baseDirectory in jni).value
+
+ val tool = if (base.exists && base.isDirectory) {
+ tools.find(t => t detect base)
+ } else {
+ None
+ }
+ tool.getOrElse(
+ sys.error("No supported native build tool detected. " +
+ s"Check that the setting 'baseDirectory in jni' (currently $base) " +
+ "points to a valid directory. Supported build tools are: " +
+ tools.map(_.name).mkString(",")
+ )
+ )
+ },
+
+ clean := {
+ val log = streams.value.log
+ val tool = try {
+ Some((jniBuildTool in jni).value)
+ } catch {
+ case _: Exception => None
+ }
+ tool foreach { t =>
+ log.debug("Cleaning native build")
+ t.api.clean(
+ (baseDirectory in jni).value,
+ log
+ )
+ }
+ clean.value
+ },
+
+ jni := {
+ val tool = (jniBuildTool in jni).value
+ val dir = (baseDirectory in jni).value
+ val targetDir = (target in jni).value / "lib"
+ val log = streams.value.log
+
+ tool.api.library(dir, targetDir, log)
+ },
+
+ javahClasses in javah := Seq(),
+
+ javahObjects in javah := Seq(),
+
+ target in javah := (baseDirectory in jni).value,
+
+ fullClasspath in javah := (fullClasspath in Compile).value,
+
+ javah := {
+ val out = (target in javah).value
+ val jcp: Seq[File] = (fullClasspath in javah).value.map(_.data)
+ val cp = jcp.mkString(sys.props("path.separator"))
+
+ val classes = (javahClasses in javah).value ++
+ (javahObjects in javah).value.map(_ + "$")
+
+ for (clazz <- classes) {
+ val parts = Seq(
+ "javah",
+ "-d", out.getAbsolutePath,
+ "-classpath", cp,
+ clazz)
+ val cmd = parts.mkString(" ")
+ val ev = Process(cmd) ! streams.value.log
+ if (ev != 0) sys.error(s"Error occured running javah. Exit code: ${ev}")
+ }
+
+ out
+ }
+
+ )
+
+ lazy val bundleSettings: Seq[Setting[_]] = Seq(
+
+ jniLibraryPath in jni := {
+ "/" + (organization.value + "." + name.value).replaceAll("\\.|-", "/")
+ },
+
+ unmanagedResourceDirectories in jni := Seq(
+ (baseDirectory).value / "lib_native"
+ ),
+
+ unmanagedClasspath ++= (unmanagedResourceDirectories in jni).value.map{ file =>
+ Attributed.blank(file)
+ },
+
+ resourceManaged in jni := (target in jni).value / "lib_managed",
+
+ managedResourceDirectories in jni := Seq(
+ (resourceManaged in jni).value
+ ),
+
+ managedClasspath ++= {
+
+ //build native library
+ val library = jni.value
+
+ //native library as a managed resource file
+ val libraryResource = (resourceManaged in jni).value /
+ NativeLoader.fullLibraryPath(
+ (jniLibraryPath in jni).value,
+ (jniPlatform in jni).value
+ )
+
+ //copy native library to a managed resource (so it can also be loaded when not packages as a jar)
+ IO.copyFile(library, libraryResource)
+
+ Seq(Attributed.blank((resourceManaged in jni).value))
+ },
+
+ packageJni in Global := {
+
+ val unmanagedMappings: Seq[(File, String)] = (unmanagedResourceDirectories in jni).value flatMap { dir =>
+ val files = (dir ** "*").filter(_.isFile).get
+ files map { file =>
+ println(file.getAbsolutePath)
+ file -> (file relativeTo dir).get.getPath
+ }
+ }
+
+ managedClasspath.value //call this to generate files in resourceManaged
+
+ val managedMappings: Seq[(File, String)] = (managedResourceDirectories in jni).value flatMap { dir =>
+ val files = (dir ** "*").filter(_.isFile).get
+ files map { file =>
+ println(file.getAbsolutePath)
+ file -> (file relativeTo dir).get.getPath
+ }
+ }
+
+ val out = target.value / (name.value + "-native.jar")
+
+ val manifest = new java.util.jar.Manifest
+ Package.makeJar(
+ unmanagedMappings ++ managedMappings,
+ out,
+ manifest,
+ streams.value.log
+ )
+ out
+ }
+
+ )
+
+ lazy val clientSettings: Seq[Setting[_]] = Seq(
+ libraryDependencies += "ch.jodersky" %% "jni-library" % "0.1-SNAPSHOT",
+ fork in run := true,
+ artifact in jni := {
+ Artifact(
+ name = name.value,
+ `type` = "jar",
+ extension = "jar",
+ classifier = Some("native"),
+ configurations = Seq(Runtime),
+ url = None
+ ) extra (
+ "platform" -> "all"
+ )
+ }
+ ) ++ addArtifact(artifact in jni, packageJni)
+
+}
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniPlugin.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniPlugin.scala
new file mode 100644
index 0000000..98f8214
--- /dev/null
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniPlugin.scala
@@ -0,0 +1,19 @@
+package ch.jodersky.sbt.jni
+
+import sbt._
+
+object JniPlugin extends AutoPlugin {
+
+ override def requires = plugins.JvmPlugin
+
+ lazy val autoImport = Keys
+ import autoImport._
+
+ override lazy val projectSettings =
+ Defaults.buildSettings ++
+ inConfig(Runtime)(Defaults.bundleSettings) ++
+ Defaults.clientSettings
+ //++inConfig(Runtime)(Defaults.bundleSettings)
+
+
+}
diff --git a/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/Keys.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/Keys.scala
new file mode 100644
index 0000000..4286ff6
--- /dev/null
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/Keys.scala
@@ -0,0 +1,53 @@
+package ch.jodersky.sbt.jni
+
+import build.BuildTool
+
+import ch.jodersky.jni.Platform
+import sbt._
+
+object Keys {
+
+ val jni = taskKey[File]("Builds a native library (by calling the native build tool).")
+ val jniClean = taskKey[Unit]("Cleans the native build directory.")
+
+ val jniPlatform = settingKey[Platform]("Platform of the system this build is running on.")
+ val jniBuildTool = taskKey[BuildTool]("The build tool to be used when building a native library.")
+
+ //bundle
+ val jniLibraryPath = settingKey[String]("Foo")
+ val packageJni = taskKey[File]("Package native librraies into a fat jar.")
+
+ val javahClasses = settingKey[Seq[String]]("Fully qualified names of classes containing native declarations.")
+ val javahObjects = settingKey[Seq[String]]("Fully qualified names of singleton objects containing native declarations.")
+ val javah = taskKey[File]("Generate JNI headers. Returns the directory containing generated headers.")
+
+
+ //ivy
+ //libraryDependencies += "com.github.jodersky" % "flow" % "2.4" extra("platform", "all") artifact("libflow", "so")
+
+ //maven
+ //libraryDependencies += "com.github.jodersky" % "flow" % "2.4" classifier "native"
+
+
+
+ //Wraps tasks associated to an existing native build tool
+ //val Native = config("native")
+
+ //Extra tasks in native
+ // val buildTool = settingKey[BuildTool]("The native build tool used.")
+ //val platform = settingKey[Platform]("Platform of the system this build is being run on.")
+
+ // organization = org.example
+ // name = foo-bar
+ // libraryPrefix = organization + "." + normalize(name)
+ // libraryPrefix = org/example/foo/bar
+ // libraryPrefix = org/example/foo/bar/native/<platform>/libraries
+
+
+ //val libraryPath = settingKey[String](
+ // "A slash (/) seprated path that specifies where native libraries should be stored (in a jar).")
+ //val libraryResourceDirectory = settingKey[File]("")
+
+ //Javah
+
+}
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
new file mode 100644
index 0000000..23df2b2
--- /dev/null
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/BuildTool.scala
@@ -0,0 +1,14 @@
+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
new file mode 100644
index 0000000..47d9a90
--- /dev/null
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/BuildToolApi.scala
@@ -0,0 +1,23 @@
+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.
+ */
+ def library(
+ baseDirectory: File,
+ targetDirectory: 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
new file mode 100644
index 0000000..cd0b363
--- /dev/null
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/CMake.scala
@@ -0,0 +1,60 @@
+package ch.jodersky.sbt.jni
+package build
+
+import Keys._
+import sbt._
+import sbt.Keys._
+import sbt.Logger
+import java.io.File
+
+
+object CMake extends BuildTool {
+
+ val name = "CMake"
+
+ def detect(baseDirectory: File) = baseDirectory.list().contains("CMakeLists.txt")
+
+ object api extends BuildToolApi {
+
+ def clean(baseDirectory: File, log: Logger) = Process("make clean", baseDirectory) ! log
+
+ def library(
+ baseDirectory: File,
+ targetDirectory: File,
+ log: Logger
+ ): File = {
+ val out = targetDirectory
+ val outPath = out.getAbsolutePath
+
+ val configure = Process(
+ //Disable producing versioned library files, not needed for fat jars.
+ s"cmake -DCMAKE_INSTALL_PREFIX:PATH=$outPath -DLIB_INSTALL_DIR:PATH=$outPath -DENABLE_VERSIONED_LIB:BOOLEAN=OFF",
+ baseDirectory
+ )
+
+ val make = Process("make", baseDirectory)
+
+ val makeInstall = Process("make install", baseDirectory)
+
+ val ev = configure #&& make #&& makeInstall ! log
+ if (ev != 0) sys.error(s"Building native library failed. Exit code: ${ev}")
+
+ val products: List[File] = (out ** ("*" -- "*.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/build/NoTool.scala b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/NoTool.scala
new file mode 100644
index 0000000..96dd85f
--- /dev/null
+++ b/jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/NoTool.scala
@@ -0,0 +1,9 @@
+package ch.jodersky.sbt.jni
+package build
+
+
+object NoTool {
+
+
+
+}
diff --git a/project/Build.scala b/project/Build.scala
new file mode 100644
index 0000000..e1551a4
--- /dev/null
+++ b/project/Build.scala
@@ -0,0 +1,36 @@
+import sbt._
+import sbt.Keys._
+
+object NativeUtilsBuild extends Build {
+
+ val commonSettings = Seq(
+ version := "0.1-SNAPSHOT",
+ organization := "ch.jodersky",
+ scalacOptions ++= Seq("-deprecation", "-feature")
+ )
+
+ lazy val root = Project(
+ id = "root",
+ base = file("."),
+ aggregate = Seq(
+ library, plugin
+ ),
+ settings = Seq(
+ publish := {}
+ )
+ )
+
+ lazy val library = Project(
+ id = "jni-library",
+ base = file("jni-library"),
+ settings = commonSettings
+ )
+
+ lazy val plugin = Project(
+ id = "sbt-jni",
+ base = file("jni-plugin"),
+ settings = commonSettings ++ Seq(sbtPlugin := true),
+ dependencies = Seq(library)
+ )
+
+}
diff --git a/project/build.properties b/project/build.properties
new file mode 100644
index 0000000..817bc38
--- /dev/null
+++ b/project/build.properties
@@ -0,0 +1 @@
+sbt.version=0.13.9