summaryrefslogblamecommitdiff
path: root/main/src/mill/main/MainRunner.scala
blob: 8f546ced7494e08405e85ed8e43bf2306b0012e0 (plain) (tree)
1
2
3
4
5
6
7
8
9
                 

                                                       
                    
                                                  
                        
                      
                           
                                     
                                            
                            
                          
                 
                  
                                                                                    
 




                                                                          
                                                      

                                             
                                    
                                               
                                                             



                                           
 
                               




                                                                                     
 
                      
                                                              


                       
 




                                                                      
                                                          



                                           
                     


                                                      
                           


                           
         
 

                                   
                                                     
 
                                                            
 
                                                                                                               
 
                               
                                           


         


                                                                    
                                     



                                                   
 
                                            
                                
                                            



                                                                             
   


                                                                               
                                                                  




                                                             


                                                                         


                                                                                            
          



                                                                             
                                                                                                        

           
                                                              










                                                                                   
 
package mill.main
import java.io.{InputStream, OutputStream, PrintStream}

import ammonite.Main
import ammonite.interp.{Interpreter, Preprocessor}
import ammonite.ops.Path
import ammonite.util._
import mill.define.Discover
import mill.eval.{Evaluator, PathRef}
import mill.main.MainRunner.WatchInterrupted
import mill.util.PrintLogger
import mill.main.RunScript
import upickle.Js
object MainRunner{
  case class WatchInterrupted(stateCache: Option[Evaluator.State]) extends Exception
}
/**
  * Customized version of [[ammonite.MainRunner]], allowing us to run Mill
  * `build.sc` scripts with mill-specific tweaks such as a custom
  * `scriptCodeWrapper` or with a persistent evaluator between runs.
  */
class MainRunner(val config: ammonite.main.Cli.Config,
                 outprintStream: PrintStream,
                 errPrintStream: PrintStream,
                 stdIn: InputStream,
                 interruptWatch: () => Boolean,
                 stateCache0: Option[Evaluator.State] = None)
  extends ammonite.MainRunner(
    config, outprintStream, errPrintStream,
    stdIn, outprintStream, errPrintStream
  ){

  var stateCache  = stateCache0
  override def watchAndWait(watched: Seq[(Path, Long)]) = {
    printInfo(s"Watching for changes to ${watched.length} files... (Ctrl-C to exit)")
    def statAll() = watched.forall{ case (file, lastMTime) =>
      Interpreter.pathSignature(file) == lastMTime
    }

    while(statAll()) {
      if (interruptWatch()) throw WatchInterrupted(stateCache)
      Thread.sleep(100)
    }
  }

  override def runScript(scriptPath: Path, scriptArgs: List[String]) =
    watchLoop(
      isRepl = false,
      printing = true,
      mainCfg => {
        val (result, interpWatched) = RunScript.runScript(
          mainCfg.wd,
          scriptPath,
          mainCfg.instantiateInterpreter(),
          scriptArgs,
          stateCache,
          new PrintLogger(
            colors != ammonite.util.Colors.BlackWhite,
            colors,
            outprintStream,
            errPrintStream,
            errPrintStream
          )
        )

        result match{
          case Res.Success(data) =>
            val (eval, evaluationWatches, res) = data

            val watched = interpWatched ++ evaluationWatches

            stateCache = Some(Evaluator.State(eval.rootModule, eval.classLoaderSig, eval.workerCache, watched))

            (Res(res), watched)
          case _ => (result, interpWatched)
        }
      }
    )

  override def handleWatchRes[T](res: Res[T], printing: Boolean) = {
    res match{
      case Res.Success(value) => true
      case _ => super.handleWatchRes(res, printing)
    }

  }

  override def initMain(isRepl: Boolean) = {
    super.initMain(isRepl).copy(
      scriptCodeWrapper = CustomCodeWrapper,
      // Ammonite does not properly forward the wd from CliConfig to Main, so
      // force forward it outselves
      wd = config.wd
    )
  }
  object CustomCodeWrapper extends Preprocessor.CodeWrapper {
    def top(pkgName: Seq[Name], imports: Imports, indexedWrapperName: Name) = {
      val wrapName = indexedWrapperName.backticked
      val literalPath = pprint.Util.literalize(config.wd.toString)
      s"""
         |package ${pkgName.head.encoded}
         |package ${Util.encodeScalaSourcePath(pkgName.tail)}
         |$imports
         |import mill._
         |object $wrapName
         |extends mill.define.BaseModule(ammonite.ops.Path($literalPath))
         |with $wrapName{
         |  // Stub to make sure Ammonite has something to call after it evaluates a script,
         |  // even if it does nothing...
         |  def $$main() = Iterator[String]()
         |
         |  // Need to wrap the returned Module in Some(...) to make sure it
         |  // doesn't get picked up during reflective child-module discovery
         |  def millSelf = Some(this)
         |
         |  implicit def millDiscover: mill.define.Discover[this.type] = mill.define.Discover[this.type]
         |}
         |
         |sealed trait $wrapName extends mill.main.MainModule{
         |""".stripMargin
    }


    def bottom(printCode: String, indexedWrapperName: Name, extraCode: String) = {
      // We need to disable the `$main` method definition inside the wrapper class,
      // because otherwise it might get picked up by Ammonite and run as a static
      // class method, which blows up since it's defined as an instance method
      "\n}"
    }
  }
}