Skip to content

Commit fa151a8

Browse files
committed
Merge the sbt compiler bridge in a subdirectory
This merges the branch move-subdirectory of github.com/smarter/dotty-bridge which was created using: git filter-branch --tree-filter 'mkdir .bridge && mv * .bridge && mv .bridge bridge' This commit does not update the dotty Build.scala to also build the dotty-bridge, this will be done by the next commit.
2 parents 04b0e85 + 6d131da commit fa151a8

File tree

490 files changed

+4308
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

490 files changed

+4308
-0
lines changed

bridge/LICENSE

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
Copyright (c) 2015-2016 The dotty-bridge contributors.
2+
All rights reserved.
3+
4+
Some parts of this project are copied or adapted from sbt which is:
5+
Copyright (c) 2008-2016 Typesafe Inc, Mark Harrah, Grzegorz Kossakowski,
6+
Josh Suereth, Indrajit Raychaudhuri, Eugene Yokota, and other contributors.
7+
All rights reserved.
8+
9+
Redistribution and use in source and binary forms, with or without
10+
modification, are permitted provided that the following conditions
11+
are met:
12+
1. Redistributions of source code must retain the above copyright
13+
notice, this list of conditions and the following disclaimer.
14+
2. Redistributions in binary form must reproduce the above copyright
15+
notice, this list of conditions and the following disclaimer in the
16+
documentation and/or other materials provided with the distribution.
17+
3. The name of the author may not be used to endorse or promote products
18+
derived from this software without specific prior written permission.
19+
20+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21+
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22+
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23+
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25+
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29+
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30+

