mirror of https://github.com/sbt/sbt.git
multi-project model based on Settings and ProjectRef
This commit is contained in:
parent
7ced3c0406
commit
087bc569e0
|
|
@ -288,6 +288,7 @@ object Resolver
|
|||
object Configurations
|
||||
{
|
||||
def config(name: String) = new Configuration(name)
|
||||
def default = defaultMavenConfigurations
|
||||
def defaultMavenConfigurations = Compile :: Runtime :: Test :: Provided :: System :: Optional :: Sources :: Javadoc :: Nil
|
||||
|
||||
lazy val Default = config("default")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,492 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2011 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import compile.{Discovered,Discovery,Eval}
|
||||
import classpath.ClasspathUtilities
|
||||
import inc.Analysis
|
||||
import scala.annotation.tailrec
|
||||
import Compile.{Compilers,Inputs}
|
||||
import Project.{ScopedKey, Setting}
|
||||
import TypeFunctions.{Endo,Id}
|
||||
import tools.nsc.reporters.ConsoleReporter
|
||||
|
||||
// name is more like BuildDefinition, but that is too long
|
||||
trait Build
|
||||
{
|
||||
def projects: Seq[Project]
|
||||
}
|
||||
trait Plugin
|
||||
{
|
||||
def settings: Seq[Project.Setting[_]]
|
||||
}
|
||||
|
||||
object Build
|
||||
{
|
||||
def default(base: File): Build = new Build { def projects = defaultProject("default", base) :: Nil }
|
||||
def defaultProject(id: String, base: File): Project = Project(id, base)
|
||||
}
|
||||
object RetrieveUnit
|
||||
{
|
||||
def apply(tempDir: File, base: URI): File =
|
||||
{
|
||||
lazy val tmp = temporary(tempDir, base)
|
||||
base.getScheme match
|
||||
{
|
||||
case "file" => val f = new File(base); if(f.isDirectory) f else error("Not a directory: '" + base + "'")
|
||||
case "git" => gitClone(base, tmp); tmp
|
||||
case "http" | "https" => downloadAndExtract(base, tmp); tmp
|
||||
case _ => error("Unknown scheme in '" + base + "'")
|
||||
}
|
||||
}
|
||||
def downloadAndExtract(base: URI, tempDir: File): Unit = if(!tempDir.exists) IO.unzipURL(base.toURL, tempDir)
|
||||
def temporary(tempDir: File, uri: URI): File = new File(tempDir, hash(uri))
|
||||
def hash(uri: URI): String = Hash.toHex(Hash(uri.toASCIIString))
|
||||
|
||||
import Process._
|
||||
def gitClone(base: URI, tempDir: File): Unit =
|
||||
if(!tempDir.exists) ("git" :: "clone" :: base.toASCIIString :: tempDir.getAbsolutePath :: Nil) ! ;
|
||||
}
|
||||
object EvaluateConfigurations
|
||||
{
|
||||
def apply(eval: Eval, srcs: Seq[File]): Seq[Setting[_]] =
|
||||
srcs flatMap { src => evaluateConfiguration(eval, src) }
|
||||
def evaluateConfiguration(eval: Eval, src: File): Seq[Setting[_]] =
|
||||
evaluateConfiguration(eval, src.getPath, IO.readLines(src))
|
||||
def evaluateConfiguration(eval: Eval, name: String, lines: Seq[String]): Seq[Setting[_]] =
|
||||
{
|
||||
val (importExpressions, settingExpressions) = splitExpressions(name, lines)
|
||||
for(settingExpression <- settingExpressions) yield evaluateSetting(eval, name, importExpressions, settingExpression)
|
||||
}
|
||||
|
||||
def evaluateSetting(eval: Eval, name: String, imports: Seq[String], expression: String): Setting[_] =
|
||||
{
|
||||
// TODO: Eval needs to be expanded to be able to:
|
||||
// handle multiple expressions at once (for efficiency and better error handling)
|
||||
// accept the source name for error display
|
||||
// accept imports to use
|
||||
eval.eval[Setting[_]](expression)
|
||||
}
|
||||
def splitExpressions(name: String, lines: Seq[String]): (Seq[String], Seq[String]) =
|
||||
{
|
||||
val blank = (_: String).trim.isEmpty
|
||||
val importOrBlank = (t: String) => blank(t) || (t.trim startsWith "import ")
|
||||
|
||||
val (imports, settings) = lines span importOrBlank
|
||||
(imports dropWhile blank, groupedLines(settings, blank))
|
||||
}
|
||||
def groupedLines(lines: Seq[String], delimiter: String => Boolean): Seq[String] =
|
||||
{
|
||||
@tailrec def group0(lines: Seq[String], delimiter: String => Boolean, accum: Seq[String]): Seq[String] =
|
||||
{
|
||||
val start = lines dropWhile delimiter
|
||||
val (next, tail) = start.span (s => !delimiter(s))
|
||||
group0(tail, delimiter, next.mkString("\n") +: accum)
|
||||
}
|
||||
group0(lines, delimiter, Nil)
|
||||
}
|
||||
}
|
||||
object EvaluateTask
|
||||
{
|
||||
import Load.BuildStructure
|
||||
import Project.display
|
||||
import std.{TaskExtra,Transform}
|
||||
import TaskExtra._
|
||||
|
||||
type Streams = std.Streams[ScopedKey[Task[_]]]
|
||||
type TaskStreams = std.TaskStreams[ScopedKey[Task[_]]]
|
||||
|
||||
val SystemProcessors = Runtime.getRuntime.availableProcessors
|
||||
val PluginTaskKey = TaskKey[(Seq[File], Analysis)]("plugin-task")
|
||||
val GlobalPath = "$global"
|
||||
|
||||
val (state, dummyState) = dummy[State]("state")
|
||||
val (streams, dummyStreams) = dummy[TaskStreams]("streams")
|
||||
|
||||
def injectSettings = Seq(
|
||||
state :== dummyState,
|
||||
streams :== dummyStreams
|
||||
)
|
||||
|
||||
def dummy[T](name: String): (TaskKey[T], Task[T]) = (TaskKey[T](name), dummyTask(name))
|
||||
def dummyTask[T](name: String): Task[T] = task( error("Dummy task '" + name + "' did not get converted to a full task.") ) named name
|
||||
|
||||
def evalPluginDef(state: State, log: Logger)(pluginDef: BuildStructure): (Seq[File], Analysis) =
|
||||
{
|
||||
val evaluated = evaluateTask(pluginDef, ScopedKey(Scope.ThisScope, PluginTaskKey.key), state)
|
||||
val result = evaluated getOrElse error("Plugin task does not exist for plugin definition at " + pluginDef.root)
|
||||
processResult(result, log)
|
||||
}
|
||||
def evaluateTask[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors): Option[Result[T]] =
|
||||
for( (task, toNode) <- getTask(structure, taskKey, state) ) yield
|
||||
runTask(task, checkCycles, maxWorkers)(toNode)
|
||||
|
||||
def getTask[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State): Option[(Task[T], Execute.NodeView[Task])] =
|
||||
{
|
||||
val x = transform(structure, "target" :: "log" :: Nil, dummyStreams, dummyState, state)
|
||||
val thisScope = Scope(Select(Project.currentRef(state)), Global, Global, Global)
|
||||
val resolvedScope = Scope.replaceThis(thisScope)( taskKey.scope )
|
||||
for( t <- structure.data.get(resolvedScope, taskKey.key)) yield
|
||||
(t, x)
|
||||
}
|
||||
|
||||
def runTask[Task[_] <: AnyRef, T](root: Task[T], checkCycles: Boolean, maxWorkers: Int)(implicit taskToNode: Execute.NodeView[Task]): Result[T] =
|
||||
{
|
||||
val (service, shutdown) = CompletionService[Task[_], Completed](maxWorkers)
|
||||
|
||||
val x = new Execute[Task](checkCycles)(taskToNode)
|
||||
try { x.run(root)(service) } finally { shutdown() }
|
||||
}
|
||||
|
||||
def transform(structure: BuildStructure, logRelativePath: Seq[String], streamsDummy: Task[TaskStreams], stateDummy: Task[State], state: State) =
|
||||
{
|
||||
val streams = mkStreams(structure, logRelativePath)
|
||||
val dummies = new Transform.Dummies(stateDummy :^: KNil, streamsDummy)
|
||||
val inject = new Transform.Injected(state :+: HNil, streams)
|
||||
Transform(dummies, inject)(structure.index.taskToKey)
|
||||
}
|
||||
|
||||
def mkStreams(structure: BuildStructure, logRelativePath: Seq[String]): Streams =
|
||||
std.Streams( path(structure, logRelativePath), display, LogManager.construct(structure.data) )
|
||||
|
||||
def path(structure: BuildStructure, sep: Seq[String])(scoped: ScopedKey[_]): File =
|
||||
{
|
||||
val (base, sub) = projectPath(structure, scoped)
|
||||
resolvePath(base, sep ++ sub ++ nonProjectPath(scoped) )
|
||||
}
|
||||
def resolvePath(base: File, components: Seq[String]): File =
|
||||
(base /: components)( (b,p) => new File(b,p) )
|
||||
|
||||
def pathComponent[T](axis: ScopeAxis[T], scoped: ScopedKey[_], label: String)(show: T => String): String =
|
||||
axis match
|
||||
{
|
||||
case Global => GlobalPath
|
||||
case This => error("Unresolved This reference for " + label + " in " + display(scoped))
|
||||
case Select(t) => show(t)
|
||||
}
|
||||
def nonProjectPath[T](scoped: ScopedKey[T]): Seq[String] =
|
||||
{
|
||||
val scope = scoped.scope
|
||||
pathComponent(scope.config, scoped, "config")(_.name) ::
|
||||
pathComponent(scope.task, scoped, "task")(_.label) ::
|
||||
pathComponent(scope.extra, scoped, "extra")(_ => error("Unimplemented")) ::
|
||||
Nil
|
||||
}
|
||||
def projectPath(structure: BuildStructure, scoped: ScopedKey[_]): (File, Seq[String]) =
|
||||
scoped.scope.project match
|
||||
{
|
||||
case Global => (structure.units(structure.root).base, GlobalPath :: Nil)
|
||||
case Select(ProjectRef(Some(uri), Some(id))) => (structure.units(uri).defined(id).base, Nil)
|
||||
case Select(pr) => error("Unresolved project reference (" + pr + ") in " + display(scoped))
|
||||
case This => error("Unresolved project reference (This) in " + display(scoped))
|
||||
}
|
||||
def processResult[T](result: Result[T], log: Logger): T =
|
||||
result match
|
||||
{
|
||||
case Value(v) => v
|
||||
case Inc(inc) =>
|
||||
log.error(Incomplete.show(inc, true))
|
||||
error("Task did not complete successfully")
|
||||
}
|
||||
}
|
||||
object Index
|
||||
{
|
||||
def taskToKeyMap(data: Settings[Scope]): Map[Task[_], ScopedKey[Task[_]]] =
|
||||
{
|
||||
// AttributeEntry + the checked type test 'value: Task[_]' ensures that the cast is correct.
|
||||
// (scalac couldn't determine that 'key' is of type AttributeKey[Task[_]] on its own and a type match didn't still required the cast)
|
||||
val pairs = for( scope <- data.scopes; AttributeEntry(key, value: Task[_]) <- data.data(scope).entries ) yield
|
||||
(value, ScopedKey(scope, key.asInstanceOf[AttributeKey[Task[_]]])) // unclear why this cast is needed even with a type test in the above filter
|
||||
pairs.toMap[Task[_], ScopedKey[Task[_]]]
|
||||
}
|
||||
def stringToKeyMap(settings: Settings[Scope]): Map[String, AttributeKey[_]] =
|
||||
{
|
||||
val multiMap = settings.data.values.flatMap(_.keys).toList.removeDuplicates.groupBy(_.label)
|
||||
val duplicates = multiMap collect { case (k, x1 :: x2 :: _) => println(k + ": " + x1 + ", " + x2); k }
|
||||
if(duplicates.isEmpty)
|
||||
multiMap.mapValues(_.head)
|
||||
else
|
||||
error(duplicates.mkString("AttributeKey ID collisions detected for '", "', '", "'"))
|
||||
}
|
||||
}
|
||||
object Load
|
||||
{
|
||||
import BuildPaths._
|
||||
|
||||
def defaultLoad(state: State, log: Logger): BuildStructure =
|
||||
{
|
||||
val stagingDirectory = defaultStaging // TODO: properly configurable
|
||||
val base = state.configuration.baseDirectory
|
||||
val loader = getClass.getClassLoader
|
||||
val provider = state.configuration.provider
|
||||
val classpath = provider.mainClasspath ++ provider.scalaProvider.jars
|
||||
val compilers = Compile.compilers(state.configuration, log)
|
||||
val evalPluginDef = EvaluateTask.evalPluginDef(state, log) _
|
||||
val config = new LoadBuildConfiguration(stagingDirectory, classpath, loader, compilers, evalPluginDef, defaultDelegates, EvaluateTask.injectSettings, log)
|
||||
apply(base, config)
|
||||
}
|
||||
def defaultDelegates: LoadedBuild => Scope => Seq[Scope] = (lb: LoadedBuild) => {
|
||||
def resolveRef(project: ProjectRef) = Scope.resolveRef(lb.root, getRootProject(lb.units), project)
|
||||
Scope.delegates(
|
||||
project => projectInherit(lb, resolveRef(project)),
|
||||
(project, config) => configInherit(lb, resolveRef(project), config),
|
||||
(project, task) => Nil,
|
||||
(project, extra) => Nil
|
||||
)
|
||||
}
|
||||
def configInherit(lb: LoadedBuild, ref: (URI, String), config: ConfigKey): Seq[ConfigKey] =
|
||||
getConfiguration(lb.units, ref._1, ref._2, config).extendsConfigs.map(c => ConfigKey(c.name))
|
||||
|
||||
def projectInherit(lb: LoadedBuild, ref: (URI, String)): Seq[ProjectRef] =
|
||||
getProject(lb.units, ref._1, ref._2).inherits
|
||||
|
||||
// build, load, and evaluate all units.
|
||||
// 1) Compile all plugin definitions
|
||||
// 2) Evaluate plugin definitions to obtain and compile plugins and get the resulting classpath for the build definition
|
||||
// 3) Instantiate Plugins on that classpath
|
||||
// 4) Compile all build definitions using plugin classpath
|
||||
// 5) Load build definitions.
|
||||
// 6) Load all configurations using build definitions and plugins (their classpaths and loaded instances).
|
||||
// 7) Combine settings from projects, plugins, and configurations
|
||||
// 8) Evaluate settings
|
||||
def apply(rootBase: File, config: LoadBuildConfiguration): BuildStructure =
|
||||
{
|
||||
val loaded = load(rootBase, config)
|
||||
val projects = loaded.units
|
||||
val settings = buildConfigurations(loaded, getRootProject(projects), config.injectSettings)
|
||||
val data = Project.make(settings)(config.delegates(loaded))
|
||||
val index = structureIndex(data)
|
||||
new BuildStructure(projects, loaded.root, settings, data, index)
|
||||
}
|
||||
|
||||
def structureIndex(settings: Settings[Scope]): StructureIndex =
|
||||
new StructureIndex(Index.stringToKeyMap(settings), Index.taskToKeyMap(settings))
|
||||
|
||||
// Reevaluates settings after modifying them. Does not recompile or reload any build components.
|
||||
def reapply(modifySettings: Endo[Seq[Setting[_]]], structure: BuildStructure, delegates: Scope => Seq[Scope]): BuildStructure =
|
||||
{
|
||||
val newSettings = modifySettings(structure.settings)
|
||||
val newData = Project.make(newSettings)(delegates)
|
||||
val newIndex = structureIndex(newData)
|
||||
new BuildStructure(units = structure.units, root = structure.root, settings = newSettings, data = newData, index = newIndex)
|
||||
}
|
||||
|
||||
def isProjectThis(s: Setting[_]) = s.key.scope.project == This
|
||||
def buildConfigurations(loaded: LoadedBuild, rootProject: URI => String, injectSettings: Seq[Setting[_]]): Seq[Setting[_]] =
|
||||
loaded.units.toSeq flatMap { case (uri, build) =>
|
||||
val eval = mkEval(build.unit.definitions, build.unit.plugins, Nil)
|
||||
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)
|
||||
|
||||
val ext = ProjectRef(uri, id)
|
||||
val thisScope = Scope(Select(ext), Global, Global, Global)
|
||||
|
||||
// map This to thisScope, Select(p) to mapRef(uri, rootProject, p)
|
||||
Project.transform(Scope.resolveScope(thisScope, uri, rootProject), settings)
|
||||
}
|
||||
pluginGlobal ++ projectSettings
|
||||
}
|
||||
def mkEval(defs: LoadedDefinitions, plugs: LoadedPlugins, options: Seq[String]): Eval =
|
||||
{
|
||||
val classpathString = Path.makeString(defs.target +: plugs.classpath)
|
||||
val optionsCp = "-cp" +: classpathString +: options // TODO: probably need to properly set up options with CompilerArguments
|
||||
new Eval(optionsCp, s => new ConsoleReporter(s), defs.loader)
|
||||
}
|
||||
def configurations(srcs: Seq[File], eval: Eval): Seq[Setting[_]] =
|
||||
if(srcs.isEmpty) Nil else EvaluateConfigurations(eval, srcs)
|
||||
|
||||
def load(file: File, config: LoadBuildConfiguration): LoadedBuild = load(file, uri => loadUnit(RetrieveUnit(config.stagingDirectory, uri), config) )
|
||||
def load(file: File, loader: URI => BuildUnit): LoadedBuild = loadURI(file.getAbsoluteFile.toURI, loader)
|
||||
def loadURI(uri: URI, loader: URI => BuildUnit): LoadedBuild =
|
||||
{
|
||||
val (referenced, map) = loadAll(uri :: Nil, Map.empty, loader, Map.empty)
|
||||
checkAll(referenced, map)
|
||||
new LoadedBuild(uri, map)
|
||||
}
|
||||
def loaded(unit: BuildUnit): (LoadedBuildUnit, List[ProjectRef]) =
|
||||
{
|
||||
val baseURI = unit.base.toURI.normalize
|
||||
def isRoot(p: Project) = p.base.toURI.normalize == baseURI
|
||||
val defined = projects(unit)
|
||||
val externals = referenced(defined).toList
|
||||
val rootProjects = defined.filter(isRoot).map(_.id)
|
||||
(new LoadedBuildUnit(unit, defined.map(d => (d.id, d)).toMap, rootProjects), externals)
|
||||
}
|
||||
|
||||
@tailrec def loadAll(bases: List[URI], references: Map[URI, List[ProjectRef]], externalLoader: URI => BuildUnit, builds: Map[URI, LoadedBuildUnit]): (Map[URI, List[ProjectRef]], Map[URI, LoadedBuildUnit]) =
|
||||
bases match
|
||||
{
|
||||
case b :: bs =>
|
||||
if(builds contains b)
|
||||
loadAll(bs, references, externalLoader, builds)
|
||||
else
|
||||
{
|
||||
val (loadedBuild, refs) = loaded(externalLoader(b))
|
||||
loadAll(refs.flatMap(_.uri) reverse_::: bs, references.updated(b, refs), externalLoader, builds.updated(b, loadedBuild))
|
||||
}
|
||||
case Nil => (references, builds)
|
||||
}
|
||||
def checkAll(referenced: Map[URI, List[ProjectRef]], builds: Map[URI, LoadedBuildUnit])
|
||||
{
|
||||
val rootProject = getRootProject(builds)
|
||||
for( (uri, refs) <- referenced; ref <- refs)
|
||||
{
|
||||
// mapRef guarantees each component is defined
|
||||
val ProjectRef(Some(refURI), Some(refID)) = Scope.mapRef(uri, rootProject, ref)
|
||||
val loadedUnit = builds(refURI)
|
||||
if(! (loadedUnit.defined contains refID) )
|
||||
error("No project '" + refID + "' in '" + refURI + "'")
|
||||
}
|
||||
}
|
||||
|
||||
def resolveBase(against: File): Project => Project =
|
||||
{
|
||||
val uri = against.getAbsoluteFile.toURI.normalize
|
||||
p => p.copy(base = new File(uri.resolve(p.base.toURI).normalize))
|
||||
}
|
||||
def projects(unit: BuildUnit): Seq[Project] = unit.definitions.builds.flatMap(_.projects map resolveBase(unit.base))
|
||||
def getRootProject(map: Map[URI, LoadedBuildUnit]): URI => String =
|
||||
uri => getBuild(map, uri).rootProjects.headOption getOrElse emptyBuild(uri)
|
||||
def getConfiguration(map: Map[URI, LoadedBuildUnit], uri: URI, id: String, conf: ConfigKey): Configuration =
|
||||
getProject(map, uri, id).configurations.find(_.name == conf.name) getOrElse noConfiguration(uri, id, conf.name)
|
||||
|
||||
def getProject(map: Map[URI, LoadedBuildUnit], uri: URI, id: String): Project =
|
||||
getBuild(map, uri).defined.getOrElse(id, noProject(uri, id))
|
||||
def getBuild(map: Map[URI, LoadedBuildUnit], uri: URI): LoadedBuildUnit =
|
||||
map.getOrElse(uri, noBuild(uri))
|
||||
|
||||
def emptyBuild(uri: URI) = error("No root project defined for build unit '" + uri + "'")
|
||||
def noBuild(uri: URI) = error("Build unit '" + uri + "' not defined.")
|
||||
def noProject(uri: URI, id: String) = error("No project '" + id + "' defined in '" + uri + "'.")
|
||||
def noConfiguration(uri: URI, id: String, conf: String) = error("No configuration '" + conf + "' defined in project '" + id + "' in '" + uri +"'")
|
||||
|
||||
def loadUnit(base: File, config: LoadBuildConfiguration): BuildUnit =
|
||||
{
|
||||
val defDir = selectProjectDir(base)
|
||||
val pluginDir = pluginDirectory(defDir)
|
||||
val plugs = plugins(pluginDir, config)
|
||||
|
||||
val defs = definitionSources(defDir)
|
||||
val loadedDefs =
|
||||
if(defs.isEmpty)
|
||||
new LoadedDefinitions(defDir, outputDirectory(defDir), plugs.loader, Build.default(base) :: Nil)
|
||||
else
|
||||
definitions(defDir, defs, plugs, config.compilers, config.log)
|
||||
|
||||
new BuildUnit(base, loadedDefs, plugs)
|
||||
}
|
||||
|
||||
def plugins(dir: File, config: LoadBuildConfiguration): LoadedPlugins = if(dir.exists) buildPlugins(dir, config) else noPlugins(config)
|
||||
def noPlugins(config: LoadBuildConfiguration): LoadedPlugins = new LoadedPlugins(config.classpath, config.loader, Nil)
|
||||
def buildPlugins(dir: File, config: LoadBuildConfiguration): LoadedPlugins =
|
||||
{
|
||||
val pluginDef = apply(dir, config)
|
||||
val (pluginClasspath, pluginAnalysis) = config.evalPluginDef(pluginDef)
|
||||
val pluginLoader = ClasspathUtilities.toLoader(pluginClasspath, config.loader)
|
||||
loadPlugins(pluginClasspath, pluginLoader, pluginAnalysis)
|
||||
}
|
||||
|
||||
def definitions(base: File, srcs: Seq[File], plugins: LoadedPlugins, compilers: Compilers, log: Logger): LoadedDefinitions =
|
||||
{
|
||||
val (inputs, defAnalysis) = build(plugins.classpath, srcs, outputDirectory(base), compilers, log)
|
||||
val target = inputs.config.classesDirectory
|
||||
val definitionLoader = ClasspathUtilities.toLoader(target :: Nil, plugins.loader)
|
||||
val defs = loadDefinitions(definitionLoader, findDefinitions(defAnalysis))
|
||||
new LoadedDefinitions(base, target, definitionLoader, defs)
|
||||
}
|
||||
|
||||
def loadDefinitions(loader: ClassLoader, defs: Seq[String]): Seq[Build] =
|
||||
defs map { definition => loadDefinition(loader, definition) }
|
||||
def loadDefinition(loader: ClassLoader, definition: String): Build =
|
||||
ModuleUtilities.getObject(definition, loader).asInstanceOf[Build]
|
||||
|
||||
def build(classpath: Seq[File], sources: Seq[File], buildDir: File, compilers: Compilers, log: Logger): (Inputs, Analysis) =
|
||||
{
|
||||
val target = crossPath(new File(buildDir, "target"), compilers.scalac.scalaInstance)
|
||||
val inputs = Compile.inputs(classpath, sources, target, Nil, Nil, Nil, Compile.DefaultMaxErrors)(compilers, log)
|
||||
val analysis = Compile(inputs, log)
|
||||
(inputs, analysis)
|
||||
}
|
||||
|
||||
def loadPlugins(classpath: Seq[File], loader: ClassLoader, analysis: Analysis): LoadedPlugins =
|
||||
new LoadedPlugins(classpath, loader, if(classpath.isEmpty) Nil else loadPlugins(loader, findPlugins(analysis) ) )
|
||||
|
||||
def loadPlugins(loader: ClassLoader, pluginNames: Seq[String]): Seq[Setting[_]] =
|
||||
pluginNames.flatMap(pluginName => loadPlugin(pluginName, loader))
|
||||
|
||||
def loadPlugin(pluginName: String, loader: ClassLoader): Seq[Setting[_]] =
|
||||
ModuleUtilities.getObject(pluginName, loader).asInstanceOf[Plugin].settings
|
||||
|
||||
def findPlugins(analysis: Analysis): Seq[String] = discover(analysis, "sbt.Plugin")
|
||||
def findDefinitions(analysis: Analysis): Seq[String] = discover(analysis, "sbt.Build")
|
||||
def discover(analysis: Analysis, subclasses: String*): Seq[String] =
|
||||
{
|
||||
val discovery = new Discovery(subclasses.toSet, Set.empty)
|
||||
discovery(Test.allDefs(analysis)).collect { case (definition, Discovered(_,_,_,true)) => definition.name }
|
||||
}
|
||||
|
||||
def initialSession(structure: BuildStructure): SessionSettings =
|
||||
new SessionSettings(structure.root, rootProjectMap(structure.units), structure.settings, Map.empty, Map.empty)
|
||||
|
||||
def rootProjectMap(units: Map[URI, LoadedBuildUnit]): Map[URI, String] =
|
||||
{
|
||||
val getRoot = getRootProject(units)
|
||||
units.keys.map(uri => (uri, getRoot(uri))).toMap
|
||||
}
|
||||
|
||||
final class EvaluatedConfigurations(val eval: Eval, val settings: Seq[Setting[_]])
|
||||
final class LoadedDefinitions(val base: File, val target: File, val loader: ClassLoader, val builds: Seq[Build])
|
||||
final class LoadedPlugins(val classpath: Seq[File], val loader: ClassLoader, val plugins: Seq[Setting[_]])
|
||||
object LoadedPlugins {
|
||||
def empty(loader: ClassLoader) = new LoadedPlugins(Nil, loader, Nil)
|
||||
}
|
||||
final class BuildUnit(val base: File, val definitions: LoadedDefinitions, val plugins: LoadedPlugins)
|
||||
|
||||
final class LoadedBuild(val root: URI, val units: Map[URI, LoadedBuildUnit])
|
||||
final class LoadedBuildUnit(val unit: BuildUnit, val defined: Map[String, Project], val rootProjects: Seq[String])
|
||||
{
|
||||
assert(!rootProjects.isEmpty, "No root projects defined for build unit '" + unit.base + "'")
|
||||
def base = unit.base
|
||||
def classpath = unit.definitions.target +: unit.plugins.classpath
|
||||
def loader = unit.definitions.loader
|
||||
}
|
||||
|
||||
// these are unresolved references
|
||||
def referenced(definitions: Seq[Project]): Seq[ProjectRef] = definitions flatMap referenced
|
||||
def referenced(definition: Project): Seq[ProjectRef] = definition.inherits ++ definition.aggregate ++ definition.dependencies.map(_.project)
|
||||
|
||||
|
||||
final class BuildStructure(val units: Map[URI, LoadedBuildUnit], val root: URI, val settings: Seq[Setting[_]], val data: Settings[Scope], val index: StructureIndex)
|
||||
final class LoadBuildConfiguration(val stagingDirectory: File, val classpath: Seq[File], val loader: ClassLoader, val compilers: Compilers, val evalPluginDef: BuildStructure => (Seq[File], Analysis), val delegates: LoadedBuild => Scope => Seq[Scope], val injectSettings: Seq[Setting[_]], val log: Logger)
|
||||
// information that is not original, but can be reconstructed from the rest of BuildStructure
|
||||
final class StructureIndex(val keyMap: Map[String, AttributeKey[_]], val taskToKey: Map[Task[_], ScopedKey[Task[_]]])
|
||||
}
|
||||
object BuildPaths
|
||||
{
|
||||
import Path._
|
||||
import GlobFilter._
|
||||
|
||||
def defaultStaging = Path.userHome / ".ivy2" / "staging"
|
||||
|
||||
def definitionSources(base: File): Seq[File] = (base * "*.scala").getFiles.toSeq
|
||||
def configurationSources(base: File): Seq[File] = (base * "*.sbt").getFiles.toSeq
|
||||
def pluginDirectory(definitionBase: Path) = definitionBase / "plugins"
|
||||
|
||||
def outputDirectory(base: Path) = base / "target"
|
||||
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: File, instance: ScalaInstance): File = base / ("scala_" + instance.version)
|
||||
}
|
||||
|
|
@ -6,7 +6,14 @@ package sbt
|
|||
import Execute.NodeView
|
||||
import java.io.File
|
||||
import Function.untupled
|
||||
import parse.Parser
|
||||
|
||||
trait NewCommand // to replace Command
|
||||
{
|
||||
type T
|
||||
def parser: State => Option[Parser[T]]
|
||||
def run: (T, State) => State
|
||||
}
|
||||
trait Command
|
||||
{
|
||||
def help: State => Seq[Help]
|
||||
|
|
@ -25,11 +32,9 @@ object Help
|
|||
object Command
|
||||
{
|
||||
val Logged = AttributeKey[Logger]("log")
|
||||
val HistoryPath = AttributeKey[Option[File]]("history")
|
||||
val HistoryPath = SettingKey[Option[File]]("history")
|
||||
val Analysis = AttributeKey[inc.Analysis]("analysis")
|
||||
val Watch = AttributeKey[Watched]("continuous-watch")
|
||||
val Navigate = AttributeKey[Navigation]("navigation")
|
||||
val TaskedKey = AttributeKey[Tasked]("tasked")
|
||||
val Watch = SettingKey[Watched]("continuous-watch")
|
||||
|
||||
def direct(h: Help*)(r: (Input, State) => Option[State]): Command =
|
||||
new Command { def help = _ => h; def run = r }
|
||||
|
|
@ -45,38 +50,6 @@ object Command
|
|||
def simple(name: String, help: Help*)(f: (Input, State) => State): Command =
|
||||
Command( help: _* ){ case (in, s) if name == in.name => f( in, s) }
|
||||
}
|
||||
/*
|
||||
final case class ProjectSpace(
|
||||
projects: Map[String, Project],
|
||||
/* sessionPrepend: Seq[Setting],
|
||||
sessionAppend: Seq[Setting],
|
||||
data: Settings,*/
|
||||
external: Map[String, Project]
|
||||
// eval: Option[Eval]
|
||||
) extends Identity*/
|
||||
|
||||
|
||||
trait Navigation
|
||||
{
|
||||
type Project <: AnyRef
|
||||
def self: Project
|
||||
def name: String
|
||||
def parent: Option[Navigation]
|
||||
def select(s: State): State
|
||||
def selected: Navigation
|
||||
def initial: Navigation
|
||||
def closure: Seq[Navigation]
|
||||
def root: Navigation
|
||||
}
|
||||
trait Tasked
|
||||
{
|
||||
type Task[T] <: AnyRef
|
||||
def act(in: Input, state: State): Option[(Task[State], NodeView[Task])]
|
||||
def help: Seq[Help]
|
||||
|
||||
def maxThreads = Runtime.getRuntime.availableProcessors
|
||||
def checkCycles = false
|
||||
}
|
||||
final case class Input(line: String, cursor: Option[Int])
|
||||
{
|
||||
lazy val (name, arguments) = line match { case Input.NameRegex(n, a) => (n, a); case _ => (line, "") }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2011 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import java.net.URI
|
||||
import Project.ScopedKey
|
||||
|
||||
object KeyIndex
|
||||
{
|
||||
def empty: ExtendableKeyIndex = new KeyIndex0(Map.empty)
|
||||
def apply(known: Seq[ScopedKey[_]]): ExtendableKeyIndex =
|
||||
(empty /: known) { _ add _ }
|
||||
def combine(indices: Seq[KeyIndex]): KeyIndex = new KeyIndex {
|
||||
def buildURIs = concat(_.buildURIs)
|
||||
def projects(uri: URI) = concat(_.projects(uri))
|
||||
def configs(proj: ProjectRef) = concat(_.configs(proj))
|
||||
def keys(proj: ProjectRef, conf: Option[String]) = concat(_.keys(proj, conf))
|
||||
def concat[T](f: KeyIndex => Set[T]): Set[T] =
|
||||
(Set.empty[T] /: indices)( (s,k) => s ++ f(k) )
|
||||
}
|
||||
}
|
||||
|
||||
trait KeyIndex
|
||||
{
|
||||
def buildURIs: Set[URI]
|
||||
def projects(uri: URI): Set[String]
|
||||
def configs(proj: ProjectRef): Set[String]
|
||||
def keys(proj: ProjectRef, conf: Option[String]): Set[String]
|
||||
}
|
||||
trait ExtendableKeyIndex extends KeyIndex
|
||||
{
|
||||
def add(scoped: ScopedKey[_]): ExtendableKeyIndex
|
||||
}
|
||||
private final class KeyIndex0(val data: Map[URI, Map[String, Map[ Option[String], Set[String]] ]]) extends ExtendableKeyIndex
|
||||
{
|
||||
def buildURIs: Set[URI] = data.keys.toSet
|
||||
def projects(uri: URI): Set[String] = get(data, uri).keys.toSet
|
||||
def configs(project: ProjectRef): Set[String] = confMap(project).keys.flatten.toSet
|
||||
def keys(project: ProjectRef, conf: Option[String]): Set[String] = get(confMap(project), conf)
|
||||
|
||||
def confMap(proj: ProjectRef): Map[Option[String], Set[String]] =
|
||||
proj match
|
||||
{
|
||||
case ProjectRef(Some(uri), Some(id)) => get( get(data, uri), id)
|
||||
case _ => Map.empty
|
||||
}
|
||||
|
||||
private[this] def get[A,B,C](m: Map[A,Map[B,C]], key: A): Map[B,C] = getOr(m, key, Map.empty)
|
||||
private[this] def get[A,B](m: Map[A,Set[B]], key: A): Set[B] = getOr(m, key, Set.empty)
|
||||
private[this] def getOr[A,B](m: Map[A,B], key: A, or: B): B = m.getOrElse(key, or)
|
||||
|
||||
def add(scoped: ScopedKey[_]): ExtendableKeyIndex =
|
||||
scoped.scope match
|
||||
{
|
||||
case Scope(Select(ProjectRef(Some(uri), Some(id))), config, _, _) => add(uri, id, config, scoped.key)
|
||||
case _ => this
|
||||
}
|
||||
def add(uri: URI, id: String, config: ScopeAxis[ConfigKey], key: AttributeKey[_]): ExtendableKeyIndex =
|
||||
add(uri, id, config match { case Select(c) => Some(c.name); case _ => None }, key)
|
||||
def add(uri: URI, id: String, config: Option[String], key: AttributeKey[_]): ExtendableKeyIndex =
|
||||
{
|
||||
val projectMap = get(data, uri)
|
||||
val configMap = get(projectMap, id)
|
||||
val newSet = get(configMap, config) + key.label
|
||||
val newProjectMap = projectMap.updated(id, configMap.updated(config, newSet))
|
||||
new KeyIndex0( data.updated(uri, newProjectMap) )
|
||||
}
|
||||
}
|
||||
|
|
@ -6,15 +6,14 @@ package sbt
|
|||
import java.io.PrintWriter
|
||||
import LogManager._
|
||||
import std.Transform
|
||||
import Project.ScopedKey
|
||||
|
||||
object LogManager
|
||||
{
|
||||
def construct(context: Transform.Context[Project]) = (task: Task[_], to: PrintWriter) =>
|
||||
def construct(data: Settings[Scope]) = (task: ScopedKey[Task[_]], to: PrintWriter) =>
|
||||
{
|
||||
val owner = context owner task
|
||||
val ownerName = owner flatMap ( context ownerName _ ) getOrElse ""
|
||||
val taskPath = (context staticName task).toList ::: ownerName :: Nil
|
||||
def level(key: AttributeKey[Level.Value], default: Level.Value): Level.Value = default//settings.get(key, taskPath) getOrElse default
|
||||
val scope = Scope.fillTaskAxis(task.scope, task.key)
|
||||
def level(key: AttributeKey[Level.Value], default: Level.Value): Level.Value = data.get(scope, key) getOrElse default
|
||||
val screenLevel = level(ScreenLogLevel, Level.Info)
|
||||
val backingLevel = level(PersistLogLevel, Level.Debug)
|
||||
|
||||
|
|
@ -30,6 +29,6 @@ object LogManager
|
|||
multi: Logger
|
||||
}
|
||||
|
||||
val ScreenLogLevel = AttributeKey[Level.Value]("screen log level")
|
||||
val PersistLogLevel = AttributeKey[Level.Value]("persist log level")
|
||||
val ScreenLogLevel = AttributeKey[Level.Value]("screen-log-level")
|
||||
val PersistLogLevel = AttributeKey[Level.Value]("persist-log-level")
|
||||
}
|
||||
133
main/Main.scala
133
main/Main.scala
|
|
@ -6,8 +6,9 @@ package sbt
|
|||
import Execute.NodeView
|
||||
import complete.HistoryCommands
|
||||
import HistoryCommands.{Start => HistoryPrefix}
|
||||
import sbt.build.{AggressiveCompile, Auto, Build, BuildException, LoadCommand, Parse, ParseException, ProjectLoad, SourceLoad}
|
||||
import Command.{Analysis,HistoryPath,Logged,Navigate,TaskedKey,Watch}
|
||||
import Project.{SessionKey, StructureKey}
|
||||
import sbt.build.{AggressiveCompile, Auto, BuildException, LoadCommand, Parse, ParseException, ProjectLoad, SourceLoad}
|
||||
import Command.{Analysis,HistoryPath,Logged,Watch}
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.JavaConversions._
|
||||
import Path._
|
||||
|
|
@ -64,7 +65,7 @@ import CommandSupport._
|
|||
object Commands
|
||||
{
|
||||
def DefaultCommands: Seq[Command] = Seq(ignore, help, reload, read, history, continuous, exit, loadCommands, loadProject, compile, discover,
|
||||
projects, project, setOnFailure, ifLast, multi, shell, alias, append, act)
|
||||
projects, project, setOnFailure, ifLast, multi, shell, alias, append)
|
||||
|
||||
def ignore = nothing(Set(FailureWall))
|
||||
|
||||
|
|
@ -98,7 +99,7 @@ object Commands
|
|||
}
|
||||
|
||||
def shell = Command.simple(Shell, ShellBrief, ShellDetailed) { (in, s) =>
|
||||
val historyPath = (s get HistoryPath) getOrElse Some((s.baseDir / ".history").asFile)
|
||||
val historyPath = (s get HistoryPath.key) getOrElse Some((s.baseDir / ".history").asFile)
|
||||
val reader = new LazyJLineReader(historyPath)
|
||||
val line = reader.readLine("> ")
|
||||
line match {
|
||||
|
|
@ -175,14 +176,14 @@ object Commands
|
|||
|
||||
def continuous =
|
||||
Command( Help(continuousBriefHelp) ) { case (in, s) if in.line startsWith ContinuousExecutePrefix =>
|
||||
withAttribute(s, Watch, "Continuous execution not configured.") { w =>
|
||||
withAttribute(s, Watch.key, "Continuous execution not configured.") { w =>
|
||||
Watched.executeContinuously(w, s, in)
|
||||
}
|
||||
}
|
||||
|
||||
def history = Command( historyHelp: _* ) { case (in, s) if in.line startsWith "!" =>
|
||||
val logError = (msg: String) => CommandSupport.logger(s).error(msg)
|
||||
HistoryCommands(in.line.substring(HistoryPrefix.length).trim, (s get HistoryPath) getOrElse None, 500/*JLine.MaxHistorySize*/, logError) match
|
||||
HistoryCommands(in.line.substring(HistoryPrefix.length).trim, (s get HistoryPath.key) getOrElse None, 500/*JLine.MaxHistorySize*/, logError) match
|
||||
{
|
||||
case Some(commands) =>
|
||||
commands.foreach(println) //printing is more appropriate than logging
|
||||
|
|
@ -191,68 +192,75 @@ object Commands
|
|||
}
|
||||
}
|
||||
|
||||
def indent(withStar: Boolean) = if(withStar) "\t*" else "\t"
|
||||
def indent(withStar: Boolean) = if(withStar) "\t*" else "\t "
|
||||
def listProject(name: String, current: Boolean, log: Logger) = log.info( indent(current) + name )
|
||||
|
||||
def act = error("TODO")
|
||||
def projects = Command.simple(ProjectsCommand, projectsBrief, projectsDetailed ) { (in,s) =>
|
||||
val log = logger(s)
|
||||
withNavigation(s) { nav =>
|
||||
nav.closure.foreach { p => listProject(p.name, nav.self eq p.self, log) }
|
||||
s
|
||||
val session = Project.session(s)
|
||||
val structure = Project.structure(s)
|
||||
val (curi, cid) = session.current
|
||||
for( (uri, build) <- structure.units)
|
||||
{
|
||||
log.info("In " + uri)
|
||||
for(id <- build.defined.keys) listProject(id, cid == id, log)
|
||||
}
|
||||
s
|
||||
}
|
||||
def withAttribute[T](s: State, key: AttributeKey[T], ifMissing: String)(f: T => State): State =
|
||||
(s get key) match {
|
||||
case None => logger(s).error(ifMissing); s.fail
|
||||
case Some(nav) => f(nav)
|
||||
}
|
||||
def withNavigation(s: State)(f: Navigation => State): State = withAttribute(s, Navigate, "No navigation configured.")(f)
|
||||
|
||||
def project = Command.simple(ProjectCommand, projectBrief, projectDetailed ) { (in,s) =>
|
||||
withNavigation(s) { nav =>
|
||||
val to = in.arguments
|
||||
if(to.isEmpty)
|
||||
val to = in.arguments
|
||||
val session = Project.session(s)
|
||||
val structure = Project.structure(s)
|
||||
val uri = session.currentBuild
|
||||
def setProject(id: String) = updateCurrent(s.put(SessionKey, session.setCurrent(uri, id)))
|
||||
if(to.isEmpty)
|
||||
{
|
||||
logger(s).info(session.currentProject(uri) + " (in build " + uri + ")")
|
||||
s
|
||||
}
|
||||
else if(to == "/")
|
||||
{
|
||||
val id = Load.getRootProject(structure.units)(uri)
|
||||
setProject(id)
|
||||
}
|
||||
else if(to.startsWith("^"))
|
||||
{
|
||||
val newBuild = (new java.net.URI(to substring 1)).normalize
|
||||
if(structure.units contains newBuild)
|
||||
updateCurrent(s.put(SessionKey, session.setCurrent(uri, session currentProject uri)))
|
||||
else
|
||||
{
|
||||
logger(s).info(nav.name)
|
||||
logger(s).error("Invalid build unit '" + newBuild + "' (type 'projects' to list available builds).")
|
||||
s
|
||||
}
|
||||
else if(to == "/")
|
||||
nav.root.select(s)
|
||||
else if(to.forall(_ == '.'))
|
||||
if(to.length > 1) gotoParent(to.length - 1, nav, s) else s
|
||||
else
|
||||
nav.closure.find { _.name == to } match
|
||||
{
|
||||
case Some(np) => np.select(s)
|
||||
case None => logger(s).error("Invalid project name '" + to + "' (type 'projects' to list available projects)."); s.fail
|
||||
}
|
||||
}
|
||||
/* else if(to.forall(_ == '.'))
|
||||
if(to.length > 1) gotoParent(to.length - 1, nav, s) else s */ // semantics currently undefined
|
||||
else if( structure.units(uri).defined.contains(to) )
|
||||
setProject(to)
|
||||
else
|
||||
{
|
||||
logger(s).error("Invalid project name '" + to + "' (type 'projects' to list available projects).")
|
||||
s.fail
|
||||
}
|
||||
}
|
||||
@tailrec def gotoParent(n: Int, nav: Navigation, s: State): State =
|
||||
nav.parent match
|
||||
{
|
||||
case Some(pp) => if(n <= 1) pp.select(s) else gotoParent(n-1, pp, s)
|
||||
case None => nav.select(s)
|
||||
}
|
||||
|
||||
def exit = Command( Help(exitBrief) ) {
|
||||
case (in, s) if TerminateActions contains in.line =>
|
||||
runExitHooks(s).exit(true)
|
||||
}
|
||||
|
||||
def act = new Command {
|
||||
def help = s => (s get TaskedKey).toSeq.flatMap { _.help }
|
||||
def run = (in, s) => (s get TaskedKey) flatMap { p =>
|
||||
import p.{checkCycles, maxThreads}
|
||||
for( (task, taskToNode) <- p.act(in, s)) yield
|
||||
processResult(runTask(task, checkCycles, maxThreads)(taskToNode), s, s.fail)
|
||||
}
|
||||
}
|
||||
|
||||
def discover = Command.simple(Discover, DiscoverBrief, DiscoverDetailed) { (in, s) =>
|
||||
withAttribute(s, Analysis, "No analysis to process.") { analysis =>
|
||||
val command = Parse.discover(in.arguments)
|
||||
val discovered = Build.discover(analysis, command)
|
||||
val discovered = build.Build.discover(analysis, command)
|
||||
println(discovered.mkString("\n"))
|
||||
s
|
||||
}
|
||||
|
|
@ -260,28 +268,43 @@ object Commands
|
|||
def compile = Command.simple(CompileName, CompileBrief, CompileDetailed ) { (in, s) =>
|
||||
val command = Parse.compile(in.arguments)(s.baseDir)
|
||||
try {
|
||||
val analysis = Build.compile(command, s.configuration)
|
||||
val analysis = build.Build.compile(command, s.configuration)
|
||||
s.put(Analysis, analysis)
|
||||
} catch { case e: xsbti.CompileFailed => s.fail /* already logged */ }
|
||||
}
|
||||
|
||||
def loadProject = Command.simple(LoadProject, LoadProjectBrief, LoadProjectDetailed) { (in, s) =>
|
||||
val base = s.configuration.baseDirectory
|
||||
lazy val p: Project = MultiProject.load(s.configuration, logger(s), ProjectInfo.externals(exts))(base)
|
||||
// lazy so that p can forward-reference it
|
||||
lazy val exts: Map[File, Project] = MultiProject.loadExternals(p :: Nil, p.info.construct).updated(base, p)
|
||||
exts// force
|
||||
setProject(p, p, runExitHooks(s))
|
||||
val structure = Load.defaultLoad(s, logger(s))
|
||||
val session = Load.initialSession(structure)
|
||||
val newAttrs = s.attributes.put(StructureKey, structure).put(SessionKey, session)
|
||||
val newState = s.copy(attributes = newAttrs)
|
||||
updateCurrent(runExitHooks(newState))
|
||||
}
|
||||
def setProject(p: Project, initial: Project, s: State): State =
|
||||
|
||||
def updateCurrent(s: State): State =
|
||||
{
|
||||
logger(s).info("Set current project to " + p.name)
|
||||
val nav = new MultiNavigation(p, setProject _, p, initial)
|
||||
val watched = new MultiWatched(p)
|
||||
// put(Logged, p.log)
|
||||
val newAttrs = s.attributes.put(Analysis, p.info.analysis).put(Navigate, nav).put(Watch, watched).put(HistoryPath, p.historyPath).put(TaskedKey, p)
|
||||
val structure = Project.structure(s)
|
||||
val (uri, id) = Project.current(s)
|
||||
val ref = ProjectRef(uri, id)
|
||||
val project = Load.getProject(structure.units, uri, id)
|
||||
logger(s).info("Set current project to " + id + " (in build " + uri +")")
|
||||
|
||||
val data = structure.data
|
||||
val historyPath = HistoryPath(ref).get(data).flatMap(identity)
|
||||
val newAttrs = s.attributes.put(Watch.key, makeWatched(data, ref, project)).put(HistoryPath.key, historyPath)
|
||||
s.copy(attributes = newAttrs)
|
||||
}
|
||||
def makeWatched(data: Settings[Scope], ref: ProjectRef, project: Project): Watched =
|
||||
{
|
||||
def getWatch(ref: ProjectRef) = Watch(ref).get(data)
|
||||
getWatch(ref) match
|
||||
{
|
||||
case Some(currentWatch) =>
|
||||
val subWatches = project.uses flatMap { p => getWatch(p) }
|
||||
Watched.multi(currentWatch, subWatches)
|
||||
case None => Watched.empty
|
||||
}
|
||||
}
|
||||
|
||||
def handleException(e: Throwable, s: State, trace: Boolean = true): State = {
|
||||
val log = logger(s)
|
||||
|
|
@ -317,7 +340,7 @@ object Commands
|
|||
try
|
||||
{
|
||||
val parsed = Parse(line)(configuration.baseDirectory)
|
||||
Right( Build( translateEmpty(parsed, defaultSuper), configuration, allowMultiple) )
|
||||
Right( build.Build( translateEmpty(parsed, defaultSuper), configuration, allowMultiple) )
|
||||
}
|
||||
catch { case e @ (_: ParseException | _: BuildException | _: xsbti.CompileFailed) => Left(e) }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2011 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import Project._
|
||||
|
||||
final case class Project(id: String, base: File, aggregate: Seq[ProjectRef] = Nil, dependencies: Seq[Project.ClasspathDependency] = Nil, inherits: Seq[ProjectRef] = Nil,
|
||||
settings: Seq[Project.Setting[_]] = Project.defaultSettings, configurations: Seq[Configuration] = Configurations.default)
|
||||
{
|
||||
def uses = aggregate ++ dependencies.map(_.project)
|
||||
}
|
||||
|
||||
object Project extends Init[Scope]
|
||||
{
|
||||
def defaultSettings: Seq[Setting[_]] = Nil
|
||||
|
||||
final case class ClasspathDependency(project: ProjectRef, configuration: Option[String])
|
||||
final class Constructor(p: ProjectRef) {
|
||||
def %(conf: String): ClasspathDependency = new ClasspathDependency(p, Some(conf))
|
||||
}
|
||||
|
||||
def getOrError[T](state: State, key: AttributeKey[T], msg: String): T = state get key getOrElse error(msg)
|
||||
def structure(state: State): Load.BuildStructure = getOrError(state, StructureKey, "No build loaded.")
|
||||
def session(state: State): SessionSettings = getOrError(state, SessionKey, "Session not initialized.")
|
||||
|
||||
def current(state: State): (URI, String) =
|
||||
{
|
||||
val s = session(state)
|
||||
val uri = s.currentBuild
|
||||
(uri, s.currentProject(uri))
|
||||
}
|
||||
def currentRef(state: State): ProjectRef =
|
||||
{
|
||||
val (unit, it) = current(state)
|
||||
ProjectRef(Some(unit), Some(it))
|
||||
}
|
||||
def display(scoped: ScopedKey[_]): String = Scope.display(scoped.scope, scoped.key.label)
|
||||
|
||||
def mapScope(f: Scope => Scope) = new (ScopedKey ~> ScopedKey) { def apply[T](key: ScopedKey[T]) =
|
||||
ScopedKey( f(key.scope), key.key)
|
||||
}
|
||||
def transform(g: Scope => Scope, ss: Seq[Setting[_]]): Seq[Setting[_]] = {
|
||||
val f = mapScope(g)
|
||||
ss.map(_ mapKey f)
|
||||
}
|
||||
|
||||
val SessionKey = AttributeKey[SessionSettings]("session-settings")
|
||||
val StructureKey = AttributeKey[Load.BuildStructure]("build-structure")
|
||||
}
|
||||
|
||||
import SessionSettings._
|
||||
|
||||
final class SessionSettings(val currentBuild: URI, val currentProject: Map[URI, String], val original: Seq[Setting[_]], val prepend: SessionMap, val append: SessionMap) {
|
||||
assert(currentProject contains currentBuild, "Current build (" + currentBuild + ") not associated with a current project.")
|
||||
def setCurrent(build: URI, project: String): SessionSettings = new SessionSettings(build, currentProject.updated(build, project), original, prepend, append)
|
||||
def current: (URI, String) = (currentBuild, currentProject(currentBuild))
|
||||
}
|
||||
object SessionSettings {
|
||||
type SessionSetting = (Setting[_], String)
|
||||
type SessionMap = Map[(URI, String), Seq[SessionSetting]]
|
||||
}
|
||||
|
||||
trait ProjectConstructors
|
||||
{
|
||||
implicit def configDependencyConstructor[T <% ProjectRef](p: T): Project.Constructor = new Project.Constructor(p)
|
||||
implicit def classpathDependency[T <% ProjectRef](p: T): Project.ClasspathDependency = new Project.ClasspathDependency(p, None)
|
||||
}
|
||||
|
||||
final case class ProjectRef(uri: Option[URI], id: Option[String])
|
||||
object ProjectRef
|
||||
{
|
||||
def apply(base: URI, id: String): ProjectRef = ProjectRef(Some(base), Some(id))
|
||||
/** Reference to the project with 'id' in the current build unit.*/
|
||||
def apply(id: String): ProjectRef = ProjectRef(None, Some(id))
|
||||
def apply(base: File, id: String): ProjectRef = ProjectRef(Some(base.toURI), Some(id))
|
||||
/** Reference to the root project at 'base'.*/
|
||||
def apply(base: URI): ProjectRef = ProjectRef(Some(base), None)
|
||||
/** Reference to the root project at 'base'.*/
|
||||
def apply(base: File): ProjectRef = ProjectRef(Some(base.toURI), None)
|
||||
/** Reference to the root project in the current build unit.*/
|
||||
def root = ProjectRef(None, None)
|
||||
|
||||
implicit def stringToRef(s: String): ProjectRef = ProjectRef(s)
|
||||
implicit def projectToRef(p: Project): ProjectRef = ProjectRef(p.id)
|
||||
implicit def uriToRef(u: URI): ProjectRef = ProjectRef(u)
|
||||
implicit def fileToRef(f: File): ProjectRef = ProjectRef(f)
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2010 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import java.io.File
|
||||
import xsbti.{AppConfiguration, AppProvider, ScalaProvider}
|
||||
import inc.Analysis
|
||||
|
||||
/** Represents the minimal information necessary to construct a Project.
|
||||
*
|
||||
* `projectDirectory` is the base directory for the project (not the root project directory)
|
||||
* `builderPath` is the base directory for the project (not the root project directory)
|
||||
* `dependencies` are the Projects that this Project depends on.
|
||||
* `parent` is the parent Project, or None if this is the root project.
|
||||
* `buildScalaVersion` contains the explicitly requested Scala version to use for building (as when using `+` or `++`) or None if the normal version should be used.
|
||||
*/
|
||||
final case class ProjectInfo(name: Option[String], projectDirectory: File, builderDir: File, dependencies: Seq[ProjectDependency], parent: Option[Project])(
|
||||
val configuration: AppConfiguration, val analysis: Analysis, val compileInputs: Compile.Inputs, val construct: File => Project, external: ExternalProjects)
|
||||
{
|
||||
def app = configuration.provider
|
||||
/** The version of Scala running sbt.*/
|
||||
def definitionScalaVersion = app.scalaProvider.version
|
||||
/** The launcher instance that booted sbt.*/
|
||||
def launcher = app.scalaProvider.launcher
|
||||
def globalLock = launcher.globalLock
|
||||
/* cannot be referenced in a Project's constructor */
|
||||
lazy val externals = external.value
|
||||
}
|
||||
final class ExternalProjects(externalMap: => Map[File, Project])
|
||||
{
|
||||
lazy val value = externalMap
|
||||
}
|
||||
|
||||
object ProjectInfo
|
||||
{
|
||||
def externals(externalMap: => Map[File, Project]) = new ExternalProjects(externalMap)
|
||||
val MetadataDirectoryName = "project"
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2011 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
|
||||
final case class Scope(project: ScopeAxis[ProjectRef], config: ScopeAxis[ConfigKey], task: ScopeAxis[AttributeKey[_]], extra: ScopeAxis[AttributeMap])
|
||||
object Scope
|
||||
{
|
||||
val ThisScope = Scope(This, This, This, This)
|
||||
val GlobalScope = Scope(Global, Global, Global, Global)
|
||||
|
||||
def resolveScope(thisScope: Scope, current: URI, rootProject: URI => String): Scope => Scope =
|
||||
replaceThis(thisScope) compose resolveProject(current, rootProject)
|
||||
|
||||
def replaceThis(thisScope: Scope): Scope => Scope = (scope: Scope) =>
|
||||
Scope(subThis(thisScope.project, scope.project), subThis(thisScope.config, scope.config), subThis(thisScope.task, scope.task), subThis(thisScope.extra, scope.extra))
|
||||
|
||||
def subThis[T](sub: ScopeAxis[T], into: ScopeAxis[T]): ScopeAxis[T] =
|
||||
if(into == This) sub else into
|
||||
|
||||
def fillTaskAxis(scope: Scope, key: AttributeKey[_]): Scope =
|
||||
scope.task match
|
||||
{
|
||||
case _: Select[_] => scope
|
||||
case _ => scope.copy(task = Select(key))
|
||||
}
|
||||
|
||||
def resolveProject(uri: URI, rootProject: URI => String): Scope => Scope =
|
||||
{
|
||||
case Scope(Select(ref), a,b,c) =>
|
||||
Scope(Select(mapRef(uri, rootProject, ref)), a,b,c)
|
||||
case x => x
|
||||
}
|
||||
|
||||
def mapRef(current: URI, rootProject: URI => String, ref: ProjectRef): ProjectRef =
|
||||
{
|
||||
val (uri, id) = resolveRef(current, rootProject, ref)
|
||||
ProjectRef(Some(uri), Some(id))
|
||||
}
|
||||
def resolveRef(current: URI, rootProject: URI => String, ref: ProjectRef): (URI, String) =
|
||||
{
|
||||
val uri = ref.uri getOrElse current
|
||||
(uri, ref.id getOrElse rootProject(uri))
|
||||
}
|
||||
|
||||
def display(config: ConfigKey): String = if(config.name == "compile") "" else config.name + "-"
|
||||
def display(scope: Scope, sep: String): String =
|
||||
{
|
||||
import scope.{project, config, task, extra}
|
||||
val projectPrefix = project match { case Select(p) => "(" + p.uri + ")" + p.id; case Global => "*"; case This => "." }
|
||||
val configPrefix = config match { case Select(c) => display(c); case _ => "" }
|
||||
val taskPostfix = task match { case Select(t) => " for " + t.label; case _ => "" }
|
||||
val extraPostfix = extra match { case Select(es) => es.entries.map( _.toString ).toList; case _ => Nil }
|
||||
val extras = taskPostfix :: extraPostfix
|
||||
val postfix = if(extras.isEmpty) "" else extras.mkString("(", ", ", ")")
|
||||
projectPrefix + "/" + configPrefix + sep + postfix
|
||||
}
|
||||
|
||||
def parseScopedKey(command: String): (Scope, String) =
|
||||
{
|
||||
val ScopedKeyRegex(_, projectID, _, config, key) = command
|
||||
val pref = if(projectID eq null) This else Select(ProjectRef(None, Some(projectID)))
|
||||
val conf = if(config eq null) This else Select(ConfigKey(config))
|
||||
(Scope(pref, conf, This, This), transformTaskName(key))
|
||||
}
|
||||
val ScopedKeyRegex = """((\w+)\/)?((\w+)\:)?([\w\-]+)""".r
|
||||
|
||||
def transformTaskName(s: String) =
|
||||
{
|
||||
val parts = s.split("-+")
|
||||
(parts.take(1) ++ parts.drop(1).map(_.capitalize)).mkString
|
||||
}
|
||||
|
||||
// *Inherit functions should be immediate delegates and not include argument itself. Transitivity will be provided by this method
|
||||
def delegates(projectInherit: ProjectRef => Seq[ProjectRef],
|
||||
configInherit: (ProjectRef, ConfigKey) => Seq[ConfigKey],
|
||||
taskInherit: (ProjectRef, AttributeKey[_]) => Seq[AttributeKey[_]],
|
||||
extraInherit: (ProjectRef, AttributeMap) => Seq[AttributeMap])(scope: Scope): Seq[Scope] =
|
||||
scope.project match
|
||||
{
|
||||
case Global | This => scope :: Nil
|
||||
case Select(proj) =>
|
||||
val prod =
|
||||
for {
|
||||
c <- linearize(scope.config)(configInherit(proj, _))
|
||||
t <- linearize(scope.task)(taskInherit(proj,_))
|
||||
e <- linearize(scope.extra)(extraInherit(proj,_))
|
||||
} yield
|
||||
Scope(Select(proj),c,t,e)
|
||||
val projI =
|
||||
linearize(scope.project)(projectInherit) map { p => scope.copy(project = p) }
|
||||
|
||||
prod ++ projI
|
||||
}
|
||||
def linearize[T](axis: ScopeAxis[T])(inherit: T => Seq[T]): Seq[ScopeAxis[T]] =
|
||||
axis match
|
||||
{
|
||||
case Select(x) => Dag.topologicalSort(x)(inherit).map(Select.apply) :+ Global
|
||||
case Global | This => Global :: Nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sealed trait ScopeAxis[+S]
|
||||
object This extends ScopeAxis[Nothing]
|
||||
object Global extends ScopeAxis[Nothing]
|
||||
final case class Select[S](s: S) extends ScopeAxis[S]
|
||||
|
||||
final case class ConfigKey(name: String)
|
||||
object ConfigKey
|
||||
{
|
||||
implicit def configurationToKey(c: Configuration): ConfigKey = ConfigKey(c.name)
|
||||
}
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2011 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
/** An abstraction on top of Settings for build configuration and task definition. */
|
||||
|
||||
import Types._
|
||||
import std.TaskExtra._
|
||||
import Task._
|
||||
import Project.{ScopedKey, Setting}
|
||||
import parse.Parser
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
|
||||
sealed trait InputTask[T] {
|
||||
type S
|
||||
def parser: Parser[S]
|
||||
def apply(s: S): Task[T]
|
||||
}
|
||||
object InputTask {
|
||||
def apply[I,T](p: Parser[I], c: I => Task[T]): InputTask[T] { type S = I } =
|
||||
new InputTask[T] { type S = I; def parser = p; def apply(s: I) = c(s) }
|
||||
}
|
||||
|
||||
sealed trait Scoped { def scope: Scope }
|
||||
sealed trait ScopedSetting[T] extends Scoped { def key: AttributeKey[T] }
|
||||
sealed trait ScopedTask[T] extends Scoped { def key: AttributeKey[Task[T]] }
|
||||
sealed trait ScopedInput[T] extends Scoped { def key: AttributeKey[InputTask[T]] }
|
||||
|
||||
sealed trait Key[T] extends Scoped { final def scope: Scope = Scope(This,This,This,This) }
|
||||
final class SettingKey[T] private(val key: AttributeKey[T]) extends Key[T] with ScopedSetting[T]
|
||||
final class TaskKey[T] private(val key: AttributeKey[Task[T]]) extends Key[T] with ScopedTask[T]
|
||||
final class InputKey[T] private(val key: AttributeKey[InputTask[T]]) extends Key[InputTask[T]] with ScopedInput[T]
|
||||
|
||||
object Scoped
|
||||
{
|
||||
implicit def richSettingScoped[T](s: ScopedSetting[T]): RichSettingScoped[T] = new RichSettingScoped[T](s.scope, s.key)
|
||||
implicit def richTaskScoped[T](s: ScopedTask[T]): RichTaskScoped[T] = new RichTaskScoped[T](s.scope, s.key)
|
||||
implicit def richInputScoped[T](s: ScopedInput[T]): RichInputScoped[T] = new RichInputScoped[T](s.scope, s.key)
|
||||
|
||||
implicit def settingScoping[T](s: SettingKey[T]): ScopingSetting[T] = new ScopingSetting[T](s.key)
|
||||
implicit def taskScoping[T](s: TaskKey[T]): ScopingTask[T] = new ScopingTask[T](s.key)
|
||||
implicit def inputScoping[T](s: InputKey[T]): ScopingInput[T] = new ScopingInput[T](s.key)
|
||||
|
||||
sealed abstract class AbstractScoping[T]
|
||||
{
|
||||
type Result
|
||||
def key: AttributeKey[T]
|
||||
def apply(s: Scope): Result
|
||||
|
||||
final def apply(p: ProjectRef): Result = apply(Select(p), This, This)
|
||||
final def apply(t: TaskKey[_]): Result = apply(This, This, Select(t))
|
||||
final def apply(c: ConfigKey): Result = apply(This, Select(c), This)
|
||||
final def apply(c: ConfigKey, t: TaskKey[_]): Result = apply(This, Select(c), Select(t))
|
||||
final def apply(p: ProjectRef, c: ConfigKey): Result = apply(Select(p), Select(c), This)
|
||||
final def apply(p: ProjectRef, t: TaskKey[_]): Result = apply(Select(p), This, Select(t))
|
||||
final def apply(p: ProjectRef, c: ConfigKey, t: TaskKey[_]): Result = apply(Select(p), Select(c), Select(t))
|
||||
final def apply(p: ScopeAxis[ProjectRef], c: ScopeAxis[ConfigKey], t: ScopeAxis[TaskKey[_]]): Result = apply( Scope(p, c, convert(t), This) )
|
||||
private def convert(tk: ScopeAxis[TaskKey[_]]): ScopeAxis[AttributeKey[_]] =
|
||||
tk match {
|
||||
case Select(t) => Select(t.key)
|
||||
case This => This
|
||||
case Global => Global
|
||||
}
|
||||
}
|
||||
final class ScopingSetting[T](val key: AttributeKey[T]) extends AbstractScoping[T]
|
||||
{
|
||||
type Result = ScopedSetting[T]
|
||||
def apply(s: Scope): Result = new ScopedSetting[T] { val scope = s; val key = ScopingSetting.this.key }
|
||||
}
|
||||
final class ScopingInput[T](val key: AttributeKey[InputTask[T]]) extends AbstractScoping[InputTask[T]]
|
||||
{
|
||||
type Result = ScopedInput[T]
|
||||
def apply(s: Scope): Result = new ScopedInput[T] { val scope = s; val key = ScopingInput.this.key }
|
||||
}
|
||||
final class ScopingTask[T](taskKey: AttributeKey[Task[T]])
|
||||
{
|
||||
def apply(p: ProjectRef): RichTaskScoped[T] = apply(Select(p), This)
|
||||
def apply(c: ConfigKey): RichTaskScoped[T] = apply(This, Select(c))
|
||||
def apply(p: ProjectRef, c: ConfigKey): RichTaskScoped[T] = apply(Select(p), Select(c))
|
||||
def apply(p: ScopeAxis[ProjectRef], c: ScopeAxis[ConfigKey]): ScopedTask[T] = apply(Scope(p, c, This, This))
|
||||
def apply(s: Scope): ScopedTask[T] = scopedTask(s, taskKey)
|
||||
}
|
||||
private[this] def scopedTask[T](s: Scope, k: AttributeKey[Task[T]]): ScopedTask[T] = new ScopedTask[T] { val scope = s; val key = k }
|
||||
|
||||
sealed abstract class RichBaseScoped[S]
|
||||
{
|
||||
def scope: Scope
|
||||
def key: AttributeKey[S]
|
||||
final val scoped = ScopedKey(scope, key)
|
||||
final val scopedList = scoped :^: KNil
|
||||
|
||||
final def :==(value: S): Setting[S] = :=(value)
|
||||
final def := (value: => S): Setting[S] = Project.value(scoped)(value)
|
||||
final def :~ (f: S => S): Setting[S] = Project.update(scoped)(f)
|
||||
final def :- [HL <: HList](app: Apply[S]): Setting[S] = app toSetting scoped
|
||||
|
||||
def apply[T](f: S => T): Apply[T] = Apply.mk(scopedList)(hl => f(hl.head))
|
||||
|
||||
def get(settings: Settings[Scope]): Option[S] = settings.get(scope, key)
|
||||
}
|
||||
final class RichInputScoped[T](val scope: Scope, val key: AttributeKey[InputTask[T]]) extends RichBaseScoped[InputTask[T]]
|
||||
final class RichSettingScoped[S](val scope: Scope, val key: AttributeKey[S]) extends RichBaseScoped[S]
|
||||
{
|
||||
def map[T](f: S => T): Apply[Task[T]] = flatMap(s => task(f(s)) )
|
||||
def flatMap[T](f: S => Task[T]): Apply[Task[T]] = Apply.mk(scopedList)(hl => f(hl.head))
|
||||
}
|
||||
final class RichTaskScoped[S](scope: Scope, key: AttributeKey[Task[S]])
|
||||
{
|
||||
type ScS = Setting[Task[S]]
|
||||
def :==(value: S): ScS = :=(value)
|
||||
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 :~ (f: S => S): ScS = Project.update(scoped)( _ map f )
|
||||
|
||||
def :- [HL <: HList](app: App[S]): ScS = app toSetting scoped
|
||||
|
||||
def get(settings: Settings[Scope]): Option[Task[S]] = settings.get(scope, key)
|
||||
|
||||
type App[T] = Apply[Task[T]]
|
||||
private[this] def scoped = ScopedKey(scope, key)
|
||||
private[this] def mk[T](onTask: Task[S] => Task[T]): App[T] = Apply.mk(scoped :^: KNil)(hl => onTask(hl.head))
|
||||
|
||||
def flatMapR[T](f: Result[S] => Task[T]): App[T] = mk(_ flatMapR f)
|
||||
def flatMap[T](f: S => Task[T]): App[T] = mk(_ flatMap f)
|
||||
def map[T](f: S => T): App[T] = mk(_ map f)
|
||||
def mapR[T](f: Result[S] => T): App[T] = mk(_ mapR f)
|
||||
def flatFailure[T](f: Incomplete => Task[T]): App[T] = mk(_ flatFailure f)
|
||||
def mapFailure[T](f: Incomplete => T): App[T] = mk(_ mapFailure f)
|
||||
def andFinally(fin: => Unit): App[S] = mk(_ andFinally fin)
|
||||
def doFinally(t: Task[Unit]): App[S] = mk(_ doFinally t)
|
||||
|
||||
def || [T >: S](alt: Task[T]): App[T] = mk(_ || alt)
|
||||
def && [T](alt: Task[T]): App[T] = mk(_ && alt)
|
||||
|
||||
def dependsOn(tasks: ScopedTask[_]*): App[S] =
|
||||
{
|
||||
val in = KCons(scopedTask(scope, key), KList.fromList(tasks))
|
||||
Apply.tasks(in) { case KCons(h,t) => h dependsOn(t.toList :_*) }
|
||||
}
|
||||
}
|
||||
|
||||
implicit def richSettingKeys[HL <: HList](in: KList[ScopedSetting, HL]): RichSettingKeys[HL] = new RichSettingKeys(in)
|
||||
final class RichSettingKeys[HL <: HList](keys: KList[ScopedSetting, HL])
|
||||
{
|
||||
type App[T] = Apply[Task[T]]
|
||||
def map[T](f: HL => T): App[T] = Apply(keys)(settings => task(f(settings)))
|
||||
def flatMap[T](f: HL => Task[T]): App[T] = Apply(keys)(f)
|
||||
}
|
||||
final class RichTaskKeys[In <: HList](keys: KList[ScopedTask, In])
|
||||
{
|
||||
type App[T] = Apply[Task[T]]
|
||||
def flatMap[T](f: In => Task[T]): App[T] = mk(_ flatMap f)
|
||||
def flatMapR[T](f: Results[In] => Task[T]): App[T] = mk(_ flatMapR f)
|
||||
def map[T](f: In => T): App[T] = mk(_ map f)
|
||||
def mapR[T](f: Results[In] => T): App[T] = mk(_ mapR f)
|
||||
def flatFailure[T](f: Seq[Incomplete] => Task[T]): App[T] = mk(_ flatFailure f)
|
||||
def mapFailure[T](f: Seq[Incomplete] => T): App[T] = mk(_ mapFailure f)
|
||||
private[this] def mk[T](onTasks: KList[Task, In] => Task[T]): App[T] = Apply.tasks(keys)(onTasks)
|
||||
}
|
||||
|
||||
implicit def t2ToApp2[A,B](t2: (ScopedSetting[A], ScopedSetting[B]) ): Apply2[A,B] = new Apply2(t2)
|
||||
implicit def t3ToApp3[A,B,C](t3: (ScopedSetting[A], ScopedSetting[B], ScopedSetting[C]) ): Apply3[A,B,C] = new Apply3(t3)
|
||||
|
||||
final class Apply[T] private(val toSetting: ScopedKey[T] => Setting[T])
|
||||
|
||||
object Apply
|
||||
{
|
||||
def mk[H <: HList, T](in: KList[ScopedKey, H])(f: H => T): Apply[T] =
|
||||
new Apply[T](scoped => Project.app(scoped, in)(f) )
|
||||
|
||||
def apply[HL <: HList, T](in: KList[ScopedSetting, HL])(f: HL => T): Apply[T] = mk(in transform ssToSK)(f)
|
||||
def tasks[HL <: HList, T](in: KList[ScopedTask, HL])(f: KList[Task, HL] => T): Apply[T] =
|
||||
{
|
||||
val kapp = new Project.KApp[HL, Task, T]
|
||||
new Apply[T](scoped => kapp(scoped, in transform stToSK)(f) )
|
||||
}
|
||||
|
||||
private val ssToSK = new (ScopedSetting ~> ScopedKey) { def apply[T](sk: ScopedSetting[T]) = new ScopedKey(sk.scope, sk.key) }
|
||||
private val stToSK = new (ScopedTask ~> ScopedTaskKey) { def apply[T](st: ScopedTask[T]) = new ScopedKey(st.scope, st.key) }
|
||||
private type ScopedTaskKey[T] = ScopedKey[Task[T]]
|
||||
}
|
||||
|
||||
final class Apply2[A,B](t2: (ScopedSetting[A], ScopedSetting[B])) {
|
||||
def apply[T](f: (A,B) => T) =
|
||||
Apply(t2._1 :^: t2._2 :^: KNil){ case a :+: b :+: HNil => f(a,b) }
|
||||
}
|
||||
final class Apply3[A,B,C](t3: (ScopedSetting[A], ScopedSetting[B], ScopedSetting[C])) {
|
||||
def apply[T](f: (A,B,C) => T) =
|
||||
Apply(t3._1 :^: t3._2 :^: t3._3 :^: KNil){ case a :+: b :+: c :+: HNil => f(a,b,c) }
|
||||
}
|
||||
|
||||
/*def unresolved(scope: Scope): Seq[String] = unresolvedProject(scope.project) ++ unresolvedThis(scope)
|
||||
def unresolvedProject(ps: ScopeAxis[ProjectRef]): Seq[String] = ps match {
|
||||
case Select(p) => ifEmpty(p.unit, "Unspecified build unit") ++ ifEmpty(p.id, "Unspecified project ID")
|
||||
case _ => Nil
|
||||
}
|
||||
def ifEmpty(p: Option[URI], msg: String): Seq[String] = if(p.isEmpty) msg :: Nil else Nil
|
||||
def unresolvedThis(scope: Scope): Seq[String] =
|
||||
unresolvedThis(scope.project, "project") ++
|
||||
unresolvedThis(scope.config, "configuration") ++
|
||||
unresolvedThis(scope.task, "task") ++
|
||||
unresolvedThis(scope.extra, "extra")
|
||||
|
||||
def unresolvedThis(axis: ScopeAxis[_], label: String): Seq[String] =
|
||||
if(axis == This) ("Unresolved This for " + label + " axis.") :: Nil else Nil*/
|
||||
|
||||
// private[this] val hcHead = new ( ({type l[t] = t :+: _})#l ~> Id ) { def apply[T](hc: t :+: _): t = hc.head }
|
||||
}
|
||||
object TaskKey
|
||||
{
|
||||
def apply[T](label: String): TaskKey[T] =
|
||||
apply( AttributeKey[Task[T]](label) )
|
||||
|
||||
def apply[T](akey: AttributeKey[Task[T]]): TaskKey[T] =
|
||||
new TaskKey[T](akey)
|
||||
}
|
||||
object SettingKey
|
||||
{
|
||||
def apply[T](label: String): SettingKey[T] =
|
||||
apply( AttributeKey[T](label) )
|
||||
|
||||
def apply[T](akey: AttributeKey[T]): SettingKey[T] =
|
||||
new SettingKey[T](akey)
|
||||
}
|
||||
|
|
@ -16,11 +16,19 @@ trait Watched
|
|||
|
||||
object Watched
|
||||
{
|
||||
private[this] class AWatched extends Watched
|
||||
|
||||
def multi(base: Watched, paths: Seq[Watched]): Watched =
|
||||
new AWatched
|
||||
{
|
||||
override val watchPaths = (base.watchPaths /: paths)(_ +++ _.watchPaths)
|
||||
override def terminateWatch(key: Int): Boolean = base.terminateWatch(key)
|
||||
}
|
||||
def empty: Watched = new AWatched
|
||||
|
||||
val PollDelaySeconds = 1
|
||||
def isEnter(key: Int): Boolean = key == 10 || key == 13
|
||||
/*
|
||||
def watched(p: Project): Seq[Watched] = MultiProject.topologicalSort(p).collect { case w: Watched => w }
|
||||
def sourcePaths(p: Project): PathFinder = (Path.emptyPathFinder /: watched(p))(_ +++ _.watchPaths)*/
|
||||
|
||||
def executeContinuously(watched: Watched, s: State, in: Input): State =
|
||||
{
|
||||
@tailrec def shouldTerminate: Boolean = (System.in.available > 0) && (watched.terminateWatch(System.in.read()) || shouldTerminate)
|
||||
|
|
|
|||
|
|
@ -151,12 +151,6 @@ abstract class BasicProject extends TestProject with MultiClasspathProject with
|
|||
|
||||
// lazy val test-only, test-quick, test-failed, package-src, package-doc, jetty-{run,stop,restart}, prepare-webapp
|
||||
|
||||
lazy val set = input map { in =>
|
||||
val Seq(name, value) = in.splitArgs.take(2)
|
||||
println(name + "=" + value)
|
||||
java.lang.System.setProperty(name, value)
|
||||
}
|
||||
|
||||
def sourceFilter: FileFilter = "*.java" | "*.scala"
|
||||
|
||||
def compileTask(inputs: Task[Compile.Inputs]): Task[Analysis] =
|
||||
|
|
@ -279,10 +273,4 @@ class TestTasks(val prefix: Option[String], val project: ClasspathProject with P
|
|||
Test(frameworkMap, loader, discovered, options, s.log)
|
||||
}
|
||||
lazy val test = (project.streams, executeTests) map { case s :+: results :+: HNil => Test.showResults(s.log, results) }
|
||||
}
|
||||
/*class PackageTasks
|
||||
/* def zipTask(sources: PathFinder, outputDirectory: Path, zipName: => String): Task =
|
||||
zipTask(sources, outputDirectory / zipName)
|
||||
def zipTask(sources: PathFinder, zipPath: => Path): Task =
|
||||
fileTask("zip", zipPath from sources) { FileUtilities.zip(sources.get, zipPath, false, log) }*/
|
||||
}*/
|
||||
}
|
||||
|
|
@ -15,12 +15,9 @@ final case class Mapped[T, In <: HList](in: Tasks[In], f: Results[In] => T) exte
|
|||
final case class FlatMapped[T, In <: HList](in: Tasks[In], f: Results[In] => Task[T]) extends Action[T]
|
||||
final case class DependsOn[T](in: Task[T], deps: Seq[Task[_]]) extends Action[T]
|
||||
final case class Join[T, U](in: Seq[Task[U]], f: Seq[Result[U]] => Either[Task[T], T]) extends Action[T]
|
||||
final case class CrossAction[T](subs: Cross[Task[T]] ) extends Action[T]
|
||||
|
||||
|
||||
object Task
|
||||
{
|
||||
type Cross[T] = Seq[(AttributeMap, T)]
|
||||
type Tasks[HL <: HList] = KList[Task, HL]
|
||||
type Results[HL <: HList] = KList[Result, HL]
|
||||
}
|
||||
|
|
@ -36,10 +33,8 @@ final case class Info[T](attributes: AttributeMap = AttributeMap.empty, original
|
|||
import Info._
|
||||
def name = attributes.get(Name)
|
||||
def description = attributes.get(Description)
|
||||
def implied = attributes.get(Implied).getOrElse(false)
|
||||
def setName(n: String) = set(Name, n)
|
||||
def setDescription(d: String) = set(Description, d)
|
||||
def setImplied(i: Boolean) = set(Implied, i)
|
||||
def set[T](key: AttributeKey[T], value: T) = copy(attributes = this.attributes.put(key, value))
|
||||
|
||||
override def toString =
|
||||
|
|
@ -52,6 +47,4 @@ object Info
|
|||
{
|
||||
val Name = AttributeKey[String]("name")
|
||||
val Description = AttributeKey[String]("description")
|
||||
val Implied = AttributeKey[Boolean]("implied")
|
||||
val Cross = AttributeKey[AttributeMap]("cross-configuration")
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
// TODO: join, reduce
|
||||
package sbt
|
||||
package std
|
||||
|
||||
import Types._
|
||||
import Task._
|
||||
|
||||
object Cross
|
||||
{
|
||||
type AttributeSet = Set[AttributeKey[_]]
|
||||
|
||||
def hasCross(s: Seq[Task[_]]): Boolean = s.exists { _.work.isInstanceOf[CrossAction[_]] }
|
||||
|
||||
def extract[T](in: AttributeMap = AttributeMap.empty): Task[T] => Task[T] = (result: Task[T]) =>
|
||||
result.work match
|
||||
{
|
||||
case CrossAction(subs) =>
|
||||
subs.filter { x => compatible(in)(x._1) } match {
|
||||
case Seq( (_,matchingTask) ) => matchingTask
|
||||
case Seq() => if(subs.isEmpty) error("Cannot introduce cross configurations with flatMap") else error("No compatible cross configurations returned by " + result)
|
||||
case _ => error("Multiple compatible cross configurations returned by " + result)
|
||||
}
|
||||
case _ => result
|
||||
}
|
||||
|
||||
def compatible(base: AttributeMap)(nested: AttributeMap): Boolean =
|
||||
nested.entries forall { case AttributeEntry(key, v) => base get(key) filter (_ == v) isDefined }
|
||||
|
||||
def expandUniform[S](uniform: Seq[Task[S]]): Cross[ Seq[Task[S]] ] =
|
||||
// the cast is necessary to avoid duplicating the contents of expandExist
|
||||
// and expandCrossed (expandExist cannot be defined in terms of expandUniform
|
||||
// because there is no valid instatiation of S)
|
||||
expandExist(uniform).asInstanceOf[ Cross[ Seq[Task[S]] ] ]
|
||||
|
||||
def expandExist(dependsOn: Seq[Task[_]]): Cross[ Seq[Task[_]] ] =
|
||||
{
|
||||
val klist = KList.fromList(dependsOn)
|
||||
map( expandCrossed( klist ) ) { _.toList }
|
||||
}
|
||||
|
||||
def keys(c: Cross[_]): AttributeSet =
|
||||
(Set.empty[AttributeKey[_]] /: c ){ _ ++ _._1.keys }
|
||||
|
||||
def uniform[S,T](in: Seq[Task[S]])(f: (AttributeMap, Seq[Task[S]]) => Task[T]): Task[T] =
|
||||
crossTask( expandUniform(in), f)
|
||||
|
||||
def exist[T](in: Seq[Task[_]])(f: (AttributeMap, Seq[Task[_]]) => Task[T]): Task[T] =
|
||||
crossTask( expandExist(in), f)
|
||||
|
||||
def apply[T, HL <: HList](in: Tasks[HL])(f: (AttributeMap, Tasks[HL]) => Task[T]): Task[T] =
|
||||
crossTask( expandCrossed(in), f)
|
||||
|
||||
def crossTask[S,T](subs: Cross[S], f: (AttributeMap, S) => Task[T]): Task[T] =
|
||||
crossTask( subs map { case (m, t) => (m, f(m, t)) } )
|
||||
|
||||
def crossTask[T](subs: Cross[Task[T]]): Task[T] =
|
||||
TaskExtra.actionToTask( CrossAction(subs) )
|
||||
|
||||
def expandCrossed[HL <: HList](in: Tasks[HL]): Cross[Tasks[HL]] =
|
||||
in match {
|
||||
case KCons(head, tail) =>
|
||||
val crossTail = expandCrossed(tail)
|
||||
head.work match {
|
||||
case CrossAction(subs) => combine(subs, crossTail)(KCons.apply)
|
||||
case _ => crossTail map { case (m, k) => (m, KCons(head, k) ) }
|
||||
}
|
||||
case x => (AttributeMap.empty, x) :: Nil
|
||||
}
|
||||
|
||||
|
||||
def combine[A,B,C](a: Cross[A], b: Cross[B])(f: (A,B) => C): Cross[C] =
|
||||
{
|
||||
val keysA = keys(a)
|
||||
val keysB = keys(b)
|
||||
val common = keysA & keysB
|
||||
if( keysA.size > keysB.size )
|
||||
merge(b,a, common)( (x,y) => f(y,x) )
|
||||
else
|
||||
merge(a,b, common)(f)
|
||||
}
|
||||
private[this] def merge[A,B,C](a: Cross[A], b: Cross[B], common: AttributeSet)(f: (A,B) => C): Seq[(AttributeMap, C)] =
|
||||
{
|
||||
def related(mapA: AttributeMap): Cross[B] =
|
||||
b filter { case (mapB, _) =>
|
||||
common forall ( c => mapA(c) == mapB(c) )
|
||||
}
|
||||
def check(aRb: Cross[B]) = if(aRb.isEmpty) error("Cross mismatch") else aRb
|
||||
|
||||
for {
|
||||
(mapA, taskA) <- a
|
||||
(mapB, taskB) <- check(related(mapA))
|
||||
} yield
|
||||
( mapA ++ mapB, f(taskA, taskB) )
|
||||
}
|
||||
|
||||
def map[A,B](c: Cross[A])(f: A => B): Cross[B] =
|
||||
for( (m, a) <- c) yield (m, f(a) )
|
||||
}
|
||||
|
|
@ -10,20 +10,18 @@ import java.io.{Closeable, File, FileInputStream, FileOutputStream, InputStreamR
|
|||
|
||||
import Path._
|
||||
|
||||
sealed trait TaskStreams
|
||||
// no longer specific to Tasks, so 'TaskStreams' should be renamed
|
||||
sealed trait TaskStreams[Key]
|
||||
{
|
||||
def default = outID
|
||||
def outID = "out"
|
||||
def errorID = "err"
|
||||
|
||||
def readText(a: Task[_], sid: String = default, update: Boolean = true): Task[BufferedReader]
|
||||
def readBinary(a: Task[_], sid: String = default, update: Boolean = true): Task[BufferedInputStream]
|
||||
def readText(key: Key, sid: String = default): BufferedReader
|
||||
def readBinary(a: Key, sid: String = default): BufferedInputStream
|
||||
|
||||
final def readText(a: Task[_], sid: Option[String], update: Boolean): Task[BufferedReader] =
|
||||
readText(a, getID(sid), update)
|
||||
|
||||
final def readBinary(a: Task[_], sid: Option[String], update: Boolean): Task[BufferedInputStream] =
|
||||
readBinary(a, getID(sid), update)
|
||||
final def readText(a: Key, sid: Option[String]): BufferedReader = readText(a, getID(sid))
|
||||
final def readBinary(a: Key, sid: Option[String]): BufferedInputStream = readBinary(a, getID(sid))
|
||||
|
||||
def text(sid: String = default): PrintWriter
|
||||
def binary(sid: String = default): BufferedOutputStream
|
||||
|
|
@ -34,37 +32,31 @@ sealed trait TaskStreams
|
|||
|
||||
private[this] def getID(s: Option[String]) = s getOrElse default
|
||||
}
|
||||
private[sbt] sealed trait ManagedTaskStreams extends TaskStreams
|
||||
private[sbt] sealed trait ManagedStreams[Key] extends TaskStreams[Key]
|
||||
{
|
||||
def open()
|
||||
def close()
|
||||
}
|
||||
|
||||
sealed trait Streams
|
||||
sealed trait Streams[Key]
|
||||
{
|
||||
def apply(a: Task[_], update: Boolean = true): ManagedTaskStreams
|
||||
def apply(a: Key): ManagedStreams[Key]
|
||||
}
|
||||
object Streams
|
||||
{
|
||||
private[this] val closeQuietly = (c: Closeable) => try { c.close() } catch { case _: IOException => () }
|
||||
|
||||
def multi[Owner](bases: Owner => File, taskOwner: Task[_] => Option[Owner], mkLogger: (Task[_], PrintWriter) => Logger): Streams =
|
||||
{
|
||||
val taskDirectory = (t: Task[_]) => taskOwner(t) map bases getOrElse error("Cannot get streams for task '" + name(t) + "' with no owner.")
|
||||
apply(taskDirectory, mkLogger)
|
||||
}
|
||||
def apply[Key](taskDirectory: Key => File, name: Key => String, mkLogger: (Key, PrintWriter) => Logger): Streams[Key] = new Streams[Key] {
|
||||
|
||||
def apply(taskDirectory: Task[_] => File, mkLogger: (Task[_], PrintWriter) => Logger): Streams = new Streams { streams =>
|
||||
|
||||
def apply(a: Task[_], update: Boolean): ManagedTaskStreams = new ManagedTaskStreams {
|
||||
def apply(a: Key): ManagedStreams[Key] = new ManagedStreams[Key] {
|
||||
private[this] var opened: List[Closeable] = Nil
|
||||
private[this] var closed = false
|
||||
|
||||
def readText(a: Task[_], sid: String = default, update: Boolean = true): Task[BufferedReader] =
|
||||
maybeUpdate(a, readText0(a, sid), update)
|
||||
def readText(a: Key, sid: String = default): BufferedReader =
|
||||
make(a, sid)(f => new BufferedReader(new InputStreamReader(new FileInputStream(f), IO.defaultCharset)) )
|
||||
|
||||
def readBinary(a: Task[_], sid: String = default, update: Boolean = true): Task[BufferedInputStream] =
|
||||
maybeUpdate(a, readBinary0(a, sid), update)
|
||||
def readBinary(a: Key, sid: String = default): BufferedInputStream =
|
||||
make(a, sid)(f => new BufferedInputStream(new FileInputStream(f)))
|
||||
|
||||
def text(sid: String = default): PrintWriter =
|
||||
make(a, sid)(f => new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f), IO.defaultCharset))) )
|
||||
|
|
@ -74,7 +66,7 @@ object Streams
|
|||
|
||||
def log(sid: String): Logger = mkLogger(a, text(sid))
|
||||
|
||||
def make[T <: Closeable](a: Task[_], sid: String)(f: File => T): T = synchronized {
|
||||
def make[T <: Closeable](a: Key, sid: String)(f: File => T): T = synchronized {
|
||||
checkOpen()
|
||||
val file = taskDirectory(a) / sid
|
||||
IO.touch(file)
|
||||
|
|
@ -83,20 +75,6 @@ object Streams
|
|||
t
|
||||
}
|
||||
|
||||
def readText0(a: Task[_], sid: String): BufferedReader =
|
||||
make(a, sid)(f => new BufferedReader(new InputStreamReader(new FileInputStream(f), IO.defaultCharset)) )
|
||||
|
||||
def readBinary0(a: Task[_], sid: String): BufferedInputStream =
|
||||
make(a, sid)(f => new BufferedInputStream(new FileInputStream(f)))
|
||||
|
||||
def maybeUpdate[T](base: Task[_], result: => T, update: Boolean) =
|
||||
{
|
||||
def basic(a: Action[T]) = Task(Info(), a)
|
||||
val main = Pure(result _)
|
||||
val act = if(update) DependsOn(basic(main), base :: Nil) else main
|
||||
basic(act)
|
||||
}
|
||||
|
||||
def open() {}
|
||||
|
||||
def close(): Unit = synchronized {
|
||||
|
|
@ -111,7 +89,4 @@ object Streams
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
def name(a: Task[_]): String = a.info.name getOrElse anonName(a)
|
||||
def anonName(a: Task[_]) = "anon-" + java.lang.Integer.toString(java.lang.System.identityHashCode(a), 36)
|
||||
}
|
||||
|
|
@ -15,17 +15,17 @@ object System
|
|||
|
||||
implicit def to_~>| [K[_], V[_]](map: RMap[K,V]) : K ~>| V = new (K ~>| V) { def apply[T](k: K[T]): Option[V[T]] = map.get(k) }
|
||||
|
||||
def dummyMap[Input, State, Owner](dummyIn: Task[Input], dummyState: Task[State], dummyCtx: Task[Transform.Context[Owner]])(
|
||||
in: Input, state: State, context: Transform.Context[Owner]): Task ~>| Task =
|
||||
def dummyMap[HL <: HList](dummies: KList[Task, HL])(inject: HL): Task ~>| Task =
|
||||
{
|
||||
// helps ensure that the same Task[Nothing] can't be passed for dummyIn and dummyState
|
||||
assert((dummyIn ne dummyState)
|
||||
&& (dummyState ne dummyCtx), "Dummy tasks for Input, State, and Context must be distinct.")
|
||||
|
||||
val pmap = new DelegatingPMap[Task, Task](new collection.mutable.ListMap)
|
||||
pmap(dummyIn) = fromDummyStrict(dummyIn, in)
|
||||
pmap(dummyState) = fromDummyStrict(dummyState, state)
|
||||
pmap(dummyCtx) = fromDummyStrict(dummyCtx, context)
|
||||
def loop[HL <: HList](ds: KList[Task, HL], vs: HL): Unit =
|
||||
(ds, vs) match {
|
||||
case (KCons(dh, dt), vh :+: vt) =>
|
||||
pmap(dh) = fromDummyStrict(dh, vh)
|
||||
loop(dt, vt)
|
||||
case _ => ()
|
||||
}
|
||||
loop(dummies, inject)
|
||||
pmap
|
||||
}
|
||||
|
||||
|
|
@ -35,33 +35,6 @@ object System
|
|||
def apply[T](in: Task[T]): Task[T] = map(in).getOrElse(in)
|
||||
}
|
||||
|
||||
def implied[Owner](owner: Task[_] => Option[Owner], subs: Owner => Iterable[Owner], static: (Owner, String) => Option[Task[_]]): Task ~> Task =
|
||||
new (Task ~> Task) {
|
||||
|
||||
def impliedDeps(t: Task[_]): Seq[Task[_]] =
|
||||
for( n <- t.info.name.toList; o <- owner(t.original).toList; agg <- subs(o); implied <- static(agg, n) ) yield implied
|
||||
|
||||
def withImplied[T](in: Task[T]): Task[T] =
|
||||
{
|
||||
val deps = impliedDeps(in)
|
||||
import TaskExtra._
|
||||
if( deps.isEmpty ) in else Task(Info(), DependsOn(in.local, deps))
|
||||
}
|
||||
|
||||
def apply[T](in: Task[T]): Task[T] = if(in.info.implied) withImplied(in) else in
|
||||
}
|
||||
|
||||
def name(staticName: Task[_] => Option[String]): Task ~> Task =
|
||||
new (Task ~> Task) {
|
||||
def apply[T](in: Task[T]): Task[T] = {
|
||||
val finalName = in.info.name orElse staticName(in.original)
|
||||
finalName match {
|
||||
case None => in
|
||||
case Some(finalName) => in.copy(info = in.info.setName(finalName) )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a natural transformation that replaces occurrences of 'a' with 'b'.
|
||||
* Only valid for M invariant in its type parameter. */
|
||||
def replace[M[_] <: AnyRef, A](a: M[A], b: M[A]) = new (M ~> M) {
|
||||
|
|
@ -79,13 +52,13 @@ object System
|
|||
}
|
||||
|
||||
|
||||
def streamed(streams: Streams, dummy: Task[TaskStreams]): Task ~> Task =
|
||||
def streamed[Key](streams: Streams[Key], dummy: Task[TaskStreams[Key]], key: Task[_] => Key): Task ~> Task =
|
||||
new (Task ~> Task) {
|
||||
def apply[T](t: Task[T]): Task[T] = if(usedInputs(t.work) contains dummy) substitute(t) else t
|
||||
|
||||
def substitute[T](t: Task[T]): Task[T] =
|
||||
{
|
||||
val inStreams = streams(t)
|
||||
val inStreams = streams(key(t))
|
||||
val streamsTask = fromDummy(dummy){ inStreams.open(); inStreams }
|
||||
|
||||
val depMap = replace( dummy, streamsTask )
|
||||
|
|
@ -107,40 +80,14 @@ object System
|
|||
}
|
||||
object Transform
|
||||
{
|
||||
final class Dummies[Input, State, Owner](val dummyIn: Task[Input], val dummyState: Task[State], val dummyStreams: Task[TaskStreams], val dummyContext: Task[Context[Owner]])
|
||||
final class Injected[Input, State](val in: Input, val state: State, val streams: Streams)
|
||||
trait Context[Owner]
|
||||
{
|
||||
def rootOwner: Owner
|
||||
def staticName: Task[_] => Option[String]
|
||||
def owner: Task[_] => Option[Owner]
|
||||
def ownerName: Owner => Option[String]
|
||||
def aggregate: Owner => Iterable[Owner]
|
||||
def static: (Owner, String) => Option[Task[_]]
|
||||
def allTasks(owner: Owner): Iterable[Task[_]]
|
||||
def ownerForName(name: String): Option[Owner]
|
||||
}
|
||||
def setOriginal(delegate: Task ~> Task): Task ~> Task =
|
||||
new (Task ~> Task) {
|
||||
def apply[T](in: Task[T]): Task[T] =
|
||||
{
|
||||
val transformed = delegate(in)
|
||||
if( (transformed eq in) || transformed.info.original.isDefined)
|
||||
transformed
|
||||
else
|
||||
transformed.copy(info = transformed.info.copy(original = in.info.original orElse Some(in)))
|
||||
}
|
||||
}
|
||||
final class Dummies[HL <: HList, Key](val direct: KList[Task, HL], val streams: Task[TaskStreams[Key]])
|
||||
final class Injected[HL <: HList, Key](val direct: HL, val streams: Streams[Key])
|
||||
|
||||
def apply[Input, State, Owner](dummies: Dummies[Input, State, Owner], injected: Injected[Input, State], context: Context[Owner]) =
|
||||
def apply[HL <: HList, Key](dummies: Dummies[HL, Key], injected: Injected[HL, Key])(implicit getKey: Task[_] => Key) =
|
||||
{
|
||||
import dummies._
|
||||
import injected._
|
||||
import context._
|
||||
import System._
|
||||
import Convert._
|
||||
val inputs = dummyMap(dummyIn, dummyState, dummyContext)(in, state, context)
|
||||
Convert.taskToNode ∙ setOriginal(streamed(streams, dummyStreams)) ∙ implied(owner, aggregate, static) ∙ setOriginal(name(staticName)) ∙ getOrId(inputs)
|
||||
val inputs = dummyMap(dummies.direct)(injected.direct)
|
||||
Convert.taskToNode ∙ streamed(injected.streams, dummies.streams, getKey) ∙ getOrId(inputs)
|
||||
}
|
||||
}
|
||||
object Convert
|
||||
|
|
@ -152,7 +99,6 @@ object Convert
|
|||
case FlatMapped(in, f) => toNode(in)( left ∙ f )
|
||||
case DependsOn(in, deps) => toNode(KList.fromList(deps))( _ => Left(in) )
|
||||
case Join(in, f) => uniform(in)(f)
|
||||
case CrossAction(subs) => error("Cannot run cross task: " + subs.mkString("\n\t","\n\t","\n"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ package std
|
|||
import Types._
|
||||
import Task._
|
||||
import java.io.{BufferedInputStream, BufferedReader, File, InputStream}
|
||||
import Cross.{combine, crossTask, exist, expandExist, extract, hasCross, uniform}
|
||||
|
||||
sealed trait MultiInTask[In <: HList]
|
||||
{
|
||||
|
|
@ -33,17 +32,10 @@ sealed trait SingleInTask[S]
|
|||
def || [T >: S](alt: Task[T]): Task[T]
|
||||
def && [T](alt: Task[T]): Task[T]
|
||||
}
|
||||
sealed trait CrossMerge[T]
|
||||
{
|
||||
def merge: Task[Cross[T]]
|
||||
}
|
||||
sealed trait TaskInfo[S]
|
||||
{
|
||||
def named(s: String): Task[S]
|
||||
def describedAs(s: String): Task[S]
|
||||
def implies: Task[S]
|
||||
def implied(flag: Boolean): Task[S]
|
||||
def local: Task[S]
|
||||
def named(s: String): Task[S]
|
||||
}
|
||||
sealed trait ForkTask[S, CC[_]]
|
||||
{
|
||||
|
|
@ -84,9 +76,6 @@ trait TaskExtra
|
|||
final def nop: Task[Unit] = const( () )
|
||||
final def const[T](t: T): Task[T] = task(t)
|
||||
|
||||
final def cross[T](key: AttributeKey[T])(values: T*): Task[T] =
|
||||
CrossAction( for(v <- values) yield ( AttributeMap.empty put (key, v), task(v) ) )
|
||||
|
||||
final implicit def t2ToMulti[A,B](t: (Task[A],Task[B])) = multInputTask(t._1 :^: t._2 :^: KNil)
|
||||
final implicit def f2ToHfun[A,B,R](f: (A,B) => R): (A :+: B :+: HNil => R) = { case a :+: b :+: HNil => f(a,b) }
|
||||
|
||||
|
|
@ -106,87 +95,43 @@ trait TaskExtra
|
|||
}
|
||||
|
||||
final implicit def joinAnyTasks(in: Seq[Task[_]]): JoinTask[Any, Seq] = joinTasks[Any](in map (x => x: Task[Any]))
|
||||
final implicit def joinTasks[S](in: Seq[Task[S]]): JoinTask[S, Seq] =
|
||||
if(hasCross(in)) multJoin(in) else basicJoin(in)
|
||||
|
||||
final def multJoin[S](in: Seq[Task[S]]): JoinTask[S, Seq] = new JoinTask[S, Seq] {
|
||||
def join: Task[Seq[S]] = uniform(in)( (_, s) => basicJoin(s).join )
|
||||
def reduce(f: (S,S) => S): Task[S] = basicJoin(in) reduce f
|
||||
}
|
||||
final def basicJoin[S](in: Seq[Task[S]]): JoinTask[S, Seq] = new JoinTask[S, Seq] {
|
||||
final implicit def joinTasks[S](in: Seq[Task[S]]): JoinTask[S, Seq] = new JoinTask[S, Seq] {
|
||||
def join: Task[Seq[S]] = new Join(in, (s: Seq[Result[S]]) => Right(TaskExtra.all(s)) )
|
||||
def reduce(f: (S,S) => S): Task[S] = TaskExtra.reduce(in.toIndexedSeq, f)
|
||||
}
|
||||
final implicit def crossMerge[T](in: Task[T]): CrossMerge[T] = new CrossMerge[T] {
|
||||
def merge: Task[Cross[T]] = in.work match {
|
||||
case CrossAction(subs) =>
|
||||
val (maps, tasks) = subs.unzip
|
||||
tasks.join.map { maps zip _ }
|
||||
case _ => in map( x => (AttributeMap.empty, x) :: Nil)
|
||||
}
|
||||
}
|
||||
|
||||
final implicit def multInputTask[In <: HList](tasks: Tasks[In]): MultiInTask[In] =
|
||||
if(hasCross(tasks.toList)) multCross(tasks) else multBasic(tasks)
|
||||
|
||||
final def multCross[In <: HList](tasks: Tasks[In]): MultiInTask[In] = new MultiBase[In] {
|
||||
def flatMapR[T](f: Results[In] => Task[T]): Task[T] = Cross(tasks)( (m, ts) => new FlatMapped[T,In](ts, extract[T](m) compose f) )
|
||||
def mapR[T](f: Results[In] => T): Task[T] = Cross(tasks)( (_, ts) => multBasic(ts) mapR f)
|
||||
}
|
||||
|
||||
final def multBasic[In <: HList](tasks: Tasks[In]): MultiInTask[In] = new MultiBase[In] {
|
||||
def flatMapR[T](f: Results[In] => Task[T]): Task[T] = new FlatMapped(tasks, extract() ∙ f)
|
||||
final implicit def multInputTask[In <: HList](tasks: Tasks[In]): MultiInTask[In] = new MultiBase[In] {
|
||||
def flatMapR[T](f: Results[In] => Task[T]): Task[T] = new FlatMapped(tasks, f)
|
||||
def mapR[T](f: Results[In] => T): Task[T] = new Mapped(tasks, f)
|
||||
}
|
||||
|
||||
final implicit def singleInputTask[S](in: Task[S]): SingleInTask[S] =
|
||||
in.work match {
|
||||
case CrossAction(subs) => singleCross(in, subs)
|
||||
case x => singleBasic(in)
|
||||
}
|
||||
|
||||
final def singleCross[S](in: Task[S], subs: Cross[Task[S]]): SingleInTask[S] =
|
||||
new SingleBase[S] {
|
||||
def impl[T](f: (AttributeMap, Task[S]) => Task[T]): Task[T] = CrossAction( subs map { case (m, t) => (m, f(m, t)) } )
|
||||
def flatMapR[T](f: Result[S] => Task[T]): Task[T] = impl( (m, t) => singleBasic(t) flatMapR( Cross.extract(m) ∙ f) )
|
||||
def mapR[T](f: Result[S] => T): Task[T] = impl( (m,t) => t mapR f)
|
||||
def dependsOn(tasks: Task[_]*): Task[S] = crossTask( combine( subs, expandExist(tasks) ){ (t,deps) => new DependsOn(t, deps) } )
|
||||
}
|
||||
|
||||
final def singleBasic[S](in: Task[S]): SingleInTask[S] = new SingleBase[S] {
|
||||
final implicit def singleInputTask[S](in: Task[S]): SingleInTask[S] = new SingleBase[S] {
|
||||
type HL = S :+: HNil
|
||||
private val ml = in :^: KNil
|
||||
private def headM = (_: Results[HL]).combine.head
|
||||
|
||||
def flatMapR[T](f: Result[S] => Task[T]): Task[T] = new FlatMapped[T, HL](ml, Cross.extract() ∙ f ∙ headM)
|
||||
def flatMapR[T](f: Result[S] => Task[T]): Task[T] = new FlatMapped[T, HL](ml, f ∙ headM)
|
||||
def mapR[T](f: Result[S] => T): Task[T] = new Mapped[T, HL](ml, f ∙ headM)
|
||||
def dependsOn(tasks: Task[_]*): Task[S] =
|
||||
if(hasCross(tasks))
|
||||
singleCross(in, (AttributeMap.empty, in) :: Nil).dependsOn(tasks :_*)
|
||||
else
|
||||
new DependsOn(in, tasks)
|
||||
def dependsOn(tasks: Task[_]*): Task[S] = new DependsOn(in, tasks)
|
||||
}
|
||||
|
||||
final implicit def toTaskInfo[S](in: Task[S]): TaskInfo[S] = new TaskInfo[S] {
|
||||
def named(s: String): Task[S] = in.copy(info = in.info.setName(s))
|
||||
def describedAs(s: String): Task[S] = in.copy(info = in.info.setDescription(s))
|
||||
def implied(flag: Boolean): Task[S] = in.copy(info = in.info.setImplied(flag))
|
||||
def implies: Task[S] = implied(true)
|
||||
def local: Task[S] = implied(false)
|
||||
|
||||
def named(s: String): Task[S] = in.copy(info = in.info.setName(s))
|
||||
}
|
||||
|
||||
final implicit def pipeToProcess(t: Task[_])(implicit streams: Task[TaskStreams]): ProcessPipe = new ProcessPipe {
|
||||
final implicit def pipeToProcess[Key](t: Task[_])(implicit streams: Task[TaskStreams[Key]], key: Task[_] => Key): ProcessPipe = new ProcessPipe {
|
||||
def #| (p: ProcessBuilder): Task[Int] = pipe0(None, p)
|
||||
def pipe(sid: String)(p: ProcessBuilder): Task[Int] = pipe0(Some(sid), p)
|
||||
private def pipe0(sid: Option[String], p: ProcessBuilder): Task[Int] =
|
||||
for(s <- streams; in <- s.readBinary(t, sid, true)) yield {
|
||||
for(s <- streams) yield {
|
||||
val in = s.readBinary(key(t), sid)
|
||||
val pio = TaskExtra.processIO(s).withInput( out => { BasicIO.transferFully(in, out); out.close() } )
|
||||
(p run pio).exitValue
|
||||
}
|
||||
}
|
||||
|
||||
final implicit def binaryPipeTask(in: Task[_])(implicit streams: Task[TaskStreams]): BinaryPipe = new BinaryPipe {
|
||||
final implicit def binaryPipeTask[Key](in: Task[_])(implicit streams: Task[TaskStreams[Key]], key: Task[_] => Key): BinaryPipe = new BinaryPipe {
|
||||
def binary[T](f: BufferedInputStream => T): Task[T] = pipe0(None, f)
|
||||
def binary[T](sid: String)(f: BufferedInputStream => T): Task[T] = pipe0(Some(sid), f)
|
||||
|
||||
|
|
@ -194,25 +139,25 @@ trait TaskExtra
|
|||
def #>(sid: String, f: File): Task[Unit] = pipe0(Some(sid), toFile(f))
|
||||
|
||||
private def pipe0 [T](sid: Option[String], f: BufferedInputStream => T): Task[T] =
|
||||
streams flatMap { s => s.readBinary(in, sid, true) map f }
|
||||
streams map { s => f(s.readBinary(key(in), sid)) }
|
||||
|
||||
private def toFile(f: File) = (in: InputStream) => IO.transfer(in, f)
|
||||
}
|
||||
final implicit def textPipeTask(in: Task[_])(implicit streams: Task[TaskStreams]): TextPipe = new TextPipe {
|
||||
final implicit def textPipeTask[Key](in: Task[_])(implicit streams: Task[TaskStreams[Key]], key: Task[_] => Key): TextPipe = new TextPipe {
|
||||
def text[T](f: BufferedReader => T): Task[T] = pipe0(None, f)
|
||||
def text [T](sid: String)(f: BufferedReader => T): Task[T] = pipe0(Some(sid), f)
|
||||
|
||||
private def pipe0 [T](sid: Option[String], f: BufferedReader => T): Task[T] =
|
||||
streams flatMap { s => s.readText(in, sid, true) map f }
|
||||
streams map { s => f(s.readText(key(in), sid)) }
|
||||
}
|
||||
final implicit def linesTask(in: Task[_])(implicit streams: Task[TaskStreams]): TaskLines = new TaskLines {
|
||||
final implicit def linesTask[Key](in: Task[_])(implicit streams: Task[TaskStreams[Key]], key: Task[_] => Key): TaskLines = new TaskLines {
|
||||
def lines: Task[List[String]] = lines0(None)
|
||||
def lines(sid: String): Task[List[String]] = lines0(Some(sid))
|
||||
|
||||
private def lines0 [T](sid: Option[String]): Task[List[String]] =
|
||||
streams flatMap { s => s.readText(in, sid, true) map IO.readLines }
|
||||
streams map { s => IO.readLines(s.readText(key(in), sid) ) }
|
||||
}
|
||||
implicit def processToTask(p: ProcessBuilder)(implicit streams: Task[TaskStreams]): Task[Int] = streams map { s =>
|
||||
implicit def processToTask(p: ProcessBuilder)(implicit streams: Task[TaskStreams[_]]): Task[Int] = streams map { s =>
|
||||
val pio = TaskExtra.processIO(s)
|
||||
(p run pio).exitValue
|
||||
}
|
||||
|
|
@ -245,7 +190,7 @@ trait TaskExtra
|
|||
}
|
||||
object TaskExtra extends TaskExtra
|
||||
{
|
||||
def processIO(s: TaskStreams): ProcessIO =
|
||||
def processIO(s: TaskStreams[_]): ProcessIO =
|
||||
{
|
||||
def transfer(id: String) = (in: InputStream) => BasicIO.transferFully(in, s.binary(id))
|
||||
new ProcessIO(BasicIO.ignoreOut, transfer(s.outID), transfer(s.errorID))
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2010 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import std._
|
||||
import TaskExtra._
|
||||
import collection.mutable
|
||||
|
||||
object TaskMap
|
||||
{
|
||||
/** Memoizes a function that produces a Task. */
|
||||
def apply[A,B](f: A => Task[B]): A => Task[B] =
|
||||
{
|
||||
val map = new mutable.HashMap[A,Task[B]]
|
||||
a => map.synchronized { map.getOrElseUpdate(a, f(a)) }
|
||||
}
|
||||
/** Lifts `f` to produce a Task and memoizes the lifted function. */
|
||||
def make[A,B](f: A => B): A => Task[B] = apply( a => task(f(a)) )
|
||||
}
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2011 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import Types._
|
||||
|
|
@ -6,15 +9,17 @@ package sbt
|
|||
|
||||
sealed trait Settings[Scope]
|
||||
{
|
||||
def data: Scope => AttributeMap
|
||||
def scopes: Seq[Scope]
|
||||
def data: Map[Scope, AttributeMap]
|
||||
def keys(scope: Scope): Set[AttributeKey[_]]
|
||||
def scopes: Set[Scope]
|
||||
def get[T](scope: Scope, key: AttributeKey[T]): Option[T]
|
||||
def set[T](scope: Scope, key: AttributeKey[T], value: T): Settings[Scope]
|
||||
}
|
||||
|
||||
private final class Settings0[Scope](val data: Map[Scope, AttributeMap], val delegates: Scope => Seq[Scope]) extends Settings[Scope]
|
||||
{
|
||||
def scopes: Seq[Scope] = data.keys.toSeq
|
||||
def scopes: Set[Scope] = data.keySet.toSet
|
||||
def keys(scope: Scope) = data(scope).keys.toSet
|
||||
|
||||
def get[T](scope: Scope, key: AttributeKey[T]): Option[T] =
|
||||
delegates(scope).toStream.flatMap(sc => scopeLocal(sc, key) ).headOption
|
||||
|
|
@ -30,7 +35,8 @@ private final class Settings0[Scope](val data: Map[Scope, AttributeMap], val del
|
|||
}
|
||||
}
|
||||
// delegates should contain the input Scope as the first entry
|
||||
final class Init[Scope](val delegates: Scope => Seq[Scope])
|
||||
// this trait is intended to be mixed into an object
|
||||
trait Init[Scope]
|
||||
{
|
||||
final case class ScopedKey[T](scope: Scope, key: AttributeKey[T])
|
||||
|
||||
|
|
@ -42,18 +48,28 @@ final class Init[Scope](val delegates: Scope => Seq[Scope])
|
|||
def value[T](key: ScopedKey[T])(value: => T): Setting[T] = new Value(key, value _)
|
||||
def update[T](key: ScopedKey[T])(f: T => T): Setting[T] = app(key, key :^: KNil)(h => f(h.head))
|
||||
def app[HL <: HList, T](key: ScopedKey[T], inputs: KList[ScopedKey, HL])(f: HL => T): Setting[T] = new Apply(key, f, inputs)
|
||||
def uniform[S,T](key: ScopedKey[T], inputs: Seq[ScopedKey[S]])(f: Seq[S] => T): Setting[T] = new Uniform(key, f, inputs)
|
||||
def kapp[HL <: HList, M[_], T](key: ScopedKey[T], inputs: KList[({type l[t] = ScopedKey[M[t]]})#l, HL])(f: KList[M, HL] => T): Setting[T] = new KApply[HL, M, T](key, f, inputs)
|
||||
|
||||
def empty: Settings[Scope] = new Settings0(Map.empty, delegates)
|
||||
def asTransform(s: Settings[Scope]): ScopedKey ~> Id = new (ScopedKey ~> Id) {
|
||||
def apply[T](k: ScopedKey[T]): T = s.get(k.scope, k.key).get
|
||||
// the following is a temporary workaround for the "... cannot be instantiated from ..." bug, which renders 'kapp' above unusable outside this source file
|
||||
class KApp[HL <: HList, M[_], T] {
|
||||
type Composed[S] = ScopedKey[M[S]]
|
||||
def apply(key: ScopedKey[T], inputs: KList[Composed, HL])(f: KList[M, HL] => T): Setting[T] = new KApply[HL, M, T](key, f, inputs)
|
||||
}
|
||||
|
||||
def make(init: Seq[Setting[_]]): Settings[Scope] =
|
||||
def empty(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = new Settings0(Map.empty, delegates)
|
||||
def asTransform(s: Settings[Scope]): ScopedKey ~> Id = new (ScopedKey ~> Id) {
|
||||
def apply[T](k: ScopedKey[T]): T = getValue(s, k)
|
||||
}
|
||||
def getValue[T](s: Settings[Scope], k: ScopedKey[T]) = s.get(k.scope, k.key).get
|
||||
def asFunction[T](s: Settings[Scope]): ScopedKey[T] => T = k => getValue(s, k)
|
||||
|
||||
def make(init: Seq[Setting[_]])(implicit delegates: Scope => Seq[Scope]): Settings[Scope] =
|
||||
{
|
||||
// group by Scope/Key, dropping dead initializations
|
||||
val sMap: ScopedMap = grouped(init)
|
||||
// delegate references to undefined values according to 'delegates'
|
||||
val dMap: ScopedMap = delegate(sMap)
|
||||
val dMap: ScopedMap = delegate(sMap)(delegates)
|
||||
// merge Seq[Setting[_]] into Compiled
|
||||
val cMap: CompiledMap = compile(dMap)
|
||||
// order the initializations. cyclic references are detected here.
|
||||
|
|
@ -80,9 +96,9 @@ final class Init[Scope](val delegates: Scope => Seq[Scope])
|
|||
def append[T](ss: Seq[Setting[T]], s: Setting[T]): Seq[Setting[T]] =
|
||||
if(s.definitive) s :: Nil else ss :+ s
|
||||
|
||||
def delegate(sMap: ScopedMap): ScopedMap =
|
||||
def delegate(sMap: ScopedMap)(implicit delegates: Scope => Seq[Scope]): ScopedMap =
|
||||
{
|
||||
val md = memoDelegates
|
||||
val md = memoDelegates(delegates)
|
||||
def refMap(refKey: ScopedKey[_]) = new (ScopedKey ~> ScopedKey) { def apply[T](k: ScopedKey[T]) = mapReferenced(sMap, k, md(k.scope), refKey) }
|
||||
val f = new (SettingSeq ~> SettingSeq) { def apply[T](ks: Seq[Setting[T]]) = ks.map{ s => s mapReferenced refMap(s.key) } }
|
||||
sMap mapValues f
|
||||
|
|
@ -102,21 +118,27 @@ final class Init[Scope](val delegates: Scope => Seq[Scope])
|
|||
private[this] def defines(map: ScopedMap, key: ScopedKey[_], refKey: ScopedKey[_]): Boolean =
|
||||
(map get key) match { case Some(Seq(x, _*)) => (refKey != key) || x.definitive; case _ => false }
|
||||
|
||||
private[this] def applyInits(ordered: Seq[Compiled]): Settings[Scope] =
|
||||
private[this] def applyInits(ordered: Seq[Compiled])(implicit delegates: Scope => Seq[Scope]): Settings[Scope] =
|
||||
(empty /: ordered){ (m, comp) => comp.eval(m) }
|
||||
|
||||
private[this] def memoDelegates: Scope => Seq[Scope] =
|
||||
private[this] def memoDelegates(implicit delegates: Scope => Seq[Scope]): Scope => Seq[Scope] =
|
||||
{
|
||||
val dcache = new mutable.HashMap[Scope, Seq[Scope]]
|
||||
(scope: Scope) => dcache.getOrElseUpdate(scope, delegates(scope))
|
||||
}
|
||||
|
||||
private[this] def applySetting[T](map: Settings[Scope], a: Setting[T]): Settings[Scope] =
|
||||
a match
|
||||
private[this] def applySetting[T](map: Settings[Scope], setting: Setting[T]): Settings[Scope] =
|
||||
{
|
||||
def execK[HL <: HList, M[_]](a: KApply[HL, M, T]) =
|
||||
map.set(a.key.scope, a.key.key, a.f(a.inputs.transform[M]( nestCon[ScopedKey, Id, M](asTransform(map)) )) )
|
||||
setting match
|
||||
{
|
||||
case s: Value[T] => map.set(s.key.scope, s.key.key, s.value())
|
||||
case a: Apply[hl, T] => map.set(a.key.scope, a.key.key, a.f(a.inputs.down(asTransform(map))))
|
||||
case u: Uniform[s, T] => map.set(u.key.scope, u.key.key, u.f(u.inputs map asFunction(map)) )
|
||||
case a: Apply[hl, T] => map.set(a.key.scope, a.key.key, a.f(a.inputs down asTransform(map) ) )
|
||||
case ka: KApply[hl, m, T] => execK[hl, m](ka) // separate method needed to workaround bug where m is not recognized as higher-kinded in inline version
|
||||
}
|
||||
}
|
||||
|
||||
final class Uninitialized(key: ScopedKey[_]) extends Exception("Update on uninitialized setting " + key.key.label + " (in " + key.scope + ")")
|
||||
final class Compiled(val dependencies: Iterable[ScopedKey[_]], val eval: Settings[Scope] => Settings[Scope])
|
||||
|
|
@ -126,18 +148,37 @@ final class Init[Scope](val delegates: Scope => Seq[Scope])
|
|||
def key: ScopedKey[T]
|
||||
def definitive: Boolean
|
||||
def dependsOn: Seq[ScopedKey[_]]
|
||||
def mapReferenced(f: MapScoped): Setting[T]
|
||||
def mapReferenced(g: MapScoped): Setting[T]
|
||||
def mapKey(g: MapScoped): Setting[T]
|
||||
}
|
||||
private[this] final class Value[T](val key: ScopedKey[T], val value: () => T) extends Setting[T]
|
||||
{
|
||||
def definitive = true
|
||||
def dependsOn = Nil
|
||||
def mapReferenced(f: MapScoped) = this
|
||||
def mapReferenced(g: MapScoped) = this
|
||||
def mapKey(g: MapScoped): Setting[T] = new Value(g(key), value)
|
||||
}
|
||||
private[this] final class Apply[HL <: HList, T](val key: ScopedKey[T], val f: HL => T, val inputs: KList[ScopedKey, HL]) extends Setting[T]
|
||||
{
|
||||
def definitive = !inputs.toList.contains(key)
|
||||
def dependsOn = inputs.toList - key
|
||||
def dependsOn = remove(inputs.toList, key)
|
||||
def mapReferenced(g: MapScoped) = new Apply(key, f, inputs transform g)
|
||||
def mapKey(g: MapScoped): Setting[T] = new Apply(g(key), f, inputs)
|
||||
}
|
||||
private[this] final class KApply[HL <: HList, M[_], T](val key: ScopedKey[T], val f: KList[M, HL] => T, val inputs: KList[({type l[t] = ScopedKey[M[t]]})#l, HL]) extends Setting[T]
|
||||
{
|
||||
def definitive = !inputs.toList.contains(key)
|
||||
def dependsOn = remove(unnest(inputs.toList), key)
|
||||
def mapReferenced(g: MapScoped) = new KApply[HL, M, T](key, f, inputs.transform[({type l[t] = ScopedKey[M[t]]})#l]( nestCon(g) ) )
|
||||
def mapKey(g: MapScoped): Setting[T] = new KApply[HL, M, T](g(key), f, inputs)
|
||||
private[this] def unnest(l: List[ScopedKey[M[T]] forSome { type T }]): List[ScopedKey[_]] = l.asInstanceOf[List[ScopedKey[_]]]
|
||||
}
|
||||
private[this] final class Uniform[S, T](val key: ScopedKey[T], val f: Seq[S] => T, val inputs: Seq[ScopedKey[S]]) extends Setting[T]
|
||||
{
|
||||
def definitive = !inputs.contains(key)
|
||||
def dependsOn = remove(inputs, key)
|
||||
def mapReferenced(g: MapScoped) = new Uniform(key, f, inputs map g.fn[S])
|
||||
def mapKey(g: MapScoped): Setting[T] = new Uniform(g(key), f, inputs)
|
||||
}
|
||||
private def remove[T](s: Seq[T], v: T) = s filterNot (_ == v)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,16 +8,24 @@ trait TypeFunctions
|
|||
type Id[X] = X
|
||||
sealed trait Const[A] { type Apply[B] = A }
|
||||
sealed trait Compose[A[_], B[_]] { type Apply[T] = A[B[T]] }
|
||||
sealed trait ∙[A[_], B[_]] { type l[T] = A[B[T]] }
|
||||
sealed trait P1of2[M[_,_], A] { type Apply[B] = M[A,B]; type Flip[B] = M[B, A] }
|
||||
|
||||
final val left = new (Id ~> P1of2[Left, Nothing]#Flip) { def apply[T](t: T) = Left(t) }
|
||||
final val right = new (Id ~> P1of2[Right, Nothing]#Apply) { def apply[T](t: T) = Right(t) }
|
||||
final val some = new (Id ~> Some) { def apply[T](t: T) = Some(t) }
|
||||
|
||||
def nestCon[M[_], N[_], G[_]](f: M ~> N): (M ∙ G)#l ~> (N ∙ G)#l =
|
||||
f.asInstanceOf[(M ∙ G)#l ~> (N ∙ G)#l] // implemented with a cast to avoid extra object+method call. castless version:
|
||||
/* new ( (M ∙ G)#l ~> (N ∙ G)#l ) {
|
||||
def apply[T](mg: M[G[T]]): N[G[T]] = f(mg)
|
||||
}*/
|
||||
|
||||
implicit def toFn1[A,B](f: A => B): Fn1[A,B] = new Fn1[A,B] {
|
||||
def ∙[C](g: C => A) = f compose g
|
||||
}
|
||||
|
||||
type Endo[T] = T=>T
|
||||
type ~>|[A[_],B[_]] = A ~> Compose[Option, B]#Apply
|
||||
}
|
||||
object TypeFunctions extends TypeFunctions
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2011 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
object Collections
|
||||
{
|
||||
def separate[T,A,B](ps: Seq[T])(f: T => Either[A,B]): (Seq[A], Seq[B]) =
|
||||
((Nil: Seq[A], Nil: Seq[B]) /: ps)( (xs, y) => prependEither(xs, f(y)) )
|
||||
|
||||
def prependEither[A,B](acc: (Seq[A], Seq[B]), next: Either[A,B]): (Seq[A], Seq[B]) =
|
||||
next match
|
||||
{
|
||||
case Left(l) => (l +: acc._1, acc._2)
|
||||
case Right(r) => (acc._1, r +: acc._2)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue