From b6fc7ba0a736bfb81d7b9e3ea50b9ceb44345f4b Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Mon, 3 Oct 2011 09:58:37 -0400 Subject: [PATCH] generalized build loaders --- main/Build.scala | 2 +- main/BuildLoader.scala | 116 ++++++++++++++++++++++++++++++++++------- main/Load.scala | 8 +-- 3 files changed, 102 insertions(+), 24 deletions(-) diff --git a/main/Build.scala b/main/Build.scala index e80f89ca6..81254e510 100644 --- a/main/Build.scala +++ b/main/Build.scala @@ -19,7 +19,7 @@ trait Build def projectDefinitions(baseDirectory: File): Seq[Project] = projects def projects: Seq[Project] = ReflectUtilities.allVals[Project](this).values.toSeq def settings: Seq[Setting[_]] = Defaults.buildCore - def buildResolvers: Seq[BuildLoader.BuildResolver] = Nil + def buildLoaders: Seq[BuildLoader.Components] = Nil } trait Plugin { diff --git a/main/BuildLoader.scala b/main/BuildLoader.scala index 3256bab49..e8fe47a17 100644 --- a/main/BuildLoader.scala +++ b/main/BuildLoader.scala @@ -7,45 +7,121 @@ package sbt import java.net.URI import Load.{BuildUnit, LoadBuildConfiguration} import BuildLoader._ + import Alternatives._ + import Types.{const,idFun} -final class ResolveInfo(val build: URI, val staging: File) -final class BuildLoader(val load: (URI, File) => BuildUnit, val builtIn: BuildResolver, val root: Option[BuildResolver], val nonRoots: List[(URI, BuildResolver)], val fail: URI => Nothing, val config: LoadBuildConfiguration) +final class MultiHandler[S,T](builtIn: S=>Option[T], root: Option[S => Option[T]], nonRoots: List[(URI, S => Option[T])], getURI: S => URI, log: S => Logger) { - import Alternatives._ - import config.{log, stagingDirectory => dir} - - def apply(uri: URI): BuildUnit = load(uri, resolve(new ResolveInfo(uri, dir))) - def resolve(info: ResolveInfo): File = + def applyFun: S => Option[T] = apply _ + def apply(info: S): Option[T] = (baseLoader(info), applyNonRoots(info)) match { - case (None, Nil) => fail(info.build) + case (None, Nil) => None case (None, xs @ (_, nr) :: ignored ) => - if(!ignored.isEmpty) warn("Using first of multiple matching non-root build resolver for " + info.build, log, xs) - nr() + if(!ignored.isEmpty) warn("Using first of multiple matching non-root build resolvers for " + getURI(info), log(info), xs) + Some(nr) case (Some(b), xs) => - if(!xs.isEmpty) warn("Ignoring shadowed non-root build resolver(s) for " + info.build, log, xs) - b() + if(!xs.isEmpty) warn("Ignoring shadowed non-root build resolver(s) for " + getURI(info), log(info), xs) + Some(b) } - def baseLoader: BuildResolver = root match { case Some(rl) => rl | builtIn; case None => builtIn } + def baseLoader: S => Option[T] = root match { case Some(rl) => rl | builtIn; case None => builtIn } - def addNonRoot(uri: URI, loader: BuildResolver) = new BuildLoader(load, builtIn, root, (uri, loader) :: nonRoots, fail, config) - def setRoot(resolver: BuildResolver) = new BuildLoader(load, builtIn, Some(resolver), nonRoots, fail, config) - def applyNonRoots(info: ResolveInfo): List[(URI, () => File)] = + def addNonRoot(uri: URI, loader: S => Option[T]) = new MultiHandler(builtIn, root, (uri, loader) :: nonRoots, getURI, log) + def setRoot(resolver: S => Option[T]) = new MultiHandler(builtIn, Some(resolver), nonRoots, getURI, log) + def applyNonRoots(info: S): List[(URI, T)] = nonRoots flatMap { case (definingURI, loader) => loader(info) map { unit => (definingURI, unit) } } - private[this] def warn(baseMessage: String, log: Logger, matching: Seq[(URI, () => File)]) + private[this] def warn(baseMessage: String, log: Logger, matching: Seq[(URI, T)]) { log.warn(baseMessage) log.debug("Non-root build resolvers defined in:") log.debug(matching.map(_._1).mkString("\n\t")) } } + object BuildLoader { /** in: Build URI and staging directory * out: None if unhandled or Some containing the retrieve function, which returns the directory retrieved to (can be the same as the staging directory) */ - type BuildResolver = ResolveInfo => Option[() => File] - def apply(load: (URI, File) => BuildUnit, builtIn: BuildResolver, fail: URI => Nothing, config: LoadBuildConfiguration): BuildLoader = - new BuildLoader(load, builtIn, None, Nil, fail, config) + type Resolver = ResolveInfo => Option[() => File] + type Builder = BuildInfo => Option[File => BuildUnit] + type Transformer = TransformInfo => BuildUnit + type Loader = LoadInfo => Option[() => BuildUnit] + + final class Components(val resolver: Resolver, val builder: Builder, val transformer: Transformer, val full: Loader) { + def | (cs: Components): Components = new Components(resolver | cs.resolver, builder | cs.builder, seq(transformer, cs.transformer), full | cs.full) + } + def transform(t: Transformer): Components = components(transformer = t) + def resolve(r: Resolver): Components = components(resolver = r) + def build(b: Builder): Components = components(builder = b) + def full(f: Loader): Components = components(full = f) + def components(resolver: Resolver = const(None), builder: Builder = const(None), transformer: Transformer = _.unit, full: Loader = const(None)) = + new Components(resolver, builder, transformer, full) + + def seq(a: Transformer, b: Transformer): Transformer = info => b(info.setUnit(a(info))) + + sealed trait Info { + def uri: URI + def config: LoadBuildConfiguration + def state: State + } + final class ResolveInfo(val uri: URI, val staging: File, val config: LoadBuildConfiguration, val state: State) extends Info + final class BuildInfo(val uri: URI, val config: LoadBuildConfiguration, val state: State) extends Info + final class TransformInfo(val uri: URI, val base: File, val unit: BuildUnit, val config: LoadBuildConfiguration, val state: State) extends Info { + def setUnit(newUnit: BuildUnit): TransformInfo = new TransformInfo(uri, base, newUnit, config, state) + } + + final class LoadInfo(val uri: URI, val staging: File, val config: LoadBuildConfiguration, val state: State, val components: Components) extends Info + + def apply(base: Components, fail: URI => Nothing, s: State, config: LoadBuildConfiguration): BuildLoader = + { + def makeMulti[S <: Info, T](base: S => Option[T]) = new MultiHandler[S,T](base, None, Nil, _.uri, _.config.log) + new BuildLoader(fail, s, config, makeMulti(base.resolver), makeMulti(base.builder), base.transformer, makeMulti(base.full)) + } + + def componentLoader: Loader = (info: LoadInfo) => { + import info.{components, config, staging, state, uri} + val cs = info.components + for { + resolve <- cs.resolver(new ResolveInfo(uri, staging, config, state)) + build <- cs.builder(new BuildInfo(uri, config, state)) + } yield () => { + val base = resolve() + val unit = build(base) + cs.transformer(new TransformInfo(uri, base, unit, config, state)) + } + } +} + +final class BuildLoader( + val fail: URI => Nothing, + val state: State, + val config: LoadBuildConfiguration, + val resolvers: MultiHandler[ResolveInfo, ()=>File], + val builders: MultiHandler[BuildInfo, File=>BuildUnit], + val transformer: Transformer, + val full: MultiHandler[LoadInfo, ()=>BuildUnit]) +{ + def addNonRoot(uri: URI, loaders: Components): BuildLoader = + new BuildLoader(fail, state, config, + resolvers.addNonRoot(uri, loaders.resolver), + builders.addNonRoot(uri, loaders.builder), + seq(transformer, loaders.transformer), + full.addNonRoot(uri, loaders.full) + ) + def setRoot(loaders: Components): BuildLoader = + new BuildLoader(fail, state, config, + resolvers.setRoot(loaders.resolver), + builders.setRoot(loaders.builder), + seq(loaders.transformer, transformer), + full.setRoot(loaders.full) + ) + def components = new Components(resolvers.applyFun, builders.applyFun, transformer, full.applyFun) + def apply(uri: URI): BuildUnit = + { + val info = new LoadInfo(uri, config.stagingDirectory, config, state, components) + val load = full(info) getOrElse fail(uri) + load() + } } \ No newline at end of file diff --git a/main/Load.scala b/main/Load.scala index de2e31ca2..e31782c6f 100644 --- a/main/Load.scala +++ b/main/Load.scala @@ -226,9 +226,11 @@ object Load def load(file: File, s: State, config: LoadBuildConfiguration): PartBuild = { - val loader = (uri: URI, local: File) => loadUnit(uri, local, s, config) val fail = (uri: URI) => error("Invalid build URI: " + uri) - val builtinLoader = BuildLoader(loader, info => RetrieveUnit(info.staging, info.build), fail, config) + val resolver = (info: BuildLoader.ResolveInfo) => RetrieveUnit(info.staging, info.uri) + val build = (info: BuildLoader.BuildInfo) => Some((base: File) => loadUnit(info.uri, base, info.state, info.config)) + val components = BuildLoader.components(resolver, build, full = BuildLoader.componentLoader) + val builtinLoader = BuildLoader(components, fail, s, config) load(file, builtinLoader) } def load(file: File, loaders: BuildLoader): PartBuild = loadURI(IO.directoryURI(file), loaders) @@ -240,7 +242,7 @@ object Load new PartBuild(uri, map) } def addResolvers(unit: BuildUnit, isRoot: Boolean, loaders: BuildLoader): BuildLoader = - unit.definitions.builds.flatMap(_.buildResolvers) match + unit.definitions.builds.flatMap(_.buildLoaders) match { case Nil => loaders case x :: xs =>