immediate theory progress for build_dialog;
authorwenzelm
Sat, 12 Jan 2013 15:00:48 +0100
changeset 50845 477ca927676f
parent 50844 b95ff3744815
child 50846 529e652d389d
immediate theory progress for build_dialog; more formal Bash_Result -- accumulate output as lines;
src/Pure/General/file.scala
src/Pure/PIDE/markup.ML
src/Pure/System/isabelle_system.scala
src/Pure/System/system_channel.scala
src/Pure/Thy/thy_info.ML
src/Pure/Tools/build.ML
src/Pure/Tools/build.scala
src/Pure/Tools/build_dialog.scala
src/Pure/library.scala
--- a/src/Pure/General/file.scala	Sat Jan 12 14:56:57 2013 +0100
+++ b/src/Pure/General/file.scala	Sat Jan 12 15:00:48 2013 +0100
@@ -75,6 +75,21 @@
   def read_gzip(path: Path): String = read_gzip(path.file)
 
 
+  /* read lines */
+
+  def read_lines(reader: BufferedReader, progress: String => Unit): List[String] =
+  {
+    val result = new mutable.ListBuffer[String]
+    var line: String = null
+    while ({ line = reader.readLine; line != null }) {
+      progress(line)
+      result += line
+    }
+    reader.close
+    result.toList
+  }
+
+
   /* try_read */
 
   def try_read(paths: Seq[Path]): String =
--- a/src/Pure/PIDE/markup.ML	Sat Jan 12 14:56:57 2013 +0100
+++ b/src/Pure/PIDE/markup.ML	Sat Jan 12 15:00:48 2013 +0100
@@ -133,6 +133,8 @@
   val invoke_scala: string -> string -> Properties.T
   val cancel_scala: string -> Properties.T
   val ML_statistics: Properties.entry
+  val loading_theory: string -> Properties.T
+  val dest_loading_theory: Properties.T -> string option
   val no_output: Output.output * Output.output
   val default_output: T -> Output.output * Output.output
   val add_mode: string -> (T -> Output.output * Output.output) -> unit
@@ -408,6 +410,11 @@
 
 val ML_statistics = (functionN, "ML_statistics");
 
+fun loading_theory name = [("function", "loading_theory"), ("name", name)];
+
+fun dest_loading_theory [("function", "loading_theory"), ("name", name)] = SOME name
+  | dest_loading_theory _ = NONE;
+
 
 
 (** print mode operations **)
--- a/src/Pure/System/isabelle_system.scala	Sat Jan 12 14:56:57 2013 +0100
+++ b/src/Pure/System/isabelle_system.scala	Sat Jan 12 15:00:48 2013 +0100
@@ -16,7 +16,6 @@
 
 import scala.io.Source
 import scala.util.matching.Regex
-import scala.collection.mutable
 
 
 object Isabelle_System
@@ -331,24 +330,35 @@
 
   /* bash */
 
-  def bash_env(cwd: JFile, env: Map[String, String], script: String): (String, String, Int) =
+  final case class Bash_Result(out_lines: List[String], err_lines: List[String], rc: Int)
+  {
+    def out: String = cat_lines(out_lines)
+    def err: String = cat_lines(err_lines)
+    def add_err(s: String): Bash_Result = copy(err_lines = err_lines ::: List(s))
+  }
+
+  def bash_env(cwd: JFile, env: Map[String, String], script: String,
+    out_progress: String => Unit = (_: String) => (),
+    err_progress: String => Unit = (_: String) => ()): Bash_Result =
   {
     File.with_tmp_file("isabelle_script") { script_file =>
       File.write(script_file, script)
       val proc = new Managed_Process(cwd, env, false, "bash", posix_path(script_file))
 
       proc.stdin.close
-      val (_, stdout) = Simple_Thread.future("bash_stdout") { File.read_stream(proc.stdout) }
-      val (_, stderr) = Simple_Thread.future("bash_stderr") { File.read_stream(proc.stderr) }
+      val (_, stdout) =
+        Simple_Thread.future("bash_stdout") { File.read_lines(proc.stdout, out_progress) }
+      val (_, stderr) =
+        Simple_Thread.future("bash_stderr") { File.read_lines(proc.stderr, err_progress) }
 
       val rc =
         try { proc.join }
         catch { case e: InterruptedException => Thread.interrupted; proc.terminate; 130 }
-      (stdout.join, stderr.join, rc)
+      Bash_Result(stdout.join, stderr.join, rc)
     }
   }
 
