aboutsummaryrefslogtreecommitdiff
path: root/project/native.scala
diff options
context:
space:
mode:
Diffstat (limited to 'project/native.scala')
-rw-r--r--project/native.scala188
1 files changed, 128 insertions, 60 deletions
diff --git a/project/native.scala b/project/native.scala
index 342a863..b6e4c19 100644
--- a/project/native.scala
+++ b/project/native.scala
@@ -1,83 +1,151 @@
-import sbt._
-import Keys._
-import java.io.File
-import java.util.jar.Manifest
-
-object NativeKeys {
+package native
- val nativeBuildDirectory = settingKey[File]("Directory containing native build scripts.")
- val nativeTargetDirectory = settingKey[File]("Base directory to store native products.")
- val nativeOutputDirectory = settingKey[File]("Actual directory where native products are stored.")
- val nativePackageUnmanagedDirectory = settingKey[File]("Directory containing external products that will be copied to the native jar.")
+import java.io.File
+import sbt._
+import sbt.Keys._
+import scala.util.Try
- val nativeClean = taskKey[Unit]("Clean native build.")
- val nativeBuild = taskKey[File]("Invoke native build.")
+/** A platform is a the representation of an os-architecture combination */
+case class Platform(kernel: String, arch: String) {
+ val id = kernel + "-" + arch
}
-object NativeDefaults {
- import NativeKeys._
-
- val autoClean = Def.task {
- val log = streams.value.log
- val build = nativeBuildDirectory.value
-
- Process("make distclean", build) #|| Process("make clean", build) ! log
+object Platform {
+
+ /** Create a platform with spaces stripped and case normalized. */
+ def normalize(kernel: String, arch: String) = Platform(
+ kernel.toLowerCase.filter(!_.isWhitespace),
+ arch
+ )
+
+ /** Run 'uname' to determine current platform. Returns None if uname does not exist. */
+ lazy val uname: Option[Platform] = {
+ val lines = Try { Process("uname -sm").lines.head }.toOption
+ lines.map { line =>
+ val parts = line.split(" ")
+ if (parts.length != 2) {
+ sys.error("Could not determine platform: 'uname -sm' returned unexpected string: " + line)
+ } else {
+ Platform.normalize(parts(0), parts(1))
+ }
}
+ }
- val autoLib = Def.task {
- val log = streams.value.log
- val build = nativeBuildDirectory.value
- val out = nativeOutputDirectory.value
+}
- val configure = Process(
- "./configure " +
- "--prefix=" + out.getAbsolutePath + " " +
- "--libdir=" + out.getAbsolutePath + " " +
- "--disable-versioned-lib", //Disable producing versioned library files, not needed for fat jars.
- build)
+object NativeKeys {
- val make = Process("make", build)
+ val Native = config("native")
- val makeInstall = Process("make install", build)
+ val platform = settingKey[Platform]("Platform of the system this build is being run on.")
- val ev = configure #&& make #&& makeInstall ! log
- if (ev != 0)
- throw new RuntimeException(s"Building native library failed. Exit code: ${ev}")
+ //fat jar settings
+ val libraryPrefix = settingKey[String]("A string to be prepended to native products when packaged.")
+ val libraryManifest = settingKey[String]("Name of a file that will contain a list of all native products.")
+ val libraryResourceDirectory = settingKey[File](
+ "Directory that contains native products when they treated as resources."
+ )
- (out ** ("*.la")).get.foreach(_.delete())
+}
- out
+/** Provides implementations of wrapper tasks suitable for projects using Autotools */
+object Autotools {
+ import NativeKeys._
+ import sbt.Def.Initialize
+
+ private val clean: Initialize[Task[Unit]] = Def.task {
+ val log = streams.value.log
+ val src = (sourceDirectory in Native).value
+
+ Process("make distclean", src) #|| Process("make clean", src) ! log
+ }
+
+ private val lib: Initialize[Task[File]] = Def.task {
+ val log = streams.value.log
+ val src = (sourceDirectory in Native).value
+ val out = (target in Native).value
+ val outPath = out.getAbsolutePath
+
+ val configure = if ((src / "config.status").exists) {
+ Process("./config.status", src)
+ } else {
+ Process(
+ //Disable producing versioned library files, not needed for fat jars.
+ s"./configure --prefix=$outPath --libdir=$outPath --disable-versioned-lib",
+ src
+ )
}
- val nativePackageMappings = Def.task {
- val managedDir = nativeTargetDirectory.value
- val unmanagedDir = nativePackageUnmanagedDirectory.value
+ val make = Process("make", src)
- val managed = (nativeBuild.value ** "*").get
- val unmanaged = (unmanagedDir ** "*").get
+ val makeInstall = Process("make install", src)
- val managedMappings: Seq[(File, String)] = for (file <- managed; if file.isFile) yield {
- file -> ("native/" + (file relativeTo managedDir).get.getPath)
- }
+ val ev = configure #&& make #&& makeInstall ! log
+ if (ev != 0)
+ throw new RuntimeException(s"Building native library failed. Exit code: ${ev}")
- val unmanagedMappings: Seq[(File, String)] = for (file <- unmanaged; if file.isFile) yield {
- file -> ("native/" + (file relativeTo unmanagedDir).get.getPath)
- }
+ val products: List[File] = (out ** ("*" -- "*.la")).get.filter(_.isFile).toList
- managedMappings ++ unmanagedMappings
+ //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
}
+ }
+
+ val settings: Seq[Setting[_]] = Seq(
+ Keys.clean in Native := Autotools.clean.value,
+ Keys.compile in Native := {
+ lib.value
+ sbt.inc.Analysis.Empty
+ },
+ Keys.packageBin in Native := {
+ lib.value
+ }
+ )
+}
- def os = System.getProperty("os.name").toLowerCase.filter(c => !c.isWhitespace)
- def arch = System.getProperty("os.arch").toLowerCase
-
- val settings: Seq[Setting[_]] = Seq(
- nativeTargetDirectory := target.value / "native",
- nativeOutputDirectory := nativeTargetDirectory.value / (os + "-" + arch),
- nativeClean := autoClean.value,
- nativeBuild := autoLib.value,
- nativePackageUnmanagedDirectory := baseDirectory.value / "lib_native",
- mappings in (Compile, packageBin) ++= nativePackageMappings.value
- )
+object NativeDefaults {
+ import NativeKeys._
+
+ /** Copy native product to resource directory and create manifest */
+ private val libraryResources = Def.task {
+ val out = (libraryResourceDirectory in Compile).value
+
+ val product = (packageBin in Native).value
+
+ val productResource = out / product.name
+ val manifestResource = out / (libraryManifest in Compile).value
+
+ IO.copyFile(product, productResource)
+ IO.write(manifestResource, productResource.name)
+
+ Seq(productResource, manifestResource)
+ }
+
+ private val fatJarSettings = Seq(
+ libraryPrefix in Compile := "",
+ libraryManifest in Compile := "library",
+ libraryResourceDirectory in Compile := (resourceManaged in Compile).value /
+ (libraryPrefix in Compile).value / (platform in Native).value.id,
+ unmanagedResourceDirectories in Compile += (baseDirectory).value / "lib_native",
+ resourceGenerators in Compile += libraryResources.taskValue
+ )
+
+ val settings: Seq[Setting[_]] = Seq(
+ platform in Native := Platform.uname.getOrElse {
+ System.err.println("Warning: Cannot determine platform! It will be set to 'unknown'.")
+ Platform("unknown", "unknown")
+ },
+ target in Native := target.value / "native" / (platform in Native).value.id
+ ) ++ fatJarSettings ++ Autotools.settings
}