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 |
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) |