-  def bash(script: String): (String, String, Int) = bash_env(null, null, script)
+  def bash(script: String): Bash_Result = bash_env(null, null, script)
 
 
   /* system tools */
--- a/src/Pure/System/system_channel.scala	Sat Jan 12 14:56:57 2013 +0100
+++ b/src/Pure/System/system_channel.scala	Sat Jan 12 15:00:48 2013 +0100
@@ -45,8 +45,8 @@
       "FIFO=\"/tmp/isabelle-fifo-${PPID}-$$" + i + "\"\n" +
       "echo -n \"$FIFO\"\n" +
       "mkfifo -m 600 \"$FIFO\"\n"
-    val (out, err, rc) = Isabelle_System.bash(script)
-    if (rc == 0) out else error(err.trim)
+    val result = Isabelle_System.bash(script)
+    if (result.rc == 0) result.out else error(result.err)
   }
 
   private def rm_fifo(fifo: String): Boolean = (new JFile(fifo)).delete
--- a/src/Pure/Thy/thy_info.ML	Sat Jan 12 14:56:57 2013 +0100
+++ b/src/Pure/Thy/thy_info.ML	Sat Jan 12 15:00:48 2013 +0100
@@ -224,6 +224,7 @@
   let
     val _ = kill_thy name;
     val _ = Output.urgent_message ("Loading theory " ^ quote name ^ required_by " " initiators);
+    val _ = Output.protocol_message (Markup.loading_theory name) "" handle Fail _ => ();
 
     val {master = (thy_path, _), imports} = deps;
     val dir = Path.dir thy_path;
--- a/src/Pure/Tools/build.ML	Sat Jan 12 14:56:57 2013 +0100
+++ b/src/Pure/Tools/build.ML	Sat Jan 12 15:00:48 2013 +0100
@@ -22,7 +22,10 @@
 fun protocol_message props output =
   (case ML_statistics props output of
     SOME stats => writeln ("\fML_statistics = " ^ ML_Syntax.print_properties stats)
-  | NONE => raise Fail "Undefined Output.protocol_message");
+  | NONE =>
+      (case Markup.dest_loading_theory props of
+        SOME name => writeln ("\floading_theory = " ^ name)
+      | NONE => raise Fail "Undefined Output.protocol_message"));
 
 
 (* build *)
--- a/src/Pure/Tools/build.scala	Sat Jan 12 14:56:57 2013 +0100
+++ b/src/Pure/Tools/build.scala	Sat Jan 12 15:00:48 2013 +0100
@@ -22,6 +22,7 @@
   /** progress context **/
 
   class Progress {
+    def theory(name: String) {}
     def echo(msg: String) {}
     def stopped: Boolean = false
   }
@@ -412,7 +413,8 @@
 
   /* jobs */
 
-  private class Job(name: String, val info: Session_Info, output: Path, do_output: Boolean,
+  private class Job(progress: Build.Progress,
+    name: String, val info: Session_Info, output: Path, do_output: Boolean,
     verbose: Boolean, browser_info: Path)
   {
     // global browser info dir
@@ -479,7 +481,12 @@
       }
 
     private val (thread, result) =
-      Simple_Thread.future("build") { Isabelle_System.bash_env(info.dir.file, env, script) }
+      Simple_Thread.future("build") {
+        Isabelle_System.bash_env(info.dir.file, env, script,
+          out_progress = (line: String) =>
+            if (line.startsWith(LOADING_THEORY))
+              progress.theory(line.substring(LOADING_THEORY.length)))
+      }
 
     def terminate: Unit = thread.interrupt
     def is_finished: Boolean = result.is_finished
@@ -494,17 +501,16 @@
       }
       else None
 
-    def join: (String, String, Int) = {
-      val (out, err, rc) = result.join
+    def join: Isabelle_System.Bash_Result =
+    {
+      val res = result.join
+
       args_file.delete
       timer.map(_.cancel())
 
-      val err1 =
-        if (rc == 130)
-          (if (err.isEmpty || err.endsWith("\n")) err else err + "\n") +
-          (if (timeout) "*** Timeout\n" else "*** Interrupt\n")
-        else err
-      (out, err1, rc)
+      if (res.rc == 130)
+        res.add_err(if (timeout) "*** Timeout" else "*** Interrupt")
+      else res
     }
   }
 
