import sbt._
import Keys._
import java.io.File
import scala.collection.mutable.HashSet
case class NativeBuild(
name: String,
cCompiler: String,
cFlags: Seq[String],
linker: String,
linkerFlags: Seq[String],
binary: String)
object NativeKeys {
//build settings
val nativeBuilds = taskKey[Seq[NativeBuild]]("All native build configurations, including cross-compilation.")
val nativeVersion = settingKey[String]("Version of native binary")
//compile settings
val nativeIncludeDirectories = settingKey[Seq[File]]("Directories to include during build (gcc -I option)")
//link settings
val nativeLibraries = settingKey[Seq[String]]("Default names of libraries to use during linking.")
val nativeLibraryDirectories = settingKey[Seq[File]]("Directories to search for libraries (gcc -L option)")
//directories
val nativeSource = settingKey[File]("Lowest level directory containing all native sources.")
val nativeCSources = taskKey[Seq[File]]("All c source files.")
val nativeTargetDirectory = settingKey[File]("Directory containing all compiled and linked files.")
//tasks
val nativeCompile = taskKey[Map[NativeBuild, Seq[File]]]("Compile all native build configurations.")
val nativeLink = taskKey[Map[NativeBuild, File]]("Link all native build configurations.")
}
object NativeDefaults {
import NativeKeys._
private def generate(generators: SettingKey[Seq[Task[Seq[File]]]]) = generators { _.join.map(_.flatten) }
private def compile(logger: Logger, compiler: String, flags: Seq[String], includeDirectories: Seq[File], src: File, out: File): File = {
IO.createDirectory(out.getParentFile)
val parts: Seq[String] =
Seq(compiler) ++
flags ++
includeDirectories.map("-I" + _.getAbsolutePath) ++
Seq("-o", out.getAbsolutePath) ++
Seq("-c", src.getAbsolutePath)
val cmd = parts.mkString(" ")
logger.info(cmd)
val ev = Process(cmd) ! logger
if (ev != 0) throw new RuntimeException(s"Compilation of ${src.getAbsoluteFile()} failed.")
out
}
private def link(logger: Logger, linker: String, flags: Seq[String], libraryDirectories: Seq[File], libraries: Seq[String], in: Seq[File], out: File): File = {
val parts: Seq[String] =
Seq(linker) ++
flags ++
Seq("-o", out.getAbsolutePath) ++
in.map(_.getAbsolutePath) ++
libraryDirectories.map("-L" + _.getAbsolutePath) ++
libraries.map("-l" + _)
val cmd = parts.mkString(" ")
logger.info(cmd)
val ev = Process(cmd) ! logger
if (ev != 0) throw new RuntimeException(s"Linking of ${out.getAbsoluteFile()} failed.")
out
}
def nativeCompileImpl() = Def.task {
val logger = streams.value.log
val builds = nativeBuilds.value
val outDir = nativeTargetDirectory.value
val includeDirs = nativeIncludeDirectories.value
val csrcs = nativeCSources.value
val compilations = for (build <- builds) yield {
logger.info("Compiling configuration " + build.name)
val objects = for (src <- csrcs) yield {
compile(logger, build.cCompiler, build.cFlags, includeDirs, src, outDir / build.name / "objects" / (src.base + ".o"))
}
build -> objects
}
compilations.toMap
}
lazy val nativeLinkImpl = Def.task {
val logger = streams.value.log
val builds = nativeBuilds.value
val outDir = nativeTargetDirectory.value
val libDirs = nativeLibraryDirectories.value
val libs = nativeLibraries.value
val compilations = nativeCompile.value
val version = nativeVersion.value
val linkages = for (build <- builds) yield {
logger.info("Linking configuration " + build.name)
val objects = compilations(build)
val binary = link(logger, build.linker, build.linkerFlags, libDirs, libs, objects, outDir / build.name / build.binary)
build -> binary
}
linkages.toMap
}
def localPlatform = try {
Process("gcc -dumpmachine").lines.headOption
} catch {
case ex: Exception => None
}
val settings: Seq[Setting[_]] = Seq(
//nativeBuilds :=
nativeSource := (sourceDirectory in Compile).value / "native",
includeFilter in nativeCSources := "*.c",
nativeCSources := (nativeSource.value ** (includeFilter in nativeCSources).value).get,
nativeTargetDirectory := target.value / "native",
nativeIncludeDirectories := Seq(nativeSource.value, nativeSource.value / "include"),
nativeLibraries := Seq(),
nativeLibraryDirectories := Seq(),
nativeCompile := nativeCompileImpl.value,
nativeLink := nativeLinkImpl.value
)
}