bridge/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
## dotty-bridge
2+
3+
### Overview
4+
5+
This is an sbt compiler bridge for [Dotty](https://github.com/lampepfl/dotty),
6+
it allows you to compile your code using Dotty instead of Scala 2.
7+
8+
### Implementation status
9+
10+
- [X] `sbt compile` support, including [incremental recompilation](http://www.scala-sbt.org/0.13/docs/Understanding-Recompilation.html)
11+
- [ ] `sbt console` support (may involve some changes to the Dotty REPL)
12+
- [ ] `sbt doc` support
13+
14+
### Usage
15+
16+
See https://github.com/smarter/dotty-example-project for an example.
17+
18+
### Discuss
19+
20+
Feel free to come chat with us on the
21+
[Dotty gitter](http://gitter.im/lampepfl/dotty)!

bridge/build.sbt

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
lazy val root = (project in file(".")).
2+
settings(
3+
organization := "ch.epfl.lamp",
4+
name := "dotty-bridge",
5+
description := "sbt compiler bridge for Dotty",
6+
resolvers += Resolver.typesafeIvyRepo("releases"),
7+
libraryDependencies := Seq(
8+
"ch.epfl.lamp" %% "dotty" % "0.1-SNAPSHOT",
9+
"org.scala-sbt" % "interface" % sbtVersion.value,
10+
"org.scala-sbt" % "api" % sbtVersion.value % "test",
11+
"org.specs2" %% "specs2" % "2.3.11" % "test"
12+
),
13+
publishArtifact in packageDoc := false,
14+
version := "0.1.1-SNAPSHOT",
15+
// 2.11.5 is the version used by Dotty itself currently, we do the same to
16+
// avoid trouble.
17+
scalaVersion := "2.11.5",
18+
// Ideally, the sources should be published with crossPaths := false and the
19+
// binaries with crossPaths := true, but I haven't figured out how to do this.
20+
crossPaths := false,
21+
22+
fork in Test := true,
23+
parallelExecution in Test := false
24+
)
25+
26+
// Options for scripted tests
27+
ScriptedPlugin.scriptedSettings
28+
scriptedLaunchOpts := Seq("-Xmx1024m")
29+
scriptedBufferLog := false
30+
// TODO: Use this instead of manually copying DottyInjectedPlugin.scala
31+
// everywhere once https://github.com/sbt/sbt/issues/2601 gets fixed.
32+
/*
33+
scriptedPrescripted := { f =>
34+
IO.write(inj, """
35+
import sbt._
36+
import Keys._
37+
38+
object DottyInjectedPlugin extends AutoPlugin {
39+
override def requires = plugins.JvmPlugin
40+
override def trigger = allRequirements
41+
42+
override val projectSettings = Seq(
43+
scalaVersion := "0.1-SNAPSHOT",
44+
scalaOrganization := "ch.epfl.lamp",
45+
scalacOptions += "-language:Scala2",
46+
scalaBinaryVersion := "2.11",
47+
autoScalaLibrary := false,
48+
libraryDependencies ++= Seq("org.scala-lang" % "scala-library" % "2.11.5"),
49+
scalaCompilerBridgeSource := ("ch.epfl.lamp" % "dotty-bridge" % "0.1.1-SNAPSHOT" % "component").sources()
50+
)
51+
}
52+
""")
53+
}
54+
*/

bridge/project/build.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
sbt.version=0.13.11

bridge/project/scripted.sbt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
libraryDependencies <+= (sbtVersion) { sv =>
2+
"org.scala-sbt" % "scripted-plugin" % sv
3+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package xsbt
2+
3+
import java.net.{URL, URLClassLoader}
4+
5+
/** A classloader to run the compiler
6+
*
7+
* A CompilerClassLoader is constructed from a list of `urls` that need to be on
8+
* the classpath to run the compiler and the classloader used by sbt.
9+
*
10+
* To understand why a custom classloader is needed for the compiler, let us
11+
* describe some alternatives that wouldn't work.
12+
* - `new URLClassLoader(urls)`:
13+
* The compiler contains sbt phases that callback to sbt using the `xsbti.*`
14+
* interfaces. If `urls` does not contain the sbt interfaces we'll get a
15+
* `ClassNotFoundException` in the compiler when we try to use them, if
16+
* `urls` does contain the interfaces we'll get a `ClassCastException` or a
17+
* `LinkageError` because if the same class is loaded by two different
18+
* classloaders, they are considered distinct by the JVM.
19+
* - `new URLClassLoader(urls, sbtLoader)`:
20+
* Because of the JVM delegation model, this means that we will only load
21+
* a class from `urls` if it's not present in the parent `sbtLoader`, but
22+
* sbt uses its own version of the scala compiler and scala library which
23+
* is not the one we need to run the compiler.
24+
*
25+
* Our solution is to implement a subclass of URLClassLoader with no parent, instead
26+
* we override `loadClass` to load the `xsbti.*` interfaces from `sbtLoader`.
27+
*/
28+
class CompilerClassLoader(urls: Array[URL], sbtLoader: ClassLoader)
29+
extends URLClassLoader(urls, null) {
30+
override def loadClass(className: String, resolve: Boolean): Class[_] =
31+
if (className.startsWith("xsbti.")) {
32+
// We can't use the loadClass overload with two arguments because it's
33+
// protected, but we can do the same by hand (the classloader instance
34+
// from which we call resolveClass does not matter).
35+
val c = sbtLoader.loadClass(className)
36+
if (resolve)
37+
resolveClass(c)
38+
c
39+
} else {
40+
super.loadClass(className, resolve)
41+
}
42+
}
43+
44+
object CompilerClassLoader {
45+
/** Fix the compiler bridge ClassLoader
46+
*
47+
* Soundtrack: https://www.youtube.com/watch?v=imamcajBEJs
48+
*
49+
* The classloader that we get from sbt looks like:
50+
*
51+
* URLClassLoader(bridgeURLs,
52+
* DualLoader(scalaLoader, notXsbtiFilter, sbtLoader, xsbtiFilter))
53+
*
54+
* DualLoader will load the `xsbti.*` interfaces using `sbtLoader` and
55+
* everything else with `scalaLoader`. Once we have loaded the dotty Main
56+
* class using `scalaLoader`, subsequent classes in the dotty compiler will
57+
* also be loaded by `scalaLoader` and _not_ by the DualLoader. But the sbt
58+
* compiler phases are part of dotty and still need access to the `xsbti.*`
59+
* interfaces in `sbtLoader`, therefore DualLoader does not work for us
60+
* (this issue is not present with scalac because the sbt phases are
61+
* currently defined in the compiler bridge itself, not in scalac).
62+
*
63+
* CompilerClassLoader is a replacement for DualLoader. Until we can fix
64+
* this in sbt proper, we need to use reflection to construct our own
65+
* fixed classloader:
66+
*
67+
* URLClassLoader(bridgeURLs,
68+
* CompilerClassLoader(scalaLoader.getURLs, sbtLoader))
69+
*
70+
* @param bridgeLoader The classloader that sbt uses to load the compiler bridge
71+
* @return A fixed classloader that works with dotty
72+
*/
73+
def fixBridgeLoader(bridgeLoader: ClassLoader) = bridgeLoader match {
74+
case bridgeLoader: URLClassLoader =>
75+
val dualLoader = bridgeLoader.getParent
76+
val dualLoaderClass = dualLoader.getClass
77+
78+
// DualLoader#parentA and DualLoader#parentB are private
79+
val parentAField = dualLoaderClass.getDeclaredField("parentA")
80+
parentAField.setAccessible(true)
81+
val parentBField = dualLoaderClass.getDeclaredField("parentB")
82+
parentBField.setAccessible(true)
83+
val scalaLoader = parentAField.get(dualLoader).asInstanceOf[URLClassLoader]
84+
val sbtLoader = parentBField.get(dualLoader).asInstanceOf[URLClassLoader]
85+
86+
val bridgeURLs = bridgeLoader.getURLs
87+
new URLClassLoader(bridgeURLs,
88+
new CompilerClassLoader(scalaLoader.getURLs, sbtLoader))
89+
}
90+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/* sbt -- Simple Build Tool
2+
* Copyright 2008, 2009 Mark Harrah
3+
*/
4+
package xsbt
5+
6+
import xsbti.{ AnalysisCallback, Logger, Problem, Reporter, Severity, DependencyContext }
7+
import xsbti.api.SourceAPI
8+
import xsbti.compile._
9+
import Log.debug
10+
import java.io.File
11+
12+
import dotty.tools.dotc.core.Contexts.ContextBase
13+
import dotty.tools.dotc.{ Main => DottyMain }
14+
import dotty.tools.dotc.interfaces._
15+
16+
import java.net.URLClassLoader
17+
18+
final class CompilerInterface {
19+
def newCompiler(options: Array[String], output: Output, initialLog: Logger,
20+
initialDelegate: Reporter, resident: Boolean): CachedCompiler = {
21+
// The classloader that sbt uses to load the compiler bridge is broken
22+
// (see CompilerClassLoader#fixBridgeLoader for details). To workaround
23+
// this we construct our own ClassLoader and then run the following code
24+
// with it:
25+
// new CachedCompilerImpl(options, output, resident)
26+
27+
val bridgeLoader = getClass.getClassLoader
28+
val fixedLoader = CompilerClassLoader.fixBridgeLoader(bridgeLoader)
29+
val cciClass = fixedLoader.loadClass("xsbt.CachedCompilerImpl")
30+
cciClass.getConstructors.head
31+
.newInstance(options, output, resident: java.lang.Boolean)
32+
.asInstanceOf[CachedCompiler]
33+
}
34+
35+
def run(sources: Array[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger,
36+
delegate: Reporter, progress: CompileProgress, cached: CachedCompiler): Unit =
37+
cached.run(sources, changes, callback, log, delegate, progress)
38+
}
39+
40+
class CachedCompilerImpl(args: Array[String], output: Output, resident: Boolean) extends CachedCompiler {
41+
val outputArgs =
42+
output match {
43+
case multi: MultipleOutput =>
44+
???
45+
case single: SingleOutput =>
46+
List("-d", single.outputDirectory.getAbsolutePath.toString)
47+
}
48+
49+
def commandArguments(sources: Array[File]): Array[String] =
50+
(outputArgs ++ args.toList ++ sources.map(_.getAbsolutePath).sortWith(_ < _)).toArray[String]
51+
52+
def run(sources: Array[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, delegate: Reporter, progress: CompileProgress): Unit = synchronized {
53+
run(sources.toList, changes, callback, log, progress)
54+
}
55+
private[this] def run(sources: List[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, compileProgress: CompileProgress): Unit = {
56+
debug(log, args.mkString("Calling Dotty compiler with arguments (CompilerInterface):\n\t", "\n\t", ""))
57+
val ctx = (new ContextBase).initialCtx.fresh
58+
.setSbtCallback(callback)
59+
val cl = getClass.getClassLoader.asInstanceOf[URLClassLoader]
60+
61+
val reporter = DottyMain.process(commandArguments(sources.toArray), ctx)
62+
if (reporter.hasErrors) {
63+
throw new InterfaceCompileFailed(args, Array())
64+
}
65+
}
66+
}
67+
68+
class InterfaceCompileFailed(override val arguments: Array[String], override val problems: Array[Problem]) extends xsbti.CompileFailed {
69+
override val toString = "Compilation failed"
70+
}

bridge/src/main/scala/xsbt/Log.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* sbt -- Simple Build Tool
2+
* Copyright 2008, 2009 Mark Harrah
3+
*/
4+
package xsbt
5+
6+
object Log {
7+
def debug(log: xsbti.Logger, msg: => String) = log.debug(Message(msg))
8+
def settingsError(log: xsbti.Logger): String => Unit =
9+
s => log.error(Message(s))
10+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/* sbt -- Simple Build Tool
2+
* Copyright 2008, 2009 Mark Harrah
3+
*/
4+
package xsbt
5+
6+
object Message {
7+
def apply[T](s: => T) = new xsbti.F0[T] { def apply() = s }
8+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
trait A {
2+
def x: Int
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
trait B extends A {
2+
override def x = 2
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
trait C extends A {
2+
def x = 5
3+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
trait D extends C with B
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
trait C extends A {
2+
abstract override def x = super.x + 5
3+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import sbt._
2+
import Keys._
3+
4+
object DottyInjectedPlugin extends AutoPlugin {
5+
override def requires = plugins.JvmPlugin
6+
override def trigger = allRequirements
7+
8+
override val projectSettings = Seq(
9+
scalaVersion := "0.1-SNAPSHOT",
10+
scalaOrganization := "ch.epfl.lamp",
11+
scalacOptions += "-language:Scala2",
12+
scalaBinaryVersion := "2.11",
13+
autoScalaLibrary := false,
14+
libraryDependencies ++= Seq("org.scala-lang" % "scala-library" % "2.11.5"),
15+
scalaCompilerBridgeSource := ("ch.epfl.lamp" % "dotty-bridge" % "0.1.1-SNAPSHOT" % "component").sources()
16+
)
17+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
> compile
2+
$ copy-file changes/C2.scala C.scala
3+
-> compile
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
InputKey[Unit]("check-number-of-compiler-iterations") <<= inputTask { (argTask: TaskKey[Seq[String]]) =>
2+
(argTask, compile in Compile) map { (args: Seq[String], a: sbt.inc.Analysis) =>
3+
assert(args.size == 1)
4+
val expectedIterationsNumber = args(0).toInt
5+
assert(a.compilations.allCompilations.size == expectedIterationsNumber, "a.compilations.allCompilations.size = %d (expected %d)".format(a.compilations.allCompilations.size, expectedIterationsNumber))
6+
}
7+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
object Bar {
2+
def bar: Outer.TypeInner = null
3+
// comment to trigger recompilation
4+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import sbt._
2+
import Keys._
3+
4+
object DottyInjectedPlugin extends AutoPlugin {
5+
override def requires = plugins.JvmPlugin
6+
override def trigger = allRequirements
7+
8+
override val projectSettings = Seq(
9+
scalaVersion := "0.1-SNAPSHOT",
10+
scalaOrganization := "ch.epfl.lamp",
11+
scalacOptions += "-language:Scala2",
12+
scalaBinaryVersion := "2.11",
13+
autoScalaLibrary := false,
14+
libraryDependencies ++= Seq("org.scala-lang" % "scala-library" % "2.11.5"),
15+
scalaCompilerBridgeSource := ("ch.epfl.lamp" % "dotty-bridge" % "0.1.1-SNAPSHOT" % "component").sources()
16+
)
17+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object Bar {
2+
def bar: Outer.TypeInner = null
3+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
object Outer {
2+
class Inner { type Xyz }
3+
4+
type TypeInner = Inner { type Xyz = Int }
5+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class Impl {
2+
def bleep = Bar.bar
3+
}

0 commit comments

Comments
 (0)