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 }
2011-09-22 04:54:46 +02:00
import Keys. { dummyState , dummyStreamsManager , 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
2011-05-31 04:10:01 +02:00
import scala.Console. { RED , RESET }
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 )
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 ,
( streamsManager in GlobalScope ) : := dummyStreamsManager
)
2011-09-03 20:59:34 +02:00
def evalPluginDef ( log : Logger ) ( pluginDef : BuildStructure , state : State ) : Seq [ Attributed [ File ] ] =
2011-04-09 01:15:13 +02:00
{
val root = ProjectRef ( pluginDef . root , Load . getRootProject ( pluginDef . units ) ( pluginDef . root ) )
2011-09-03 20:59:34 +02:00
val pluginKey = Keys . fullClasspath in Configurations . Runtime
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 )
2011-09-22 04:54:46 +02:00
val ( newS , result ) = evaluated getOrElse error ( "Plugin classpath 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
}
def logIncResult ( result : Result [ _ ] , streams : Streams ) = result match { case Inc ( i ) => logIncomplete ( i , streams ) ; case _ => ( ) }
def logIncomplete ( result : Incomplete , streams : Streams )
{
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 )
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
2011-05-31 04:10:01 +02:00
val keyString = if ( log . ansiCodesSupported ) RED + key . key . label + RESET else key . key . label
2011-07-24 23:36:42 +02:00
log . error ( Scope . display ( key . scope , keyString ) + ": " + msgString )
2011-05-31 04:10:01 +02:00
}
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 ( ) }
}
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 ) )
2011-11-20 05:56:30 +01:00
def runTask [ T ] ( root : Task [ T ] , state : State , streams : Streams , triggers : Triggers [ Task ] , config : EvaluateConfig ) ( implicit taskToNode : Execute.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 )
logIncResult ( replaced , streams )
( 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
}