@@ -27,6 +27,7 @@ import internal.librarymanagement._
27
27
import librarymanagement ._
28
28
import sbt .dependencygraph .SbtAccess
29
29
import sbt .dependencygraph .DependencyGraphSbtCompat .Implicits ._
30
+ import sbt .complete .Parsers
30
31
31
32
object DependencyGraphSettings {
32
33
import DependencyGraphKeys ._
@@ -47,70 +48,95 @@ object DependencyGraphSettings {
47
48
def reportSettings =
48
49
Seq (Compile , Test , IntegrationTest , Runtime , Provided , Optional ).flatMap(ivyReportForConfig)
49
50
50
- def ivyReportForConfig (config : Configuration ) = inConfig(config)(Seq (
51
- ivyReport := { Def .task { ivyReportFunction.value.apply(config.toString) } dependsOn (ignoreMissingUpdate) }.value,
52
- crossProjectId := sbt.CrossVersion (scalaVersion.value, scalaBinaryVersion.value)(projectID.value),
53
- moduleGraphSbt :=
54
- ignoreMissingUpdate.value.configuration(configuration.value).map(report ⇒ SbtUpdateReport .fromConfigurationReport(report, crossProjectId.value)).getOrElse(ModuleGraph .empty),
55
- moduleGraphIvyReport := IvyReport .fromReportFile(absoluteReportPath(ivyReport.value)),
56
- moduleGraph := {
57
- sbtVersion.value match {
58
- case Version (0 , 13 , x, _) if x >= 6 ⇒ moduleGraphSbt.value
59
- case Version (1 , _, _, _) ⇒ moduleGraphSbt.value
60
- }
61
- },
62
- moduleGraph := {
63
- // FIXME: remove busywork
64
- val scalaVersion = Keys .scalaVersion.value
65
- val moduleGraph = DependencyGraphKeys .moduleGraph.value
66
-
67
- if (filterScalaLibrary.value) GraphTransformations .ignoreScalaLibrary(scalaVersion, moduleGraph)
68
- else moduleGraph
69
- },
70
- moduleGraphStore := (moduleGraph storeAs moduleGraphStore triggeredBy moduleGraph).value,
71
- asciiTree := rendering.AsciiTree .asciiTree(moduleGraph.value),
72
- dependencyTree := print(asciiTree).value,
73
- dependencyGraphMLFile := { target.value / " dependencies-%s.graphml" .format(config.toString) },
74
- dependencyGraphML := dependencyGraphMLTask.value,
75
- dependencyDotFile := { target.value / " dependencies-%s.dot" .format(config.toString) },
76
- dependencyDotString := rendering.DOT .dotGraph(moduleGraph.value, dependencyDotHeader.value, dependencyDotNodeLabel.value, rendering.DOT .AngleBrackets ),
77
- dependencyDot := writeToFile(dependencyDotString, dependencyDotFile).value,
78
- dependencyBrowseGraphTarget := { target.value / " browse-dependency-graph" },
79
- dependencyBrowseGraphHTML := browseGraphHTMLTask.value,
80
- dependencyBrowseGraph := openBrowser(dependencyBrowseGraphHTML).value,
81
- dependencyBrowseTreeTarget := { target.value / " browse-dependency-tree" },
82
- dependencyBrowseTreeHTML := browseTreeHTMLTask.value,
83
- dependencyBrowseTree := openBrowser(dependencyBrowseTreeHTML).value,
84
- dependencyList := printFromGraph(rendering.FlatList .render(_, _.id.idString)).value,
85
- dependencyStats := printFromGraph(rendering.Statistics .renderModuleStatsList).value,
86
- dependencyDotHeader :=
87
- """ |digraph "dependency-graph" {
51
+ val renderingAlternatives : Seq [(TaskKey [Unit ], ModuleGraph ⇒ String )] =
52
+ Seq (
53
+ dependencyTree -> rendering.AsciiTree .asciiTree _,
54
+ dependencyList -> rendering.FlatList .render(_.id.idString),
55
+ dependencyStats -> rendering.Statistics .renderModuleStatsList _,
56
+ licenseInfo -> rendering.LicenseInfo .render _)
57
+
58
+ def ivyReportForConfig (config : Configuration ) = inConfig(config)(
59
+ Seq (
60
+ ivyReport := { Def .task { ivyReportFunction.value.apply(config.toString) } dependsOn (ignoreMissingUpdate) }.value,
61
+ crossProjectId := sbt.CrossVersion (scalaVersion.value, scalaBinaryVersion.value)(projectID.value),
62
+ moduleGraphSbt :=
63
+ ignoreMissingUpdate.value.configuration(configuration.value).map(report ⇒ SbtUpdateReport .fromConfigurationReport(report, crossProjectId.value)).getOrElse(ModuleGraph .empty),
64
+ moduleGraphIvyReport := IvyReport .fromReportFile(absoluteReportPath(ivyReport.value)),
65
+ moduleGraph := {
66
+ sbtVersion.value match {
67
+ case Version (0 , 13 , x, _) if x >= 6 ⇒ moduleGraphSbt.value
68
+ case Version (1 , _, _, _) ⇒ moduleGraphSbt.value
69
+ }
70
+ },
71
+ moduleGraph := {
72
+ // FIXME: remove busywork
73
+ val scalaVersion = Keys .scalaVersion.value
74
+ val moduleGraph = DependencyGraphKeys .moduleGraph.value
75
+
76
+ if (filterScalaLibrary.value) GraphTransformations .ignoreScalaLibrary(scalaVersion, moduleGraph)
77
+ else moduleGraph
78
+ },
79
+ moduleGraphStore := (moduleGraph storeAs moduleGraphStore triggeredBy moduleGraph).value,
80
+
81
+ // browse
82
+ dependencyBrowseGraphTarget := { target.value / " browse-dependency-graph" },
83
+ dependencyBrowseGraphHTML := browseGraphHTMLTask.value,
84
+ dependencyBrowseGraph := openBrowser(dependencyBrowseGraphHTML).value,
85
+
86
+ dependencyBrowseTreeTarget := { target.value / " browse-dependency-tree" },
87
+ dependencyBrowseTreeHTML := browseTreeHTMLTask.value,
88
+ dependencyBrowseTree := openBrowser(dependencyBrowseTreeHTML).value,
89
+
90
+ // dot support
91
+ dependencyDotFile := { target.value / " dependencies-%s.dot" .format(config.toString) },
92
+ dependencyDotString := rendering.DOT .dotGraph(moduleGraph.value, dependencyDotHeader.value, dependencyDotNodeLabel.value, rendering.DOT .AngleBrackets ),
93
+ dependencyDot := writeToFile(dependencyDotString, dependencyDotFile).value,
94
+ dependencyDotHeader :=
95
+ """ |digraph "dependency-graph" {
88
96
| graph[rankdir="LR"]
89
97
| edge [
90
98
| arrowtail="none"
91
99
| ]""" .stripMargin,
92
- dependencyDotNodeLabel := { (organisation : String , name : String , version : String ) ⇒
93
- """ %s<BR/><B>%s</B><BR/>%s""" .format(organisation, name, version)
94
- },
95
- whatDependsOn := {
96
- val ArtifactPattern (org, name, versionFilter) = artifactPatternParser.parsed
97
- val graph = moduleGraph .value
98
- val modules =
99
- versionFilter match {
100
- case Some (version) ⇒ ModuleId (org, name, version) :: Nil
101
- case None ⇒ graph.nodes.filter(m ⇒ m.id.organisation == org && m.id. name == name).map(_.id)
102
- }
103
- val output =
104
- modules
105
- .map { module ⇒
106
- rendering. AsciiTree .asciiTree( GraphTransformations .reverseGraphStartingAt(graph, module) )
100
+ dependencyDotNodeLabel := { (organisation : String , name : String , version : String ) ⇒
101
+ """ %s<BR/><B>%s</B><BR/>%s""" .format(organisation, name, version)
102
+ },
103
+
104
+ // GraphML support
105
+ dependencyGraphMLFile := { target .value / " dependencies-%s.graphml " .format(config.toString) },
106
+ dependencyGraphML := dependencyGraphMLTask.value,
107
+
108
+ whatDependsOn := {
109
+ val ArtifactPattern ( org, name, versionFilter) = artifactPatternParser.parsed
110
+ val graph = moduleGraph.value
111
+ val modules =
112
+ versionFilter match {
113
+ case Some (version) ⇒ ModuleId (org, name, version) :: Nil
114
+ case None ⇒ graph.nodes.filter(m ⇒ m.id.organisation == org && m.id.name == name).map(_.id )
107
115
}
108
- .mkString(" \n " )
116
+ val output =
117
+ modules
118
+ .map { module ⇒
119
+ rendering.AsciiTree .asciiTree(GraphTransformations .reverseGraphStartingAt(graph, module))
120
+ }
121
+ .mkString(" \n " )
109
122
110
- streams.value.log.info(output)
111
- output
112
- },
113
- licenseInfo := showLicenseInfo(moduleGraph.value, streams.value)) ++ AsciiGraph .asciiGraphSetttings)
123
+ streams.value.log.info(output)
124
+ output
125
+ },
126
+ // deprecated settings
127
+ asciiTree := (asString in dependencyTree).value) ++
128
+ renderingAlternatives.flatMap((renderingTaskSettings _).tupled) ++
129
+ AsciiGraph .asciiGraphSetttings)
130
+
131
+ def renderingTaskSettings (key : TaskKey [Unit ], renderer : ModuleGraph ⇒ String ): Seq [Setting [_]] =
132
+ Seq (
133
+ asString in key := renderer(moduleGraph.value),
134
+ printToConsole in key := streams.value.log.info((asString in key).value),
135
+ toFile in key := {
136
+ val (targetFile, force) = targetFileAndForceParser.parsed
137
+ writeToFile(key.key.label, (asString in key).value, targetFile, force, streams.value)
138
+ },
139
+ key := (printToConsole in key).value)
114
140
115
141
def ivyReportFunctionTask = Def .task {
116
142
val ivyConfig = Keys .ivyConfiguration.value.asInstanceOf [InlineIvyConfiguration ]
@@ -157,13 +183,17 @@ object DependencyGraphSettings {
157
183
outFile
158
184
}
159
185
160
- def absoluteReportPath = (file : File ) ⇒ file.getAbsolutePath
186
+ def writeToFile (what : String , data : String , targetFile : File , force : Boolean , streams : TaskStreams ): File =
187
+ if (targetFile.exists && ! force)
188
+ throw new RuntimeException (s " Target file for $what already exists at ${targetFile.getAbsolutePath}. Use '-f' to override " )
189
+ else {
190
+ IOUtil .writeToFile(data, targetFile)
161
191
162
- def print (key : TaskKey [String ]) =
163
- Def .task { streams.value.log.info(key.value) }
192
+ streams.log.info(s " Wrote $what to ' $targetFile' " )
193
+ targetFile
194
+ }
164
195
165
- def printFromGraph (f : ModuleGraph ⇒ String ) =
166
- Def .task { streams.value.log.info(f(moduleGraph.value)) }
196
+ def absoluteReportPath = (file : File ) ⇒ file.getAbsolutePath
167
197
168
198
def openBrowser (uriKey : TaskKey [URI ]) =
169
199
Def .task {
@@ -173,33 +203,16 @@ object DependencyGraphSettings {
173
203
uri
174
204
}
175
205
176
- def showLicenseInfo (graph : ModuleGraph , streams : TaskStreams ): Unit = {
177
- val output =
178
- graph.nodes.filter(_.isUsed).groupBy(_.license).toSeq.sortBy(_._1).map {
179
- case (license, modules) ⇒
180
- license.getOrElse(" No license specified" ) + " \n " +
181
- modules.map(_.id.idString formatted " \t %s" ).mkString(" \n " )
182
- }.mkString(" \n\n " )
183
- streams.log.info(output)
184
- }
185
-
186
- import Project ._
187
- val shouldForceParser : State ⇒ Parser [Boolean ] = { (state : State ) ⇒
188
- import sbt .complete .DefaultParsers ._
189
-
190
- (Space ~> token(" --force" )).? .map(_.isDefined)
191
- }
192
-
193
206
case class ArtifactPattern (
194
207
organisation : String ,
195
208
name : String ,
196
209
version : Option [String ])
197
210
211
+ import sbt .complete .DefaultParsers ._
198
212
val artifactPatternParser : Def .Initialize [State ⇒ Parser [ArtifactPattern ]] =
199
213
resolvedScoped { ctx ⇒ (state : State ) ⇒
200
214
val graph = loadFromContext(moduleGraphStore, ctx, state) getOrElse ModuleGraph (Nil , Nil )
201
215
202
- import sbt .complete .DefaultParsers ._
203
216
graph.nodes
204
217
.map(_.id)
205
218
.groupBy(m ⇒ (m.organisation, m.name))
@@ -222,6 +235,10 @@ object DependencyGraphSettings {
222
235
}
223
236
}
224
237
}
238
+ val shouldForceParser : Parser [Boolean ] = (Space ~> (Parser .literal(" -f" ) | " --force" )).? .map(_.isDefined)
239
+
240
+ val targetFileAndForceParser : Parser [(File , Boolean )] =
241
+ Parsers .fileParser(new File (" ." )) ~ shouldForceParser
225
242
226
243
// This is to support 0.13.8's InlineConfigurationWithExcludes while not forcing 0.13.8
227
244
type HasModule = {
@@ -230,7 +247,7 @@ object DependencyGraphSettings {
230
247
def crossName (ivyModule : IvySbt # Module ) =
231
248
ivyModule.moduleSettings match {
232
249
case ic : InlineConfiguration ⇒ ic.module.name
233
- case hm : HasModule if hm.getClass.getName == " sbt.InlineConfigurationWithExcludes" ⇒ hm.module.name
250
+ case hm : HasModule @ unchecked if hm.getClass.getName == " sbt.InlineConfigurationWithExcludes" ⇒ hm.module.name
234
251
case _ ⇒
235
252
throw new IllegalStateException (" sbt-dependency-graph plugin currently only supports InlineConfiguration of ivy settings (the default in sbt)" )
236
253
}
0 commit comments