summaryrefslogtreecommitdiff
path: root/project/CheatingComponentCompiler.scala
blob: e23acfd9463095f94abae64ffe6e43c1d44870ab (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
139
140
141
142
143
144
145
146
147
148
import sbt._

import xsbti.{Logger => _,_}
import compiler._
import inc._
import Locate.DefinesClass
import java.io.File

import java.util.concurrent._
import Keys._
import Compiler.Compilers


object CheatingCompilerSettings {
  val hackedmodules = TaskKey[Map[String,File]]("hacked-sbt-components")

  def settings: Seq[Setting[_]] = Seq(
    hackedmodules := Map.empty,

    // Now look for org.scala-sbt compiler-interface-src!
    hackedmodules <<= (hackedmodules, target, streams) map { (h, t, s) => Map(sbtCompilerInterface(t,s)) ++ h },

    compilers <<= (hackedmodules, scalaInstance, appConfiguration, streams, classpathOptions, javaHome) map { 
      (hacks, si, app, s, co, jh) => CheatingCompilers.compilers(hacks, si, co, jh)(app, s.log) 
    }
  )


  def sbtCompilerInterface(cacheDir: File, s: TaskStreams): (String, File) =
    "compiler-interface-src" -> pullFile("compiler-interface-src", "http://typesafe.artifactoryonline.com/typesafe/ivy-releases/org.scala-sbt/compiler-interface/0.12.0-M2/jars/compiler-interface-src.jar", cacheDir, s)

  import dispatch.{url=>_,_}
  def pullFile(name: String, url: String, cacheDir: File, s: TaskStreams): File = {
    val cachedFile = cacheDir / (name + ".jar")
    if (!cachedFile.exists) {
      // Ensure the directory for the cache exists.
      cachedFile.getParentFile.mkdirs()
      val fous = new java.io.FileOutputStream(cachedFile)
      try Http(dispatch.url(url) >>> fous) finally fous.close()
      val sha = pullSha(url)
      if(!isShaOk(cachedFile, sha)) sys.error("Downloaded sha does not match file: " + cachedFile.getAbsolutePath)
      IO.write(file(cachedFile.getAbsolutePath + ".sha1"), sha)
    }
    cachedFile
  }

  def isShaOk(f: File, sha: String): Boolean = 
     ShaResolve.calculateSha(f) == sha

  def isCached(f: File, url: String): Boolean = 
    if(f.exists) {
      val savedsha = file(f.getAbsolutePath + ".sha1")
      savedsha.exists && isShaOk(f, IO.read(savedsha))
    } else false

  def pullSha(url: String): String = 
    Http(dispatch.url(url + ".sha1") >- identity)
}


object CheatingCompilers {

  def compilers(hacks: Map[String,File], instance: ScalaInstance, cpOptions: ClasspathOptions, javaHome: Option[File])(implicit app: AppConfiguration, log: Logger): Compilers = {
    val javac = Compiler.directOrFork(instance, cpOptions, javaHome)
    val scalac = scalaCompiler(hacks, instance, cpOptions)
    new Compilers(scalac, javac)
  }

  def scalaCompiler(hacks: Map[String,File], instance: ScalaInstance, cpOptions: ClasspathOptions)(implicit app: AppConfiguration, log: Logger): AnalyzingCompiler = {
    val launcher = app.provider.scalaProvider.launcher
    val componentManager = new CheatingComponentManager(hacks, launcher.globalLock, app.provider.components, Option(launcher.ivyHome), log)
    new AnalyzingCompiler(instance, componentManager, cpOptions, log)
  }
}


class CheatingComponentManager(val hacks: Map[String,File], globalLock: xsbti.GlobalLock, provider: xsbti.ComponentProvider, ivyHome: Option[File], log: Logger) 
  extends ComponentManager(globalLock, provider, ivyHome, log) {
  
  private[this] val ivyCache = new IvyCache(ivyHome)
  /** Get all of the files for component 'id', throwing an exception if no files exist for the component. */
  override def files(id: String)(ifMissing: IfMissing): Iterable[File] = {
    def fromGlobal1 =
      lockGlobalCache {
        try { update(id); getOrElse1(createAndCache1) }
        catch { case e: NotInCache => createAndCache1 }
      }
    def getOrElse1(orElse: => Iterable[File]): Iterable[File] = {
      val existing = provider.component(hacked(id))
      if(existing.isEmpty) orElse else existing
    }
    def notFound1 = invalid("Could not find required component '" + id + "'")
    def createAndCache1 =
      ifMissing match {
        case IfMissing.Fail      => notFound1
        case d: IfMissing.Define =>
          d()
          if(d.cache) cache(id)
          getOrElse1(notFound1)
      }
    val result = lockLocalCache { getOrElse1(fromGlobal1) }
    println("cheating-component-manager.files("+id+")")
    result foreach println
    result
  }
  private def isHacked(id: String) = hacks.keySet contains id
  private def hacked(id: String) = if(isHacked(id)) "hacked-"+id else id
  /** This is used to lock the local cache in project/boot/.  By checking the local cache first, we can avoid grabbing a global lock. */
  private def lockLocalCache[T](action: => T): T = lock(provider.lockFile)( action )
  /** This is used to ensure atomic access to components in the global Ivy cache.*/
  private def lockGlobalCache[T](action: => T): T = lock(ivyCache.lockFile)( action )
  private def lock[T](file: File)(action: => T): T = globalLock(file, new Callable[T] { def call = action })
  private def invalid(msg: String) = throw new InvalidComponent(msg)
  private def invalid(e: NotInCache) = throw new InvalidComponent(e.getMessage, e)

  override def define(id: String, files: Iterable[File]) = lockLocalCache { provider.defineComponent(hacked(id), files.toSeq.toArray) }
  /** Retrieve the file for component 'id' from the local repository. */
  private def update(id: String): Unit = 
    if(isHacked(id)) {
      // Grab jar magically
      println("Grabbing hacked jar for ["+id+"] = " + hacks.get(id))
      define(hacked(id), Seq(hacks(id)))
    } else ivyCache.withCachedJar(sbtModuleID(id), Some(globalLock), log) {jar => 
      define(id, Seq(jar)) 
    }


  //---------------------!!!!!!!!!!!!!!!!----------------------------------------
  // YUK -  Ugly hack to use newer compiler interface - YUK
  //---------------------!!!!!!!!!!!!!!!!----------------------------------------
  private def sbtModuleID(id: String) = if(isHacked(id)) {
    println("Making hackd module id!")
    val version = "0.12.0-M2" // HACKED 20120314203952
    val timestamp = "20120315T001212" // Hacked  "20120314203952" //
    val stampedVersion = version + "_" + timestamp
    val result = ModuleID("org.scala-sbt", id, stampedVersion)
    println("Looking for hacked module id: " + result)
    result
  } else ModuleID("org.scala-tools.sbt", id, ComponentManager.stampedVersion)
  //---------------------!!!!!!!!!!!!!!!!----------------------------------------
  // ENDYUK -  Ugly hack to use newer compiler interface - ENDYUK
  //---------------------!!!!!!!!!!!!!!!!----------------------------------------

  /** Install the files for component 'id' to the local repository.  This is usually used after writing files to the directory returned by 'location'. */
  override def cache(id: String): Unit = ivyCache.cacheJar(sbtModuleID(id), file(id)(IfMissing.Fail), Some(globalLock), log)
  override def clearCache(id: String): Unit = lockGlobalCache { ivyCache.clearCachedJar(sbtModuleID(id), Some(globalLock), log) }
}