| author | wenzelm | 
| Wed, 02 Sep 2009 10:35:47 +0200 | |
| changeset 32487 | 4af24f127fcf | 
| parent 32450 | 375db037f4d2 | 
| child 34024 | 0bae8702a7c5 | 
| permissions | -rw-r--r-- | 
| 30175 | 1 | /* Title: Pure/System/isabelle_system.scala | 
| 27919 | 2 | Author: Makarius | 
| 3 | ||
| 31796 | 4 | Isabelle system support, with basic Cygwin/Posix compatibility. | 
| 27919 | 5 | */ | 
| 6 | ||
| 7 | package isabelle | |
| 8 | ||
| 31520 
0a99c8716312
simplified IsabelleSystem.platform_path for cygwin;
 wenzelm parents: 
31500diff
changeset | 9 | import java.util.regex.Pattern | 
| 31820 | 10 | import java.util.Locale | 
| 28063 | 11 | import java.io.{BufferedReader, InputStreamReader, FileInputStream, File, IOException}
 | 
| 12 | ||
| 13 | import scala.io.Source | |
| 31520 
0a99c8716312
simplified IsabelleSystem.platform_path for cygwin;
 wenzelm parents: 
31500diff
changeset | 14 | import scala.util.matching.Regex | 
| 27936 | 15 | |
| 27919 | 16 | |
| 31796 | 17 | object Isabelle_System | 
| 31498 | 18 | {
 | 
| 28057 | 19 | val charset = "UTF-8" | 
| 20 | ||
| 31822 | 21 | |
| 31498 | 22 | /* shell processes */ | 
| 23 | ||
| 24 | private def raw_execute(env: Map[String, String], redirect: Boolean, args: String*): Process = | |
| 25 |   {
 | |
| 26 | val cmdline = new java.util.LinkedList[String] | |
| 27 | for (s <- args) cmdline.add(s) | |
| 28 | ||
| 29 | val proc = new ProcessBuilder(cmdline) | |
| 30 | proc.environment.clear | |
| 31 | for ((x, y) <- env) proc.environment.put(x, y) | |
| 32 | proc.redirectErrorStream(redirect) | |
| 33 | ||
| 34 |     try { proc.start }
 | |
| 35 |     catch { case e: IOException => error(e.getMessage) }
 | |
| 36 | } | |
| 37 | ||
| 38 | private def process_output(proc: Process): (String, Int) = | |
| 39 |   {
 | |
| 40 | proc.getOutputStream.close | |
| 41 | val output = Source.fromInputStream(proc.getInputStream, charset).mkString | |
| 42 | val rc = proc.waitFor | |
| 43 | (output, rc) | |
| 44 | } | |
| 45 | } | |
| 46 | ||
| 47 | ||
| 31796 | 48 | class Isabelle_System | 
| 31498 | 49 | {
 | 
| 31796 | 50 | /** unique ids **/ | 
| 31443 | 51 | |
| 52 | private var id_count: BigInt = 0 | |
| 53 |   def id(): String = synchronized { id_count += 1; "j" + id_count }
 | |
| 54 | ||
| 55 | ||
| 31796 | 56 | |
| 57 | /** Isabelle environment **/ | |
| 58 | ||
| 59 | /* platform prefixes */ | |
| 31498 | 60 | |
| 31520 
0a99c8716312
simplified IsabelleSystem.platform_path for cygwin;
 wenzelm parents: 
31500diff
changeset | 61 | private val (platform_root, drive_prefix, shell_prefix) = | 
| 31500 | 62 |   {
 | 
| 31825 | 63 |     if (Platform.is_windows) {
 | 
| 31829 | 64 | val (root, drive) = Cygwin.config() | 
| 31826 | 65 |       if (!Cygwin.check(root)) error("Bad Cygwin installation: " + root)
 | 
| 31520 
0a99c8716312
simplified IsabelleSystem.platform_path for cygwin;
 wenzelm parents: 
31500diff
changeset | 66 | val shell = List(root + "\\bin\\bash", "-l") | 
| 
0a99c8716312
simplified IsabelleSystem.platform_path for cygwin;
 wenzelm parents: 
31500diff
changeset | 67 | (root, drive, shell) | 
| 31500 | 68 | } | 
| 31520 
0a99c8716312
simplified IsabelleSystem.platform_path for cygwin;
 wenzelm parents: 
31500diff
changeset | 69 |     else ("/", "", Nil)
 | 
| 31500 | 70 | } | 
| 71 | ||
| 31796 | 72 | |
| 73 | /* bash environment */ | |
| 74 | ||
| 31498 | 75 | private val environment: Map[String, String] = | 
| 76 |   {
 | |
| 77 | import scala.collection.jcl.Conversions._ | |
| 78 | ||
| 79 | val env0 = Map(java.lang.System.getenv.toList: _*) | |
| 27919 | 80 | |
| 31927 
9a0f28bcc81d
init isabelle home from existing setting or hint via system property;
 wenzelm parents: 
31829diff
changeset | 81 | val isabelle_home = | 
| 
9a0f28bcc81d
init isabelle home from existing setting or hint via system property;
 wenzelm parents: 
31829diff
changeset | 82 |       env0.get("ISABELLE_HOME") match {
 | 
| 31498 | 83 |         case None | Some("") =>
 | 
| 31927 
9a0f28bcc81d
init isabelle home from existing setting or hint via system property;
 wenzelm parents: 
31829diff
changeset | 84 |           val path = java.lang.System.getProperty("isabelle.home")
 | 
| 
9a0f28bcc81d
init isabelle home from existing setting or hint via system property;
 wenzelm parents: 
31829diff
changeset | 85 |           if (path == null || path == "") error("Unknown Isabelle home directory")
 | 
| 
9a0f28bcc81d
init isabelle home from existing setting or hint via system property;
 wenzelm parents: 
31829diff
changeset | 86 | else path | 
| 
9a0f28bcc81d
init isabelle home from existing setting or hint via system property;
 wenzelm parents: 
31829diff
changeset | 87 | case Some(path) => path | 
| 31498 | 88 | } | 
| 29177 | 89 | |
| 31498 | 90 |     val dump = File.createTempFile("isabelle", null)
 | 
| 91 |     try {
 | |
| 31927 
9a0f28bcc81d
init isabelle home from existing setting or hint via system property;
 wenzelm parents: 
31829diff
changeset | 92 | val cmdline = shell_prefix ::: | 
| 
9a0f28bcc81d
init isabelle home from existing setting or hint via system property;
 wenzelm parents: 
31829diff
changeset | 93 | List(isabelle_home + "/bin/isabelle", "getenv", "-d", dump.toString) | 
| 31796 | 94 | val proc = Isabelle_System.raw_execute(env0, true, cmdline: _*) | 
| 95 | val (output, rc) = Isabelle_System.process_output(proc) | |
| 31498 | 96 | if (rc != 0) error(output) | 
| 97 | ||
| 98 | val entries = | |
| 99 |         for (entry <- Source.fromFile(dump).mkString split "\0" if entry != "") yield {
 | |
| 100 |           val i = entry.indexOf('=')
 | |
| 101 | if (i <= 0) (entry -> "") | |
| 102 | else (entry.substring(0, i) -> entry.substring(i + 1)) | |
| 103 | } | |
| 31704 
b8628ac68b73
environment: keep original HOME and PATH (required for Cygwin integrity);
 wenzelm parents: 
31703diff
changeset | 104 | Map(entries: _*) + | 
| 
b8628ac68b73
environment: keep original HOME and PATH (required for Cygwin integrity);
 wenzelm parents: 
31703diff
changeset | 105 |         ("HOME" -> java.lang.System.getenv("HOME")) +
 | 
| 
b8628ac68b73
environment: keep original HOME and PATH (required for Cygwin integrity);
 wenzelm parents: 
31703diff
changeset | 106 |         ("PATH" -> java.lang.System.getenv("PATH"))
 | 
| 31498 | 107 | } | 
| 108 |     finally { dump.delete }
 | |
| 27953 | 109 | } | 
| 27919 | 110 | |
| 31796 | 111 | |
| 112 | /* getenv */ | |
| 113 | ||
| 31498 | 114 | def getenv(name: String): String = | 
| 115 | environment.getOrElse(if (name == "HOME") "HOME_JVM" else name, "") | |
| 116 | ||
| 117 | def getenv_strict(name: String): String = | |
| 118 |   {
 | |
| 31234 
6ce6801129de
getenv_strict needs to be based on getenv (accidentally broken in 0e88d33e8d19);
 wenzelm parents: 
30175diff
changeset | 119 | val value = getenv(name) | 
| 27993 
6dd90ef9f927
simplified exceptions: use plain error function / RuntimeException;
 wenzelm parents: 
27974diff
changeset | 120 |     if (value != "") value else error("Undefined environment variable: " + name)
 | 
| 27919 | 121 | } | 
| 122 | ||
| 31703 | 123 |   override def toString = getenv("ISABELLE_HOME")
 | 
| 124 | ||
| 27936 | 125 | |
| 31796 | 126 | |
| 127 | /** file path specifications **/ | |
| 128 | ||
| 31820 | 129 | /* expand_path */ | 
| 31796 | 130 | |
| 31820 | 131 | def expand_path(isabelle_path: String): String = | 
| 31796 | 132 |   {
 | 
| 133 | val result_path = new StringBuilder | |
| 134 | def init(path: String) | |
| 135 |     {
 | |
| 136 |       if (path.startsWith("/")) {
 | |
| 137 | result_path.clear | |
| 138 | result_path += '/' | |
| 139 | } | |
| 140 | } | |
| 141 | def append(path: String) | |
| 142 |     {
 | |
| 143 | init(path) | |
| 31803 | 144 |       for (p <- path.split("/") if p != "" && p != ".") {
 | 
| 145 |         if (p == "..") {
 | |
| 146 | val result = result_path.toString | |
| 147 |           val i = result.lastIndexOf("/")
 | |
| 148 | if (result == "") | |
| 149 | result_path ++= ".." | |
| 150 | else if (result.substring(i + 1) == "..") | |
| 151 | result_path ++= "/.." | |
| 152 | else if (i < 1) | |
| 153 | result_path.length = i + 1 | |
| 154 | else | |
| 155 | result_path.length = i | |
| 156 | } | |
| 157 |         else {
 | |
| 158 | val len = result_path.length | |
| 159 | if (len > 0 && result_path(len - 1) != '/') | |
| 160 | result_path += '/' | |
| 161 | result_path ++= p | |
| 162 | } | |
| 31796 | 163 | } | 
| 164 | } | |
| 31820 | 165 | init(isabelle_path) | 
| 166 |     for (p <- isabelle_path.split("/")) {
 | |
| 31796 | 167 |       if (p.startsWith("$")) append(getenv_strict(p.substring(1)))
 | 
| 168 |       else if (p == "~") append(getenv_strict("HOME"))
 | |
| 169 |       else if (p == "~~") append(getenv_strict("ISABELLE_HOME"))
 | |
| 170 | else append(p) | |
| 171 | } | |
| 172 | result_path.toString | |
| 173 | } | |
| 174 | ||
| 175 | ||
| 31820 | 176 | /* platform_path */ | 
| 27936 | 177 | |
| 31820 | 178 | private val Cygdrive = | 
| 179 | new Regex(Pattern.quote(drive_prefix) + "/([a-zA-Z])($|/.*)") | |
| 31520 
0a99c8716312
simplified IsabelleSystem.platform_path for cygwin;
 wenzelm parents: 
31500diff
changeset | 180 | |
| 31820 | 181 | def platform_path(isabelle_path: String): String = | 
| 31498 | 182 |   {
 | 
| 27936 | 183 | val result_path = new StringBuilder | 
| 31796 | 184 | val rest = | 
| 31820 | 185 |       expand_path(isabelle_path) match {
 | 
| 31825 | 186 | case Cygdrive(drive, rest) if Platform.is_windows => | 
| 31796 | 187 | result_path ++= (drive + ":" + File.separator) | 
| 31520 
0a99c8716312
simplified IsabelleSystem.platform_path for cygwin;
 wenzelm parents: 
31500diff
changeset | 188 | rest | 
| 31796 | 189 |         case path if path.startsWith("/") =>
 | 
| 190 | result_path ++= platform_root | |
| 191 | path | |
| 192 | case path => path | |
| 31500 | 193 | } | 
| 31796 | 194 |     for (p <- rest.split("/") if p != "") {
 | 
| 195 | val len = result_path.length | |
| 196 | if (len > 0 && result_path(len - 1) != File.separatorChar) | |
| 197 | result_path += File.separatorChar | |
| 198 | result_path ++= p | |
| 27936 | 199 | } | 
| 200 | result_path.toString | |
| 201 | } | |
| 27953 | 202 | |
| 31498 | 203 | def platform_file(path: String) = new File(platform_path(path)) | 
| 29152 | 204 | |
| 27953 | 205 | |
| 31820 | 206 | /* isabelle_path */ | 
| 207 | ||
| 208 |   private val Platform_Root = new Regex("(?i)" +
 | |
| 31821 
ce6be870a5d3
isabelle_path: slightly more liberal root pattern;
 wenzelm parents: 
31820diff
changeset | 209 | Pattern.quote(platform_root) + """(?:\\+|\z)(.*)""") | 
| 31820 | 210 |   private val Drive = new Regex("""([a-zA-Z]):\\*(.*)""")
 | 
| 211 | ||
| 212 | def isabelle_path(platform_path: String): String = | |
| 213 |   {
 | |
| 31825 | 214 |     if (Platform.is_windows) {
 | 
| 31820 | 215 |       platform_path.replace('/', '\\') match {
 | 
| 216 |         case Platform_Root(rest) => "/" + rest.replace('\\', '/')
 | |
| 217 | case Drive(letter, rest) => | |
| 218 | drive_prefix + "/" + letter.toLowerCase(Locale.ENGLISH) + | |
| 219 |             (if (rest == "") "" else "/" + rest.replace('\\', '/'))
 | |
| 220 |         case path => path.replace('\\', '/')
 | |
| 221 | } | |
| 222 | } | |
| 223 | else platform_path | |
| 224 | } | |
| 225 | ||
| 226 | ||
| 31436 | 227 | /* source files */ | 
| 228 | ||
| 229 | private def try_file(file: File) = if (file.isFile) Some(file) else None | |
| 230 | ||
| 31498 | 231 | def source_file(path: String): Option[File] = | 
| 232 |   {
 | |
| 233 |     if (path.startsWith("/") || path == "")
 | |
| 31436 | 234 | try_file(platform_file(path)) | 
| 235 |     else {
 | |
| 236 |       val pure_file = platform_file("~~/src/Pure/" + path)
 | |
| 237 | if (pure_file.isFile) Some(pure_file) | |
| 238 |       else if (getenv("ML_SOURCES") != "")
 | |
| 239 |         try_file(platform_file("$ML_SOURCES/" + path))
 | |
| 240 | else None | |
| 241 | } | |
| 242 | } | |
| 243 | ||
| 244 | ||
| 32450 | 245 | |
| 31796 | 246 | /** system tools **/ | 
| 247 | ||
| 31498 | 248 | /* external processes */ | 
| 27953 | 249 | |
| 31498 | 250 | def execute(redirect: Boolean, args: String*): Process = | 
| 251 |   {
 | |
| 252 | val cmdline = | |
| 31825 | 253 |       if (Platform.is_windows) List(platform_path("/bin/env")) ++ args
 | 
| 31498 | 254 | else args | 
| 31796 | 255 | Isabelle_System.raw_execute(environment, redirect, cmdline: _*) | 
| 28046 | 256 | } | 
| 257 | ||
| 31818 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
31803diff
changeset | 258 | def isabelle_tool(name: String, args: String*): (String, Int) = | 
| 31498 | 259 |   {
 | 
| 31818 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
31803diff
changeset | 260 |     getenv_strict("ISABELLE_TOOLS").split(":").find(dir => {
 | 
| 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
31803diff
changeset | 261 | val file = platform_file(dir + "/" + name) | 
| 31824 | 262 |       try { file.isFile && file.canRead } //  file.canExecute requires Java 1.6
 | 
| 31818 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
31803diff
changeset | 263 |       catch { case _: SecurityException => false }
 | 
| 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
31803diff
changeset | 264 |     }) match {
 | 
| 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
31803diff
changeset | 265 | case Some(dir) => | 
| 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
31803diff
changeset | 266 | val proc = execute(true, (List(platform_path(dir + "/" + name)) ++ args): _*) | 
| 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
31803diff
changeset | 267 | Isabelle_System.process_output(proc) | 
| 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
31803diff
changeset | 268 |       case None => ("Unknown Isabelle tool: " + name, 2)
 | 
| 
f698f76a3713
builtin isabelle_tool for ML and Scala -- avoids excessive shell script (especially important for Cygwin);
 wenzelm parents: 
31803diff
changeset | 269 | } | 
| 28063 | 270 | } | 
| 271 | ||
| 272 | ||
| 273 | /* named pipes */ | |
| 274 | ||
| 31498 | 275 | def mk_fifo(): String = | 
| 276 |   {
 | |
| 28496 
4cff10648928
renamed isatool to isabelle_tool in programming interfaces;
 wenzelm parents: 
28063diff
changeset | 277 |     val (result, rc) = isabelle_tool("mkfifo")
 | 
| 28063 | 278 | if (rc == 0) result.trim | 
| 279 | else error(result) | |
| 280 | } | |
| 281 | ||
| 31498 | 282 | def rm_fifo(fifo: String) | 
| 283 |   {
 | |
| 28496 
4cff10648928
renamed isatool to isabelle_tool in programming interfaces;
 wenzelm parents: 
28063diff
changeset | 284 |     val (result, rc) = isabelle_tool("rmfifo", fifo)
 | 
| 28063 | 285 | if (rc != 0) error(result) | 
| 286 | } | |
| 287 | ||
| 31498 | 288 | def fifo_reader(fifo: String): BufferedReader = | 
| 289 |   {
 | |
| 29177 | 290 | // blocks until writer is ready | 
| 291 | val stream = | |
| 31825 | 292 | if (Platform.is_windows) execute(false, "cat", fifo).getInputStream | 
| 29177 | 293 | else new FileInputStream(fifo) | 
| 31796 | 294 | new BufferedReader(new InputStreamReader(stream, Isabelle_System.charset)) | 
| 29177 | 295 | } | 
| 28063 | 296 | |
| 29152 | 297 | |
| 32450 | 298 | |
| 31796 | 299 | /** Isabelle resources **/ | 
| 300 | ||
| 32328 | 301 | /* components */ | 
| 302 | ||
| 303 | def components(): List[String] = | |
| 304 |     getenv("ISABELLE_COMPONENTS").split(":").toList
 | |
| 305 | ||
| 306 | ||
| 29152 | 307 | /* find logics */ | 
| 308 | ||
| 31498 | 309 | def find_logics(): List[String] = | 
| 310 |   {
 | |
| 29152 | 311 |     val ml_ident = getenv_strict("ML_IDENTIFIER")
 | 
| 312 | var logics: Set[String] = Set() | |
| 313 |     for (dir <- getenv_strict("ISABELLE_PATH").split(":")) {
 | |
| 314 | val files = platform_file(dir + "/" + ml_ident).listFiles() | |
| 315 |       if (files != null) {
 | |
| 316 | for (file <- files if file.isFile) logics += file.getName | |
| 317 | } | |
| 318 | } | |
| 319 | logics.toList.sort(_ < _) | |
| 320 | } | |
| 29570 | 321 | |
| 322 | ||
| 323 | /* symbols */ | |
| 324 | ||
| 31498 | 325 | private def read_symbols(path: String): Iterator[String] = | 
| 326 |   {
 | |
| 29570 | 327 | val file = new File(platform_path(path)) | 
| 31436 | 328 | if (file.isFile) Source.fromFile(file).getLines | 
| 29570 | 329 | else Iterator.empty | 
| 330 | } | |
| 331 | val symbols = new Symbol.Interpretation( | |
| 332 |     read_symbols("$ISABELLE_HOME/etc/symbols") ++
 | |
| 333 |     read_symbols("$ISABELLE_HOME_USER/etc/symbols"))
 | |
| 27919 | 334 | } |