From 673f9923ab8d680709ddbcb6624148d880e056b2 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Sun, 10 Apr 2011 16:22:48 -0400 Subject: [PATCH] dependency filters, selecting/filtering UpdateReport --- ivy/DependencyFilter.scala | 66 ++++++++++++++++++++++++++++++++++++ ivy/IvyRetrieve.scala | 27 --------------- ivy/UpdateReport.scala | 68 ++++++++++++++++++++++++++++++++++++++ main/Defaults.scala | 18 +++------- sbt/package.scala | 2 +- util/io/NameFilter.scala | 6 ++++ 6 files changed, 146 insertions(+), 41 deletions(-) create mode 100644 ivy/DependencyFilter.scala create mode 100644 ivy/UpdateReport.scala diff --git a/ivy/DependencyFilter.scala b/ivy/DependencyFilter.scala new file mode 100644 index 000000000..1773525c6 --- /dev/null +++ b/ivy/DependencyFilter.scala @@ -0,0 +1,66 @@ +/* sbt -- Simple Build Tool + * Copyright 2011 Mark Harrah + */ +package sbt + +trait DependencyFilterExtra +{ + def moduleFilter(organization: NameFilter = AllPassFilter, name: NameFilter = AllPassFilter, revision: NameFilter = AllPassFilter): ModuleFilter = + new ModuleFilter { + def apply(m: ModuleID): Boolean = organization.accept(m.organization) && name.accept(m.name) && revision.accept(m.revision) + } + def artifactFilter(name: NameFilter = AllPassFilter, `type`: NameFilter = AllPassFilter, extension: NameFilter = AllPassFilter, classifier: NameFilter = AllPassFilter): ArtifactFilter = + new ArtifactFilter { + def apply(a: Artifact): Boolean = name.accept(a.name) && `type`.accept(a.`type`) && extension.accept(a.extension) && classifier.accept(a.classifier getOrElse "") + } + def configurationFilter(name: NameFilter = AllPassFilter): ConfigurationFilter = + new ConfigurationFilter { + def apply(c: String): Boolean = name.accept(c) + } +} +object DependencyFilter extends DependencyFilterExtra +{ + def make(configuration: ConfigurationFilter = configurationFilter(), module: ModuleFilter = moduleFilter(), artifact: ArtifactFilter = artifactFilter()): DependencyFilter = + new DependencyFilter { + def apply(c: String, m: ModuleID, a: Artifact): Boolean = configuration(c) && module(m) && artifact(a) + } + def apply(x: DependencyFilter, y: DependencyFilter, combine: (Boolean, Boolean) => Boolean): DependencyFilter = + new DependencyFilter { + def apply(c: String, m: ModuleID, a: Artifact): Boolean = combine(x(c, m, a), y(c, m, a)) + } + def allPass: DependencyFilter = configurationFilter() + implicit def fnToModuleFilter(f: ModuleID => Boolean): ModuleFilter = new ModuleFilter { def apply(m: ModuleID) = f(m) } + implicit def fnToArtifactFilter(f: Artifact => Boolean): ArtifactFilter = new ArtifactFilter { def apply(m: Artifact) = f(m) } + implicit def fnToConfigurationFilter(f: String => Boolean): ConfigurationFilter = new ConfigurationFilter { def apply(c: String) = f(c) } +} +trait DependencyFilter +{ + def apply(configuration: String, module: ModuleID, artifact: Artifact): Boolean + final def &&(o: DependencyFilter) = DependencyFilter(this, o, _ && _) + final def ||(o: DependencyFilter) = DependencyFilter(this, o, _ || _) + final def -- (o: DependencyFilter) = DependencyFilter(this, o, _ && !_) +} +sealed trait SubDepFilter[Arg, Self <: SubDepFilter[Arg, Self]] extends DependencyFilter +{ self: Self => + def apply(a: Arg): Boolean + protected def make(f: Arg => Boolean): Self + final def &(o: Self): Self = combine(o, _ && _) + final def |(o: Self): Self = combine(o, _ || _) + final def -(o: Self): Self = combine(o, _ && !_) + private[this] def combine(o: Self, f: (Boolean, Boolean) => Boolean): Self = make( (m: Arg) => f(this(m), o(m)) ) +} +trait ModuleFilter extends SubDepFilter[ModuleID, ModuleFilter] +{ + protected final def make(f: ModuleID => Boolean) = new ModuleFilter { def apply(m: ModuleID) = f(m) } + final def apply(configuration: String, module: ModuleID, artifact: Artifact): Boolean = apply(module) +} +trait ArtifactFilter extends SubDepFilter[Artifact, ArtifactFilter] +{ + protected final def make(f: Artifact => Boolean) = new ArtifactFilter { def apply(m: Artifact) = f(m) } + final def apply(configuration: String, module: ModuleID, artifact: Artifact): Boolean = apply(artifact) +} +trait ConfigurationFilter extends SubDepFilter[String, ConfigurationFilter] +{ + protected final def make(f: String => Boolean) = new ConfigurationFilter { def apply(m: String) = f(m) } + final def apply(configuration: String, module: ModuleID, artifact: Artifact): Boolean = apply(configuration) +} \ No newline at end of file diff --git a/ivy/IvyRetrieve.scala b/ivy/IvyRetrieve.scala index f706fc66f..328a33078 100644 --- a/ivy/IvyRetrieve.scala +++ b/ivy/IvyRetrieve.scala @@ -50,30 +50,3 @@ object IvyRetrieve def configurationReport(confReport: ConfigurationResolveReport): ConfigurationReport = new ConfigurationReport(confReport.getConfiguration, moduleReports(confReport)) } - -final class UpdateReport(val configurations: Seq[ConfigurationReport]) -{ - override def toString = "Update report:\n" + configurations.mkString - def allModules: Seq[ModuleID] = configurations.flatMap(_.allModules).distinct - def retrieve(f: (String, ModuleID, Artifact, File) => File): UpdateReport = - new UpdateReport(configurations map { _ retrieve f} ) - def configuration(s: String) = configurations.find(_.configuration == s) -} -final class ConfigurationReport(val configuration: String, val modules: Seq[ModuleReport]) -{ - override def toString = "\t" + configuration + ":\n" + modules.mkString - def allModules: Seq[ModuleID] = modules.map(_.module) - def retrieve(f: (String, ModuleID, Artifact, File) => File): ConfigurationReport = - new ConfigurationReport(configuration, modules map { _.retrieve( (mid,art,file) => f(configuration, mid, art, file)) }) -} -final class ModuleReport(val module: ModuleID, val artifacts: Seq[(Artifact, File)], val missingArtifacts: Seq[Artifact]) -{ - override def toString = - { - val arts = artifacts.map(_.toString) ++ missingArtifacts.map(art => "(MISSING) " + art) - "\t\t" + module + ": " + - (if(arts.size <= 1) "" else "\n\t\t\t") + arts.mkString("\n\t\t\t") + "\n" - } - def retrieve(f: (ModuleID, Artifact, File) => File): ModuleReport = - new ModuleReport(module, artifacts.map { case (art,file) => (art, f(module, art, file)) }, missingArtifacts) -} \ No newline at end of file diff --git a/ivy/UpdateReport.scala b/ivy/UpdateReport.scala new file mode 100644 index 000000000..50097eeeb --- /dev/null +++ b/ivy/UpdateReport.scala @@ -0,0 +1,68 @@ +/* sbt -- Simple Build Tool + * Copyright 2011 Mark Harrah + */ +package sbt + + import java.io.File + +final class UpdateReport(val configurations: Seq[ConfigurationReport]) +{ + override def toString = "Update report:\n" + configurations.mkString + def allModules: Seq[ModuleID] = configurations.flatMap(_.allModules).distinct + def retrieve(f: (String, ModuleID, Artifact, File) => File): UpdateReport = + new UpdateReport(configurations map { _ retrieve f} ) + def configuration(s: String) = configurations.find(_.configuration == s) +} +final class ConfigurationReport(val configuration: String, val modules: Seq[ModuleReport]) +{ + override def toString = "\t" + configuration + ":\n" + modules.mkString + def allModules: Seq[ModuleID] = modules.map(_.module) + def retrieve(f: (String, ModuleID, Artifact, File) => File): ConfigurationReport = + new ConfigurationReport(configuration, modules map { _.retrieve( (mid,art,file) => f(configuration, mid, art, file)) }) +} +final class ModuleReport(val module: ModuleID, val artifacts: Seq[(Artifact, File)], val missingArtifacts: Seq[Artifact]) +{ + override def toString = + { + val arts = artifacts.map(_.toString) ++ missingArtifacts.map(art => "(MISSING) " + art) + "\t\t" + module + ": " + + (if(arts.size <= 1) "" else "\n\t\t\t") + arts.mkString("\n\t\t\t") + "\n" + } + def retrieve(f: (ModuleID, Artifact, File) => File): ModuleReport = + new ModuleReport(module, artifacts.map { case (art,file) => (art, f(module, art, file)) }, missingArtifacts) +} +object UpdateReport +{ + implicit def richUpdateReport(report: UpdateReport): RichUpdateReport = new RichUpdateReport(report) + final class RichUpdateReport(report: UpdateReport) + { + import DependencyFilter._ + + def allFiles: Seq[File] = matching(DependencyFilter.allPass) + def matching(f: DependencyFilter): Seq[File] = select0(f).distinct + def select(configuration: ConfigurationFilter = configurationFilter(), module: ModuleFilter = moduleFilter(), artifact: ArtifactFilter = artifactFilter()): Seq[File] = + matching(DependencyFilter.make(configuration, module, artifact)) + + private[this] def select0(f: DependencyFilter): Seq[File] = + for(cReport <- report.configurations; mReport <- cReport.modules; (artifact, file) <- mReport.artifacts if f(cReport.configuration, mReport.module, artifact)) yield { + if(file == null) error("Null file: conf=" + cReport.configuration + ", module=" + mReport.module + ", art: " + artifact) + file + } + + def filter(f: DependencyFilter): UpdateReport = + { + val newConfigurations = report.configurations.map { confReport => + import confReport._ + val newModules = + modules map { modReport => + import modReport._ + val newArtifacts = artifacts filter { case (art, file) => f(configuration, module, art) } + val newMissing = missingArtifacts filter { art => f(configuration, module, art) } + new ModuleReport(module, newArtifacts, newMissing) + } + new ConfigurationReport(configuration, newModules) + } + new UpdateReport(newConfigurations) + } + } +} \ No newline at end of file diff --git a/main/Defaults.scala b/main/Defaults.scala index 17424d84a..3e922a8f0 100644 --- a/main/Defaults.scala +++ b/main/Defaults.scala @@ -482,7 +482,7 @@ object Classpaths moduleID :== normalizedName, defaultConfiguration in GlobalScope :== Some(Configurations.Compile), defaultConfigurationMapping in GlobalScope <<= defaultConfiguration{ case Some(d) => "*->" + d.name; case None => "*->*" }, - ivyPaths <<= (baseDirectory, appConfiguration) { (base, app) => new IvyPaths(base, Option(app.provider.scalaProvider.launcher.ivyHome)) }, + ivyPaths <<= (baseDirectory, appConfiguration) { (base, app) => new IvyPaths(base, Option(app.provider.scalaProvider.launcher.ivyHome).map(_ / "cache")) }, otherResolvers <<= publishTo(_.toList), projectResolver <<= projectResolverTask, projectDependencies <<= projectDependenciesTask, @@ -565,7 +565,7 @@ object Classpaths val f = Tracked.inputChanged(cacheFile / "inputs") { (inChanged: Boolean, in: In) => val outCache = Tracked.lastOutput[In, UpdateReport](cacheFile / "output") { - case (_, Some(out)) if !inChanged && allFiles(out).forall(_.exists) => out + case (_, Some(out)) if !inChanged && out.allFiles.forall(_.exists) => out case _ => work(in) } outCache(in) @@ -726,16 +726,8 @@ object Classpaths flatten(defaultConfiguration in p get data) getOrElse Configurations.Default def flatten[T](o: Option[Option[T]]): Option[T] = o flatMap identity - def managedJars(config: Configuration, jarTypes: Set[String], up: UpdateReport): Classpath = managedFiles(config, up)(isJar(jarTypes)) - def allFiles(up: UpdateReport): Seq[File] = data( up.configurations flatMap { cr => allJars(cr)(_ => true) }).distinct - def managedFiles(config: Configuration, up: UpdateReport)(pred: Artifact => Boolean): Classpath = - allJars( confReport(config.name, up) )(pred) - def confReport(config: String, up: UpdateReport): ConfigurationReport = - up.configuration(config) getOrElse error("Configuration '" + config + "' unresolved by 'update'.") - def allJars(cr: ConfigurationReport)(pred: Artifact => Boolean): Seq[File] = cr.modules.flatMap(mr => allJars(mr.artifacts)(pred)) - def allJars(as: Seq[(Artifact,File)])(pred: Artifact => Boolean): Seq[File] = as collect { case (a, f) if pred(a) => f } - def isJar(jarTypes: Set[String])(a: Artifact): Boolean = - jarTypes contains a.`type` - lazy val dbResolver = Resolver.url("sbt-db", new URL("http://databinder.net/repo/"))(Resolver.ivyStylePatterns) + + import DependencyFilter._ + def managedJars(config: Configuration, jarTypes: Set[String], up: UpdateReport): Classpath = up.select( configuration = configurationFilter(config.name), artifact = artifactFilter(`type` = jarTypes) ) } diff --git a/sbt/package.scala b/sbt/package.scala index 5fd0c3e18..939b4901a 100644 --- a/sbt/package.scala +++ b/sbt/package.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010, 2011 Mark Harrah */ -package object sbt extends sbt.std.TaskExtra with sbt.Types with sbt.ProcessExtra with sbt.impl.DependencyBuilders with sbt.PathExtra with sbt.ProjectExtra +package object sbt extends sbt.std.TaskExtra with sbt.Types with sbt.ProcessExtra with sbt.impl.DependencyBuilders with sbt.PathExtra with sbt.ProjectExtra with sbt.DependencyFilterExtra { type Setting[T] = Project.Setting[T] type ScopedKey[T] = Project.ScopedKey[T] diff --git a/util/io/NameFilter.scala b/util/io/NameFilter.scala index 7856030dc..c60d57dc9 100644 --- a/util/io/NameFilter.scala +++ b/util/io/NameFilter.scala @@ -56,6 +56,12 @@ object NothingFilter extends NameFilter def accept(name: String) = false } +object NameFilter +{ + implicit def fnToNameFilter(f: String => Boolean): NameFilter = new NameFilter { + def accept(name: String) = f(name) + } +} object GlobFilter { implicit def apply(expression: String): NameFilter =