2011-01-19 00:24:11 +01:00
|
|
|
/* sbt -- Simple Build Tool
|
|
|
|
|
* Copyright 2011 Mark Harrah
|
|
|
|
|
*/
|
|
|
|
|
package sbt
|
|
|
|
|
|
|
|
|
|
import java.io.File
|
|
|
|
|
import java.net.URI
|
2011-03-02 12:46:28 +01:00
|
|
|
import compiler.{Discovered,Discovery,Eval,EvalImports}
|
2011-01-19 00:24:11 +01:00
|
|
|
import classpath.ClasspathUtilities
|
|
|
|
|
import scala.annotation.tailrec
|
2011-01-26 04:14:02 +01:00
|
|
|
import collection.mutable
|
2011-03-05 14:25:17 +01:00
|
|
|
import Compiler.{Compilers,Inputs}
|
2011-02-27 22:28:00 +01:00
|
|
|
import Project.{inScope, ScopedKey, ScopeLocal, Setting}
|
2011-03-21 03:54:01 +01:00
|
|
|
import Keys.{appConfiguration, baseDirectory, configuration, streams, thisProject, thisProjectRef}
|
2011-01-19 00:24:11 +01:00
|
|
|
import TypeFunctions.{Endo,Id}
|
|
|
|
|
import tools.nsc.reporters.ConsoleReporter
|
2011-02-25 05:30:06 +01:00
|
|
|
import Build.{analyzed, data}
|
2011-02-27 22:28:00 +01:00
|
|
|
import Scope.{GlobalScope, ThisScope}
|
2011-01-19 00:24:11 +01:00
|
|
|
|
|
|
|
|
// name is more like BuildDefinition, but that is too long
|
|
|
|
|
trait Build
|
|
|
|
|
{
|
|
|
|
|
def projects: Seq[Project]
|
2011-03-05 14:25:17 +01:00
|
|
|
def settings: Seq[Setting[_]] = Defaults.buildCore
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
|
|
|
|
trait Plugin
|
|
|
|
|
{
|
2011-03-14 02:33:28 +01:00
|
|
|
def settings: Seq[Project.Setting[_]] = Nil
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
2011-02-25 05:30:06 +01:00
|
|
|
|
|
|
|
|
def data[T](in: Seq[Attributed[T]]): Seq[T] = in.map(_.data)
|
2011-03-02 12:46:28 +01:00
|
|
|
def analyzed(in: Seq[Attributed[_]]): Seq[inc.Analysis] = in.flatMap{ _.metadata.get(Keys.analysis) }
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
{
|
2011-01-27 01:49:54 +01:00
|
|
|
def apply(eval: Eval, srcs: Seq[File], imports: Seq[String]): Seq[Setting[_]] =
|
|
|
|
|
srcs flatMap { src => evaluateConfiguration(eval, src, imports) }
|
|
|
|
|
def evaluateConfiguration(eval: Eval, src: File, imports: Seq[String]): Seq[Setting[_]] =
|
|
|
|
|
evaluateConfiguration(eval, src.getPath, IO.readLines(src), imports)
|
|
|
|
|
def evaluateConfiguration(eval: Eval, name: String, lines: Seq[String], imports: Seq[String]): Seq[Setting[_]] =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
|
|
|
|
val (importExpressions, settingExpressions) = splitExpressions(name, lines)
|
2011-01-27 01:49:54 +01:00
|
|
|
for((settingExpression,line) <- settingExpressions) yield
|
2011-02-03 01:25:18 +01:00
|
|
|
evaluateSetting(eval, name, (imports.map(s => (s, -1)) ++ importExpressions), settingExpression, line)
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
|
|
|
|
|
2011-01-27 01:49:54 +01:00
|
|
|
def evaluateSetting(eval: Eval, name: String, imports: Seq[(String,Int)], expression: String, line: Int): Setting[_] =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
2011-01-27 01:49:54 +01:00
|
|
|
val result = eval.eval(expression, imports = new EvalImports(imports, name), srcName = name, tpeName = Some("sbt.Project.Setting[_]"), line = line)
|
|
|
|
|
result.value.asInstanceOf[Setting[_]]
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
2011-01-27 01:49:54 +01:00
|
|
|
private[this] def fstS(f: String => Boolean): ((String,Int)) => Boolean = { case (s,i) => f(s) }
|
|
|
|
|
def splitExpressions(name: String, lines: Seq[String]): (Seq[(String,Int)], Seq[(String,Int)]) =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
|
|
|
|
val blank = (_: String).trim.isEmpty
|
2011-01-27 01:49:54 +01:00
|
|
|
val importOrBlank = fstS(t => blank(t) || (t.trim startsWith "import "))
|
2011-01-19 00:24:11 +01:00
|
|
|
|
2011-01-27 01:49:54 +01:00
|
|
|
val (imports, settings) = lines.zipWithIndex span importOrBlank
|
|
|
|
|
(imports filterNot fstS(blank), groupedLines(settings, blank))
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
2011-01-27 01:49:54 +01:00
|
|
|
def groupedLines(lines: Seq[(String,Int)], delimiter: String => Boolean): Seq[(String,Int)] =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
2011-01-27 01:49:54 +01:00
|
|
|
val fdelim = fstS(delimiter)
|
|
|
|
|
@tailrec def group0(lines: Seq[(String,Int)], accum: Seq[(String,Int)]): Seq[(String,Int)] =
|
2011-01-25 00:08:43 +01:00
|
|
|
if(lines.isEmpty) accum.reverse
|
|
|
|
|
else
|
|
|
|
|
{
|
2011-01-27 01:49:54 +01:00
|
|
|
val start = lines dropWhile fstS(delimiter)
|
|
|
|
|
val (next, tail) = start.span { case (s,_) => !delimiter(s) }
|
|
|
|
|
val grouped = if(next.isEmpty) accum else (next.map(_._1).mkString("\n"), next.head._2) +: accum
|
|
|
|
|
group0(tail, grouped)
|
2011-01-25 00:08:43 +01:00
|
|
|
}
|
2011-01-27 01:49:54 +01:00
|
|
|
group0(lines, Nil)
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
object EvaluateTask
|
|
|
|
|
{
|
|
|
|
|
import Load.BuildStructure
|
|
|
|
|
import Project.display
|
|
|
|
|
import std.{TaskExtra,Transform}
|
|
|
|
|
import TaskExtra._
|
2011-01-21 23:22:18 +01:00
|
|
|
import BuildStreams.{Streams, TaskStreams}
|
2011-01-19 00:24:11 +01:00
|
|
|
|
|
|
|
|
val SystemProcessors = Runtime.getRuntime.availableProcessors
|
2011-03-21 03:54:01 +01:00
|
|
|
|
|
|
|
|
val isDummyTask = AttributeKey[Boolean]("is-dummy-task")
|
|
|
|
|
val taskDefinitionKey = AttributeKey[ScopedKey[_]]("task-definition-key")
|
2011-01-19 00:24:11 +01:00
|
|
|
val (state, dummyState) = dummy[State]("state")
|
2011-02-06 03:39:34 +01:00
|
|
|
val (streamsManager, dummyStreamsManager) = dummy[Streams]("streams-manager")
|
2011-02-12 02:22:17 +01:00
|
|
|
val resolvedScoped = SettingKey[ScopedKey[_]]("resolved-scoped")
|
2011-02-21 16:07:39 +01:00
|
|
|
private[sbt] val parseResult: TaskKey[_] = TaskKey("$parse-result")
|
2011-01-19 00:24:11 +01:00
|
|
|
|
2011-01-31 05:19:28 +01:00
|
|
|
def injectSettings: Seq[Project.Setting[_]] = Seq(
|
2011-02-27 22:28:00 +01:00
|
|
|
(state in GlobalScope) ::= dummyState,
|
|
|
|
|
(streamsManager in GlobalScope) ::= dummyStreamsManager
|
2011-01-19 00:24:11 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def dummy[T](name: String): (TaskKey[T], Task[T]) = (TaskKey[T](name), dummyTask(name))
|
2011-03-21 03:54:01 +01:00
|
|
|
def dummyTask[T](name: String): Task[T] =
|
|
|
|
|
{
|
|
|
|
|
val base: Task[T] = task( error("Dummy task '" + name + "' did not get converted to a full task.") ) named name
|
|
|
|
|
base.copy(info = base.info.set(isDummyTask, true))
|
|
|
|
|
}
|
|
|
|
|
def isDummy(t: Task[_]): Boolean = t.info.attributes.get(isDummyTask) getOrElse false
|
2011-01-19 00:24:11 +01:00
|
|
|
|
2011-02-25 05:30:06 +01:00
|
|
|
def evalPluginDef(log: Logger)(pluginDef: BuildStructure, state: State): Seq[Attributed[File]] =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
2011-02-05 04:02:39 +01:00
|
|
|
val root = ProjectRef(pluginDef.root, Load.getRootProject(pluginDef.units)(pluginDef.root))
|
2011-03-02 12:46:28 +01:00
|
|
|
val pluginKey = Keys.fullClasspath in Configurations.Compile
|
2011-02-25 05:30:06 +01:00
|
|
|
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)
|
2011-01-19 00:24:11 +01:00
|
|
|
processResult(result, log)
|
|
|
|
|
}
|
2011-03-02 12:46:28 +01:00
|
|
|
def evaluateTask[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, ref: ProjectRef, checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors): Option[Result[T]] =
|
2011-02-06 03:39:34 +01:00
|
|
|
withStreams(structure) { str =>
|
2011-03-02 12:46:28 +01:00
|
|
|
for( (task, toNode) <- getTask(structure, taskKey, state, str, ref) ) yield
|
2011-03-21 03:54:01 +01:00
|
|
|
runTask(task, str, checkCycles, maxWorkers)(toNode)
|
2011-02-06 03:39:34 +01:00
|
|
|
}
|
2011-03-21 03:54:01 +01:00
|
|
|
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 = for(Incomplete(Some(key: Project.ScopedKey[_]), _, msg, _, ex) <- Incomplete linearize result) yield (key, msg, ex)
|
|
|
|
|
for( (key, _, Some(ex)) <- all)
|
|
|
|
|
getStreams(key, streams).log.trace(ex)
|
|
|
|
|
log.error("Incomplete task(s):")
|
|
|
|
|
for( (key, msg, ex) <- all if(msg.isDefined || ex.isDefined) )
|
|
|
|
|
getStreams(key, streams).log.error(" " + Project.display(key) + ": " + (msg.toList ++ ex.toList).mkString("\n\t"))
|
|
|
|
|
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))
|
2011-02-06 03:39:34 +01:00
|
|
|
def withStreams[T](structure: BuildStructure)(f: Streams => T): T =
|
|
|
|
|
{
|
|
|
|
|
val str = std.Streams.closeable(structure.streams)
|
|
|
|
|
try { f(str) } finally { str.close() }
|
|
|
|
|
}
|
2011-01-19 00:24:11 +01:00
|
|
|
|
2011-03-02 12:46:28 +01:00
|
|
|
def getTask[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, streams: Streams, ref: ProjectRef): Option[(Task[T], Execute.NodeView[Task])] =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
2011-03-03 12:44:19 +01:00
|
|
|
val thisScope = Load.projectScope(ref)
|
2011-01-19 00:24:11 +01:00
|
|
|
val resolvedScope = Scope.replaceThis(thisScope)( taskKey.scope )
|
|
|
|
|
for( t <- structure.data.get(resolvedScope, taskKey.key)) yield
|
2011-02-06 03:39:34 +01:00
|
|
|
(t, nodeView(state, streams))
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
2011-02-21 16:07:39 +01:00
|
|
|
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))
|
2011-01-19 00:24:11 +01:00
|
|
|
|
2011-03-21 03:54:01 +01:00
|
|
|
def runTask[Task[_] <: AnyRef, T](root: Task[T], streams: Streams, checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors)(implicit taskToNode: Execute.NodeView[Task]): Result[T] =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
|
|
|
|
val (service, shutdown) = CompletionService[Task[_], Completed](maxWorkers)
|
|
|
|
|
|
|
|
|
|
val x = new Execute[Task](checkCycles)(taskToNode)
|
2011-03-21 03:54:01 +01:00
|
|
|
val result = try { x.run(root)(service) } finally { shutdown() }
|
|
|
|
|
val replaced = transformInc(result)
|
|
|
|
|
logIncResult(replaced, streams)
|
|
|
|
|
replaced
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
2011-03-21 03:54:01 +01:00
|
|
|
def transformInc[T](result: Result[T]): Result[T] =
|
|
|
|
|
result.toEither.left.map { i =>
|
|
|
|
|
Incomplete.transform(i) {
|
|
|
|
|
case in @ Incomplete(Some(node: Task[_]), _, _, _, _) => in.copy(node = transformNode(node))
|
|
|
|
|
case _ => i
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
def transformNode(node: Task[_]): Option[ScopedKey[_]] =
|
|
|
|
|
node.info.attributes get taskDefinitionKey
|
2011-01-19 00:24:11 +01:00
|
|
|
|
2011-02-10 14:13:48 +01:00
|
|
|
def processResult[T](result: Result[T], log: Logger, show: Boolean = false): T =
|
2011-02-21 16:07:39 +01:00
|
|
|
onResult(result, log) { v => if(show) println("Result: " + v); v }
|
|
|
|
|
def onResult[T, S](result: Result[T], log: Logger)(f: T => S): S =
|
2011-01-19 00:24:11 +01:00
|
|
|
result match
|
|
|
|
|
{
|
2011-02-21 16:07:39 +01:00
|
|
|
case Value(v) => f(v)
|
2011-03-21 03:54:01 +01:00
|
|
|
case Inc(inc) => throw inc
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
2011-02-06 03:39:34 +01:00
|
|
|
|
2011-02-12 02:22:17 +01:00
|
|
|
// if the return type Seq[Setting[_]] is not explicitly given, scalac hangs
|
|
|
|
|
val injectStreams: ScopedKey[_] => Seq[Setting[_]] = scoped =>
|
2011-02-06 03:39:34 +01:00
|
|
|
if(scoped.key == streams.key)
|
2011-02-12 02:22:17 +01:00
|
|
|
Seq(streams in scoped.scope <<= streamsManager map { mgr =>
|
|
|
|
|
val stream = mgr(scoped)
|
2011-02-06 03:39:34 +01:00
|
|
|
stream.open()
|
|
|
|
|
stream
|
|
|
|
|
})
|
2011-02-12 02:22:17 +01:00
|
|
|
else if(scoped.key == resolvedScoped.key)
|
|
|
|
|
Seq(resolvedScoped in scoped.scope :== scoped)
|
|
|
|
|
else if(scoped.key == parseResult.key)
|
2011-02-21 16:07:39 +01:00
|
|
|
Seq(parseResult in scoped.scope := error("Unsubstituted parse result for " + Project.display(scoped)) )
|
2011-02-12 02:22:17 +01:00
|
|
|
else
|
|
|
|
|
Nil
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
|
|
|
|
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[_]] =
|
|
|
|
|
{
|
2011-03-01 14:44:41 +01:00
|
|
|
val multiMap = settings.data.values.flatMap(_.keys).toList.distinct.groupBy(_.label)
|
2011-01-27 01:49:54 +01:00
|
|
|
val duplicates = multiMap collect { case (k, x1 :: x2 :: _) => k }
|
2011-01-19 00:24:11 +01:00
|
|
|
if(duplicates.isEmpty)
|
|
|
|
|
multiMap.mapValues(_.head)
|
|
|
|
|
else
|
|
|
|
|
error(duplicates.mkString("AttributeKey ID collisions detected for '", "', '", "'"))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
object Load
|
|
|
|
|
{
|
|
|
|
|
import BuildPaths._
|
2011-01-21 23:22:18 +01:00
|
|
|
import BuildStreams._
|
2011-01-19 00:24:11 +01:00
|
|
|
|
2011-02-05 04:02:39 +01:00
|
|
|
// note that there is State is passed in but not pulled out
|
2011-03-12 04:33:30 +01:00
|
|
|
def defaultLoad(state: State, baseDirectory: File, log: Logger): (() => Eval, BuildStructure) =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
2011-03-12 16:28:53 +01:00
|
|
|
val provider = state.configuration.provider
|
|
|
|
|
val scalaProvider = provider.scalaProvider
|
|
|
|
|
val stagingDirectory = defaultStaging.getCanonicalFile
|
2011-03-12 04:33:30 +01:00
|
|
|
val base = baseDirectory.getCanonicalFile
|
2011-01-19 00:24:11 +01:00
|
|
|
val loader = getClass.getClassLoader
|
2011-03-12 16:28:53 +01:00
|
|
|
val classpath = provider.mainClasspath ++ scalaProvider.jars
|
2011-03-05 14:25:17 +01:00
|
|
|
val compilers = Compiler.compilers(state.configuration, log)
|
2011-02-05 04:02:39 +01:00
|
|
|
val evalPluginDef = EvaluateTask.evalPluginDef(log) _
|
2011-01-26 04:14:02 +01:00
|
|
|
val delegates = memo(defaultDelegates)
|
2011-03-02 12:46:28 +01:00
|
|
|
val inject: Seq[Project.Setting[_]] = ((appConfiguration in GlobalScope) :== state.configuration) +: EvaluateTask.injectSettings
|
2011-03-17 03:22:46 +01:00
|
|
|
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)
|
2011-02-05 04:02:39 +01:00
|
|
|
apply(base, state, config)
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
2011-03-17 03:22:46 +01:00
|
|
|
def buildGlobalPlugins(baseDirectory: File, state: State, config: LoadBuildConfiguration): Seq[Attributed[File]] =
|
|
|
|
|
if(baseDirectory.isDirectory) buildPluginDefinition(baseDirectory, state, config) else Nil
|
2011-01-19 00:24:11 +01:00
|
|
|
def defaultDelegates: LoadedBuild => Scope => Seq[Scope] = (lb: LoadedBuild) => {
|
2011-01-26 04:14:02 +01:00
|
|
|
val rootProject = getRootProject(lb.units)
|
2011-03-03 12:44:19 +01:00
|
|
|
def resolveRef(project: Reference): ResolvedReference = Scope.resolveReference(lb.root, rootProject, project)
|
2011-01-19 00:24:11 +01:00
|
|
|
Scope.delegates(
|
|
|
|
|
project => projectInherit(lb, resolveRef(project)),
|
2011-03-03 12:44:19 +01:00
|
|
|
(project, config) => configInherit(lb, resolveRef(project), config, rootProject),
|
2011-01-19 00:24:11 +01:00
|
|
|
(project, task) => Nil,
|
|
|
|
|
(project, extra) => Nil
|
|
|
|
|
)
|
|
|
|
|
}
|
2011-03-03 12:44:19 +01:00
|
|
|
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
|
2011-01-19 00:24:11 +01:00
|
|
|
|
|
|
|
|
// 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
|
2011-02-05 04:02:39 +01:00
|
|
|
def apply(rootBase: File, s: State, config: LoadBuildConfiguration): (() => Eval, BuildStructure) =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
2011-01-26 04:14:02 +01:00
|
|
|
// load, which includes some resolution, but can't fill in project IDs yet, so follow with full resolution
|
2011-02-05 04:02:39 +01:00
|
|
|
val loaded = resolveProjects(load(rootBase, s, config))
|
2011-01-19 00:24:11 +01:00
|
|
|
val projects = loaded.units
|
2011-01-25 13:24:52 +01:00
|
|
|
lazy val rootEval = lazyEval(loaded.units(loaded.root).unit)
|
2011-02-12 02:22:17 +01:00
|
|
|
val settings = finalTransforms(config.injectSettings ++ buildConfigurations(loaded, getRootProject(projects), rootEval))
|
2011-01-25 13:24:52 +01:00
|
|
|
val delegates = config.delegates(loaded)
|
2011-02-06 03:39:34 +01:00
|
|
|
val data = Project.makeSettings(settings, delegates, config.scopeLocal)
|
2011-01-19 00:24:11 +01:00
|
|
|
val index = structureIndex(data)
|
2011-01-21 23:22:18 +01:00
|
|
|
val streams = mkStreams(projects, loaded.root, data)
|
2011-02-06 03:39:34 +01:00
|
|
|
(rootEval, new BuildStructure(projects, loaded.root, settings, data, index, streams, delegates, config.scopeLocal))
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-12 02:22:17 +01:00
|
|
|
// 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[_]] =
|
|
|
|
|
{
|
2011-03-21 03:54:01 +01:00
|
|
|
import EvaluateTask.{parseResult, resolvedScoped}
|
2011-02-12 02:22:17 +01:00
|
|
|
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
|
|
|
|
|
}
|
2011-03-21 03:54:01 +01:00
|
|
|
def setDefining[T] = (key: ScopedKey[T], value: T) => value match {
|
|
|
|
|
case tk: Task[t] if !EvaluateTask.isDummy(tk) => Task(tk.info.set(EvaluateTask.taskDefinitionKey, key), tk.work).asInstanceOf[T]
|
|
|
|
|
case _ => value
|
|
|
|
|
}
|
|
|
|
|
ss.map(s => s mapReferenced mapSpecial(s.key) mapInit setDefining )
|
2011-02-12 02:22:17 +01:00
|
|
|
}
|
|
|
|
|
|
2011-01-19 00:24:11 +01:00
|
|
|
def structureIndex(settings: Settings[Scope]): StructureIndex =
|
2011-01-26 04:22:11 +01:00
|
|
|
new StructureIndex(Index.stringToKeyMap(settings), Index.taskToKeyMap(settings), KeyIndex(settings.allKeys( (s,k) => ScopedKey(s,k))))
|
2011-01-19 00:24:11 +01:00
|
|
|
|
|
|
|
|
// Reevaluates settings after modifying them. Does not recompile or reload any build components.
|
2011-01-25 13:24:52 +01:00
|
|
|
def reapply(newSettings: Seq[Setting[_]], structure: BuildStructure): BuildStructure =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
2011-02-06 03:39:34 +01:00
|
|
|
val newData = Project.makeSettings(newSettings, structure.delegates, structure.scopeLocal)
|
2011-01-19 00:24:11 +01:00
|
|
|
val newIndex = structureIndex(newData)
|
2011-01-21 23:22:18 +01:00
|
|
|
val newStreams = mkStreams(structure.units, structure.root, newData)
|
2011-02-06 03:39:34 +01:00
|
|
|
new BuildStructure(units = structure.units, root = structure.root, settings = newSettings, data = newData, index = newIndex, streams = newStreams, delegates = structure.delegates, scopeLocal = structure.scopeLocal)
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
|
|
|
|
|
2011-03-03 12:44:19 +01:00
|
|
|
def isProjectThis(s: Setting[_]) = s.key.scope.project match { case This | Select(ThisProject) => true; case _ => false }
|
2011-01-31 05:19:28 +01:00
|
|
|
def buildConfigurations(loaded: LoadedBuild, rootProject: URI => String, rootEval: () => Eval): Seq[Setting[_]] =
|
2011-01-19 00:24:11 +01:00
|
|
|
loaded.units.toSeq flatMap { case (uri, build) =>
|
2011-01-25 13:24:52 +01:00
|
|
|
val eval = if(uri == loaded.root) rootEval else lazyEval(build.unit)
|
2011-01-19 00:24:11 +01:00
|
|
|
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)
|
2011-03-03 12:44:19 +01:00
|
|
|
val ref = ProjectRef(uri, id)
|
2011-03-02 12:46:28 +01:00
|
|
|
val defineConfig = for(c <- project.configurations) yield ( (configuration in (ref, ConfigKey(c.name))) :== c)
|
2011-01-31 05:19:28 +01:00
|
|
|
val settings =
|
2011-03-02 12:46:28 +01:00
|
|
|
(thisProject :== project) +:
|
|
|
|
|
(thisProjectRef :== ref) +:
|
2011-02-01 00:15:15 +01:00
|
|
|
(defineConfig ++ project.settings ++ pluginThisProject ++ configurations(srcs, eval, build.imports))
|
2011-01-19 00:24:11 +01:00
|
|
|
|
|
|
|
|
// map This to thisScope, Select(p) to mapRef(uri, rootProject, p)
|
2011-03-03 12:44:19 +01:00
|
|
|
transformSettings(projectScope(ref), uri, rootProject, settings)
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
2011-03-03 12:44:19 +01:00
|
|
|
val buildScope = Scope(Select(BuildRef(uri)), Global, Global, Global)
|
2011-03-16 01:35:43 +01:00
|
|
|
val buildBase = baseDirectory :== build.localBase
|
|
|
|
|
pluginGlobal ++ inScope(buildScope)(buildBase +: build.buildSettings) ++ projectSettings
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
2011-01-25 13:24:52 +01:00
|
|
|
def transformSettings(thisScope: Scope, uri: URI, rootProject: URI => String, settings: Seq[Setting[_]]): Seq[Setting[_]] =
|
|
|
|
|
Project.transform(Scope.resolveScope(thisScope, uri, rootProject), settings)
|
2011-03-03 12:44:19 +01:00
|
|
|
def projectScope(project: Reference): Scope = Scope(Select(project), Global, Global, Global)
|
2011-01-25 13:24:52 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def lazyEval(unit: BuildUnit): () => Eval =
|
|
|
|
|
{
|
|
|
|
|
lazy val eval = mkEval(unit)
|
|
|
|
|
() => eval
|
|
|
|
|
}
|
|
|
|
|
def mkEval(unit: BuildUnit): Eval = mkEval(unit.definitions, unit.plugins, Nil)
|
2011-01-19 00:24:11 +01:00
|
|
|
def mkEval(defs: LoadedDefinitions, plugs: LoadedPlugins, options: Seq[String]): Eval =
|
2011-02-03 01:38:41 +01:00
|
|
|
new Eval(options, defs.target +: plugs.classpath, s => new ConsoleReporter(s), defs.loader, Some(evalOutputDirectory(defs.base)))
|
|
|
|
|
|
2011-01-27 01:49:54 +01:00
|
|
|
def configurations(srcs: Seq[File], eval: () => Eval, imports: Seq[String]): Seq[Setting[_]] =
|
|
|
|
|
if(srcs.isEmpty) Nil else EvaluateConfigurations(eval(), srcs, imports)
|
2011-01-19 00:24:11 +01:00
|
|
|
|
2011-03-03 12:44:19 +01:00
|
|
|
def load(file: File, s: State, config: LoadBuildConfiguration): PartBuild =
|
2011-02-05 04:02:39 +01:00
|
|
|
load(file, uri => loadUnit(uri, RetrieveUnit(config.stagingDirectory, uri), s, config) )
|
2011-03-03 12:44:19 +01:00
|
|
|
def load(file: File, loader: URI => BuildUnit): PartBuild = loadURI(IO.directoryURI(file), loader)
|
|
|
|
|
def loadURI(uri: URI, loader: URI => BuildUnit): PartBuild =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
2011-01-24 04:34:17 +01:00
|
|
|
IO.assertAbsolute(uri)
|
2011-01-19 00:24:11 +01:00
|
|
|
val (referenced, map) = loadAll(uri :: Nil, Map.empty, loader, Map.empty)
|
|
|
|
|
checkAll(referenced, map)
|
2011-03-03 12:44:19 +01:00
|
|
|
new PartBuild(uri, map)
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
2011-03-03 12:44:19 +01:00
|
|
|
def loaded(unit: BuildUnit): (PartBuildUnit, List[ProjectReference]) =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
|
|
|
|
val defined = projects(unit)
|
2011-01-24 04:34:17 +01:00
|
|
|
if(defined.isEmpty) error("No projects defined in build unit " + unit)
|
2011-01-26 04:14:02 +01:00
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
2011-01-19 00:24:11 +01:00
|
|
|
val externals = referenced(defined).toList
|
2011-01-24 04:34:17 +01:00
|
|
|
val projectsInRoot = defined.filter(isRoot).map(_.id)
|
|
|
|
|
val rootProjects = if(projectsInRoot.isEmpty) defined.head.id :: Nil else projectsInRoot
|
2011-03-03 12:44:19 +01:00
|
|
|
(new PartBuildUnit(unit, defined.map(d => (d.id, d)).toMap, rootProjects, buildSettings(unit)), externals)
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
2011-02-27 22:28:00 +01:00
|
|
|
def buildSettings(unit: BuildUnit): Seq[Setting[_]] =
|
2011-03-16 01:35:43 +01:00
|
|
|
{
|
|
|
|
|
val buildScope = GlobalScope.copy(project = Select(BuildRef(unit.uri)))
|
|
|
|
|
val resolve = Scope.resolveBuildScope(buildScope, unit.uri)
|
|
|
|
|
Project.transform(resolve, unit.definitions.builds.flatMap(_.settings))
|
|
|
|
|
}
|
2011-01-19 00:24:11 +01:00
|
|
|
|
2011-03-03 12:44:19 +01:00
|
|
|
@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]) =
|
2011-01-19 00:24:11 +01:00
|
|
|
bases match
|
|
|
|
|
{
|
|
|
|
|
case b :: bs =>
|
|
|
|
|
if(builds contains b)
|
|
|
|
|
loadAll(bs, references, externalLoader, builds)
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
val (loadedBuild, refs) = loaded(externalLoader(b))
|
2011-03-03 12:44:19 +01:00
|
|
|
checkBuildBase(loadedBuild.unit.localBase)
|
|
|
|
|
loadAll(refs.flatMap(Reference.uri) reverse_::: bs, references.updated(b, refs), externalLoader, builds.updated(b, loadedBuild))
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
|
|
|
|
case Nil => (references, builds)
|
|
|
|
|
}
|
2011-01-24 04:34:17 +01:00
|
|
|
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)
|
2011-02-25 05:30:06 +01:00
|
|
|
if(base.isFile)
|
|
|
|
|
error("Not a directory: " + base)
|
|
|
|
|
else if(!base.exists)
|
|
|
|
|
IO createDirectory base
|
2011-01-24 04:34:17 +01:00
|
|
|
}
|
2011-03-03 12:44:19 +01:00
|
|
|
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])
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
|
|
|
|
val rootProject = getRootProject(builds)
|
|
|
|
|
for( (uri, refs) <- referenced; ref <- refs)
|
|
|
|
|
{
|
2011-03-03 12:44:19 +01:00
|
|
|
val ProjectRef(refURI, refID) = Scope.resolveProjectRef(uri, rootProject, ref)
|
2011-01-19 00:24:11 +01:00
|
|
|
val loadedUnit = builds(refURI)
|
|
|
|
|
if(! (loadedUnit.defined contains refID) )
|
|
|
|
|
error("No project '" + refID + "' in '" + refURI + "'")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-26 04:14:02 +01:00
|
|
|
def resolveBase(against: File): Project => Project =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
2011-01-24 04:34:17 +01:00
|
|
|
def resolve(f: File) =
|
|
|
|
|
{
|
|
|
|
|
val fResolved = new File(IO.directoryURI(IO.resolve(against, f)))
|
|
|
|
|
checkProjectBase(against, fResolved)
|
|
|
|
|
fResolved
|
|
|
|
|
}
|
2011-01-26 04:14:02 +01:00
|
|
|
p => p.copy(base = resolve(p.base))
|
|
|
|
|
}
|
2011-03-03 12:44:19 +01:00
|
|
|
def resolveProjects(loaded: PartBuild): LoadedBuild =
|
2011-01-26 04:14:02 +01:00
|
|
|
{
|
|
|
|
|
val rootProject = getRootProject(loaded.units)
|
2011-03-03 12:44:19 +01:00
|
|
|
new LoadedBuild(loaded.root, loaded.units map { case (uri, unit) =>
|
2011-01-26 04:14:02 +01:00
|
|
|
IO.assertAbsolute(uri)
|
|
|
|
|
(uri, resolveProjects(uri, unit, rootProject))
|
|
|
|
|
})
|
|
|
|
|
}
|
2011-03-03 12:44:19 +01:00
|
|
|
def resolveProjects(uri: URI, unit: PartBuildUnit, rootProject: URI => String): LoadedBuildUnit =
|
2011-01-26 04:14:02 +01:00
|
|
|
{
|
|
|
|
|
IO.assertAbsolute(uri)
|
2011-03-03 12:44:19 +01:00
|
|
|
val resolve = (_: Project).resolve(ref => Scope.resolveProjectRef(uri, rootProject, ref))
|
2011-02-27 22:28:00 +01:00
|
|
|
new LoadedBuildUnit(unit.unit, unit.defined mapValues resolve, unit.rootProjects, unit.buildSettings)
|
2011-01-26 04:14:02 +01:00
|
|
|
}
|
2011-01-24 04:34:17 +01:00
|
|
|
def projects(unit: BuildUnit): Seq[Project] =
|
|
|
|
|
{
|
2011-01-26 04:14:02 +01:00
|
|
|
// we don't have the complete build graph loaded, so we don't have the rootProject function yet.
|
2011-03-03 12:44:19 +01:00
|
|
|
// 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)
|
2011-01-24 04:34:17 +01:00
|
|
|
unit.definitions.builds.flatMap(_.projects map resolve)
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
2011-03-03 12:44:19 +01:00
|
|
|
def getRootProject(map: Map[URI, BuildUnitBase]): URI => String =
|
2011-01-19 00:24:11 +01:00
|
|
|
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)
|
|
|
|
|
|
2011-03-03 12:44:19 +01:00
|
|
|
def getProject(map: Map[URI, LoadedBuildUnit], uri: URI, id: String): ResolvedProject =
|
2011-01-19 00:24:11 +01:00
|
|
|
getBuild(map, uri).defined.getOrElse(id, noProject(uri, id))
|
2011-03-03 12:44:19 +01:00
|
|
|
def getBuild[T](map: Map[URI, T], uri: URI): T =
|
2011-01-19 00:24:11 +01:00
|
|
|
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 +"'")
|
|
|
|
|
|
2011-02-05 04:02:39 +01:00
|
|
|
def loadUnit(uri: URI, localBase: File, s: State, config: LoadBuildConfiguration): BuildUnit =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
2011-01-24 04:34:17 +01:00
|
|
|
val normBase = localBase.getCanonicalFile
|
|
|
|
|
val defDir = selectProjectDir(normBase)
|
2011-01-19 00:24:11 +01:00
|
|
|
val pluginDir = pluginDirectory(defDir)
|
2011-02-05 04:02:39 +01:00
|
|
|
val plugs = plugins(pluginDir, s, config)
|
2011-01-19 00:24:11 +01:00
|
|
|
|
|
|
|
|
val defs = definitionSources(defDir)
|
2011-02-03 01:34:52 +01:00
|
|
|
val target = buildOutputDirectory(defDir, config.compilers)
|
|
|
|
|
IO.createDirectory(target)
|
2011-01-19 00:24:11 +01:00
|
|
|
val loadedDefs =
|
|
|
|
|
if(defs.isEmpty)
|
2011-02-03 01:34:52 +01:00
|
|
|
new LoadedDefinitions(defDir, target, plugs.loader, Build.default(normBase) :: Nil, Nil)
|
2011-01-19 00:24:11 +01:00
|
|
|
else
|
2011-02-03 01:34:52 +01:00
|
|
|
definitions(defDir, target, defs, plugs, config.compilers, config.log, normBase)
|
2011-01-19 00:24:11 +01:00
|
|
|
|
2011-01-24 04:34:17 +01:00
|
|
|
new BuildUnit(uri, normBase, loadedDefs, plugs)
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
|
|
|
|
|
2011-03-12 04:33:30 +01:00
|
|
|
def plugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins = if(dir.exists) buildPlugins(dir, s, config) else noPlugins(dir, config)
|
2011-03-17 03:22:46 +01:00
|
|
|
def noPlugins(dir: File, config: LoadBuildConfiguration): LoadedPlugins = loadPluginDefinition(dir, config, config.commonPluginClasspath)
|
2011-02-05 04:02:39 +01:00
|
|
|
def buildPlugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins =
|
2011-03-17 03:22:46 +01:00
|
|
|
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]] =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
2011-02-05 04:02:39 +01:00
|
|
|
val (eval,pluginDef) = apply(dir, s, config)
|
|
|
|
|
val pluginState = Project.setProject(Load.initialSession(pluginDef, eval), pluginDef, s)
|
2011-03-17 03:22:46 +01:00
|
|
|
val thisPluginClasspath = config.evalPluginDef(pluginDef, pluginState)
|
|
|
|
|
(thisPluginClasspath ++ config.commonPluginClasspath).distinct
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-03 01:34:52 +01:00
|
|
|
def definitions(base: File, targetBase: File, srcs: Seq[File], plugins: LoadedPlugins, compilers: Compilers, log: Logger, buildBase: File): LoadedDefinitions =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
2011-02-03 01:34:52 +01:00
|
|
|
val (inputs, defAnalysis) = build(plugins.classpath, srcs, targetBase, compilers, log)
|
2011-01-19 00:24:11 +01:00
|
|
|
val target = inputs.config.classesDirectory
|
|
|
|
|
val definitionLoader = ClasspathUtilities.toLoader(target :: Nil, plugins.loader)
|
2011-01-27 01:49:54 +01:00
|
|
|
val defNames = findDefinitions(defAnalysis)
|
2011-02-03 01:34:52 +01:00
|
|
|
val defs = if(defNames.isEmpty) Build.default(buildBase) :: Nil else loadDefinitions(definitionLoader, defNames)
|
2011-01-27 01:49:54 +01:00
|
|
|
new LoadedDefinitions(base, target, definitionLoader, defs, defNames)
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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]
|
|
|
|
|
|
2011-02-27 05:56:30 +01:00
|
|
|
def build(classpath: Seq[File], sources: Seq[File], target: File, compilers: Compilers, log: Logger): (Inputs, inc.Analysis) =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
2011-03-10 00:04:53 +01:00
|
|
|
val inputs = Compiler.inputs(classpath, sources, target, Nil, Nil, Compiler.DefaultMaxErrors)(compilers, log)
|
2011-03-05 14:25:17 +01:00
|
|
|
val analysis = Compiler(inputs, log)
|
2011-01-19 00:24:11 +01:00
|
|
|
(inputs, analysis)
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-12 04:33:30 +01:00
|
|
|
def loadPlugins(dir: File, classpath: Seq[File], loader: ClassLoader, analysis: Seq[inc.Analysis]): LoadedPlugins =
|
2011-01-27 01:49:54 +01:00
|
|
|
{
|
|
|
|
|
val (pluginNames, plugins) = if(classpath.isEmpty) (Nil, Nil) else {
|
2011-03-14 02:34:17 +01:00
|
|
|
val names = ( binaryPlugins(loader) ++ (analysis flatMap findPlugins) ).distinct
|
2011-01-27 01:49:54 +01:00
|
|
|
(names, loadPlugins(loader, names) )
|
|
|
|
|
}
|
2011-03-12 04:33:30 +01:00
|
|
|
new LoadedPlugins(dir, classpath, loader, plugins, pluginNames)
|
2011-01-27 01:49:54 +01:00
|
|
|
}
|
2011-03-14 02:34:17 +01:00
|
|
|
def binaryPlugins(loader: ClassLoader): Seq[String] =
|
|
|
|
|
{
|
|
|
|
|
import collection.JavaConversions._
|
|
|
|
|
loader.getResources("sbt/sbt.plugins").toSeq flatMap { u => IO.readLinesURL(u) map { _.trim } filter { !_.isEmpty } };
|
|
|
|
|
}
|
2011-01-19 00:24:11 +01:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
2011-01-27 01:49:54 +01:00
|
|
|
def importAll(values: Seq[String]) = if(values.isEmpty) Nil else values.map( _ + "._" ).mkString("import ", ", ", "") :: Nil
|
|
|
|
|
|
2011-02-27 05:56:30 +01:00
|
|
|
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] =
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
2011-02-03 01:25:18 +01:00
|
|
|
val subclassSet = subclasses.toSet
|
2011-03-05 14:25:17 +01:00
|
|
|
val ds = Discovery(subclassSet, Set.empty)(Tests.allDefs(analysis))
|
2011-02-23 04:36:48 +01:00
|
|
|
ds.flatMap {
|
|
|
|
|
case (definition, Discovered(subs,_,_,true)) =>
|
2011-03-01 14:44:41 +01:00
|
|
|
if((subs & subclassSet).isEmpty) Nil else definition.name :: Nil
|
2011-02-23 04:36:48 +01:00
|
|
|
case _ => Nil
|
2011-02-03 01:25:18 +01:00
|
|
|
}
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
|
|
|
|
|
2011-01-25 13:24:52 +01:00
|
|
|
def initialSession(structure: BuildStructure, rootEval: () => Eval): SessionSettings =
|
|
|
|
|
new SessionSettings(structure.root, rootProjectMap(structure.units), structure.settings, Map.empty, Map.empty, rootEval)
|
2011-01-27 01:49:54 +01:00
|
|
|
|
2011-01-19 00:24:11 +01:00
|
|
|
def rootProjectMap(units: Map[URI, LoadedBuildUnit]): Map[URI, String] =
|
|
|
|
|
{
|
|
|
|
|
val getRoot = getRootProject(units)
|
|
|
|
|
units.keys.map(uri => (uri, getRoot(uri))).toMap
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-15 00:55:05 +01:00
|
|
|
def baseImports = "import sbt._, Process._, Keys._" :: Nil
|
2011-01-27 01:49:54 +01:00
|
|
|
|
2011-01-19 00:24:11 +01:00
|
|
|
final class EvaluatedConfigurations(val eval: Eval, val settings: Seq[Setting[_]])
|
2011-01-27 01:49:54 +01:00
|
|
|
final class LoadedDefinitions(val base: File, val target: File, val loader: ClassLoader, val builds: Seq[Build], val buildNames: Seq[String])
|
2011-03-12 04:33:30 +01:00
|
|
|
final class LoadedPlugins(val base: File, val classpath: Seq[File], val loader: ClassLoader, val plugins: Seq[Setting[_]], val pluginNames: Seq[String])
|
2011-01-24 04:34:17 +01:00
|
|
|
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 +")")
|
|
|
|
|
}
|
2011-01-19 00:24:11 +01:00
|
|
|
|
|
|
|
|
final class LoadedBuild(val root: URI, val units: Map[URI, LoadedBuildUnit])
|
2011-03-03 12:44:19 +01:00
|
|
|
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
|
2011-01-19 00:24:11 +01:00
|
|
|
{
|
2011-01-24 04:34:17 +01:00
|
|
|
assert(!rootProjects.isEmpty, "No root projects defined for build unit " + unit)
|
|
|
|
|
def localBase = unit.localBase
|
2011-01-19 00:24:11 +01:00
|
|
|
def classpath = unit.definitions.target +: unit.plugins.classpath
|
2011-01-27 01:49:54 +01:00
|
|
|
def loader = unit.definitions.loader
|
2011-02-25 05:30:06 +01:00
|
|
|
def imports = getImports(unit)
|
2011-01-24 04:34:17 +01:00
|
|
|
override def toString = unit.toString
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
2011-02-25 05:30:06 +01:00
|
|
|
def getImports(unit: BuildUnit) = baseImports ++ importAll(unit.plugins.pluginNames ++ unit.definitions.buildNames)
|
2011-01-19 00:24:11 +01:00
|
|
|
|
2011-03-03 12:44:19 +01:00
|
|
|
def referenced[PR <: ProjectReference](definitions: Seq[ProjectDefinition[PR]]): Seq[PR] = definitions flatMap { _.referenced }
|
2011-01-19 00:24:11 +01:00
|
|
|
|
2011-02-12 02:22:17 +01:00
|
|
|
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)
|
2011-03-17 03:22:46 +01:00
|
|
|
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)
|
2011-01-19 00:24:11 +01:00
|
|
|
// information that is not original, but can be reconstructed from the rest of BuildStructure
|
2011-01-26 04:22:11 +01:00
|
|
|
final class StructureIndex(val keyMap: Map[String, AttributeKey[_]], val taskToKey: Map[Task[_], ScopedKey[Task[_]]], val keyIndex: KeyIndex)
|
2011-01-26 04:14:02 +01:00
|
|
|
|
|
|
|
|
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))
|
|
|
|
|
}
|
2011-01-19 00:24:11 +01:00
|
|
|
}
|
2011-01-21 23:22:18 +01:00
|
|
|
object BuildStreams
|
|
|
|
|
{
|
|
|
|
|
import Load.{BuildStructure, LoadedBuildUnit}
|
|
|
|
|
import Project.display
|
|
|
|
|
import std.{TaskExtra,Transform}
|
|
|
|
|
|
2011-02-06 03:39:34 +01:00
|
|
|
type Streams = std.Streams[ScopedKey[_]]
|
|
|
|
|
type TaskStreams = std.TaskStreams[ScopedKey[_]]
|
2011-01-21 23:22:18 +01:00
|
|
|
val GlobalPath = "$global"
|
2011-03-03 12:44:19 +01:00
|
|
|
val BuildUnitPath = "$build"
|
2011-01-21 23:22:18 +01:00
|
|
|
|
|
|
|
|
def mkStreams(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope], logRelativePath: Seq[String] = defaultLogPath): Streams =
|
|
|
|
|
std.Streams( path(units, root, logRelativePath), display, LogManager.construct(data) )
|
|
|
|
|
|
|
|
|
|
def defaultLogPath = "target" :: "streams" :: Nil
|
|
|
|
|
|
|
|
|
|
def path(units: Map[URI, LoadedBuildUnit], root: URI, sep: Seq[String])(scoped: ScopedKey[_]): File =
|
|
|
|
|
{
|
|
|
|
|
val (base, sub) = projectPath(units, root, 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(units: Map[URI, LoadedBuildUnit], root: URI, scoped: ScopedKey[_]): (File, Seq[String]) =
|
|
|
|
|
scoped.scope.project match
|
|
|
|
|
{
|
2011-01-24 04:34:17 +01:00
|
|
|
case Global => (units(root).localBase, GlobalPath :: Nil)
|
2011-03-03 12:44:19 +01:00
|
|
|
case Select(BuildRef(uri)) => (units(uri).localBase, BuildUnitPath :: Nil)
|
|
|
|
|
case Select(ProjectRef(uri, id)) => (units(uri).defined(id).base, Nil)
|
2011-01-21 23:22:18 +01:00
|
|
|
case Select(pr) => error("Unresolved project reference (" + pr + ") in " + display(scoped))
|
|
|
|
|
case This => error("Unresolved project reference (This) in " + display(scoped))
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-01-19 00:24:11 +01:00
|
|
|
object BuildPaths
|
|
|
|
|
{
|
|
|
|
|
import Path._
|
|
|
|
|
import GlobFilter._
|
|
|
|
|
|
2011-03-12 16:28:53 +01:00
|
|
|
def defaultStaging = Path.userHome / ".sbt" / "staging"
|
2011-03-17 03:22:46 +01:00
|
|
|
def defaultGlobalPlugins = Path.userHome / ".sbt" / "plugins"
|
2011-01-19 00:24:11 +01:00
|
|
|
|
2011-03-17 03:22:46 +01:00
|
|
|
def definitionSources(base: File): Seq[File] = (base * "*.scala").getFiles
|
|
|
|
|
def configurationSources(base: File): Seq[File] = (base * "*.sbt").getFiles
|
2011-01-19 00:24:11 +01:00
|
|
|
def pluginDirectory(definitionBase: Path) = definitionBase / "plugins"
|
|
|
|
|
|
2011-02-03 01:34:52 +01:00
|
|
|
def evalOutputDirectory(base: Path) = outputDirectory(base) / "config-classes"
|
2011-01-19 00:24:11 +01:00
|
|
|
def outputDirectory(base: Path) = base / "target"
|
2011-02-03 01:34:52 +01:00
|
|
|
def buildOutputDirectory(base: Path, compilers: Compilers) = crossPath(outputDirectory(base), compilers.scalac.scalaInstance)
|
|
|
|
|
|
2011-01-19 00:24:11 +01:00
|
|
|
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)
|
|
|
|
|
}
|