1 /* Title: Pure/System/isabelle_system.scala
4 Isabelle system support (settings environment etc.).
9 import java.util.regex.Pattern
10 import java.util.Locale
11 import java.io.{BufferedWriter, OutputStreamWriter, FileOutputStream,
12 BufferedInputStream, FileInputStream, BufferedReader, InputStreamReader,
14 import java.awt.{GraphicsEnvironment, Font}
16 import scala.io.Source
17 import scala.util.matching.Regex
18 import scala.collection.mutable
21 class Isabelle_System extends Standard_System
23 /** Isabelle environment **/
25 private val environment: Map[String, String] =
27 import scala.collection.JavaConversions._
29 val env0 = Map(java.lang.System.getenv.toList: _*) +
30 ("THIS_JAVA" -> this_java())
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")
38 case Some(path) => path
41 Standard_System.with_tmp_file("settings") { dump =>
43 if (Platform.is_windows) List(platform_root + "\\bin\\bash", "-l") else Nil
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)
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))
56 ("HOME" -> java.lang.System.getenv("HOME")) +
57 ("PATH" -> java.lang.System.getenv("PATH"))
62 /* external processes */
64 def execute(redirect: Boolean, args: String*): Process =
67 if (Platform.is_windows) List(platform_root + "\\bin\\env.exe") ++ args
69 Standard_System.raw_execute(null, environment, redirect, cmdline: _*)
75 def getenv(name: String): String =
76 environment.getOrElse(if (name == "HOME") "HOME_JVM" else name, "")
78 def getenv_strict(name: String): String =
80 val value = getenv(name)
81 if (value != "") value else error("Undefined environment variable: " + name)
84 override def toString = getenv("ISABELLE_HOME")
88 /** file path specifications **/
92 private val Root = new Regex("(//+[^/]*|/)(.*)")
93 private val Only_Root = new Regex("//+[^/]*|/")
95 def expand_path(isabelle_path: String): String =
97 val result_path = new StringBuilder
98 def init(path: String): String =
101 case Root(root, rest) =>
108 def append(path: String)
110 val rest = init(path)
111 for (p <- rest.split("/") if p != "" && p != ".") {
113 val result = result_path.toString
114 if (!Only_Root.pattern.matcher(result).matches) {
115 val i = result.lastIndexOf("/")
118 else if (result.substring(i + 1) == "..")
119 result_path ++= "/.."
121 result_path.length = 0
123 result_path.length = i
127 val len = result_path.length
128 if (len > 0 && result_path(len - 1) != '/')
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"))
147 def platform_path(isabelle_path: String): String =
148 jvm_path(expand_path(isabelle_path))
150 def platform_file(path: String) = new File(platform_path(path))
155 def try_read(path: String): String =
157 val file = platform_file(path)
158 if (file.isFile) Source.fromFile(file).mkString else ""
164 private def try_file(file: File) = if (file.isFile) Some(file) else None
166 def source_file(path: String): Option[File] =
168 if (path.startsWith("/") || path == "")
169 try_file(platform_file(path))
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))
183 def bash_output(script: String): (String, Int) =
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 =>
189 Standard_System.write_file(script_file, script)
191 val proc = execute(true, expand_path("$ISABELLE_HOME/lib/scripts/bash"), "group",
192 script_file.getPath, pid_file.getPath, output_file.getPath)
194 def kill(strict: Boolean) =
197 try { Some(Standard_System.read_file(pid_file)) }
198 catch { case _: IOException => None }
202 while (running && count > 0) {
203 if (execute(true, "kill", "-INT", "-" + pid.get).waitFor != 0)
207 if (!strict) count -= 1
213 val shutdown_hook = new Thread { override def run = kill(true) }
214 Runtime.getRuntime.addShutdownHook(shutdown_hook) // FIXME tmp files during shutdown?!?
217 try { Runtime.getRuntime.removeShutdownHook(shutdown_hook) }
218 catch { case _: IllegalStateException => }
222 try { proc.waitFor } // FIXME read stderr (!??)
223 catch { case e: InterruptedException => Thread.interrupted; kill(false); throw e }
226 proc.getOutputStream.close
227 proc.getInputStream.close
228 proc.getErrorStream.close
234 try { Standard_System.read_file(output_file) }
235 catch { case _: IOException => "" }
243 def isabelle_tool(name: String, args: String*): (String, Int) =
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 }
251 Standard_System.process_output(
252 execute(true, (List(expand_path(dir + "/" + name)) ++ args): _*))
253 case None => ("Unknown Isabelle tool: " + name, 2)
260 def mk_fifo(): String =
262 val (result, rc) = isabelle_tool("mkfifo")
263 if (rc == 0) result.trim
267 def rm_fifo(fifo: String)
269 val (result, rc) = isabelle_tool("rmfifo", fifo)
270 if (rc != 0) error(result)
273 def fifo_stream(fifo: String): BufferedInputStream =
275 // blocks until writer is ready
277 if (Platform.is_windows) {
278 val proc = execute(false, "cat", fifo)
279 proc.getOutputStream.close
280 proc.getErrorStream.close
283 else new FileInputStream(fifo)
284 new BufferedInputStream(stream)
289 /** Isabelle resources **/
293 def components(): List[String] =
294 getenv("ISABELLE_COMPONENTS").split(":").toList
299 def find_logics(): List[String] =
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()
306 for (file <- files if file.isFile) logics += file.getName
309 logics.toList.sortWith(_ < _)
315 private def read_symbols(path: String): List[String] =
316 Library.chunks(try_read(path)).map(_.toString).toList
318 val symbols = new Symbol.Interpretation(
319 read_symbols("$ISABELLE_HOME/etc/symbols") :::
320 read_symbols("$ISABELLE_HOME_USER/etc/symbols"))
325 val font_family = "IsabelleText"
327 def get_font(size: Int = 1, bold: Boolean = false): Font =
328 new Font(font_family, if (bold) Font.BOLD else Font.PLAIN, size)
332 def create_font(bold: Boolean): Font =
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))
339 def check_font() = get_font().getFamily == font_family
342 val font = create_font(false)
343 val bold_font = create_font(true)
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")