src/Pure/System/isabelle_system.scala
author wenzelm
Sat, 17 Apr 2010 20:42:26 +0200
changeset 36193 067a01827fca
parent 36144 89b1a136edef
child 36784 cf36fd1e4cda
permissions -rw-r--r--
improved ISABELLE_JAVA, based on THIS_JAVA of the actually running JVM;
     1 /*  Title:      Pure/System/isabelle_system.scala
     2     Author:     Makarius
     3 
     4 Isabelle system support (settings environment etc.).
     5 */
     6 
     7 package isabelle
     8 
     9 import java.util.regex.Pattern
    10 import java.util.Locale
    11 import java.io.{BufferedWriter, OutputStreamWriter, FileOutputStream,
    12   BufferedInputStream, FileInputStream, BufferedReader, InputStreamReader,
    13   File, IOException}
    14 import java.awt.{GraphicsEnvironment, Font}
    15 
    16 import scala.io.Source
    17 import scala.util.matching.Regex
    18 import scala.collection.mutable
    19 
    20 
    21 class Isabelle_System extends Standard_System
    22 {
    23   /** Isabelle environment **/
    24 
    25   private val environment: Map[String, String] =
    26   {
    27     import scala.collection.JavaConversions._
    28 
    29     val env0 = Map(java.lang.System.getenv.toList: _*) +
    30       ("THIS_JAVA" -> this_java())
    31 
    32     val isabelle_home =
    33       env0.get("ISABELLE_HOME") match {
    34         case None | Some("") =>
    35           val path = java.lang.System.getProperty("isabelle.home")
    36           if (path == null || path == "") error("Unknown Isabelle home directory")
    37           else path
    38         case Some(path) => path
    39       }
    40 
    41     Standard_System.with_tmp_file("settings") { dump =>
    42         val shell_prefix =
    43           if (Platform.is_windows) List(platform_root + "\\bin\\bash", "-l") else Nil
    44         val cmdline =
    45           shell_prefix ::: List(isabelle_home + "/bin/isabelle", "getenv", "-d", dump.toString)
    46         val (output, rc) = Standard_System.raw_exec(null, env0, true, cmdline: _*)
    47         if (rc != 0) error(output)
    48 
    49         val entries =
    50           for (entry <- Source.fromFile(dump).mkString split "\0" if entry != "") yield {
    51             val i = entry.indexOf('=')
    52             if (i <= 0) (entry -> "")
    53             else (entry.substring(0, i) -> entry.substring(i + 1))
    54           }
    55         Map(entries: _*) +
    56           ("HOME" -> java.lang.System.getenv("HOME")) +
    57           ("PATH" -> java.lang.System.getenv("PATH"))
    58       }
    59   }
    60 
    61 
    62   /* external processes */
    63 
    64   def execute(redirect: Boolean, args: String*): Process =
    65   {
    66     val cmdline =
    67       if (Platform.is_windows) List(platform_root + "\\bin\\env.exe") ++ args
    68       else args
    69     Standard_System.raw_execute(null, environment, redirect, cmdline: _*)
    70   }
    71 
    72 
    73   /* getenv */
    74 
    75   def getenv(name: String): String =
    76     environment.getOrElse(if (name == "HOME") "HOME_JVM" else name, "")
    77 
    78   def getenv_strict(name: String): String =
    79   {
    80     val value = getenv(name)
    81     if (value != "") value else error("Undefined environment variable: " + name)
    82   }
    83 
    84   override def toString = getenv("ISABELLE_HOME")
    85 
    86 
    87 
    88   /** file path specifications **/
    89 
    90   /* expand_path */
    91 
    92   private val Root = new Regex("(//+[^/]*|/)(.*)")
    93   private val Only_Root = new Regex("//+[^/]*|/")
    94 
    95   def expand_path(isabelle_path: String): String =
    96   {
    97     val result_path = new StringBuilder
    98     def init(path: String): String =
    99     {
   100       path match {
   101         case Root(root, rest) =>
   102           result_path.clear
   103           result_path ++= root
   104           rest
   105         case _ => path
   106       }
   107     }
   108     def append(path: String)
   109     {
   110       val rest = init(path)
   111       for (p <- rest.split("/") if p != "" && p != ".") {
   112         if (p == "..") {
   113           val result = result_path.toString
   114           if (!Only_Root.pattern.matcher(result).matches) {
   115             val i = result.lastIndexOf("/")
   116             if (result == "")
   117               result_path ++= ".."
   118             else if (result.substring(i + 1) == "..")
   119               result_path ++= "/.."
   120             else if (i < 0)
   121               result_path.length = 0
   122             else
   123               result_path.length = i
   124           }
   125         }
   126         else {
   127           val len = result_path.length
   128           if (len > 0 && result_path(len - 1) != '/')
   129             result_path += '/'
   130           result_path ++= p
   131         }
   132       }
   133     }
   134     val rest = init(isabelle_path)
   135     for (p <- rest.split("/")) {
   136       if (p.startsWith("$")) append(getenv_strict(p.substring(1)))
   137       else if (p == "~") append(getenv_strict("HOME"))
   138       else if (p == "~~") append(getenv_strict("ISABELLE_HOME"))
   139       else append(p)
   140     }
   141     result_path.toString
   142   }
   143 
   144 
   145   /* platform_path */
   146 
   147   def platform_path(isabelle_path: String): String =
   148     jvm_path(expand_path(isabelle_path))
   149 
   150   def platform_file(path: String) = new File(platform_path(path))
   151 
   152 
   153   /* source files */
   154 
   155   private def try_file(file: File) = if (file.isFile) Some(file) else None
   156 
   157   def source_file(path: String): Option[File] =
   158   {
   159     if (path.startsWith("/") || path == "")
   160       try_file(platform_file(path))
   161     else {
   162       val pure_file = platform_file("$ISABELLE_HOME/src/Pure/" + path)
   163       if (pure_file.isFile) Some(pure_file)
   164       else if (getenv("ML_SOURCES") != "")
   165         try_file(platform_file("$ML_SOURCES/" + path))
   166       else None
   167     }
   168   }
   169 
   170 
   171 
   172   /** system tools **/
   173 
   174   def bash_output(script: String): (String, Int) =
   175   {
   176     Standard_System.with_tmp_file("isabelle_script") { script_file =>
   177       Standard_System.with_tmp_file("isabelle_pid") { pid_file =>
   178         Standard_System.with_tmp_file("isabelle_output") { output_file =>
   179 
   180           Standard_System.write_file(script_file, script)
   181 
   182           val proc = execute(true, expand_path("$ISABELLE_HOME/lib/scripts/bash"), "group",
   183             script_file.getPath, pid_file.getPath, output_file.getPath)
   184 
   185           def kill(strict: Boolean) =
   186           {
   187             val pid =
   188               try { Some(Standard_System.read_file(pid_file)) }
   189               catch { case _: IOException => None }
   190             if (pid.isDefined) {
   191               var running = true
   192               var count = 10
   193               while (running && count > 0) {
   194                 if (execute(true, "kill", "-INT", "-" + pid.get).waitFor != 0)
   195                   running = false
   196                 else {
   197                   Thread.sleep(100)
   198                   if (!strict) count -= 1
   199                 }
   200               }
   201             }
   202           }
   203 
   204           val shutdown_hook = new Thread { override def run = kill(true) }
   205           Runtime.getRuntime.addShutdownHook(shutdown_hook)  // FIXME tmp files during shutdown?!?
   206 
   207           def cleanup() =
   208             try { Runtime.getRuntime.removeShutdownHook(shutdown_hook) }
   209             catch { case _: IllegalStateException => }
   210 
   211           val rc =
   212             try {
   213               try { proc.waitFor }  // FIXME read stderr (!??)
   214               catch { case e: InterruptedException => Thread.interrupted; kill(false); throw e }
   215             }
   216             finally {
   217               proc.getOutputStream.close
   218               proc.getInputStream.close
   219               proc.getErrorStream.close
   220               proc.destroy
   221               cleanup()
   222             }
   223 
   224           val output =
   225             try { Standard_System.read_file(output_file) }
   226             catch { case _: IOException => "" }
   227 
   228           (output, rc)
   229         }
   230       }
   231     }
   232   }
   233 
   234   def isabelle_tool(name: String, args: String*): (String, Int) =
   235   {
   236     getenv_strict("ISABELLE_TOOLS").split(":").find { dir =>
   237       val file = platform_file(dir + "/" + name)
   238       try { file.isFile && file.canRead && file.canExecute }
   239       catch { case _: SecurityException => false }
   240     } match {
   241       case Some(dir) =>
   242         Standard_System.process_output(
   243           execute(true, (List(expand_path(dir + "/" + name)) ++ args): _*))
   244       case None => ("Unknown Isabelle tool: " + name, 2)
   245     }
   246   }
   247 
   248 
   249   /* named pipes */
   250 
   251   def mk_fifo(): String =
   252   {
   253     val (result, rc) = isabelle_tool("mkfifo")
   254     if (rc == 0) result.trim
   255     else error(result)
   256   }
   257 
   258   def rm_fifo(fifo: String)
   259   {
   260     val (result, rc) = isabelle_tool("rmfifo", fifo)
   261     if (rc != 0) error(result)
   262   }
   263 
   264   def fifo_stream(fifo: String): BufferedInputStream =
   265   {
   266     // blocks until writer is ready
   267     val stream =
   268       if (Platform.is_windows) {
   269         val proc = execute(false, "cat", fifo)
   270         proc.getOutputStream.close
   271         proc.getErrorStream.close
   272         proc.getInputStream
   273       }
   274       else new FileInputStream(fifo)
   275     new BufferedInputStream(stream)
   276   }
   277 
   278 
   279 
   280   /** Isabelle resources **/
   281 
   282   /* components */
   283 
   284   def components(): List[String] =
   285     getenv("ISABELLE_COMPONENTS").split(":").toList
   286 
   287 
   288   /* find logics */
   289 
   290   def find_logics(): List[String] =
   291   {
   292     val ml_ident = getenv_strict("ML_IDENTIFIER")
   293     val logics = new mutable.ListBuffer[String]
   294     for (dir <- getenv_strict("ISABELLE_PATH").split(":")) {
   295       val files = platform_file(dir + "/" + ml_ident).listFiles()
   296       if (files != null) {
   297         for (file <- files if file.isFile) logics += file.getName
   298       }
   299     }
   300     logics.toList.sortWith(_ < _)
   301   }
   302 
   303 
   304   /* symbols */
   305 
   306   private def read_symbols(path: String): List[String] =
   307   {
   308     val file = platform_file(path)
   309     if (file.isFile) Source.fromFile(file).getLines().toList
   310     else Nil
   311   }
   312   val symbols = new Symbol.Interpretation(
   313     read_symbols("$ISABELLE_HOME/etc/symbols") :::
   314     read_symbols("$ISABELLE_HOME_USER/etc/symbols"))
   315 
   316 
   317   /* fonts */
   318 
   319   val font_family = "IsabelleText"
   320 
   321   def get_font(bold: Boolean): Font =
   322     new Font(font_family, if (bold) Font.BOLD else Font.PLAIN, 1)
   323 
   324   def install_fonts()
   325   {
   326     def create_font(bold: Boolean): Font =
   327     {
   328       val name =
   329         if (bold) "$ISABELLE_HOME/lib/fonts/IsabelleTextBold.ttf"
   330         else "$ISABELLE_HOME/lib/fonts/IsabelleText.ttf"
   331       Font.createFont(Font.TRUETYPE_FONT, platform_file(name))
   332     }
   333     def check_font() = get_font(false).getFamily == font_family
   334 
   335     if (!check_font()) {
   336       val font = create_font(false)
   337       val bold_font = create_font(true)
   338 
   339       val ge = GraphicsEnvironment.getLocalGraphicsEnvironment()
   340       ge.registerFont(font)
   341       // workaround strange problem with Apple's Java 1.6 font manager
   342       if (bold_font.getFamily == font_family) ge.registerFont(bold_font)
   343       if (!check_font()) error("Failed to install IsabelleText fonts")
   344     }
   345   }
   346 }