src/Pure/Tools/build.scala
changeset 50845 477ca927676f
parent 50777 20126dd9772c
child 50846 529e652d389d
equal deleted inserted replaced
50844:b95ff3744815 50845:477ca927676f
    20 object Build
    20 object Build
    21 {
    21 {
    22   /** progress context **/
    22   /** progress context **/
    23 
    23 
    24   class Progress {
    24   class Progress {
       
    25     def theory(name: String) {}
    25     def echo(msg: String) {}
    26     def echo(msg: String) {}
    26     def stopped: Boolean = false
    27     def stopped: Boolean = false
    27   }
    28   }
    28 
    29 
    29   object Ignore_Progress extends Progress
    30   object Ignore_Progress extends Progress
   410     session_content(false, Nil, session).check_errors.syntax
   411     session_content(false, Nil, session).check_errors.syntax
   411 
   412 
   412 
   413 
   413   /* jobs */
   414   /* jobs */
   414 
   415 
   415   private class Job(name: String, val info: Session_Info, output: Path, do_output: Boolean,
   416   private class Job(progress: Build.Progress,
       
   417     name: String, val info: Session_Info, output: Path, do_output: Boolean,
   416     verbose: Boolean, browser_info: Path)
   418     verbose: Boolean, browser_info: Path)
   417   {
   419   {
   418     // global browser info dir
   420     // global browser info dir
   419     if (info.options.bool("browser_info") && !(browser_info + Path.explode("index.html")).is_file)
   421     if (info.options.bool("browser_info") && !(browser_info + Path.explode("index.html")).is_file)
   420     {
   422     {
   477         exit "$RC"
   479         exit "$RC"
   478         """
   480         """
   479       }
   481       }
   480 
   482 
   481     private val (thread, result) =
   483     private val (thread, result) =
   482       Simple_Thread.future("build") { Isabelle_System.bash_env(info.dir.file, env, script) }
   484       Simple_Thread.future("build") {
       
   485         Isabelle_System.bash_env(info.dir.file, env, script,
       
   486           out_progress = (line: String) =>
       
   487             if (line.startsWith(LOADING_THEORY))
       
   488               progress.theory(line.substring(LOADING_THEORY.length)))
       
   489       }
   483 
   490 
   484     def terminate: Unit = thread.interrupt
   491     def terminate: Unit = thread.interrupt
   485     def is_finished: Boolean = result.is_finished
   492     def is_finished: Boolean = result.is_finished
   486 
   493 
   487     @volatile private var timeout = false
   494     @volatile private var timeout = false
   492         t.schedule(new TimerTask { def run = { terminate; timeout = true } }, time.ms)
   499         t.schedule(new TimerTask { def run = { terminate; timeout = true } }, time.ms)
   493         Some(t)
   500         Some(t)
   494       }
   501       }
   495       else None
   502       else None
   496 
   503 
   497     def join: (String, String, Int) = {
   504     def join: Isabelle_System.Bash_Result =
   498       val (out, err, rc) = result.join
   505     {
       
   506       val res = result.join
       
   507 
   499       args_file.delete
   508       args_file.delete
   500       timer.map(_.cancel())
   509       timer.map(_.cancel())
   501 
   510 
   502       val err1 =
   511       if (res.rc == 130)
   503         if (rc == 130)
   512         res.add_err(if (timeout) "*** Timeout" else "*** Interrupt")
   504           (if (err.isEmpty || err.endsWith("\n")) err else err + "\n") +
   513       else res
   505           (if (timeout) "*** Timeout\n" else "*** Interrupt\n")
       
   506         else err
       
   507       (out, err1, rc)
       
   508     }
   514     }
   509   }
   515   }
   510 
   516 
   511 
   517 
   512   /* log files */
   518   /* log files */
   514   private val LOG = Path.explode("log")
   520   private val LOG = Path.explode("log")
   515   private def log(name: String): Path = LOG + Path.basic(name)
   521   private def log(name: String): Path = LOG + Path.basic(name)
   516   private def log_gz(name: String): Path = log(name).ext("gz")
   522   private def log_gz(name: String): Path = log(name).ext("gz")
   517 
   523 
   518   private val SESSION_PARENT_PATH = "\fSession.parent_path = "
   524   private val SESSION_PARENT_PATH = "\fSession.parent_path = "
       
   525   private val LOADING_THEORY = "\floading_theory = "
   519 
   526 
   520   sealed case class Log_Info(stats: List[Properties.T], timing: Properties.T)
   527   sealed case class Log_Info(stats: List[Properties.T], timing: Properties.T)
   521 
   528 
   522   def parse_log(text: String): Log_Info =
   529   def parse_log(text: String): Log_Info =
   523   {
   530   {
   632       else
   639       else
   633         running.find({ case (_, (_, job)) => job.is_finished }) match {
   640         running.find({ case (_, (_, job)) => job.is_finished }) match {
   634           case Some((name, (parent_heap, job))) =>
   641           case Some((name, (parent_heap, job))) =>
   635             //{{{ finish job
   642             //{{{ finish job
   636 
   643 
   637             val (out, err, rc) = job.join
   644             val res = job.join
   638             val out_lines = split_lines(out)
   645             progress.echo(res.err)
   639             progress.echo(Library.trim_line(err))
       
   640 
   646 
   641             val (parent_path, heap) =
   647             val (parent_path, heap) =
   642               if (rc == 0) {
   648               if (res.rc == 0) {
   643                 (output_dir + log(name)).file.delete
   649                 (output_dir + log(name)).file.delete
   644 
   650 
   645                 val sources = make_stamp(name)
   651                 val sources = make_stamp(name)
   646                 val heap = heap_stamp(job.output_path)
   652                 val heap = heap_stamp(job.output_path)
   647                 File.write_gzip(output_dir + log_gz(name),
   653                 File.write_gzip(output_dir + log_gz(name),
   648                   sources + "\n" + parent_heap + "\n" + heap + "\n" + out)
   654                   sources + "\n" + parent_heap + "\n" + heap + "\n" + res.out)
   649 
   655 
   650                 val parent_path =
   656                 val parent_path =
   651                   if (job.info.options.bool("browser_info"))
   657                   if (job.info.options.bool("browser_info"))
   652                     out_lines.find(_.startsWith(SESSION_PARENT_PATH)).map(line =>
   658                     res.out_lines.find(_.startsWith(SESSION_PARENT_PATH)).map(line =>
   653                       line.substring(SESSION_PARENT_PATH.length))
   659                       line.substring(SESSION_PARENT_PATH.length))
   654                   else None
   660                   else None
   655 
   661 
   656                 (parent_path, heap)
   662                 (parent_path, heap)
   657               }
   663               }
   658               else {
   664               else {
   659                 (output_dir + Path.basic(name)).file.delete
   665                 (output_dir + Path.basic(name)).file.delete
   660                 (output_dir + log_gz(name)).file.delete
   666                 (output_dir + log_gz(name)).file.delete
   661 
   667 
   662                 File.write(output_dir + log(name), out)
   668                 File.write(output_dir + log(name), res.out)
   663                 progress.echo(name + " FAILED")
   669                 progress.echo(name + " FAILED")
   664                 if (rc != 130) {
   670                 if (res.rc != 130) {
   665                   progress.echo("(see also " + (output_dir + log(name)).file.toString + ")")
   671                   progress.echo("(see also " + (output_dir + log(name)).file.toString + ")")
   666                   val lines = out_lines.filterNot(_.startsWith("\f"))
   672                   val lines = res.out_lines.filterNot(_.startsWith("\f"))
   667                   val tail = lines.drop(lines.length - 20 max 0)
   673                   val tail = lines.drop(lines.length - 20 max 0)
   668                   progress.echo("\n" + cat_lines(tail))
   674                   progress.echo("\n" + cat_lines(tail))
   669                 }
   675                 }
   670 
   676 
   671                 (None, no_heap)
   677                 (None, no_heap)
   672               }
   678               }
   673             loop(pending - name, running - name,
   679             loop(pending - name, running - name,
   674               results + (name -> Result(false, parent_path, heap, rc)))
   680               results + (name -> Result(false, parent_path, heap, res.rc)))
   675             //}}}
   681             //}}}
   676           case None if (running.size < (max_jobs max 1)) =>
   682           case None if (running.size < (max_jobs max 1)) =>
   677             //{{{ check/start next job
   683             //{{{ check/start next job
   678             pending.dequeue(running.isDefinedAt(_)) match {
   684             pending.dequeue(running.isDefinedAt(_)) match {
   679               case Some((name, info)) =>
   685               case Some((name, info)) =>
   707                   if (verbose) progress.echo("Skipping " + name + " ...")
   713                   if (verbose) progress.echo("Skipping " + name + " ...")
   708                   loop(pending - name, running, results + (name -> Result(false, None, heap, 1)))
   714                   loop(pending - name, running, results + (name -> Result(false, None, heap, 1)))
   709                 }
   715                 }
   710                 else if (parent_result.rc == 0) {
   716                 else if (parent_result.rc == 0) {
   711                   progress.echo((if (do_output) "Building " else "Running ") + name + " ...")
   717                   progress.echo((if (do_output) "Building " else "Running ") + name + " ...")
   712                   val job = new Job(name, info, output, do_output, verbose, browser_info)
   718                   val job = new Job(progress, name, info, output, do_output, verbose, browser_info)
   713                   loop(pending, running + (name -> (parent_result.heap, job)), results)
   719                   loop(pending, running + (name -> (parent_result.heap, job)), results)
   714                 }
   720                 }
   715                 else {
   721                 else {
   716                   progress.echo(name + " CANCELLED")
   722                   progress.echo(name + " CANCELLED")
   717                   loop(pending - name, running, results + (name -> Result(false, None, heap, 1)))
   723                   loop(pending - name, running, results + (name -> Result(false, None, heap, 1)))