src/Pure/System/isabelle_system.scala
author wenzelm
Thu, 20 May 2010 11:36:30 +0200
changeset 37006 ccb8da7f76e6
parent 36809 1fd4f28e6ce1
child 37024 641923374eba
permissions -rw-r--r--
general Isabelle_System.try_read;
     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   /* try_read */
   154 
   155   def try_read(path: String): String =
   156   {
   157     val file = platform_file(path)
   158     if (file.isFile) Source.fromFile(file).mkString else ""
   159   }
   160 
   161 
   162   /* source files */
   163 
   164   private def try_file(file: File) = if (file.isFile) Some(file) else None
   165 
   166   def source_file(path: String): Option[File] =
   167   {
   168     if (path.startsWith("/") || path == "")
   169       try_file(platform_file(path))
   170     else {
   171       val pure_file = platform_file("$ISABELLE_HOME/src/Pure/" + path)
   172       if (pure_file.isFile) Some(pure_file)
   173       else if (getenv("ML_SOURCES") != "")
   174         try_file(platform_file("$ML_SOURCES/" + path))
   175       else None
   176     }
   177   }
   178 
   179 
   180 
   181   /** system tools **/
   182 
   183   def bash_output(script: String): (String, Int) =
   184   {
   185     Standard_System.with_tmp_file("isabelle_script") { script_file =>
   186       Standard_System.with_tmp_file("isabelle_pid") { pid_file =>
   187         Standard_System.with_tmp_file("isabelle_output") { output_file =>
   188 
   189           Standard_System.write_file(script_file, script)
   190 
   191           val proc = execute(true, expand_path("$ISABELLE_HOME/lib/scripts/bash"), "group",
   192             script_file.getPath, pid_file.getPath, output_file.getPath)
   193 
   194           def kill(strict: Boolean) =
   195           {
   196             val pid =
   197               try { Some(Standard_System.read_file(pid_file)) }
   198               catch { case _: IOException => None }
   199             if (pid.isDefined) {
   200               var running = true
   201               var count = 10
   202               while (running && count > 0) {
   203                 if (execute(true, "kill", "-INT", "-" + pid.get).waitFor != 0)
   204                   running = false
   205                 else {
   206                   Thread.sleep(100)
   207                   if (!strict) count -= 1
   208                 }
   209               }
   210             }
   211           }
   212 
   213           val shutdown_hook = new Thread { override def run = kill(true) }
   214           Runtime.getRuntime.addShutdownHook(shutdown_hook)  // FIXME tmp files during shutdown?!?
   215 
   216           def cleanup() =
   217             try { Runtime.getRuntime.removeShutdownHook(shutdown_hook) }
   218             catch { case _: IllegalStateException => }
   219 
   220           val rc =
   221             try {
   222               try { proc.waitFor }  // FIXME read stderr (!??)
   223               catch { case e: InterruptedException => Thread.interrupted; kill(false); throw e }
   224             }
   225             finally {
   226               proc.getOutputStream.close
   227               proc.getInputStream.close
   228               proc.getErrorStream.close
   229               proc.destroy
   230               cleanup()
   231             }
   232 
   233           val output =
   234             try { Standard_System.read_file(output_file) }
   235             catch { case _: IOException => "" }
   236 
   237           (output, rc)
   238         }
   239       }
   240     }
   241   }
   242 
   243   def isabelle_tool(name: String, args: String*): (String, Int) =
   244   {
   245     getenv_strict("ISABELLE_TOOLS").split(":").find { dir =>
   246       val file = platform_file(dir + "/" + name)
   247       try { file.isFile && file.canRead && file.canExecute }
   248       catch { case _: SecurityException => false }
   249     } match {
   250       case Some(dir) =>
   251         Standard_System.process_output(
   252           execute(true, (List(expand_path(dir + "/" + name)) ++ args): _*))
   253       case None => ("Unknown Isabelle tool: " + name, 2)
   254     }
   255   }
   256 
   257 
   258   /* named pipes */
   259 
   260   def mk_fifo(): String =
   261   {
   262     val (result, rc) = isabelle_tool("mkfifo")
   263     if (rc == 0) result.trim
   264     else error(result)
   265   }
   266 
   267   def rm_fifo(fifo: String)
   268   {
   269     val (result, rc) = isabelle_tool("rmfifo", fifo)
   270     if (rc != 0) error(result)
   271   }
   272 
   273   def fifo_stream(fifo: String): BufferedInputStream =
   274   {
   275     // blocks until writer is ready
   276     val stream =
   277       if (Platform.is_windows) {
   278         val proc = execute(false, "cat", fifo)
   279         proc.getOutputStream.close
   280         proc.getErrorStream.close
   281         proc.getInputStream
   282       }
   283       else new FileInputStream(fifo)
   284     new BufferedInputStream(stream)
   285   }
   286 
   287 
   288 
   289   /** Isabelle resources **/
   290 
   291   /* components */
   292 
   293   def components(): List[String] =
   294     getenv("ISABELLE_COMPONENTS").split(":").toList
   295 
   296 
   297   /* find logics */
   298 
   299   def find_logics(): List[String] =
   300   {
   301     val ml_ident = getenv_strict("ML_IDENTIFIER")
   302     val logics = new mutable.ListBuffer[String]
   303     for (dir <- getenv_strict("ISABELLE_PATH").split(":")) {
   304       val files = platform_file(dir + "/" + ml_ident).listFiles()
   305       if (files != null) {
   306         for (file <- files if file.isFile) logics += file.getName
   307       }
   308     }
   309     logics.toList.sortWith(_ < _)
   310   }
   311 
   312 
   313   /* symbols */
   314 
   315   private def read_symbols(path: String): List[String] =
   316     Library.chunks(try_read(path)).map(_.toString).toList
   317 
   318   val symbols = new Symbol.Interpretation(
   319     read_symbols("$ISABELLE_HOME/etc/symbols") :::
   320     read_symbols("$ISABELLE_HOME_USER/etc/symbols"))
   321 
   322 
   323   /* fonts */
   324 
   325   val font_family = "IsabelleText"
   326 
   327   def get_font(size: Int = 1, bold: Boolean = false): Font =
   328     new Font(font_family, if (bold) Font.BOLD else Font.PLAIN, size)
   329 
   330   def install_fonts()
   331   {
   332     def create_font(bold: Boolean): Font =
   333     {
   334       val name =
   335         if (bold) "$ISABELLE_HOME/lib/fonts/IsabelleTextBold.ttf"
   336         else "$ISABELLE_HOME/lib/fonts/IsabelleText.ttf"
   337       Font.createFont(Font.TRUETYPE_FONT, platform_file(name))
   338     }
   339     def check_font() = get_font().getFamily == font_family
   340 
   341     if (!check_font()) {
   342       val font = create_font(false)
   343       val bold_font = create_font(true)
   344 
   345       val ge = GraphicsEnvironment.getLocalGraphicsEnvironment()
   346       ge.registerFont(font)
   347       // workaround strange problem with Apple's Java 1.6 font manager
   348       if (bold_font.getFamily == font_family) ge.registerFont(bold_font)
   349       if (!check_font()) error("Failed to install IsabelleText fonts")
   350     }
   351   }
   352 }