Skip to content

Commit 15d4f6b

Browse files
Merge pull request #1 from hammerlab/release
release 0.10.0, 0.10.1 under org.hammerlab, with dependencyTree filtering, file output
2 parents 1a56850 + 636a9a0 commit 15d4f6b

File tree

29 files changed

+627
-67
lines changed

29 files changed

+627
-67
lines changed

README.md

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ install it as a [global plugin] so that you can use it in any SBT project withou
1111
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:
1212

1313
```scala
14-
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.9.0")
14+
addSbtPlugin("org.hammerlab" % "sbt-dependency-graph" % "0.10.1")
1515
```
1616

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

2222
## Main Tasks
2323

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

43+
### `dependencyTree` filtering
44+
The `dependencyTree` task supports filtering with inclusion/exclusion rules:
45+
46+
- exclusion rules are prefixed by `-`
47+
- inclusion rules are the default (or can be prefixed by `+`)
48+
49+
Dependencies are "preserved" iff:
50+
- they match at least one inclusion rule (or no inclusion rules are provided), and
51+
- they match no exclusion rules (including when none are provided)
52+
53+
They are then displayed if they are preserved *or at least one of their transitive dependencies is preserved*.
54+
55+
This mimics the behavior of [Maven dependency:tree](https://maven.apache.org/plugins/maven-dependency-plugin/tree-mojo.html)'s `includes` and `excludes` parameters.
56+
57+
#### Examples
58+
59+
Inclusions/Exclusions can be partial-matched against any part of a dependency's Maven coordinate:
60+
61+
```
62+
dependencyTree -foo // exclude deps that contain "foo" in the group, name, or version
63+
dependencyTree foo // include deps that contain "foo" in the group, name, or version
64+
```
65+
66+
Or they can be fully-matched against specific parts of the coordinate:
67+
68+
```
69+
dependencyTree -:foo* // exclude deps whose name starts with "foo"
70+
dependencyTree -*foo*::*bar // exclude deps whose group contains "foo" and version ends with "bar"
71+
```
72+
73+
Inclusions and exclusions can be combined and repeated:
74+
```
75+
dependencyTree foo bar -baz // include only deps that contain "foo" or "bar" and not "baz"
76+
```
77+
78+
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).
79+
80+
#### Writing output to file
81+
82+
`dependencyTree` can have its output written to a file:
83+
84+
```
85+
$ sbt
86+
> dependencyTree -o foo
87+
```
88+
89+
or, directly from the shell:
90+
91+
```bash
92+
sbt 'dependency-tree -o foo'
93+
```
94+
4395
## Configuration settings
4496

4597
* `filterScalaLibrary`: Defines if the scala library should be excluded from the output of the dependency-* functions.

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@ scalacOptions ++= Seq(
2727
"-unchecked"
2828
)
2929

30-
ScalariformSupport.formatSettings
30+
ScalariformSupport.formatSettings

project.sbt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ sbtPlugin := true
22

33
name := "sbt-dependency-graph"
44

5-
organization := "net.virtual-void"
5+
organization := "org.hammerlab"
66

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

9-
licenses in GlobalScope += "Apache License 2.0" -> url("https://github.com/jrudolph/sbt-dependency-graph/raw/master/LICENSE")
9+
licenses in GlobalScope += "Apache License 2.0" -> url("https://github.com/hammerlab/sbt-dependency-graph/raw/master/LICENSE")

project/ScalariformSupport.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ object ScalariformSupport {
1616
.setPreference(AlignParameters, true)
1717
.setPreference(AlignSingleLineCaseStatements, true)
1818
.setPreference(DoubleIndentClassDeclaration, true)
19+
.setPreference(PreserveDanglingCloseParenthesis, true)
1920

2021
}

project/plugins.sbt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ libraryDependencies += "org.scala-sbt" % "scripted-plugin" % sbtVersion.value
22

33
addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.2.1")
44
addSbtPlugin("com.dwijnand" % "sbt-dynver" % "2.0.0")
5+
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "1.1")

publish.sbt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
publishTo := {
22
val nexus = "https://oss.sonatype.org/"
33
Some {
4-
if (version.value.trim.contains("+")) "snapshots" at nexus + "content/repositories/snapshots"
5-
else "releases" at nexus + "service/local/staging/deploy/maven2"
4+
if (isSnapshot.value) "snapshots" at nexus + "content/repositories/snapshots"
5+
else "releases" at nexus + "service/local/staging/deploy/maven2"
66
}
77
}
88

src/main/scala-sbt-0.13/net/virtualvoid/sbt/graph/rendering/AsciiGraph.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package rendering
1919

2020
import com.github.mdr.ascii.layout._
2121
import net.virtualvoid.sbt.graph.DependencyGraphKeys._
22+
import net.virtualvoid.sbt.graph.model.{ Module, ModuleGraph }
2223
import sbt.Keys._
2324

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

52-
log.info(rendering.AsciiTree.asciiTree(moduleGraph.value))
53+
log.info(AsciiTree(moduleGraph.value))
5354

