diff options
Diffstat (limited to 'jni-plugin/src/main/scala/ch/jodersky/sbt/jni')
7 files changed, 370 insertions, 0 deletions
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 { + + + +} |