From 61fe6045198afe6186fe0661c8cc721d16a01be9 Mon Sep 17 00:00:00 2001 From: "Eugene Yokota (eed3si9n)" Date: Sat, 9 Aug 2025 14:15:27 -0400 Subject: [PATCH] refactor: Unify to dependency-tree **Problem** While sbt-dependency-graph is useful, not just for the basic ASCII graph, but for DOT file generation etc, it adds a large number of settings and tasks for combination of formats and actions to the point that we actually disable most of them by default. **Solution* I've had an idea for a while that dependencyTree can be implemented as a inputTask that accepts its own subcommands and options, and this implements that. For example, to open the browser that hosts a DOT file, now you can write dependencyTree dot --browse --- .github/workflows/ci.yml | 2 +- build.sbt | 14 - .../sbt/plugins/DependencyTreePlugin.scala | 30 -- main/src/main/scala/sbt/Defaults.scala | 10 - .../scala/sbt/internal/PluginDiscovery.scala | 2 +- .../internal/graph/rendering/DagreHTML.scala | 8 +- .../internal/graph/rendering/GraphML.scala | 38 +- .../internal/graph/rendering/TreeView.scala | 7 +- .../sbt/plugins/DependencyTreeKeys.scala | 71 +--- .../sbt/plugins/DependencyTreePlugin.scala | 42 +++ .../sbt/plugins/DependencyTreeSettings.scala | 328 +++++++++--------- .../plugins/MiniDependencyTreePlugin.scala | 28 -- .../sbt/plugins/DependencyTreeTest.scala | 43 +++ .../asciiGraphWidth/build.sbt | 11 +- .../asciiGraphWidth/project/plugins.sbt | 2 +- .../cachedResolution/build.sbt | 2 +- .../ignoreScalaLibrary/build.sbt | 12 +- .../ignoreScalaLibrary/{test => pending} | 0 .../{toFileSubTask => output}/build.sbt | 2 +- .../expected/licenses.txt | 0 .../expected/list.txt | 0 .../expected/stats.txt | 0 .../expected/tree.txt | 0 .../src/sbt-test/dependency-graph/output/test | 5 + .../showMissingUpdates/build.sbt | 2 +- .../testDotFileGeneration/build.sbt | 11 +- .../testDotFileGeneration/project/plugins.sbt | 1 - .../testHtmlFileGeneration/build.sbt | 9 +- .../project/plugins.sbt | 1 - .../toFileSubTask/project/plugins.sbt | 1 - .../dependency-graph/toFileSubTask/test | 5 - .../{test => pending} | 0 .../dependency-graph/whatDependsOn/build.sbt | 24 +- .../whatDependsOn/{test => pending} | 0 34 files changed, 354 insertions(+), 357 deletions(-) delete mode 100644 dependency-tree/src/main/scala/sbt/plugins/DependencyTreePlugin.scala create mode 100644 main/src/main/scala/sbt/plugins/DependencyTreePlugin.scala delete mode 100644 main/src/main/scala/sbt/plugins/MiniDependencyTreePlugin.scala create mode 100644 main/src/test/scala/sbt/plugins/DependencyTreeTest.scala rename sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/{test => pending} (100%) rename sbt-app/src/sbt-test/dependency-graph/{toFileSubTask => output}/build.sbt (90%) rename sbt-app/src/sbt-test/dependency-graph/{toFileSubTask => output}/expected/licenses.txt (100%) rename sbt-app/src/sbt-test/dependency-graph/{toFileSubTask => output}/expected/list.txt (100%) rename sbt-app/src/sbt-test/dependency-graph/{toFileSubTask => output}/expected/stats.txt (100%) rename sbt-app/src/sbt-test/dependency-graph/{toFileSubTask => output}/expected/tree.txt (100%) create mode 100644 sbt-app/src/sbt-test/dependency-graph/output/test delete mode 100644 sbt-app/src/sbt-test/dependency-graph/testDotFileGeneration/project/plugins.sbt delete mode 100644 sbt-app/src/sbt-test/dependency-graph/testHtmlFileGeneration/project/plugins.sbt delete mode 100644 sbt-app/src/sbt-test/dependency-graph/toFileSubTask/project/plugins.sbt delete mode 100644 sbt-app/src/sbt-test/dependency-graph/toFileSubTask/test rename sbt-app/src/sbt-test/dependency-graph/whatDependsOn-without-previous-initialization/{test => pending} (100%) rename sbt-app/src/sbt-test/dependency-graph/whatDependsOn/{test => pending} (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 52b00556a..41533b74a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -157,7 +157,7 @@ jobs: if: ${{ matrix.jobtype == 3 }} shell: bash run: | - # ./sbt -v "dependencyTreeProj/publishLocal; scripted dependency-graph/*" + ./sbt -v --client "scripted dependency-graph/*" ./sbt -v --client "scripted dependency-management/* project-load/* project-matrix/* java/* run/*" # ./sbt -v --client "scripted plugins/*" # ./sbt -v --client "scripted nio/*" diff --git a/build.sbt b/build.sbt index 16409bcae..6a2863aba 100644 --- a/build.sbt +++ b/build.sbt @@ -628,19 +628,6 @@ lazy val scriptedSbtProj = (project in file("scripted-sbt")) .dependsOn(lmCore) .configure(addSbtIO, addSbtCompilerInterface) -lazy val dependencyTreeProj = (project in file("dependency-tree")) - .dependsOn(sbtProj) - .settings( - sbtPlugin := true, - baseSettings, - name := "sbt-dependency-tree", - pluginCrossBuild / sbtVersion := version.value, - publishMavenStyle := true, - sbtPluginPublishLegacyMavenStyle := false, - // mimaSettings, - mimaPreviousArtifacts := Set.empty, - ) - lazy val remoteCacheProj = (project in file("sbt-remote-cache")) .dependsOn(sbtProj) .settings( @@ -1212,7 +1199,6 @@ def allProjects = stdTaskProj, runProj, scriptedSbtProj, - dependencyTreeProj, protocolProj, actionsProj, commandProj, diff --git a/dependency-tree/src/main/scala/sbt/plugins/DependencyTreePlugin.scala b/dependency-tree/src/main/scala/sbt/plugins/DependencyTreePlugin.scala deleted file mode 100644 index 2bbe5409e..000000000 --- a/dependency-tree/src/main/scala/sbt/plugins/DependencyTreePlugin.scala +++ /dev/null @@ -1,30 +0,0 @@ -/* - * sbt - * Copyright 2023, Scala center - * Copyright 2011 - 2022, Lightbend, Inc. - * Copyright 2008 - 2010, Mark Harrah - * Licensed under Apache License 2.0 (see LICENSE) - */ - -package sbt -package plugins - -import scala.annotation.nowarn - -object DependencyTreePlugin extends AutoPlugin { - object autoImport extends DependencyTreeKeys - override def trigger = AllRequirements - override def requires = MiniDependencyTreePlugin - - @nowarn - val configurations = Vector(Compile, Test, Runtime, Provided, Optional) - - // MiniDependencyTreePlugin provides baseBasicReportingSettings for Compile and Test - override lazy val projectSettings: Seq[Def.Setting[?]] = - configurations.diff(Vector(Compile, Test)).flatMap { config => - inConfig(config)(DependencyTreeSettings.baseBasicReportingSettings) - } ++ - configurations.flatMap { config => - inConfig(config)(DependencyTreeSettings.baseFullReportingSettings) - } -} diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index a23e8ff26..6a6cff3a8 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -4442,16 +4442,6 @@ trait BuildExtra extends BuildCommon with DefExtra { Seq(compose(onLoad, add), compose(onUnload, remove)) } - /** - * Adds Dependency tree plugin. - */ - def addDependencyTreePlugin: Setting[Seq[ModuleID]] = - libraryDependencies += sbtPluginExtra( - ModuleID("org.scala-sbt", "sbt-dependency-tree", sbtVersion.value), - sbtBinaryVersion.value, - scalaBinaryVersion.value - ) - /** * Adds Maven resolver plugin. */ diff --git a/main/src/main/scala/sbt/internal/PluginDiscovery.scala b/main/src/main/scala/sbt/internal/PluginDiscovery.scala index 65f6a81d0..934a0a53d 100644 --- a/main/src/main/scala/sbt/internal/PluginDiscovery.scala +++ b/main/src/main/scala/sbt/internal/PluginDiscovery.scala @@ -53,7 +53,7 @@ object PluginDiscovery: "sbt.plugins.SemanticdbPlugin" -> sbt.plugins.SemanticdbPlugin, "sbt.plugins.JUnitXmlReportPlugin" -> sbt.plugins.JUnitXmlReportPlugin, "sbt.plugins.Giter8TemplatePlugin" -> sbt.plugins.Giter8TemplatePlugin, - "sbt.plugins.MiniDependencyTreePlugin" -> sbt.plugins.MiniDependencyTreePlugin, + "sbt.plugins.DependencyTreePlugin" -> sbt.plugins.DependencyTreePlugin, ) val detectedAutoPlugins = discover[AutoPlugin](AutoPlugins) val allAutoPlugins = (defaultAutoPlugins ++ detectedAutoPlugins.modules) map { (name, value) => diff --git a/main/src/main/scala/sbt/internal/graph/rendering/DagreHTML.scala b/main/src/main/scala/sbt/internal/graph/rendering/DagreHTML.scala index d8247cff3..696788aa0 100644 --- a/main/src/main/scala/sbt/internal/graph/rendering/DagreHTML.scala +++ b/main/src/main/scala/sbt/internal/graph/rendering/DagreHTML.scala @@ -18,6 +18,11 @@ import sbt.io.IO object DagreHTML { def createLink(dotGraph: String, targetDirectory: File): URI = { + val graphHTML = createFile(dotGraph, targetDirectory) + new URI(graphHTML.toURI.toString) + } + + def createFile(dotGraph: String, targetDirectory: File): File = { targetDirectory.mkdirs() val graphHTML = new File(targetDirectory, "graph.html") TreeView.saveResource("graph.html", graphHTML) @@ -33,7 +38,6 @@ object DagreHTML { s"""data = "$graphString";""", IO.utf8 ) - - new URI(graphHTML.toURI.toString) + graphHTML } } diff --git a/main/src/main/scala/sbt/internal/graph/rendering/GraphML.scala b/main/src/main/scala/sbt/internal/graph/rendering/GraphML.scala index 7415b90ea..e12af5d40 100644 --- a/main/src/main/scala/sbt/internal/graph/rendering/GraphML.scala +++ b/main/src/main/scala/sbt/internal/graph/rendering/GraphML.scala @@ -11,23 +11,25 @@ package internal package graph package rendering -import scala.xml.XML +import java.io.StringWriter +import scala.xml.{ Elem, XML } -object GraphML { - def saveAsGraphML(graph: ModuleGraph, outputFile: String): Unit = { +object GraphML: + def graphML(graph: ModuleGraph): Elem = val nodesXml = - for (n <- graph.nodes) - yield - - {n.id.idString} - - + for n <- graph.nodes + yield + + + {n.id.idString} + + val edgesXml = - for (e <- graph.edges) - yield + for e <- graph.edges + yield - val xml = + val r = @@ -35,7 +37,13 @@ object GraphML { {edgesXml} + r - XML.save(outputFile, xml) - } -} + def graphMLAsString(graph: ModuleGraph): String = + val w = StringWriter() + XML.write(w, graphML(graph), "UTF-8", false, None.orNull) + w.toString() + + def saveAsGraphML(graph: ModuleGraph, outputFile: String): Unit = + XML.save(outputFile, graphML(graph)) +end GraphML diff --git a/main/src/main/scala/sbt/internal/graph/rendering/TreeView.scala b/main/src/main/scala/sbt/internal/graph/rendering/TreeView.scala index 5c19f2381..36b2b6d80 100644 --- a/main/src/main/scala/sbt/internal/graph/rendering/TreeView.scala +++ b/main/src/main/scala/sbt/internal/graph/rendering/TreeView.scala @@ -28,12 +28,17 @@ object TreeView { } def createLink(graphJson: String, targetDirectory: File): URI = { + val graphHTML = createFile(graphJson, targetDirectory) + new URI(graphHTML.toURI.toString) + } + + def createFile(graphJson: String, targetDirectory: File): File = { targetDirectory.mkdirs() val graphHTML = new File(targetDirectory, "tree.html") saveResource("tree.html", graphHTML) IO.write(new File(targetDirectory, "tree.json"), graphJson, IO.utf8) IO.write(new File(targetDirectory, "tree.data.js"), s"tree_data = $graphJson;", IO.utf8) - new URI(graphHTML.toURI.toString) + graphHTML } private[rendering] def processSubtree( diff --git a/main/src/main/scala/sbt/plugins/DependencyTreeKeys.scala b/main/src/main/scala/sbt/plugins/DependencyTreeKeys.scala index db03b79b1..68b73539e 100644 --- a/main/src/main/scala/sbt/plugins/DependencyTreeKeys.scala +++ b/main/src/main/scala/sbt/plugins/DependencyTreeKeys.scala @@ -9,39 +9,15 @@ package sbt package plugins -import java.io.File -import java.net.URI import sbt.internal.graph.* import sbt.Def.* import sbt.librarymanagement.{ ModuleID, UpdateReport } -trait MiniDependencyTreeKeys { +abstract class DependencyTreeKeys: + val dependencyTree = inputKey[String]("Displays dependencies in ascii tree and other formats") val dependencyTreeIncludeScalaLibrary = settingKey[Boolean]( "Specifies if scala dependency should be included in dependencyTree output" ) - val dependencyTree = taskKey[Unit]("Prints an ascii tree of all the dependencies to the console") - val asString = taskKey[String]("Provides the string value for the task it is scoped for") - // val printToConsole = TaskKey[Unit]("printToConsole", "Prints the tasks value to the console") - val toFile = inputKey[File]("Writes the task value to the given file") - - // internal - private[sbt] val dependencyTreeIgnoreMissingUpdate = - taskKey[UpdateReport]("update used for dependencyTree task") - private[sbt] val dependencyTreeModuleGraphStore = - taskKey[ModuleGraph]("The stored module-graph from the last run") - val whatDependsOn = inputKey[String]("Shows information about what depends on the given module") - private[sbt] val dependencyTreeCrossProjectId = settingKey[ModuleID]("") -} - -object MiniDependencyTreeKeys extends MiniDependencyTreeKeys - -abstract class DependencyTreeKeys { - val dependencyGraphMLFile = - settingKey[File]("The location the graphml file should be generated at") - val dependencyGraphML = - taskKey[File]("Creates a graphml file containing the dependency-graph for a project") - val dependencyDotFile = - settingKey[File]("The location the dot file should be generated at") val dependencyDotNodeColors = settingKey[Boolean]( "The boxes of nodes are painted with colors. Otherwise they're black." ) @@ -51,41 +27,18 @@ abstract class DependencyTreeKeys { val dependencyDotHeader = settingKey[String]( "The header of the dot file. (e.g. to set your preferred node shapes)" ) - val dependencyDot = taskKey[File]( - "Creates a dot file containing the dependency-graph for a project" - ) - val dependencyDotString = taskKey[String]( - "Creates a String containing the dependency-graph for a project in dot format" - ) - val dependencyBrowseGraphTarget = settingKey[File]( - "The location dependency browse graph files should be put." - ) - val dependencyBrowseGraphHTML = taskKey[URI]( - "Creates an HTML page that can be used to view the graph." - ) - val dependencyBrowseGraph = taskKey[URI]( - "Opens an HTML page that can be used to view the graph." - ) - val dependencyBrowseTreeTarget = settingKey[File]( - "The location dependency browse tree files should be put." - ) - val dependencyBrowseTreeHTML = taskKey[URI]( - "Creates an HTML page that can be used to view the dependency tree" - ) - val dependencyBrowseTree = taskKey[URI]( - "Opens an HTML page that can be used to view the dependency tree" - ) + + // internal + private[sbt] val dependencyTreeIgnoreMissingUpdate = + taskKey[UpdateReport]("update used for dependencyTree task") + private[sbt] val dependencyTreeModuleGraphStore = + taskKey[ModuleGraph]("The stored module-graph from the last run") + val whatDependsOn = inputKey[String]("Shows information about what depends on the given module") + private[sbt] val dependencyTreeCrossProjectId = settingKey[ModuleID]("") + // 0 was added to avoid conflict with sbt-dependency-tree private[sbt] val dependencyTreeModuleGraph0 = taskKey[ModuleGraph]("The dependency graph for a project") - - val dependencyList = - taskKey[Unit]("Prints a list of all dependencies to the console") - val dependencyStats = - taskKey[Unit]("Prints statistics for all dependencies to the console") - val dependencyLicenseInfo = taskKey[Unit]( - "Aggregates and shows information about the licenses of dependencies" - ) -} +end DependencyTreeKeys object DependencyTreeKeys extends DependencyTreeKeys diff --git a/main/src/main/scala/sbt/plugins/DependencyTreePlugin.scala b/main/src/main/scala/sbt/plugins/DependencyTreePlugin.scala new file mode 100644 index 000000000..7f61089d6 --- /dev/null +++ b/main/src/main/scala/sbt/plugins/DependencyTreePlugin.scala @@ -0,0 +1,42 @@ +/* + * sbt + * Copyright 2023, Scala center + * Copyright 2011 - 2022, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt +package plugins + +import sbt.PluginTrigger.AllRequirements +import sbt.ProjectExtra.* +import sbt.librarymanagement.Configurations.{ Compile, Test } + +object DependencyTreePlugin extends AutoPlugin { + object autoImport extends DependencyTreeKeys + + private val defaultDependencyDotHeader = + """|digraph "dependency-graph" { + | graph[rankdir="LR"; splines=polyline] + | edge [ + | arrowtail="none" + | ]""".stripMargin + + private val defaultDependencyDotNodeLabel = + (organization: String, name: String, version: String) => + s"""${organization}
${name}
${version}""" + + import autoImport.* + override def trigger: PluginTrigger = AllRequirements + override def globalSettings: Seq[Def.Setting[?]] = Seq( + dependencyTreeIncludeScalaLibrary :== false, + dependencyDotNodeColors :== true, + dependencyDotHeader := defaultDependencyDotHeader, + dependencyDotNodeLabel := defaultDependencyDotNodeLabel, + ) + override lazy val projectSettings: Seq[Def.Setting[?]] = + DependencyTreeSettings.coreSettings ++ + inConfig(Compile)(DependencyTreeSettings.baseSettings) ++ + inConfig(Test)(DependencyTreeSettings.baseSettings) +} diff --git a/main/src/main/scala/sbt/plugins/DependencyTreeSettings.scala b/main/src/main/scala/sbt/plugins/DependencyTreeSettings.scala index a05956ac7..1ccd8fa42 100644 --- a/main/src/main/scala/sbt/plugins/DependencyTreeSettings.scala +++ b/main/src/main/scala/sbt/plugins/DependencyTreeSettings.scala @@ -10,6 +10,7 @@ package sbt package plugins import java.io.File +import java.util.Locale import sbt.Def.* import sbt.Keys.* @@ -19,15 +20,88 @@ import sbt.internal.graph.backend.SbtUpdateReport import sbt.internal.graph.rendering.{ DagreHTML, TreeView } import sbt.internal.librarymanagement.* import sbt.internal.util.complete.{ Parser, Parsers } +import sbt.internal.util.complete.DefaultParsers.* import sbt.io.IO import sbt.io.syntax.* import sbt.librarymanagement.* +import sbt.util.Logger +import scala.Console -object DependencyTreeSettings { +private[sbt] object DependencyTreeSettings: import sjsonnew.BasicJsonProtocol.* - import MiniDependencyTreeKeys.* import DependencyTreeKeys.* + enum Arg: + case Help + case Quiet + case Format(format: Fmt) + case Out(out: String) + case Browse + + enum Fmt: + case Tree + case List + case Stats + case Json + case Graph + case HtmlGraph + case Html + case Xml + + // Parser for the supported formats + lazy val FmtParser: Parser[Fmt] = + (("tree" ^^^ Fmt.Tree) + | ("list" ^^^ Fmt.List) + | ("stats" ^^^ Fmt.Stats) + | ("json" ^^^ Fmt.Json) + | ("dot" ^^^ Fmt.Graph) + | ("graph" ^^^ Fmt.Graph) + | ("html-graph" ^^^ Fmt.HtmlGraph) + | ("html" ^^^ Fmt.Html) + | ("xml" ^^^ Fmt.Xml)) + + lazy val ArgParser: Parser[Arg] = + Space ~> (("help" ^^^ Arg.Help) + | ("--help" ^^^ Arg.Help) + | FmtParser.map(fmt => Arg.Format(fmt))) + + lazy val ArgOptionParser: Parser[Arg] = + Space ~> (("--quiet" ^^^ Arg.Quiet) + | ("--browse" ^^^ Arg.Browse) + | ("--out" ~> Space ~> StringBasic) + .map(Arg.Out(_)) + .examples("--out /tmp/deps.txt")) + + // You can have zero-or-one format and options afterwards + lazy val ArgsParser: Parser[Seq[Arg]] = + (ArgParser.? ~ ArgOptionParser.*).map: + case (a, opts) => a.toList ::: opts.toList + + def usageText: String = + s"""dependencyTree task displays the dependency graph. + +USAGE + dependencyTree [subcommand] [options] + +SUBCOMMAND + tree Prints ascii tree (default) + list Prints list of all dependencies + graph Prints GraphViz DOT file + dot Same as graph + html Creates HTML page + html-graph Creates HTML page with GraphViz DOT file + json Prints JSON + xml Prints GraphML + stats Prints statistics for all dependencies + help Prints this help + +OPTIONS + --quiet Returns the output as task value, replacing asString + --out Writes the output to the specified file; + The file extension will influence the default subcommand + --browse Opens the browser when combined with graph or html subcommand +""" + /** * Core settings needed for any graphing tasks. */ @@ -59,10 +133,10 @@ object DependencyTreeSettings { ) /** - * MiniDependencyTreePlugin includes these settings for Compile and Test scopes + * DependencyTreePlugin includes these settings for Compile and Test scopes * to provide dependencyTree task. */ - lazy val baseBasicReportingSettings: Seq[Def.Setting[?]] = + lazy val baseSettings: Seq[Def.Setting[?]] = Seq( dependencyTreeCrossProjectId := CrossVersion(scalaVersion.value, scalaBinaryVersion.value)( projectID.value @@ -82,57 +156,81 @@ object DependencyTreeSettings { .storeAs(dependencyTreeModuleGraphStore) .triggeredBy(dependencyTreeModuleGraph0) .value, - ) ++ { - renderingTaskSettings(dependencyTree) :+ { - dependencyTree / asString := { - rendering.AsciiTree.asciiTree(dependencyTreeModuleGraph0.value, asciiGraphWidth.value) - } - } - } - - /** - * This is the maximum strength settings for DependencyTreePlugin. - */ - lazy val baseFullReportingSettings: Seq[Def.Setting[?]] = - Seq( - // browse - dependencyBrowseGraphTarget := { target.value / "browse-dependency-graph" }, - dependencyBrowseGraphHTML := browseGraphHTMLTask.value, - dependencyBrowseGraph := openBrowser(dependencyBrowseGraphHTML).value, - dependencyBrowseTreeTarget := { target.value / "browse-dependency-tree" }, - dependencyBrowseTreeHTML := browseTreeHTMLTask.value, - dependencyBrowseTree := openBrowser(dependencyBrowseTreeHTML).value, - // dot support - dependencyDotFile := { - val config = configuration.value - target.value / s"dependencies-${config.toString}.dot" - }, - dependencyDot / asString := Def.uncached( - rendering.DOT.dotGraph( - dependencyTreeModuleGraph0.value, - dependencyDotHeader.value, - dependencyDotNodeLabel.value, - rendering.DOT.HTMLLabelRendering.AngleBrackets, - dependencyDotNodeColors.value - ) - ), - dependencyDot := writeToFile(dependencyDot / asString, dependencyDotFile).value, - dependencyDotHeader := - """|digraph "dependency-graph" { - | graph[rankdir="LR"; splines=polyline] - | edge [ - | arrowtail="none" - | ]""".stripMargin, - dependencyDotNodeColors := true, - dependencyDotNodeLabel := { (organization: String, name: String, version: String) => - s"""${organization}
${name}
${version}""" - }, - // GraphML support - dependencyGraphMLFile := { - val config = configuration.value - target.value / s"dependencies-${config.toString}.graphml" - }, - dependencyGraphML := dependencyGraphMLTask.value, + dependencyTree := (Def.inputTaskDyn { + val s = streams.value + val args = ArgsParser.parsed.toList + val isHelp = args.contains(Arg.Help) + val isQuiet = args.contains(Arg.Quiet) + val isBrowse = args.contains(Arg.Browse) + if isHelp then Def.task { s.log.info(usageText); "" } + else + val formatOpt = (args + .collect { case Arg.Format(fmt) => fmt }) + .reverse + .headOption + val outFileNameOpt = (args + .collect { case Arg.Out(out) => out }) + .reverse + .headOption + val outFileOpt = outFileNameOpt.map(new File(_)) + val format = (formatOpt, outFileNameOpt) match + case (None, Some(out)) if out.endsWith(".dot") => Fmt.Graph + case (None, Some(out)) if out.endsWith(".html") => Fmt.Html + case (None, Some(out)) if out.endsWith(".xml") => Fmt.Xml + case (None, Some(out)) if out.endsWith(".json") => Fmt.Json + case (Some(Fmt.Graph), Some(out)) if out.endsWith(".html") => Fmt.HtmlGraph + case (Some(Fmt.Graph), _) if isBrowse => Fmt.HtmlGraph + case (Some(fmt), _) => fmt + case _ => Fmt.Tree + val config = configuration.value.name + val targetDir = target.value / config / format.toString.toLowerCase(Locale.ENGLISH) + format match + case Fmt.Tree | Fmt.List | Fmt.Stats => + Def.task { + val graph = dependencyTreeModuleGraph0.value + val output = format match + case Fmt.List => rendering.FlatList.render(_.id.idString)(graph) + case Fmt.Stats => rendering.Statistics.renderModuleStatsList(graph) + case _ => rendering.AsciiTree.asciiTree(graph, asciiGraphWidth.value) + handleOutput(output, outFileOpt, isQuiet, s.log) + } + case Fmt.Json => + Def.task { + val graph = dependencyTreeModuleGraph0.value + val output = TreeView.createJson(graph) + handleOutput(output, outFileOpt, isQuiet, s.log) + } + case Fmt.Xml => + Def.task { + val graph = dependencyTreeModuleGraph0.value + val output = rendering.GraphML.graphMLAsString(graph) + handleOutput(output, outFileOpt, isQuiet, s.log) + } + case Fmt.Html => + Def.task { + val graph = dependencyTreeModuleGraph0.value + val renderedTree = TreeView.createJson(graph) + val outputFile = TreeView.createFile(renderedTree, targetDir) + if isBrowse then openBrowser(outputFile.toURI) + outputFile.getAbsolutePath + } + case Fmt.Graph | Fmt.HtmlGraph => + Def.task { + val graph = dependencyTreeModuleGraph0.value + val output = rendering.DOT.dotGraph( + graph, + dependencyDotHeader.value, + dependencyDotNodeLabel.value, + rendering.DOT.HTMLLabelRendering.AngleBrackets, + dependencyDotNodeColors.value + ) + if format == Fmt.Graph then handleOutput(output, outFileOpt, isQuiet, s.log) + else + val outputFile = DagreHTML.createFile(output, targetDir) + if isBrowse then openBrowser(outputFile.toURI) + outputFile.getAbsolutePath + } + }).evaluated, whatDependsOn := { val ArtifactPattern(org, name, versionFilter) = artifactPatternParser.parsed val graph = dependencyTreeModuleGraph0.value @@ -155,112 +253,32 @@ object DependencyTreeSettings { } output }, - ) ++ - renderingAlternatives.flatMap { (key, renderer) => renderingTaskSettings(key, renderer) } - - def renderingAlternatives: Seq[(TaskKey[Unit], ModuleGraph => String)] = - Seq( - dependencyList -> rendering.FlatList.render(_.id.idString), - dependencyStats -> rendering.Statistics.renderModuleStatsList, - dependencyLicenseInfo -> rendering.LicenseInfo.render ) - def renderingTaskSettings(key: TaskKey[Unit], renderer: ModuleGraph => String): Seq[Setting[?]] = - renderingTaskSettings(key) :+ { - key / asString := renderer(dependencyTreeModuleGraph0.value) - } + private def handleOutput( + content: String, + outputFileOpt: Option[File], + isQuiet: Boolean, + log: Logger, + ): String = + outputFileOpt match + case Some(output) => + IO.write(output, content, IO.utf8) + if !isQuiet then log.info(s"wrote dependencies to $output") + output.toString + case None => + if isQuiet then content + else + Console.out.println(content); "" - def renderingTaskSettings(key: TaskKey[Unit]): Seq[Setting[?]] = - Seq( - key := { - val s = streams.value - val str = (key / asString).value - synchronized { - s.log.info(str) - } - }, - (key / toFile) := { - val (targetFile, force) = targetFileAndForceParser.parsed - writeToFile(key.key.label, (key / asString).value, targetFile, force, streams.value) - }, - ) - - def dependencyGraphMLTask = - Def.task { - val resultFile = dependencyGraphMLFile.value - val graph = dependencyTreeModuleGraph0.value - rendering.GraphML.saveAsGraphML(graph, resultFile.getAbsolutePath) - streams.value.log.info(s"Wrote dependency graph to '${resultFile}'") - resultFile - } - - def browseGraphHTMLTask = - Def.task { - val graph = dependencyTreeModuleGraph0.value - val dotGraph = rendering.DOT.dotGraph( - graph, - dependencyDotHeader.value, - dependencyDotNodeLabel.value, - rendering.DOT.HTMLLabelRendering.AngleBrackets, - dependencyDotNodeColors.value - ) - val link = DagreHTML.createLink(dotGraph, dependencyBrowseGraphTarget.value) - streams.value.log.info(s"HTML graph written to $link") - link - } - - def browseTreeHTMLTask = - Def.task { - val graph = dependencyTreeModuleGraph0.value - val renderedTree = TreeView.createJson(graph) - val link = TreeView.createLink(renderedTree, dependencyBrowseTreeTarget.value) - streams.value.log.info(s"HTML tree written to $link") - link - } - - def writeToFile(dataTask: TaskKey[String], fileTask: SettingKey[File]) = - Def.task { - val outFile = fileTask.value - IO.write(outFile, dataTask.value, IO.utf8) - - streams.value.log.info(s"Wrote dependency graph to '${outFile}'") - outFile - } - - def writeToFile( - what: String, - data: String, - targetFile: File, - force: Boolean, - streams: TaskStreams - ): File = - if (targetFile.exists && !force) - throw new RuntimeException( - s"Target file for $what already exists at ${targetFile.getAbsolutePath}. Use '-f' to override" - ) - else { - IO.write(targetFile, data, IO.utf8) - - streams.log.info(s"Wrote $what to '$targetFile'") - targetFile - } - - def absoluteReportPath = (file: File) => file.getAbsolutePath - - def openBrowser(uriKey: TaskKey[URI]) = - Def.task { - val uri = uriKey.value - streams.value.log.info(s"Opening ${uri} in browser...") - val desktop = java.awt.Desktop.getDesktop - desktop.synchronized { - desktop.browse(uri) - } - uri + def openBrowser(uri: URI): Unit = + val desktop = java.awt.Desktop.getDesktop + desktop.synchronized { + desktop.browse(uri) } case class ArtifactPattern(organization: String, name: String, version: Option[String]) - import sbt.internal.util.complete.DefaultParsers.* val artifactPatternParser: Def.Initialize[State => Parser[ArtifactPattern]] = Keys.resolvedScoped { ctx => (state: State) => val graph = @@ -320,4 +338,4 @@ object DependencyTreeSettings { case _ => None } } -} +end DependencyTreeSettings diff --git a/main/src/main/scala/sbt/plugins/MiniDependencyTreePlugin.scala b/main/src/main/scala/sbt/plugins/MiniDependencyTreePlugin.scala deleted file mode 100644 index b0896f8f0..000000000 --- a/main/src/main/scala/sbt/plugins/MiniDependencyTreePlugin.scala +++ /dev/null @@ -1,28 +0,0 @@ -/* - * sbt - * Copyright 2023, Scala center - * Copyright 2011 - 2022, Lightbend, Inc. - * Copyright 2008 - 2010, Mark Harrah - * Licensed under Apache License 2.0 (see LICENSE) - */ - -package sbt -package plugins - -import sbt.PluginTrigger.AllRequirements -import sbt.ProjectExtra.* -import sbt.librarymanagement.Configurations.{ Compile, Test } - -object MiniDependencyTreePlugin extends AutoPlugin { - object autoImport extends MiniDependencyTreeKeys - - import autoImport.* - override def trigger: PluginTrigger = AllRequirements - override def globalSettings: Seq[Def.Setting[?]] = Seq( - dependencyTreeIncludeScalaLibrary := false - ) - override lazy val projectSettings: Seq[Def.Setting[?]] = - DependencyTreeSettings.coreSettings ++ - inConfig(Compile)(DependencyTreeSettings.baseBasicReportingSettings) ++ - inConfig(Test)(DependencyTreeSettings.baseBasicReportingSettings) -} diff --git a/main/src/test/scala/sbt/plugins/DependencyTreeTest.scala b/main/src/test/scala/sbt/plugins/DependencyTreeTest.scala new file mode 100644 index 000000000..89942aecf --- /dev/null +++ b/main/src/test/scala/sbt/plugins/DependencyTreeTest.scala @@ -0,0 +1,43 @@ +/* + * sbt + * Copyright 2023, Scala center + * Copyright 2011 - 2022, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt +package plugins + +import sbt.internal.util.complete.Parser +import DependencyTreeSettings.{ Arg, ArgsParser, Fmt, FmtParser } + +object DependencyTreeTest extends verify.BasicTestSuite: + test("Parse args") { + assert(parseArgs(List("help")) == List(Arg.Help)) + assert(parseArgs(List("--help")) == List(Arg.Help)) + assert(parseArgs(List("--quiet")) == List(Arg.Quiet)) + assert(parseArgs(List("tree")) == List(Arg.Format(Fmt.Tree))) + assert(parseArgs(List("--out", "/tmp/deps.txt")) == List(Arg.Out("/tmp/deps.txt"))) + assert(parseArgs(List("--browse")) == List(Arg.Browse)) + } + + test("Parse format") { + assert(parseFormat("tree") == Fmt.Tree) + assert(parseFormat("list") == Fmt.List) + assert(parseFormat("stats") == Fmt.Stats) + assert(parseFormat("json") == Fmt.Json) + assert(parseFormat("html") == Fmt.Html) + assert(parseFormat("graph") == Fmt.Graph) + } + + def parseArgs(args: List[String]): Seq[Arg] = + Parser.parse(" " + args.mkString(" "), ArgsParser) match + case Right(args) => args + case Left(err) => sys.error(err) + + def parseFormat(fmt: String): Fmt = + Parser.parse(fmt, FmtParser) match + case Right(x) => x + case Left(err) => sys.error(err) +end DependencyTreeTest diff --git a/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/build.sbt b/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/build.sbt index 167083797..4db34c065 100644 --- a/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/build.sbt +++ b/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/build.sbt @@ -5,11 +5,13 @@ name := "asciiGraphWidthSpecs" lazy val whenIsDefault = (project in file("when-is-default")) .settings( + name := "whenisdefault", libraryDependencies += "org.typelevel" %% "cats-effect" % "3.1.0", check := checkTask.value ) lazy val whenIs20 = (project in file("when-is-20")) .settings( + name := "whenis20", asciiGraphWidth := 20, libraryDependencies += "org.typelevel" %% "cats-effect" % "3.1.0", check := checkTask.value @@ -19,6 +21,11 @@ lazy val check = taskKey[Unit]("check") lazy val checkTask = Def.task { val context = thisProject.value val expected = IO.read(file(s"${context.base}/expected.txt")) - val actual = (Compile / dependencyTree / asString).value - require(actual == expected, s"${context.id} is failed.") + val actual = (Compile / dependencyTree).toTask(" --quiet").value + require(actual == expected, s"""${context.id} failed + +actual: $actual + +expected: $expected +""") } diff --git a/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/project/plugins.sbt b/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/project/plugins.sbt index 93c66d2a9..96b6c74ec 100644 --- a/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/project/plugins.sbt +++ b/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/project/plugins.sbt @@ -1 +1 @@ -addDependencyTreePlugin +// addDependencyTreePlugin diff --git a/sbt-app/src/sbt-test/dependency-graph/cachedResolution/build.sbt b/sbt-app/src/sbt-test/dependency-graph/cachedResolution/build.sbt index 4ecda4a16..66d255bf6 100644 --- a/sbt-app/src/sbt-test/dependency-graph/cachedResolution/build.sbt +++ b/sbt-app/src/sbt-test/dependency-graph/cachedResolution/build.sbt @@ -5,7 +5,7 @@ updateOptions := updateOptions.value.withCachedResolution(true) TaskKey[Unit]("check") := { val report = (Test / updateFull).value - val graph = (Test / dependencyTree / asString).value + val graph = (Test / dependencyTree).toTask(" --quiet").value def sanitize(str: String): String = str.split('\n').drop(1).mkString("\n") val expectedGraph = diff --git a/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/build.sbt b/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/build.sbt index c05b42e71..4cbc0cb4a 100644 --- a/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/build.sbt +++ b/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/build.sbt @@ -1,5 +1,6 @@ ThisBuild / scalaVersion := "2.12.20" +name := "foo" libraryDependencies ++= Seq( "org.slf4j" % "slf4j-api" % "1.7.2", "ch.qos.logback" % "logback-classic" % "1.0.7" @@ -8,8 +9,11 @@ csrMavenDependencyOverride := false TaskKey[Unit]("check") := { val report = updateFull.value - val graph = (Test / dependencyTree / asString).value - def sanitize(str: String): String = str.split('\n').drop(1).map(_.trim).mkString("\n") + val graph = (Test / dependencyTree).toTask(" --quiet").value + def sanitize(str: String): String = str.linesIterator.toList + .drop(1) + .map(_.trim) + .mkString("\n") /* Started to return: @@ -31,8 +35,8 @@ default:sbt_8ae1da13_2.12:0.1.0-SNAPSHOT [S] | | | +-org.slf4j:slf4j-api:1.7.2 | """.stripMargin - IO.writeLines(file("/tmp/blib"), sanitize(graph).split("\n")) - IO.writeLines(file("/tmp/blub"), sanitize(expectedGraph).split("\n")) + // IO.writeLines(file("/tmp/blib"), sanitize(graph).split("\n")) + // IO.writeLines(file("/tmp/blub"), sanitize(expectedGraph).split("\n")) require(sanitize(graph) == sanitize(expectedGraph), "Graph for report %s was '\n%s' but should have been '\n%s'" format (report, sanitize(graph), sanitize(expectedGraph))) () } diff --git a/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/test b/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/pending similarity index 100% rename from sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/test rename to sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/pending diff --git a/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/build.sbt b/sbt-app/src/sbt-test/dependency-graph/output/build.sbt similarity index 90% rename from sbt-app/src/sbt-test/dependency-graph/toFileSubTask/build.sbt rename to sbt-app/src/sbt-test/dependency-graph/output/build.sbt index 7d19cacd5..ad998d58c 100644 --- a/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/build.sbt +++ b/sbt-app/src/sbt-test/dependency-graph/output/build.sbt @@ -5,7 +5,7 @@ version := "0.1" name := "blubber" libraryDependencies += "org.typelevel" %% "cats-effect" % "2.2.0" TaskKey[Unit]("check") := { - val candidates = "tree list stats licenses".split(' ').map(_.trim) + val candidates = "tree list stats".split(' ').map(_.trim) candidates.foreach { c => val expected = new File(s"expected/$c.txt") val actual = new File(s"target/$c.txt") diff --git a/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/expected/licenses.txt b/sbt-app/src/sbt-test/dependency-graph/output/expected/licenses.txt similarity index 100% rename from sbt-app/src/sbt-test/dependency-graph/toFileSubTask/expected/licenses.txt rename to sbt-app/src/sbt-test/dependency-graph/output/expected/licenses.txt diff --git a/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/expected/list.txt b/sbt-app/src/sbt-test/dependency-graph/output/expected/list.txt similarity index 100% rename from sbt-app/src/sbt-test/dependency-graph/toFileSubTask/expected/list.txt rename to sbt-app/src/sbt-test/dependency-graph/output/expected/list.txt diff --git a/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/expected/stats.txt b/sbt-app/src/sbt-test/dependency-graph/output/expected/stats.txt similarity index 100% rename from sbt-app/src/sbt-test/dependency-graph/toFileSubTask/expected/stats.txt rename to sbt-app/src/sbt-test/dependency-graph/output/expected/stats.txt diff --git a/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/expected/tree.txt b/sbt-app/src/sbt-test/dependency-graph/output/expected/tree.txt similarity index 100% rename from sbt-app/src/sbt-test/dependency-graph/toFileSubTask/expected/tree.txt rename to sbt-app/src/sbt-test/dependency-graph/output/expected/tree.txt diff --git a/sbt-app/src/sbt-test/dependency-graph/output/test b/sbt-app/src/sbt-test/dependency-graph/output/test new file mode 100644 index 000000000..b3b437082 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-graph/output/test @@ -0,0 +1,5 @@ +> dependencyTree --out target/tree.txt +> dependencyTree list --out target/list.txt +> dependencyTree stats --out target/stats.txt +# > dependencyLicenseInfo/toFile target/licenses.txt +> check diff --git a/sbt-app/src/sbt-test/dependency-graph/showMissingUpdates/build.sbt b/sbt-app/src/sbt-test/dependency-graph/showMissingUpdates/build.sbt index 192ecb0a9..04b2d69d2 100644 --- a/sbt-app/src/sbt-test/dependency-graph/showMissingUpdates/build.sbt +++ b/sbt-app/src/sbt-test/dependency-graph/showMissingUpdates/build.sbt @@ -5,7 +5,7 @@ libraryDependencies += TaskKey[Unit]("check") := { val report = updateFull.value - val graph = (Test / dependencyTree / asString).value + val graph = (Test / dependencyTree).toTask(" --quiet").value def sanitize(str: String): String = str.split('\n').drop(1).mkString("\n") val expectedGraph = diff --git a/sbt-app/src/sbt-test/dependency-graph/testDotFileGeneration/build.sbt b/sbt-app/src/sbt-test/dependency-graph/testDotFileGeneration/build.sbt index 2dee27b36..2aadd1317 100644 --- a/sbt-app/src/sbt-test/dependency-graph/testDotFileGeneration/build.sbt +++ b/sbt-app/src/sbt-test/dependency-graph/testDotFileGeneration/build.sbt @@ -14,7 +14,7 @@ lazy val test_project = project .dependsOn(justADependencyProject, justATransitiveDependencyProject) .settings( TaskKey[Unit]("check") := { - val dotFile = (dependencyDot in Compile).value + val graph = (Compile / dependencyTree).toTask(" dot --quiet").value val expectedGraph = """digraph "dependency-graph" { | graph[rankdir="LR"; splines=polyline] @@ -23,7 +23,7 @@ lazy val test_project = project | ] | "justadependencyproject:justadependencyproject_2.9.2:0.1-SNAPSHOT"[shape=box label=justadependencyproject_2.9.2
0.1-SNAPSHOT> style="" penwidth="5" color="#B6E316"] | "justatransitivedependencyproject:justatransitivedependencyproject_2.9.2:0.1-SNAPSHOT"[shape=box label=justatransitivedependencyproject_2.9.2
0.1-SNAPSHOT> style="" penwidth="5" color="#0E92BE"] - | "justatransitivedependencyendpointproject:justatransitivedependencyendpointproject_2.9.2:0.1-SNAPSHOT"[shape=box label=justatransitivedependencyendpointproject_2.9.2
0.1-SNAPSHOT> style="" penwidth="5" color="#9EAD1B"] + | "justatransitivedependencyendpointproject:justatransitivedependencyendpointproject_2.9.2:0.1-SNAPSHOT"[shape=box label=justatransitivedependencyendpointproject_2.9.2
0.1-SNAPSHOT> style="" penwidth="5" color="#A1168F"] | "test_project:test_project_2.9.2:0.1-SNAPSHOT"[shape=box label=test_project_2.9.2
0.1-SNAPSHOT> style="" penwidth="5" color="#C37661"] | "justatransitivedependencyproject:justatransitivedependencyproject_2.9.2:0.1-SNAPSHOT" -> "justatransitivedependencyendpointproject:justatransitivedependencyendpointproject_2.9.2:0.1-SNAPSHOT" | "test_project:test_project_2.9.2:0.1-SNAPSHOT" -> "justadependencyproject:justadependencyproject_2.9.2:0.1-SNAPSHOT" @@ -31,8 +31,7 @@ lazy val test_project = project |} """.stripMargin - val graph: String = scala.io.Source.fromFile(dotFile.getAbsolutePath).mkString - val errors = compareByLine(graph, expectedGraph) + val errors = compareByLine(graph.trim, expectedGraph.trim) require(errors.isEmpty, errors.mkString("\n")) () } @@ -40,7 +39,7 @@ lazy val test_project = project def compareByLine(got: String, expected: String): Seq[String] = { val errors = ListBuffer[String]() - got.split("\n").zip(expected.split("\n").toSeq).zipWithIndex.foreach { + got.linesIterator.toList.sorted.zip(expected.linesIterator.toList.sorted).zipWithIndex.foreach { case ((got_line: String, expected_line: String), i: Int) => if (got_line != expected_line) { errors.append("""not matching lines at line %s @@ -49,5 +48,5 @@ def compareByLine(got: String, expected: String): Seq[String] = { |""".stripMargin.format(i, expected_line, got_line)) } } - errors + errors.toList } diff --git a/sbt-app/src/sbt-test/dependency-graph/testDotFileGeneration/project/plugins.sbt b/sbt-app/src/sbt-test/dependency-graph/testDotFileGeneration/project/plugins.sbt deleted file mode 100644 index 93c66d2a9..000000000 --- a/sbt-app/src/sbt-test/dependency-graph/testDotFileGeneration/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addDependencyTreePlugin diff --git a/sbt-app/src/sbt-test/dependency-graph/testHtmlFileGeneration/build.sbt b/sbt-app/src/sbt-test/dependency-graph/testHtmlFileGeneration/build.sbt index 8cf1ccf2f..1fa74f3fd 100644 --- a/sbt-app/src/sbt-test/dependency-graph/testHtmlFileGeneration/build.sbt +++ b/sbt-app/src/sbt-test/dependency-graph/testHtmlFileGeneration/build.sbt @@ -14,7 +14,7 @@ lazy val test_project = project .dependsOn(justADependencyProject, justATransitiveDependencyProject) .settings( TaskKey[Unit]("check") := { - val htmlFile = (dependencyBrowseGraphHTML in Compile).value + val htmlFile = (Compile / dependencyTree).toTask(" html-graph").value val expectedHtml = """ | @@ -60,11 +60,10 @@ lazy val test_project = project | - | """.stripMargin val html: String = scala.io.Source.fromFile(htmlFile).mkString - val errors = compareByLine(html, expectedHtml) + val errors = compareByLine(html.trim, expectedHtml.trim) require(errors.isEmpty, errors.mkString("\n")) () } @@ -72,7 +71,7 @@ lazy val test_project = project def compareByLine(got: String, expected: String): Seq[String] = { val errors = ListBuffer[String]() - got.split("\n").zip(expected.split("\n").toSeq).zipWithIndex.foreach { + got.linesIterator.toList.zip(expected.linesIterator.toList).zipWithIndex.foreach { case ((got_line: String, expected_line: String), i: Int) => if (got_line != expected_line) { errors.append("""not matching lines at line %s @@ -81,5 +80,5 @@ def compareByLine(got: String, expected: String): Seq[String] = { |""".stripMargin.format(i, expected_line, got_line)) } } - errors + errors.toList } diff --git a/sbt-app/src/sbt-test/dependency-graph/testHtmlFileGeneration/project/plugins.sbt b/sbt-app/src/sbt-test/dependency-graph/testHtmlFileGeneration/project/plugins.sbt deleted file mode 100644 index 93c66d2a9..000000000 --- a/sbt-app/src/sbt-test/dependency-graph/testHtmlFileGeneration/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addDependencyTreePlugin diff --git a/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/project/plugins.sbt b/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/project/plugins.sbt deleted file mode 100644 index 93c66d2a9..000000000 --- a/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addDependencyTreePlugin diff --git a/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/test b/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/test deleted file mode 100644 index b905a41b8..000000000 --- a/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/test +++ /dev/null @@ -1,5 +0,0 @@ -> dependencyTree/toFile target/tree.txt -> dependencyList/toFile target/list.txt -> dependencyStats/toFile target/stats.txt -> dependencyLicenseInfo/toFile target/licenses.txt -> check diff --git a/sbt-app/src/sbt-test/dependency-graph/whatDependsOn-without-previous-initialization/test b/sbt-app/src/sbt-test/dependency-graph/whatDependsOn-without-previous-initialization/pending similarity index 100% rename from sbt-app/src/sbt-test/dependency-graph/whatDependsOn-without-previous-initialization/test rename to sbt-app/src/sbt-test/dependency-graph/whatDependsOn-without-previous-initialization/pending diff --git a/sbt-app/src/sbt-test/dependency-graph/whatDependsOn/build.sbt b/sbt-app/src/sbt-test/dependency-graph/whatDependsOn/build.sbt index 1c7a78e3f..8f424ec33 100644 --- a/sbt-app/src/sbt-test/dependency-graph/whatDependsOn/build.sbt +++ b/sbt-app/src/sbt-test/dependency-graph/whatDependsOn/build.sbt @@ -11,26 +11,26 @@ libraryDependencies ++= Seq( val check = TaskKey[Unit]("check") check := { - def sanitize(str: String): String = str.split('\n').map(_.trim).mkString("\n") + def sanitize(str: String): String = str.linesIterator.toList.map(_.trim).mkString("\n") def checkOutput(output: String, expected: String): Unit = - require(sanitize(expected) == sanitize(output), s"Tree should have been [\n${sanitize(expected)}\n] but was [\n${sanitize(output)}\n]") + require(sanitize(expected) == sanitize(output), + s"Tree should have been [\n${expected}\n] but was [\n${output}\n]") val withVersion = (Compile / whatDependsOn) .toTask(" org.typelevel cats-core_2.13 2.6.0") .value - val expectedGraphWithVersion = { + val expectedGraphWithVersion = """org.typelevel:cats-core_2.13:2.6.0 [S] - |+-org.typelevel:cats-effect-kernel_2.13:3.1.0 [S] - |+-org.typelevel:cats-effect-std_2.13:3.1.0 [S] - || +-org.typelevel:cats-effect_2.13:3.1.0 [S] - || +-whatdependson:whatdependson_2.13:0.1.0-SNAPSHOT [S] - || - |+-org.typelevel:cats-effect_2.13:3.1.0 [S] - |+-whatdependson:whatdependson_2.13:0.1.0-SNAPSHOT [S]""".stripMargin - } + | +-org.typelevel:cats-effect-kernel_2.13:3.1.0 [S] + | +-org.typelevel:cats-effect-std_2.13:3.1.0 [S] + | | +-org.typelevel:cats-effect_2.13:3.1.0 [S] + | | +-whatdependson:whatdependson_2.13:0.1.0-SNAPSHOT [S] + | | + | +-org.typelevel:cats-effect_2.13:3.1.0 [S] + | +-whatdependson:whatdependson_2.13:0.1.0-SNAPSHOT [S]""".stripMargin - checkOutput(withVersion.trim, expectedGraphWithVersion.trim) + checkOutput(withVersion.trim, expectedGraphWithVersion) val withoutVersion = (Compile / whatDependsOn) diff --git a/sbt-app/src/sbt-test/dependency-graph/whatDependsOn/test b/sbt-app/src/sbt-test/dependency-graph/whatDependsOn/pending similarity index 100% rename from sbt-app/src/sbt-test/dependency-graph/whatDependsOn/test rename to sbt-app/src/sbt-test/dependency-graph/whatDependsOn/pending