diff --git a/project/Build.scala b/project/Build.scala index 09f14558db0b..cdec08924461 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -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) @@ -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 @@ -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. diff --git a/sbt-bridge/src/xsbt/ScaladocInterface.scala b/sbt-bridge/src/xsbt/ScaladocInterface.scala index c5124d8996e0..0461447eefbc 100644 --- a/sbt-bridge/src/xsbt/ScaladocInterface.scala +++ b/sbt-bridge/src/xsbt/ScaladocInterface.scala @@ -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()) + } } } diff --git a/sbt-dotty/sbt-test/sbt-dotty/example-project/src/main/scala/hello/Hello.scala b/sbt-dotty/sbt-test/sbt-dotty/example-project/src/main/scala/hello/Hello.scala index 79c1971ce830..0b5f90467eff 100644 --- a/sbt-dotty/sbt-test/sbt-dotty/example-project/src/main/scala/hello/Hello.scala +++ b/sbt-dotty/sbt-test/sbt-dotty/example-project/src/main/scala/hello/Hello.scala @@ -1,4 +1,5 @@ package hello +/** Hello, world! */ object Hello { def main(args: Array[String]): Unit = { val dotty: Int | String = "dotty" diff --git a/sbt-dotty/sbt-test/sbt-dotty/example-project/test b/sbt-dotty/sbt-test/sbt-dotty/example-project/test index c6355b638050..86538f5cc17f 100644 --- a/sbt-dotty/sbt-test/sbt-dotty/example-project/test +++ b/sbt-dotty/sbt-test/sbt-dotty/example-project/test @@ -1,4 +1,5 @@ > run +> doc > 'set initialCommands := "1 + 1" ' # FIXME: does not work on the CI #> console diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala index 50cc0ddbba52..b4c0c395f40e 100644 --- a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala +++ b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala @@ -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 { @@ -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) @@ -174,7 +178,43 @@ object DottyPlugin extends AutoPlugin { scalaVersion.value.split("\\.").take(2).mkString(".") else scalaBinaryVersion.value - } + }, + + scalaInstance := Def.taskDyn { + 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}`.") + } + } }