Skip to content

Commit

Permalink
Automatically restart compiler when classfiles are missing.
Browse files Browse the repository at this point in the history
Previously, mdoc reused the same compiler instance for all compilations.
Now, we create a new compiler instance when the following situation
happens:

- compilation succeeds without any reported errors
- compilation produced no classfiles

I've consistently been able to reproduce this situation in the
https://github.com/spotify/scio repo when running mdoc on all the
markdown sources but I haven't been able to minimize the error to a
single source file. In Metals we restart the compiler frequently to
avoid cryptic errors like this. The compiler has a lot of state that
tends to go bad after several repeated compile requests.

Fixes #164.
  • Loading branch information
olafurpg committed May 4, 2019
1 parent 8730f45 commit ddc6297
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 9 deletions.
3 changes: 2 additions & 1 deletion mdoc/src/main/scala/mdoc/internal/cli/Settings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ case class Settings(
"Compiler flags such as compiler plugins '-Xplugin:kind-projector.jar' " +
"or custom options '-deprecated'. Formatted as a single string with space separated values. " +
"To pass multiple values: --scalac-options \"-Yrangepos -deprecated\". " +
"Defaults to the value of 'scalacOptions' in the 'mdoc.properties' resource file, if any."
"Defaults to the value of 'scalacOptions' in the 'mdoc.properties' resource file, if any. " +
"When using sbt-mdoc, update the `scalacOptions` sbt setting instead of passing --scalac-options to `mdocExtraArguments`."
)
scalacOptions: String = "",
@Description("Remove all files in the output directory before generating a new site.")
Expand Down
42 changes: 34 additions & 8 deletions mdoc/src/main/scala/mdoc/internal/markdown/MarkdownCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,8 @@ object MarkdownCompiler {
val instrumentedInput = InstrumentedInput(filename, instrumented)
val compileInput = Input.VirtualFile(filename, instrumented)
val edit = TokenEditDistance.fromTrees(sectionInputs.map(_.source), compileInput)
val doc = compiler.compile(compileInput, reporter, edit) match {
case Some(loader) =>
val cls = loader.loadClass("repl.Session$")
val doc = compiler.compile(compileInput, reporter, edit, "repl.Session$") match {
case Some(cls) =>
val ctor = cls.getDeclaredConstructor()
ctor.setAccessible(true)
val doc = ctor.newInstance().asInstanceOf[DocumentBuilder].$doc
Expand Down Expand Up @@ -119,7 +118,10 @@ class MarkdownCompiler(
settings.processArgumentString(scalacOptions)

private val sreporter = new FilterStoreReporter(settings)
val global = new Global(settings, sreporter)
var global = new Global(settings, sreporter)
private def reset(): Unit = {
global = new Global(settings, sreporter)
}
private val appClasspath: Array[URL] = classpath
.split(File.pathSeparator)
.map(path => new File(path).toURI.toURL)
Expand All @@ -140,7 +142,8 @@ class MarkdownCompiler(

def fail(original: Seq[Tree], input: Input, sectionPos: Position): String = {
sreporter.reset()
val run = new global.Run
val g = global
val run = new g.Run
run.compileSources(List(toSource(input)))
val out = new ByteArrayOutputStream()
val ps = new PrintStream(out)
Expand All @@ -161,15 +164,38 @@ class MarkdownCompiler(
def compileSources(input: Input, vreporter: Reporter, edit: TokenEditDistance): Unit = {
clearTarget()
sreporter.reset()
val run = new global.Run
val g = global
val run = new g.Run
run.compileSources(List(toSource(input)))
report(vreporter, input, edit)
}

def compile(input: Input, vreporter: Reporter, edit: TokenEditDistance): Option[ClassLoader] = {
def compile(
input: Input,
vreporter: Reporter,
edit: TokenEditDistance,
className: String,
retry: Int = 0
): Option[Class[_]] = {
compileSources(input, vreporter, edit)
if (!sreporter.hasErrors) {
Some(new AbstractFileClassLoader(target, appClassLoader))
val loader = new AbstractFileClassLoader(target, appClassLoader)
try {
Some(loader.loadClass(className))
} catch {
case _: ClassNotFoundException =>
if (retry < 1) {
reset()
compile(input, vreporter, edit, className, retry + 1)
} else {
vreporter.error(
s"${input.syntax}: skipping file, the compiler produced no classfiles " +
"and reported no errors to explain what went wrong during compilation. " +
"Please report an issue to https://github.com/scalameta/mdoc/issues."
)
None
}
}
} else {
None
}
Expand Down

0 comments on commit ddc6297

Please sign in to comment.