mirror of https://github.com/sbt/sbt.git
reorganize main
This commit is contained in:
parent
76ab8f8e53
commit
1b147b4953
568
main/Build.scala
568
main/Build.scala
|
|
@ -5,19 +5,12 @@ package sbt
|
|||
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import compiler.{Discovered,Discovery,Eval,EvalImports}
|
||||
import classpath.ClasspathUtilities
|
||||
import scala.annotation.tailrec
|
||||
import collection.mutable
|
||||
import compiler.{Eval, EvalImports}
|
||||
import complete.DefaultParsers.validID
|
||||
import Compiler.{Compilers,Inputs}
|
||||
import Project.{inScope, ScopedKey, ScopeLocal, Setting}
|
||||
import Keys.{appConfiguration, baseDirectory, configuration, streams, Streams, TaskStreams, thisProject, thisProjectRef}
|
||||
import Keys.{dummyState, dummyStreamsManager, isDummy, parseResult, resolvedScoped, streamsManager, taskDefinitionKey}
|
||||
import TypeFunctions.{Endo,Id}
|
||||
import tools.nsc.reporters.ConsoleReporter
|
||||
import Build.{analyzed, data}
|
||||
import Scope.{GlobalScope, ThisScope}
|
||||
import Compiler.Compilers
|
||||
import Project.{ScopedKey, Setting}
|
||||
import Keys.Streams
|
||||
import scala.annotation.tailrec
|
||||
|
||||
// name is more like BuildDefinition, but that is too long
|
||||
trait Build
|
||||
|
|
@ -105,132 +98,6 @@ object EvaluateConfigurations
|
|||
group0(lines, Nil)
|
||||
}
|
||||
}
|
||||
object EvaluateTask
|
||||
{
|
||||
import Load.BuildStructure
|
||||
import Project.display
|
||||
import std.{TaskExtra,Transform}
|
||||
import TaskExtra._
|
||||
import Keys.state
|
||||
|
||||
val SystemProcessors = Runtime.getRuntime.availableProcessors
|
||||
|
||||
def injectSettings: Seq[Project.Setting[_]] = Seq(
|
||||
(state in GlobalScope) ::= dummyState,
|
||||
(streamsManager in GlobalScope) ::= dummyStreamsManager
|
||||
)
|
||||
|
||||
def evalPluginDef(log: Logger)(pluginDef: BuildStructure, state: State): Seq[Attributed[File]] =
|
||||
{
|
||||
val root = ProjectRef(pluginDef.root, Load.getRootProject(pluginDef.units)(pluginDef.root))
|
||||
val pluginKey = Keys.fullClasspath in Configurations.Compile
|
||||
val evaluated = evaluateTask(pluginDef, ScopedKey(pluginKey.scope, pluginKey.key), state, root)
|
||||
val result = evaluated getOrElse error("Plugin classpath does not exist for plugin definition at " + pluginDef.root)
|
||||
processResult(result, log)
|
||||
}
|
||||
def evaluateTask[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, ref: ProjectRef, checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors): Option[Result[T]] =
|
||||
withStreams(structure) { str =>
|
||||
for( (task, toNode) <- getTask(structure, taskKey, state, str, ref) ) yield
|
||||
runTask(task, str, checkCycles, maxWorkers)(toNode)
|
||||
}
|
||||
def logIncResult(result: Result[_], streams: Streams) = result match { case Inc(i) => logIncomplete(i, streams); case _ => () }
|
||||
def logIncomplete(result: Incomplete, streams: Streams)
|
||||
{
|
||||
val log = streams(ScopedKey(GlobalScope, Keys.logged)).log
|
||||
val all = Incomplete linearize result
|
||||
val keyed = for(Incomplete(Some(key: Project.ScopedKey[_]), _, msg, _, ex) <- all) yield (key, msg, ex)
|
||||
val un = all.filter { i => i.node.isEmpty || i.message.isEmpty }
|
||||
for( (key, _, Some(ex)) <- keyed)
|
||||
getStreams(key, streams).log.trace(ex)
|
||||
log.error("Incomplete task(s):")
|
||||
for( (key, msg, ex) <- keyed if(msg.isDefined || ex.isDefined) )
|
||||
getStreams(key, streams).log.error(" " + Project.display(key) + ": " + (msg.toList ++ ex.toList).mkString("\n\t"))
|
||||
for(u <- un)
|
||||
log.debug(u.toString)
|
||||
log.error("Run 'last <task>' for the full log(s).")
|
||||
}
|
||||
def getStreams(key: ScopedKey[_], streams: Streams): TaskStreams =
|
||||
streams(ScopedKey(Project.fillTaskAxis(key).scope, Keys.streams.key))
|
||||
def withStreams[T](structure: BuildStructure)(f: Streams => T): T =
|
||||
{
|
||||
val str = std.Streams.closeable(structure.streams)
|
||||
try { f(str) } finally { str.close() }
|
||||
}
|
||||
|
||||
def getTask[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, streams: Streams, ref: ProjectRef): Option[(Task[T], Execute.NodeView[Task])] =
|
||||
{
|
||||
val thisScope = Load.projectScope(ref)
|
||||
val resolvedScope = Scope.replaceThis(thisScope)( taskKey.scope )
|
||||
for( t <- structure.data.get(resolvedScope, taskKey.key)) yield
|
||||
(t, nodeView(state, streams))
|
||||
}
|
||||
def nodeView[HL <: HList](state: State, streams: Streams, extraDummies: KList[Task, HL] = KNil, extraValues: HL = HNil): Execute.NodeView[Task] =
|
||||
Transform(dummyStreamsManager :^: KCons(dummyState, extraDummies), streams :+: HCons(state, extraValues))
|
||||
|
||||
def runTask[Task[_] <: AnyRef, T](root: Task[T], streams: Streams, checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors)(implicit taskToNode: Execute.NodeView[Task]): Result[T] =
|
||||
{
|
||||
val (service, shutdown) = CompletionService[Task[_], Completed](maxWorkers)
|
||||
|
||||
val x = new Execute[Task](checkCycles)(taskToNode)
|
||||
val result = try { x.run(root)(service) } finally { shutdown() }
|
||||
val replaced = transformInc(result)
|
||||
logIncResult(replaced, streams)
|
||||
replaced
|
||||
}
|
||||
def transformInc[T](result: Result[T]): Result[T] =
|
||||
result.toEither.left.map { i => Incomplete.transformBU(i)(convertCyclicInc andThen liftAnonymous andThen taskToKey ) }
|
||||
def taskToKey: Incomplete => Incomplete = {
|
||||
case in @ Incomplete(Some(node: Task[_]), _, _, _, _) => in.copy(node = transformNode(node))
|
||||
case i => i
|
||||
}
|
||||
type AnyCyclic = Execute[Task]#CyclicException[_]
|
||||
def convertCyclicInc: Incomplete => Incomplete = {
|
||||
case in @ Incomplete(_, _, _, _, Some(c: AnyCyclic)) => in.copy(directCause = Some(new RuntimeException(convertCyclic(c))) )
|
||||
case i => i
|
||||
}
|
||||
def convertCyclic(c: AnyCyclic): String =
|
||||
(c.caller, c.target) match {
|
||||
case (caller: Task[_], target: Task[_]) =>
|
||||
c.toString + (if(caller eq target) "(task: " + name(caller) + ")" else "(caller: " + name(caller) + ", target: " + name(target) + ")" )
|
||||
case _ => c.toString
|
||||
}
|
||||
def name(node: Task[_]): String =
|
||||
node.info.name orElse transformNode(node).map(Project.display) getOrElse ("<anon-" + System.identityHashCode(node).toHexString + ">")
|
||||
def liftAnonymous: Incomplete => Incomplete = {
|
||||
case i @ Incomplete(node, tpe, None, causes, None) =>
|
||||
causes.find( inc => inc.message.isDefined || inc.directCause.isDefined) match {
|
||||
case Some(lift) => i.copy(directCause = lift.directCause, message = lift.message)
|
||||
case None => i
|
||||
}
|
||||
case i => i
|
||||
}
|
||||
def transformNode(node: Task[_]): Option[ScopedKey[_]] =
|
||||
node.info.attributes get taskDefinitionKey
|
||||
|
||||
def processResult[T](result: Result[T], log: Logger, show: Boolean = false): T =
|
||||
onResult(result, log) { v => if(show) println("Result: " + v); v }
|
||||
def onResult[T, S](result: Result[T], log: Logger)(f: T => S): S =
|
||||
result match
|
||||
{
|
||||
case Value(v) => f(v)
|
||||
case Inc(inc) => throw inc
|
||||
}
|
||||
|
||||
// if the return type Seq[Setting[_]] is not explicitly given, scalac hangs
|
||||
val injectStreams: ScopedKey[_] => Seq[Setting[_]] = scoped =>
|
||||
if(scoped.key == streams.key)
|
||||
Seq(streams in scoped.scope <<= streamsManager map { mgr =>
|
||||
val stream = mgr(scoped)
|
||||
stream.open()
|
||||
stream
|
||||
})
|
||||
else if(scoped.key == resolvedScoped.key)
|
||||
Seq(resolvedScoped in scoped.scope :== scoped)
|
||||
else if(scoped.key == parseResult.key)
|
||||
Seq(parseResult in scoped.scope := error("Unsubstituted parse result for " + Project.display(scoped)) )
|
||||
else
|
||||
Nil
|
||||
}
|
||||
object Index
|
||||
{
|
||||
def taskToKeyMap(data: Settings[Scope]): Map[Task[_], ScopedKey[Task[_]]] =
|
||||
|
|
@ -251,431 +118,6 @@ object Index
|
|||
error(duplicates.mkString("AttributeKey ID collisions detected for '", "', '", "'"))
|
||||
}
|
||||
}
|
||||
object Load
|
||||
{
|
||||
import BuildPaths._
|
||||
import BuildStreams._
|
||||
|
||||
// note that there is State is passed in but not pulled out
|
||||
def defaultLoad(state: State, baseDirectory: File, log: Logger): (() => Eval, BuildStructure) =
|
||||
{
|
||||
val provider = state.configuration.provider
|
||||
val scalaProvider = provider.scalaProvider
|
||||
val stagingDirectory = defaultStaging.getCanonicalFile
|
||||
val base = baseDirectory.getCanonicalFile
|
||||
val loader = getClass.getClassLoader
|
||||
val classpath = provider.mainClasspath ++ scalaProvider.jars
|
||||
val compilers = Compiler.compilers(state.configuration, log)
|
||||
val evalPluginDef = EvaluateTask.evalPluginDef(log) _
|
||||
val delegates = memo(defaultDelegates)
|
||||
val inject: Seq[Project.Setting[_]] = ((appConfiguration in GlobalScope) :== state.configuration) +: EvaluateTask.injectSettings
|
||||
val rawConfig = new LoadBuildConfiguration(stagingDirectory, Nil, classpath, loader, compilers, evalPluginDef, delegates, EvaluateTask.injectStreams, inject, log)
|
||||
val commonPlugins = buildGlobalPlugins(defaultGlobalPlugins, state, rawConfig)
|
||||
val config = rawConfig.copy(commonPluginClasspath = commonPlugins)
|
||||
apply(base, state, config)
|
||||
}
|
||||
def buildGlobalPlugins(baseDirectory: File, state: State, config: LoadBuildConfiguration): Seq[Attributed[File]] =
|
||||
if(baseDirectory.isDirectory) buildPluginDefinition(baseDirectory, state, config) else Nil
|
||||
def defaultDelegates: LoadedBuild => Scope => Seq[Scope] = (lb: LoadedBuild) => {
|
||||
val rootProject = getRootProject(lb.units)
|
||||
def resolveRef(project: Reference): ResolvedReference = Scope.resolveReference(lb.root, rootProject, project)
|
||||
Scope.delegates(
|
||||
project => projectInherit(lb, resolveRef(project)),
|
||||
(project, config) => configInherit(lb, resolveRef(project), config, rootProject),
|
||||
(project, task) => Nil,
|
||||
(project, extra) => Nil
|
||||
)
|
||||
}
|
||||
def configInherit(lb: LoadedBuild, ref: ResolvedReference, config: ConfigKey, rootProject: URI => String): Seq[ConfigKey] =
|
||||
ref match
|
||||
{
|
||||
case pr: ProjectRef => configInheritRef(lb, pr, config)
|
||||
case BuildRef(uri) => configInheritRef(lb, ProjectRef(uri, rootProject(uri)), config)
|
||||
}
|
||||
def configInheritRef(lb: LoadedBuild, ref: ProjectRef, config: ConfigKey): Seq[ConfigKey] =
|
||||
getConfiguration(lb.units, ref.build, ref.project, config).extendsConfigs.map(c => ConfigKey(c.name))
|
||||
|
||||
def projectInherit(lb: LoadedBuild, ref: ResolvedReference): Seq[ProjectRef] =
|
||||
ref match
|
||||
{
|
||||
case pr: ProjectRef => projectInheritRef(lb, pr)
|
||||
case BuildRef(uri) => Nil
|
||||
}
|
||||
def projectInheritRef(lb: LoadedBuild, ref: ProjectRef): Seq[ProjectRef] =
|
||||
getProject(lb.units, ref.build, ref.project).delegates
|
||||
|
||||
// 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, s: State, config: LoadBuildConfiguration): (() => Eval, BuildStructure) =
|
||||
{
|
||||
// load, which includes some resolution, but can't fill in project IDs yet, so follow with full resolution
|
||||
val loaded = resolveProjects(load(rootBase, s, config))
|
||||
val projects = loaded.units
|
||||
lazy val rootEval = lazyEval(loaded.units(loaded.root).unit)
|
||||
val settings = finalTransforms(config.injectSettings ++ buildConfigurations(loaded, getRootProject(projects), rootEval))
|
||||
val delegates = config.delegates(loaded)
|
||||
val data = Project.makeSettings(settings, delegates, config.scopeLocal)
|
||||
val index = structureIndex(data)
|
||||
val streams = mkStreams(projects, loaded.root, data)
|
||||
(rootEval, new BuildStructure(projects, loaded.root, settings, data, index, streams, delegates, config.scopeLocal))
|
||||
}
|
||||
|
||||
// map dependencies on the special tasks so that the scope is the same as the defining key
|
||||
// additionally, set the task axis to the defining key if it is not set
|
||||
def finalTransforms(ss: Seq[Setting[_]]): Seq[Setting[_]] =
|
||||
{
|
||||
import Keys.{parseResult, resolvedScoped}
|
||||
def isSpecial(key: AttributeKey[_]) = key == streams.key || key == resolvedScoped.key || key == parseResult.key
|
||||
def mapSpecial(to: ScopedKey[_]) = new (ScopedKey ~> ScopedKey){ def apply[T](key: ScopedKey[T]) =
|
||||
if(isSpecial(key.key))
|
||||
{
|
||||
val replaced = Scope.replaceThis(to.scope)(key.scope)
|
||||
val scope = if(key.key == resolvedScoped.key) replaced else Scope.fillTaskAxis(replaced, to.key)
|
||||
ScopedKey(scope, key.key)
|
||||
}
|
||||
else key
|
||||
}
|
||||
def setDefining[T] = (key: ScopedKey[T], value: T) => value match {
|
||||
case tk: Task[t] => setDefinitionKey(tk, key).asInstanceOf[T]
|
||||
case ik: InputTask[t] => ik.mapTask( tk => setDefinitionKey(tk, key) ).asInstanceOf[T]
|
||||
case _ => value
|
||||
}
|
||||
ss.map(s => s mapReferenced mapSpecial(s.key) mapInit setDefining )
|
||||
}
|
||||
def setDefinitionKey[T](tk: Task[T], key: ScopedKey[_]): Task[T] =
|
||||
if(isDummy(tk)) tk else Task(tk.info.set(Keys.taskDefinitionKey, key), tk.work)
|
||||
|
||||
def structureIndex(settings: Settings[Scope]): StructureIndex =
|
||||
new StructureIndex(Index.stringToKeyMap(settings), Index.taskToKeyMap(settings), KeyIndex(settings.allKeys( (s,k) => ScopedKey(s,k))))
|
||||
|
||||
// Reevaluates settings after modifying them. Does not recompile or reload any build components.
|
||||
def reapply(newSettings: Seq[Setting[_]], structure: BuildStructure): BuildStructure =
|
||||
{
|
||||
val newData = Project.makeSettings(newSettings, structure.delegates, structure.scopeLocal)
|
||||
val newIndex = structureIndex(newData)
|
||||
val newStreams = mkStreams(structure.units, structure.root, newData)
|
||||
new BuildStructure(units = structure.units, root = structure.root, settings = newSettings, data = newData, index = newIndex, streams = newStreams, delegates = structure.delegates, scopeLocal = structure.scopeLocal)
|
||||
}
|
||||
|
||||
def isProjectThis(s: Setting[_]) = s.key.scope.project match { case This | Select(ThisProject) => true; case _ => false }
|
||||
def buildConfigurations(loaded: LoadedBuild, rootProject: URI => String, rootEval: () => Eval): Seq[Setting[_]] =
|
||||
loaded.units.toSeq flatMap { case (uri, build) =>
|
||||
val eval = if(uri == loaded.root) rootEval else lazyEval(build.unit)
|
||||
val pluginSettings = build.unit.plugins.plugins
|
||||
val (pluginThisProject, pluginGlobal) = pluginSettings partition isProjectThis
|
||||
val projectSettings = build.defined flatMap { case (id, project) =>
|
||||
val srcs = configurationSources(project.base)
|
||||
val ref = ProjectRef(uri, id)
|
||||
val defineConfig = for(c <- project.configurations) yield ( (configuration in (ref, ConfigKey(c.name))) :== c)
|
||||
val settings =
|
||||
(thisProject :== project) +:
|
||||
(thisProjectRef :== ref) +:
|
||||
(defineConfig ++ project.settings ++ pluginThisProject ++ configurations(srcs, eval, build.imports))
|
||||
|
||||
// map This to thisScope, Select(p) to mapRef(uri, rootProject, p)
|
||||
transformSettings(projectScope(ref), uri, rootProject, settings)
|
||||
}
|
||||
val buildScope = Scope(Select(BuildRef(uri)), Global, Global, Global)
|
||||
val buildBase = baseDirectory :== build.localBase
|
||||
pluginGlobal ++ inScope(buildScope)(buildBase +: build.buildSettings) ++ projectSettings
|
||||
}
|
||||
def transformSettings(thisScope: Scope, uri: URI, rootProject: URI => String, settings: Seq[Setting[_]]): Seq[Setting[_]] =
|
||||
Project.transform(Scope.resolveScope(thisScope, uri, rootProject), settings)
|
||||
def projectScope(project: Reference): Scope = Scope(Select(project), Global, Global, Global)
|
||||
|
||||
|
||||
def lazyEval(unit: BuildUnit): () => Eval =
|
||||
{
|
||||
lazy val eval = mkEval(unit)
|
||||
() => eval
|
||||
}
|
||||
def mkEval(unit: BuildUnit): Eval = mkEval(unit.definitions, unit.plugins, Nil)
|
||||
def mkEval(defs: LoadedDefinitions, plugs: LoadedPlugins, options: Seq[String]): Eval =
|
||||
new Eval(options, defs.target +: plugs.classpath, s => new ConsoleReporter(s), defs.loader, Some(evalOutputDirectory(defs.base)))
|
||||
|
||||
def configurations(srcs: Seq[File], eval: () => Eval, imports: Seq[String]): Seq[Setting[_]] =
|
||||
if(srcs.isEmpty) Nil else EvaluateConfigurations(eval(), srcs, imports)
|
||||
|
||||
def load(file: File, s: State, config: LoadBuildConfiguration): PartBuild =
|
||||
load(file, uri => loadUnit(uri, RetrieveUnit(config.stagingDirectory, uri), s, config) )
|
||||
def load(file: File, loader: URI => BuildUnit): PartBuild = loadURI(IO.directoryURI(file), loader)
|
||||
def loadURI(uri: URI, loader: URI => BuildUnit): PartBuild =
|
||||
{
|
||||
IO.assertAbsolute(uri)
|
||||
val (referenced, map) = loadAll(uri :: Nil, Map.empty, loader, Map.empty)
|
||||
checkAll(referenced, map)
|
||||
new PartBuild(uri, map)
|
||||
}
|
||||
def loaded(unit: BuildUnit): (PartBuildUnit, List[ProjectReference]) =
|
||||
{
|
||||
val defined = projects(unit)
|
||||
if(defined.isEmpty) error("No projects defined in build unit " + unit)
|
||||
|
||||
// since base directories are resolved at this point (after 'projects'),
|
||||
// we can compare Files instead of converting to URIs
|
||||
def isRoot(p: Project) = p.base == unit.localBase
|
||||
|
||||
val externals = referenced(defined).toList
|
||||
val projectsInRoot = defined.filter(isRoot).map(_.id)
|
||||
val rootProjects = if(projectsInRoot.isEmpty) defined.head.id :: Nil else projectsInRoot
|
||||
(new PartBuildUnit(unit, defined.map(d => (d.id, d)).toMap, rootProjects, buildSettings(unit)), externals)
|
||||
}
|
||||
def buildSettings(unit: BuildUnit): Seq[Setting[_]] =
|
||||
{
|
||||
val buildScope = GlobalScope.copy(project = Select(BuildRef(unit.uri)))
|
||||
val resolve = Scope.resolveBuildScope(buildScope, unit.uri)
|
||||
Project.transform(resolve, unit.definitions.builds.flatMap(_.settings))
|
||||
}
|
||||
|
||||
@tailrec def loadAll(bases: List[URI], references: Map[URI, List[ProjectReference]], externalLoader: URI => BuildUnit, builds: Map[URI, PartBuildUnit]): (Map[URI, List[ProjectReference]], Map[URI, PartBuildUnit]) =
|
||||
bases match
|
||||
{
|
||||
case b :: bs =>
|
||||
if(builds contains b)
|
||||
loadAll(bs, references, externalLoader, builds)
|
||||
else
|
||||
{
|
||||
val (loadedBuild, refs) = loaded(externalLoader(b))
|
||||
checkBuildBase(loadedBuild.unit.localBase)
|
||||
loadAll(refs.flatMap(Reference.uri) reverse_::: bs, references.updated(b, refs), externalLoader, builds.updated(b, loadedBuild))
|
||||
}
|
||||
case Nil => (references, builds)
|
||||
}
|
||||
def checkProjectBase(buildBase: File, projectBase: File)
|
||||
{
|
||||
checkDirectory(projectBase)
|
||||
assert(buildBase == projectBase || IO.relativize(buildBase, projectBase).isDefined, "Directory " + projectBase + " is not contained in build root " + buildBase)
|
||||
}
|
||||
def checkBuildBase(base: File) = checkDirectory(base)
|
||||
def checkDirectory(base: File)
|
||||
{
|
||||
assert(base.isAbsolute, "Not absolute: " + base)
|
||||
if(base.isFile)
|
||||
error("Not a directory: " + base)
|
||||
else if(!base.exists)
|
||||
IO createDirectory base
|
||||
}
|
||||
def resolveAll(builds: Map[URI, PartBuildUnit]): Map[URI, LoadedBuildUnit] =
|
||||
{
|
||||
val rootProject = getRootProject(builds)
|
||||
builds map { case (uri,unit) =>
|
||||
(uri, unit.resolveRefs( ref => Scope.resolveProjectRef(uri, rootProject, ref) ))
|
||||
} toMap;
|
||||
}
|
||||
def checkAll(referenced: Map[URI, List[ProjectReference]], builds: Map[URI, PartBuildUnit])
|
||||
{
|
||||
val rootProject = getRootProject(builds)
|
||||
for( (uri, refs) <- referenced; ref <- refs)
|
||||
{
|
||||
val ProjectRef(refURI, refID) = Scope.resolveProjectRef(uri, rootProject, ref)
|
||||
val loadedUnit = builds(refURI)
|
||||
if(! (loadedUnit.defined contains refID) )
|
||||
error("No project '" + refID + "' in '" + refURI + "'")
|
||||
}
|
||||
}
|
||||
|
||||
def resolveBase(against: File): Project => Project =
|
||||
{
|
||||
def resolve(f: File) =
|
||||
{
|
||||
val fResolved = new File(IO.directoryURI(IO.resolve(against, f)))
|
||||
checkProjectBase(against, fResolved)
|
||||
fResolved
|
||||
}
|
||||
p => p.copy(base = resolve(p.base))
|
||||
}
|
||||
def resolveProjects(loaded: PartBuild): LoadedBuild =
|
||||
{
|
||||
val rootProject = getRootProject(loaded.units)
|
||||
new LoadedBuild(loaded.root, loaded.units map { case (uri, unit) =>
|
||||
IO.assertAbsolute(uri)
|
||||
(uri, resolveProjects(uri, unit, rootProject))
|
||||
})
|
||||
}
|
||||
def resolveProjects(uri: URI, unit: PartBuildUnit, rootProject: URI => String): LoadedBuildUnit =
|
||||
{
|
||||
IO.assertAbsolute(uri)
|
||||
val resolve = (_: Project).resolve(ref => Scope.resolveProjectRef(uri, rootProject, ref))
|
||||
new LoadedBuildUnit(unit.unit, unit.defined mapValues resolve, unit.rootProjects, unit.buildSettings)
|
||||
}
|
||||
def projects(unit: BuildUnit): Seq[Project] =
|
||||
{
|
||||
// we don't have the complete build graph loaded, so we don't have the rootProject function yet.
|
||||
// Therefore, we use resolveProjectBuild instead of resolveProjectRef. After all builds are loaded, we can fully resolve ProjectReferences.
|
||||
val resolveBuild = (_: Project).resolveBuild(ref => Scope.resolveProjectBuild(unit.uri, ref))
|
||||
val resolve = resolveBuild compose resolveBase(unit.localBase)
|
||||
unit.definitions.builds.flatMap(_.projects map resolve)
|
||||
}
|
||||
def getRootProject(map: Map[URI, BuildUnitBase]): 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): ResolvedProject =
|
||||
getBuild(map, uri).defined.getOrElse(id, noProject(uri, id))
|
||||
def getBuild[T](map: Map[URI, T], uri: URI): T =
|
||||
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(uri: URI, localBase: File, s: State, config: LoadBuildConfiguration): BuildUnit =
|
||||
{
|
||||
val normBase = localBase.getCanonicalFile
|
||||
val defDir = selectProjectDir(normBase)
|
||||
val pluginDir = pluginDirectory(defDir)
|
||||
val plugs = plugins(pluginDir, s, config)
|
||||
|
||||
val defs = definitionSources(defDir)
|
||||
val target = buildOutputDirectory(defDir, config.compilers)
|
||||
IO.createDirectory(target)
|
||||
val loadedDefs =
|
||||
if(defs.isEmpty)
|
||||
new LoadedDefinitions(defDir, target, plugs.loader, Build.default(normBase) :: Nil, Nil)
|
||||
else
|
||||
definitions(defDir, target, defs, plugs, config.compilers, config.log, normBase)
|
||||
|
||||
new BuildUnit(uri, normBase, loadedDefs, plugs)
|
||||
}
|
||||
|
||||
def plugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins = if(dir.exists) buildPlugins(dir, s, config) else noPlugins(dir, config)
|
||||
def noPlugins(dir: File, config: LoadBuildConfiguration): LoadedPlugins = loadPluginDefinition(dir, config, config.commonPluginClasspath)
|
||||
def buildPlugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins =
|
||||
loadPluginDefinition(dir, config, buildPluginDefinition(dir, s, config))
|
||||
|
||||
def loadPluginDefinition(dir: File, config: LoadBuildConfiguration, pluginClasspath: Seq[Attributed[File]]): LoadedPlugins =
|
||||
{
|
||||
val definitionClasspath = if(pluginClasspath.isEmpty) config.classpath else (data(pluginClasspath) ++ config.classpath).distinct
|
||||
val pluginLoader = if(pluginClasspath.isEmpty) config.loader else ClasspathUtilities.toLoader(definitionClasspath, config.loader)
|
||||
loadPlugins(dir, definitionClasspath, pluginLoader, analyzed(pluginClasspath))
|
||||
}
|
||||
def buildPluginDefinition(dir: File, s: State, config: LoadBuildConfiguration): Seq[Attributed[File]] =
|
||||
{
|
||||
val (eval,pluginDef) = apply(dir, s, config)
|
||||
val pluginState = Project.setProject(Load.initialSession(pluginDef, eval), pluginDef, s)
|
||||
val thisPluginClasspath = config.evalPluginDef(pluginDef, pluginState)
|
||||
(thisPluginClasspath ++ config.commonPluginClasspath).distinct
|
||||
}
|
||||
|
||||
def definitions(base: File, targetBase: File, srcs: Seq[File], plugins: LoadedPlugins, compilers: Compilers, log: Logger, buildBase: File): LoadedDefinitions =
|
||||
{
|
||||
val (inputs, defAnalysis) = build(plugins.classpath, srcs, targetBase, compilers, log)
|
||||
val target = inputs.config.classesDirectory
|
||||
val definitionLoader = ClasspathUtilities.toLoader(target :: Nil, plugins.loader)
|
||||
val defNames = findDefinitions(defAnalysis)
|
||||
val defs = if(defNames.isEmpty) Build.default(buildBase) :: Nil else loadDefinitions(definitionLoader, defNames)
|
||||
new LoadedDefinitions(base, target, definitionLoader, defs, defNames)
|
||||
}
|
||||
|
||||
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], target: File, compilers: Compilers, log: Logger): (Inputs, inc.Analysis) =
|
||||
{
|
||||
val inputs = Compiler.inputs(classpath, sources, target, Nil, Nil, Compiler.DefaultMaxErrors, CompileOrder.Mixed)(compilers, log)
|
||||
val analysis =
|
||||
try { Compiler(inputs, log) }
|
||||
catch { case _: xsbti.CompileFailed => throw new NoMessageException } // compiler already logged errors
|
||||
(inputs, analysis)
|
||||
}
|
||||
|
||||
def loadPlugins(dir: File, classpath: Seq[File], loader: ClassLoader, analysis: Seq[inc.Analysis]): LoadedPlugins =
|
||||
{
|
||||
val (pluginNames, plugins) = if(classpath.isEmpty) (Nil, Nil) else {
|
||||
val names = ( binaryPlugins(loader) ++ (analysis flatMap findPlugins) ).distinct
|
||||
(names, loadPlugins(loader, names) )
|
||||
}
|
||||
new LoadedPlugins(dir, classpath, loader, plugins, pluginNames)
|
||||
}
|
||||
def binaryPlugins(loader: ClassLoader): Seq[String] =
|
||||
{
|
||||
import collection.JavaConversions._
|
||||
loader.getResources("sbt/sbt.plugins").toSeq flatMap { u => IO.readLinesURL(u) map { _.trim } filter { !_.isEmpty } };
|
||||
}
|
||||
|
||||
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 importAll(values: Seq[String]) = if(values.isEmpty) Nil else values.map( _ + "._" ).mkString("import ", ", ", "") :: Nil
|
||||
|
||||
def findPlugins(analysis: inc.Analysis): Seq[String] = discover(analysis, "sbt.Plugin")
|
||||
def findDefinitions(analysis: inc.Analysis): Seq[String] = discover(analysis, "sbt.Build")
|
||||
def discover(analysis: inc.Analysis, subclasses: String*): Seq[String] =
|
||||
{
|
||||
val subclassSet = subclasses.toSet
|
||||
val ds = Discovery(subclassSet, Set.empty)(Tests.allDefs(analysis))
|
||||
ds.flatMap {
|
||||
case (definition, Discovered(subs,_,_,true)) =>
|
||||
if((subs & subclassSet).isEmpty) Nil else definition.name :: Nil
|
||||
case _ => Nil
|
||||
}
|
||||
}
|
||||
|
||||
def initialSession(structure: BuildStructure, rootEval: () => Eval): SessionSettings =
|
||||
new SessionSettings(structure.root, rootProjectMap(structure.units), structure.settings, Map.empty, Map.empty, rootEval)
|
||||
|
||||
def rootProjectMap(units: Map[URI, LoadedBuildUnit]): Map[URI, String] =
|
||||
{
|
||||
val getRoot = getRootProject(units)
|
||||
units.keys.map(uri => (uri, getRoot(uri))).toMap
|
||||
}
|
||||
|
||||
def baseImports = "import sbt._, Process._, Keys._" :: Nil
|
||||
|
||||
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], val buildNames: Seq[String])
|
||||
final class LoadedPlugins(val base: File, val classpath: Seq[File], val loader: ClassLoader, val plugins: Seq[Setting[_]], val pluginNames: Seq[String])
|
||||
final class BuildUnit(val uri: URI, val localBase: File, val definitions: LoadedDefinitions, val plugins: LoadedPlugins)
|
||||
{
|
||||
override def toString = if(uri.getScheme == "file") localBase.toString else (uri + " (locally: " + localBase +")")
|
||||
}
|
||||
|
||||
final class LoadedBuild(val root: URI, val units: Map[URI, LoadedBuildUnit])
|
||||
final class PartBuild(val root: URI, val units: Map[URI, PartBuildUnit])
|
||||
sealed trait BuildUnitBase { def rootProjects: Seq[String]; def buildSettings: Seq[Setting[_]] }
|
||||
final class PartBuildUnit(val unit: BuildUnit, val defined: Map[String, Project], val rootProjects: Seq[String], val buildSettings: Seq[Setting[_]]) extends BuildUnitBase
|
||||
{
|
||||
def resolve(f: Project => ResolvedProject): LoadedBuildUnit = new LoadedBuildUnit(unit, defined mapValues f, rootProjects, buildSettings)
|
||||
def resolveRefs(f: ProjectReference => ProjectRef): LoadedBuildUnit = resolve(_ resolve f)
|
||||
}
|
||||
final class LoadedBuildUnit(val unit: BuildUnit, val defined: Map[String, ResolvedProject], val rootProjects: Seq[String], val buildSettings: Seq[Setting[_]]) extends BuildUnitBase
|
||||
{
|
||||
assert(!rootProjects.isEmpty, "No root projects defined for build unit " + unit)
|
||||
def localBase = unit.localBase
|
||||
def classpath = unit.definitions.target +: unit.plugins.classpath
|
||||
def loader = unit.definitions.loader
|
||||
def imports = getImports(unit)
|
||||
override def toString = unit.toString
|
||||
}
|
||||
def getImports(unit: BuildUnit) = baseImports ++ importAll(unit.plugins.pluginNames ++ unit.definitions.buildNames)
|
||||
|
||||
def referenced[PR <: ProjectReference](definitions: Seq[ProjectDefinition[PR]]): Seq[PR] = definitions flatMap { _.referenced }
|
||||
|
||||
final class BuildStructure(val units: Map[URI, LoadedBuildUnit], val root: URI, val settings: Seq[Setting[_]], val data: Settings[Scope], val index: StructureIndex, val streams: Streams, val delegates: Scope => Seq[Scope], val scopeLocal: ScopeLocal)
|
||||
final case class LoadBuildConfiguration(stagingDirectory: File, commonPluginClasspath: Seq[Attributed[File]], classpath: Seq[File], loader: ClassLoader, compilers: Compilers, evalPluginDef: (BuildStructure, State) => Seq[Attributed[File]], delegates: LoadedBuild => Scope => Seq[Scope], scopeLocal: ScopeLocal, injectSettings: Seq[Setting[_]], 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[_]]], val keyIndex: KeyIndex)
|
||||
|
||||
private[this] def memo[A,B](implicit f: A => B): A => B =
|
||||
{
|
||||
val dcache = new mutable.HashMap[A,B]
|
||||
(a: A) => dcache.getOrElseUpdate(a, f(a))
|
||||
}
|
||||
}
|
||||
object BuildStreams
|
||||
{
|
||||
import Load.{BuildStructure, LoadedBuildUnit}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2011 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import java.io.File
|
||||
import compiler.AnalyzingCompiler
|
||||
|
||||
object ConsoleProject
|
||||
{
|
||||
def apply(state: State, extra: String)(implicit log: Logger)
|
||||
{
|
||||
val extracted = Project extract state
|
||||
val bindings = ("currentState" -> state) :: ("extracted" -> extracted ) :: Nil
|
||||
val unit = extracted.currentUnit
|
||||
val compiler = Compiler.compilers(state.configuration, log).scalac
|
||||
val imports = Load.getImports(unit.unit) ++ Load.importAll(bindings.map(_._1))
|
||||
val importString = imports.mkString("", ";\n", ";\n\n")
|
||||
val initCommands = importString + extra
|
||||
val loader = classOf[State].getClassLoader
|
||||
(new Console(compiler))(unit.classpath, Nil, initCommands)(Some(loader), bindings)
|
||||
}
|
||||
}
|
||||
|
|
@ -322,7 +322,7 @@ object Defaults
|
|||
def discoverMainClasses(analysis: inc.Analysis): Seq[String] =
|
||||
Discovery.applications(Tests.allDefs(analysis)) collect { case (definition, discovered) if(discovered.hasMain) => definition.name }
|
||||
|
||||
def consoleProjectTask = (state, streams, initialCommands in consoleProject) map { (state, s, extra) => Console.sbt(state, extra)(s.log); println() }
|
||||
def consoleProjectTask = (state, streams, initialCommands in consoleProject) map { (state, s, extra) => ConsoleProject(state, extra)(s.log); println() }
|
||||
def consoleTask: Initialize[Task[Unit]] = consoleTask(fullClasspath, console)
|
||||
def consoleQuickTask = consoleTask(externalDependencyClasspath, consoleQuick)
|
||||
def consoleTask(classpath: TaskKey[Classpath], task: TaskKey[_]): Initialize[Task[Unit]] = (compilers, classpath, scalacOptions in task, initialCommands in task, streams) map {
|
||||
|
|
@ -426,7 +426,9 @@ object Defaults
|
|||
lazy val itSettings = inConfig(Configurations.IntegrationTest)(testSettings)
|
||||
lazy val defaultConfigs = inConfig(CompileConf)(compileSettings) ++ inConfig(TestConf)(testSettings)
|
||||
|
||||
lazy val defaultSettings: Seq[Setting[_]] = projectCore ++ paths ++ baseClasspaths ++ baseTasks ++ compileBase ++ defaultConfigs ++ disableAggregation
|
||||
// settings that are not specific to a configuration
|
||||
lazy val projectBaseSettings: Seq[Setting[_]] = projectCore ++ paths ++ baseClasspaths ++ baseTasks ++ compileBase ++ disableAggregation
|
||||
lazy val defaultSettings: Seq[Setting[_]] = projectBaseSettings ++ defaultConfigs
|
||||
}
|
||||
object Classpaths
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,137 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2011 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import java.io.File
|
||||
import Project.{ScopedKey, Setting}
|
||||
import Keys.{streams, Streams, TaskStreams}
|
||||
import Keys.{dummyState, dummyStreamsManager, parseResult, resolvedScoped, streamsManager, taskDefinitionKey}
|
||||
import Scope.{GlobalScope, ThisScope}
|
||||
|
||||
object EvaluateTask
|
||||
{
|
||||
import Load.BuildStructure
|
||||
import Project.display
|
||||
import std.{TaskExtra,Transform}
|
||||
import TaskExtra._
|
||||
import Keys.state
|
||||
|
||||
val SystemProcessors = Runtime.getRuntime.availableProcessors
|
||||
|
||||
def injectSettings: Seq[Setting[_]] = Seq(
|
||||
(state in GlobalScope) ::= dummyState,
|
||||
(streamsManager in GlobalScope) ::= dummyStreamsManager
|
||||
)
|
||||
|
||||
def evalPluginDef(log: Logger)(pluginDef: BuildStructure, state: State): Seq[Attributed[File]] =
|
||||
{
|
||||
val root = ProjectRef(pluginDef.root, Load.getRootProject(pluginDef.units)(pluginDef.root))
|
||||
val pluginKey = Keys.fullClasspath in Configurations.Compile
|
||||
val evaluated = evaluateTask(pluginDef, ScopedKey(pluginKey.scope, pluginKey.key), state, root)
|
||||
val result = evaluated getOrElse error("Plugin classpath does not exist for plugin definition at " + pluginDef.root)
|
||||
processResult(result, log)
|
||||
}
|
||||
def evaluateTask[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, ref: ProjectRef, checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors): Option[Result[T]] =
|
||||
withStreams(structure) { str =>
|
||||
for( (task, toNode) <- getTask(structure, taskKey, state, str, ref) ) yield
|
||||
runTask(task, str, checkCycles, maxWorkers)(toNode)
|
||||
}
|
||||
def logIncResult(result: Result[_], streams: Streams) = result match { case Inc(i) => logIncomplete(i, streams); case _ => () }
|
||||
def logIncomplete(result: Incomplete, streams: Streams)
|
||||
{
|
||||
val log = streams(ScopedKey(GlobalScope, Keys.logged)).log
|
||||
val all = Incomplete linearize result
|
||||
val keyed = for(Incomplete(Some(key: Project.ScopedKey[_]), _, msg, _, ex) <- all) yield (key, msg, ex)
|
||||
val un = all.filter { i => i.node.isEmpty || i.message.isEmpty }
|
||||
for( (key, _, Some(ex)) <- keyed)
|
||||
getStreams(key, streams).log.trace(ex)
|
||||
log.error("Incomplete task(s):")
|
||||
for( (key, msg, ex) <- keyed if(msg.isDefined || ex.isDefined) )
|
||||
getStreams(key, streams).log.error(" " + Project.display(key) + ": " + (msg.toList ++ ex.toList).mkString("\n\t"))
|
||||
for(u <- un)
|
||||
log.debug(u.toString)
|
||||
log.error("Run 'last <task>' for the full log(s).")
|
||||
}
|
||||
def getStreams(key: ScopedKey[_], streams: Streams): TaskStreams =
|
||||
streams(ScopedKey(Project.fillTaskAxis(key).scope, Keys.streams.key))
|
||||
def withStreams[T](structure: BuildStructure)(f: Streams => T): T =
|
||||
{
|
||||
val str = std.Streams.closeable(structure.streams)
|
||||
try { f(str) } finally { str.close() }
|
||||
}
|
||||
|
||||
def getTask[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, streams: Streams, ref: ProjectRef): Option[(Task[T], Execute.NodeView[Task])] =
|
||||
{
|
||||
val thisScope = Load.projectScope(ref)
|
||||
val resolvedScope = Scope.replaceThis(thisScope)( taskKey.scope )
|
||||
for( t <- structure.data.get(resolvedScope, taskKey.key)) yield
|
||||
(t, nodeView(state, streams))
|
||||
}
|
||||
def nodeView[HL <: HList](state: State, streams: Streams, extraDummies: KList[Task, HL] = KNil, extraValues: HL = HNil): Execute.NodeView[Task] =
|
||||
Transform(dummyStreamsManager :^: KCons(dummyState, extraDummies), streams :+: HCons(state, extraValues))
|
||||
|
||||
def runTask[Task[_] <: AnyRef, T](root: Task[T], streams: Streams, checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors)(implicit taskToNode: Execute.NodeView[Task]): Result[T] =
|
||||
{
|
||||
val (service, shutdown) = CompletionService[Task[_], Completed](maxWorkers)
|
||||
|
||||
val x = new Execute[Task](checkCycles)(taskToNode)
|
||||
val result = try { x.run(root)(service) } finally { shutdown() }
|
||||
val replaced = transformInc(result)
|
||||
logIncResult(replaced, streams)
|
||||
replaced
|
||||
}
|
||||
def transformInc[T](result: Result[T]): Result[T] =
|
||||
result.toEither.left.map { i => Incomplete.transformBU(i)(convertCyclicInc andThen liftAnonymous andThen taskToKey ) }
|
||||
def taskToKey: Incomplete => Incomplete = {
|
||||
case in @ Incomplete(Some(node: Task[_]), _, _, _, _) => in.copy(node = transformNode(node))
|
||||
case i => i
|
||||
}
|
||||
type AnyCyclic = Execute[Task]#CyclicException[_]
|
||||
def convertCyclicInc: Incomplete => Incomplete = {
|
||||
case in @ Incomplete(_, _, _, _, Some(c: AnyCyclic)) => in.copy(directCause = Some(new RuntimeException(convertCyclic(c))) )
|
||||
case i => i
|
||||
}
|
||||
def convertCyclic(c: AnyCyclic): String =
|
||||
(c.caller, c.target) match {
|
||||
case (caller: Task[_], target: Task[_]) =>
|
||||
c.toString + (if(caller eq target) "(task: " + name(caller) + ")" else "(caller: " + name(caller) + ", target: " + name(target) + ")" )
|
||||
case _ => c.toString
|
||||
}
|
||||
def name(node: Task[_]): String =
|
||||
node.info.name orElse transformNode(node).map(Project.display) getOrElse ("<anon-" + System.identityHashCode(node).toHexString + ">")
|
||||
def liftAnonymous: Incomplete => Incomplete = {
|
||||
case i @ Incomplete(node, tpe, None, causes, None) =>
|
||||
causes.find( inc => inc.message.isDefined || inc.directCause.isDefined) match {
|
||||
case Some(lift) => i.copy(directCause = lift.directCause, message = lift.message)
|
||||
case None => i
|
||||
}
|
||||
case i => i
|
||||
}
|
||||
def transformNode(node: Task[_]): Option[ScopedKey[_]] =
|
||||
node.info.attributes get taskDefinitionKey
|
||||
|
||||
def processResult[T](result: Result[T], log: Logger, show: Boolean = false): T =
|
||||
onResult(result, log) { v => if(show) println("Result: " + v); v }
|
||||
def onResult[T, S](result: Result[T], log: Logger)(f: T => S): S =
|
||||
result match
|
||||
{
|
||||
case Value(v) => f(v)
|
||||
case Inc(inc) => throw inc
|
||||
}
|
||||
|
||||
// if the return type Seq[Setting[_]] is not explicitly given, scalac hangs
|
||||
val injectStreams: ScopedKey[_] => Seq[Setting[_]] = scoped =>
|
||||
if(scoped.key == streams.key)
|
||||
Seq(streams in scoped.scope <<= streamsManager map { mgr =>
|
||||
val stream = mgr(scoped)
|
||||
stream.open()
|
||||
stream
|
||||
})
|
||||
else if(scoped.key == resolvedScoped.key)
|
||||
Seq(resolvedScoped in scoped.scope :== scoped)
|
||||
else if(scoped.key == parseResult.key)
|
||||
Seq(parseResult in scoped.scope := error("Unsubstituted parse result for " + Project.display(scoped)) )
|
||||
else
|
||||
Nil
|
||||
}
|
||||
|
|
@ -0,0 +1,444 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2011 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import compiler.{Discovered,Discovery,Eval,EvalImports}
|
||||
import classpath.ClasspathUtilities
|
||||
import scala.annotation.tailrec
|
||||
import collection.mutable
|
||||
import Compiler.{Compilers,Inputs}
|
||||
import Project.{inScope, ScopedKey, ScopeLocal, Setting}
|
||||
import Keys.{appConfiguration, baseDirectory, configuration, streams, Streams, thisProject, thisProjectRef}
|
||||
import Keys.{isDummy, parseResult, resolvedScoped, taskDefinitionKey}
|
||||
import tools.nsc.reporters.ConsoleReporter
|
||||
import Build.{analyzed, data}
|
||||
import Scope.{GlobalScope, ThisScope}
|
||||
|
||||
object Load
|
||||
{
|
||||
import BuildPaths._
|
||||
import BuildStreams._
|
||||
|
||||
// note that there is State is passed in but not pulled out
|
||||
def defaultLoad(state: State, baseDirectory: File, log: Logger): (() => Eval, BuildStructure) =
|
||||
{
|
||||
val provider = state.configuration.provider
|
||||
val scalaProvider = provider.scalaProvider
|
||||
val stagingDirectory = defaultStaging.getCanonicalFile
|
||||
val base = baseDirectory.getCanonicalFile
|
||||
val loader = getClass.getClassLoader
|
||||
val classpath = provider.mainClasspath ++ scalaProvider.jars
|
||||
val compilers = Compiler.compilers(state.configuration, log)
|
||||
val evalPluginDef = EvaluateTask.evalPluginDef(log) _
|
||||
val delegates = memo(defaultDelegates)
|
||||
val inject: Seq[Project.Setting[_]] = ((appConfiguration in GlobalScope) :== state.configuration) +: EvaluateTask.injectSettings
|
||||
val rawConfig = new LoadBuildConfiguration(stagingDirectory, Nil, classpath, loader, compilers, evalPluginDef, delegates, EvaluateTask.injectStreams, inject, log)
|
||||
val commonPlugins = buildGlobalPlugins(defaultGlobalPlugins, state, rawConfig)
|
||||
val config = rawConfig.copy(commonPluginClasspath = commonPlugins)
|
||||
apply(base, state, config)
|
||||
}
|
||||
def buildGlobalPlugins(baseDirectory: File, state: State, config: LoadBuildConfiguration): Seq[Attributed[File]] =
|
||||
if(baseDirectory.isDirectory) buildPluginDefinition(baseDirectory, state, config) else Nil
|
||||
def defaultDelegates: LoadedBuild => Scope => Seq[Scope] = (lb: LoadedBuild) => {
|
||||
val rootProject = getRootProject(lb.units)
|
||||
def resolveRef(project: Reference): ResolvedReference = Scope.resolveReference(lb.root, rootProject, project)
|
||||
Scope.delegates(
|
||||
project => projectInherit(lb, resolveRef(project)),
|
||||
(project, config) => configInherit(lb, resolveRef(project), config, rootProject),
|
||||
(project, task) => Nil,
|
||||
(project, extra) => Nil
|
||||
)
|
||||
}
|
||||
def configInherit(lb: LoadedBuild, ref: ResolvedReference, config: ConfigKey, rootProject: URI => String): Seq[ConfigKey] =
|
||||
ref match
|
||||
{
|
||||
case pr: ProjectRef => configInheritRef(lb, pr, config)
|
||||
case BuildRef(uri) => configInheritRef(lb, ProjectRef(uri, rootProject(uri)), config)
|
||||
}
|
||||
def configInheritRef(lb: LoadedBuild, ref: ProjectRef, config: ConfigKey): Seq[ConfigKey] =
|
||||
getConfiguration(lb.units, ref.build, ref.project, config).extendsConfigs.map(c => ConfigKey(c.name))
|
||||
|
||||
def projectInherit(lb: LoadedBuild, ref: ResolvedReference): Seq[ProjectRef] =
|
||||
ref match
|
||||
{
|
||||
case pr: ProjectRef => projectInheritRef(lb, pr)
|
||||
case BuildRef(uri) => Nil
|
||||
}
|
||||
def projectInheritRef(lb: LoadedBuild, ref: ProjectRef): Seq[ProjectRef] =
|
||||
getProject(lb.units, ref.build, ref.project).delegates
|
||||
|
||||
// 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, s: State, config: LoadBuildConfiguration): (() => Eval, BuildStructure) =
|
||||
{
|
||||
// load, which includes some resolution, but can't fill in project IDs yet, so follow with full resolution
|
||||
val loaded = resolveProjects(load(rootBase, s, config))
|
||||
val projects = loaded.units
|
||||
lazy val rootEval = lazyEval(loaded.units(loaded.root).unit)
|
||||
val settings = finalTransforms(config.injectSettings ++ buildConfigurations(loaded, getRootProject(projects), rootEval))
|
||||
val delegates = config.delegates(loaded)
|
||||
val data = Project.makeSettings(settings, delegates, config.scopeLocal)
|
||||
val index = structureIndex(data)
|
||||
val streams = mkStreams(projects, loaded.root, data)
|
||||
(rootEval, new BuildStructure(projects, loaded.root, settings, data, index, streams, delegates, config.scopeLocal))
|
||||
}
|
||||
|
||||
// map dependencies on the special tasks so that the scope is the same as the defining key
|
||||
// additionally, set the task axis to the defining key if it is not set
|
||||
def finalTransforms(ss: Seq[Setting[_]]): Seq[Setting[_]] =
|
||||
{
|
||||
import Keys.{parseResult, resolvedScoped}
|
||||
def isSpecial(key: AttributeKey[_]) = key == streams.key || key == resolvedScoped.key || key == parseResult.key
|
||||
def mapSpecial(to: ScopedKey[_]) = new (ScopedKey ~> ScopedKey){ def apply[T](key: ScopedKey[T]) =
|
||||
if(isSpecial(key.key))
|
||||
{
|
||||
val replaced = Scope.replaceThis(to.scope)(key.scope)
|
||||
val scope = if(key.key == resolvedScoped.key) replaced else Scope.fillTaskAxis(replaced, to.key)
|
||||
ScopedKey(scope, key.key)
|
||||
}
|
||||
else key
|
||||
}
|
||||
def setDefining[T] = (key: ScopedKey[T], value: T) => value match {
|
||||
case tk: Task[t] => setDefinitionKey(tk, key).asInstanceOf[T]
|
||||
case ik: InputTask[t] => ik.mapTask( tk => setDefinitionKey(tk, key) ).asInstanceOf[T]
|
||||
case _ => value
|
||||
}
|
||||
ss.map(s => s mapReferenced mapSpecial(s.key) mapInit setDefining )
|
||||
}
|
||||
def setDefinitionKey[T](tk: Task[T], key: ScopedKey[_]): Task[T] =
|
||||
if(isDummy(tk)) tk else Task(tk.info.set(Keys.taskDefinitionKey, key), tk.work)
|
||||
|
||||
def structureIndex(settings: Settings[Scope]): StructureIndex =
|
||||
new StructureIndex(Index.stringToKeyMap(settings), Index.taskToKeyMap(settings), KeyIndex(settings.allKeys( (s,k) => ScopedKey(s,k))))
|
||||
|
||||
// Reevaluates settings after modifying them. Does not recompile or reload any build components.
|
||||
def reapply(newSettings: Seq[Setting[_]], structure: BuildStructure): BuildStructure =
|
||||
{
|
||||
val newData = Project.makeSettings(newSettings, structure.delegates, structure.scopeLocal)
|
||||
val newIndex = structureIndex(newData)
|
||||
val newStreams = mkStreams(structure.units, structure.root, newData)
|
||||
new BuildStructure(units = structure.units, root = structure.root, settings = newSettings, data = newData, index = newIndex, streams = newStreams, delegates = structure.delegates, scopeLocal = structure.scopeLocal)
|
||||
}
|
||||
|
||||
def isProjectThis(s: Setting[_]) = s.key.scope.project match { case This | Select(ThisProject) => true; case _ => false }
|
||||
def buildConfigurations(loaded: LoadedBuild, rootProject: URI => String, rootEval: () => Eval): Seq[Setting[_]] =
|
||||
loaded.units.toSeq flatMap { case (uri, build) =>
|
||||
val eval = if(uri == loaded.root) rootEval else lazyEval(build.unit)
|
||||
val pluginSettings = build.unit.plugins.plugins
|
||||
val (pluginThisProject, pluginGlobal) = pluginSettings partition isProjectThis
|
||||
val projectSettings = build.defined flatMap { case (id, project) =>
|
||||
val srcs = configurationSources(project.base)
|
||||
val ref = ProjectRef(uri, id)
|
||||
val defineConfig = for(c <- project.configurations) yield ( (configuration in (ref, ConfigKey(c.name))) :== c)
|
||||
val settings =
|
||||
(thisProject :== project) +:
|
||||
(thisProjectRef :== ref) +:
|
||||
(defineConfig ++ project.settings ++ pluginThisProject ++ configurations(srcs, eval, build.imports))
|
||||
|
||||
// map This to thisScope, Select(p) to mapRef(uri, rootProject, p)
|
||||
transformSettings(projectScope(ref), uri, rootProject, settings)
|
||||
}
|
||||
val buildScope = Scope(Select(BuildRef(uri)), Global, Global, Global)
|
||||
val buildBase = baseDirectory :== build.localBase
|
||||
pluginGlobal ++ inScope(buildScope)(buildBase +: build.buildSettings) ++ projectSettings
|
||||
}
|
||||
def transformSettings(thisScope: Scope, uri: URI, rootProject: URI => String, settings: Seq[Setting[_]]): Seq[Setting[_]] =
|
||||
Project.transform(Scope.resolveScope(thisScope, uri, rootProject), settings)
|
||||
def projectScope(project: Reference): Scope = Scope(Select(project), Global, Global, Global)
|
||||
|
||||
|
||||
def lazyEval(unit: BuildUnit): () => Eval =
|
||||
{
|
||||
lazy val eval = mkEval(unit)
|
||||
() => eval
|
||||
}
|
||||
def mkEval(unit: BuildUnit): Eval = mkEval(unit.definitions, unit.plugins, Nil)
|
||||
def mkEval(defs: LoadedDefinitions, plugs: LoadedPlugins, options: Seq[String]): Eval =
|
||||
new Eval(options, defs.target +: plugs.classpath, s => new ConsoleReporter(s), defs.loader, Some(evalOutputDirectory(defs.base)))
|
||||
|
||||
def configurations(srcs: Seq[File], eval: () => Eval, imports: Seq[String]): Seq[Setting[_]] =
|
||||
if(srcs.isEmpty) Nil else EvaluateConfigurations(eval(), srcs, imports)
|
||||
|
||||
def load(file: File, s: State, config: LoadBuildConfiguration): PartBuild =
|
||||
load(file, uri => loadUnit(uri, RetrieveUnit(config.stagingDirectory, uri), s, config) )
|
||||
def load(file: File, loader: URI => BuildUnit): PartBuild = loadURI(IO.directoryURI(file), loader)
|
||||
def loadURI(uri: URI, loader: URI => BuildUnit): PartBuild =
|
||||
{
|
||||
IO.assertAbsolute(uri)
|
||||
val (referenced, map) = loadAll(uri :: Nil, Map.empty, loader, Map.empty)
|
||||
checkAll(referenced, map)
|
||||
new PartBuild(uri, map)
|
||||
}
|
||||
def loaded(unit: BuildUnit): (PartBuildUnit, List[ProjectReference]) =
|
||||
{
|
||||
val defined = projects(unit)
|
||||
if(defined.isEmpty) error("No projects defined in build unit " + unit)
|
||||
|
||||
// since base directories are resolved at this point (after 'projects'),
|
||||
// we can compare Files instead of converting to URIs
|
||||
def isRoot(p: Project) = p.base == unit.localBase
|
||||
|
||||
val externals = referenced(defined).toList
|
||||
val projectsInRoot = defined.filter(isRoot).map(_.id)
|
||||
val rootProjects = if(projectsInRoot.isEmpty) defined.head.id :: Nil else projectsInRoot
|
||||
(new PartBuildUnit(unit, defined.map(d => (d.id, d)).toMap, rootProjects, buildSettings(unit)), externals)
|
||||
}
|
||||
def buildSettings(unit: BuildUnit): Seq[Setting[_]] =
|
||||
{
|
||||
val buildScope = GlobalScope.copy(project = Select(BuildRef(unit.uri)))
|
||||
val resolve = Scope.resolveBuildScope(buildScope, unit.uri)
|
||||
Project.transform(resolve, unit.definitions.builds.flatMap(_.settings))
|
||||
}
|
||||
|
||||
@tailrec def loadAll(bases: List[URI], references: Map[URI, List[ProjectReference]], externalLoader: URI => BuildUnit, builds: Map[URI, PartBuildUnit]): (Map[URI, List[ProjectReference]], Map[URI, PartBuildUnit]) =
|
||||
bases match
|
||||
{
|
||||
case b :: bs =>
|
||||
if(builds contains b)
|
||||
loadAll(bs, references, externalLoader, builds)
|
||||
else
|
||||
{
|
||||
val (loadedBuild, refs) = loaded(externalLoader(b))
|
||||
checkBuildBase(loadedBuild.unit.localBase)
|
||||
loadAll(refs.flatMap(Reference.uri) reverse_::: bs, references.updated(b, refs), externalLoader, builds.updated(b, loadedBuild))
|
||||
}
|
||||
case Nil => (references, builds)
|
||||
}
|
||||
def checkProjectBase(buildBase: File, projectBase: File)
|
||||
{
|
||||
checkDirectory(projectBase)
|
||||
assert(buildBase == projectBase || IO.relativize(buildBase, projectBase).isDefined, "Directory " + projectBase + " is not contained in build root " + buildBase)
|
||||
}
|
||||
def checkBuildBase(base: File) = checkDirectory(base)
|
||||
def checkDirectory(base: File)
|
||||
{
|
||||
assert(base.isAbsolute, "Not absolute: " + base)
|
||||
if(base.isFile)
|
||||
error("Not a directory: " + base)
|
||||
else if(!base.exists)
|
||||
IO createDirectory base
|
||||
}
|
||||
def resolveAll(builds: Map[URI, PartBuildUnit]): Map[URI, LoadedBuildUnit] =
|
||||
{
|
||||
val rootProject = getRootProject(builds)
|
||||
builds map { case (uri,unit) =>
|
||||
(uri, unit.resolveRefs( ref => Scope.resolveProjectRef(uri, rootProject, ref) ))
|
||||
} toMap;
|
||||
}
|
||||
def checkAll(referenced: Map[URI, List[ProjectReference]], builds: Map[URI, PartBuildUnit])
|
||||
{
|
||||
val rootProject = getRootProject(builds)
|
||||
for( (uri, refs) <- referenced; ref <- refs)
|
||||
{
|
||||
val ProjectRef(refURI, refID) = Scope.resolveProjectRef(uri, rootProject, ref)
|
||||
val loadedUnit = builds(refURI)
|
||||
if(! (loadedUnit.defined contains refID) )
|
||||
error("No project '" + refID + "' in '" + refURI + "'")
|
||||
}
|
||||
}
|
||||
|
||||
def resolveBase(against: File): Project => Project =
|
||||
{
|
||||
def resolve(f: File) =
|
||||
{
|
||||
val fResolved = new File(IO.directoryURI(IO.resolve(against, f)))
|
||||
checkProjectBase(against, fResolved)
|
||||
fResolved
|
||||
}
|
||||
p => p.copy(base = resolve(p.base))
|
||||
}
|
||||
def resolveProjects(loaded: PartBuild): LoadedBuild =
|
||||
{
|
||||
val rootProject = getRootProject(loaded.units)
|
||||
new LoadedBuild(loaded.root, loaded.units map { case (uri, unit) =>
|
||||
IO.assertAbsolute(uri)
|
||||
(uri, resolveProjects(uri, unit, rootProject))
|
||||
})
|
||||
}
|
||||
def resolveProjects(uri: URI, unit: PartBuildUnit, rootProject: URI => String): LoadedBuildUnit =
|
||||
{
|
||||
IO.assertAbsolute(uri)
|
||||
val resolve = (_: Project).resolve(ref => Scope.resolveProjectRef(uri, rootProject, ref))
|
||||
new LoadedBuildUnit(unit.unit, unit.defined mapValues resolve, unit.rootProjects, unit.buildSettings)
|
||||
}
|
||||
def projects(unit: BuildUnit): Seq[Project] =
|
||||
{
|
||||
// we don't have the complete build graph loaded, so we don't have the rootProject function yet.
|
||||
// Therefore, we use resolveProjectBuild instead of resolveProjectRef. After all builds are loaded, we can fully resolve ProjectReferences.
|
||||
val resolveBuild = (_: Project).resolveBuild(ref => Scope.resolveProjectBuild(unit.uri, ref))
|
||||
val resolve = resolveBuild compose resolveBase(unit.localBase)
|
||||
unit.definitions.builds.flatMap(_.projects map resolve)
|
||||
}
|
||||
def getRootProject(map: Map[URI, BuildUnitBase]): 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): ResolvedProject =
|
||||
getBuild(map, uri).defined.getOrElse(id, noProject(uri, id))
|
||||
def getBuild[T](map: Map[URI, T], uri: URI): T =
|
||||
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(uri: URI, localBase: File, s: State, config: LoadBuildConfiguration): BuildUnit =
|
||||
{
|
||||
val normBase = localBase.getCanonicalFile
|
||||
val defDir = selectProjectDir(normBase)
|
||||
val pluginDir = pluginDirectory(defDir)
|
||||
val plugs = plugins(pluginDir, s, config)
|
||||
|
||||
val defs = definitionSources(defDir)
|
||||
val target = buildOutputDirectory(defDir, config.compilers)
|
||||
IO.createDirectory(target)
|
||||
val loadedDefs =
|
||||
if(defs.isEmpty)
|
||||
new LoadedDefinitions(defDir, target, plugs.loader, Build.default(normBase) :: Nil, Nil)
|
||||
else
|
||||
definitions(defDir, target, defs, plugs, config.compilers, config.log, normBase)
|
||||
|
||||
new BuildUnit(uri, normBase, loadedDefs, plugs)
|
||||
}
|
||||
|
||||
def plugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins = if(dir.exists) buildPlugins(dir, s, config) else noPlugins(dir, config)
|
||||
def noPlugins(dir: File, config: LoadBuildConfiguration): LoadedPlugins = loadPluginDefinition(dir, config, config.commonPluginClasspath)
|
||||
def buildPlugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins =
|
||||
loadPluginDefinition(dir, config, buildPluginDefinition(dir, s, config))
|
||||
|
||||
def loadPluginDefinition(dir: File, config: LoadBuildConfiguration, pluginClasspath: Seq[Attributed[File]]): LoadedPlugins =
|
||||
{
|
||||
val definitionClasspath = if(pluginClasspath.isEmpty) config.classpath else (data(pluginClasspath) ++ config.classpath).distinct
|
||||
val pluginLoader = if(pluginClasspath.isEmpty) config.loader else ClasspathUtilities.toLoader(definitionClasspath, config.loader)
|
||||
loadPlugins(dir, definitionClasspath, pluginLoader, analyzed(pluginClasspath))
|
||||
}
|
||||
def buildPluginDefinition(dir: File, s: State, config: LoadBuildConfiguration): Seq[Attributed[File]] =
|
||||
{
|
||||
val (eval,pluginDef) = apply(dir, s, config)
|
||||
val pluginState = Project.setProject(Load.initialSession(pluginDef, eval), pluginDef, s)
|
||||
val thisPluginClasspath = config.evalPluginDef(pluginDef, pluginState)
|
||||
(thisPluginClasspath ++ config.commonPluginClasspath).distinct
|
||||
}
|
||||
|
||||
def definitions(base: File, targetBase: File, srcs: Seq[File], plugins: LoadedPlugins, compilers: Compilers, log: Logger, buildBase: File): LoadedDefinitions =
|
||||
{
|
||||
val (inputs, defAnalysis) = build(plugins.classpath, srcs, targetBase, compilers, log)
|
||||
val target = inputs.config.classesDirectory
|
||||
val definitionLoader = ClasspathUtilities.toLoader(target :: Nil, plugins.loader)
|
||||
val defNames = findDefinitions(defAnalysis)
|
||||
val defs = if(defNames.isEmpty) Build.default(buildBase) :: Nil else loadDefinitions(definitionLoader, defNames)
|
||||
new LoadedDefinitions(base, target, definitionLoader, defs, defNames)
|
||||
}
|
||||
|
||||
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], target: File, compilers: Compilers, log: Logger): (Inputs, inc.Analysis) =
|
||||
{
|
||||
val inputs = Compiler.inputs(classpath, sources, target, Nil, Nil, Compiler.DefaultMaxErrors, CompileOrder.Mixed)(compilers, log)
|
||||
val analysis =
|
||||
try { Compiler(inputs, log) }
|
||||
catch { case _: xsbti.CompileFailed => throw new NoMessageException } // compiler already logged errors
|
||||
(inputs, analysis)
|
||||
}
|
||||
|
||||
def loadPlugins(dir: File, classpath: Seq[File], loader: ClassLoader, analysis: Seq[inc.Analysis]): LoadedPlugins =
|
||||
{
|
||||
val (pluginNames, plugins) = if(classpath.isEmpty) (Nil, Nil) else {
|
||||
val names = ( binaryPlugins(loader) ++ (analysis flatMap findPlugins) ).distinct
|
||||
(names, loadPlugins(loader, names) )
|
||||
}
|
||||
new LoadedPlugins(dir, classpath, loader, plugins, pluginNames)
|
||||
}
|
||||
def binaryPlugins(loader: ClassLoader): Seq[String] =
|
||||
{
|
||||
import collection.JavaConversions._
|
||||
loader.getResources("sbt/sbt.plugins").toSeq flatMap { u => IO.readLinesURL(u) map { _.trim } filter { !_.isEmpty } };
|
||||
}
|
||||
|
||||
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 importAll(values: Seq[String]) = if(values.isEmpty) Nil else values.map( _ + "._" ).mkString("import ", ", ", "") :: Nil
|
||||
|
||||
def findPlugins(analysis: inc.Analysis): Seq[String] = discover(analysis, "sbt.Plugin")
|
||||
def findDefinitions(analysis: inc.Analysis): Seq[String] = discover(analysis, "sbt.Build")
|
||||
def discover(analysis: inc.Analysis, subclasses: String*): Seq[String] =
|
||||
{
|
||||
val subclassSet = subclasses.toSet
|
||||
val ds = Discovery(subclassSet, Set.empty)(Tests.allDefs(analysis))
|
||||
ds.flatMap {
|
||||
case (definition, Discovered(subs,_,_,true)) =>
|
||||
if((subs & subclassSet).isEmpty) Nil else definition.name :: Nil
|
||||
case _ => Nil
|
||||
}
|
||||
}
|
||||
|
||||
def initialSession(structure: BuildStructure, rootEval: () => Eval): SessionSettings =
|
||||
new SessionSettings(structure.root, rootProjectMap(structure.units), structure.settings, Map.empty, Map.empty, rootEval)
|
||||
|
||||
def rootProjectMap(units: Map[URI, LoadedBuildUnit]): Map[URI, String] =
|
||||
{
|
||||
val getRoot = getRootProject(units)
|
||||
units.keys.map(uri => (uri, getRoot(uri))).toMap
|
||||
}
|
||||
|
||||
def baseImports = "import sbt._, Process._, Keys._" :: Nil
|
||||
|
||||
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], val buildNames: Seq[String])
|
||||
final class LoadedPlugins(val base: File, val classpath: Seq[File], val loader: ClassLoader, val plugins: Seq[Setting[_]], val pluginNames: Seq[String])
|
||||
final class BuildUnit(val uri: URI, val localBase: File, val definitions: LoadedDefinitions, val plugins: LoadedPlugins)
|
||||
{
|
||||
override def toString = if(uri.getScheme == "file") localBase.toString else (uri + " (locally: " + localBase +")")
|
||||
}
|
||||
|
||||
final class LoadedBuild(val root: URI, val units: Map[URI, LoadedBuildUnit])
|
||||
final class PartBuild(val root: URI, val units: Map[URI, PartBuildUnit])
|
||||
sealed trait BuildUnitBase { def rootProjects: Seq[String]; def buildSettings: Seq[Setting[_]] }
|
||||
final class PartBuildUnit(val unit: BuildUnit, val defined: Map[String, Project], val rootProjects: Seq[String], val buildSettings: Seq[Setting[_]]) extends BuildUnitBase
|
||||
{
|
||||
def resolve(f: Project => ResolvedProject): LoadedBuildUnit = new LoadedBuildUnit(unit, defined mapValues f, rootProjects, buildSettings)
|
||||
def resolveRefs(f: ProjectReference => ProjectRef): LoadedBuildUnit = resolve(_ resolve f)
|
||||
}
|
||||
final class LoadedBuildUnit(val unit: BuildUnit, val defined: Map[String, ResolvedProject], val rootProjects: Seq[String], val buildSettings: Seq[Setting[_]]) extends BuildUnitBase
|
||||
{
|
||||
assert(!rootProjects.isEmpty, "No root projects defined for build unit " + unit)
|
||||
def localBase = unit.localBase
|
||||
def classpath = unit.definitions.target +: unit.plugins.classpath
|
||||
def loader = unit.definitions.loader
|
||||
def imports = getImports(unit)
|
||||
override def toString = unit.toString
|
||||
}
|
||||
def getImports(unit: BuildUnit) = baseImports ++ importAll(unit.plugins.pluginNames ++ unit.definitions.buildNames)
|
||||
|
||||
def referenced[PR <: ProjectReference](definitions: Seq[ProjectDefinition[PR]]): Seq[PR] = definitions flatMap { _.referenced }
|
||||
|
||||
final class BuildStructure(val units: Map[URI, LoadedBuildUnit], val root: URI, val settings: Seq[Setting[_]], val data: Settings[Scope], val index: StructureIndex, val streams: Streams, val delegates: Scope => Seq[Scope], val scopeLocal: ScopeLocal)
|
||||
final case class LoadBuildConfiguration(stagingDirectory: File, commonPluginClasspath: Seq[Attributed[File]], classpath: Seq[File], loader: ClassLoader, compilers: Compilers, evalPluginDef: (BuildStructure, State) => Seq[Attributed[File]], delegates: LoadedBuild => Scope => Seq[Scope], scopeLocal: ScopeLocal, injectSettings: Seq[Setting[_]], 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[_]]], val keyIndex: KeyIndex)
|
||||
|
||||
private[this] def memo[A,B](implicit f: A => B): A => B =
|
||||
{
|
||||
val dcache = new mutable.HashMap[A,B]
|
||||
(a: A) => dcache.getOrElseUpdate(a, f(a))
|
||||
}
|
||||
}
|
||||
|
|
@ -3,8 +3,8 @@
|
|||
*/
|
||||
package sbt
|
||||
|
||||
import java.io.File
|
||||
import compiler.AnalyzingCompiler
|
||||
import java.io.File
|
||||
import compiler.AnalyzingCompiler
|
||||
|
||||
final class Console(compiler: AnalyzingCompiler)
|
||||
{
|
||||
|
|
@ -27,22 +27,8 @@ final class Console(compiler: AnalyzingCompiler)
|
|||
object Console
|
||||
{
|
||||
def apply(conf: Compiler.Inputs): Console = new Console( conf.compilers.scalac )
|
||||
|
||||
def sbt(state: State, extra: String)(implicit log: Logger)
|
||||
{
|
||||
val extracted = Project extract state
|
||||
val bindings = ("currentState" -> state) :: ("extracted" -> extracted ) :: Nil
|
||||
val unit = extracted.currentUnit
|
||||
val compiler = Compiler.compilers(state.configuration, log).scalac
|
||||
val imports = Load.getImports(unit.unit) ++ Load.importAll(bindings.map(_._1))
|
||||
val importString = imports.mkString("", ";\n", ";\n\n")
|
||||
val initCommands = importString + extra
|
||||
val loader = classOf[State].getClassLoader
|
||||
(new Console(compiler))(unit.classpath, Nil, initCommands)(Some(loader), bindings)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final class Scaladoc(maximumErrors: Int, compiler: AnalyzingCompiler)
|
||||
{
|
||||
final def apply(label: String, sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String])(implicit log: Logger)
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
[scala]
|
||||
version: 2.8.0.RC6
|
||||
|
||||
[app]
|
||||
org: org.scala-tools.sbt
|
||||
name: alternate-compiler-test
|
||||
version: 0.9.0-SNAPSHOT
|
||||
class: sbt.AggressiveCompiler
|
||||
components: xsbti
|
||||
cross-versioned: true
|
||||
|
||||
[repositories]
|
||||
local
|
||||
maven-local
|
||||
scala-tools-releases
|
||||
scala-tools-snapshots
|
||||
|
||||
[boot]
|
||||
directory: project/boot
|
||||
|
|
@ -80,20 +80,21 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
|
|||
|
||||
// Standard task system. This provides map, flatMap, join, and more on top of the basic task model.
|
||||
val stdTaskSub = testedBase(tasksPath / "standard", "Task System", taskSub, collectionSub, logSub, ioSub, processSub)
|
||||
// The main integration project for sbt. It brings all of the subsystems together, configures them, and provides for overriding conventions.
|
||||
val mainSub = baseProject("main", "Main",
|
||||
// Implementation and support code for defining actions.
|
||||
val actionsSub = baseProject(mainPath / "actions", "Actions",
|
||||
classfileSub, classpathSub, compileIncrementalSub, compilePersistSub, compilerSub, completeSub, discoverySub,
|
||||
interfaceSub, ioSub, ivySub, launchInterfaceSub, logSub, processSub, taskSub, stdTaskSub, runSub, trackingSub, testingSub)
|
||||
interfaceSub, ioSub, ivySub, logSub, processSub, runSub, stdTaskSub, taskSub, trackingSub, testingSub)
|
||||
|
||||
// The main integration project for sbt. It brings all of the subsystems together, configures them, and provides for overriding conventions.
|
||||
val mainSub = baseProject(mainPath, "Main", actionsSub, interfaceSub, ioSub, ivySub, launchInterfaceSub, logSub, processSub, runSub)
|
||||
// Strictly for bringing implicits and aliases from subsystems into the top-level sbt namespace through a single package object
|
||||
val sbtSub = project(sbtPath, "Simple Build Tool", new Sbt(_), mainSub) // technically, we need a dependency on all of mainSub's dependencies, but we don't do that since this is strictly an integration project
|
||||
// technically, we need a dependency on all of mainSub's dependencies, but we don't do that since this is strictly an integration project
|
||||
// with the sole purpose of providing certain identifiers without qualification (with a package object)
|
||||
val sbtSub = project(sbtPath, "Simple Build Tool", new Sbt(_), mainSub)
|
||||
|
||||
/** following modules are not updated for 2.8 or 0.9 */
|
||||
/*
|
||||
val sbtSub = project(sbtPath, "Simple Build Tool", new SbtProject(_) {},
|
||||
compilerSub, launchInterfaceSub, testingSub, cacheSub, taskSub)
|
||||
|
||||
val installerSub = project(sbtPath / "install", "Installer", new InstallerProject(_) {}, sbtSub)
|
||||
|
||||
lazy val dist = task { None } dependsOn(launchSub.proguard, sbtSub.publishLocal, installerSub.publishLocal)*/
|
||||
|
||||
def baseProject(path: Path, name: String, deps: Project*) = project(path, name, new Base(_), deps : _*)
|
||||
|
|
@ -106,6 +107,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
|
|||
def launchPath = path("launch")
|
||||
def utilPath = path("util")
|
||||
def compilePath = path("compile")
|
||||
def mainPath = path("main")
|
||||
|
||||
def compilerInterfaceClasspath = compileInterfaceSub.projectClasspath(Configurations.Test)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue