From 1567de67b22ba1b3b6a55827fe426d33ac316eb8 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Tue, 21 Sep 2010 22:38:18 -0400 Subject: [PATCH] fix update, default project supports triggered execution --- ivy/IvyActions.scala | 43 +++++++++++++------ main/ClasspathProject.scala | 83 ++++++++++++++++++++++++++----------- main/DefaultProject.scala | 7 +++- main/MultiProject.scala | 6 ++- 4 files changed, 97 insertions(+), 42 deletions(-) diff --git a/ivy/IvyActions.scala b/ivy/IvyActions.scala index 9b90d007b..bcda71dc1 100644 --- a/ivy/IvyActions.scala +++ b/ivy/IvyActions.scala @@ -15,14 +15,15 @@ import core.module.descriptor.{DefaultArtifact, DefaultDependencyArtifactDescrip import core.module.descriptor.{DefaultDependencyDescriptor, DefaultModuleDescriptor, DependencyDescriptor, ModuleDescriptor} import core.module.id.{ArtifactId,ModuleId, ModuleRevisionId} import core.publish.PublishOptions -import core.report.ResolveReport +import core.report.{ArtifactDownloadReport,ResolveReport} import core.resolve.ResolveOptions import core.retrieve.RetrieveOptions import plugins.parser.m2.{PomModuleDescriptorParser,PomModuleDescriptorWriter} -final class UpdateConfiguration(val retrieveDirectory: File, val outputPattern: String, val synchronize: Boolean, val logging: UpdateLogging.Value) extends NotNull +final class UpdateConfiguration(val retrieve: Option[RetrieveConfiguration], val logging: UpdateLogging.Value) +final class RetrieveConfiguration(val retrieveDirectory: File, val outputPattern: String, val synchronize: Boolean) final class MakePomConfiguration(val extraDependencies: Iterable[ModuleID], val configurations: Option[Iterable[Configuration]], - val extra: NodeSeq, val process: Node => Node, val filterRepositories: MavenRepository => Boolean) extends NotNull + val extra: NodeSeq, val process: Node => Node, val filterRepositories: MavenRepository => Boolean) object MakePomConfiguration { def apply(extraDependencies: Iterable[ModuleID], configurations: Option[Iterable[Configuration]], extra: NodeSeq) = @@ -107,15 +108,20 @@ object IvyActions def update(module: IvySbt#Module, configuration: UpdateConfiguration) = { module.withModule { case (ivy, md, default) => - import configuration._ + import configuration.{retrieve => rConf, logging} val report = resolve(logging)(ivy, md, default) - import IvyRetrieve._ - /*if(synchronize) - IO delete retrieveDirectory - IO createDirectory retrieveDirectory - retrieve( cachePaths(report), , retrieveDirectory)*/ - cachePaths(report) - /*val retrieveOptions = new RetrieveOptions + rConf match + { + case None => IvyRetrieve.cachePaths(report) + case Some(conf) => retrieve(ivy, md, conf, logging) + } + } + } + // doesn't work. perhaps replace retrieve/determineArtifactsToCopy with custom code + private def retrieve(ivy: Ivy, md: ModuleDescriptor, conf: RetrieveConfiguration, logging: UpdateLogging.Value) = + { + import conf._ + val retrieveOptions = new RetrieveOptions retrieveOptions.setSync(synchronize) val patternBase = retrieveDirectory.getAbsolutePath val pattern = @@ -123,8 +129,19 @@ object IvyActions patternBase + outputPattern else patternBase + File.separatorChar + outputPattern - ivy.retrieve(md.getModuleRevisionId, pattern, retrieveOptions)*/ - } + + val engine = ivy.getRetrieveEngine + engine.retrieve(md.getModuleRevisionId, pattern, retrieveOptions) + + //TODO: eliminate the duplication for better efficiency (retrieve already calls determineArtifactsToCopy once) + val rawMap = engine.determineArtifactsToCopy(md.getModuleRevisionId, pattern, retrieveOptions) + val map = rawMap.asInstanceOf[java.util.Map[ArtifactDownloadReport,java.util.Set[String]]] + val confMap = new collection.mutable.HashMap[String, Seq[File]] + + import collection.JavaConversions.{asScalaMap,asScalaSet} + for( (report, all) <- map; retrieved <- all; val file = new File(retrieved); conf <- report.getArtifact.getConfigurations) + confMap.put(conf, file +: confMap.getOrElse(conf, Nil)) + confMap.toMap } private def resolve(logging: UpdateLogging.Value)(ivy: Ivy, module: DefaultModuleDescriptor, defaultConf: String): ResolveReport = { diff --git a/main/ClasspathProject.scala b/main/ClasspathProject.scala index ae8e90185..1c8f0da81 100644 --- a/main/ClasspathProject.scala +++ b/main/ClasspathProject.scala @@ -11,10 +11,14 @@ package sbt import Path._ import Types._ import scala.xml.NodeSeq + import scala.collection.mutable.{LinkedHashMap, LinkedHashSet} trait ClasspathProject { + def name: String def configurations: Seq[Configuration] + def defaultConfiguration: Option[Configuration] + val products: Classpath val unmanagedClasspath: Classpath val managedClasspath: Classpath @@ -39,6 +43,9 @@ trait ClasspathProject val prod = products(configuration) (dep, prod) map concat[Attributed[File]] } + + lazy val configurationMap: Map[String, Configuration] = + configurations map { conf => (conf.name, conf) } toMap; } trait BasicClasspathProject extends ClasspathProject @@ -47,9 +54,7 @@ trait BasicClasspathProject extends ClasspathProject val moduleSettings: Task[ModuleSettings] val unmanagedBase: Task[File] - lazy val updateConfig: Task[UpdateConfiguration] = task { - new UpdateConfiguration(null, null, true, UpdateLogging.Full) - } + val updateConfig: Task[UpdateConfiguration] lazy val ivySbt: Task[IvySbt] = ivyConfiguration map { conf => new IvySbt(conf) } @@ -60,7 +65,7 @@ trait BasicClasspathProject extends ClasspathProject } def classpathFilter: FileFilter = GlobFilter("*.jar") - def defaultExcludeFilter: FileFilter = MultiProject.defaultExcludes + def defaultExcludes: FileFilter override val managedClasspath: Classpath = TaskMap { configuration => @@ -70,14 +75,11 @@ trait BasicClasspathProject extends ClasspathProject val unmanagedClasspath: Classpath = TaskMap { configuration => unmanagedBase map { base => - attributed( (base * (classpathFilter -- defaultExcludeFilter) +++ - (base / configuration.toString).descendentsExcept(classpathFilter, defaultExcludeFilter)).getFiles.toSeq ) + attributed( (base * (classpathFilter -- defaultExcludes) +++ + (base / configuration.toString).descendentsExcept(classpathFilter, defaultExcludes)).getFiles.toSeq ) } } - lazy val configurationMap: Map[String, Configuration] = - configurations map { conf => (conf.name, conf) } toMap; - import Types._ lazy val update = (ivyModule, updateConfig) map { case module :+: config :+: HNil => val confMap = configurationMap @@ -94,6 +96,14 @@ trait DefaultClasspathProject extends BasicClasspathProject with Project def otherResolvers: Seq[Resolver] = Nil def moduleConfigurations: Seq[ModuleConfiguration] = Nil + def retrievePattern = "[type]/[organisation]/[module]/[artifact](-[revision])(-[classifier]).[ext]" + override lazy val updateConfig: Task[UpdateConfiguration] = retrieveConfig map { rConf => + new UpdateConfiguration(rConf, UpdateLogging.Full) + } + lazy val retrieveConfig: Task[Option[RetrieveConfiguration]] = task { + None//Some(new RetrieveConfiguration(managedDependencyPath asFile, retrievePattern, true)) + } + def offline: Boolean = false def paths: IvyPaths = new IvyPaths(info.projectDirectory, None) @@ -104,6 +114,7 @@ trait DefaultClasspathProject extends BasicClasspathProject with Project def libraryDependencies: Iterable[ModuleID] = ReflectUtilities.allVals[ModuleID](this).map(_._2) + def managedDependencyPath: Path = info.projectDirectory / "lib_managed" def dependencyPath: Path = info.projectDirectory / "lib" def ivyXML: NodeSeq = NodeSeq.Empty @@ -111,19 +122,7 @@ trait DefaultClasspathProject extends BasicClasspathProject with Project def ivyScala: Option[IvyScala] = None def ivyValidate: Boolean = false - //TODO: transitive dependencies - lazy val internalDependencyClasspath: Classpath = - TaskMap { (conf: Configuration) => - val confMap = configurationMap - val productsTasks = - for( (p: ClasspathProject, Some(confString)) <- ClasspathProject.resolvedDependencies(this)) yield - { - println("Project " + p.name + ", conf: " + confString) - val to = parseSimpleConfigurations(confString).getOrElse(conf.toString, missingMapping(this.name, p.name, conf.toString)) - p.products(confMap(to)) - } - (productsTasks.toSeq.join) named(name + "/join") map(_.flatten) named(name + "/int") - } + lazy val internalDependencyClasspath: Classpath = internalDependencies(this) lazy val unmanagedBase = task { dependencyPath.asFile } @@ -209,18 +208,52 @@ object ClasspathProject case Left(extPath) => context.info.externals(extPath) case Right(proj) => proj } - + + def mapped(c: String, mapping: Option[String], default: String)(errMsg: => String): String = + parseSimpleConfigurations(mapping getOrElse default).getOrElse(c, errMsg) + def internalDependencies(project: Project): Classpath = + TaskMap { (conf: Configuration) => + val visited = new LinkedHashSet[(Project,String)] + def visit(p: Project, c: String) + { + for( (dep, confMapping) <- ClasspathProject.resolvedDependencies(p)) + { + val depConf = mapped(c, confMapping, defaultConfiguration(dep).toString) { missingMapping(p.name, dep.name, c) } + println("Dep: " + dep.name + " mapping: " + confMapping + " depConf: " + depConf) + val unvisited = visited.add( (dep, depConf) ) + if(unvisited) visit(dep, depConf) + } + } + visit(project, conf.toString) + + val productsTasks = new LinkedHashSet[Task[Seq[Attributed[File]]]] + for( (dep: ClasspathProject, conf) <- visited ) + if(dep ne project) productsTasks += products(dep, conf) + (productsTasks.toSeq.join) named(project.name + "/join") map(_.flatten) + } def parseSimpleConfigurations(confString: String): Map[String, String] = confString.split(";").flatMap( conf => trim(conf.split("->",2)) match { - case x :: Nil => (x,x) :: Nil - case x :: y :: Nil => trim(x.split(",")) map { a => (a,y) } + case x :: Nil => for(a <- parseList(x)) yield (a,a) + case x :: y :: Nil => for(a <- parseList(x); b <- parseList(x)) yield (a,b) case _ => error("Invalid configuration '" + conf + "'") // shouldn't get here } ).toMap + + def parseList(s: String): Seq[String] = trim(s split ",") private def trim(a: Array[String]): List[String] = a.toList.map(_.trim) def missingMapping(from: String, to: String, conf: String) = error("No configuration mapping defined from '" + from + "' to '" + to + "' for '" + conf + "'") + def missingConfiguration(in: String, conf: String) = + error("Configuration '" + conf + "' not defined in '" + in) + def products(dep: ClasspathProject, conf: String) = + dep.products(dep.configurationMap.getOrElse(conf,missingConfiguration(dep.name, conf))) + def defaultConfiguration(p: Project): Configuration = + p match + { + case cp: ClasspathProject => cp.defaultConfiguration getOrElse Configurations.Default + case _ => Configurations.Default + } } \ No newline at end of file diff --git a/main/DefaultProject.scala b/main/DefaultProject.scala index 555e72631..861be5b17 100644 --- a/main/DefaultProject.scala +++ b/main/DefaultProject.scala @@ -8,11 +8,14 @@ package sbt import TaskExtra._ import Configurations.{Compile => CompileConfig, Test => TestConfig} import ClasspathProject._ - + import Types._ + import java.io.File abstract class DefaultProject extends TestProject with MultiClasspathProject with ReflectiveClasspathProject { + override def watchPaths: PathFinder = descendents("src","*") + def javacOptions: Seq[String] = Nil def scalacOptions: Seq[String] = Nil @@ -49,7 +52,7 @@ abstract class DefaultProject extends TestProject with MultiClasspathProject wit val scalaSrc = base / "scala" val out = "target" / compilers.scalac.scalaInstance.actualVersion - val sources = ((javaSrc +++ scalaSrc) ** sourceFilter) +++ (if(configuration == Compile) (".": Path) * sourceFilter else Path.emptyPathFinder) + val sources = descendents((javaSrc +++ scalaSrc), sourceFilter) +++ (if(configuration == Compile) (".": Path) * (sourceFilter -- defaultExcludes) else Path.emptyPathFinder) println("Sources: " + sources) val classes = classesDirectory(configuration) val classpath = (classes +: data(prodcp)) ++ data(cp) diff --git a/main/MultiProject.scala b/main/MultiProject.scala index b42a20f7f..3b1c65478 100644 --- a/main/MultiProject.scala +++ b/main/MultiProject.scala @@ -143,7 +143,7 @@ object MultiContext } } -trait Project extends Tasked with HistoryEnabled with Member[Project] with Named with ConsoleTask +trait Project extends Tasked with HistoryEnabled with Member[Project] with Named with ConsoleTask with Watched { val info: ProjectInfo @@ -186,7 +186,9 @@ trait ProjectExtra /** Converts a String to a path relative to the project directory of this project. */ implicit def path(component: String): Path = info.projectDirectory / component /** Converts a String to a simple name filter. * has the special meaning: zero or more of any character */ - implicit def filter(simplePattern: String): NameFilter = GlobFilter(simplePattern) + implicit def globFilter(simplePattern: String): NameFilter = GlobFilter(simplePattern) + def defaultExcludes: FileFilter = MultiProject.defaultExcludes + def descendents(path: PathFinder, filter: FileFilter): PathFinder = path.descendentsExcept(filter, defaultExcludes) } trait ReflectiveProject extends Project {