src/Pure/System/isabelle_system.scala
changeset 39583 c1e9c6dfeff8
parent 39581 430ff865089b
child 39584 f2a10986e85a
equal deleted inserted replaced
39582:a873158542d0 39583:c1e9c6dfeff8
     7 package isabelle
     7 package isabelle
     8 
     8 
     9 import java.util.regex.Pattern
     9 import java.util.regex.Pattern
    10 import java.util.Locale
    10 import java.util.Locale
    11 import java.io.{InputStream, FileInputStream, OutputStream, FileOutputStream, File,
    11 import java.io.{InputStream, FileInputStream, OutputStream, FileOutputStream, File,
    12   BufferedReader, InputStreamReader, IOException}
    12   BufferedReader, InputStreamReader, BufferedWriter, OutputStreamWriter, IOException}
    13 import java.awt.{GraphicsEnvironment, Font}
    13 import java.awt.{GraphicsEnvironment, Font}
    14 import java.awt.font.TextAttribute
    14 import java.awt.font.TextAttribute
    15 
    15 
    16 import scala.io.Source
    16 import scala.io.Source
    17 import scala.util.matching.Regex
    17 import scala.util.matching.Regex
    66           }
    66           }
    67         Map(entries: _*) +
    67         Map(entries: _*) +
    68           ("HOME" -> java.lang.System.getenv("HOME")) +
    68           ("HOME" -> java.lang.System.getenv("HOME")) +
    69           ("PATH" -> java.lang.System.getenv("PATH"))
    69           ("PATH" -> java.lang.System.getenv("PATH"))
    70       }
    70       }
    71   }
       
    72 
       
    73 
       
    74   /* external processes */
       
    75 
       
    76   def execute(redirect: Boolean, args: String*): Process =
       
    77   {
       
    78     val cmdline =
       
    79       if (Platform.is_windows) List(platform_root + "\\bin\\env.exe") ++ args
       
    80       else args
       
    81     Standard_System.raw_execute(null, environment, redirect, cmdline: _*)
       
    82   }
    71   }
    83 
    72 
    84 
    73 
    85   /* getenv */
    74   /* getenv */
    86 
    75 
   193     }
   182     }
   194   }
   183   }
   195 
   184 
   196 
   185 
   197 
   186 
   198   /** system tools **/
   187   /** external processes **/
       
   188 
       
   189   /* plain execute */
       
   190 
       
   191   def execute(redirect: Boolean, args: String*): Process =
       
   192   {
       
   193     val cmdline =
       
   194       if (Platform.is_windows) List(platform_root + "\\bin\\env.exe") ++ args
       
   195       else args
       
   196     Standard_System.raw_execute(null, environment, redirect, cmdline: _*)
       
   197   }
       
   198 
       
   199 
       
   200   /* managed process */
       
   201 
       
   202   class Managed_Process(redirect: Boolean, args: String*)
       
   203   {
       
   204     private val params =
       
   205       List(expand_path("$ISABELLE_HOME/lib/scripts/process"), "group", "-", "no_script")
       
   206     private val proc = execute(redirect, (params ++ args):_*)
       
   207 
       
   208 
       
   209     // channels
       
   210 
       
   211     val stdin: BufferedWriter =
       
   212       new BufferedWriter(new OutputStreamWriter(proc.getOutputStream, Standard_System.charset))
       
   213 
       
   214     val stdout: BufferedReader =
       
   215       new BufferedReader(new InputStreamReader(proc.getInputStream, Standard_System.charset))
       
   216 
       
   217     val stderr: BufferedReader =
       
   218       new BufferedReader(new InputStreamReader(proc.getErrorStream, Standard_System.charset))
       
   219 
       
   220 
       
   221     // signals
       
   222 
       
   223     private val pid = stdout.readLine
       
   224 
       
   225     private def kill(signal: String): Boolean =
       
   226       execute(true, "kill", "-" + signal, "-" + pid).waitFor == 0
       
   227 
       
   228     private def multi_kill(signal: String): Boolean =
       
   229     {
       
   230       var running = true
       
   231       var count = 10
       
   232       while (running && count > 0) {
       
   233         if (kill(signal)) {
       
   234           Thread.sleep(100)
       
   235           count -= 1
       
   236         }
       
   237         else running = false
       
   238       }
       
   239       running
       
   240     }
       
   241 
       
   242     def interrupt() { multi_kill("INT") }
       
   243     def terminate() { multi_kill("INT") && multi_kill("TERM") && kill("KILL"); proc.destroy }
       
   244 
       
   245 
       
   246     // JVM shutdown hook
       
   247 
       
   248     private val shutdown_hook = new Thread { override def run = terminate() }
       
   249 
       
   250     try { Runtime.getRuntime.addShutdownHook(shutdown_hook) }
       
   251     catch { case _: IllegalStateException => }
       
   252 
       
   253     private def cleanup() =
       
   254       try { Runtime.getRuntime.removeShutdownHook(shutdown_hook) }
       
   255       catch { case _: IllegalStateException => }
       
   256 
       
   257 
       
   258     /* result */
       
   259 
       
   260     def join: Int = { val rc = proc.waitFor; cleanup(); rc }
       
   261   }
       
   262 
       
   263 
       
   264   /* bash */
   199 
   265 
   200   def bash(script: String): (String, String, Int) =
   266   def bash(script: String): (String, String, Int) =
   201   {
   267   {
   202     Standard_System.with_tmp_file("isabelle_script") { script_file =>
   268     Standard_System.with_tmp_file("isabelle_script") { script_file =>
   203       Standard_System.write_file(script_file, script)
   269       Standard_System.write_file(script_file, script)
   204 
   270       val proc = new Managed_Process(false, "bash", posix_path(script_file.getPath))
   205       val proc =
   271 
   206         execute(false, expand_path("$ISABELLE_HOME/lib/scripts/process"), "group", "-",
   272       proc.stdin.close
   207           "exec bash " + posix_path(script_file.getPath))
   273       val stdout = Simple_Thread.future { Standard_System.slurp(proc.stdout) }
   208 
   274       val stderr = Simple_Thread.future { Standard_System.slurp(proc.stderr) }
   209       val stdout_reader =
       
   210         new BufferedReader(new InputStreamReader(proc.getInputStream, Standard_System.charset))
       
   211 
       
   212       val stderr_reader =
       
   213         new BufferedReader(new InputStreamReader(proc.getErrorStream, Standard_System.charset))
       
   214 
       
   215       val pid = stdout_reader.readLine
       
   216 
       
   217       def kill(strict: Boolean) =
       
   218       {
       
   219         var running = true
       
   220         var count = 10   // FIXME property!?
       
   221         while (running && count > 0) {
       
   222           if (execute(true, "kill", "-INT", "-" + pid).waitFor != 0)
       
   223             running = false
       
   224           else {
       
   225             Thread.sleep(100)   // FIXME property!?
       
   226             if (!strict) count -= 1
       
   227           }
       
   228         }
       
   229       }
       
   230 
       
   231       val shutdown_hook = new Thread { override def run = kill(true) }
       
   232       Runtime.getRuntime.addShutdownHook(shutdown_hook)  // FIXME tmp file during shutdown?!?
       
   233 
       
   234       def cleanup() =
       
   235         try { Runtime.getRuntime.removeShutdownHook(shutdown_hook) }
       
   236         catch { case _: IllegalStateException => }
       
   237 
       
   238       val stdout = Simple_Thread.future { Standard_System.slurp(stdout_reader) }
       
   239       val stderr = Simple_Thread.future { Standard_System.slurp(stderr_reader) }
       
   240       proc.getOutputStream.close
       
   241 
   275 
   242       val rc =
   276       val rc =
   243         try {
   277         try { proc.join }
   244           try { proc.waitFor }
   278         catch { case e: InterruptedException => Thread.interrupted; proc.terminate; 130 }
   245           catch { case e: InterruptedException => Thread.interrupted; kill(false); 2 }
       
   246         }
       
   247         finally {
       
   248           stdout.join
       
   249           stderr.join
       
   250           proc.destroy  // FIXME kill -TERM !?
       
   251           cleanup()
       
   252         }
       
   253       (stdout.join, stderr.join, rc)
   279       (stdout.join, stderr.join, rc)
   254     }
   280     }
   255   }
   281   }
       
   282 
       
   283 
       
   284   /* system tools */
   256 
   285 
   257   def isabelle_tool(name: String, args: String*): (String, Int) =
   286   def isabelle_tool(name: String, args: String*): (String, Int) =
   258   {
   287   {
   259     getenv_strict("ISABELLE_TOOLS").split(":").find { dir =>
   288     getenv_strict("ISABELLE_TOOLS").split(":").find { dir =>
   260       val file = platform_file(dir + "/" + name)
   289       val file = platform_file(dir + "/" + name)