diff options
author | Josh Suereth <joshua.suereth@gmail.com> | 2012-04-03 15:45:23 -0400 |
---|---|---|
committer | Josh Suereth <joshua.suereth@gmail.com> | 2012-04-03 15:45:23 -0400 |
commit | 31546d1b3d841a0fddc9c84af48361c74243ce8e (patch) | |
tree | 852867f14139fa62488f97362a2c2c1d4c6e88e3 /project | |
parent | 4467438a58a3ce0342c1ab608b02b0f880c61aaf (diff) | |
download | scala-31546d1b3d841a0fddc9c84af48361c74243ce8e.tar.gz scala-31546d1b3d841a0fddc9c84af48361c74243ce8e.tar.bz2 scala-31546d1b3d841a0fddc9c84af48361c74243ce8e.zip |
Added an incredibly hacky way for us to override the default sbt compiler interface.
Diffstat (limited to 'project')
-rw-r--r-- | project/Build.scala | 2 | ||||
-rw-r--r-- | project/CheatingComponentCompiler.scala | 148 |
2 files changed, 149 insertions, 1 deletions
diff --git a/project/Build.scala b/project/Build.scala index 76df76d38c..7d80994c4a 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -170,7 +170,7 @@ object ScalaBuild extends Build with Layers { skip in Compile <<= lockFile.map(_ exists), lock <<= lockFile map { f => IO.touch(f) }, unlock <<= lockFile map IO.delete - ) + ) ++ CheatingCompilerSettings.settings // -------------------------------------------------------------- // Libraries used by Scalac that change infrequently diff --git a/project/CheatingComponentCompiler.scala b/project/CheatingComponentCompiler.scala new file mode 100644 index 0000000000..e23acfd946 --- /dev/null +++ b/project/CheatingComponentCompiler.scala @@ -0,0 +1,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) } +} + |