2010-08-14 15:55:49 +02:00
/* sbt -- Simple Build Tool
* Copyright 2010 Mark Harrah
*/
package sbt
package std
2010-08-28 01:17:03 +02:00
import Types._
import Task._
import java.io. { BufferedInputStream , BufferedReader , File , InputStream }
2010-08-14 15:55:49 +02:00
2012-07-31 17:52:10 +02:00
sealed trait MultiInTask [ K [ L [ x ] ] ]
2010-08-14 15:55:49 +02:00
{
2012-07-31 17:52:10 +02:00
def flatMap [ T ] ( f : K [ Id ] => Task [ T ] ) : Task [ T ]
def flatMapR [ T ] ( f : K [ Result ] => Task [ T ] ) : Task [ T ]
def map [ T ] ( f : K [ Id ] => T ) : Task [ T ]
def mapR [ T ] ( f : K [ Result ] => T ) : Task [ T ]
2010-08-14 15:55:49 +02:00
def flatFailure [ T ] ( f : Seq [ Incomplete ] => Task [ T ] ) : Task [ T ]
def mapFailure [ T ] ( f : Seq [ Incomplete ] => T ) : Task [ T ]
}
2012-07-31 17:52:10 +02:00
2010-08-14 15:55:49 +02:00
sealed trait SingleInTask [ S ]
{
def flatMap [ T ] ( f : S => Task [ T ] ) : Task [ T ]
def map [ T ] ( f : S => T ) : Task [ T ]
def dependsOn ( tasks : Task [ _ ] * ) : Task [ S ]
def andFinally ( fin : => Unit ) : Task [ S ]
2010-11-14 02:02:25 +01:00
def doFinally ( t : Task [ Unit ] ) : Task [ S ]
2010-08-14 15:55:49 +02:00
def || [ T >: S ] ( alt : Task [ T ] ) : Task [ T ]
def && [ T ] ( alt : Task [ T ] ) : Task [ T ]
2012-12-02 09:17:19 +01:00
def failure : Task [ Incomplete ]
def result : Task [ Result [ S ] ]
@deprecated ( "Use the `result` method to create a task that returns the full Result of this task. Then, call `map` on the new task." , "0.13.0" )
def mapR [ T ] ( f : Result [ S ] => T ) : Task [ T ]
@deprecated ( "Use the `failure` method to create a task that returns Incomplete when this task fails and then call `flatMap` on the new task." , "0.13.0" )
def flatFailure [ T ] ( f : Incomplete => Task [ T ] ) : Task [ T ]
@deprecated ( "Use the `failure` method to create a task that returns Incomplete when this task fails and then call `mapFailure` on the new task." , "0.13.0" )
def mapFailure [ T ] ( f : Incomplete => T ) : Task [ T ]
@deprecated ( "Use the `result` method to create a task that returns the full Result of this task. Then, call `flatMap` on the new task." , "0.13.0" )
def flatMapR [ T ] ( f : Result [ S ] => Task [ T ] ) : Task [ T ]
2010-08-14 15:55:49 +02:00
}
sealed trait TaskInfo [ S ]
{
def describedAs ( s : String ) : Task [ S ]
2011-01-19 00:24:11 +01:00
def named ( s : String ) : Task [ S ]
2010-08-14 15:55:49 +02:00
}
sealed trait ForkTask [ S , CC [ _ ] ]
{
def fork [ T ] ( f : S => T ) : CC [ Task [ T ] ]
2010-08-28 01:17:03 +02:00
def tasks : Seq [ Task [ S ] ]
2010-08-14 15:55:49 +02:00
}
sealed trait JoinTask [ S , CC [ _ ] ]
{
def join : Task [ CC [ S ] ]
2011-05-29 05:49:17 +02:00
// had to rename from 'reduce' for 2.9.0
def reduced ( f : ( S , S ) => S ) : Task [ S ]
2010-08-14 15:55:49 +02:00
}
2010-08-28 01:17:03 +02:00
2010-08-14 15:55:49 +02:00
sealed trait BinaryPipe
{
2010-08-22 04:45:50 +02:00
def binary [ T ] ( f : BufferedInputStream => T ) : Task [ T ]
2010-08-22 04:55:42 +02:00
def binary [ T ] ( sid : String ) ( f : BufferedInputStream => T ) : Task [ T ]
2010-08-14 15:55:49 +02:00
def # > ( f : File ) : Task [ Unit ]
2010-08-22 04:55:42 +02:00
def # > ( sid : String , f : File ) : Task [ Unit ]
2010-08-14 15:55:49 +02:00
}
sealed trait TextPipe
{
2010-08-22 04:45:50 +02:00
def text [ T ] ( f : BufferedReader => T ) : Task [ T ]
2010-08-22 04:55:42 +02:00
def text [ T ] ( sid : String ) ( f : BufferedReader => T ) : Task [ T ]
2010-08-14 15:55:49 +02:00
}
sealed trait TaskLines
{
def lines : Task [ List [ String ] ]
def lines ( sid : String ) : Task [ List [ String ] ]
}
2010-08-28 01:17:03 +02:00
sealed trait ProcessPipe
{
2010-08-14 15:55:49 +02:00
def # | ( p : ProcessBuilder ) : Task [ Int ]
2010-08-22 04:55:42 +02:00
def pipe ( sid : String ) ( p : ProcessBuilder ) : Task [ Int ]
2010-08-14 15:55:49 +02:00
}
trait TaskExtra
{
2011-07-09 22:54:41 +02:00
final def nop : Task [ Unit ] = constant ( ( ) )
final def constant [ T ] ( t : T ) : Task [ T ] = task ( t )
2010-11-24 20:03:26 +01:00
2010-08-28 01:17:03 +02:00
final implicit def actionToTask [ T ] ( a : Action [ T ] ) : Task [ T ] = Task ( Info ( ) , a )
2010-08-14 15:55:49 +02:00
final def task [ T ] ( f : => T ) : Task [ T ] = toTask ( f _ )
2012-05-20 00:20:20 +02:00
final implicit def toTask [ T ] ( f : ( ) => T ) : Task [ T ] = new Pure ( f , false )
final def inlineTask [ T ] ( value : T ) : Task [ T ] = new Pure ( ( ) => value , true )
2010-08-14 15:55:49 +02:00
2012-07-31 17:52:10 +02:00
final implicit def upcastTask [ A >: B , B ] ( t : Task [ B ] ) : Task [ A ] = t map { x => x : A }
2010-08-14 15:55:49 +02:00
final implicit def toTasks [ S ] ( in : Seq [ ( ) => S ] ) : Seq [ Task [ S ] ] = in . map ( toTask )
final implicit def iterableTask [ S ] ( in : Seq [ S ] ) : ForkTask [ S , Seq ] = new ForkTask [ S , Seq ] {
2010-08-28 01:17:03 +02:00
def fork [ T ] ( f : S => T ) : Seq [ Task [ T ] ] = in . map ( x => task ( f ( x ) ) )
2011-04-16 17:24:58 +02:00
def tasks : Seq [ Task [ S ] ] = fork ( idFun )
2010-08-14 15:55:49 +02:00
}
2010-08-28 01:17:03 +02:00
2012-07-31 17:52:10 +02:00
import TaskExtra. { allM , anyFailM , existToAny , failM , successM }
final implicit def joinAnyTasks ( in : Seq [ Task [ _ ] ] ) : JoinTask [ Any , Seq ] = joinTasks [ Any ] ( existToAny ( in ) )
2011-01-19 00:24:11 +01:00
final implicit def joinTasks [ S ] ( in : Seq [ Task [ S ] ] ) : JoinTask [ S , Seq ] = new JoinTask [ S , Seq ] {
2010-08-22 04:55:42 +02:00
def join : Task [ Seq [ S ] ] = new Join ( in , ( s : Seq [ Result [ S ] ] ) => Right ( TaskExtra . all ( s ) ) )
2011-05-29 05:49:17 +02:00
def reduced ( f : ( S , S ) => S ) : Task [ S ] = TaskExtra . reduced ( in . toIndexedSeq , f )
2010-08-14 15:55:49 +02:00
}
2012-07-31 17:52:10 +02:00
final implicit def multT2Task [ A ,B ] ( in : ( Task [ A ] , Task [ B ] ) ) = multInputTask [ ( { type l [ L [ x ] ] = ( L [ A ] , L [ B ] ) } ) # l ] ( in ) ( AList . tuple2 [ A ,B ] )
final implicit def multInputTask [ K [ L [ X ] ] ] ( tasks : K [ Task ] ) ( implicit a : AList [ K ] ) : MultiInTask [ K ] = new MultiInTask [ K ] {
def flatMapR [ T ] ( f : K [ Result ] => Task [ T ] ) : Task [ T ] = new FlatMapped [ T ,K ] ( tasks , f , a )
def flatMap [ T ] ( f : K [ Id ] => Task [ T ] ) : Task [ T ] = new FlatMapped [ T , K ] ( tasks , f compose allM , a )
def flatFailure [ T ] ( f : Seq [ Incomplete ] => Task [ T ] ) : Task [ T ] = new FlatMapped [ T ,K ] ( tasks , f compose anyFailM , a )
def mapR [ T ] ( f : K [ Result ] => T ) : Task [ T ] = new Mapped [ T ,K ] ( tasks , f , a )
def map [ T ] ( f : K [ Id ] => T ) : Task [ T ] = new Mapped [ T ,K ] ( tasks , f compose allM , a )
def mapFailure [ T ] ( f : Seq [ Incomplete ] => T ) : Task [ T ] = new Mapped [ T ,K ] ( tasks , f compose anyFailM , a )
2010-08-14 15:55:49 +02:00
}
2010-08-22 04:55:42 +02:00
2011-01-29 03:14:12 +01:00
final implicit def singleInputTask [ S ] ( in : Task [ S ] ) : SingleInTask [ S ] = new SingleInTask [ S ] {
2012-07-31 17:52:10 +02:00
type K [ L [ x ] ] = L [ S ]
private def ml = AList . single [ S ]
2012-12-02 09:17:19 +01:00
def failure : Task [ Incomplete ] = mapFailure ( idFun )
def result : Task [ Result [ S ] ] = mapR ( idFun )
2010-08-14 15:55:49 +02:00
2012-07-31 17:52:10 +02:00
def flatMapR [ T ] ( f : Result [ S ] => Task [ T ] ) : Task [ T ] = new FlatMapped [ T , K ] ( in , f , ml )
def mapR [ T ] ( f : Result [ S ] => T ) : Task [ T ] = new Mapped [ T , K ] ( in , f , ml )
2011-01-19 00:24:11 +01:00
def dependsOn ( tasks : Task [ _ ] * ) : Task [ S ] = new DependsOn ( in , tasks )
2011-01-29 03:14:12 +01:00
def flatMap [ T ] ( f : S => Task [ T ] ) : Task [ T ] = flatMapR ( f compose successM )
def flatFailure [ T ] ( f : Incomplete => Task [ T ] ) : Task [ T ] = flatMapR ( f compose failM )
def map [ T ] ( f : S => T ) : Task [ T ] = mapR ( f compose successM )
def mapFailure [ T ] ( f : Incomplete => T ) : Task [ T ] = mapR ( f compose failM )
def andFinally ( fin : => Unit ) : Task [ S ] = mapR ( x => Result . tryValue [ S ] ( { fin ; x } ) )
def doFinally ( t : Task [ Unit ] ) : Task [ S ] = flatMapR ( x => t . mapR { tx => Result . tryValues [ S ] ( tx : : Nil , x ) } )
def || [ T >: S ] ( alt : Task [ T ] ) : Task [ T ] = flatMapR { case Value ( v ) => task ( v ) ; case Inc ( i ) => alt }
def && [ T ] ( alt : Task [ T ] ) : Task [ T ] = flatMap ( _ => alt )
2010-08-14 15:55:49 +02:00
}
2010-08-28 01:17:03 +02:00
2010-08-14 15:55:49 +02:00
final implicit def toTaskInfo [ S ] ( in : Task [ S ] ) : TaskInfo [ S ] = new TaskInfo [ S ] {
2010-08-22 04:55:42 +02:00
def describedAs ( s : String ) : Task [ S ] = in . copy ( info = in . info . setDescription ( s ) )
2011-01-19 00:24:11 +01:00
def named ( s : String ) : Task [ S ] = in . copy ( info = in . info . setName ( s ) )
2010-08-14 15:55:49 +02:00
}
2011-01-19 00:24:11 +01:00
final implicit def pipeToProcess [ Key ] ( t : Task [ _ ] ) ( implicit streams : Task [ TaskStreams [ Key ] ] , key : Task [ _ ] => Key ) : ProcessPipe = new ProcessPipe {
2010-08-14 15:55:49 +02:00
def # | ( p : ProcessBuilder ) : Task [ Int ] = pipe0 ( None , p )
2010-08-22 04:55:42 +02:00
def pipe ( sid : String ) ( p : ProcessBuilder ) : Task [ Int ] = pipe0 ( Some ( sid ) , p )
2010-08-14 15:55:49 +02:00
private def pipe0 ( sid : Option [ String ] , p : ProcessBuilder ) : Task [ Int ] =
2011-01-19 00:24:11 +01:00
for ( s <- streams ) yield {
val in = s . readBinary ( key ( t ) , sid )
2010-08-14 15:55:49 +02:00
val pio = TaskExtra . processIO ( s ) . withInput ( out => { BasicIO . transferFully ( in , out ) ; out . close ( ) } )
( p run pio ) . exitValue
}
}
2011-01-19 00:24:11 +01:00
final implicit def binaryPipeTask [ Key ] ( in : Task [ _ ] ) ( implicit streams : Task [ TaskStreams [ Key ] ] , key : Task [ _ ] => Key ) : BinaryPipe = new BinaryPipe {
2010-08-22 04:45:50 +02:00
def binary [ T ] ( f : BufferedInputStream => T ) : Task [ T ] = pipe0 ( None , f )
2010-08-22 04:55:42 +02:00
def binary [ T ] ( sid : String ) ( f : BufferedInputStream => T ) : Task [ T ] = pipe0 ( Some ( sid ) , f )
2010-08-14 15:55:49 +02:00
def # > ( f : File ) : Task [ Unit ] = pipe0 ( None , toFile ( f ) )
2010-08-22 04:55:42 +02:00
def # > ( sid : String , f : File ) : Task [ Unit ] = pipe0 ( Some ( sid ) , toFile ( f ) )
2010-08-14 15:55:49 +02:00
private def pipe0 [ T ] ( sid : Option [ String ] , f : BufferedInputStream => T ) : Task [ T ] =
2011-01-19 00:24:11 +01:00
streams map { s => f ( s . readBinary ( key ( in ) , sid ) ) }
2010-08-14 15:55:49 +02:00
private def toFile ( f : File ) = ( in : InputStream ) => IO . transfer ( in , f )
}
2011-01-19 00:24:11 +01:00
final implicit def textPipeTask [ Key ] ( in : Task [ _ ] ) ( implicit streams : Task [ TaskStreams [ Key ] ] , key : Task [ _ ] => Key ) : TextPipe = new TextPipe {
2010-08-22 04:45:50 +02:00
def text [ T ] ( f : BufferedReader => T ) : Task [ T ] = pipe0 ( None , f )
2010-08-22 04:55:42 +02:00
def text [ T ] ( sid : String ) ( f : BufferedReader => T ) : Task [ T ] = pipe0 ( Some ( sid ) , f )
2010-08-14 15:55:49 +02:00
private def pipe0 [ T ] ( sid : Option [ String ] , f : BufferedReader => T ) : Task [ T ] =
2011-01-19 00:24:11 +01:00
streams map { s => f ( s . readText ( key ( in ) , sid ) ) }
2010-08-14 15:55:49 +02:00
}
2011-01-19 00:24:11 +01:00
final implicit def linesTask [ Key ] ( in : Task [ _ ] ) ( implicit streams : Task [ TaskStreams [ Key ] ] , key : Task [ _ ] => Key ) : TaskLines = new TaskLines {
2010-08-14 15:55:49 +02:00
def lines : Task [ List [ String ] ] = lines0 ( None )
def lines ( sid : String ) : Task [ List [ String ] ] = lines0 ( Some ( sid ) )
private def lines0 [ T ] ( sid : Option [ String ] ) : Task [ List [ String ] ] =
2011-01-19 00:24:11 +01:00
streams map { s => IO . readLines ( s . readText ( key ( in ) , sid ) ) }
2010-08-14 15:55:49 +02:00
}
2011-01-19 00:24:11 +01:00
implicit def processToTask ( p : ProcessBuilder ) ( implicit streams : Task [ TaskStreams [ _ ] ] ) : Task [ Int ] = streams map { s =>
2010-08-14 15:55:49 +02:00
val pio = TaskExtra . processIO ( s )
( p run pio ) . exitValue
}
}
object TaskExtra extends TaskExtra
{
2011-01-19 00:24:11 +01:00
def processIO ( s : TaskStreams [ _ ] ) : ProcessIO =
2010-08-14 15:55:49 +02:00
{
def transfer ( id : String ) = ( in : InputStream ) => BasicIO . transferFully ( in , s . binary ( id ) )
2012-05-17 08:24:22 +02:00
new ProcessIO ( BasicIO . closeOut , transfer ( s . outID ) , transfer ( s . errorID ) , inheritInput = { _ => false } )
2010-08-14 15:55:49 +02:00
}
2011-05-29 05:49:17 +02:00
def reduced [ S ] ( i : IndexedSeq [ Task [ S ] ] , f : ( S , S ) => S ) : Task [ S ] =
2010-08-14 15:55:49 +02:00
i match
{
case Seq ( ) => error ( "Cannot reduce empty sequence" )
case Seq ( x ) => x
case Seq ( x , y ) => reducePair ( x , y , f )
case z =>
val ( a , b ) = i . splitAt ( i . size / 2 )
2011-05-29 05:49:17 +02:00
reducePair ( reduced ( a , f ) , reduced ( b , f ) , f )
2010-08-14 15:55:49 +02:00
}
2012-07-31 17:52:10 +02:00
2010-08-14 15:55:49 +02:00
def reducePair [ S ] ( a : Task [ S ] , b : Task [ S ] , f : ( S , S ) => S ) : Task [ S ] =
2012-07-31 17:52:10 +02:00
multInputTask [ ( { type l [ L [ x ] ] = ( L [ S ] , L [ S ] ) } ) # l ] ( ( a , b ) ) ( AList . tuple2 [ S ,S ] ) map f . tupled
2010-08-22 04:55:42 +02:00
2012-07-31 17:52:10 +02:00
def anyFailM [ K [ L [ x ] ] ] ( implicit a : AList [ K ] ) : K [ Result ] => Seq [ Incomplete ] = in =>
2010-08-22 04:55:42 +02:00
{
2012-07-31 17:52:10 +02:00
val incs = failuresM ( a ) ( in )
2010-08-22 04:55:42 +02:00
if ( incs . isEmpty ) expectedFailure else incs
}
def failM [ T ] : Result [ T ] => Incomplete = { case Inc ( i ) => i ; case x => expectedFailure }
2011-03-21 03:54:01 +01:00
def expectedFailure = throw Incomplete ( None , message = Some ( "Expected dependency to fail." ) )
2010-08-22 04:55:42 +02:00
def successM [ T ] : Result [ T ] => T = { case Inc ( i ) => throw i ; case Value ( t ) => t }
2012-07-31 17:52:10 +02:00
def allM [ K [ L [ x ] ] ] ( implicit a : AList [ K ] ) : K [ Result ] => K [ Id ] = in =>
2010-08-22 04:55:42 +02:00
{
2012-07-31 17:52:10 +02:00
val incs = failuresM ( a ) ( in )
if ( incs . isEmpty ) a . transform ( in , Result . tryValue ) else throw incompleteDeps ( incs )
2010-08-22 04:55:42 +02:00
}
2012-07-31 17:52:10 +02:00
def failuresM [ K [ L [ x ] ] ] ( implicit a : AList [ K ] ) : K [ Result ] => Seq [ Incomplete ] = x => failures [ Any ] ( a . toList ( x ) )
2010-08-22 04:55:42 +02:00
def all [ D ] ( in : Seq [ Result [ D ] ] ) =
{
val incs = failures ( in )
2011-03-21 03:54:01 +01:00
if ( incs . isEmpty ) in . map ( Result . tryValue . fn [ D ] ) else throw incompleteDeps ( incs )
2010-08-22 04:55:42 +02:00
}
def failures [ A ] ( results : Seq [ Result [ A ] ] ) : Seq [ Incomplete ] = results . collect { case Inc ( i ) => i }
2011-03-21 03:54:01 +01:00
2011-03-22 01:50:20 +01:00
def incompleteDeps ( incs : Seq [ Incomplete ] ) : Incomplete = Incomplete ( None , causes = incs )
2012-07-31 17:52:10 +02:00
private [ sbt ] def existToAny ( in : Seq [ Task [ _ ] ] ) : Seq [ Task [ Any ] ] = in . asInstanceOf [ Seq [ Task [ Any ] ] ]
2012-05-16 17:56:33 +02:00
}