summaryrefslogblamecommitdiff
path: root/main/src/main/MainModule.scala
blob: 400141695922882d02b17601e18f727048205d71 (plain) (tree)
1
2
3
4
5
6
7
8
9

                 

                                               
             
                                    
                                             
                                            
                                   
                  
                                                                                       
                                                       
                                                                                          



                                                   
                                                                                        
                                                                   


                                                                                         


                                            


     



                                                    
                                                                      
                                                                             



                            




                                                


                                    


                                                                           
                                                                       


                                                                       
 
                   
                                           
                       
                             


                          
     
   
 



                                                                               
                                                                    

                                    


                                                           




                                           
                                                                   
                           
                                                                    
             
                                 
                       

                                                                        








                                                                               
                                                                             
































                                                                            
         


     


                                                                         
                                                                       
                                                                             
                                    













                                                                                              
                          
       

                     

     
 




                                           
                                                                   

                                                                            
     
   
 



                                                                            
                                                                    
                             
                     

                                                                               
                                  
                                                                                                 


                     

                         

                                     
                                        
       
     
   




                                                                            
                                                                      



                                            
                                                                        


















                                                                           
                                    



                          



                                                                                  
                                                                         


                                                                               


                                                                                            
                                                                             
                                     
                                           


                                                                                                             
     
   
 







                                                    
                                                                                       

                                                    
                                                                                                    
                                                                          





                                                                                      


                                                                    

                                           

                                        




                                                
 
package mill.main

import java.util.concurrent.LinkedBlockingQueue

import mill.T
import mill.define.{NamedTask, Task}
import mill.eval.{Evaluator, PathRef, Result}
import mill.util.{Ctx, PrintLogger, Watched}
import pprint.{Renderer, Truncated}
object MainModule{
  def resolveTasks[T](evaluator: Evaluator, targets: Seq[String], multiSelect: Boolean)
                     (f: List[NamedTask[Any]] => T) = {
    RunScript.resolveTasks(mill.main.ResolveTasks, evaluator, targets, multiSelect) match{
      case Left(err) => Result.Failure(err)
      case Right(tasks) => Result.Success(f(tasks))
    }
  }
  def evaluateTasks[T](evaluator: Evaluator, targets: Seq[String], multiSelect: Boolean)
                      (f: Seq[(Any, Option[ujson.Value])] => T) = {
    RunScript.evaluateTasks(evaluator, targets, multiSelect) match{
      case Left(err) => Result.Failure(err)
      case Right((watched, Left(err))) => Result.Failure(err, Some(Watched((), watched)))
      case Right((watched, Right(res))) =>
        f(res)
        Result.Success(Watched((), watched))
    }
  }
}

trait MainModule extends mill.Module{

  implicit def millDiscover: mill.define.Discover[_]
  implicit def millScoptTasksReads[T] = new mill.main.Tasks.Scopt[T]()
  implicit def millScoptEvaluatorReads[T] = new mill.main.EvaluatorScopt[T]()

  /**
    * Show the mill version.
    */
  def version() = mill.T.command {
    val res = System.getProperty("MILL_VERSION")
    println(res)
    res
  }

  private val OutDir: String = "out"

  /**
    * Resolves a mill query string and prints out the tasks it resolves to.
    */
  def resolve(evaluator: Evaluator, targets: String*) = mill.T.command{
    val resolved = RunScript.resolveTasks(
      mill.main.ResolveMetadata, evaluator, targets, multiSelect = true
    )

    resolved match{
      case Left(err) => Result.Failure(err)
      case Right(rs) =>
        for(r <- rs.sorted) {
          println(r)
        }
        Result.Success(())
    }
  }

  /**
    * Given a set of tasks, prints out the execution plan of what tasks will be
    * executed in what order, without actually executing them.
    */
  def plan(evaluator: Evaluator, targets: String*) = mill.T.command{
    plan0(evaluator, targets) match{
      case Right(success) => {
        val renderedTasks = success.map{ _.segments.render}
        renderedTasks.foreach(println)
        Result.Success(renderedTasks)
      }
      case Left(err) => Result.Failure(err)
    }
  }

  private def plan0(evaluator: Evaluator, targets: Seq[String]) = {
    RunScript.resolveTasks(
      mill.main.ResolveTasks, evaluator, targets, multiSelect = true
    ) match {
      case Left(err) => Left(err)
      case Right(rs) =>
        val (sortedGroups, _) = Evaluator.plan(evaluator.rootModule, rs)
        Right(sortedGroups.keys().collect{ case Right(r) => r}.toArray)
    }
  }

  /**
    * Prints out some dependency path from the `src` task to the `dest` task.
    *
    * If there are multiple dependency paths between `src` and `dest`, the path
    * chosen is arbitrary.
    */
  def path(evaluator: Evaluator, src: String, dest: String) = mill.T.command{
    val resolved = RunScript.resolveTasks(
      mill.main.ResolveTasks, evaluator, List(src, dest), multiSelect = true
    )

    resolved match{
      case Left(err) => Result.Failure(err)
      case Right(Seq(src1, dest1)) =>
        val queue = collection.mutable.Queue[List[Task[_]]](List(src1))
        var found = Option.empty[List[Task[_]]]
        val seen = collection.mutable.Set.empty[Task[_]]
        while(queue.nonEmpty && found.isEmpty){
          val current = queue.dequeue()
          if (current.head == dest1) found = Some(current)
          else{
            for{
              next <- current.head.inputs
              if !seen.contains(next)
            }{
              seen.add(next)
              queue.enqueue(next :: current)
            }
          }
        }
        found match{
          case None =>
            Result.Failure(s"No path found between $src and $dest")
          case Some(list) =>
            val labels = list
              .collect{case n: NamedTask[_] => n.ctx.segments.render}

            labels.foreach(mill.T.ctx().log.outputStream.println(_))

            Result.Success(labels)
        }
    }
  }

