aboutsummaryrefslogtreecommitdiff
path: root/project/native.scala
blob: 68c970edb6e3e89eb2b82c03927c70e12c1c0fe6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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
  )
}