2011-04-09 01:15:13 +02:00
/* sbt -- Simple Build Tool
* Copyright 2011 Mark Harrah
*/
package sbt
import java.io.File
import Project. { ScopedKey , Setting }
2011-10-30 23:39:18 +01:00
import Keys. { streams , Streams , TaskStreams }
2012-04-02 05:19:08 +02:00
import Keys. { dummyRoots , dummyState , dummyStreamsManager , executionRoots , pluginData , streamsManager , taskDefinitionKey , transformState }
2011-04-09 01:15:13 +02:00
import Scope. { GlobalScope , ThisScope }
2011-10-19 04:43:25 +02:00
import Types.const
2012-05-20 00:20:19 +02:00
import scala.Console.RED
2011-04-09 01:15:13 +02:00
2011-11-20 05:56:30 +01:00
final case class EvaluateConfig ( cancelable : Boolean , restrictions : Seq [ Tags . Rule ] , checkCycles : Boolean = false )
2012-06-17 05:40:52 +02:00
final case class PluginData ( classpath : Seq [ Attributed [ File ] ] , resolvers : Option [ Seq [ Resolver ] ] , report : Option [ UpdateReport ] )
2011-04-09 01:15:13 +02:00
object EvaluateTask
{
2011-09-03 20:59:34 +02:00
import Load.BuildStructure
import Project.display
2011-04-09 01:15:13 +02:00
import std. { TaskExtra , Transform }
import TaskExtra._
import Keys.state
val SystemProcessors = Runtime . getRuntime . availableProcessors
2011-11-20 05:56:30 +01:00
def defaultConfig ( state : State ) : EvaluateConfig =
EvaluateConfig ( false , restrictions ( state ) )
def defaultConfig ( extracted : Extracted , structure : Load . BuildStructure ) =
EvaluateConfig ( false , restrictions ( extracted , structure ) )
2011-10-19 04:43:25 +02:00
def extractedConfig ( extracted : Extracted , structure : BuildStructure ) : EvaluateConfig =
{
2011-11-20 05:56:30 +01:00
val workers = restrictions ( extracted , structure )
2011-10-19 04:43:25 +02:00
val canCancel = cancelable ( extracted , structure )
2011-11-20 05:56:30 +01:00
EvaluateConfig ( cancelable = canCancel , restrictions = workers )
2011-10-19 04:43:25 +02:00
}
2011-11-20 05:56:30 +01:00
def defaultRestrictions ( maxWorkers : Int ) = Tags . limitAll ( maxWorkers ) : : Nil
def defaultRestrictions ( extracted : Extracted , structure : Load . BuildStructure ) : Seq [ Tags . Rule ] =
Tags . limitAll ( maxWorkers ( extracted , structure ) ) : : Nil
def restrictions ( state : State ) : Seq [ Tags . Rule ] =
{
val extracted = Project . extract ( state )
restrictions ( extracted , extracted . structure )
}
def restrictions ( extracted : Extracted , structure : Load . BuildStructure ) : Seq [ Tags . Rule ] =
getSetting ( Keys . concurrentRestrictions , defaultRestrictions ( extracted , structure ) , extracted , structure )
2011-10-19 04:43:25 +02:00
def maxWorkers ( extracted : Extracted , structure : Load . BuildStructure ) : Int =
2011-11-20 05:56:30 +01:00
if ( getSetting ( Keys . parallelExecution , true , extracted , structure ) )
SystemProcessors
2011-10-19 04:43:25 +02:00
else
1
def cancelable ( extracted : Extracted , structure : Load . BuildStructure ) : Boolean =
2011-11-20 05:56:30 +01:00
getSetting ( Keys . cancelable , false , extracted , structure )
def getSetting [ T ] ( key : SettingKey [ T ] , default : T , extracted : Extracted , structure : Load . BuildStructure ) : T =
key in extracted . currentRef get structure . data getOrElse default
2011-04-09 01:15:13 +02:00
def injectSettings : Seq [ Setting [ _ ] ] = Seq (
( state in GlobalScope ) : := dummyState ,
2012-03-18 00:31:03 +01:00
( streamsManager in GlobalScope ) : := dummyStreamsManager ,
( executionRoots in GlobalScope ) : := dummyRoots
2011-04-09 01:15:13 +02:00
)
2012-04-02 05:19:08 +02:00
def evalPluginDef ( log : Logger ) ( pluginDef : BuildStructure , state : State ) : PluginData =
2011-04-09 01:15:13 +02:00
{
val root = ProjectRef ( pluginDef . root , Load . getRootProject ( pluginDef . units ) ( pluginDef . root ) )
2012-04-02 05:19:08 +02:00
val pluginKey = pluginData
2011-11-20 05:56:30 +01:00
val config = defaultConfig ( Project . extract ( state ) , pluginDef )
val evaluated = apply ( pluginDef , ScopedKey ( pluginKey . scope , pluginKey . key ) , state , root , config )
2012-04-02 05:19:08 +02:00
val ( newS , result ) = evaluated getOrElse error ( "Plugin data does not exist for plugin definition at " + pluginDef . root )
2011-10-19 04:43:25 +02:00
Project . runUnloadHooks ( newS ) // discard states
2011-09-03 20:59:34 +02:00
processResult ( result , log )
2011-04-09 01:15:13 +02:00
}
2011-09-22 04:54:46 +02:00
2011-11-20 05:56:30 +01:00
@deprecated ( "This method does not apply state changes requested during task execution and does not honor concurrent execution restrictions. Use 'apply' instead." , "0.11.1" )
2011-04-09 01:15:13 +02:00
def evaluateTask [ T ] ( structure : BuildStructure , taskKey : ScopedKey [ Task [ T ] ] , state : State , ref : ProjectRef , checkCycles : Boolean = false , maxWorkers : Int = SystemProcessors ) : Option [ Result [ T ] ] =
2011-11-20 05:56:30 +01:00
apply ( structure , taskKey , state , ref , EvaluateConfig ( false , defaultRestrictions ( maxWorkers ) , checkCycles ) ) . map ( _ . _2 )
def apply [ T ] ( structure : BuildStructure , taskKey : ScopedKey [ Task [ T ] ] , state : State , ref : ProjectRef ) : Option [ ( State , Result [ T ] ) ] =
apply [ T ] ( structure , taskKey , state , ref , defaultConfig ( Project . extract ( state ) , structure ) )
def apply [ T ] ( structure : BuildStructure , taskKey : ScopedKey [ Task [ T ] ] , state : State , ref : ProjectRef , config : EvaluateConfig ) : Option [ ( State , Result [ T ] ) ] =
2011-10-30 23:39:18 +01:00
withStreams ( structure , state ) { str =>
2011-04-09 01:15:13 +02:00
for ( ( task , toNode ) <- getTask ( structure , taskKey , state , str , ref ) ) yield
2011-10-19 04:43:25 +02:00
runTask ( task , state , str , structure . index . triggers , config ) ( toNode )
2011-04-09 01:15:13 +02:00
}
2012-05-20 00:20:19 +02:00
def logIncResult ( result : Result [ _ ] , state : State , streams : Streams ) = result match { case Inc ( i ) => logIncomplete ( i , state , streams ) ; case _ => ( ) }
def logIncomplete ( result : Incomplete , state : State , streams : Streams )
2011-04-09 01:15:13 +02:00
{
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 }
2012-05-20 00:20:19 +02:00
import ExceptionCategory._
for ( ( key , msg , Some ( ex ) ) <- keyed ) {
def log = getStreams ( key , streams ) . log
ExceptionCategory ( ex ) match {
case AlreadyHandled => ( )
case m : MessageOnly => if ( msg . isEmpty ) log . error ( m . message )
case f : Full => log . trace ( f . exception )
}
}
2011-06-15 01:31:55 +02:00
2011-04-09 01:15:13 +02:00
for ( ( key , msg , ex ) <- keyed if ( msg . isDefined || ex . isDefined ) )
2011-05-31 04:10:01 +02:00
{
val msgString = ( msg . toList ++ ex . toList . map ( ErrorHandling . reducedToString ) ) . mkString ( "\n\t" )
2011-07-24 23:36:42 +02:00
val log = getStreams ( key , streams ) . log
2012-05-20 00:20:19 +02:00
val display = contextDisplay ( state , log . ansiCodesSupported )
2012-05-20 00:20:19 +02:00
log . error ( "(" + display ( key ) + ") " + msgString )
2011-05-31 04:10:01 +02:00
}
2011-04-09 01:15:13 +02:00
}
2012-05-20 00:20:19 +02:00
private [ this ] def contextDisplay ( state : State , highlight : Boolean ) = Project . showContextKey ( state , if ( highlight ) Some ( RED ) else None )
def suppressedMessage ( key : ScopedKey [ _ ] ) ( implicit display : Show [ ScopedKey [ _ ] ] ) : String =
"Stack trace suppressed. Run 'last %s' for the full log." . format ( display ( key ) )
2011-04-09 01:15:13 +02:00
def getStreams ( key : ScopedKey [ _ ] , streams : Streams ) : TaskStreams =
streams ( ScopedKey ( Project . fillTaskAxis ( key ) . scope , Keys . streams . key ) )
2011-10-30 23:39:18 +01:00
def withStreams [ T ] ( structure : BuildStructure , state : State ) ( f : Streams => T ) : T =
2011-04-09 01:15:13 +02:00
{
2011-10-30 23:39:18 +01:00
val str = std . Streams . closeable ( structure . streams ( state ) )
2011-04-09 01:15:13 +02:00
try { f ( str ) } finally { str . close ( ) }
}
2012-05-20 00:20:20 +02:00
def getTask [ T ] ( structure : BuildStructure , taskKey : ScopedKey [ Task [ T ] ] , state : State , streams : Streams , ref : ProjectRef ) : Option [ ( Task [ T ] , NodeView [ Task ] ) ] =
2011-04-09 01:15:13 +02:00
{
val thisScope = Load . projectScope ( ref )
val resolvedScope = Scope . replaceThis ( thisScope ) ( taskKey . scope )
for ( t <- structure . data . get ( resolvedScope , taskKey . key ) ) yield
2012-03-18 00:31:03 +01:00
( t , nodeView ( state , streams , taskKey : : Nil ) )
2011-04-09 01:15:13 +02:00
}
2012-05-20 00:20:20 +02:00
def nodeView [ HL <: HList ] ( state : State , streams : Streams , roots : Seq [ ScopedKey [ _ ] ] , extraDummies : KList [ Task , HL ] = KNil , extraValues : HL = HNil ) : NodeView [ Task ] =
2012-03-18 00:31:03 +01:00
Transform ( dummyRoots : ^: dummyStreamsManager :^: KCons ( dummyState , extraDummies ) , roots : +: streams :+: HCons ( state , extraValues ) )
2011-04-09 01:15:13 +02:00
2012-05-20 00:20:20 +02:00
def runTask [ T ] ( root : Task [ T ] , state : State , streams : Streams , triggers : Triggers [ Task ] , config : EvaluateConfig ) ( implicit taskToNode : NodeView [ Task ] ) : ( State , Result [ T ] ) =
2011-04-09 01:15:13 +02:00
{
2011-11-20 05:56:30 +01:00
import ConcurrentRestrictions. { completionService , TagMap , Tag , tagged , tagsKey }
2011-10-19 04:43:25 +02:00
val log = state . log
2011-11-20 05:56:30 +01:00
log . debug ( "Running task... Cancelable: " + config . cancelable + ", check cycles: " + config . checkCycles )
val tags = tagged [ Task [ _ ] ] ( _ . info get tagsKey getOrElse Map . empty , Tags . predicate ( config . restrictions ) )
val ( service , shutdown ) = completionService [ Task [ _ ] , Completed ] ( tags , ( s : String ) => log . warn ( s ) )
2011-04-09 01:15:13 +02:00
2011-10-19 04:43:25 +02:00
def run ( ) = {
val x = new Execute [ Task ] ( config . checkCycles , triggers ) ( taskToNode )
val ( newState , result ) =
try applyResults ( x . runKeep ( root ) ( service ) , state , root )
catch { case inc : Incomplete => ( state , Inc ( inc ) ) }
finally shutdown ( )
val replaced = transformInc ( result )
2012-05-20 00:20:19 +02:00
logIncResult ( replaced , state , streams )
2011-10-19 04:43:25 +02:00
( newState , replaced )
}
val cancel = ( ) => {
println ( "" )
log . warn ( "Canceling execution..." )
shutdown ( )
}
if ( config . cancelable )
Signals . withHandler ( cancel ) { run }
else
run ( )
2011-04-09 01:15:13 +02:00
}
2011-09-22 04:54:46 +02:00
def applyResults [ T ] ( results : RMap [ Task , Result ] , state : State , root : Task [ T ] ) : ( State , Result [ T ] ) =
( stateTransform ( results ) ( state ) , results ( root ) )
def stateTransform ( results : RMap [ Task , Result ] ) : State => State =
Function . chain (
results . toTypedSeq flatMap {
case results . TPair ( Task ( info , _ ) , Value ( v ) ) => info . post ( v ) get transformState
case _ => Nil
}
)
2011-04-09 01:15:13 +02:00
def transformInc [ T ] ( result : Result [ T ] ) : Result [ T ] =
2011-04-10 00:43:21 +02:00
// taskToKey needs to be before liftAnonymous. liftA only lifts non-keyed (anonymous) Incompletes.
result . toEither . left . map { i => Incomplete . transformBU ( i ) ( convertCyclicInc andThen taskToKey andThen liftAnonymous ) }
2011-04-09 01:15:13 +02:00
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 =
2011-08-04 13:20:25 +02:00
node . info . name orElse transformNode ( node ) . map ( Project . displayFull ) getOrElse ( "<anon-" + System . identityHashCode ( node ) . toHexString + ">" )
2011-04-09 01:15:13 +02:00
def liftAnonymous : Incomplete => Incomplete = {
case i @ Incomplete ( node , tpe , None , causes , None ) =>
2011-04-10 00:43:21 +02:00
causes . find ( inc => ! inc . node . isDefined && ( inc . message . isDefined || inc . directCause . isDefined ) ) match {
2011-04-09 01:15:13 +02:00
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
Nil
}