reorganize plugin sources and make this an AutoPlugin

This commit is contained in:
Johannes Rudolph 2015-11-14 16:26:43 +01:00
parent 7b0e090662
commit adc55e31a7
6 changed files with 167 additions and 102 deletions

View File

@ -1,39 +0,0 @@
package sbt
import net.virtualvoid.sbt.graph.Plugin._
import Keys._
import CrossVersion._
object SbtDependencyGraphCompat {
/**
* 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] = 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 => 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)
}
def getTerminalWidth: Int = JLine.usingTerminal(_.getWidth)
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2014 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
import sbt._
trait DependencyGraphKeys {
val dependencyGraphMLFile = SettingKey[File]("dependency-graph-ml-file",
"The location the graphml file should be generated at")
val dependencyGraphML = TaskKey[File]("dependency-graph-ml",
"Creates a graphml file containing the dependency-graph for a project")
val dependencyDotFile = SettingKey[File]("dependency-dot-file",
"The location the dot file should be generated at")
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")
val dependencyDotHeader = SettingKey[String]("dependency-dot-header",
"The header of the dot file. (e.g. to set your preferred node shapes)")
val dependencyDot = TaskKey[File]("dependency-dot",
"Creates a dot file containing the dpendency-graph for a project")
val moduleGraph = TaskKey[IvyGraphMLDependencies.ModuleGraph]("module-graph",
"The dependency graph for a project")
val asciiGraph = TaskKey[String]("dependency-graph-string",
"Returns a string containing the ascii representation of the dependency graph for a project")
val dependencyGraph = InputKey[Unit]("dependency-graph",
"Prints the ascii graph to the console")
val asciiTree = TaskKey[String]("dependency-tree-string",
"Returns a string containing an ascii tree representation of the dependency graph for a project")
val dependencyTree = TaskKey[Unit]("dependency-tree",
"Prints the ascii tree to the console")
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")
val ivyReport = TaskKey[File]("ivy-report",
"A task which returns the location of the ivy report file for a given configuration (default `compile`).")
val ignoreMissingUpdate = Keys.update in ivyReport
val filterScalaLibrary = SettingKey[Boolean]("filter-scala-library",
"Specifies if scala dependency should be filtered in dependency-* output"
)
val licenseInfo = TaskKey[Unit]("dependency-license-info",
"Aggregates and shows information about the licenses of dependencies")
// internal
private[graph] val moduleGraphStore = TaskKey[IvyGraphMLDependencies.ModuleGraph]("module-graph-store", "The stored module-graph from the last run")
private[graph] val whatDependsOn = InputKey[Unit]("what-depends-on", "Shows information about what depends on the given module")
}
object DependencyGraphKeys extends DependencyGraphKeys

View File

@ -0,0 +1,26 @@
/*
* Copyright 2011, 2012 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
import sbt._
import Keys._
object DependencyGraphPlugin extends AutoPlugin {
object autoImport extends DependencyGraphKeys
override def projectSettings: Seq[Def.Setting[_]] = DependencyGraphSettings.graphSettings
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2011, 2012 Johannes Rudolph
* Copyright 2015 Johannes Rudolph
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,53 +18,21 @@ package net.virtualvoid.sbt.graph
import sbt._
import Keys._
import complete.Parser
import CrossVersion._
import sbt.complete.DefaultParsers._
import sbt.complete.Parser
import org.apache.ivy.core.resolve.ResolveOptions
import net.virtualvoid.sbt.graph.IvyGraphMLDependencies.ModuleGraph
object Plugin extends sbt.Plugin {
val dependencyGraphMLFile = SettingKey[File]("dependency-graph-ml-file",
"The location the graphml file should be generated at")
val dependencyGraphML = TaskKey[File]("dependency-graph-ml",
"Creates a graphml file containing the dependency-graph for a project")
val dependencyDotFile = SettingKey[File]("dependency-dot-file",
"The location the dot file should be generated at")
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")
val dependencyDotHeader = SettingKey[String]("dependency-dot-header",
"The header of the dot file. (e.g. to set your preferred node shapes)")
val dependencyDot = TaskKey[File]("dependency-dot",
"Creates a dot file containing the dpendency-graph for a project")
val moduleGraph = TaskKey[IvyGraphMLDependencies.ModuleGraph]("module-graph",
"The dependency graph for a project")
val asciiGraph = TaskKey[String]("dependency-graph-string",
"Returns a string containing the ascii representation of the dependency graph for a project")
val dependencyGraph = InputKey[Unit]("dependency-graph",
"Prints the ascii graph to the console")
val asciiTree = TaskKey[String]("dependency-tree-string",
"Returns a string containing an ascii tree representation of the dependency graph for a project")
val dependencyTree = TaskKey[Unit]("dependency-tree",
"Prints the ascii tree to the console")
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")
val ivyReport = TaskKey[File]("ivy-report",
"A task which returns the location of the ivy report file for a given configuration (default `compile`).")
val ignoreMissingUpdate = update in ivyReport
val filterScalaLibrary = SettingKey[Boolean]("filter-scala-library",
"Specifies if scala dependency should be filtered in dependency-* output"
)
import IvyGraphMLDependencies.ModuleGraph
val licenseInfo = TaskKey[Unit]("dependency-license-info",
"Aggregates and shows information about the licenses of dependencies")
// internal
object DependencyGraphSettings {
import DependencyGraphKeys._
import ModuleGraphProtocol._
val moduleGraphStore = TaskKey[IvyGraphMLDependencies.ModuleGraph]("module-graph-store", "The stored module-graph from the last run")
val whatDependsOn = InputKey[Unit]("what-depends-on", "Shows information about what depends on the given module")
def graphSettings = seq(
def graphSettings = Seq(
ivyReportFunction <<= (sbtVersion, target, projectID, ivyModule, appConfiguration, streams) map { (sbtV, target, projectID, ivyModule, config, streams) =>
sbtV match {
case Version(0, min, fix, _) if min > 12 || (min == 12 && fix >= 3) =>
@ -80,11 +48,11 @@ object Plugin extends sbt.Plugin {
}
},
updateConfiguration in ignoreMissingUpdate <<= updateConfiguration(config => new UpdateConfiguration(config.retrieve, true, config.logging)),
SbtDependencyGraphCompat.ignoreMissingUpdateT,
ignoreMissingUpdateT,
filterScalaLibrary in Global := true
) ++ Seq(Compile, Test, 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),
moduleGraph <<= ivyReport map (absoluteReportPath.andThen(IvyGraphMLDependencies.graph)),
moduleGraph <<= (scalaVersion, moduleGraph, filterScalaLibrary) map { (scalaV, graph, filter) =>
@ -118,15 +86,15 @@ object Plugin extends sbt.Plugin {
dependencyDotFile <<= target / "dependencies-%s.dot".format(config.toString),
dependencyDot <<= dependencyDotTask,
dependencyDotHeader := """digraph "dependency-graph" {
| graph[rankdir="LR"]
| node [
| shape="record"
| ]
| edge [
| arrowtail="none"
| ]""".stripMargin,
| graph[rankdir="LR"]
| node [
| shape="record"
| ]
| edge [
| arrowtail="none"
| ]""".stripMargin,
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 =>
(module, streams, moduleGraph) map { (module, streams, graph) =>
@ -149,9 +117,9 @@ object Plugin extends sbt.Plugin {
(moduleGraph, dependencyDotHeader, dependencyDotNodeLabel, dependencyDotFile, streams).map {
(graph, dotHead, nodeLabel, outFile, streams) =>
val resultFile = IvyGraphMLDependencies.saveAsDot(graph, dotHead, nodeLabel, outFile)
streams.log.info("Wrote dependency graph to '%s'" format resultFile)
resultFile
val resultFile = IvyGraphMLDependencies.saveAsDot(graph, dotHead, nodeLabel, outFile)
streams.log.info("Wrote dependency graph to '%s'" format resultFile)
resultFile
}
def absoluteReportPath = (file: File) => file.getAbsolutePath
@ -163,14 +131,14 @@ object Plugin extends sbt.Plugin {
graph.nodes.filter(_.isUsed).groupBy(_.license).toSeq.sortBy(_._1).map {
case (license, modules) =>
license.getOrElse("No license specified")+"\n"+
modules.map(_.id.idString formatted "\t %s").mkString("\n")
modules.map(_.id.idString formatted "\t %s").mkString("\n")
}.mkString("\n\n")
streams.log.info(output)
}
import Project._
val shouldForceParser: State => Parser[Boolean] = { (state: State) =>
import complete.DefaultParsers._
import sbt.complete.DefaultParsers._
(Space ~> token("--force")).?.map(_.isDefined)
}
@ -181,9 +149,7 @@ object Plugin extends sbt.Plugin {
resolvedScoped { ctx => (state: State) =>
val graph = loadFromContext(moduleGraphStore, ctx, state) getOrElse ModuleGraph(Nil, Nil)
import complete.DefaultParsers._
import SbtDependencyGraphCompat._
import sbt.complete.DefaultParsers._
def moduleFrom(modules: Seq[ModuleId]) =
modules.map { m =>
(token(m.name) ~ Space ~ token(m.version)).map(_ => m)
@ -218,4 +184,34 @@ object Plugin extends sbt.Plugin {
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

@ -5,8 +5,6 @@
*/
package net.virtualvoid.sbt.graph
import sbt.SbtDependencyGraphCompat
object Graph
{
// [info] foo
@ -48,7 +46,7 @@ object Graph
}
def defaultColumnSize: Int = {
val termWidth = SbtDependencyGraphCompat.getTerminalWidth
val termWidth = sbt.SbtAccess.getTerminalWidth
if (termWidth > 20) termWidth - 8
else 80 // ignore termWidth
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2015 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 sbt
object SbtAccess {
val unmanagedScalaInstanceOnly = Defaults.unmanagedScalaInstanceOnly
def getTerminalWidth: Int = JLine.usingTerminal(_.getWidth)
}