Skip to content
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

release 0.10.0, 0.10.1 under org.hammerlab, with dependencyTree filtering, file output #1

Merged
merged 10 commits into from
Dec 8, 2017
56 changes: 54 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ install it as a [global plugin] so that you can use it in any SBT project withou
this, add the plugin dependency to `~/.sbt/0.13/plugins/plugins.sbt` for sbt 0.13 or `~/.sbt/1.0/plugins/plugins.sbt` for sbt 1.0:

```scala
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.9.0")
addSbtPlugin("org.hammerlab" % "sbt-dependency-graph" % "0.10.1")
```

To add the plugin only to a single project, put this line into `project/plugins.sbt` of your project, instead.
Expand All @@ -21,7 +21,7 @@ the notes of version [0.8.2](https://github.com/jrudolph/sbt-dependency-graph/tr

## Main Tasks

* `dependencyTree`: Shows an ASCII tree representation of the project's dependencies
* `dependencyTree`: Shows an ASCII tree representation of the project's dependencies (see [below](#dependencyTree-filtering) for examples filtering the output)
* `dependencyBrowseGraph`: Opens a browser window with a visualization of the dependency graph (courtesy of graphlib-dot + dagre-d3).
* `dependencyList`: Shows a flat list of all transitive dependencies on the sbt console (sorted by organization and name)
* `whatDependsOn <organization> <module> <revision>`: Find out what depends on an artifact. Shows a reverse dependency
Expand All @@ -40,6 +40,58 @@ All tasks can be scoped to a configuration to get the report for a specific conf
for example, prints the dependencies in the `test` configuration. If you don't specify any configuration, `compile` is
assumed as usual.

### `dependencyTree` filtering
The `dependencyTree` task supports filtering with inclusion/exclusion rules:

- exclusion rules are prefixed by `-`
- inclusion rules are the default (or can be prefixed by `+`)

Dependencies are "preserved" iff:
- they match at least one inclusion rule (or no inclusion rules are provided), and
- they match no exclusion rules (including when none are provided)

They are then displayed if they are preserved *or at least one of their transitive dependencies is preserved*.

This mimics the behavior of [Maven dependency:tree](https://maven.apache.org/plugins/maven-dependency-plugin/tree-mojo.html)'s `includes` and `excludes` parameters.

#### Examples

Inclusions/Exclusions can be partial-matched against any part of a dependency's Maven coordinate:

```
dependencyTree -foo // exclude deps that contain "foo" in the group, name, or version
dependencyTree foo // include deps that contain "foo" in the group, name, or version
```

Or they can be fully-matched against specific parts of the coordinate:

```
dependencyTree -:foo* // exclude deps whose name starts with "foo"
dependencyTree -*foo*::*bar // exclude deps whose group contains "foo" and version ends with "bar"
```

Inclusions and exclusions can be combined and repeated:
```
dependencyTree foo bar -baz // include only deps that contain "foo" or "bar" and not "baz"
```

In all cases, the full paths to dependencies that match the query are displayed (which can mean that dependencies are displayed even though they would have been excluded in their own right, because they form part of a chain to a dependency that was not excluded).

#### Writing output to file

`dependencyTree` can have its output written to a file:

```
$ sbt
> dependencyTree -o foo
```

or, directly from the shell:

```bash
sbt 'dependency-tree -o foo'
```

## Configuration settings

* `filterScalaLibrary`: Defines if the scala library should be excluded from the output of the dependency-* functions.
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ scalacOptions ++= Seq(
"-unchecked"
)

ScalariformSupport.formatSettings
ScalariformSupport.formatSettings
6 changes: 3 additions & 3 deletions project.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ sbtPlugin := true

name := "sbt-dependency-graph"

organization := "net.virtual-void"
organization := "org.hammerlab"

homepage := Some(url("http://github.com/jrudolph/sbt-dependency-graph"))
homepage := Some(url("http://github.com/hammerlab/sbt-dependency-graph"))

licenses in GlobalScope += "Apache License 2.0" -> url("https://github.com/jrudolph/sbt-dependency-graph/raw/master/LICENSE")
licenses in GlobalScope += "Apache License 2.0" -> url("https://github.com/hammerlab/sbt-dependency-graph/raw/master/LICENSE")
1 change: 1 addition & 0 deletions project/ScalariformSupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ object ScalariformSupport {
.setPreference(AlignParameters, true)
.setPreference(AlignSingleLineCaseStatements, true)
.setPreference(DoubleIndentClassDeclaration, true)
.setPreference(PreserveDanglingCloseParenthesis, true)

}
1 change: 1 addition & 0 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ libraryDependencies += "org.scala-sbt" % "scripted-plugin" % sbtVersion.value

addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.2.1")
addSbtPlugin("com.dwijnand" % "sbt-dynver" % "2.0.0")
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "1.1")
4 changes: 2 additions & 2 deletions publish.sbt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
publishTo := {
val nexus = "https://oss.sonatype.org/"
Some {
if (version.value.trim.contains("+")) "snapshots" at nexus + "content/repositories/snapshots"
else "releases" at nexus + "service/local/staging/deploy/maven2"
if (isSnapshot.value) "snapshots" at nexus + "content/repositories/snapshots"
else "releases" at nexus + "service/local/staging/deploy/maven2"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package rendering

import com.github.mdr.ascii.layout._
import net.virtualvoid.sbt.graph.DependencyGraphKeys._
import net.virtualvoid.sbt.graph.model.{ Module, ModuleGraph }
import sbt.Keys._

object AsciiGraph {
Expand Down Expand Up @@ -49,7 +50,7 @@ object AsciiGraph {
log.info("Note: The old tree layout is still available by using `dependency-tree`")
}

log.info(rendering.AsciiTree.asciiTree(moduleGraph.value))
log.info(AsciiTree(moduleGraph.value))

if (!force) {
log.info("\n")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package net.virtualvoid.sbt.graph

import net.virtualvoid.sbt.graph.model.ModuleGraph
import sbt._

trait DependencyGraphKeys {
Expand Down Expand Up @@ -49,9 +50,9 @@ trait DependencyGraphKeys {
"Returns a string containing the ascii representation of the dependency graph for a project")
val dependencyGraph = InputKey[Unit]("dependency-graph",
"Prints the ascii graph to the console")
val asciiTree = TaskKey[String]("dependency-tree-string",
val asciiTree = InputKey[String]("dependency-tree-string",
"Returns a string containing an ascii tree representation of the dependency graph for a project")
val dependencyTree = TaskKey[Unit]("dependency-tree",
val dependencyTree = InputKey[Unit]("dependency-tree",
"Prints an ascii tree of all the dependencies to the console")
val dependencyList = TaskKey[Unit]("dependency-list",
"Prints a list of all dependencies to the console")
Expand All @@ -74,4 +75,4 @@ trait DependencyGraphKeys {
private[graph] val crossProjectId = SettingKey[ModuleID]("dependency-graph-cross-project-id")
}

object DependencyGraphKeys extends DependencyGraphKeys
object DependencyGraphKeys extends DependencyGraphKeys
129 changes: 102 additions & 27 deletions src/main/scala/net/virtualvoid/sbt/graph/DependencyGraphSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,26 @@

package net.virtualvoid.sbt.graph

import scala.language.reflectiveCalls
import java.nio.file.Files.newOutputStream
import java.nio.file.{ Path, Paths }

import sbt._
import Keys._
import sbt.complete.Parser
import net.virtualvoid.sbt.graph.GraphTransformations.reverseGraphStartingAt
import net.virtualvoid.sbt.graph.backend.{ IvyReport, SbtUpdateReport }
import net.virtualvoid.sbt.graph.rendering.{ AsciiGraph, DagreHTML }
import net.virtualvoid.sbt.graph.model.{ FilterRule, ModuleGraph, ModuleId }
import net.virtualvoid.sbt.graph.rendering.{ AsciiGraph, AsciiTree, DagreHTML }
import net.virtualvoid.sbt.graph.util.IOUtil
import internal.librarymanagement._
import librarymanagement._
import sbt.Keys._
import sbt._
import sbt.complete.Parser
import sbt.dependencygraph.DependencyGraphSbtCompat
import sbt.dependencygraph.DependencyGraphSbtCompat.Implicits._
import sbt.internal.librarymanagement._

import scala.language.reflectiveCalls

object DependencyGraphSettings {
import DependencyGraphKeys._
import ModuleGraphProtocol._
import net.virtualvoid.sbt.graph.model.ModuleGraphProtocol._

def graphSettings = baseSettings ++ reportSettings

Expand All @@ -45,10 +49,20 @@ object DependencyGraphSettings {
Seq(Compile, Test, IntegrationTest, Runtime, Provided, Optional).flatMap(ivyReportForConfig)

def ivyReportForConfig(config: Configuration) = inConfig(config)(Seq(
ivyReport := { Def.task { ivyReportFunction.value.apply(config.toString) } dependsOn (ignoreMissingUpdate) }.value,
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),
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 {
Expand All @@ -65,8 +79,23 @@ object DependencyGraphSettings {
else moduleGraph
},
moduleGraphStore := (moduleGraph storeAs moduleGraphStore triggeredBy moduleGraph).value,
asciiTree := rendering.AsciiTree.asciiTree(moduleGraph.value),
dependencyTree := print(asciiTree).value,
asciiTree := AsciiTree(
moduleGraph.value,
filterRulesParser.parsed: _*
),
dependencyTree := {
val tree = asciiTree.evaluated
dependencyTreeOutputPathParser.parsed match {
case Some(path) ⇒
streams.value.log.info(s"Writing dependency-tree to path: $path")
val os = newOutputStream(path)
os.write(tree.getBytes)
os.close()
case _ ⇒
streams.value.log.info(tree)
}

},
dependencyGraphMLFile := { target.value / "dependencies-%s.graphml".format(config.toString) },
dependencyGraphML := dependencyGraphMLTask.value,
dependencyDotFile := { target.value / "dependencies-%s.dot".format(config.toString) },
Expand All @@ -92,8 +121,17 @@ object DependencyGraphSettings {
"""%s<BR/><B>%s</B><BR/>%s""".format(organisation, name, version)
},
whatDependsOn := {
val module = artifactIdParser.parsed
streams.value.log.info(rendering.AsciiTree.asciiTree(GraphTransformations.reverseGraphStartingAt(moduleGraph.value, module)))
streams
.value
.log
.info(
AsciiTree(
reverseGraphStartingAt(
moduleGraph.value,
artifactIdParser.parsed
)
)
)
},
licenseInfo := showLicenseInfo(moduleGraph.value, streams.value)) ++ AsciiGraph.asciiGraphSetttings)

Expand All @@ -105,7 +143,7 @@ object DependencyGraphSettings {
(config: String) ⇒ {
val org = projectID.organization
val name = crossName(ivyModule)
file(s"${crossTarget}/resolution-cache/reports/$org-$name-$config.xml")
file(s"$crossTarget/resolution-cache/reports/$org-$name-$config.xml")
}
}

Expand Down Expand Up @@ -152,28 +190,65 @@ object DependencyGraphSettings {
streams.log.info(output)
}

import Project._
val shouldForceParser: State ⇒ Parser[Boolean] = { (state: State) ⇒
import sbt.complete.DefaultParsers._
import sbt.complete.DefaultParsers._

val shouldForceParser: State ⇒ Parser[Boolean] = { (state: State) ⇒
(Space ~> token("--force")).?.map(_.isDefined)
}

val dependencyTreeOutputPathParser: State ⇒ Parser[Option[Path]] = { (state: State) ⇒
(
Space ~
(token("--out") | token("-o")) ~ Space ~>
StringBasic
)
.map(Paths.get(_))
.?
}

val filterRulesParser: Def.Initialize[State ⇒ Parser[Seq[FilterRule]]] =
resolvedScoped { ctx ⇒
(state: State) ⇒
(Space ~> token(StringBasic, "filter")).*.map {
_.map(FilterRule(_))
}
}

val artifactIdParser: Def.Initialize[State ⇒ Parser[ModuleId]] =
resolvedScoped { ctx ⇒
(state: State) ⇒
val graph = loadFromContext(moduleGraphStore, ctx, state) getOrElse ModuleGraph(Nil, Nil)

import sbt.complete.DefaultParsers._
graph.nodes.map(_.id).map {
case id @ ModuleId(org, name, version) ⇒
(Space ~ token(org) ~ token(Space ~ name) ~ token(Space ~ version)).map(_ ⇒ id)
}.reduceOption(_ | _).getOrElse {
(Space ~> token(StringBasic, "organization") ~ Space ~ token(StringBasic, "module") ~ Space ~ token(StringBasic, "version")).map {
case ((((org, _), mod), _), version) ⇒
ModuleId(org, mod, version)
graph
.nodes
.map(_.id)
.map {
case id @ ModuleId(org, name, version) ⇒
(
Space ~
token(org) ~
token(Space ~ name) ~
token(Space ~ version))
.map(_ ⇒ id)
}
.reduceOption(_ | _)
.getOrElse {
(
Space ~>
token(StringBasic, "organization") ~ Space ~
token(StringBasic, "module") ~ Space ~
token(StringBasic, "version"))
.map {
case (
(
((org, _), mod),
_
),
version
) ⇒
ModuleId(org, mod, version)
}
}
}
}

// This is to support 0.13.8's InlineConfigurationWithExcludes while not forcing 0.13.8
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package net.virtualvoid.sbt.graph

import net.virtualvoid.sbt.graph.model.{ Module, ModuleGraph, ModuleId }

object GraphTransformations {
def reverseGraphStartingAt(graph: ModuleGraph, root: ModuleId): ModuleGraph = {
val deps = graph.reverseDependencyMap
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
package net.virtualvoid.sbt.graph
package backend

import scala.xml.{ NodeSeq, Document, Node }
import net.virtualvoid.sbt.graph.model.{ Module, ModuleGraph, ModuleId }

import scala.xml.{ Document, Node, NodeSeq }
import scala.xml.parsing.ConstructingParser

object IvyReport {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
package net.virtualvoid.sbt.graph
package backend

import net.virtualvoid.sbt.graph.model.{ Module, ModuleGraph, ModuleId }

import scala.language.implicitConversions
import scala.language.reflectiveCalls

import sbt._

object SbtUpdateReport {
Expand Down
Loading