summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/io/Sources.scala
blob: 48682e4ad45e74e094a9239e99a458c28d97820d (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
package scala.tools.nsc
package io

import util.ClassPath
import java.util.concurrent.{ Future, ConcurrentHashMap, ExecutionException }
import java.util.zip.ZipException
import Path.{ isJarOrZip, locateJarByName }
import collection.JavaConverters._
import Properties.{ envOrElse, propOrElse }

class Sources(val path: String) {
  val expandedPath        = ClassPath.join(ClassPath expandPath path: _*)
  val cache               = new ConcurrentHashMap[String, List[Fileish]]
  def allNames            = cache.keys.asScala.toList.sorted
  def apply(name: String) = get(name)
  def size                = cache.asScala.values map (_.length) sum

  private var debug = false
  private def dbg(msg: => Any) = if (debug) Console println msg
  private val partitioned = ClassPath toPaths expandedPath partition (_.isDirectory)

  val dirs   = partitioned._1 map (_.toDirectory)
  val jars   = partitioned._2 filter isJarOrZip map (_.toFile)
  val (isDone, force) = {
    val f1  = spawn(calculateDirs())
    val f2  = spawn(calculateJars())
    val fn1 = () => { f1.isDone() && f2.isDone() }
    val fn2 = () => { f1.get() ; f2.get() ; () }

    (fn1, fn2)
  }

  private def catchZip(body: => Unit): Unit = {
    try body
    catch { case x: ZipException => dbg("Caught: " + x) }
  }

  private def calculateDirs() =
    dirs foreach { d => dbg(d) ; catchZip(addSources(d.deepFiles map (x => Fileish(x)))) }

  private def calculateJars() =
    jars foreach { j => dbg(j) ; catchZip(addSources(new Jar(j).fileishIterator)) }

  private def addSources(fs: TraversableOnce[Fileish]) =
    fs foreach { f => if (f.isSourceFile) add(f.name, f) }

  private def get(key: String): List[Fileish] =
    if (cache containsKey key) cache.get(key) else Nil

  private def add(key: String, value: Fileish) = {
    if (cache containsKey key) cache.replace(key, value :: cache.get(key))
    else cache.put(key, List(value))
  }
  override def toString = "Sources(%d dirs, %d jars, %d sources)".format(
    dirs.size, jars.size, cache.asScala.values map (_.length) sum
  )
}

trait LowPrioritySourcesImplicits {
  self: Sources.type =>

  implicit def fallbackSources: Sources = defaultSources
}


object Sources extends LowPrioritySourcesImplicits {
  // Examples of what libraryJar might be, each of which we'd like to find
  // the source files automatically:
  //
  // /scala/trunk/build/pack/lib/scala-library.jar
  // /scala/trunk/build/quick/classes/library
  // /scala/inst/scala-2.9.0.r24213-b20110206233447/lib/scala-library.jar
  private def libraryJar = Path.locateJarByClass(classOf[ScalaObject]) map (_.toAbsolute.path)
  private def autoSourcePaths: List[String] = libraryJar.toList flatMap { lib =>
    val markers = List("build/pack/lib", "build/quick/classes", "scala-library.jar")
    markers filter (lib contains _) flatMap { m =>
      val dir = Path(lib take lib.indexOf(m)) / "src"

      if (dir.exists) ClassPath.expandDir(dir.path)
      else Nil
    }
  }

  val sourcePathEnv   = envOrElse("SOURCEPATH", "")
  val defaultSources  = apply(autoSourcePaths :+ sourcePathEnv: _*)

  def apply(paths: String*): Sources = new Sources(ClassPath.join(paths: _*))
}