Merge master and adapt solution

A new case class ArtifactPattern was created which models both searching with
and without version.
This commit is contained in:
Johannes Rudolph 2018-07-16 14:59:16 +02:00
commit 6dbcad89d6
No known key found for this signature in database
GPG Key ID: 4D293A24CCD39E19
56 changed files with 705 additions and 646 deletions

5
.travis.yml Normal file
View File

@ -0,0 +1,5 @@
sudo: false
language: scala
jdk: oraclejdk8
script:
- sbt ";^test ;^scripted"

130
CHANGELOG.md Normal file
View File

@ -0,0 +1,130 @@
# Changelog
## Version 0.9.0 (2017-10-25)
This version (finally!) adds support for sbt 1.0. *sbt-dependency-graph* depends on a lot of internals from sbt to do its
work which is why it was quite an effort to do the migration. Thanks [@MasseGuillaume](https://github.com/MasseGuillaume) from Scala Center,
[@2m](https://github.com/2m), and [@xuwei-k](https://github.com/xuwei-k) for helping out with the effort.
The plugin is cross-built for sbt 0.13 (and will continued to be for while). The `dependencyGraph` task is currently not
supported on sbt 1.0. Use `dependencyBrowseGraph`, instead.
## Version 0.8.2 (2016-02-01)
This is a maintenance release [fixing](https://github.com/jrudolph/sbt-dependency-graph/issues/89) `dependencyBrowseGraph`
in the latest Chrome versions. Thanks [@chtefi](https://github.com/chtefi)!
## Version 0.8.1 (2016-01-08)
This is a maintenance release fixing a regression in 0.8.0 and adding two small features.
All changes:
* [#84](https://github.com/jrudolph/sbt-dependency-graph/issues/84): Fix regression of DOT label rendering introduced in 0.8.0.
* [#83](https://github.com/jrudolph/sbt-dependency-graph/issues/83): Added new task `dependencyStats` which prints a
simple table of jar sizes for all your dependencies. Handy if you want to know why your assembled jar gets so big.
* [#85](https://github.com/jrudolph/sbt-dependency-graph/issues/85): Added new task `dependencyList` which prints a
flat, deduplicated list of all the transitive dependencies.
## Version 0.8.0 (2015-11-26)
sbt-dependency-graph is finally an AutoPlugin and can now show the dependency graph in the browser directly.
### New features
- (experimental) open dependency graph directly in the browser with `dependencyBrowseGraph` ([#29](https://github.com/jrudolph/sbt-dependency-graph/issues/29))
![dependencyBrowseGraph in action](https://gist.githubusercontent.com/jrudolph/941754bcf67a0fafe495/raw/7d80d766feb7af6ba2a69494e1f3ceb1fd40d4da/Screenshot%2520from%25202015-11-26%252014:18:19.png)
- this plugin is finally an sbt AutoPlugin and it is automatically enabled
([#51](https://github.com/jrudolph/sbt-dependency-graph/issues/51))
**Note: To update from 0.7.x remove the `net.virtualvoid.sbt.graph.Plugin.graphSettings` line from your configurations.**
### Other changes
- a new backend was implemented which accesses the in-memory dependency data structures of sbt directly. The plugin doesn't
require accessing the ivy report XML any more (the old backend can still be wired in for comparisons if needed) which
should have solved the race condition and the dreaded `FileNotFoundException` ([#39](https://github.com/jrudolph/sbt-dependency-graph/issues/39))
in multi-module projects. The new backend is only used for sbt >= 0.13.6.
- code was restructured which touched a lot of the classes but didn't change the function or syntax of settings and tasks.
- fixed [#77](https://github.com/jrudolph/sbt-dependency-graph/issues/77)
## Version 0.7.5 (2015-03-30)
This is a maintenance release adding support for sbt 0.13.8.
All changes:
* [#67](https://github.com/jrudolph/sbt-dependency-graph/issues/67): Added support for sbt 0.13.8. Thanks
[@eed3si9n](https://github.com/eed3si9n) for the fix.
* [#37](https://github.com/jrudolph/sbt-dependency-graph/issues/37): Don't fail with StringIndexOutOfBoundsException
for deep trees.
* [#44](https://github.com/jrudolph/sbt-dependency-graph/issues/44): Only match scala lib by org/name.
Thanks [@2beaucoup](https://github.com/2beaucoup) for the fix.
## Version 0.7.4 (2013-06-26)
This is a maintenance release fixing an exception when generating graphs without a terminal [#32](https://github.com/jrudolph/sbt-dependency-graph/issues/32).
## Version 0.7.3 (2013-04-28)
This is a maintenance release. Following issues have been fixed:
* [#27](https://github.com/jrudolph/sbt-dependency-graph/issues/27): A dependency configured with
a version range was not properly associated with its dependant.
* [#30](https://github.com/jrudolph/sbt-dependency-graph/issues/30) & [#31](https://github.com/jrudolph/sbt-dependency-graph/issues/31):
Make it work again with sbt 0.12.3. The path of the dependency resolution file changed in sbt 0.12.3.
Thanks [ebowman](https://github.com/ebowman) for the fix.
## Version 0.7.2 (2013-03-02)
This is a maintenance release. Following issues have been fixed:
* [#27](https://github.com/jrudolph/sbt-dependency-graph/issues/27): A dependency configured with
a version range was not properly associated with its dependant.
## Version 0.7.1
New features in this version:
* `dependency-license-info`: show dependencies grouped by declared license
* `dependency-dot`: create dot file from dependency graph. Contributed by
[berleon](https://github.com/berleon).
## Version 0.7.0 (2012-10-24)
New features in this version:
* `dependency-graph` now renders a real graph. Thanks go to [Matt Russell](https://github.com/mdr/) for
this added awesomeness.
* The tree output from previous versions is now available with `dependency-tree`.
* New task `what-depends-on` showing reverse dependency tree for a selected module (incl. tab-completion for modules)
* Don't fail in cases of a missing dependency. Show errors directly in the output.
* Show info about evicted versions.
* By default, exclude scala-library dependency and append `[S]` to the artifact name instead. Set
`filter-scala-library` to `false` to disable this feature.
* Works with sbt 0.12.1. The ivy report files were moved to a new location making an update necessary.
## Version 0.6.0 (2012-05-23)
New features in this version:
* `dependency-graph` task now prints the dependency graph to the console
(contributed by @gseitz)
* `dependency-graph-ml` contains now the old functionality of `dependency-graph`
which generates a `.graphml` file. Nodes now contain the dependency version as well (contributed by @gseitz).
* The output filename of `dependency-graph-ml` has been changed to include the configuration name. It is now
configurable using the `dependency-graph-ml-file` setting.
* The common `scalaVersion in update` idiom to support Scala 2.9.1 libraries in a
Scala 2.9.2 broke the plugin in 0.5.2, because it wouldn't find the ivy report xml file
any more. This was fixed.
* All tasks are scoped by configuration.
## Version 0.5.2 (2012-02-13)
## Version 0.5.1 (2011-11-18)
## Version 0.5 (2011-11-15)

View File

@ -1,46 +1,47 @@
# sbt-dependency-graph # sbt-dependency-graph
[![Join the chat at https://gitter.im/jrudolph/sbt-dependency-graph](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jrudolph/sbt-dependency-graph?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Visualize your project's dependencies. Visualize your project's dependencies.
## Preliminaries
The plugin works best with sbt >= 0.13.6. See the [compatibility notes](#compatibility-notes) to use this plugin with an older version of sbt.
## Usage Instructions ## Usage Instructions
Since sbt-dependency-graph is an informational tool rather than one that changes your build, you will more than likely wish to sbt-dependency-graph is an informational tool rather than one that changes your build, so you will more than likely wish to
install it as a [global plugin] so that you can use it in any SBT project without the need to explicitly add it to each one. To do install it as a [global plugin] so that you can use it in any SBT project without the need to explicitly add it to each one. To do
this, add the plugin dependency to `~/.sbt/0.13/plugins/plugins.sbt`: 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 ```scala
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.2") addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.9.0")
``` ```
To add the plugin only to a single project, put this line into `project/plugins.sbt` of your project, instead. To add the plugin only to a single project, put this line into `project/plugins.sbt` of your project, instead.
This plugin is an auto-plugin which will be automatically enabled starting from sbt 0.13.5. The plugin currently supports sbt versions >= 0.13.10 and sbt 1.0.x. For versions supporting older versions of sbt see
the notes of version [0.8.2](https://github.com/jrudolph/sbt-dependency-graph/tree/v0.8.2#compatibility-notes).
## Main Tasks ## Main Tasks
* `dependencyTree`: Shows an ASCII tree representation of the project's dependencies * `dependencyTree`: Shows an ASCII tree representation of the project's dependencies
* `dependencyBrowseGraph`: Opens a browser window with a visualization of the dependency graph (courtesy of graphlib-dot + dagre-d3). * `dependencyBrowseGraph`: Opens a browser window with a visualization of the dependency graph (courtesy of graphlib-dot + dagre-d3).
* `dependencyGraph`: Shows an ASCII graph of the project's dependencies on the sbt console
* `dependencyList`: Shows a flat list of all transitive dependencies on the sbt console (sorted by organization and name) * `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 * `whatDependsOn <organization> <module> <revision>?`: Find out what depends on an artifact. Shows a reverse dependency
tree for the selected module. tree for the selected module. The `<revision>` argument is optional.
* `dependencyLicenseInfo`: show dependencies grouped by declared license * `dependencyLicenseInfo`: show dependencies grouped by declared license
* `dependencyStats`: Shows a table with each module a row with (transitive) Jar sizes and number of dependencies * `dependencyStats`: Shows a table with each module a row with (transitive) Jar sizes and number of dependencies
* `dependencyGraphMl`: Generates a `.graphml` file with the project's dependencies to `target/dependencies-<config>.graphml`. * `dependencyGraphMl`: Generates a `.graphml` file with the project's dependencies to `target/dependencies-<config>.graphml`.
Use e.g. [yEd](http://www.yworks.com/en/products_yed_about.html) to format the graph to your needs. Use e.g. [yEd](http://www.yworks.com/en/products_yed_about.html) to format the graph to your needs.
* `dependencyDot`: Generates a .dot file with the project's dependencies to `target/dependencies-<config>.dot`. * `dependencyDot`: Generates a .dot file with the project's dependencies to `target/dependencies-<config>.dot`.
Use [graphviz](http://www.graphviz.org/) to render it to your preferred graphic format. Use [graphviz](http://www.graphviz.org/) to render it to your preferred graphic format.
* `ivyReport`: let's ivy generate the resolution report for you project. Use * `dependencyGraph`: Shows an ASCII graph of the project's dependencies on the sbt console (only supported on sbt 0.13)
* `ivyReport`: Lets ivy generate the resolution report for you project. Use
`show ivyReport` for the filename of the generated report `show ivyReport` for the filename of the generated report
All tasks can be scoped to a configuration to get the report for a specific configuration. `test:dependencyGraph`, All tasks can be scoped to a configuration to get the report for a specific configuration. `test:dependencyGraph`,
for example, prints the dependencies in the `test` configuration. If you don't specify any configuration, `compile` is for example, prints the dependencies in the `test` configuration. If you don't specify any configuration, `compile` is
assumed as usual. assumed as usual.
Note: If you want to run tasks with parameters from outside the sbt shell, make sure to put the whole task invocation in quotes, e.g. `sbt "whatDependsOn <org> <module> <version>"`.
## Configuration settings ## Configuration settings
* `filterScalaLibrary`: Defines if the scala library should be excluded from the output of the dependency-* functions. * `filterScalaLibrary`: Defines if the scala library should be excluded from the output of the dependency-* functions.
@ -64,19 +65,6 @@ dependencyDotFile := file("dependencies.dot") //render dot file to `./dependenci
* [#19]: There's an unfixed bug with graph generation for particular layouts. Workaround: * [#19]: There's an unfixed bug with graph generation for particular layouts. Workaround:
Use `dependency-tree` instead of `dependency-graph`. Use `dependency-tree` instead of `dependency-graph`.
* [#39]: When using sbt-dependency-graph with sbt < 0.13.6.
## Compatibility notes
* sbt < 0.13.6: The plugin will fall back on the old ivy report XML backend which suffers from [#39].
* sbt < 0.13.5: Old versions of sbt have no `AutoPlugin` support, you need to add
```scala
net.virtualvoid.sbt.graph.DependencyGraphSettings.graphSettings
```
to your `build.sbt` or (`~/.sbt/0.13/user.sbt` for global configuration) to enable the plugin.
* sbt <= 0.12.x: Old versions of sbt are not actively supported any more. Please use the old version from the [0.7 branch](https://github.com/jrudolph/sbt-dependency-graph/tree/0.7).
## License ## License
@ -85,4 +73,3 @@ Published under the [Apache License 2.0](http://en.wikipedia.org/wiki/Apache_lic
[global plugin]: http://www.scala-sbt.org/0.13/tutorial/Using-Plugins.html#Global+plugins [global plugin]: http://www.scala-sbt.org/0.13/tutorial/Using-Plugins.html#Global+plugins
[global build configuration]: http://www.scala-sbt.org/0.13/docs/Global-Settings.html [global build configuration]: http://www.scala-sbt.org/0.13/docs/Global-Settings.html
[#19]: https://github.com/jrudolph/sbt-dependency-graph/issues/19 [#19]: https://github.com/jrudolph/sbt-dependency-graph/issues/19
[#39]: https://github.com/jrudolph/sbt-dependency-graph/issues/39

View File

@ -1,28 +1,28 @@
crossBuildingSettings scriptedLaunchOpts += s"-Dproject.version=${version.value}"
CrossBuilding.crossSbtVersions := Seq("0.13") libraryDependencies ++= {
if ((sbtVersion in pluginCrossBuild).value startsWith "0.13")
CrossBuilding.scriptedSettings Seq("com.github.mdr" %% "ascii-graphs" % "0.0.3")
else
libraryDependencies += "com.github.mdr" %% "ascii-graphs" % "0.0.3" Nil
libraryDependencies <++= scalaVersion { version =>
if (version startsWith "2.1") Seq("org.scala-lang" % "scala-reflect" % version % "provided")
else Nil
} }
libraryDependencies <+= scalaVersion { version =>
if (version startsWith "2.9") "org.specs2" % "specs2_2.9.3" % "1.12.4.1" % "test"
else "org.specs2" %% "specs2" % "2.3.11" % "test"
}
scalacOptions ++= Seq("-deprecation", "-unchecked") libraryDependencies += "org.specs2" %% "specs2-core" % "3.10.0" % Test
sbt.CrossBuilding.latestCompatibleVersionMapper ~= { libraryDependencies += Defaults.sbtPluginExtra(
original => { "com.dwijnand" % "sbt-compat" % "1.2.6",
case "0.13" => "0.13.6" (sbtBinaryVersion in pluginCrossBuild).value,
case x => original(x) (scalaBinaryVersion in update).value
} )
}
crossSbtVersions := Seq("1.1.6", "0.13.16")
scalacOptions ++= Seq(
"-deprecation",
"-encoding", "UTF-8",
"-feature",
"-unchecked"
)
ScalariformSupport.formatSettings ScalariformSupport.formatSettings

View File

@ -1,12 +0,0 @@
New features in this version:
* `dependency-graph` task now prints the dependency graph to the console
(contributed by @gseitz)
* `dependency-graph-ml` contains now the old functionality of `dependency-graph`
which generates a `.graphml` file. Nodes now contain the dependency version as well (contributed by @gseitz).
* The output filename of `dependency-graph-ml` has been changed to include the configuration name. It is now
configurable using the `dependency-graph-ml-file` setting.
* The common `scalaVersion in update` idiom to support Scala 2.9.1 libraries in a
Scala 2.9.2 broke the plugin in 0.5.2, because it wouldn't find the ivy report xml file
any more. This was fixed.
* All tasks are scoped by configuration.

View File

@ -1,12 +0,0 @@
New features in this version:
* `dependency-graph` now renders a real graph. Thanks go to [Matt Russell](https://github.com/mdr/) for
this added awesomeness.
* The tree output from previous versions is now available with `dependency-tree`.
* New task `what-depends-on` showing reverse dependency tree for a selected module (incl. tab-completion for modules)
* Don't fail in cases of a missing dependency. Show errors directly in the output.
* Show info about evicted versions.
* By default, exclude scala-library dependency and append `[S]` to the artifact name instead. Set
`filter-scala-library` to `false` to disable this feature.
* Works with sbt 0.12.1. The ivy report files were moved to a new location making an update necessary.

View File

@ -1,5 +0,0 @@
New features in this version:
* `dependency-license-info`: show dependencies grouped by declared license
* `dependency-dot`: create dot file from dependency graph. Contributed by
[berleon](https://github.com/berleon).

View File

@ -1,5 +0,0 @@
This is a maintenance release. Following issues have been fixed:
* [#27](https://github.com/jrudolph/sbt-dependency-graph/issues/27): A dependency configured with
a version range was not properly associated with its dependant.

View File

@ -1,7 +0,0 @@
This is a maintenance release. Following issues have been fixed:
* [#27](https://github.com/jrudolph/sbt-dependency-graph/issues/27): A dependency configured with
a version range was not properly associated with its dependant.
* [#30](https://github.com/jrudolph/sbt-dependency-graph/issues/30) & [#31](https://github.com/jrudolph/sbt-dependency-graph/issues/31):
Make it work again with sbt 0.12.3. The path of the dependency resolution file changed in sbt 0.12.3.
Thanks [ebowman](https://github.com/ebowman) for the fix.

View File

@ -1 +0,0 @@
This is a maintenance release fixing an exception when generating graphs without a terminal [#32](https://github.com/jrudolph/sbt-dependency-graph/issues/32).

View File

@ -1,10 +0,0 @@
This is a maintenance release adding support for sbt 0.13.8.
All changes:
* [#67](https://github.com/jrudolph/sbt-dependency-graph/issues/67): Added support for sbt 0.13.8. Thanks
[@eed3si9n](https://github.com/eed3si9n) for the fix.
* [#37](https://github.com/jrudolph/sbt-dependency-graph/issues/37): Don't fail with StringIndexOutOfBoundsException
for deep trees.
* [#44](https://github.com/jrudolph/sbt-dependency-graph/issues/44): Only match scala lib by org/name.
Thanks [@2beaucoup](https://github.com/2beaucoup) for the fix.

View File

@ -1,20 +0,0 @@
sbt-dependency-graph is finally an AutoPlugin and can now show the dependency graph in the browser directly.
## New features
- (experimental) open dependency graph directly in the browser with `dependencyBrowseGraph` ([#29](https://github.com/jrudolph/sbt-dependency-graph/issues/29))
![dependencyBrowseGraph in action](https://gist.githubusercontent.com/jrudolph/941754bcf67a0fafe495/raw/7d80d766feb7af6ba2a69494e1f3ceb1fd40d4da/Screenshot%2520from%25202015-11-26%252014:18:19.png)
- this plugin is finally an sbt AutoPlugin and it is automatically enabled
([#51](https://github.com/jrudolph/sbt-dependency-graph/issues/51))
**Note: To update from 0.7.x remove the `net.virtualvoid.sbt.graph.Plugin.graphSettings` line from your configurations.**
## Other changes
- a new backend was implemented which accesses the in-memory dependency data structures of sbt directly. The plugin doesn't
require accessing the ivy report XML any more (the old backend can still be wired in for comparisons if needed) which
should have solved the race condition and the dreaded `FileNotFoundException` ([#39](https://github.com/jrudolph/sbt-dependency-graph/issues/39))
in multi-module projects. The new backend is only used for sbt >= 0.13.6.
- code was restructured which touched a lot of the classes but didn't change the function or syntax of settings and tasks.
- fixed [#77](https://github.com/jrudolph/sbt-dependency-graph/issues/77)

View File

@ -1,9 +0,0 @@
This is a maintenance release fixing a regression in 0.8.0 and adding two small features.
All changes:
* [#84](https://github.com/jrudolph/sbt-dependency-graph/issues/84): Fix regression of DOT label rendering introduced in 0.8.0.
* [#83](https://github.com/jrudolph/sbt-dependency-graph/issues/83): Added new task `dependencyStats` which prints a
simple table of jar sizes for all your dependencies. Handy if you want to know why your assembled jar gets so big.
* [#85](https://github.com/jrudolph/sbt-dependency-graph/issues/85): Added new task `dependencyList` which prints a
flat, deduplicated list of all the transitive dependencies.

View File

@ -1,2 +0,0 @@
This is a maintenance release [fixing](https://github.com/jrudolph/sbt-dependency-graph/issues/89) `dependencyBrowseGraph`
in the latest Chrome versions. Thanks [@chtefi](https://github.com/chtefi)!

View File

@ -1 +0,0 @@
[sbt-dependency-graph](https://github.com/jrudolph/sbt-dependency-graph/) is an sbt plugin to visualize dependencies of your build.

View File

@ -4,8 +4,6 @@ name := "sbt-dependency-graph"
organization := "net.virtual-void" organization := "net.virtual-void"
version := "0.8.3-SNAPSHOT"
homepage := Some(url("http://github.com/jrudolph/sbt-dependency-graph")) homepage := Some(url("http://github.com/jrudolph/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/jrudolph/sbt-dependency-graph/raw/master/LICENSE")

View File

@ -1,16 +0,0 @@
import java.net.URL
object Helpers {
def generatePomExtra(scmUrl: String, scmConnection: String,
developerId: String, developerName: String): xml.NodeSeq =
<scm>
<url>{ scmUrl }</url>
<connection>{ scmConnection }</connection>
</scm>
<developers>
<developer>
<id>{ developerId }</id>
<name>{ developerName }</name>
</developer>
</developers>
}

View File

@ -1 +1 @@
sbt.version=0.13.8 sbt.version=1.1.6

View File

@ -1 +1 @@
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0")

View File

@ -1,3 +1,4 @@
addSbtPlugin("net.virtual-void" % "sbt-cross-building" % "0.8.1") libraryDependencies += "org.scala-sbt" %% "scripted-plugin" % sbtVersion.value
addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.2.1") addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.2")
addSbtPlugin("com.dwijnand" % "sbt-dynver" % "2.0.0")

View File

@ -1,7 +1,9 @@
publishTo <<= version { v: String => publishTo := {
val nexus = "https://oss.sonatype.org/" val nexus = "https://oss.sonatype.org/"
if (v.trim.endsWith("SNAPSHOT")) Some("snapshots" at nexus + "content/repositories/snapshots") Some {
else Some("releases" at nexus + "service/local/staging/deploy/maven2") if (version.value.trim.contains("+")) "snapshots" at nexus + "content/repositories/snapshots"
else "releases" at nexus + "service/local/staging/deploy/maven2"
}
} }
publishMavenStyle := true publishMavenStyle := true
@ -10,9 +12,20 @@ publishArtifact in Test := false
pomIncludeRepository := { _ => false } pomIncludeRepository := { _ => false }
pomExtra := scmInfo := Some(
Helpers.generatePomExtra("git@github.com:jrudolph/sbt-dependency-graph.git", ScmInfo(
"scm:git:git@github.com:jrudolph/sbt-dependency-graph.git", browseUrl = url("https://github.com/jrudolph/sbt-dependency-graph"),
"jrudolph", "Johannes Rudolph") connection = "scm:git:git@github.com:jrudolph/sbt-dependency-graph.git"
)
)
developers := List(
Developer(
"jrudolph",
"Johannes Rudolph",
"johannes.rudolph@gmail.com",
url("https://virtual-void.net")
)
)
useGpg := true useGpg := true

View File

@ -1,18 +0,0 @@
{
"organization":"net.virtualvoid",
"name":"sbt-dependency-graph",
"version":"0.5.1",
"description":"An sbt plugin which allows to create a graphml file from the dependencies of the project.",
"site":"http://github.com/jrudolph/sbt-dependency-graph",
"tags":["dependency","graph","sbt-plugin","sbt"],
"docs":"http://github.com/jrudolph/sbt-dependency-graph",
"licenses": [{
"name": "Apache License 2.0",
"url": "https://github.com/jrudolph/sbt-dependency-graph/raw/master/LICENSE"
}],
"resolvers": ["http://scala-tools.org/repo-releases"],
"dependencies": [],
"scalas": ["2.9.1"],
"sbt": true
}

View File

@ -1,18 +0,0 @@
{
"organization":"net.virtual-void",
"name":"sbt-dependency-graph",
"version":"0.5.2",
"description":"An sbt plugin which allows to create a graphml file from the dependencies of the project.",
"site":"http://github.com/jrudolph/sbt-dependency-graph",
"tags":["dependency","graph","sbt-plugin","sbt"],
"docs":"http://github.com/jrudolph/sbt-dependency-graph",
"licenses": [{
"name": "Apache License 2.0",
"url": "https://github.com/jrudolph/sbt-dependency-graph/raw/master/LICENSE"
}],
"resolvers": ["http://scala-tools.org/repo-releases"],
"dependencies": [],
"scalas": ["2.9.1"],
"sbt": true
}

View File

@ -1,17 +0,0 @@
{
"organization":"net.virtual-void",
"name":"sbt-dependency-graph",
"version":"0.6.0",
"description":"An sbt plugin to visualize dependencies of your build.",
"site":"http://github.com/jrudolph/sbt-dependency-graph",
"tags":["dependency","graph","sbt-plugin","sbt"],
"docs":"http://github.com/jrudolph/sbt-dependency-graph",
"licenses": [{
"name": "Apache License 2.0",
"url": "https://github.com/jrudolph/sbt-dependency-graph/raw/master/LICENSE"
}],
"resolvers": ["https://oss.sonatype.org/content/repositories/releases"],
"dependencies": [],
"scalas": ["2.9.1"],
"sbt": true
}

View File

@ -1,29 +0,0 @@
{
"organization" : "net.virtual-void",
"name" : "sbt-dependency-graph",
"version" : "0.7.0",
"description" : "An sbt plugin to visualize dependencies of your build.",
"site" : "http://github.com/jrudolph/sbt-dependency-graph",
"tags" : [ "dependency", "graph", "sbt-plugin", "sbt" ],
"docs" : "http://github.com/jrudolph/sbt-dependency-graph",
"resolvers" : [ "https://oss.sonatype.org/content/repositories/releases" ],
"dependencies" : [ {
"organization" : "org.scala-sbt",
"name" : "scripted-sbt",
"version" : "0.12.0"
}, {
"organization" : "org.scala-sbt",
"name" : "sbt-launch",
"version" : "0.12.0"
}, {
"organization" : "com.github.mdr",
"name" : "ascii-graphs",
"version" : "0.0.2"
} ],
"scalas" : [ "2.9.2" ],
"licenses" : [ {
"name" : "Apache License 2.0",
"url" : "https://github.com/jrudolph/sbt-dependency-graph/raw/master/LICENSE"
} ],
"sbt" : true
}

View File

@ -1,29 +0,0 @@
{
"organization" : "net.virtual-void",
"name" : "sbt-dependency-graph",
"version" : "0.7.1",
"description" : "An sbt plugin to visualize dependencies of your build.",
"site" : "http://github.com/jrudolph/sbt-dependency-graph",
"tags" : [ "dependency", "graph", "sbt-plugin", "sbt" ],
"docs" : "http://github.com/jrudolph/sbt-dependency-graph",
"resolvers" : [ "https://oss.sonatype.org/content/repositories/releases" ],
"dependencies" : [ {
"organization" : "org.scala-sbt",
"name" : "scripted-sbt",
"version" : "0.12.0"
}, {
"organization" : "org.scala-sbt",
"name" : "sbt-launch",
"version" : "0.12.0"
}, {
"organization" : "com.github.mdr",
"name" : "ascii-graphs",
"version" : "0.0.2"
} ],
"scalas" : [ "2.9.2" ],
"licenses" : [ {
"name" : "Apache License 2.0",
"url" : "https://github.com/jrudolph/sbt-dependency-graph/raw/master/LICENSE"
} ],
"sbt" : true
}

View File

@ -1,29 +0,0 @@
{
"organization" : "net.virtual-void",
"name" : "sbt-dependency-graph",
"version" : "0.7.2",
"description" : "An sbt plugin to visualize dependencies of your build.",
"site" : "http://github.com/jrudolph/sbt-dependency-graph",
"tags" : [ "dependency", "graph", "sbt-plugin", "sbt" ],
"docs" : "http://github.com/jrudolph/sbt-dependency-graph",
"resolvers" : [ "https://oss.sonatype.org/content/repositories/releases" ],
"dependencies" : [ {
"organization" : "org.scala-sbt",
"name" : "scripted-sbt",
"version" : "0.12.1"
}, {
"organization" : "org.scala-sbt",
"name" : "sbt-launch",
"version" : "0.12.1"
}, {
"organization" : "com.github.mdr",
"name" : "ascii-graphs",
"version" : "0.0.2"
} ],
"scalas" : [ "2.9.2" ],
"licenses" : [ {
"name" : "Apache License 2.0",
"url" : "https://github.com/jrudolph/sbt-dependency-graph/raw/master/LICENSE"
} ],
"sbt" : true
}

View File

@ -1,29 +0,0 @@
{
"organization" : "net.virtual-void",
"name" : "sbt-dependency-graph",
"version" : "0.7.3",
"description" : "An sbt plugin to visualize dependencies of your build.",
"site" : "http://github.com/jrudolph/sbt-dependency-graph",
"tags" : [ "dependency", "graph", "sbt-plugin", "sbt" ],
"docs" : "http://github.com/jrudolph/sbt-dependency-graph",
"resolvers" : [ "https://oss.sonatype.org/content/repositories/releases" ],
"dependencies" : [ {
"organization" : "org.scala-sbt",
"name" : "scripted-sbt",
"version" : "0.12.1"
}, {
"organization" : "org.scala-sbt",
"name" : "sbt-launch",
"version" : "0.12.1"
}, {
"organization" : "com.github.mdr",
"name" : "ascii-graphs",
"version" : "0.0.2"
} ],
"scalas" : [ "2.9.2" ],
"licenses" : [ {
"name" : "Apache License 2.0",
"url" : "https://github.com/jrudolph/sbt-dependency-graph/raw/master/LICENSE"
} ],
"sbt" : true
}

View File

@ -1,29 +0,0 @@
{
"organization" : "net.virtual-void",
"name" : "sbt-dependency-graph",
"version" : "0.7.4",
"description" : "An sbt plugin to visualize dependencies of your build.",
"site" : "http://github.com/jrudolph/sbt-dependency-graph",
"tags" : [ "dependency", "graph", "sbt-plugin", "sbt" ],
"docs" : "http://github.com/jrudolph/sbt-dependency-graph",
"resolvers" : [ "https://oss.sonatype.org/content/repositories/releases" ],
"dependencies" : [ {
"organization" : "org.scala-sbt",
"name" : "scripted-sbt",
"version" : "0.12.1"
}, {
"organization" : "org.scala-sbt",
"name" : "sbt-launch",
"version" : "0.12.1"
}, {
"organization" : "com.github.mdr",
"name" : "ascii-graphs",
"version" : "0.0.2"
} ],
"scalas" : [ "2.9.2" ],
"licenses" : [ {
"name" : "Apache License 2.0",
"url" : "https://github.com/jrudolph/sbt-dependency-graph/raw/master/LICENSE"
} ],
"sbt" : true
}

View File

@ -0,0 +1,3 @@
package net.virtualvoid.sbt.graph
trait ModuleGraphProtocolCompat

View File

@ -18,6 +18,8 @@ package net.virtualvoid.sbt.graph
package rendering package rendering
import com.github.mdr.ascii.layout._ import com.github.mdr.ascii.layout._
import net.virtualvoid.sbt.graph.DependencyGraphKeys._
import sbt.Keys._
object AsciiGraph { object AsciiGraph {
def asciiGraph(graph: ModuleGraph): String = def asciiGraph(graph: ModuleGraph): String =
@ -35,4 +37,23 @@ object AsciiGraph {
val edges = moduleGraph.edges.toList.map { case (from, to) (renderVertex(moduleGraph.module(from)), renderVertex(moduleGraph.module(to))) } val edges = moduleGraph.edges.toList.map { case (from, to) (renderVertex(moduleGraph.module(from)), renderVertex(moduleGraph.module(to))) }
Graph(vertices, edges) Graph(vertices, edges)
} }
def asciiGraphSetttings = Seq[sbt.Def.Setting[_]](
DependencyGraphKeys.asciiGraph := asciiGraph(moduleGraph.value),
dependencyGraph := {
val force = DependencyGraphSettings.shouldForceParser.parsed
val log = streams.value.log
if (force || moduleGraph.value.nodes.size < 15) {
log.info(rendering.AsciiGraph.asciiGraph(moduleGraph.value))
log.info("\n\n")
log.info("Note: The old tree layout is still available by using `dependency-tree`")
}
log.info(rendering.AsciiTree.asciiTree(moduleGraph.value))
if (!force) {
log.info("\n")
log.info("Note: The graph was estimated to be too big to display (> 15 nodes). Use `sbt 'dependency-graph --force'` (with the single quotes) to force graph display.")
}
})
} }

View File

@ -0,0 +1,81 @@
package sbt
package dependencygraph
import scala.language.implicitConversions
import java.util.concurrent.TimeUnit
import Keys._
import Def.Initialize
import CrossVersion._
import scala.concurrent.duration.FiniteDuration
object DependencyGraphSbtCompat {
object Implicits {
implicit def convertConfig(config: sbt.Configuration): String = config.toString
implicit class RichUpdateConfiguration(val updateConfig: UpdateConfiguration) extends AnyVal {
def withMissingOk(missingOk: Boolean): UpdateConfiguration =
updateConfig.copy(missingOk = missingOk)
}
}
/**
* This is copied directly from https://github.com/sbt/sbt/blob/2952a2b9b672c5402b824ad2d2076243eb643598/main/src/main/scala/sbt/Defaults.scala#L1471-L1523
* and then changed to update the UpdateConfiguration to ignore missing artifacts.
*/
def updateTask(task: TaskKey[_]): Initialize[Task[UpdateReport]] = Def.task {
val depsUpdated = transitiveUpdate.value.exists(!_.stats.cached)
val isRoot = executionRoots.value contains resolvedScoped.value
val forceUpdate = forceUpdatePeriod.value
val s = streams.value
val fullUpdateOutput = s.cacheDirectory / "out"
val forceUpdateByTime = forceUpdate match {
case None false
case Some(period)
val elapsedDuration = new FiniteDuration(System.currentTimeMillis() - fullUpdateOutput.lastModified(), TimeUnit.MILLISECONDS)
fullUpdateOutput.exists() && elapsedDuration > period
}
val scalaProvider = appConfiguration.value.provider.scalaProvider
// Only substitute unmanaged jars for managed jars when the major.minor parts of the versions the same for:
// 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 {
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] = Defaults.unmanagedScalaInstanceOnly.value match {
case Some(si) subUnmanaged(si.version, si.jars)
case None sv if (scalaProvider.version == sv) scalaProvider.jars else Nil
}
val transform: UpdateReport UpdateReport = r sbt.Classpaths.substituteScalaFiles(scalaOrganization.value, r)(subScalaJars)
val uwConfig = (unresolvedWarningConfiguration in update).value
val show = Reference.display(thisProjectRef.value)
val st = state.value
val logicalClock = LogicalClock(st.hashCode)
val depDir = dependencyCacheDirectory.value
val uc0 = (updateConfiguration in task).value
val ms = publishMavenStyle.value
val cw = compatibilityWarningOptions.value
// Normally, log would capture log messages at all levels.
// Ivy logs are treated specially using sbt.UpdateConfiguration.logging.
// This code bumps up the sbt.UpdateConfiguration.logging to Full when logLevel is Debug.
import UpdateLogging.{ Full, DownloadOnly, Default }
val uc = (logLevel in update).?.value orElse st.get(logLevel.key) match {
case Some(Level.Debug) if uc0.logging == Default uc0.copy(logging = Full)
case Some(x) if uc0.logging == Default uc0.copy(logging = DownloadOnly)
case _ uc0
}
val ewo =
if (executionRoots.value exists { _.key == evicted.key }) EvictionWarningOptions.empty
else (evictionWarningOptions in update).value
sbt.Classpaths.cachedUpdate(s.cacheDirectory / updateCacheName.value, show, ivyModule.value, uc, transform,
skip = (skip in update).value, force = isRoot || forceUpdateByTime, depsUpdated = depsUpdated,
uwConfig = uwConfig, logicalClock = logicalClock, depDir = Some(depDir),
ewo = ewo, mavenStyle = ms, compatWarning = cw, log = s.log)
}
}

View File

@ -0,0 +1,25 @@
package net.virtualvoid.sbt.graph
import java.io.{ ByteArrayInputStream, ByteArrayOutputStream, File }
import java.util.Base64
import sbinary.{ Format, JavaInput, JavaOutput }
import sjsonnew.{ Builder, Unbuilder }
trait ModuleGraphProtocolCompat {
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))
}
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2017 Johannes Rudolph
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.virtualvoid.sbt.graph
package rendering
object AsciiGraph {
def asciiGraphSetttings = Seq.empty[sbt.Def.Setting[_]]
}

View File

@ -0,0 +1,63 @@
package sbt
package dependencygraph
import Keys._
import Def.Initialize
import CrossVersion.partialVersion
import sbt.internal.LibraryManagement
object DependencyGraphSbtCompat {
object Implicits
// https://github.com/sbt/sbt/blob/4ce4fb72bde3b8acfaf526b79d32ca1463bc687b/main/src/main/scala/sbt/Defaults.scala#L2298 adapted
// to allow customization of UpdateConfiguration
def updateTask(task: TaskKey[_]): Initialize[Task[UpdateReport]] = Def.task {
val depsUpdated = transitiveUpdate.value.exists(!_.stats.cached)
val isRoot = executionRoots.value contains resolvedScoped.value
val s = streams.value
val scalaProvider = appConfiguration.value.provider.scalaProvider
// Only substitute unmanaged jars for managed jars when the major.minor parts of the versions the same for:
// 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 {
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.allJars)
case None sv if (scalaProvider.version == sv) scalaProvider.jars else Nil
}
val transform: UpdateReport UpdateReport =
r Classpaths.substituteScalaFiles(scalaOrganization.value, r)(subScalaJars)
val evictionOptions = Def.taskDyn {
if (executionRoots.value.exists(_.key == evicted.key))
Def.task(EvictionWarningOptions.empty)
else Def.task((evictionWarningOptions in update).value)
}.value
LibraryManagement.cachedUpdate(
// LM API
lm = dependencyResolution.value,
// Ivy-free ModuleDescriptor
module = ivyModule.value,
s.cacheStoreFactory.sub(updateCacheName.value),
Reference.display(thisProjectRef.value),
(updateConfiguration in task).value,
transform = transform,
skip = (skip in update).value,
force = isRoot,
depsUpdated = transitiveUpdate.value.exists(!_.stats.cached),
uwConfig = (unresolvedWarningConfiguration in update).value,
ewo = evictionOptions,
mavenStyle = publishMavenStyle.value,
compatWarning = compatibilityWarningOptions.value,
log = s.log)
}
}

View File

@ -19,53 +19,76 @@ package net.virtualvoid.sbt.graph
import sbt._ import sbt._
trait DependencyGraphKeys { trait DependencyGraphKeys {
val dependencyGraphMLFile = SettingKey[File]("dependency-graph-ml-file", val dependencyGraphMLFile = SettingKey[File](
"dependency-graph-ml-file",
"The location the graphml file should be generated at") "The location the graphml file should be generated at")
val dependencyGraphML = TaskKey[File]("dependency-graph-ml", val dependencyGraphML = TaskKey[File](
"dependency-graph-ml",
"Creates a graphml file containing the dependency-graph for a project") "Creates a graphml file containing the dependency-graph for a project")
val dependencyDotFile = SettingKey[File]("dependency-dot-file", val dependencyDotFile = SettingKey[File](
"dependency-dot-file",
"The location the dot file should be generated at") "The location the dot file should be generated at")
val dependencyDotNodeLabel = SettingKey[(String, String, String) String]("dependency-dot-node-label", val dependencyDotNodeLabel = SettingKey[(String, String, String) String](
"dependency-dot-node-label",
"Returns a formated string of a dependency. Takes organisation, name and version as parameters") "Returns a formated string of a dependency. Takes organisation, name and version as parameters")
val dependencyDotHeader = SettingKey[String]("dependency-dot-header", val dependencyDotHeader = SettingKey[String](
"dependency-dot-header",
"The header of the dot file. (e.g. to set your preferred node shapes)") "The header of the dot file. (e.g. to set your preferred node shapes)")
val dependencyDot = TaskKey[File]("dependency-dot", val dependencyDot = TaskKey[File](
"dependency-dot",
"Creates a dot file containing the dependency-graph for a project") "Creates a dot file containing the dependency-graph for a project")
val dependencyDotString = TaskKey[String]("dependency-dot-string", val dependencyDotString = TaskKey[String](
"dependency-dot-string",
"Creates a String containing the dependency-graph for a project in dot format") "Creates a String containing the dependency-graph for a project in dot format")
val dependencyBrowseGraphTarget = SettingKey[File]("dependency-browse-graph-target", val dependencyBrowseGraphTarget = SettingKey[File](
"dependency-browse-graph-target",
"The location dependency browse graph files should be put.") "The location dependency browse graph files should be put.")
val dependencyBrowseGraphHTML = TaskKey[URI]("dependency-browse-graph-html", val dependencyBrowseGraphHTML = TaskKey[URI](
"dependency-browse-graph-html",
"Creates an HTML page that can be used to view the graph.") "Creates an HTML page that can be used to view the graph.")
val dependencyBrowseGraph = TaskKey[URI]("dependency-browse-graph", val dependencyBrowseGraph = TaskKey[URI](
"dependency-browse-graph",
"Opens an HTML page that can be used to view the graph.") "Opens an HTML page that can be used to view the graph.")
val moduleGraph = TaskKey[ModuleGraph]("module-graph", val moduleGraph = TaskKey[ModuleGraph](
"module-graph",
"The dependency graph for a project") "The dependency graph for a project")
val moduleGraphIvyReport = TaskKey[ModuleGraph]("module-graph-ivy-report", val moduleGraphIvyReport = TaskKey[ModuleGraph](
"module-graph-ivy-report",
"The dependency graph for a project as generated from an Ivy Report XML") "The dependency graph for a project as generated from an Ivy Report XML")
val moduleGraphSbt = TaskKey[ModuleGraph]("module-graph-sbt", val moduleGraphSbt = TaskKey[ModuleGraph](
"module-graph-sbt",
"The dependency graph for a project as generated from SBT data structures.") "The dependency graph for a project as generated from SBT data structures.")
val asciiGraph = TaskKey[String]("dependency-graph-string", val asciiGraph = TaskKey[String](
"dependency-graph-string",
"Returns a string containing the ascii representation of the dependency graph for a project") "Returns a string containing the ascii representation of the dependency graph for a project")
val dependencyGraph = InputKey[Unit]("dependency-graph", val dependencyGraph = InputKey[Unit](
"dependency-graph",
"Prints the ascii graph to the console") "Prints the ascii graph to the console")
val asciiTree = TaskKey[String]("dependency-tree-string", val asciiTree = TaskKey[String](
"dependency-tree-string",
"Returns a string containing an ascii tree representation of the dependency graph for a project") "Returns a string containing an ascii tree representation of the dependency graph for a project")
val dependencyTree = TaskKey[Unit]("dependency-tree", val dependencyTree = TaskKey[Unit](
"dependency-tree",
"Prints an ascii tree of all the dependencies to the console") "Prints an ascii tree of all the dependencies to the console")
val dependencyList = TaskKey[Unit]("dependency-list", val dependencyList = TaskKey[Unit](
"dependency-list",
"Prints a list of all dependencies to the console") "Prints a list of all dependencies to the console")
val dependencyStats = TaskKey[Unit]("dependency-stats", val dependencyStats = TaskKey[Unit](
"dependency-stats",
"Prints statistics for all dependencies to the console") "Prints statistics for all dependencies to the console")
val ivyReportFunction = TaskKey[String File]("ivy-report-function", val ivyReportFunction = TaskKey[String File](
"ivy-report-function",
"A function which returns the file containing the ivy report from the ivy cache for a given configuration") "A function which returns the file containing the ivy report from the ivy cache for a given configuration")
val ivyReport = TaskKey[File]("ivy-report", val ivyReport = TaskKey[File](
"ivy-report",
"A task which returns the location of the ivy report file for a given configuration (default `compile`).") "A task which returns the location of the ivy report file for a given configuration (default `compile`).")
val ignoreMissingUpdate = Keys.update in ivyReport val ignoreMissingUpdate = Keys.update in ivyReport
val filterScalaLibrary = SettingKey[Boolean]("filter-scala-library", val filterScalaLibrary = SettingKey[Boolean](
"filter-scala-library",
"Specifies if scala dependency should be filtered in dependency-* output") "Specifies if scala dependency should be filtered in dependency-* output")
val licenseInfo = TaskKey[Unit]("dependency-license-info", val licenseInfo = TaskKey[Unit](
"dependency-license-info",
"Aggregates and shows information about the licenses of dependencies") "Aggregates and shows information about the licenses of dependencies")
// internal // internal

View File

@ -16,157 +16,142 @@
package net.virtualvoid.sbt.graph package net.virtualvoid.sbt.graph
import scala.language.reflectiveCalls
import sbt._ import sbt._
import Keys._ import Keys._
import CrossVersion._
import sbt.complete.Parser import sbt.complete.Parser
import org.apache.ivy.core.resolve.ResolveOptions
import net.virtualvoid.sbt.graph.backend.{ IvyReport, SbtUpdateReport } import net.virtualvoid.sbt.graph.backend.{ IvyReport, SbtUpdateReport }
import net.virtualvoid.sbt.graph.rendering.DagreHTML import net.virtualvoid.sbt.graph.rendering.{ AsciiGraph, DagreHTML }
import net.virtualvoid.sbt.graph.util.IOUtil import net.virtualvoid.sbt.graph.util.IOUtil
import internal.librarymanagement._
import librarymanagement._
import sbt.dependencygraph.DependencyGraphSbtCompat
import sbt.dependencygraph.DependencyGraphSbtCompat.Implicits._
object DependencyGraphSettings { object DependencyGraphSettings {
import DependencyGraphKeys._ import DependencyGraphKeys._
import ModuleGraphProtocol._ import ModuleGraphProtocol._
def graphSettings = Seq( def graphSettings = baseSettings ++ reportSettings
ivyReportFunction <<= ivyReportFunctionTask,
updateConfiguration in ignoreMissingUpdate <<= updateConfiguration(config new UpdateConfiguration(config.retrieve, true, config.logging)), def baseSettings = Seq(
ignoreMissingUpdateT, ivyReportFunction := ivyReportFunctionTask.value,
filterScalaLibrary in Global := true) ++ Seq(Compile, Test, IntegrationTest, Runtime, Provided, Optional).flatMap(ivyReportForConfig) updateConfiguration in ignoreMissingUpdate := updateConfiguration.value.withMissingOk(true),
ignoreMissingUpdate := DependencyGraphSbtCompat.updateTask(ignoreMissingUpdate).value,
filterScalaLibrary in Global := true)
def reportSettings =
Seq(Compile, Test, IntegrationTest, Runtime, Provided, Optional).flatMap(ivyReportForConfig)
def ivyReportForConfig(config: Configuration) = inConfig(config)(Seq( def ivyReportForConfig(config: Configuration) = inConfig(config)(Seq(
ivyReport <<= ivyReportFunction map (_(config.toString)) dependsOn (ignoreMissingUpdate), ivyReport := { Def.task { ivyReportFunction.value.apply(config.toString) } dependsOn (ignoreMissingUpdate) }.value,
crossProjectId <<= (scalaVersion, scalaBinaryVersion, projectID)((sV, sBV, id) CrossVersion(sV, sBV)(id)), crossProjectId := sbt.CrossVersion(scalaVersion.value, scalaBinaryVersion.value)(projectID.value),
moduleGraphSbt <<= moduleGraphSbtTask, moduleGraphSbt :=
moduleGraphIvyReport <<= moduleGraphIvyReportTask, ignoreMissingUpdate.value.configuration(configuration.value).map(report SbtUpdateReport.fromConfigurationReport(report, crossProjectId.value)).getOrElse(ModuleGraph.empty),
moduleGraph <<= (sbtVersion, moduleGraphSbt, moduleGraphIvyReport) { (version, graphSbt, graphIvy) moduleGraphIvyReport := IvyReport.fromReportFile(absoluteReportPath(ivyReport.value)),
version match { moduleGraph := {
case Version(0, 13, x, _) if x >= 6 graphSbt sbtVersion.value match {
case _ graphIvy case Version(0, 13, x, _) if x >= 6 moduleGraphSbt.value
case Version(1, _, _, _) moduleGraphSbt.value
} }
}, },
moduleGraph <<= (scalaVersion, moduleGraph, filterScalaLibrary) map { (scalaV, graph, filter) moduleGraph := {
if (filter) GraphTransformations.ignoreScalaLibrary(scalaV, graph) // FIXME: remove busywork
else graph val scalaVersion = Keys.scalaVersion.value
}, val moduleGraph = DependencyGraphKeys.moduleGraph.value
moduleGraphStore <<= moduleGraph storeAs moduleGraphStore triggeredBy moduleGraph,
asciiGraph <<= moduleGraph map rendering.AsciiGraph.asciiGraph,
dependencyGraph <<= InputTask(shouldForceParser) { force
(force, moduleGraph, streams) map { (force, graph, streams)
if (force || graph.nodes.size < 15) {
streams.log.info(rendering.AsciiGraph.asciiGraph(graph))
streams.log.info("\n\n")
streams.log.info("Note: The old tree layout is still available by using `dependency-tree`")
} else {
streams.log.info(rendering.AsciiTree.asciiTree(graph))
if (!force) { if (filterScalaLibrary.value) GraphTransformations.ignoreScalaLibrary(scalaVersion, moduleGraph)
streams.log.info("\n") else moduleGraph
streams.log.info("Note: The graph was estimated to be too big to display (> 15 nodes). Use `sbt 'dependency-graph --force'` (with the single quotes) to force graph display.")
}
}
}
}, },
asciiTree <<= moduleGraph map rendering.AsciiTree.asciiTree, moduleGraphStore := (moduleGraph storeAs moduleGraphStore triggeredBy moduleGraph).value,
dependencyTree <<= print(asciiTree), asciiTree := rendering.AsciiTree.asciiTree(moduleGraph.value),
dependencyGraphMLFile <<= target / "dependencies-%s.graphml".format(config.toString), dependencyTree := print(asciiTree).value,
dependencyGraphML <<= dependencyGraphMLTask, dependencyGraphMLFile := { target.value / "dependencies-%s.graphml".format(config.toString) },
dependencyDotFile <<= target / "dependencies-%s.dot".format(config.toString), dependencyGraphML := dependencyGraphMLTask.value,
dependencyDotString <<= dependencyDotStringTask, dependencyDotFile := { target.value / "dependencies-%s.dot".format(config.toString) },
dependencyDot <<= writeToFile(dependencyDotString, dependencyDotFile), dependencyDotString := rendering.DOT.dotGraph(moduleGraph.value, dependencyDotHeader.value, dependencyDotNodeLabel.value, rendering.DOT.AngleBrackets),
dependencyBrowseGraphTarget <<= target / "browse-dependency-graph", dependencyDot := writeToFile(dependencyDotString, dependencyDotFile).value,
dependencyBrowseGraphHTML <<= browseGraphHTMLTask, dependencyBrowseGraphTarget := { target.value / "browse-dependency-graph" },
dependencyBrowseGraph <<= (dependencyBrowseGraphHTML, streams).map { (uri, streams) dependencyBrowseGraphHTML := browseGraphHTMLTask.value,
streams.log.info("Opening in browser...") dependencyBrowseGraph := {
val uri = dependencyBrowseGraphHTML.value
streams.value.log.info("Opening in browser...")
java.awt.Desktop.getDesktop.browse(uri) java.awt.Desktop.getDesktop.browse(uri)
uri uri
}, },
dependencyList <<= printFromGraph(rendering.FlatList.render(_, _.id.idString)), dependencyList := printFromGraph(rendering.FlatList.render(_, _.id.idString)).value,
dependencyStats <<= printFromGraph(rendering.Statistics.renderModuleStatsList), dependencyStats := printFromGraph(rendering.Statistics.renderModuleStatsList).value,
dependencyDotHeader := """digraph "dependency-graph" { dependencyDotHeader :=
| graph[rankdir="LR"] """|digraph "dependency-graph" {
| edge [ | graph[rankdir="LR"]
| arrowtail="none" | edge [
| ]""".stripMargin, | arrowtail="none"
| ]""".stripMargin,
dependencyDotNodeLabel := { (organisation: String, name: String, version: String) dependencyDotNodeLabel := { (organisation: String, name: String, version: String)
"""%s<BR/><B>%s</B><BR/>%s""".format(organisation, name, version) """%s<BR/><B>%s</B><BR/>%s""".format(organisation, name, version)
}, },
whatDependsOn <<= InputTask(artifactIdParser) { module whatDependsOn := {
(module, streams, moduleGraph) map { (module, streams, graph) val ArtifactPattern(org, name, versionFilter) = artifactPatternParser.parsed
if (module.version.isEmpty) { val graph = moduleGraph.value
val modules = graph.reverseDependencyMap.filter(m m._1.copy(version = "") == module).keys val modules =
modules.foreach(m streams.log.info(rendering.AsciiTree.asciiTree(GraphTransformations.reverseGraphStartingAt(graph, m)))) versionFilter match {
} else streams.log.info(rendering.AsciiTree.asciiTree(GraphTransformations.reverseGraphStartingAt(graph, module))) case Some(version) ModuleId(org, name, version) :: Nil
} case None graph.nodes.filter(m m.id.organisation == org && m.id.name == name).map(_.id)
}
modules
.foreach { module
streams.value.log.info(rendering.AsciiTree.asciiTree(GraphTransformations.reverseGraphStartingAt(graph, module)))
}
}, },
licenseInfo <<= (moduleGraph, streams) map showLicenseInfo)) licenseInfo := showLicenseInfo(moduleGraph.value, streams.value)) ++ AsciiGraph.asciiGraphSetttings)
def ivyReportFunctionTask = def ivyReportFunctionTask = Def.task {
(sbtVersion, target, projectID, ivyModule, appConfiguration, streams) map { (sbtV, target, projectID, ivyModule, config, streams) val crossTarget = Keys.crossTarget.value
sbtV match { val projectID = Keys.projectID.value
case Version(0, min, fix, _) if min > 12 || (min == 12 && fix >= 3) val ivyModule = Keys.ivyModule.value
(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 (config: String) {
ivyModule.withModule(streams.log) { (i, moduleDesc, _) val org = projectID.organization
val id = ResolveOptions.getDefaultResolveId(moduleDesc) val name = crossName(ivyModule)
(c: String) file("%s/resolution-cache/reports/%s/%s-resolved.xml" format (target, id, c)) file(s"${crossTarget}/resolution-cache/reports/$org-$name-$config.xml")
}
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 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 = def dependencyGraphMLTask =
(moduleGraph, dependencyGraphMLFile, streams) map { (graph, resultFile, streams) Def.task {
rendering.GraphML.saveAsGraphML(graph, resultFile.getAbsolutePath) val resultFile = dependencyGraphMLFile.value
streams.log.info("Wrote dependency graph to '%s'" format resultFile) rendering.GraphML.saveAsGraphML(moduleGraph.value, resultFile.getAbsolutePath)
streams.value.log.info("Wrote dependency graph to '%s'" format resultFile)
resultFile resultFile
} }
def dependencyDotStringTask =
(moduleGraph, dependencyDotHeader, dependencyDotNodeLabel).map {
(graph, dotHead, nodeLabel) rendering.DOT.dotGraph(graph, dotHead, nodeLabel, rendering.DOT.AngleBrackets)
}
def browseGraphHTMLTask = def browseGraphHTMLTask =
(moduleGraph, dependencyDotHeader, dependencyDotNodeLabel, dependencyBrowseGraphTarget, streams).map { (graph, dotHead, nodeLabel, target, streams) Def.task {
val dotGraph = rendering.DOT.dotGraph(graph, dotHead, nodeLabel, rendering.DOT.LabelTypeHtml) val dotGraph = rendering.DOT.dotGraph(moduleGraph.value, dependencyDotHeader.value, dependencyDotNodeLabel.value, rendering.DOT.LabelTypeHtml)
val link = DagreHTML.createLink(dotGraph, target) val link = DagreHTML.createLink(dotGraph, target.value)
streams.log.info(s"HTML graph written to $link") streams.value.log.info(s"HTML graph written to $link")
link link
} }
def writeToFile(dataTask: TaskKey[String], fileTask: SettingKey[File]) = def writeToFile(dataTask: TaskKey[String], fileTask: SettingKey[File]) =
(dataTask, fileTask, streams).map { (data, outFile, streams) Def.task {
IOUtil.writeToFile(data, outFile) 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 outFile
} }
def absoluteReportPath = (file: File) file.getAbsolutePath def absoluteReportPath = (file: File) file.getAbsolutePath
def print(key: TaskKey[String]) = def print(key: TaskKey[String]) =
(streams, key) map (_.log.info(_)) Def.task { streams.value.log.info(key.value) }
def printFromGraph(f: ModuleGraph String) = 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) { def showLicenseInfo(graph: ModuleGraph, streams: TaskStreams): Unit = {
val output = val output =
graph.nodes.filter(_.isUsed).groupBy(_.license).toSeq.sortBy(_._1).map { graph.nodes.filter(_.isUsed).groupBy(_.license).toSeq.sortBy(_._1).map {
case (license, modules) case (license, modules)
@ -183,24 +168,32 @@ object DependencyGraphSettings {
(Space ~> token("--force")).?.map(_.isDefined) (Space ~> token("--force")).?.map(_.isDefined)
} }
val artifactIdParser: Initialize[State Parser[ModuleId]] = case class ArtifactPattern(
resolvedScoped { ctx organisation: String,
(state: State) name: String,
val graph = loadFromContext(moduleGraphStore, ctx, state) getOrElse ModuleGraph(Nil, Nil) version: Option[String])
import sbt.complete.DefaultParsers._ val artifactPatternParser: Def.Initialize[State Parser[ArtifactPattern]] =
graph.nodes.map(_.id).map { resolvedScoped { ctx (state: State)
case id @ ModuleId(org, name, version) val graph = loadFromContext(moduleGraphStore, ctx, state) getOrElse ModuleGraph(Nil, Nil)
(Space ~ token(org) ~ token(Space ~ name) ~ token(Space ~ version).?).map(_._2 match {
case Some(_) id import sbt.complete.DefaultParsers._
case None id.copy(version = "") graph.nodes
}) .map(_.id)
}.reduceOption(_ | _).getOrElse { .groupBy(m (m.organisation, m.name))
(Space ~> token(StringBasic, "organization") ~ Space ~ token(StringBasic, "module") ~ (Space ~ token(StringBasic, "version")).?).map { .map {
case (((org, _), mod), version) case ((org, name), modules)
ModuleId(org, mod, version.map(_._2).getOrElse("")) val versionParsers: Seq[Parser[Option[String]]] =
} modules.map { id
token(Space ~> id.version).?
}
(Space ~> token(org) ~ token(Space ~> name) ~ oneOf(versionParsers)).map {
case ((org, name), version) ArtifactPattern(org, name, version)
}
} }
.reduceOption(_ | _).getOrElse(failure("No dependencies found"))
} }
// This is to support 0.13.8's InlineConfigurationWithExcludes while not forcing 0.13.8 // This is to support 0.13.8's InlineConfigurationWithExcludes while not forcing 0.13.8
@ -222,34 +215,4 @@ object DependencyGraphSettings {
case _ None case _ None
} }
} }
/**
* This is copied directly from sbt/main/Defaults.java and then changed to update the UpdateConfiguration
* to ignore missing artifacts.
*/
def ignoreMissingUpdateT =
ignoreMissingUpdate <<= Def.task {
val depsUpdated = transitiveUpdate.value.exists(!_.stats.cached)
val isRoot = executionRoots.value contains resolvedScoped.value
val s = streams.value
val scalaProvider = appConfiguration.value.provider.scalaProvider
// Only substitute unmanaged jars for managed jars when the major.minor parts of the versions the same for:
// 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 {
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)
case None sv if (scalaProvider.version == sv) scalaProvider.jars else Nil
}
val transform: UpdateReport UpdateReport = r Classpaths.substituteScalaFiles(scalaOrganization.value, 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)
}
} }

View File

@ -36,7 +36,8 @@ object IvyReport {
revision mod \ "revision" revision mod \ "revision"
rev = revision.attribute("name").get.text rev = revision.attribute("name").get.text
moduleId = moduleIdFromElement(mod, rev) moduleId = moduleIdFromElement(mod, rev)
module = Module(moduleId, module = Module(
moduleId,
(revision \ "license").headOption.flatMap(_.attribute("name")).map(_.text), (revision \ "license").headOption.flatMap(_.attribute("name")).map(_.text),
evictedByVersion = (revision \ "evicted-by").headOption.flatMap(_.attribute("rev").map(_.text)), evictedByVersion = (revision \ "evicted-by").headOption.flatMap(_.attribute("rev").map(_.text)),
error = revision.attribute("error").map(_.text)) error = revision.attribute("error").map(_.text))

View File

@ -17,6 +17,9 @@
package net.virtualvoid.sbt.graph package net.virtualvoid.sbt.graph
package backend package backend
import scala.language.implicitConversions
import scala.language.reflectiveCalls
import sbt._ import sbt._
object SbtUpdateReport { object SbtUpdateReport {
@ -35,7 +38,8 @@ object SbtUpdateReport {
def moduleEdge(chosenVersion: Option[String])(report: ModuleReport): (Module, Seq[Edge]) = { def moduleEdge(chosenVersion: Option[String])(report: ModuleReport): (Module, Seq[Edge]) = {
val evictedByVersion = if (report.evicted) chosenVersion else None val evictedByVersion = if (report.evicted) chosenVersion else None
val jarFile = report.artifacts.find(_._1.`type` == "jar").orElse(report.artifacts.find(_._1.extension == "jar")).map(_._2) val jarFile = report.artifacts.find(_._1.`type` == "jar").orElse(report.artifacts.find(_._1.extension == "jar")).map(_._2)
(Module( (
Module(
id = report.module, id = report.module,
license = report.licenses.headOption.map(_._1), license = report.licenses.headOption.map(_._1),
evictedByVersion = evictedByVersion, evictedByVersion = evictedByVersion,

View File

@ -18,19 +18,23 @@ package net.virtualvoid.sbt.graph
import java.io.File import java.io.File
import scala.collection.mutable.{ MultiMap, HashMap, Set } import sbinary.Format
case class ModuleId(organisation: String, import scala.collection.mutable.{ HashMap, MultiMap, Set }
name: String,
version: String) { case class ModuleId(
organisation: String,
name: String,
version: String) {
def idString: String = organisation + ":" + name + ":" + version def idString: String = organisation + ":" + name + ":" + version
} }
case class Module(id: ModuleId, case class Module(
license: Option[String] = None, id: ModuleId,
extraInfo: String = "", license: Option[String] = None,
evictedByVersion: Option[String] = None, extraInfo: String = "",
jarFile: Option[File] = None, evictedByVersion: Option[String] = None,
error: Option[String] = None) { jarFile: Option[File] = None,
error: Option[String] = None) {
def hadError: Boolean = error.isDefined def hadError: Boolean = error.isDefined
def isUsed: Boolean = !isEvicted def isUsed: Boolean = !isEvicted
def isEvicted: Boolean = evictedByVersion.isDefined def isEvicted: Boolean = evictedByVersion.isDefined
@ -65,8 +69,9 @@ case class ModuleGraph(nodes: Seq[Module], edges: Seq[Edge]) {
nodes.filter(n !edges.exists(_._2 == n.id)).sortBy(_.id.idString) nodes.filter(n !edges.exists(_._2 == n.id)).sortBy(_.id.idString)
} }
import sbinary.{ Format, DefaultProtocol } object ModuleGraphProtocol extends ModuleGraphProtocolCompat {
object ModuleGraphProtocol extends DefaultProtocol { import sbinary.DefaultProtocol._
implicit def seqFormat[T: Format]: Format[Seq[T]] = wrap[Seq[T], List[T]](_.toList, _.toSeq) implicit def seqFormat[T: Format]: Format[Seq[T]] = wrap[Seq[T], List[T]](_.toList, _.toSeq)
implicit val ModuleIdFormat: Format[ModuleId] = asProduct3(ModuleId)(ModuleId.unapply(_).get) implicit val ModuleIdFormat: Format[ModuleId] = asProduct3(ModuleId)(ModuleId.unapply(_).get)
implicit val ModuleFormat: Format[Module] = asProduct6(Module)(Module.unapply(_).get) implicit val ModuleFormat: Format[Module] = asProduct6(Module)(Module.unapply(_).get)

View File

@ -20,15 +20,17 @@ package rendering
object DOT { object DOT {
val EvictedStyle = "stroke-dasharray: 5,5" val EvictedStyle = "stroke-dasharray: 5,5"
def dotGraph(graph: ModuleGraph, def dotGraph(
dotHead: String, graph: ModuleGraph,
nodeFormation: (String, String, String) String, dotHead: String,
labelRendering: HTMLLabelRendering): String = { nodeFormation: (String, String, String) String,
labelRendering: HTMLLabelRendering): String = {
val nodes = { val nodes = {
for (n graph.nodes) yield { for (n graph.nodes) yield {
val style = if (n.isEvicted) EvictedStyle else "" val style = if (n.isEvicted) EvictedStyle else ""
val label = nodeFormation(n.id.organisation, n.id.name, n.id.version) val label = nodeFormation(n.id.organisation, n.id.name, n.id.version)
""" "%s"[%s style="%s"]""".format(n.id.idString, """ "%s"[%s style="%s"]""".format(
n.id.idString,
labelRendering.renderLabel(label), labelRendering.renderLabel(label),
style) style)
} }

View File

@ -21,7 +21,7 @@ import net.virtualvoid.sbt.graph.ModuleGraph
import scala.xml.XML import scala.xml.XML
object GraphML { object GraphML {
def saveAsGraphML(graph: ModuleGraph, outputFile: String) { def saveAsGraphML(graph: ModuleGraph, outputFile: String): Unit = {
val nodesXml = val nodesXml =
for (n graph.nodes) for (n graph.nodes)
yield <node id={ n.id.idString }><data key="d0"> yield <node id={ n.id.idString }><data key="d0">

View File

@ -20,12 +20,12 @@ package rendering
object Statistics { object Statistics {
def renderModuleStatsList(graph: ModuleGraph): String = { def renderModuleStatsList(graph: ModuleGraph): String = {
case class ModuleStats( case class ModuleStats(
id: ModuleId, id: ModuleId,
numDirectDependencies: Int, numDirectDependencies: Int,
numTransitiveDependencies: Int, numTransitiveDependencies: Int,
selfSize: Option[Long], selfSize: Option[Long],
transitiveSize: Long, transitiveSize: Long,
transitiveDependencyStats: Map[ModuleId, ModuleStats]) { transitiveDependencyStats: Map[ModuleId, ModuleStats]) {
def transitiveStatsWithSelf: Map[ModuleId, ModuleStats] = transitiveDependencyStats + (id -> this) def transitiveStatsWithSelf: Map[ModuleId, ModuleStats] = transitiveDependencyStats + (id -> this)
} }

View File

@ -5,16 +5,19 @@
*/ */
package net.virtualvoid.sbt.graph.util package net.virtualvoid.sbt.graph.util
import sbt.dependencygraph.SbtAccess
object AsciiTreeLayout { object AsciiTreeLayout {
// [info] foo // [info] foo
// [info] +-bar // [info] +-bar
// [info] | +-baz // [info] | +-baz
// [info] | // [info] |
// [info] +-quux // [info] +-quux
def toAscii[A](top: A, def toAscii[A](
children: A Seq[A], top: A,
display: A String, children: A Seq[A],
maxColumn: Int = defaultColumnSize): String = { display: A String,
maxColumn: Int = defaultColumnSize): String = {
val twoSpaces = " " + " " // prevent accidentally being converted into a tab val twoSpaces = " " + " " // prevent accidentally being converted into a tab
def limitLine(s: String): String = def limitLine(s: String): String =
if (s.length > maxColumn) s.slice(0, maxColumn - 2) + ".." if (s.length > maxColumn) s.slice(0, maxColumn - 2) + ".."
@ -52,7 +55,7 @@ object AsciiTreeLayout {
} }
def defaultColumnSize: Int = { def defaultColumnSize: Int = {
val termWidth = sbt.SbtAccess.getTerminalWidth val termWidth = SbtAccess.getTerminalWidth
if (termWidth > 20) termWidth - 8 if (termWidth > 20) termWidth - 8
else 80 // ignore termWidth else 80 // ignore termWidth
} }

View File

@ -14,11 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
package sbt package sbt.dependencygraph
import sbt.Defaults
/** Accessors to private[sbt] symbols. */ /** Accessors to private[sbt] symbols. */
object SbtAccess { object SbtAccess {
val unmanagedScalaInstanceOnly = Defaults.unmanagedScalaInstanceOnly val unmanagedScalaInstanceOnly = Defaults.unmanagedScalaInstanceOnly
def getTerminalWidth: Int = JLine.usingTerminal(_.getWidth) def getTerminalWidth: Int = sbt.internal.util.JLine.usingTerminal(_.getWidth)
} }

View File

@ -1 +0,0 @@
sbt.version=0.13.6

View File

@ -5,7 +5,9 @@ libraryDependencies ++= Seq(
"ch.qos.logback" % "logback-classic" % "1.0.7" "ch.qos.logback" % "logback-classic" % "1.0.7"
) )
TaskKey[Unit]("check") <<= (ivyReport in Test, asciiTree in Test) map { (report, graph) => TaskKey[Unit]("check") := {
val report = (ivyReport in Test).value
val graph = (asciiTree in Test).value
def sanitize(str: String): String = str.split('\n').drop(1).map(_.trim).mkString("\n") def sanitize(str: String): String = str.split('\n').drop(1).map(_.trim).mkString("\n")
val expectedGraph = val expectedGraph =
"""default:default-e95e05_2.9.2:0.1-SNAPSHOT [S] """default:default-e95e05_2.9.2:0.1-SNAPSHOT [S]

View File

@ -1,17 +1,22 @@
scalaVersion := "2.9.1" scalaVersion := "2.9.1"
resolvers += "typesafe maven" at "https://repo.typesafe.com/typesafe/maven-releases/"
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"com.codahale" % "jerkson_2.9.1" % "0.5.0" "com.codahale" % "jerkson_2.9.1" % "0.5.0"
) )
TaskKey[Unit]("check") <<= (ivyReport in Test, asciiTree in Test) map { (report, graph) => TaskKey[Unit]("check") := {
val report = (ivyReport in Test).value
val graph = (asciiTree in Test).value
def sanitize(str: String): String = str.split('\n').drop(1).map(_.trim).mkString("\n") def sanitize(str: String): String = str.split('\n').drop(1).map(_.trim).mkString("\n")
val expectedGraph = val expectedGraph =
"""default:default-dbc48d_2.9.2:0.1-SNAPSHOT [S] """default:default-dbc48d_2.9.2:0.1-SNAPSHOT [S]
| +-com.codahale:jerkson_2.9.1:0.5.0 [S] | +-com.codahale:jerkson_2.9.1:0.5.0 [S]
| +-org.codehaus.jackson:jackson-core-asl:1.9.13 | +-org.codehaus.jackson:jackson-core-asl:1.9.11
| +-org.codehaus.jackson:jackson-mapper-asl:1.9.13 | +-org.codehaus.jackson:jackson-mapper-asl:1.9.11
| +-org.codehaus.jackson:jackson-core-asl:1.9.13 | +-org.codehaus.jackson:jackson-core-asl:1.9.11
| """.stripMargin | """.stripMargin
IO.writeLines(file("/tmp/blib"), sanitize(graph).split("\n")) IO.writeLines(file("/tmp/blib"), sanitize(graph).split("\n"))
IO.writeLines(file("/tmp/blub"), sanitize(expectedGraph).split("\n")) IO.writeLines(file("/tmp/blub"), sanitize(expectedGraph).split("\n"))

View File

@ -1 +1 @@
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.3-SNAPSHOT") addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % sys.props("project.version"))

View File

@ -3,7 +3,10 @@ scalaVersion := "2.9.2"
libraryDependencies += libraryDependencies +=
"at.blub" % "blib" % "1.2.3" % "test" "at.blub" % "blib" % "1.2.3" % "test"
TaskKey[Unit]("check") <<= (ivyReport in Test, asciiTree in Test) map { (report, graph) => TaskKey[Unit]("check") := {
val report = (ivyReport in Test).value
val graph = (asciiTree in Test).value
def sanitize(str: String): String = str.split('\n').drop(1).mkString("\n") def sanitize(str: String): String = str.split('\n').drop(1).mkString("\n")
val expectedGraph = val expectedGraph =
"""default:default-91180e_2.9.2:0.1-SNAPSHOT """default:default-91180e_2.9.2:0.1-SNAPSHOT

View File

@ -0,0 +1,68 @@
import collection.mutable.ListBuffer
import net.virtualvoid.sbt.graph.DependencyGraphKeys.dependencyDot
import scala.collection.mutable.ListBuffer
def defaultSettings =
Seq(
scalaVersion := "2.9.2",
version := "0.1-SNAPSHOT"
)
lazy val justATransiviteDependencyEndpointProject =
Project("just-a-transitive-dependency-endpoint", file("a"))
.settings(defaultSettings: _*)
lazy val justATransitiveDependencyProject =
Project("just-a-transitive-dependency", file("b"))
.settings(defaultSettings: _*)
.dependsOn(justATransiviteDependencyEndpointProject)
lazy val justADependencyProject =
Project("just-a-dependency", file("c"))
.settings(defaultSettings: _*)
lazy val test_project =
Project("test-dot-file-generation", file("d"))
.settings(defaultSettings: _*)
.settings(
TaskKey[Unit]("check") := {
val dotFile = (dependencyDot in Compile).value
val expectedGraph =
"""digraph "dependency-graph" {
| graph[rankdir="LR"]
| edge [
| arrowtail="none"
| ]
| "test-dot-file-generation:test-dot-file-generation_2.9.2:0.1-SNAPSHOT"[label=<test-dot-file-generation<BR/><B>test-dot-file-generation_2.9.2</B><BR/>0.1-SNAPSHOT> style=""]
| "just-a-transitive-dependency:just-a-transitive-dependency_2.9.2:0.1-SNAPSHOT"[label=<just-a-transitive-dependency<BR/><B>just-a-transitive-dependency_2.9.2</B><BR/>0.1-SNAPSHOT> style=""]
| "just-a-transitive-dependency-endpoint:just-a-transitive-dependency-endpoint_2.9.2:0.1-SNAPSHOT"[label=<just-a-transitive-dependency-endpoint<BR/><B>just-a-transitive-dependency-endpoint_2.9.2</B><BR/>0.1-SNAPSHOT> style=""]
| "just-a-dependency:just-a-dependency_2.9.2:0.1-SNAPSHOT"[label=<just-a-dependency<BR/><B>just-a-dependency_2.9.2</B><BR/>0.1-SNAPSHOT> style=""]
| "test-dot-file-generation:test-dot-file-generation_2.9.2:0.1-SNAPSHOT" -> "just-a-transitive-dependency:just-a-transitive-dependency_2.9.2:0.1-SNAPSHOT"
| "just-a-transitive-dependency:just-a-transitive-dependency_2.9.2:0.1-SNAPSHOT" -> "just-a-transitive-dependency-endpoint:just-a-transitive-dependency-endpoint_2.9.2:0.1-SNAPSHOT"
| "test-dot-file-generation:test-dot-file-generation_2.9.2:0.1-SNAPSHOT" -> "just-a-dependency:just-a-dependency_2.9.2:0.1-SNAPSHOT"
|}
""".stripMargin
val graph : String = scala.io.Source.fromFile(dotFile.getAbsolutePath).mkString
val errors = compareByLine(graph, expectedGraph)
require(errors.isEmpty , errors.mkString("\n"))
()
}
)
.dependsOn(justADependencyProject, justATransitiveDependencyProject)
def compareByLine(got : String, expected : String) : Seq[String] = {
val errors = ListBuffer[String]()
got.split("\n").zip(expected.split("\n").toSeq).zipWithIndex.foreach { case((got_line : String, expected_line : String), i : Int) =>
if(got_line != expected_line) {
errors.append(
"""not matching lines at line %s
|expected: %s
|got: %s
|""".stripMargin.format(i,expected_line, got_line))
}
}
errors
}

View File

@ -1,68 +0,0 @@
import collection.mutable.ListBuffer
import sbt._
import sbt.Keys._
import net.virtualvoid.sbt.graph.DependencyGraphKeys._
object Build extends sbt.Build {
def defaultSettings =
Seq(scalaVersion := "2.9.2")
lazy val justATransiviteDependencyEndpointProject =
Project("just-a-transitive-dependency-endpoint", file("a"))
.settings(defaultSettings: _*)
lazy val justATransitiveDependencyProject =
Project("just-a-transitive-dependency", file("b"))
.settings(defaultSettings: _*)
.dependsOn(justATransiviteDependencyEndpointProject)
lazy val justADependencyProject =
Project("just-a-dependency", file("c"))
.settings(defaultSettings: _*)
lazy val test_project =
Project("test-dot-file-generation", file("d"))
.settings(defaultSettings: _*)
.settings(
TaskKey[Unit]("check") <<= (dependencyDot in Compile) map { (dotFile) =>
val expectedGraph =
"""digraph "dependency-graph" {
| graph[rankdir="LR"]
| edge [
| arrowtail="none"
| ]
| "test-dot-file-generation:test-dot-file-generation_2.9.2:0.1-SNAPSHOT"[label=<test-dot-file-generation<BR/><B>test-dot-file-generation_2.9.2</B><BR/>0.1-SNAPSHOT> style=""]
| "just-a-transitive-dependency:just-a-transitive-dependency_2.9.2:0.1-SNAPSHOT"[label=<just-a-transitive-dependency<BR/><B>just-a-transitive-dependency_2.9.2</B><BR/>0.1-SNAPSHOT> style=""]
| "just-a-transitive-dependency-endpoint:just-a-transitive-dependency-endpoint_2.9.2:0.1-SNAPSHOT"[label=<just-a-transitive-dependency-endpoint<BR/><B>just-a-transitive-dependency-endpoint_2.9.2</B><BR/>0.1-SNAPSHOT> style=""]
| "just-a-dependency:just-a-dependency_2.9.2:0.1-SNAPSHOT"[label=<just-a-dependency<BR/><B>just-a-dependency_2.9.2</B><BR/>0.1-SNAPSHOT> style=""]
| "test-dot-file-generation:test-dot-file-generation_2.9.2:0.1-SNAPSHOT" -> "just-a-transitive-dependency:just-a-transitive-dependency_2.9.2:0.1-SNAPSHOT"
| "just-a-transitive-dependency:just-a-transitive-dependency_2.9.2:0.1-SNAPSHOT" -> "just-a-transitive-dependency-endpoint:just-a-transitive-dependency-endpoint_2.9.2:0.1-SNAPSHOT"
| "test-dot-file-generation:test-dot-file-generation_2.9.2:0.1-SNAPSHOT" -> "just-a-dependency:just-a-dependency_2.9.2:0.1-SNAPSHOT"
|}
""".stripMargin
val graph : String = scala.io.Source.fromFile(dotFile.getAbsolutePath).mkString
val errors = compareByLine(graph, expectedGraph)
require(errors.isEmpty , errors.mkString("\n"))
()
}
)
.dependsOn(justADependencyProject, justATransitiveDependencyProject)
def compareByLine(got : String, expected : String) : Seq[String] = {
val errors = ListBuffer[String]()
got.split("\n").zip(expected.split("\n").toSeq).zipWithIndex.foreach { case((got_line : String, expected_line : String), i : Int) =>
if(got_line != expected_line) {
errors.append(
"""not matching lines at line %s
|expected: %s
|got: %s
|""".stripMargin.format(i,expected_line, got_line))
}
}
errors
}
}