-
Notifications
You must be signed in to change notification settings - Fork 121
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The `scala3-compiler-bridge` lives in its own module because it shares almost no sources with the Scala 2 `compiler-bridge`. It is compiled by Scala 3, using `sbt-dotty`, because it depends on the Scala 3 compiler, which is compiled in Scala 3. As soon as `sbt-dotty` is merged into sbt, the `scala3-compiler-bridge` should be able to compiles itself. This first minimal implementation is a direct translation of `sbt-bridge` in `https://github.com/lampepfl/dotty`: - https://github.com/lampepfl/dotty/blob/master/sbt-bridge/src/xsbt/CompilerInterface.java - https://github.com/lampepfl/dotty/blob/master/sbt-bridge/src/xsbt/CachedCompilerImpl.java One minor difference is that it implements the new `xsbti.compile.CompilerInterface2`.
- Loading branch information
Showing
7 changed files
with
272 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
...la3-compiler-bridge/src/main/resources/META-INF/services/xsbti.compile.CompilerInterface2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
xsbt.CompilerBridge |
97 changes: 97 additions & 0 deletions
97
internal/scala3-compiler-bridge/src/main/scala/xsbt/CompilerBridge.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
/* | ||
* Zinc - The incremental compiler for Scala. | ||
* Copyright Lightbend, Inc. and Mark Harrah | ||
* | ||
* Licensed under Apache License 2.0 | ||
* (http://www.apache.org/licenses/LICENSE-2.0). | ||
* | ||
* See the NOTICE file distributed with this work for | ||
* additional information regarding copyright ownership. | ||
*/ | ||
|
||
package xsbt | ||
|
||
import dotty.tools.dotc.Main | ||
import dotty.tools.dotc.config.Properties | ||
import dotty.tools.dotc.core.Contexts.ContextBase | ||
import xsbt.Log.debug | ||
import xsbti.{ AnalysisCallback, Logger, PathBasedFile, Problem, Reporter, VirtualFile } | ||
import xsbti.compile._ | ||
|
||
/** | ||
* This is the entry point for the compiler bridge (implementation of CompilerInterface2) | ||
*/ | ||
final class CompilerBridge extends xsbti.compile.CompilerInterface2 { | ||
override def run( | ||
sources: Array[VirtualFile], | ||
changes: DependencyChanges, | ||
options: Array[String], | ||
output: Output, | ||
callback: AnalysisCallback, | ||
delegate: Reporter, | ||
progress: CompileProgress, | ||
log: Logger | ||
): Unit = { | ||
val cached = new CachedCompilerImpl(options, output) | ||
cached.run(sources, callback, log, delegate) | ||
} | ||
} | ||
|
||
private final class CachedCompilerImpl( | ||
args: Array[String], | ||
output: Output | ||
) { | ||
|
||
private val outputArgs = output match { | ||
case output: SingleOutput => | ||
Array("-d", output.getOutputDirectoryAsPath.toString) | ||
case _ => | ||
throw new IllegalArgumentException( | ||
s"output should be a SingleOutput, was a ${output.getClass.getName}" | ||
); | ||
} | ||
|
||
private def commandArguments(sources: Array[VirtualFile]): Array[String] = { | ||
val files = sources.map { | ||
case pathBased: PathBasedFile => pathBased | ||
case virtualFile => | ||
throw new IllegalArgumentException( | ||
s"source should be a PathBasedFile, was ${virtualFile.getClass.getName}" | ||
) | ||
} | ||
outputArgs ++ args ++ files.map(_.toPath.toString) | ||
} | ||
|
||
private def infoOnCachedCompiler(compilerId: String): String = | ||
s"[zinc] Running cached compiler $compilerId for Scala compiler ${Properties.versionString}" | ||
|
||
private def prettyPrintCompilationArguments(args: Array[String]) = | ||
args.mkString("[zinc] The Scala compiler is invoked with:\n\t", "\n\t", "") | ||
|
||
def run( | ||
sources: Array[VirtualFile], | ||
callback: AnalysisCallback, | ||
log: Logger, | ||
delegate: Reporter, | ||
): Unit = synchronized { | ||
debug(log, infoOnCachedCompiler(hashCode().toLong.toHexString)) | ||
debug(log, prettyPrintCompilationArguments(args)) | ||
val dreporter = new DelegatingReporter(delegate) | ||
try { | ||
val ctx = new ContextBase().initialCtx.fresh | ||
.setSbtCallback(callback) | ||
.setReporter(dreporter) | ||
val reporter = Main.process(commandArguments(sources), ctx) | ||
if (reporter.hasErrors) | ||
throw new InterfaceCompileFailed(args, Array(), "Compilation failed") | ||
} finally { | ||
dreporter.dropDelegate() | ||
} | ||
} | ||
} | ||
|
||
class InterfaceCompileFailed( | ||
val arguments: Array[String], | ||
val problems: Array[Problem], | ||
override val toString: String | ||
) extends xsbti.CompileFailed |
130 changes: 130 additions & 0 deletions
130
internal/scala3-compiler-bridge/src/main/scala/xsbt/DelegatingReporter.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
/* | ||
* Zinc - The incremental compiler for Scala. | ||
* Copyright Lightbend, Inc. and Mark Harrah | ||
* | ||
* Licensed under Apache License 2.0 | ||
* (http://www.apache.org/licenses/LICENSE-2.0). | ||
* | ||
* See the NOTICE file distributed with this work for | ||
* additional information regarding copyright ownership. | ||
*/ | ||
|
||
package xsbt | ||
|
||
import java.io.File | ||
import java.util.Optional | ||
|
||
import dotty.tools.dotc.core.Contexts.Context | ||
import dotty.tools.dotc.reporting.{ AbstractReporter, Diagnostic } | ||
import dotty.tools.dotc.interfaces | ||
import dotty.tools.dotc.util.{ SourceFile, SourcePosition } | ||
import xsbti.{ Position, Problem, Severity } | ||
|
||
private final class DelegatingReporter( | ||
private[this] var delegate: xsbti.Reporter | ||
) extends AbstractReporter { | ||
import DelegatingReporter._ | ||
def dropDelegate(): Unit = { delegate = null } | ||
|
||
override def printSummary(using ctx: Context): Unit = delegate.printSummary() | ||
|
||
override def doReport(diagnostic: Diagnostic)(using Context): Unit = { | ||
val severity = convert(diagnostic.level) | ||
val position = convert(diagnostic.pos) | ||
val message = diagnostic.msg | ||
val explain = if (Diagnostic.shouldExplain(diagnostic) && message.explanation.nonEmpty) { | ||
explanation(message) | ||
} else "" | ||
val rendered = messageAndPos(message, diagnostic.pos, diagnosticLevel(diagnostic)) + explain | ||
delegate.log(CompileProblem(position, message.message, severity, rendered)) | ||
} | ||
} | ||
|
||
private object DelegatingReporter { | ||
private def convert(pos: SourcePosition): Position = { | ||
if (pos.exists) new PositionImpl(pos, pos.source) | ||
else EmptyPosition | ||
} | ||
|
||
private def convert(level: Int): xsbti.Severity = { | ||
level match { | ||
case interfaces.Diagnostic.ERROR => Severity.Error | ||
case interfaces.Diagnostic.WARNING => Severity.Warn | ||
case interfaces.Diagnostic.INFO => Severity.Info | ||
case level => throw new IllegalArgumentException(s"Bad diagnostic level: $level") | ||
} | ||
} | ||
|
||
object EmptyPosition extends xsbti.Position { | ||
override def line(): Optional[Integer] = Optional.empty | ||
override def lineContent(): String = "" | ||
override def offset(): Optional[Integer] = Optional.empty | ||
override def pointer(): Optional[Integer] = Optional.empty | ||
override def pointerSpace(): Optional[String] = Optional.empty | ||
override def sourcePath(): Optional[String] = Optional.empty | ||
override def sourceFile(): Optional[File] = Optional.empty | ||
} | ||
|
||
class PositionImpl( | ||
pos: SourcePosition, | ||
src: SourceFile | ||
) extends xsbti.Position { | ||
def line: Optional[Integer] = { | ||
if (src.content.isEmpty) | ||
Optional.empty | ||
else { | ||
val line = pos.line | ||
if (line == -1) Optional.empty | ||
else Optional.of(line + 1) | ||
} | ||
} | ||
def lineContent: String = { | ||
if (src.content.isEmpty) "" | ||
else { | ||
val line = pos.lineContent | ||
if (line.endsWith("\r\n")) | ||
line.substring(0, line.length - 2) | ||
else if (line.endsWith("\n") || line.endsWith("\u000c")) | ||
line.substring(0, line.length - 1) | ||
else line | ||
} | ||
} | ||
def offset: Optional[Integer] = Optional.of(pos.point) | ||
def sourcePath: Optional[String] = { | ||
if (!src.exists) Optional.empty | ||
else Optional.ofNullable(src.file.path) | ||
} | ||
def sourceFile: Optional[File] = { | ||
if (!src.exists) Optional.empty | ||
else Optional.ofNullable(src.file.file) | ||
} | ||
def pointer: Optional[Integer] = { | ||
if (src.content.isEmpty) Optional.empty | ||
else Optional.of(pos.point - src.startOfLine(pos.point)) | ||
} | ||
def pointerSpace: Optional[String] = { | ||
if (src.content.isEmpty) Optional.empty | ||
else { | ||
// Don't crash if pointer is out-of-bounds (happens with some macros) | ||
val fixedPointer = Math.min(pointer.get, lineContent.length) | ||
val result = lineContent | ||
.take(fixedPointer) | ||
.map { | ||
case '\t' => '\t' | ||
case _ => ' ' | ||
} | ||
Optional.of(result) | ||
} | ||
} | ||
} | ||
|
||
private final case class CompileProblem( | ||
position: Position, | ||
message: String, | ||
severity: Severity, | ||
_rendered: String | ||
) extends Problem { | ||
override def category = "" | ||
override def rendered: Optional[String] = Optional.of(_rendered) | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
internal/scala3-compiler-bridge/src/main/scala/xsbt/Log.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/* | ||
* Zinc - The incremental compiler for Scala. | ||
* Copyright Lightbend, Inc. and Mark Harrah | ||
* | ||
* Licensed under Apache License 2.0 | ||
* (http://www.apache.org/licenses/LICENSE-2.0). | ||
* | ||
* See the NOTICE file distributed with this work for | ||
* additional information regarding copyright ownership. | ||
*/ | ||
|
||
package xsbt | ||
|
||
object Log { | ||
def debug(log: xsbti.Logger, msg: => String) = log.debug(() => msg) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters