aboutsummaryrefslogtreecommitdiff
path: root/plugins/essentials/DynamicOverrides.scala
blob: 8d67be745fdd3708718244a698f0edb64448a1e6 (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
package cbt
import cbt.eval.Eval
trait DynamicOverrides extends BaseBuild{
  private val twitterEval = {
    taskCache[DynamicOverrides]( "eval" ).memoize{
      new Eval{
        override lazy val impliedClassPath: List[String] = context.parentBuild.get.classpath.strings.toList//new ScalaCompilerDependency( context.cbtLastModified, context.paths.mavenCache, scalaVersion ).classpath.strings.toList
        override def classLoader = DynamicOverrides.this.getClass.getClassLoader
      }
    }
  }

  protected [cbt] def overrides: String = ""

  // TODO: add support for Build inner classes
  import scala.reflect.runtime.universe._
  def newBuild[T <: DynamicOverrides:TypeTag]: T = newBuild[T](context)("")
  def newBuild[T <: DynamicOverrides:TypeTag](body: String): T = newBuild[T](context)(body)
  def newBuild[T <: DynamicOverrides:TypeTag](context: Context)(body: String): T = {
    val tag = typeTag[T]
    val mixinClass = tag.mirror.runtimeClass(tag.tpe)
    assert(mixinClass.getTypeParameters.size == 0)
    val mixin = if(
      mixinClass == classOf[Nothing]
      || mixinClass.getSimpleName == "Build"
    ) "" else " with "+mixinClass.getName
    import scala.collection.JavaConverters._
    val parent = Option(
      if(this.getClass.getName.startsWith("Evaluator__"))
        this.getClass.getName.dropWhile(_ != '$').drop(1).stripSuffix("$1")
      else
        this.getClass.getName
    ).getOrElse(
      throw new Exception( "You cannot have more than one newBuild call on the Stack right now." )
    )
    val overrides = "" // currently disables, but can be used to force overrides everywhere
    if(mixin == "" && overrides == "" && body == ""){
      // TODO: is it possible for the contructor to have the wrong signature and
      // thereby produce a pretty hard to understand error message here?
      this.getClass
        .getConstructor(classOf[Context])
        .newInstance(context)
        .asInstanceOf[T]
    } else {
      val baseName = "DynamicBuild" + System.currentTimeMillis
      val overrideName = baseName+"Overrides"
      val (finalName, code) = if(overrides == ""){
        (
          baseName,
          s"""
          class $baseName(context: _root_.cbt.Context)
            extends $parent(context)$mixin{
              $body
          }
          """
        )
    } else {
        (
          overrideName,
          s"""
          class $baseName(context: _root_.cbt.Context)
            extends $parent(context)$mixin{
              $body
          }
          class $overrideName(context: _root_.cbt.Context)
            extends $baseName(context){
              $overrides
          }
          """
        )
      }
      logger.dynamic("Dynamically generated code:\n" ++ code)
      twitterEval.compile(code)
      val createBuild = twitterEval.apply[Context => T](s"new $finalName(_: _root_.cbt.Context)",false)
      createBuild( context ).asInstanceOf[T]
    }
  }

  def runFlat: ExitCode = newBuild[DynamicOverrides]{"""
    override def flatClassLoader = true
  """}.run
}