aboutsummaryrefslogtreecommitdiff
path: root/project/native.scala
blob: 5c84f11535cc59b003920949bb20cb0a5ef56624 (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
134
135
136
137
138
import sbt._
import Keys._
import java.io.File
import scala.collection.mutable.HashSet

object NativeKeys {
  
  val Native = config("native")
  
  //compilation
  val cCompiler = settingKey[String]("Default c compiler.")
  val cppCompiler = settingKey[String]("Default c++ comppiler.")
  val cFlags = settingKey[Seq[String]]("Default flags for c compiler.")
  val cppFlags = settingKey[Seq[String]]("Default flags for c++ compiler.")
  val flags = settingKey[Seq[String]]("Default flags prepended to c and c++ flags.")
  val nativeCompile = taskKey[Seq[File]]("Compile native sources.")
    
  val linker = settingKey[String]("Linker used in project.")
  val linkFlags = settingKey[Seq[String]]("Default options for linker.")
  val libraryNames = settingKey[Seq[String]]("Default names of libraries to use during linking.")
  val binaryName = settingKey[String]("Final binary product.")
  val link = taskKey[File]("Link compiled objects to a final product.")

  //directories
  val nativeSource = settingKey[File]("Lowest level directory containing all native sources.")
  val cSources = taskKey[Seq[File]]("All c source files, managed and unmanaged.")
  val cppSources = taskKey[Seq[File]]("All c++ source files, managed and unmanaged.")
  val includeDirectories = settingKey[Seq[File]]("Directories to include during build (gcc -I option)")
  val libraryDirectories = settingKey[Seq[File]]("Directories to search for libraries (gcc -L option)")
  val objectDirectory = settingKey[File]("Directory containing all compiled objects.")
  //val objects = taskKey[Seq[File]]("Object files generated from source files. Note: there should be a one-to-one mapping between source and object files.")
  
}

object NativeDefaults {
  import NativeKeys._

  private def generate(generators: SettingKey[Seq[Task[Seq[File]]]]) = generators { _.join.map(_.flatten) }

  def compileImpl = Def.task {
    implicit val log = streams.value.log

    def compile(compiler: String, flags: Seq[String], src: File) = {
      val obj = objectDirectory.value / (src.base + ".o")
      IO.createDirectory(obj.getParentFile())
      val parts: Seq[String] = Seq(compiler) ++
        flags ++
        includeDirectories.value.map("-I" + _.getAbsolutePath) ++
        Seq("-o", obj.getAbsolutePath()) ++
        Seq("-c", src.getAbsolutePath())

      val cmd = parts.mkString(" ")
      log.info(cmd)
      val ev = Process(cmd) ! log
      if (ev != 0) throw new RuntimeException(s"compilation of ${src.getAbsoluteFile()} failed")
      obj
    }

    cSources.value.map { src =>
      compile(cCompiler.value, cFlags.value, src)
    } ++
      cppSources.value.map { src =>
        compile(cppCompiler.value, cppFlags.value, src)
      }
  }

  def linkImpl = Def.task {
    implicit val log = streams.value.log

    val out = (target.value / binaryName.value)
    val parts: Seq[String] = Seq(linker.value) ++
      linkFlags.value ++
      Seq("-o", out.getAbsolutePath) ++
      nativeCompile.value.map(_.getAbsolutePath) ++
      libraryDirectories.value.map("-L" + _.getAbsolutePath) ++
      libraryNames.value.map("-l" + _)

    val cmd = parts.mkString(" ")
    log.info(cmd)
    val ev = Process(cmd) ! log
    if (ev != 0) throw new RuntimeException(s"linking of ${out.getAbsoluteFile()} failed")
    out
  }

  val compileSettings: Seq[Setting[_]] = inConfig(Native)(Seq(
    cCompiler := "gcc",
    cppCompiler := "g++",
    cFlags := flags.value,
    cppFlags := flags.value,
    flags := Seq("-fPIC", "-O2"),
    nativeCompile := compileImpl.value,
    sourceGenerators := Seq()))

  val linkSettings: Seq[Setting[_]] = inConfig(Native)(Seq(
    linker := "gcc",
    linkFlags := Seq(),
    libraryNames := Seq(),
    binaryName := binaryName.value,
    link := linkImpl.value))

  val fileSettings: Seq[Setting[_]] = inConfig(Native)(Seq(
    target := (target in Compile).value / "native",
    nativeSource := (sourceDirectory in Compile).value / "native",
    sourceManaged := target.value / "src_managed",
    unmanagedSources := (nativeSource.value ** (includeFilter in unmanagedSources).value).get,
    managedSources := (generate(sourceGenerators).value ** (includeFilter in managedSources).value).get,
    sources := unmanagedSources.value ++ managedSources.value,
    cSources := sources.value.filter(src => (includeFilter in cSources).value accept src),
    cppSources := sources.value.filter(src => (includeFilter in cppSources).value accept src),
    includeFilter in (unmanagedSources) := ("*.c" || "*.cpp" || "*.cxx" || "*.cc"),
    includeFilter in (managedSources) := (includeFilter in unmanagedSources).value,
    includeFilter in cSources := "*.c",
    includeFilter in cppSources := "*.cpp" || "*.cc" || "*.cxx",
    objectDirectory := target.value / "objects",
    includeDirectories := Seq(nativeSource.value, sourceManaged.value),
    libraryDirectories := Seq(),
    binaryName := (name in Compile).value))

  val defaultSettings: Seq[Setting[_]] = inConfig(Native)(
    compileSettings ++
      linkSettings ++
      fileSettings)
      
  implicit class RichNativeProject(project: Project) {
    def dependsOnNative(other: Project): Project = {
      val newSettings: Seq[Setting[_]] = inConfig(Native)(Seq(
        link in project := ((link in project) dependsOn (link in other)).value,
        includeDirectories in project ++= (includeDirectories in other).value,
        libraryDirectories in project += (target in other).value 
      ))
      project.settings(newSettings: _*)
    }
    
  }
  
  def NativeProject(id: String, base: File) = Project(id, base).settings(NativeDefaults.defaultSettings: _*)

}