  /**
    * Displays metadata about the given task without actually running it.
    */
  def inspect(evaluator: Evaluator, targets: String*) = mill.T.command{
    MainModule.resolveTasks(evaluator, targets, multiSelect = true){ tasks =>
      val output = new StringBuilder
      for{
        task <- tasks
        tree = ReplApplyHandler.pprintTask(task, evaluator)
        val defaults = pprint.PPrinter()
        val renderer = new Renderer(
          defaults.defaultWidth,
          defaults.colorApplyPrefix,
          defaults.colorLiteral,
          defaults.defaultIndent
        )
        val rendered = renderer.rec(tree, 0, 0).iter
        val truncated = new Truncated(rendered, defaults.defaultWidth, defaults.defaultHeight)
        str <- truncated ++ Iterator("\n")
      } {
        output.append(str)
      }
      println(output)
      output.toString
    }
  }

  /**
    * Runs multiple tasks in a single call.
    *
    *
    */
  def all(evaluator: Evaluator, targets: String*) = mill.T.command{
    MainModule.evaluateTasks(evaluator, targets, multiSelect = true) {res =>
      res.flatMap(_._2)
    }
  }

  /**
    * Runs a given task and prints the JSON result to stdout. This is useful
    * to integrate Mill into external scripts and tooling.
    */
  def show(evaluator: Evaluator, targets: String*) = mill.T.command{
    MainModule.evaluateTasks(
      evaluator.copy(
        // When using `show`, redirect all stdout of the evaluated tasks so the
        // printed JSON is the only thing printed to stdout.
        log = evaluator.log match{
          case PrintLogger(c1, d, c2, o, i, e, in, de) => PrintLogger(c1, d, c2, e, i, e, in, de)
          case l => l
        }
      ),
      targets,
      multiSelect = false
    ) {res =>
      for(json <- res.flatMap(_._2)){
        println(json.render(indent = 4))
      }
    }
  }

  /**
    * Deletes the given targets from the out directory. Providing no targets
    * will clean everything.
    */
  def clean(evaluator: Evaluator, targets: String*) = mill.T.command {
    val rootDir = ammonite.ops.pwd / OutDir

    val KeepPattern = "(mill-.+)".r.anchored

    def keepPath(path: os.Path) = path.segments.toSeq.lastOption match {
      case Some(KeepPattern(_)) => true
      case _ => false
    }

    val pathsToRemove =
      if (targets.isEmpty)
        Right(ammonite.ops.ls(rootDir).filterNot(keepPath))
      else
        RunScript.resolveTasks(
          mill.main.ResolveSegments, evaluator, targets, multiSelect = true
        ).map(
          _.map { segments =>
            Evaluator.resolveDestPaths(rootDir, segments).out
          })

    pathsToRemove match {
      case Left(err) =>
        Result.Failure(err)
      case Right(paths) =>
        paths.foreach(os.remove.all)
        Result.Success(())
    }
  }


  /**
    * Renders the dependencies between the given tasks as a SVG for you to look at
    */
  def visualize(evaluator: Evaluator, targets: String*) = mill.T.command{
    visualize0(evaluator, targets, T.ctx(), mill.main.VisualizeModule.worker())
  }

  /**
    * Renders the dependencies between the given tasks, and all their dependencies, as a SVG
    */
  def visualizePlan(evaluator: Evaluator, targets: String*) = mill.T.command{
    plan0(evaluator, targets) match {
      case Left(err) => Result.Failure(err)
      case Right(planResults) => visualize0(
        evaluator, targets, T.ctx(), mill.main.VisualizeModule.worker(), Some(planResults.toList.map(_.task))
      )
    }
  }

  /**
    * Shuts down mill's background server
    */
  def shutdown() = mill.T.command {
    T.ctx().log.info("Shutting down Mill server...")
    System.exit(0)
  }

  private type VizWorker = (LinkedBlockingQueue[(scala.Seq[_], scala.Seq[_], os.Path)],
    LinkedBlockingQueue[Result[scala.Seq[PathRef]]])

  private def visualize0(evaluator: Evaluator, targets: Seq[String], ctx: Ctx, vizWorker: VizWorker,
                         planTasks: Option[List[NamedTask[_]]] = None) = {
    def callVisualizeModule(rs: List[NamedTask[Any]], allRs: List[NamedTask[Any]]) = {
      val (in, out) = vizWorker
      in.put((rs, allRs, ctx.dest))
      out.take()
    }

    RunScript.resolveTasks(
      mill.main.ResolveTasks, evaluator, targets, multiSelect = true
    ) match {
      case Left(err) => Result.Failure(err)
      case Right(rs) => planTasks match {
        case Some(allRs) => {
          callVisualizeModule(rs, allRs)
        }
        case None => callVisualizeModule(rs, rs)
      }
    }
  }
}