From 3e90312b3f2d912bf27e91b454a6ef21a81a2fc5 Mon Sep 17 00:00:00 2001 From: Jakob Odersky Date: Fri, 4 Dec 2015 20:50:53 -0800 Subject: initial commit --- .gitignore | 5 + README.md | 14 ++ examples/demo/build.sbt | 16 ++ examples/demo/native/CMakeLists.txt | 45 +++++ examples/demo/native/main.c | 16 ++ .../demo/native/org_example_jni_demo_Library__.h | 21 +++ examples/demo/project/plugins.sbt | 1 + .../main/scala/org/example/jni/demo/Library.scala | 8 + .../src/main/scala/org/example/jni/demo/Main.scala | 12 ++ .../main/scala/ch/jodersky/jni/NativeLoader.scala | 75 ++++++++ .../src/main/scala/ch/jodersky/jni/Platform.scala | 50 ++++++ .../main/scala/ch/jodersky/sbt/jni/Defaults.scala | 192 +++++++++++++++++++++ .../main/scala/ch/jodersky/sbt/jni/JniPlugin.scala | 19 ++ .../src/main/scala/ch/jodersky/sbt/jni/Keys.scala | 53 ++++++ .../ch/jodersky/sbt/jni/build/BuildTool.scala | 14 ++ .../ch/jodersky/sbt/jni/build/BuildToolApi.scala | 23 +++ .../scala/ch/jodersky/sbt/jni/build/CMake.scala | 60 +++++++ .../scala/ch/jodersky/sbt/jni/build/NoTool.scala | 9 + project/Build.scala | 36 ++++ project/build.properties | 1 + 20 files changed, 670 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 examples/demo/build.sbt create mode 100644 examples/demo/native/CMakeLists.txt create mode 100644 examples/demo/native/main.c create mode 100644 examples/demo/native/org_example_jni_demo_Library__.h create mode 100644 examples/demo/project/plugins.sbt create mode 100644 examples/demo/src/main/scala/org/example/jni/demo/Library.scala create mode 100644 examples/demo/src/main/scala/org/example/jni/demo/Main.scala create mode 100644 jni-library/src/main/scala/ch/jodersky/jni/NativeLoader.scala create mode 100644 jni-library/src/main/scala/ch/jodersky/jni/Platform.scala create mode 100644 jni-plugin/src/main/scala/ch/jodersky/sbt/jni/Defaults.scala create mode 100644 jni-plugin/src/main/scala/ch/jodersky/sbt/jni/JniPlugin.scala create mode 100644 jni-plugin/src/main/scala/ch/jodersky/sbt/jni/Keys.scala create mode 100644 jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/BuildTool.scala create mode 100644 jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/BuildToolApi.scala create mode 100644 jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/CMake.scala create mode 100644 jni-plugin/src/main/scala/ch/jodersky/sbt/jni/build/NoTool.scala create mode 100644 project/Build.scala create mode 100644 project/build.properties 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 +#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 +/* 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//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 -- cgit v1.2.3