Skip to content

Support doc task with dottydoc #4952

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -289,10 +289,14 @@ object Build {
compilerJar = jars.find(_.getName.startsWith("dotty-compiler_2.12")).get
}

// All compiler dependencies except the library
val otherDependencies = dependencyClasspath.in(`dotty-compiler`, Compile).value
.filterNot(_.get(artifact.key).exists(_.name == "dotty-library"))
.map(_.data)
// All dotty-doc's and compiler's dependencies except the library.
// (we get the compiler's dependencies because dottydoc depends on the compiler)
val otherDependencies = {
val excluded = Set("dotty-library", "dotty-compiler")
fullClasspath.in(`dotty-doc`, Compile).value
.filterNot(_.get(artifact.key).exists(a => excluded.contains(a.name)))
.map(_.data)
}

val allJars = libraryJar :: compilerJar :: otherDependencies.toList
val classLoader = state.value.classLoaderCache(allJars)
Expand Down Expand Up @@ -785,7 +789,7 @@ object Build {
description := "sbt compiler bridge for Dotty",
resolvers += Resolver.typesafeIvyRepo("releases"), // For org.scala-sbt:api
libraryDependencies ++= Seq(
Dependencies.compilerInterface(sbtVersion.value),
Dependencies.compilerInterface(sbtVersion.value) % Provided,
(Dependencies.zincApiinfo(sbtVersion.value) % Test).withDottyCompat(scalaVersion.value)
),
// The sources should be published with crossPaths := false since they
Expand Down Expand Up @@ -1269,6 +1273,7 @@ object Build {
def asDottySbtBridge(implicit mode: Mode): Project = project.withCommonSettings.
disablePlugins(ScriptedPlugin).
dependsOn(dottyCompiler % Provided).
dependsOn(dottyDoc % Provided).
settings(dottySbtBridgeSettings)

def asDottyBench(implicit mode: Mode): Project = project.withCommonSettings.
Expand Down
66 changes: 18 additions & 48 deletions sbt-bridge/src/xsbt/ScaladocInterface.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,57 +7,27 @@ import xsbti.{ Logger, Severity }
import java.net.URL
import java.util.Optional

import dotty.tools.dotc.core.Contexts.{ Context, ContextBase }
import dotty.tools.dotc.reporting.Reporter

class ScaladocInterface {
def run(args: Array[String], log: Logger, delegate: xsbti.Reporter) =
(new DottydocRunner(args, log, delegate)).run()
def run(args: Array[String], log: Logger, delegate: xsbti.Reporter) = {
new DottydocRunner(args, log, delegate).run()
}
}

class DottydocRunner(args: Array[String], log: Logger, delegate: xsbti.Reporter) {
def run(): Unit = delegate.log(Problem(
NoPosition,
"""|The dotty sbt-bridge currently does not support doc generation directly
|via sbt. Please see the dotty documentation at dotty.epfl.ch""".stripMargin,
Severity.Error
))

private[this] val NoPosition = new xsbti.Position {
val line = Optional.empty[Integer]
val lineContent = ""
val offset = Optional.empty[Integer]
val sourcePath = Optional.empty[String]
val sourceFile = Optional.empty[java.io.File]
val pointer = Optional.empty[Integer]
val pointerSpace = Optional.empty[String]
}

private def getStringSetting(name: String): Option[String] =
args find (_.startsWith(name)) map (_.drop(name.length))

private def getOutputFolder(args: Array[String]): Option[String] =
args sliding(2) find { case Array(x, _) => x == "-d" } map (_.tail.head.trim)

private def getTemplate(resources: List[URL]): Option[URL] =
resources.find(_.getFile.endsWith("template.html"))

private def getResources(args: Array[String]): List[URL] = {
val cp = args sliding (2) find { case Array(x, _) => x == "-classpath" } map (_.tail.head.trim) getOrElse ""

cp.split(":").find(_.endsWith("dottydoc-client.jar")).map { resourceJar =>
import java.util.jar.JarFile
val jarEntries = (new JarFile(resourceJar)).entries
var entries: List[URL] = Nil

while (jarEntries.hasMoreElements) {
val entry = jarEntries.nextElement()

if (!entry.isDirectory()) {
val path = s"jar:file:$resourceJar!/${entry.getName}"
val url = new URL(path)
entries = url :: entries
}
}

entries
} getOrElse (Nil)
def run(): Unit = {
log.debug(() => args.mkString("Calling Dottydoc with arguments (ScaladocInterface):\n\t", "\n\t", ""))

val ctx = (new ContextBase).initialCtx.fresh
.setReporter(new DelegatingReporter(delegate))

val dottydocMainClass = Class.forName("dotty.tools.dottydoc.Main")
val processMethod = dottydocMainClass.getMethod("process", classOf[Array[String]], classOf[Context])
val reporter = processMethod.invoke(null, args, ctx).asInstanceOf[Reporter]
if (reporter.hasErrors) {
throw new InterfaceCompileFailed(args, Array())
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package hello
/** Hello, world! */
object Hello {
def main(args: Array[String]): Unit = {
val dotty: Int | String = "dotty"
Expand Down
1 change: 1 addition & 0 deletions sbt-dotty/sbt-test/sbt-dotty/example-project/test
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
> run
> doc
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not run on PRs. Did you run it locally? I'll take your words for it 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works fine on my machine at least.

> 'set initialCommands := "1 + 1" '
# FIXME: does not work on the CI
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's preventing this from being run on the CI? It would be nice to also check that this generates what it's supposed to.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The REPL doesn't work in the CI. JLine doesn't work outside of a "real terminal"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There might be a way via some JLine option to support dumb or virtual terminals
(https://github.com/jline/jline3/wiki/Terminals) or sth. else... do we have an issue to investigate?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default JLine spawns a "dumb" terminal if not able to create a terminal. But you can't do anything with it, not sure we can even use it for test purpose. So i disabled it:
https://github.com/lampepfl/dotty/blob/0d7826c8a77bfa0da727ac21213475739faa9428/compiler/src/dotty/tools/repl/JLineTerminal.scala#L21

#> console
44 changes: 42 additions & 2 deletions sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package dotty.tools.sbtplugin

import sbt._
import sbt.Keys._
// import sbt.inc.{ ClassfileManager, IncOptions }
import sbt.librarymanagement.DependencyResolution
import sbt.internal.inc.ScalaInstance
import xsbti.compile._
import java.net.URLClassLoader
import java.util.Optional

object DottyPlugin extends AutoPlugin {
Expand Down Expand Up @@ -151,6 +153,8 @@ object DottyPlugin extends AutoPlugin {
scalaOrganization.value
},

scalacOptions in (Compile, doc) ++= Seq("-project", name.value),

incOptions in Compile := {
val inc = (incOptions in Compile).value
if (isDotty.value)
Expand All @@ -174,7 +178,43 @@ object DottyPlugin extends AutoPlugin {
scalaVersion.value.split("\\.").take(2).mkString(".")
else
scalaBinaryVersion.value
}
},

scalaInstance := Def.taskDyn {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a taskDyn? Is it to only evaluate fetchArtifactsOf if isDotty is true?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

val si = scalaInstance.value
if (isDotty.value) {
Def.task {
val dottydocArtifacts = fetchArtifactsOf("dotty-doc").value
val includeArtifact = (f: File) => f.getName.endsWith(".jar")
val dottydocJars = dottydocArtifacts.filter(includeArtifact).toArray
val allJars = (si.allJars ++ dottydocJars).distinct
val loader = new URLClassLoader(Path.toURLs(dottydocJars), si.loader)
new ScalaInstance(si.version, loader, si.loaderLibraryOnly, si.libraryJar, si.compilerJar, allJars, si.explicitActual)
}
} else {
Def.task { si }
}
}.value
)
}

/** Fetch artefacts for scalaOrganization.value %% moduleName % scalaVersion.value */
private def fetchArtifactsOf(moduleName: String) = Def.task {
val dependencyResolution = Keys.dependencyResolution.value
val log = streams.value.log
val scalaInfo = scalaModuleInfo.value
val updateConfiguration = Keys.updateConfiguration.value
val warningConfiguration = (unresolvedWarningConfiguration in update).value

val moduleID = (scalaOrganization.value %% moduleName % scalaVersion.value).cross(CrossVersion.binary)
val descriptor = dependencyResolution.wrapDependencyInModule(moduleID, scalaInfo)

dependencyResolution.update(descriptor, updateConfiguration, warningConfiguration, log) match {
case Right(report) =>
report.allFiles
case _ =>
throw new MessageOnlyException(
s"Couldn't retrieve `${scalaOrganization.value} %% $moduleName %% ${scalaVersion.value}`.")
}
}
}