summaryrefslogtreecommitdiff
path: root/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/PartialClasspath.scala
blob: 949cd6e1c4f046ef124722a0d014cedcbb4e10df (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
/*                     __                                               *\
**     ________ ___   / /  ___      __ ____  Scala.js tools             **
**    / __/ __// _ | / /  / _ | __ / // __/  (c) 2013-2014, LAMP/EPFL   **
**  __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \    http://scala-js.org/       **
** /____/\___/_/ |_/____/_/ | |__/ /____/                               **
**                          |/____/                                     **
\*                                                                      */


package scala.scalajs.tools.classpath

import scala.collection.immutable.{Seq, Traversable}

import scala.scalajs.tools.jsdep._
import scala.scalajs.tools.io._
import scala.scalajs.tools.corelib.CoreJSLibs

/** A partial Scala.js classpath is a collection of:
 *  - Scala.js binary files *.sjsir
 *  - Native JavaScript libraries
 *  - Description of dependencies on other JavaScript libraries
 *
 *  PartialClasspaths can be combined (using [[merge]]) and eventually resolved
 *  to a [[CompleteIRClasspath]]
 */
final class PartialClasspath(
    /** Description of JS libraries the content of this classpath depends on */
    val dependencies: Traversable[JSDependencyManifest],
    /** JS libraries this partial classpath provides */
    val availableLibs: Map[String, VirtualJSFile],
    /** Scala.js IR contained in this PartialClasspath (unordered) */
    val scalaJSIR: Traversable[VirtualScalaJSIRFile],
    val version: Option[String]
) {
  import PartialClasspath.DependencyFilter

  /** Merges another [[PartialClasspath]] with this one. This means:
   *  - Concatenate/merge dependencies
   *  - Merge availableLibs (libs in that shadow libs in this)
   *  - Merge Scala.js IR
   */
  def merge(that: PartialClasspath): PartialClasspath = {
    new PartialClasspath(
        this.dependencies  ++ that.dependencies,
        this.availableLibs ++ that.availableLibs,
        this.scalaJSIR     ++ that.scalaJSIR,
        CacheUtils.joinVersions(this.version, that.version))
  }

  /** Construct a [[IRClasspath]] out of this [[PartialClasspath]] by
   *  resolving library dependencies (and failing if they are not met)
   */
  def resolve(filter: DependencyFilter = identity): IRClasspath = {
    new IRClasspath(resolveDependencies(filter), mergeCompliance(), scalaJSIR,
        dependencies.exists(_.requiresDOM), version)
  }

  /** Constructs an ordered list of JS libraries to include. Fails if:
   *  - Dependencies have cycles
   *  - Not all dependencies are available
   */
  protected def resolveDependencies(
      filter: DependencyFilter): List[ResolvedJSDependency] = {
    val flatDeps = filter(dependencies.flatMap(_.flatten))
    val includeList = JSDependencyManifest.createIncludeList(flatDeps)

    val missingDeps = includeList.filterNot { info =>
      availableLibs.contains(info.resourceName)
    }

    if (missingDeps.nonEmpty)
      throw new MissingJSLibException(missingDeps)

    for (info <- includeList)
      yield new ResolvedJSDependency(availableLibs(info.resourceName), info)
  }

  protected def mergeCompliance(): Traversable[ComplianceRequirement] = {
    val flatTups = for {
      dependency <- dependencies
      semantics  <- dependency.compliantSemantics
    } yield (semantics, dependency.origin)

    for {
      (semantics, tups) <- flatTups.groupBy(_._1)
    } yield new ComplianceRequirement(semantics, tups.map(_._2).toList)
  }

}

object PartialClasspath {

  type DependencyFilter =
    Traversable[FlatJSDependency] => Traversable[FlatJSDependency]

  /** Creates an empty PartialClasspath */
  def empty: PartialClasspath =
    new PartialClasspath(Nil, Map.empty, Nil, Some(""))
}