work on special settings, TupleN/FunctionN interface for map/flatMap

This commit is contained in:
Mark Harrah 2011-01-30 23:19:28 -05:00
parent 1c9caf40a3
commit b9e4c9299b
5 changed files with 95 additions and 622 deletions

View File

@ -11,7 +11,7 @@ package sbt
import scala.annotation.tailrec
import collection.mutable
import Compile.{Compilers,Inputs}
import Project.{ScopedKey, Setting}
import Project.{AppConfig, ScopedKey, Setting, ThisProject, ThisProjectRef}
import TypeFunctions.{Endo,Id}
import tools.nsc.reporters.ConsoleReporter
@ -108,9 +108,9 @@ object EvaluateTask
val (state, dummyState) = dummy[State]("state")
val (streams, dummyStreams) = dummy[TaskStreams]("streams")
def injectSettings = Seq(
state :== dummyState,
streams :== dummyStreams
def injectSettings: Seq[Project.Setting[_]] = Seq(
state(Scope.GlobalScope) :== dummyState,
streams(Scope.GlobalScope) :== dummyStreams
)
def dummy[T](name: String): (TaskKey[T], Task[T]) = (TaskKey[T](name), dummyTask(name))
@ -195,7 +195,8 @@ object Load
val compilers = Compile.compilers(state.configuration, log)
val evalPluginDef = EvaluateTask.evalPluginDef(state, log) _
val delegates = memo(defaultDelegates)
val config = new LoadBuildConfiguration(stagingDirectory, classpath, loader, compilers, evalPluginDef, delegates, EvaluateTask.injectSettings, log)
val inject: Seq[Project.Setting[_]] = (AppConfig(Scope.GlobalScope) :== state.configuration) +: EvaluateTask.injectSettings
val config = new LoadBuildConfiguration(stagingDirectory, classpath, loader, compilers, evalPluginDef, delegates, inject, log)
apply(base, config)
}
def defaultDelegates: LoadedBuild => Scope => Seq[Scope] = (lb: LoadedBuild) => {
@ -229,7 +230,7 @@ object Load
val loaded = resolveProjects(load(rootBase, config))
val projects = loaded.units
lazy val rootEval = lazyEval(loaded.units(loaded.root).unit)
val settings = buildConfigurations(loaded, getRootProject(projects), config.injectSettings, rootEval)
val settings = config.injectSettings ++ buildConfigurations(loaded, getRootProject(projects), rootEval)
val delegates = config.delegates(loaded)
val data = Project.make(settings)(delegates)
val index = structureIndex(data)
@ -250,14 +251,17 @@ object Load
}
def isProjectThis(s: Setting[_]) = s.key.scope.project == This
def buildConfigurations(loaded: LoadedBuild, rootProject: URI => String, injectSettings: Seq[Setting[_]], rootEval: () => Eval): Seq[Setting[_]] =
def buildConfigurations(loaded: LoadedBuild, rootProject: URI => String, rootEval: () => Eval): Seq[Setting[_]] =
loaded.units.toSeq flatMap { case (uri, build) =>
val eval = if(uri == loaded.root) rootEval else lazyEval(build.unit)
val pluginSettings = build.unit.plugins.plugins
val (pluginThisProject, pluginGlobal) = pluginSettings partition isProjectThis
val projectSettings = build.defined flatMap { case (id, project) =>
val srcs = configurationSources(project.base)
val settings = injectSettings ++ project.settings ++ pluginThisProject ++ configurations(srcs, eval, build.imports)
val settings =
(ThisProject :== project) +:
(ThisProjectRef :== ProjectRef(Some(uri), Some(id))) +:
(project.settings ++ pluginThisProject ++ configurations(srcs, eval, build.imports))
// map This to thisScope, Select(p) to mapRef(uri, rootProject, p)
transformSettings(projectScope(uri, id), uri, rootProject, settings)

View File

@ -88,6 +88,10 @@ object Project extends Init[Scope]
val SessionKey = AttributeKey[SessionSettings]("session-settings")
val StructureKey = AttributeKey[Load.BuildStructure]("build-structure")
val AppConfig = SettingKey[xsbti.AppConfiguration]("app-configuration")
val ThisProject = SettingKey[Project]("project")
val ThisProjectRef = SettingKey[ProjectRef]("project-ref")
}
import SessionSettings._

View File

@ -87,11 +87,13 @@ object Scoped
protected final val scopedList = scoped :^: KNil
final def :==(value: S): Setting[S] = :=(value)
final def :==(value: SettingKey[S]): Setting[S] = :-(value app identity)
final def := (value: => S): Setting[S] = Project.value(scoped)(value)
final def :~ (f: S => S): Setting[S] = Project.update(scoped)(f)
final def :- (app: Apply[S]): Setting[S] = app toSetting scoped
final def apply[T](f: S => T): Apply[T] = Apply.mk(scopedList)(hl => f(hl.head))
final def app[T](f: S => T): Apply[T] = apply(f)
final def get(settings: Settings[Scope]): Option[S] = settings.get(scope, key)
}
@ -108,10 +110,12 @@ object Scoped
def :==(value: Task[S]): ScS = Project.value(scoped)( value )
def := (value: => S): ScS = :==(task(value))
def :== (v: TaskKey[S]): ScS = Project.app(scoped, ScopedKey(scope, v.key) :^: KNil)(_.head)
def :== (v: SettingKey[S]): ScS = :-( v app const )
def :~ (f: S => S): ScS = Project.update(scoped)( _ map f )
def :- (app: App[S]): ScS = app toSetting scoped
def setting: ScopedSetting[Task[S]] = scopedSetting(scope, key)
def get(settings: Settings[Scope]): Option[Task[S]] = settings.get(scope, key)
type App[T] = Apply[Task[T]]
@ -204,7 +208,82 @@ object Scoped
tasks flatMapR f
}
}
// this is the least painful arrangement I came up with
implicit def t2ToTable2[A,B](t2: (ScopedTaskable[A], ScopedTaskable[B]) ): RichTaskable2[A,B] = new RichTaskable2(t2)
implicit def t3ToTable3[A,B,C](t3: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C]) ): RichTaskable3[A,B,C] = new RichTaskable3(t3)
implicit def t4ToTable4[A,B,C,D](t4: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D]) ): RichTaskable4[A,B,C,D] = new RichTaskable4(t4)
implicit def t5ToTable5[A,B,C,D,E](t5: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E]) ): RichTaskable5[A,B,C,D,E] = new RichTaskable5(t5)
implicit def t6ToTable6[A,B,C,D,E,F](t6: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F]) ): RichTaskable6[A,B,C,D,E,F] = new RichTaskable6(t6)
implicit def t7ToTable7[A,B,C,D,E,F,G](t7: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F], ScopedTaskable[G]) ): RichTaskable7[A,B,C,D,E,F,G] = new RichTaskable7(t7)
implicit def t8ToTable8[A,B,C,D,E,F,G,H](t8: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F], ScopedTaskable[G], ScopedTaskable[H]) ): RichTaskable8[A,B,C,D,E,F,G,H] = new RichTaskable8(t8)
implicit def t9ToTable9[A,B,C,D,E,F,G,H,I](t9: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F], ScopedTaskable[G], ScopedTaskable[H], ScopedTaskable[I]) ): RichTaskable9[A,B,C,D,E,F,G,H,I] = new RichTaskable9(t9)
sealed abstract class RichTaskables[In <: HList](keys: KList[ScopedTaskable, In])
{
type App[T] = Apply[Task[T]]
type Fun[M[_],Ret]
protected def convertH[Ret](f: Fun[Id,Ret]): In => Ret
protected def convertK[M[_],Ret](f: Fun[M,Ret]): KList[M,In] => Ret
private[this] val red = reduced(keys)
def flatMap[T](f: Fun[Id,Task[T]]): App[T] = red.combine(Combine.flatMapR, convertH(f) compose allM)
def flatMapR[T](f: Fun[Result,Task[T]]): App[T] = red.combine(Combine.flatMapR, convertK(f))
def map[T](f: Fun[Id, T]): App[T] = red.combine[Id,T](Combine.mapR, convertH(f) compose allM)
def mapR[T](f: Fun[Result,T]): App[T] = red.combine[Id,T](Combine.mapR, convertK(f))
def flatFailure[T](f: Seq[Incomplete] => Task[T]): App[T] = red.combine(Combine.flatMapR, f compose anyFailM)
def mapFailure[T](f: Seq[Incomplete] => T): App[T] = red.combine[Id,T](Combine.mapR, f compose anyFailM)
}
final class RichTaskable2[A,B](t2: (ScopedTaskable[A], ScopedTaskable[B])) extends RichTaskables(t2._1 :^: t2._2 :^: KNil)
{
type Fun[M[_],Ret] = (M[A],M[B]) => Ret
protected def convertH[R](f: (A,B) => R) = { case a :+: b :+: HNil => f(a,b) }
protected def convertK[M[_],R](f: (M[A],M[B]) => R) = { case a :^: b :^: KNil => f(a,b) }
}
final class RichTaskable3[A,B,C](t3: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C])) extends RichTaskables(t3._1 :^: t3._2 :^: t3._3 :^: KNil)
{
type Fun[M[_],Ret] = (M[A],M[B],M[C]) => Ret
protected def convertH[R](f: Fun[Id,R]) = { case a :+: b :+: c :+: HNil => f(a,b,c) }
protected def convertK[M[_],R](f: Fun[M,R]) = { case a :^: b :^: c :^: KNil => f(a,b,c) }
}
final class RichTaskable4[A,B,C,D](t4: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D])) extends RichTaskables(t4._1 :^: t4._2 :^: t4._3 :^: t4._4 :^: KNil)
{
type Fun[M[_],Ret] = (M[A],M[B],M[C],M[D]) => Ret
protected def convertH[R](f: Fun[Id,R]) = { case a :+: b :+: c :+: d :+: HNil => f(a,b,c,d) }
protected def convertK[M[_],R](f: Fun[M,R]) = { case a :^: b :^: c :^: d :^: KNil => f(a,b,c,d) }
}
final class RichTaskable5[A,B,C,D,E](t5: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E])) extends RichTaskables(t5._1 :^: t5._2 :^: t5._3 :^: t5._4 :^: t5._5 :^: KNil)
{
type Fun[M[_],Ret] = (M[A],M[B],M[C],M[D],M[E]) => Ret
protected def convertH[R](f: Fun[Id,R]) = { case a :+: b :+: c :+: d :+: e :+: HNil => f(a,b,c,d,e) }
protected def convertK[M[_],R](f: Fun[M,R]) = { case a :^: b :^: c :^: d :^: e :^: KNil => f(a,b,c,d,e) }
}
final class RichTaskable6[A,B,C,D,E,F](t6: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F])) extends RichTaskables(t6._1 :^: t6._2 :^: t6._3 :^: t6._4 :^: t6._5 :^: t6._6 :^: KNil)
{
type Fun[M[_],Ret] = (M[A],M[B],M[C],M[D],M[E],M[F]) => Ret
protected def convertH[R](z: Fun[Id,R]) = { case a :+: b :+: c :+: d :+: e :+: f :+: HNil => z(a,b,c,d,e,f) }
protected def convertK[M[_],R](z: Fun[M,R]) = { case a :^: b :^: c :^: d :^: e :^: f :^: KNil => z(a,b,c,d,e,f) }
}
final class RichTaskable7[A,B,C,D,E,F,G](t7: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F], ScopedTaskable[G])) extends RichTaskables(t7._1 :^: t7._2 :^: t7._3 :^: t7._4 :^: t7._5 :^: t7._6 :^: t7._7 :^: KNil)
{
type Fun[M[_],Ret] = (M[A],M[B],M[C],M[D],M[E],M[F],M[G]) => Ret
protected def convertH[R](z: Fun[Id,R]) = { case a :+: b :+: c :+: d :+: e :+: f :+: g :+: HNil => z(a,b,c,d,e,f,g) }
protected def convertK[M[_],R](z: Fun[M,R]) = { case a :^: b :^: c :^: d :^: e :^: f :^: g :^: KNil => z(a,b,c,d,e,f,g) }
}
final class RichTaskable8[A,B,C,D,E,F,G,H](t8: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F], ScopedTaskable[G], ScopedTaskable[H])) extends RichTaskables(t8._1 :^: t8._2 :^: t8._3 :^: t8._4 :^: t8._5 :^: t8._6 :^: t8._7 :^: t8._8 :^: KNil)
{
type Fun[M[_],Ret] = (M[A],M[B],M[C],M[D],M[E],M[F],M[G],M[H]) => Ret
protected def convertH[R](z: Fun[Id,R]) = { case a :+: b :+: c :+: d :+: e :+: f :+: g :+: h :+: HNil => z(a,b,c,d,e,f,g,h) }
protected def convertK[M[_],R](z: Fun[M,R]) = { case a :^: b :^: c :^: d :^: e :^: f :^: g :^: h :^: KNil => z(a,b,c,d,e,f,g,h) }
}
final class RichTaskable9[A,B,C,D,E,F,G,H,I](t9: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F], ScopedTaskable[G], ScopedTaskable[H], ScopedTaskable[I])) extends RichTaskables(t9._1 :^: t9._2 :^: t9._3 :^: t9._4 :^: t9._5 :^: t9._6 :^: t9._7 :^: t9._8 :^: t9._9 :^: KNil)
{
type Fun[M[_],Ret] = (M[A],M[B],M[C],M[D],M[E],M[F],M[G],M[H],M[I]) => Ret
protected def convertH[R](z: Fun[Id,R]) = { case a :+: b :+: c :+: d :+: e :+: f :+: g :+: h :+: i :+: HNil => z(a,b,c,d,e,f,g,h,i) }
protected def convertK[M[_],R](z: Fun[M,R]) = { case a :^: b :^: c :^: d :^: e :^: f :^: g :^: h :^: i :^: KNil => z(a,b,c,d,e,f,g,h,i) }
}
// this doesn't actually work for mixed KLists because the compiler crashes trying to infer the bound when constructing the KList
implicit def richTaskableKeys[HL <: HList](in: KList[ScopedTaskable, HL]): RichTaskableKeys[HL] = new RichTaskableKeys(in)
final class RichTaskableKeys[In <: HList](keys: KList[ScopedTaskable, In])
{

View File

@ -1,350 +0,0 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
import std._
import inc.Analysis
import TaskExtra._
import ClasspathProject._
import java.io.File
import Path._
import Types._
import scala.xml.{Node => XNode,NodeSeq}
trait ClasspathProject
{
def name: String
def configurations: Seq[Configuration]
def defaultConfiguration: Option[Configuration]
val products: Classpath
val unmanagedClasspath: Classpath
val managedClasspath: Classpath
val internalDependencyClasspath: Classpath
lazy val externalDependencyClasspath: Classpath =
TaskMap { (configuration: Configuration) =>
val un = unmanagedClasspath(configuration)
val m = managedClasspath(configuration)
(un, m) map concat[Attributed[File]]
}
lazy val dependencyClasspath: Classpath =
TaskMap { (configuration: Configuration) =>
val external = externalDependencyClasspath(configuration)
val internal = internalDependencyClasspath(configuration)
(external, internal) map concat[Attributed[File]]
}
lazy val fullClasspath: Classpath =
TaskMap { case (configuration: Configuration) =>
val dep = dependencyClasspath(configuration)
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
{
val ivyConfiguration: Task[IvyConfiguration]
val moduleSettings: Task[ModuleSettings]
val unmanagedBase: Task[File]
def cacheDirectory: File
val updateConfig: Task[UpdateConfiguration]
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 defaultExcludes: FileFilter
override lazy val managedClasspath: Classpath =
TaskMap { configuration =>
update map { x => attributed(x.getOrElse(configuration, error("No such configuration '" + configuration.toString + "'")) ) }
}
lazy val unmanagedClasspath: Classpath =
TaskMap { configuration =>
unmanagedBase map { base =>
attributed( (base * (classpathFilter -- defaultExcludes) +++
(base / configuration.toString).descendentsExcept(classpathFilter, defaultExcludes)).getFiles.toSeq )
}
}
lazy val update = (ivyModule, updateConfig) map cachedUpdate(cacheDirectory / "update", configurationMap)
}
trait PublishProject extends BasicClasspathProject
{
val publishConfig: Task[PublishConfiguration]
val publishLocalConfig: Task[PublishConfiguration]
val makePomConfig: Task[MakePomConfiguration]
def packageToPublish: Seq[Task[_]]
def publishMavenStyle = true
def deliverDepends = if(publishMavenStyle) makePom :: Nil else packageToPublish
lazy val makePom = (ivyModule, makePomConfig) map(IvyActions.makePom) dependsOn( packageToPublish : _*)
lazy val deliver = (ivyModule, publishConfig) map cachedPublish(cacheDirectory / "deliver")(IvyActions.deliver) dependsOn(deliverDepends : _*)
lazy val deliverLocal = (ivyModule, publishLocalConfig) map cachedPublish(cacheDirectory / "deliver-local")(IvyActions.deliver) dependsOn(deliverDepends : _*)
lazy val publish = (ivyModule, publishConfig) map cachedPublish(cacheDirectory / "publish")(IvyActions.publish) dependsOn(deliver)
lazy val publishLocal = (ivyModule, publishLocalConfig) map cachedPublish(cacheDirectory / "publish-local")(IvyActions.publish) dependsOn(deliverLocal)
}
trait DefaultClasspathProject extends BasicClasspathProject with PublishProject with Project
{
def outputDirectory: Path
def projectID: ModuleID
def baseResolvers: Seq[Resolver]
lazy val resolvers: Task[Seq[Resolver]] = task { baseResolvers }
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.Quiet)
}
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)
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: Seq[ModuleID] = ReflectUtilities.allVals[ModuleID](this).toSeq.map(_._2)
def managedDependencyPath: Path = info.projectDirectory / "lib_managed"
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
def moduleID = normalizedName
def pomFile: File
def publishTo: Resolver = error("Repository for publishing is not specified.")
lazy val internalDependencyClasspath: Classpath = internalDependencies(this)
lazy val unmanagedBase = task { dependencyPath.asFile }
lazy val moduleSettings: Task[ModuleSettings] = task {
new InlineConfiguration(projectID, libraryDependencies, ivyXML, configurations, defaultConfiguration, ivyScala, ivyValidate)
}
lazy val publishConfig = task { publishConfiguration( publishPatterns(outputDirectory), resolverName = publishTo.name ) }
lazy val publishLocalConfig = task { publishConfiguration( publishPatterns(outputDirectory, true) ) }
lazy val makePomConfig = task { makePomConfiguration(pomFile) }
}
trait MultiClasspathProject extends DefaultClasspathProject
{
def dependencies: Seq[ProjectDependency.Classpath]
def name: String
def organization: String
def version: String
def pomFile: File = outputDirectory / (moduleID + "-" + version + ".pom")
def artifacts: Seq[Artifact] = Nil
def projectDependencies: Seq[ModuleID] =
resolvedDependencies(this) collect { case (p: DefaultClasspathProject, conf) => p.projectID.copy(configurations = conf) }
lazy val projectResolver =
depMap(this) map { m =>
new RawRepository(new ProjectResolver("inter-project", m))
}
override def projectID = ModuleID(organization, moduleID, version).cross(true).artifacts(artifacts.toSeq : _*)
override def libraryDependencies: Seq[ModuleID] = super.libraryDependencies ++ projectDependencies
override lazy val resolvers: Task[Seq[Resolver]] = projectResolver map { _ +: baseResolvers }
}
trait ReflectiveClasspathProject extends DefaultClasspathProject
{
private[this] def vals[T: Manifest] = ReflectUtilities.allVals[T](this).toSeq.map(_._2)
def configurations: Seq[Configuration] = vals[Configuration] ++ Configurations.defaultMavenConfigurations
def baseResolvers: Seq[Resolver] = Resolver.withDefaultResolvers(vals[Resolver] )
}
import org.apache.ivy.core.module
import module.id.ModuleRevisionId
import module.descriptor.ModuleDescriptor
object ClasspathProject
{
type Classpath = Configuration => Task[Seq[Attributed[File]]]
val Analyzed = AttributeKey[Analysis]("analysis")
def attributed[T](in: Seq[T]): Seq[Attributed[T]] = in map Attributed.blank
def analyzed[T](data: T, analysis: Analysis) = Attributed.blank(data).put(Analyzed, analysis)
def analyzed(compile: Task[Analysis], inputs: Task[Compile.Inputs]): Task[Attributed[File]] =
(compile, inputs) map { case analysis :+: i :+: HNil =>
analyzed(i.config.classesDirectory, analysis)
}
def makeProducts(compile: Task[Analysis], inputs: Task[Compile.Inputs], name: String, prefix: String): Task[Seq[Attributed[File]]] =
{
def mkName(postfix: String) = name + "/" + prefix + postfix
analyzed(compile, inputs) named(mkName("analyzed")) map { _ :: Nil } named(mkName("products"))
}
def concat[A]: (Seq[A], Seq[A]) => Seq[A] = _ ++ _
def extractAnalysis[T](a: Attributed[T]): (T, Analysis) =
(a.data, a.metadata get Analyzed getOrElse Analysis.Empty)
def analysisMap[T](cp: Seq[Attributed[T]]): Map[T, Analysis] =
(cp map extractAnalysis).toMap
def data[T](in: Seq[Attributed[T]]): Seq[T] = in.map(_.data)
def taskData[T](in: Task[Seq[Attributed[T]]]): Task[Seq[T]] = in map data
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): Seq[(Project, Option[String])] =
p.dependencies map { cp =>
(resolveProject(cp.project, p), cp.configuration)
}
def resolveProject(e: Either[File, Project], context: Project): Project =
e match {
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 = asSet(new java.util.LinkedHashSet[(Project,String)])
def visit(p: Project, c: String)
{
val applicableConfigs = allConfigs(p, c)
for(ac <- applicableConfigs)
visited add (p, ac)
for( (dep, confMapping) <- resolvedDependencies(p))
{
val depConf = mapped(c, confMapping, defaultConfiguration(dep).toString) { missingMapping(p.name, dep.name, c) }
if( ! visited( (dep, depConf) ) )
visit(dep, depConf)
}
}
visit(project, conf.name)
val productsTasks = asSet(new java.util.LinkedHashSet[Task[Seq[Attributed[File]]]])
for( (dep: ClasspathProject, c) <- visited )
if( (dep ne project) || conf.name != c )
productsTasks += products(dep, c)
def name(action: String) = project.name + "/" + conf + "/" + action + "-products"
(productsTasks.toSeq.join) named(name("join")) map(_.flatten) named(name("flatten"))
}
def parseSimpleConfigurations(confString: String): Map[String, String] =
confString.split(";").flatMap( conf =>
trim(conf.split("->",2)) match {
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 allConfigs(dep: Project, conf: String): Seq[String] =
dep match {
case cp: ClasspathProject => Dag.topologicalSort(configuration(cp, conf))(_.extendsConfigs).map(_.name)
case _ => Nil
}
def configuration(dep: ClasspathProject, conf: String): Configuration =
dep.configurationMap.getOrElse(conf,missingConfiguration(dep.name, conf))
def products(dep: ClasspathProject, conf: String) =
dep.products(configuration(dep, conf))
def defaultConfiguration(p: Project): Configuration =
p match
{
case cp: ClasspathProject => cp.defaultConfiguration getOrElse Configurations.Default
case _ => Configurations.Default
}
def makePomConfiguration(file: File, configurations: Option[Iterable[Configuration]] = None, extra: NodeSeq = NodeSeq.Empty, process: XNode => XNode = n => n, filterRepositories: MavenRepository => Boolean = _ => true) =
new MakePomConfiguration(file, configurations, extra, process, filterRepositories)
def publishConfiguration(patterns: PublishPatterns, resolverName: String = "local", status: String = "release", logging: UpdateLogging.Value = UpdateLogging.DownloadOnly) =
new PublishConfiguration(patterns, status, resolverName, None, logging)
def publishPatterns(outputPath: Path, publishIvy: Boolean = false): PublishPatterns =
{
val deliverPattern = (outputPath / "[artifact]-[revision](-[classifier]).[ext]").absolutePath
val srcArtifactPatterns: Seq[String] =
{
val pathPatterns =
(outputPath / "[artifact]-[revision]-[type](-[classifier]).[ext]") ::
(outputPath / "[artifact]-[revision](-[classifier]).[ext]") ::
Nil
pathPatterns.map(_.absolutePath)
}
new PublishPatterns( if(publishIvy) Some(deliverPattern) else None, srcArtifactPatterns)
}
import Cache._
import Types._
import CacheIvy.{classpathFormat, publishIC, updateIC}
def cachedUpdate(cacheFile: File, configMap: Map[String, Configuration]): (IvySbt#Module :+: UpdateConfiguration :+: HNil) => Map[Configuration, Seq[File]] =
{ case module :+: config :+: HNil =>
implicit val updateCache = updateIC
val f = cached(cacheFile) { (conf: IvyConfiguration, settings: ModuleSettings, config: UpdateConfiguration) =>
println("Updating...")
val r = IvyActions.update(module, config)
println("Done updating.")
r
}
val classpaths = f(module.owner.configuration :+: module.moduleSettings :+: config :+: HNil)
val confMap = configMap
classpaths map { case (key, value) => (confMap(key), value) } toMap;
}
// can't cache deliver/publish easily since files involved are hidden behind patterns. publish will be difficult to verify target-side anyway
def cachedPublish(cacheFile: File)(g: (IvySbt#Module, PublishConfiguration) => Unit): (IvySbt#Module :+: PublishConfiguration :+: HNil) => Unit =
{ case module :+: config :+: HNil =>
/* implicit val publishCache = publishIC
val f = cached(cacheFile) { (conf: IvyConfiguration, settings: ModuleSettings, config: PublishConfiguration) =>*/
g(module, config)
/*}
f(module.owner.configuration :+: module.moduleSettings :+: config :+: HNil)*/
}
}

View File

@ -1,264 +0,0 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
import std._
import Path._
import TaskExtra._
import GlobFilter._
import Transform.Context
import inc.Analysis
import build.{Auto, Build}
import xsbti.AppConfiguration
import MultiProject.transformName
import java.io.File
import annotation.tailrec
object MultiProject
{
val ScalaVersion = AttributeKey[String]("scala-version")
val defaultExcludes: FileFilter = (".*" - ".") || HiddenFileFilter
def descendents(base: PathFinder, select: FileFilter) = base.descendentsExcept(select, defaultExcludes)
def children(base: PathFinder, select: FileFilter) = base * (select -- defaultExcludes)
def projectClassName = classOf[Project].getName
def projectStandard(base: Path) = base / "project"
def projectHidden(base: Path) = base / ".sbt"
def selectProjectDir(base: Path) =
{
val a = projectHidden(base)
val b = projectStandard(base)
if(a.exists) a else b
}
def crossPath(base: Path, instance: ScalaInstance) = base / ("scala_" + instance.actualVersion)
def construct[T <: AnyRef](arg: T)(implicit mf: Manifest[T]): Class[_] => Any = clazz =>
Build.constructor(clazz, mf.erasure) match {
case Some(c) => c.newInstance(arg)
case None => error("Couldn't find constructor with one argument of type " + mf.toString)
}
// externals must not be evaluated until after _all_ projects have been loaded
def load(configuration: AppConfiguration, log: Logger, externals: ExternalProjects)(base: File): Project =
{
if(!base.isDirectory) throw new build.BuildException(base + " is not a project directory")
val projectDir = selectProjectDir(base)
val buildDir = projectDir / "build"
val srcMain = buildDir / "src" / "main"
val javaSrcBase = srcMain / "java"
val sources = children(buildDir +++ projectDir, "*.scala") +++ descendents(buildDir / "scala" +++ buildDir / "java", "*.scala" | "*.java")
val classpath = configuration.provider.mainClasspath.toSeq
val compilers = Compile.compilers(configuration, log)
val target = crossPath(buildDir / "target", compilers.scalac.scalaInstance)
val inputs = Compile.inputs(classpath, sources.getFiles.toSeq, target, Nil, Nil, javaSrcBase :: Nil, Compile.DefaultMaxErrors)(compilers, log)
val analysis = Compile(inputs, log)
val info = ProjectInfo(None, base, projectDir, Nil, None)(configuration, analysis, inputs, load(configuration, log, externals), externals)
val discovered = Build.discover(analysis, Some(false), Auto.Subclass, projectClassName)
val discoveredOrDefault = if(discovered.isEmpty) List(build.ToLoad("sbt.DefaultProject", false)) else discovered
val toLoad = Build.check(discoveredOrDefault, false)
Build.binaries(inputs.config.classpath, toLoad, getClass.getClassLoader)(construct(info)).head.asInstanceOf[Project]
}
def loadExternals(from: Seq[Project], loadImpl: File => Project): Map[File, Project] =
{
def load(loaded: Map[File, Project], file: File): Map[File, Project] =
(loaded get file) match
{
case None => doLoad(loaded, file)
case Some(p) => loaded
}
def doLoad(loaded: Map[File, Project], file: File): Map[File, Project] =
{
val loadedProject = loadImpl(file)
val newMap = loaded.updated(file, loadedProject)
loadAll( externals(loadedProject :: Nil), newMap )
}
def loadAll(files: Set[File], loaded: Map[File, Project]): Map[File, Project] = (loaded /: files)(load)
loadAll( externals(from) , Map.empty)
}
def externals(containers: Seq[Project]): Set[File] =
{
def exts(containers: Seq[Project]): Seq[File] =
containers flatMap { container => externalProjects(container) ++ exts(internalProjects(container)) }
exts(containers).toSet
}
def externalProjects(p: Project) = lefts( allDependencies(p) )
def internalProjects(p: Project) = rights( allDependencies(p) )
def allDependencies(p: Project) = (p.aggregate ++ p.dependencies).map(_.project)
def internalTopologicalSort(root: Project): Seq[Project] =
Dag.topologicalSort(root)(internalProjects)
def topologicalSort(root: Project): Seq[Project] = topologicalSort(root, root.info.externals)
def topologicalSort(root: Project, resolveExternal: File => Project): Seq[Project] =
Dag.topologicalSort(root) { p =>
(externalProjects(p) map resolveExternal) ++ internalProjects(p)
}
def makeContext(root: Project) =
{
val contexts = topologicalSort(root) map { p => (p, ReflectiveContext(p, p.name, root)) }
val externals = root.info.externals
def subs(f: Project => Seq[ProjectDependency]): Project => Seq[Project] = p =>
f(p) map( _.project match { case Left(path) => externals(path); case Right(proj) => proj } )
MultiContext(contexts, root)(subs(_.aggregate), subs(_.dependencies) )
}
def lefts[A,B](e: Seq[Either[A,B]]):Seq[A] = e collect { case Left(l) => l }
def rights[A,B](e: Seq[Either[A,B]]):Seq[B] = e collect { case Right(r)=> r }
def transformName(s: String) =
{
val parts = s.split("-+")
(parts.take(1) ++ parts.drop(1).map(_.capitalize)).mkString
}
}
object MultiContext
{
def identityMap[A,B](in: Iterable[(A,B)]): collection.Map[A,B] =
{
import collection.JavaConversions._
val map: collection.mutable.Map[A, B] = new java.util.IdentityHashMap[A,B]
for( (a,b) <- in) map(a) = b
map
}
def fun[A,B](map: collection.Map[A,B]): A => Option[B] = map get _
def apply[Owner <: AnyRef](contexts: Iterable[(Owner, Context[Owner])], root: Owner)(agg: Owner => Iterable[Owner], deps: Owner => Iterable[Owner]): Context[Owner] = new Context[Owner]
{
val ownerReverse = (for( (owner, context) <- contexts; name <- context.ownerName(owner).toList) yield (name, owner) ).toMap
val ownerMap = identityMap[Task[_],Owner]( for((owner, context) <- contexts; task <- context.allTasks(owner) ) yield (task, owner) )
val context = identityMap(contexts)
def subMap(f: Owner => Iterable[Owner]) = identityMap( contexts.map { case (o,_) => (o,f(o)) })
val aggMap = subMap(agg)
val depMap = subMap(deps)
def rootOwner: Owner = root
def allTasks(owner: Owner): Iterable[Task[_]] = context(owner).allTasks(owner)
def ownerForName(name: String): Option[Owner] = ownerReverse get name
val staticName: Task[_] => Option[String] = t => owner(t) flatMap { o => context(o).staticName(t) }
val ownerName = (o: Owner) => context(o).ownerName(o)
val owner = (t: Task[_]) => ownerMap.get(t)
val aggregate = (o: Owner) => (aggMap get o).toList.flatten
def dependencies(o: Owner): Iterable[Owner] = (depMap get o).toList.flatten
val static = (o: Owner, s: String) => context(o).static(o, s)
}
}
final class MultiNavigation(val self: Project, val selectFun: (Project, Project, State) => State, val selectedProject: Project, val initialProject: Project) extends Navigation
{
type Project = sbt.Project
def parent = self.info.parent map nav
def name = self.name
def select(s: State): State = if(selectedProject == self) s else selectFun(self, initialProject, s)
@tailrec final lazy val root: MultiNavigation = parent match { case Some(p) => p.root; case None => this }
def initial = nav(initialProject)
def closure = projectClosure map nav
def selected = nav(selectedProject)
def projectClosure: Seq[Project] = MultiProject.topologicalSort(initialProject)
private val nav = (p: Project) => new MultiNavigation(p, selectFun, selectedProject, initialProject)
}
final class MultiWatched(val self: Project) extends Watched
{
def watched(p: Project): Seq[Watched] = MultiProject.topologicalSort(p)
def sourcePaths(p: Project): PathFinder = (Path.emptyPathFinder /: watched(p))(_ +++ _.watchPaths)
override def watchPaths = sourcePaths(self)
override def terminateWatch(key: Int): Boolean = self.terminateWatch(key)
}
trait Project extends Tasked with ConsoleTask with Watched
{
val info: ProjectInfo
def name: String = info.name getOrElse error("'name' not overridden")
def normalizedName: String = StringUtilities.normalize(name)
def base = info.projectDirectory
def outputRootPath = base / "target"
def historyPath: Option[File] = Some(outputRootPath / ".history")
def streamBase = outputRootPath / "streams"
implicit def streams = Dummy.Streams
def input = Dummy.In
def state = Dummy.State
def context = Dummy.Context
def aggregate: Seq[ProjectDependency.Execution] = info.dependencies collect { case ex: ProjectDependency.Execution => ex }
def dependencies: Seq[ProjectDependency.Classpath] = info.dependencies collect { case cp: ProjectDependency.Classpath => cp }
type Task[T] = sbt.Task[T]
def act(input: Input, state: State): Option[(Task[State], Execute.NodeView[Task])] =
{
import Dummy._
val context = MultiProject.makeContext(this)
val dummies = new Transform.Dummies(In, State, Streams, Context)
def name(t: Task[_]): String = context.staticName(t.original) getOrElse std.Streams.name(t)
val mklog = LogManager.construct(context)
def getOwner(t: Task[_]) = context.owner(t.original).getOrElse(error("No owner for " + name(t.original) + "\n\t" + t.original))
val actualStreams = std.Streams(t => getOwner(t).streamBase / name(t), mklog )
val injected = new Transform.Injected( input, state, actualStreams )
context.static(this, transformName(input.name)) map { t => (t.merge.map(_ => state), Transform(dummies, injected, context) ) }
}
def help: Seq[Help] = Nil
}
trait ProjectExtra
{
val info: ProjectInfo
/** 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 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
{
private[this] def vals[T: Manifest] = ReflectUtilities.allVals[T](this).toSeq.map(_._2)
override def aggregate: Seq[ProjectDependency.Execution] = vals[ProjectDependency.Execution] ++ vals[Project].map(p => ProjectDependency.Execution(Right(p))) ++ super.aggregate
override def dependencies: Seq[ProjectDependency.Classpath] = vals[ProjectDependency.Classpath] ++ super.dependencies
}
trait ConsoleTask
{
val info: ProjectInfo
lazy val projectConsole = task { Console.sbtDefault(info.compileInputs, this)(ConsoleLogger()) }
}
trait ProjectConstructors extends Project
{
val info: ProjectInfo
//def project(base: Path, name: String, deps: ProjectDependency*): Project = project(path, name, info => new DefaultProject(info), deps: _* )
def project[P <: Project](path: Path, name: String, construct: ProjectInfo => P, deps: ProjectDependency*): P =
construct( info.copy(Some(name), projectDirectory = path.asFile, parent = Some(this), dependencies = deps)() )
def project(base: Path): ProjectDependency.Execution = new ProjectDependency.Execution(Left(base.asFile))
implicit def defaultProjectDependency(p: Project): ProjectDependency = new ProjectDependency.Classpath(Right(p), None)
implicit def dependencyConstructor(p: Project): ProjectDependencyConstructor = dependencyConstructor(Right(p))
implicit def extDependencyConstructor(p: File): ProjectDependencyConstructor = dependencyConstructor(Left(p))
implicit def extDependencyConstructor(p: ProjectDependency.Execution): ProjectDependencyConstructor = dependencyConstructor(p.project)
def dependencyConstructor(p: Either[File, Project]): ProjectDependencyConstructor = new ProjectDependencyConstructor {
def %(conf: String) = new ProjectDependency.Classpath(p, Some(conf))
}
}
sealed trait ProjectDependency { val project: Either[File, Project] }
object ProjectDependency
{
final case class Execution(project: Either[File, Project]) extends ProjectDependency
final case class Classpath(project: Either[File, Project], configuration: Option[String]) extends ProjectDependency
}
sealed trait ProjectDependencyConstructor {
def %(conf: String): ProjectDependency.Classpath
}