From 43d593e281b60df914669fed8b49c5199b2d6b57 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Sun, 12 Sep 2010 23:16:07 -0400 Subject: [PATCH] start of managed project support does proper inter-project resolution still needs proper scope/configuration setup --- main/ClasspathProject.scala | 149 ++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 main/ClasspathProject.scala diff --git a/main/ClasspathProject.scala b/main/ClasspathProject.scala new file mode 100644 index 000000000..352cb8fca --- /dev/null +++ b/main/ClasspathProject.scala @@ -0,0 +1,149 @@ +package sbt + + import std._ + import TaskExtra._ + import ClasspathProject._ + import java.io.File + import Path._ + import scala.xml.NodeSeq + +trait ClasspathProject +{ + def configurations: Seq[Configuration] + def products(configuration: Configuration, intermediate: Boolean): Task[Seq[File]] + def unmanagedClasspath(configuration: Configuration): Task[Seq[File]] + def managedClasspath(configuration: Configuration): Task[Seq[File]] + + def dependencyClasspath(configuration: Configuration): Task[Seq[File]] = + (unmanagedClasspath(configuration), managedClasspath(configuration)) map concat[File] + + def fullClasspath(configuration: Configuration, intermediate: Boolean): Task[Seq[File]] = + (dependencyClasspath(configuration), products(configuration, intermediate) ) map concat[File] +} + +trait BasicClasspathProject extends ClasspathProject +{ + val ivyConfiguration: Task[IvyConfiguration] + val moduleSettings: Task[ModuleSettings] + val unmanagedBase: Task[File] + + lazy val updateConfig: Task[UpdateConfiguration] = task { + new UpdateConfiguration(null, null, true, UpdateLogging.Full) + } + + lazy val ivySbt: Task[IvySbt] = + ivyConfiguration map { conf => new IvySbt(conf) } + + lazy val ivyModule: Task[IvySbt#Module] = + (ivySbt, moduleSettings) map { (ivySbt: IvySbt, settings: ModuleSettings) => + new ivySbt.Module(settings) + } + + def classpathFilter: FileFilter = GlobFilter("*.jar") + def defaultExcludeFilter: FileFilter = MultiProject.defaultExcludes + + override def managedClasspath(configuration: Configuration) = + update map { _.getOrElse(configuration, error("No such configuration '" + configuration.toString + "'")) } + + def unmanagedClasspath(configuration: Configuration): Task[Seq[File]] = + unmanagedBase map { base => + (base * (classpathFilter -- defaultExcludeFilter) +++ + (base / configuration.toString).descendentsExcept(classpathFilter, defaultExcludeFilter)).getFiles.toSeq + } + + import Types._ + lazy val update = (ivyModule, updateConfig) map { case module :+: config :+: HNil => + val confMap = configurations map { conf => (conf.name, conf) } toMap; + IvyActions.update(module, config) map { case (key, value) => (confMap(key), value) } toMap; + } +} + +trait DefaultClasspathProject extends BasicClasspathProject with Project +{ + def projectID: ModuleID + def baseResolvers: Seq[Resolver] = Resolver.withDefaultResolvers(ReflectUtilities.allVals[Resolver](this).toSeq.map(_._2) ) + lazy val resolvers: Task[Seq[Resolver]] = task { baseResolvers } + + def otherResolvers: Seq[Resolver] = Nil + def moduleConfigurations: Seq[ModuleConfiguration] = Nil + + def offline: Boolean = false + def paths: IvyPaths = new IvyPaths(info.projectDirectory, None) + + lazy val ivyConfiguration: Task[IvyConfiguration] = resolvers map { rs => + // TODO: log should be passed directly to IvyActions and pulled from Streams + new InlineIvyConfiguration(paths, rs, otherResolvers, moduleConfigurations, offline, Some(info.globalLock), ConsoleLogger()) + } + + def libraryDependencies: Iterable[ModuleID] = ReflectUtilities.allVals[ModuleID](this).map(_._2) + + def dependencyPath: Path = info.projectDirectory / "lib" + + def ivyXML: NodeSeq = NodeSeq.Empty + def defaultConfiguration: Option[Configuration] = None + def ivyScala: Option[IvyScala] = None + def ivyValidate: Boolean = false + + lazy val unmanagedBase = task { dependencyPath.asFile } + + lazy val moduleSettings: Task[ModuleSettings] = task { + new InlineConfiguration(projectID, libraryDependencies, ivyXML, configurations, defaultConfiguration, ivyScala, ivyValidate) + } +} +trait MultiClasspathProject extends DefaultClasspathProject +{ + def dependencies: Iterable[ProjectDependency.Classpath] + def name: String + def organization: String + def version: String + + def projectDependencies: Iterable[ModuleID] = + ClasspathProject.resolvedDependencies(this) collect { case (p: DefaultClasspathProject, conf) => p.projectID.copy(configurations = conf) } + + lazy val projectResolver = + ClasspathProject.depMap(this) map { m => + new RawRepository(new ProjectResolver("inter-project", m)) + } + + override def projectID = ModuleID(organization, name, version) + override def libraryDependencies: Iterable[ModuleID] = super.libraryDependencies ++ projectDependencies + + override lazy val resolvers: Task[Seq[Resolver]] = projectResolver map { _ +: baseResolvers } +} + + import org.apache.ivy.core.module + import module.id.ModuleRevisionId + import module.descriptor.ModuleDescriptor + +object ClasspathProject +{ + def concat[A]: (Seq[A], Seq[A]) => Seq[A] = _ ++ _ + + def depMap(root: Project): Task[Map[ModuleRevisionId, ModuleDescriptor]] = + depMap(MultiProject.topologicalSort(root).dropRight(1) collect { case cp: DefaultClasspathProject => cp }) + + def depMap(projects: Seq[DefaultClasspathProject]): Task[Map[ModuleRevisionId, ModuleDescriptor]] = + projects.map( _.ivyModule ).join.map { mods => + (mods.map{ mod => + val md = mod.moduleDescriptor + (md.getModuleRevisionId, md) + }).toMap + } + + def resolvedDependencies(p: Project): Iterable[(Project, Option[String])] = + { + import ProjectDependency.Classpath + p.dependencies map { + case Classpath(Left(extPath), conf) => (p.info.externals(extPath), conf) + case Classpath(Right(proj), conf) => (proj, conf) + } + } + + def parseSimpleConfigurations(confString: String): Map[String, String] = + confString.split(";").map( + _.split("->",2).toList.map(_.trim) match { + case x :: Nil => (x,x) + case x :: y :: Nil => (x,y) + } + ).toMap +} \ No newline at end of file