diff --git a/build.sbt b/build.sbt index ab195ffae..f33b9585a 100644 --- a/build.sbt +++ b/build.sbt @@ -1,7 +1,8 @@ ScriptedPlugin.scriptedSettings libraryDependencies ++= { - if (sbtVersion.value startsWith "0.13") + println(s"Evaluated ${sbtVersion in pluginCrossBuild value}") + if ((sbtVersion in pluginCrossBuild).value startsWith "0.13") Seq("com.github.mdr" %% "ascii-graphs" % "0.0.3") else Nil @@ -12,3 +13,19 @@ libraryDependencies += "org.specs2" %% "specs2-core" % "3.9.1" % "test" scalacOptions ++= Seq("-deprecation", "-unchecked") ScalariformSupport.formatSettings + +crossSbtVersions := Seq("1.0.1", "0.13.16") + +//sbtVersion in pluginCrossBuild := "1.0.0" + +/* +Try to prevent silly warnings + +libraryDependencies += ("org.scala-sbt" %% "main-settings" % "1.0.1-SNAPSHOT")//.excludeAll(ExclusionRule(organization = "org.scala-sbt")) + +libraryDependencies += "org.scala-sbt" %% "command" % "1.0.0"force() +libraryDependencies += "org.scala-sbt" %% "completion" % "1.0.0"force() +libraryDependencies += "org.scala-sbt" %% "task-system" % "1.0.0"force() +libraryDependencies += "org.scala-sbt" %% "core-macros" % "1.0.0" force() + +*/ \ No newline at end of file diff --git a/src/main/scala-sbt-0.13/sbt/compat/SbtCompat.scala b/src/main/scala-sbt-0.13/sbt/compat/SbtCompat.scala new file mode 100644 index 000000000..c3d8570bd --- /dev/null +++ b/src/main/scala-sbt-0.13/sbt/compat/SbtCompat.scala @@ -0,0 +1,11 @@ +package sbt.compat + +object SbtCompat { + object librarymanagement + object internal { + object librarymanagement + object util { + val JLine: { def usingTerminal[T](f: jline.Terminal => T): T } = sbt.JLine + } + } +} \ No newline at end of file diff --git a/src/main/scala-sbt-1.0/sbt/compat/SbtCompat.scala b/src/main/scala-sbt-1.0/sbt/compat/SbtCompat.scala new file mode 100644 index 000000000..9da65489d --- /dev/null +++ b/src/main/scala-sbt-1.0/sbt/compat/SbtCompat.scala @@ -0,0 +1,3 @@ +package sbt.compat + +object SbtCompat \ No newline at end of file diff --git a/src/main/scala/net/virtualvoid/sbt/graph/DependencyGraphSettings.scala b/src/main/scala/net/virtualvoid/sbt/graph/DependencyGraphSettings.scala index 790453585..fd3828488 100644 --- a/src/main/scala/net/virtualvoid/sbt/graph/DependencyGraphSettings.scala +++ b/src/main/scala/net/virtualvoid/sbt/graph/DependencyGraphSettings.scala @@ -25,48 +25,58 @@ import net.virtualvoid.sbt.graph.backend.{ IvyReport, SbtUpdateReport } import net.virtualvoid.sbt.graph.rendering.{ AsciiGraph, DagreHTML } import net.virtualvoid.sbt.graph.util.IOUtil +import sbt.compat.SbtCompat._ +import internal.librarymanagement._ +import librarymanagement._ + object DependencyGraphSettings { import DependencyGraphKeys._ import ModuleGraphProtocol._ def graphSettings = Seq( - ivyReportFunction <<= ivyReportFunctionTask, - updateConfiguration in ignoreMissingUpdate <<= updateConfiguration(config ⇒ new UpdateConfiguration(config.retrieve, true, config.logging)), - ignoreMissingUpdateT, + ivyReportFunction := ivyReportFunctionTask.value, + updateConfiguration in ignoreMissingUpdate := updateConfiguration.value.withMissingOk(true), + ignoreMissingUpdate := update.value, filterScalaLibrary in Global := true) ++ Seq(Compile, Test, IntegrationTest, Runtime, Provided, Optional).flatMap(ivyReportForConfig) def ivyReportForConfig(config: Configuration) = inConfig(config)(Seq( - ivyReport <<= ivyReportFunction map (_(config.toString)) dependsOn (ignoreMissingUpdate), - crossProjectId <<= (scalaVersion, scalaBinaryVersion, projectID)((sV, sBV, id) ⇒ CrossVersion(sV, sBV)(id)), - moduleGraphSbt <<= moduleGraphSbtTask, - moduleGraphIvyReport <<= moduleGraphIvyReportTask, - moduleGraph <<= (sbtVersion, moduleGraphSbt, moduleGraphIvyReport) { (version, graphSbt, graphIvy) ⇒ - version match { - case Version(0, 13, x, _) if x >= 6 ⇒ graphSbt - case _ ⇒ graphIvy + ivyReport := { Def.task { ivyReportFunction.value.apply(config.toString) } dependsOn (ignoreMissingUpdate) }.value, + crossProjectId := sbt.CrossVersion(scalaVersion.value, scalaBinaryVersion.value)(projectID.value), + moduleGraphSbt := + ignoreMissingUpdate.value.configuration(configuration.value).map(report ⇒ SbtUpdateReport.fromConfigurationReport(report, crossProjectId.value)).getOrElse(ModuleGraph.empty), + moduleGraphIvyReport := IvyReport.fromReportFile(absoluteReportPath(ivyReport.value)), + moduleGraph := { + sbtVersion.value match { + case Version(0, 13, x, _) if x >= 6 ⇒ moduleGraphSbt.value + case Version(1, _, _, _) ⇒ moduleGraphSbt.value } }, - moduleGraph <<= (scalaVersion, moduleGraph, filterScalaLibrary) map { (scalaV, graph, filter) ⇒ - if (filter) GraphTransformations.ignoreScalaLibrary(scalaV, graph) - else graph + moduleGraph := { + // FIXME: remove busywork + val scalaVersion = Keys.scalaVersion.value + val moduleGraph = DependencyGraphKeys.moduleGraph.value + + if (filterScalaLibrary.value) GraphTransformations.ignoreScalaLibrary(scalaVersion, moduleGraph) + else moduleGraph }, - moduleGraphStore <<= moduleGraph storeAs moduleGraphStore triggeredBy moduleGraph, - asciiTree <<= moduleGraph map rendering.AsciiTree.asciiTree, - dependencyTree <<= print(asciiTree), - dependencyGraphMLFile <<= target / "dependencies-%s.graphml".format(config.toString), - dependencyGraphML <<= dependencyGraphMLTask, - dependencyDotFile <<= target / "dependencies-%s.dot".format(config.toString), - dependencyDotString <<= dependencyDotStringTask, - dependencyDot <<= writeToFile(dependencyDotString, dependencyDotFile), - dependencyBrowseGraphTarget <<= target / "browse-dependency-graph", - dependencyBrowseGraphHTML <<= browseGraphHTMLTask, - dependencyBrowseGraph <<= (dependencyBrowseGraphHTML, streams).map { (uri, streams) ⇒ - streams.log.info("Opening in browser...") + moduleGraphStore := (moduleGraph storeAs moduleGraphStore triggeredBy moduleGraph).value, + asciiTree := rendering.AsciiTree.asciiTree(moduleGraph.value), + dependencyTree := print(asciiTree).value, + dependencyGraphMLFile := { target.value / "dependencies-%s.graphml".format(config.toString) }, + dependencyGraphML := dependencyGraphMLTask.value, + dependencyDotFile := { target.value / "dependencies-%s.dot".format(config.toString) }, + dependencyDotString := rendering.DOT.dotGraph(moduleGraph.value, dependencyDotHeader.value, dependencyDotNodeLabel.value, rendering.DOT.AngleBrackets), + dependencyDot := writeToFile(dependencyDotString, dependencyDotFile).value, + dependencyBrowseGraphTarget := { target.value / "browse-dependency-graph" }, + dependencyBrowseGraphHTML := browseGraphHTMLTask.value, + dependencyBrowseGraph := { + val uri = dependencyBrowseGraphHTML.value + streams.value.log.info("Opening in browser...") java.awt.Desktop.getDesktop.browse(uri) uri }, - dependencyList <<= printFromGraph(rendering.FlatList.render(_, _.id.idString)), - dependencyStats <<= printFromGraph(rendering.Statistics.renderModuleStatsList), + dependencyList := printFromGraph(rendering.FlatList.render(_, _.id.idString)).value, + dependencyStats := printFromGraph(rendering.Statistics.renderModuleStatsList).value, dependencyDotHeader := """digraph "dependency-graph" { | graph[rankdir="LR"] | edge [ @@ -75,79 +85,71 @@ object DependencyGraphSettings { dependencyDotNodeLabel := { (organisation: String, name: String, version: String) ⇒ """%s
%s
%s""".format(organisation, name, version) }, - whatDependsOn <<= InputTask(artifactIdParser) { module ⇒ - (module, streams, moduleGraph) map { (module, streams, graph) ⇒ - streams.log.info(rendering.AsciiTree.asciiTree(GraphTransformations.reverseGraphStartingAt(graph, module))) - } + whatDependsOn := { + val module = artifactIdParser.parsed + streams.value.log.info(rendering.AsciiTree.asciiTree(GraphTransformations.reverseGraphStartingAt(moduleGraph.value, module))) }, - licenseInfo <<= (moduleGraph, streams) map showLicenseInfo) ++ AsciiGraph.asciiGraphSetttings) + licenseInfo := showLicenseInfo(moduleGraph.value, streams.value)) ++ AsciiGraph.asciiGraphSetttings) - def ivyReportFunctionTask = - (sbtVersion, target, projectID, ivyModule, appConfiguration, streams) map { (sbtV, target, projectID, ivyModule, config, streams) ⇒ - sbtV match { - case Version(0, min, fix, _) if min > 12 || (min == 12 && fix >= 3) ⇒ - (c: String) ⇒ file("%s/resolution-cache/reports/%s-%s-%s.xml".format(target, projectID.organization, crossName(ivyModule), c)) - case Version(0, min, fix, _) if min == 12 && fix >= 1 && fix < 3 ⇒ - ivyModule.withModule(streams.log) { (i, moduleDesc, _) ⇒ - val id = ResolveOptions.getDefaultResolveId(moduleDesc) - (c: String) ⇒ file("%s/resolution-cache/reports/%s/%s-resolved.xml" format (target, id, c)) - } - case _ ⇒ - val home = config.provider.scalaProvider.launcher.ivyHome - (c: String) ⇒ file("%s/cache/%s-%s-%s.xml" format (home, projectID.organization, crossName(ivyModule), c)) - } + def ivyReportFunctionTask = Def.task { + // FIXME: and remove busywork after https://github.com/sbt/sbt/issues/3299 is fixed + val target = Keys.target.value + val projectID = Keys.projectID.value + val ivyModule = Keys.ivyModule.value + + sbtVersion.value match { + case Version(0, min, fix, _) if min > 12 || (min == 12 && fix >= 3) ⇒ + (c: String) ⇒ file("%s/resolution-cache/reports/%s-%s-%s.xml".format(target, projectID.organization, crossName(ivyModule), c)) + case Version(0, min, fix, _) if min == 12 && fix >= 1 && fix < 3 ⇒ + ivyModule.withModule(streams.value.log) { (i, moduleDesc, _) ⇒ + val id = ResolveOptions.getDefaultResolveId(moduleDesc) + (c: String) ⇒ file("%s/resolution-cache/reports/%s/%s-resolved.xml" format (target, id, c)) + } + case _ ⇒ + val home = appConfiguration.value.provider.scalaProvider.launcher.ivyHome + (c: String) ⇒ file("%s/cache/%s-%s-%s.xml" format (home, projectID.organization, crossName(ivyModule), c)) } - - def moduleGraphIvyReportTask = ivyReport map (absoluteReportPath.andThen(IvyReport.fromReportFile)) - def moduleGraphSbtTask = - (ignoreMissingUpdate, crossProjectId, configuration) map { (update, root, config) ⇒ - update.configuration(config.name).map(report ⇒ SbtUpdateReport.fromConfigurationReport(report, root)).getOrElse(ModuleGraph.empty) - } - - def printAsciiGraphTask = - (streams, asciiGraph) map (_.log.info(_)) + } def dependencyGraphMLTask = - (moduleGraph, dependencyGraphMLFile, streams) map { (graph, resultFile, streams) ⇒ - rendering.GraphML.saveAsGraphML(graph, resultFile.getAbsolutePath) - streams.log.info("Wrote dependency graph to '%s'" format resultFile) + Def.task { + val resultFile = dependencyGraphMLFile.value + rendering.GraphML.saveAsGraphML(moduleGraph.value, resultFile.getAbsolutePath) + streams.value.log.info("Wrote dependency graph to '%s'" format resultFile) resultFile } - def dependencyDotStringTask = - (moduleGraph, dependencyDotHeader, dependencyDotNodeLabel).map { - (graph, dotHead, nodeLabel) ⇒ rendering.DOT.dotGraph(graph, dotHead, nodeLabel, rendering.DOT.AngleBrackets) - } def browseGraphHTMLTask = - (moduleGraph, dependencyDotHeader, dependencyDotNodeLabel, dependencyBrowseGraphTarget, streams).map { (graph, dotHead, nodeLabel, target, streams) ⇒ - val dotGraph = rendering.DOT.dotGraph(graph, dotHead, nodeLabel, rendering.DOT.LabelTypeHtml) - val link = DagreHTML.createLink(dotGraph, target) - streams.log.info(s"HTML graph written to $link") + Def.task { + val dotGraph = rendering.DOT.dotGraph(moduleGraph.value, dependencyDotHeader.value, dependencyDotNodeLabel.value, rendering.DOT.LabelTypeHtml) + val link = DagreHTML.createLink(dotGraph, target.value) + streams.value.log.info(s"HTML graph written to $link") link } def writeToFile(dataTask: TaskKey[String], fileTask: SettingKey[File]) = - (dataTask, fileTask, streams).map { (data, outFile, streams) ⇒ - IOUtil.writeToFile(data, outFile) + Def.task { + val outFile = fileTask.value + IOUtil.writeToFile(dataTask.value, outFile) - streams.log.info("Wrote dependency graph to '%s'" format outFile) + streams.value.log.info("Wrote dependency graph to '%s'" format outFile) outFile } def absoluteReportPath = (file: File) ⇒ file.getAbsolutePath def print(key: TaskKey[String]) = - (streams, key) map (_.log.info(_)) + Def.task { streams.value.log.info(key.value) } def printFromGraph(f: ModuleGraph ⇒ String) = - (streams, moduleGraph) map ((streams, graph) ⇒ streams.log.info(f(graph))) + Def.task { streams.value.log.info(f(moduleGraph.value)) } def showLicenseInfo(graph: ModuleGraph, streams: TaskStreams) { val output = graph.nodes.filter(_.isUsed).groupBy(_.license).toSeq.sortBy(_._1).map { case (license, modules) ⇒ license.getOrElse("No license specified") + "\n" + - modules.map(_.id.idString formatted "\t %s").mkString("\n") + modules.map("\t %s" format _.id.idString).mkString("\n") }.mkString("\n\n") streams.log.info(output) } @@ -159,7 +161,7 @@ object DependencyGraphSettings { (Space ~> token("--force")).?.map(_.isDefined) } - val artifactIdParser: Initialize[State ⇒ Parser[ModuleId]] = + val artifactIdParser: Def.Initialize[State ⇒ Parser[ModuleId]] = resolvedScoped { ctx ⇒ (state: State) ⇒ val graph = loadFromContext(moduleGraphStore, ctx, state) getOrElse ModuleGraph(Nil, Nil) @@ -197,11 +199,17 @@ object DependencyGraphSettings { } /** - * This is copied directly from sbt/main/Defaults.java and then changed to update the UpdateConfiguration + * This is copied directly from sbt/main/Defaults.scala and then changed to update the UpdateConfiguration * to ignore missing artifacts. */ - def ignoreMissingUpdateT = - ignoreMissingUpdate <<= Def.task { + /*def ignoreMissingUpdateT = + ignoreMissingUpdate := { + // FIXME: remove busywork + + val scalaVersion = Keys.scalaVersion.value + val unmanagedScalaInstanceOnly = SbtAccess.unmanagedScalaInstanceOnly.value + val scalaOrganization = Keys.scalaOrganization.value + val depsUpdated = transitiveUpdate.value.exists(!_.stats.cached) val isRoot = executionRoots.value contains resolvedScoped.value val s = streams.value @@ -211,18 +219,19 @@ object DependencyGraphSettings { // the resolved Scala version and the scalaHome version: compatible (weakly- no qualifier checked) // the resolved Scala version and the declared scalaVersion: assume the user intended scalaHome to override anything with scalaVersion def subUnmanaged(subVersion: String, jars: Seq[File]) = (sv: String) ⇒ - (partialVersion(sv), partialVersion(subVersion), partialVersion(scalaVersion.value)) match { + (partialVersion(sv), partialVersion(subVersion), partialVersion(scalaVersion)) match { case (Some(res), Some(sh), _) if res == sh ⇒ jars case (Some(res), _, Some(decl)) if res == decl ⇒ jars case _ ⇒ Nil } - val subScalaJars: String ⇒ Seq[File] = SbtAccess.unmanagedScalaInstanceOnly.value match { - case Some(si) ⇒ subUnmanaged(si.version, si.jars) + val subScalaJars: String ⇒ Seq[File] = unmanagedScalaInstanceOnly match { + case Some(si) ⇒ subUnmanaged(si.version, si.allJars) case None ⇒ sv ⇒ if (scalaProvider.version == sv) scalaProvider.jars else Nil } - val transform: UpdateReport ⇒ UpdateReport = r ⇒ Classpaths.substituteScalaFiles(scalaOrganization.value, r)(subScalaJars) + val transform: UpdateReport ⇒ UpdateReport = r ⇒ Classpaths.substituteScalaFiles(scalaOrganization, r)(subScalaJars) val show = Reference.display(thisProjectRef.value) - Classpaths.cachedUpdate(s.cacheDirectory, show, ivyModule.value, (updateConfiguration in ignoreMissingUpdate).value, transform, skip = (skip in update).value, force = isRoot, depsUpdated = depsUpdated, log = s.log) - } + SbtAccess.cachedUpdater( + s.cacheDirectory, show, ivyModule.value, (updateConfiguration in ignoreMissingUpdate).value, transform, skip = (skip in update).value, force = isRoot, depsUpdated = depsUpdated, log = s.log) + }*/ } diff --git a/src/main/scala/net/virtualvoid/sbt/graph/model.scala b/src/main/scala/net/virtualvoid/sbt/graph/model.scala index 68b6a8862..850a43047 100644 --- a/src/main/scala/net/virtualvoid/sbt/graph/model.scala +++ b/src/main/scala/net/virtualvoid/sbt/graph/model.scala @@ -16,9 +16,13 @@ package net.virtualvoid.sbt.graph -import java.io.File +import java.io.{ ByteArrayInputStream, ByteArrayOutputStream, File } +import java.util.Base64 -import scala.collection.mutable.{ MultiMap, HashMap, Set } +import sbinary.{ JavaInput, JavaOutput } +import sjsonnew.{ Builder, Unbuilder } + +import scala.collection.mutable.{ HashMap, MultiMap, Set } case class ModuleId(organisation: String, name: String, @@ -71,4 +75,21 @@ object ModuleGraphProtocol extends DefaultProtocol { implicit val ModuleIdFormat: Format[ModuleId] = asProduct3(ModuleId)(ModuleId.unapply(_).get) implicit val ModuleFormat: Format[Module] = asProduct6(Module)(Module.unapply(_).get) implicit val ModuleGraphFormat: Format[ModuleGraph] = asProduct2(ModuleGraph.apply _)(ModuleGraph.unapply(_).get) + + // + implicit def sjsonNewAndShinyTransformAndTranspileAdapterFactoryModuleImplementation[T](implicit format: Format[T]): sjsonnew.JsonFormat[T] = + new sjsonnew.JsonFormat[T] { + // note, how this is simpler to write than to learn any sjonnew protocol syntax + def write[J](obj: T, builder: Builder[J]): Unit = { + val baos = new ByteArrayOutputStream() + format.writes(new JavaOutput(baos), obj) + val str = Base64.getEncoder.encodeToString(baos.toByteArray) + builder.writeString(str) + } + def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): T = { + val str = unbuilder.readString(jsOpt.get) + val bais = new ByteArrayInputStream(Base64.getDecoder.decode(str)) + format.reads(new JavaInput(bais)) + } + } } diff --git a/src/main/scala/sbt/SbtAccess.scala b/src/main/scala/sbt/SbtAccess.scala index a75a57c31..455e232e1 100644 --- a/src/main/scala/sbt/SbtAccess.scala +++ b/src/main/scala/sbt/SbtAccess.scala @@ -16,9 +16,18 @@ package sbt +import sbt.compat.SbtCompat._ +import librarymanagement._ +import internal._ +import librarymanagement._ +import Classpaths._ +import LibraryManagement._ +import internal.util.JLine + /** Accessors to private[sbt] symbols. */ object SbtAccess { val unmanagedScalaInstanceOnly = Defaults.unmanagedScalaInstanceOnly def getTerminalWidth: Int = JLine.usingTerminal(_.getWidth) + }