@@ -516,6 +522,7 @@
   private def log_gz(name: String): Path = log(name).ext("gz")
 
   private val SESSION_PARENT_PATH = "\fSession.parent_path = "
+  private val LOADING_THEORY = "\floading_theory = "
 
   sealed case class Log_Info(stats: List[Properties.T], timing: Properties.T)
 
@@ -634,22 +641,21 @@
           case Some((name, (parent_heap, job))) =>
             //{{{ finish job
 
-            val (out, err, rc) = job.join
-            val out_lines = split_lines(out)
-            progress.echo(Library.trim_line(err))
+            val res = job.join
+            progress.echo(res.err)
 
             val (parent_path, heap) =
-              if (rc == 0) {
+              if (res.rc == 0) {
                 (output_dir + log(name)).file.delete
 
                 val sources = make_stamp(name)
                 val heap = heap_stamp(job.output_path)
                 File.write_gzip(output_dir + log_gz(name),
-                  sources + "\n" + parent_heap + "\n" + heap + "\n" + out)
+                  sources + "\n" + parent_heap + "\n" + heap + "\n" + res.out)
 
                 val parent_path =
                   if (job.info.options.bool("browser_info"))
-                    out_lines.find(_.startsWith(SESSION_PARENT_PATH)).map(line =>
+                    res.out_lines.find(_.startsWith(SESSION_PARENT_PATH)).map(line =>
                       line.substring(SESSION_PARENT_PATH.length))
                   else None
 
@@ -659,11 +665,11 @@
                 (output_dir + Path.basic(name)).file.delete
                 (output_dir + log_gz(name)).file.delete
 
-                File.write(output_dir + log(name), out)
+                File.write(output_dir + log(name), res.out)
                 progress.echo(name + " FAILED")
-                if (rc != 130) {
+                if (res.rc != 130) {
                   progress.echo("(see also " + (output_dir + log(name)).file.toString + ")")
-                  val lines = out_lines.filterNot(_.startsWith("\f"))
+                  val lines = res.out_lines.filterNot(_.startsWith("\f"))
                   val tail = lines.drop(lines.length - 20 max 0)
                   progress.echo("\n" + cat_lines(tail))
                 }
@@ -671,7 +677,7 @@
                 (None, no_heap)
               }
             loop(pending - name, running - name,
-              results + (name -> Result(false, parent_path, heap, rc)))
+              results + (name -> Result(false, parent_path, heap, res.rc)))
             //}}}
           case None if (running.size < (max_jobs max 1)) =>
             //{{{ check/start next job
@@ -709,7 +715,7 @@
                 }
                 else if (parent_result.rc == 0) {
                   progress.echo((if (do_output) "Building " else "Running ") + name + " ...")
-                  val job = new Job(name, info, output, do_output, verbose, browser_info)
+                  val job = new Job(progress, name, info, output, do_output, verbose, browser_info)
                   loop(pending, running + (name -> (parent_result.heap, job)), results)
                 }
                 else {
--- a/src/Pure/Tools/build_dialog.scala	Sat Jan 12 14:56:57 2013 +0100
+++ b/src/Pure/Tools/build_dialog.scala	Sat Jan 12 15:00:48 2013 +0100
@@ -83,6 +83,7 @@
 
     val progress = new Build.Progress
     {
+      override def theory(name: String): Unit = echo("  theory " + name)
       override def echo(msg: String): Unit = Swing_Thread.now { text.append(msg + "\n") }
       override def stopped: Boolean =
         Swing_Thread.now { val b = is_stopped; is_stopped = false; b  }
--- a/src/Pure/library.scala	Sat Jan 12 14:56:57 2013 +0100
+++ b/src/Pure/library.scala	Sat Jan 12 15:00:48 2013 +0100
@@ -82,10 +82,6 @@
     else ""
   }
 
-  def trim_line(str: String): String =
-    if (str.endsWith("\n")) str.substring(0, str.length - 1)
-    else str
-
   def lowercase(str: String): String = str.toLowerCase(Locale.ENGLISH)
   def uppercase(str: String): String = str.toUpperCase(Locale.ENGLISH)