5455
if (!force) {
5556
log.info("\n")

src/main/scala/net/virtualvoid/sbt/graph/DependencyGraphKeys.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package net.virtualvoid.sbt.graph
1818

19+
import net.virtualvoid.sbt.graph.model.ModuleGraph
1920
import sbt._
2021

2122
trait DependencyGraphKeys {
@@ -49,9 +50,9 @@ trait DependencyGraphKeys {
4950
"Returns a string containing the ascii representation of the dependency graph for a project")
5051
val dependencyGraph = InputKey[Unit]("dependency-graph",
5152
"Prints the ascii graph to the console")
52-
val asciiTree = TaskKey[String]("dependency-tree-string",
53+
val asciiTree = InputKey[String]("dependency-tree-string",
5354
"Returns a string containing an ascii tree representation of the dependency graph for a project")
54-
val dependencyTree = TaskKey[Unit]("dependency-tree",
55+
val dependencyTree = InputKey[Unit]("dependency-tree",
5556
"Prints an ascii tree of all the dependencies to the console")
5657
val dependencyList = TaskKey[Unit]("dependency-list",
5758
"Prints a list of all dependencies to the console")
@@ -74,4 +75,4 @@ trait DependencyGraphKeys {
7475
private[graph] val crossProjectId = SettingKey[ModuleID]("dependency-graph-cross-project-id")
7576
}
7677

77-
object DependencyGraphKeys extends DependencyGraphKeys
78+
object DependencyGraphKeys extends DependencyGraphKeys

src/main/scala/net/virtualvoid/sbt/graph/DependencyGraphSettings.scala

Lines changed: 102 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,26 @@
1616

1717
package net.virtualvoid.sbt.graph
1818

19-
import scala.language.reflectiveCalls
19+
import java.nio.file.Files.newOutputStream
20+
import java.nio.file.{ Path, Paths }
2021

21-
import sbt._
22-
import Keys._
23-
import sbt.complete.Parser
22+
import net.virtualvoid.sbt.graph.GraphTransformations.reverseGraphStartingAt
2423
import net.virtualvoid.sbt.graph.backend.{ IvyReport, SbtUpdateReport }
25-
import net.virtualvoid.sbt.graph.rendering.{ AsciiGraph, DagreHTML }
24+
import net.virtualvoid.sbt.graph.model.{ FilterRule, ModuleGraph, ModuleId }
25+
import net.virtualvoid.sbt.graph.rendering.{ AsciiGraph, AsciiTree, DagreHTML }
2626
import net.virtualvoid.sbt.graph.util.IOUtil
27-
import internal.librarymanagement._
28-
import librarymanagement._
27+
import sbt.Keys._
28+
import sbt._
29+
import sbt.complete.Parser
2930
import sbt.dependencygraph.DependencyGraphSbtCompat
3031
import sbt.dependencygraph.DependencyGraphSbtCompat.Implicits._
32+
import sbt.internal.librarymanagement._
33+
34+
import scala.language.reflectiveCalls
3135

3236
object DependencyGraphSettings {
3337
import DependencyGraphKeys._
34-
import ModuleGraphProtocol._
38+
import net.virtualvoid.sbt.graph.model.ModuleGraphProtocol._
3539

3640
def graphSettings = baseSettings ++ reportSettings
3741

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

4751
def ivyReportForConfig(config: Configuration) = inConfig(config)(Seq(
48-
ivyReport := { Def.task { ivyReportFunction.value.apply(config.toString) } dependsOn (ignoreMissingUpdate) }.value,
52+
ivyReport := { Def.task { ivyReportFunction.value.apply(config.toString) } dependsOn ignoreMissingUpdate }.value,
4953
crossProjectId := sbt.CrossVersion(scalaVersion.value, scalaBinaryVersion.value)(projectID.value),
5054
moduleGraphSbt :=
51-
ignoreMissingUpdate.value.configuration(configuration.value).map(report SbtUpdateReport.fromConfigurationReport(report, crossProjectId.value)).getOrElse(ModuleGraph.empty),
55+
ignoreMissingUpdate
56+
.value
57+
.configuration(configuration.value)
58+
.map(
59+
report
60+
SbtUpdateReport.fromConfigurationReport(
61+
report,
62+
crossProjectId.value
63+
)
64+
)
65+
.getOrElse(ModuleGraph.empty),
5266
moduleGraphIvyReport := IvyReport.fromReportFile(absoluteReportPath(ivyReport.value)),
5367
moduleGraph := {
5468
sbtVersion.value match {
@@ -65,8 +79,23 @@ object DependencyGraphSettings {
6579
else moduleGraph
6680
},
6781
moduleGraphStore := (moduleGraph storeAs moduleGraphStore triggeredBy moduleGraph).value,
68-
asciiTree := rendering.AsciiTree.asciiTree(moduleGraph.value),
69-
dependencyTree := print(asciiTree).value,
82+
asciiTree := AsciiTree(
83+
moduleGraph.value,
84+
filterRulesParser.parsed: _*
85+
),
86+
dependencyTree := {
87+
val tree = asciiTree.evaluated
88+
dependencyTreeOutputPathParser.parsed match {
89+
case Some(path)
90+
streams.value.log.info(s"Writing dependency-tree to path: $path")
91+
val os = newOutputStream(path)
92+
os.write(tree.getBytes)
93+
os.close()
94+
case _
95+
streams.value.log.info(tree)
96+
}
97+
98+
},
7099
dependencyGraphMLFile := { target.value / "dependencies-%s.graphml".format(config.toString) },
71100
dependencyGraphML := dependencyGraphMLTask.value,
72101
dependencyDotFile := { target.value / "dependencies-%s.dot".format(config.toString) },
@@ -92,8 +121,17 @@ object DependencyGraphSettings {
92121
"""%s<BR/><B>%s</B><BR/>%s""".format(organisation, name, version)
93122
},
94123
whatDependsOn := {
95-
val module = artifactIdParser.parsed
96-
streams.value.log.info(rendering.AsciiTree.asciiTree(GraphTransformations.reverseGraphStartingAt(moduleGraph.value, module)))
124+
streams
125+
.value
126+
.log
127+
.info(
128+
AsciiTree(
129+
reverseGraphStartingAt(
130+
moduleGraph.value,
131+
artifactIdParser.parsed
132+
)
133+
)
134+
)
97135
},
98136
licenseInfo := showLicenseInfo(moduleGraph.value, streams.value)) ++ AsciiGraph.asciiGraphSetttings)
99137

@@ -105,7 +143,7 @@ object DependencyGraphSettings {
105143
(config: String) {
106144
val org = projectID.organization
107145
val name = crossName(ivyModule)
108-
file(s"${crossTarget}/resolution-cache/reports/$org-$name-$config.xml")
146+
file(s"$crossTarget/resolution-cache/reports/$org-$name-$config.xml")
109147
}
110148
}
111149

@@ -152,28 +190,65 @@ object DependencyGraphSettings {
152190
streams.log.info(output)
153191
}
154192

155-
import Project._
156-
val shouldForceParser: State Parser[Boolean] = { (state: State)
157-
import sbt.complete.DefaultParsers._
193+
import sbt.complete.DefaultParsers._
158194

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

199+
val dependencyTreeOutputPathParser: State Parser[Option[Path]] = { (state: State)
200+
(
201+
Space ~
202+
(token("--out") | token("-o")) ~ Space ~>
203+
StringBasic
204+
)
205+
.map(Paths.get(_))
206+
.?
207+
}
208+
209+
val filterRulesParser: Def.Initialize[State Parser[Seq[FilterRule]]] =
210+
resolvedScoped { ctx
211+
(state: State)
212+
(Space ~> token(StringBasic, "filter")).*.map {
213+
_.map(FilterRule(_))
214+
}
215+
}
216+
162217
val artifactIdParser: Def.Initialize[State Parser[ModuleId]] =
163218
resolvedScoped { ctx
164219
(state: State)
165220
val graph = loadFromContext(moduleGraphStore, ctx, state) getOrElse ModuleGraph(Nil, Nil)
166221

167-
import sbt.complete.DefaultParsers._
168-
graph.nodes.map(_.id).map {
169-
case id @ ModuleId(org, name, version)
170-
(Space ~ token(org) ~ token(Space ~ name) ~ token(Space ~ version)).map(_ id)
171-
}.reduceOption(_ | _).getOrElse {
172-
(Space ~> token(StringBasic, "organization") ~ Space ~ token(StringBasic, "module") ~ Space ~ token(StringBasic, "version")).map {
173-
case ((((org, _), mod), _), version)
174-
ModuleId(org, mod, version)
222+
graph
223+
.nodes
224+
.map(_.id)
225+
.map {
226+
case id @ ModuleId(org, name, version)
227+
(
228+
Space ~
229+
token(org) ~
230+
token(Space ~ name) ~
231+
token(Space ~ version))
232+
.map(_ id)
233+
}
234+
.reduceOption(_ | _)
235+
.getOrElse {
236+
(
237+
Space ~>
238+
token(StringBasic, "organization") ~ Space ~
239+
token(StringBasic, "module") ~ Space ~
240+
token(StringBasic, "version"))
241+
.map {
242+
case (
243+
(
244+
((org, _), mod),
245+
_
246+
),
247+
version
248+
)
249+
ModuleId(org, mod, version)
250+
}
175251
}
176-
}
177252
}
178253

179254
// This is to support 0.13.8's InlineConfigurationWithExcludes while not forcing 0.13.8

src/main/scala/net/virtualvoid/sbt/graph/GraphTransformations.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package net.virtualvoid.sbt.graph
1818

19+
import net.virtualvoid.sbt.graph.model.{ Module, ModuleGraph, ModuleId }
20+
1921
object GraphTransformations {
2022
def reverseGraphStartingAt(graph: ModuleGraph, root: ModuleId): ModuleGraph = {
2123
val deps = graph.reverseDependencyMap

0 commit comments

Comments
 (0)