| author | wenzelm | 
| Mon, 08 Jan 2024 21:30:21 +0100 | |
| changeset 79433 | 88341f610b33 | 
| parent 78985 | 24e686fe043e | 
| permissions | -rw-r--r-- | 
| 72662 | 1 | /* Title: Pure/Tools/build_job.scala | 
| 2 | Author: Makarius | |
| 3 | ||
| 4 | Build job running prover process, with rudimentary PIDE session. | |
| 5 | */ | |
| 6 | ||
| 7 | package isabelle | |
| 8 | ||
| 9 | ||
| 10 | import scala.collection.mutable | |
| 11 | ||
| 12 | ||
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 13 | trait Build_Job {
 | 
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 14 | def cancel(): Unit = () | 
| 77298 | 15 | def is_finished: Boolean = false | 
| 78530 | 16 | def join: Option[Build_Job.Result] = None | 
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 17 | } | 
| 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 18 | |
| 75393 | 19 | object Build_Job {
 | 
| 78530 | 20 | sealed case class Result(process_result: Process_Result, output_shasum: SHA1.Shasum) | 
| 21 | ||
| 22 | ||
| 77396 | 23 | /* build session */ | 
| 24 | ||
| 77474 | 25 | def start_session( | 
| 78421 | 26 | build_context: Build.Context, | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78213diff
changeset | 27 | session_context: Session_Context, | 
| 77505 | 28 | progress: Progress, | 
| 29 | log: Logger, | |
| 78372 | 30 | server: SSH.Server, | 
| 77474 | 31 | session_background: Sessions.Background, | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78213diff
changeset | 32 | sources_shasum: SHA1.Shasum, | 
| 77474 | 33 | input_shasum: SHA1.Shasum, | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78213diff
changeset | 34 | node_info: Host.Node_Info, | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78213diff
changeset | 35 | store_heap: Boolean | 
| 77505 | 36 |   ): Session_Job = {
 | 
| 78372 | 37 | new Session_Job(build_context, session_context, progress, log, server, | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78213diff
changeset | 38 | session_background, sources_shasum, input_shasum, node_info, store_heap) | 
| 77505 | 39 | } | 
| 77474 | 40 | |
| 77442 | 41 |   object Session_Context {
 | 
| 42 | def load( | |
| 78374 | 43 | database_server: Option[SQL.Database], | 
| 77529 
40ccee0fe19a
separate static build_uuid from dynamic worker_uuid, to allow multiple worker processes participate in one build process;
 wenzelm parents: 
77521diff
changeset | 44 | build_uuid: String, | 
| 77444 
0c704aba71e3
clarified signature: reduce explicit access to static Sessions.Structure;
 wenzelm parents: 
77442diff
changeset | 45 | name: String, | 
| 
0c704aba71e3
clarified signature: reduce explicit access to static Sessions.Structure;
 wenzelm parents: 
77442diff
changeset | 46 | deps: List[String], | 
| 
0c704aba71e3
clarified signature: reduce explicit access to static Sessions.Structure;
 wenzelm parents: 
77442diff
changeset | 47 | ancestors: List[String], | 
| 77610 
3b09ae9e40cb
clarified session prefs (or "options" within the database);
 wenzelm parents: 
77587diff
changeset | 48 | session_prefs: String, | 
| 77458 | 49 | sources_shasum: SHA1.Shasum, | 
| 77442 | 50 | timeout: Time, | 
| 78178 | 51 | store: Store, | 
| 78374 | 52 | progress: Progress = new Progress | 
| 77442 | 53 |     ): Session_Context = {
 | 
| 77450 | 54 | def default: Session_Context = | 
| 77496 | 55 | Session_Context( | 
| 77610 
3b09ae9e40cb
clarified session prefs (or "options" within the database);
 wenzelm parents: 
77587diff
changeset | 56 | name, deps, ancestors, session_prefs, sources_shasum, timeout, | 
| 
3b09ae9e40cb
clarified session prefs (or "options" within the database);
 wenzelm parents: 
77587diff
changeset | 57 | Time.zero, Bytes.empty, build_uuid) | 
| 77444 
0c704aba71e3
clarified signature: reduce explicit access to static Sessions.Structure;
 wenzelm parents: 
77442diff
changeset | 58 | |
| 78374 | 59 |       def read(db: SQL.Database): Session_Context = {
 | 
| 60 |         def ignore_error(msg: String) = {
 | |
| 61 | progress.echo_warning( | |
| 62 | "Ignoring bad database " + db + " for session " + quote(name) + | |
| 63 | if_proper(msg, ":\n" + msg)) | |
| 64 | default | |
| 65 | } | |
| 66 |         try {
 | |
| 67 | val command_timings = store.read_command_timings(db, name) | |
| 68 | val elapsed = | |
| 69 |             store.read_session_timing(db, name) match {
 | |
| 70 | case Markup.Elapsed(s) => Time.seconds(s) | |
| 71 | case _ => Time.zero | |
| 72 | } | |
| 73 | new Session_Context( | |
| 74 | name, deps, ancestors, session_prefs, sources_shasum, timeout, | |
| 75 | elapsed, command_timings, build_uuid) | |
| 76 | } | |
| 77 |         catch {
 | |
| 78 | case ERROR(msg) => ignore_error(msg) | |
| 79 | case exn: java.lang.Error => ignore_error(Exn.message(exn)) | |
| 80 |           case _: XML.Error => ignore_error("XML.Error")
 | |
| 81 | } | |
| 82 | } | |
| 83 | ||
| 84 |       database_server match {
 | |
| 85 | case Some(db) => if (store.session_info_exists(db)) read(db) else default | |
| 86 | case None => using_option(store.try_open_database(name))(read) getOrElse default | |
| 77442 | 87 | } | 
| 88 | } | |
| 89 | } | |
| 90 | ||
| 77496 | 91 | sealed case class Session_Context( | 
| 92 | name: String, | |
| 93 | deps: List[String], | |
| 94 | ancestors: List[String], | |
| 77610 
3b09ae9e40cb
clarified session prefs (or "options" within the database);
 wenzelm parents: 
77587diff
changeset | 95 | session_prefs: String, | 
| 77496 | 96 | sources_shasum: SHA1.Shasum, | 
| 97 | timeout: Time, | |
| 98 | old_time: Time, | |
| 99 | old_command_timings_blob: Bytes, | |
| 77529 
40ccee0fe19a
separate static build_uuid from dynamic worker_uuid, to allow multiple worker processes participate in one build process;
 wenzelm parents: 
77521diff
changeset | 100 | build_uuid: String | 
| 77652 
5f706f7c624b
more thorough synchronization of internal "_state" vs. external "_database";
 wenzelm parents: 
77651diff
changeset | 101 | ) extends Library.Named | 
| 77442 | 102 | |
| 77474 | 103 | class Session_Job private[Build_Job]( | 
| 78421 | 104 | build_context: Build.Context, | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78213diff
changeset | 105 | session_context: Session_Context, | 
| 77505 | 106 | progress: Progress, | 
| 107 | log: Logger, | |
| 78372 | 108 | server: SSH.Server, | 
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 109 | session_background: Sessions.Background, | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78213diff
changeset | 110 | sources_shasum: SHA1.Shasum, | 
| 77460 
ccca589d7027
tuned signature: support general Build_Job instances;
 wenzelm parents: 
77458diff
changeset | 111 | input_shasum: SHA1.Shasum, | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78213diff
changeset | 112 | node_info: Host.Node_Info, | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78213diff
changeset | 113 | store_heap: Boolean | 
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 114 |   ) extends Build_Job {
 | 
| 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 115 | def session_name: String = session_background.session_name | 
| 77349 
5a84de89170d
more operations to support management of jobs, e.g. from external database;
 wenzelm parents: 
77298diff
changeset | 116 | |
| 78530 | 117 | private val future_result: Future[Option[Result]] = | 
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 118 |       Future.thread("build", uninterruptible = true) {
 | 
| 78359 
cb0a90df4871
prefer asynchronous operations: reduce time spent within synchronized_database("Build_Process.start_job");
 wenzelm parents: 
78280diff
changeset | 119 | val info = session_background.sessions_structure(session_name) | 
| 78842 
eb572f7b6689
prefer extensible next_node_info in build process over process_options in build engine (which needs the final node info anyway);
 Fabian Huch <huch@in.tum.de> parents: 
78674diff
changeset | 120 | val options = Host.node_options(info.options, node_info) | 
| 78359 
cb0a90df4871
prefer asynchronous operations: reduce time spent within synchronized_database("Build_Process.start_job");
 wenzelm parents: 
78280diff
changeset | 121 | |
| 
cb0a90df4871
prefer asynchronous operations: reduce time spent within synchronized_database("Build_Process.start_job");
 wenzelm parents: 
78280diff
changeset | 122 | val store = build_context.store | 
| 
cb0a90df4871
prefer asynchronous operations: reduce time spent within synchronized_database("Build_Process.start_job");
 wenzelm parents: 
78280diff
changeset | 123 | |
| 78372 | 124 |         using_optional(store.maybe_open_database_server(server = server)) { database_server =>
 | 
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 125 | |
| 78372 | 126 | store.clean_output(database_server, session_name, session_init = true) | 
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 127 | |
| 78372 | 128 | val session_sources = | 
| 129 | Store.Sources.load(session_background.base, cache = store.cache.compress) | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 130 | |
| 78372 | 131 | val env = | 
| 132 | Isabelle_System.settings( | |
| 133 |               List("ISABELLE_ML_DEBUGGER" -> options.bool("ML_debugger").toString))
 | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 134 | |
| 78372 | 135 | val session_heaps = | 
| 136 |             session_background.info.parent match {
 | |
| 137 | case None => Nil | |
| 138 | case Some(logic) => ML_Process.session_heaps(store, session_background, logic = logic) | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 139 | } | 
| 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 140 | |
| 78372 | 141 | val use_prelude = if (session_heaps.isEmpty) Thy_Header.ml_roots.map(_._1) else Nil | 
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 142 | |
| 78372 | 143 | val eval_store = | 
| 144 |             if (store_heap) {
 | |
| 145 |               (if (info.theories.nonEmpty) List("ML_Heap.share_common_data ()") else Nil) :::
 | |
| 146 |               List("ML_Heap.save_child " +
 | |
| 147 | ML_Syntax.print_string_bytes(File.platform_path(store.output_heap(session_name)))) | |
| 148 | } | |
| 149 | else Nil | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 150 | |
| 78372 | 151 | def session_blobs(node_name: Document.Node.Name): List[(Command.Blob, Document.Blobs.Item)] = | 
| 152 |             session_background.base.theory_load_commands.get(node_name.theory) match {
 | |
| 153 | case None => Nil | |
| 154 | case Some(spans) => | |
| 155 | val syntax = session_background.base.theory_syntax(node_name) | |
| 156 | val master_dir = Path.explode(node_name.master_dir) | |
| 157 | for (span <- spans; file <- span.loaded_files(syntax).files) | |
| 158 |                   yield {
 | |
| 159 | val src_path = Path.explode(file) | |
| 160 | val blob_name = Document.Node.Name(File.symbolic_path(master_dir + src_path)) | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 161 | |
| 78372 | 162 | val bytes = session_sources(blob_name.node).bytes | 
| 163 | val text = bytes.text | |
| 164 | val chunk = Symbol.Text_Chunk(text) | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 165 | |
| 78372 | 166 | Command.Blob(blob_name, src_path, Some((SHA1.digest(bytes), chunk))) -> | 
| 167 | Document.Blobs.Item(bytes, text, chunk, changed = false) | |
| 168 | } | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 169 | } | 
| 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 170 | |
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 171 | |
| 78372 | 172 | /* session */ | 
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 173 | |
| 78372 | 174 | val resources = | 
| 175 | new Resources(session_background, log = log, | |
| 176 | command_timings = | |
| 177 | Properties.uncompress(session_context.old_command_timings_blob, cache = store.cache)) | |
| 77485 | 178 | |
| 78372 | 179 | val session = | 
| 180 |             new Session(options, resources) {
 | |
| 181 | override val cache: Term.Cache = store.cache | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 182 | |
| 78372 | 183 | override def build_blobs_info(node_name: Document.Node.Name): Command.Blobs_Info = | 
| 184 | Command.Blobs_Info.make(session_blobs(node_name)) | |
| 185 | ||
| 186 | override def build_blobs(node_name: Document.Node.Name): Document.Blobs = | |
| 187 | Document.Blobs.make(session_blobs(node_name)) | |
| 188 | } | |
| 189 | ||
| 190 |           object Build_Session_Errors {
 | |
| 191 | private val promise: Promise[List[String]] = Future.promise | |
| 192 | ||
| 193 | def result: Exn.Result[List[String]] = promise.join_result | |
| 194 | def cancel(): Unit = promise.cancel() | |
| 195 |             def apply(errs: List[String]): Unit = {
 | |
| 196 |               try { promise.fulfill(errs) }
 | |
| 197 |               catch { case _: IllegalStateException => }
 | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 198 | } | 
| 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 199 | } | 
| 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 200 | |
| 78372 | 201 | val export_consumer = | 
| 202 | Export.consumer(store.open_database(session_name, output = true, server = server), | |
| 203 | store.cache, progress = progress) | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 204 | |
| 78372 | 205 | val stdout = new StringBuilder(1000) | 
| 206 | val stderr = new StringBuilder(1000) | |
| 207 | val command_timings = new mutable.ListBuffer[Properties.T] | |
| 208 | val theory_timings = new mutable.ListBuffer[Properties.T] | |
| 209 | val session_timings = new mutable.ListBuffer[Properties.T] | |
| 210 | val runtime_statistics = new mutable.ListBuffer[Properties.T] | |
| 211 | val task_statistics = new mutable.ListBuffer[Properties.T] | |
| 212 | ||
| 213 | def fun( | |
| 214 | name: String, | |
| 215 | acc: mutable.ListBuffer[Properties.T], | |
| 216 | unapply: Properties.T => Option[Properties.T] | |
| 217 |           ): (String, Session.Protocol_Function) = {
 | |
| 218 | name -> ((msg: Prover.Protocol_Output) => | |
| 219 |               unapply(msg.properties) match {
 | |
| 220 | case Some(props) => acc += props; true | |
| 221 | case _ => false | |
| 222 | }) | |
| 77485 | 223 | } | 
| 224 | ||
| 78372 | 225 |           session.init_protocol_handler(new Session.Protocol_Handler {
 | 
| 226 | override def exit(): Unit = Build_Session_Errors.cancel() | |
| 227 | ||
| 228 |               private def build_session_finished(msg: Prover.Protocol_Output): Boolean = {
 | |
| 229 | val (rc, errors) = | |
| 230 |                   try {
 | |
| 231 |                     val (rc, errs) = {
 | |
| 232 | import XML.Decode._ | |
| 233 | pair(int, list(x => x))(Symbol.decode_yxml(msg.text)) | |
| 234 | } | |
| 235 | val errors = | |
| 236 |                       for (err <- errs) yield {
 | |
| 237 | val prt = Protocol_Message.expose_no_reports(err) | |
| 238 | Pretty.string_of(prt, metric = Symbol.Metric) | |
| 239 | } | |
| 240 | (rc, errors) | |
| 241 | } | |
| 242 |                   catch { case ERROR(err) => (Process_Result.RC.failure, List(err)) }
 | |
| 243 | ||
| 244 |                 session.protocol_command("Prover.stop", rc.toString)
 | |
| 245 | Build_Session_Errors(errors) | |
| 246 | true | |
| 247 | } | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 248 | |
| 78372 | 249 | private def loading_theory(msg: Prover.Protocol_Output): Boolean = | 
| 250 |                 msg.properties match {
 | |
| 251 | case Markup.Loading_Theory(Markup.Name(name)) => | |
| 252 | progress.theory(Progress.Theory(name, session = session_name)) | |
| 253 | false | |
| 254 | case _ => false | |
| 255 | } | |
| 256 | ||
| 257 | private def export_(msg: Prover.Protocol_Output): Boolean = | |
| 258 |                 msg.properties match {
 | |
| 259 | case Protocol.Export(args) => | |
| 260 | export_consumer.make_entry(session_name, args, msg.chunk) | |
| 261 | true | |
| 262 | case _ => false | |
| 263 | } | |
| 264 | ||
| 265 | override val functions: Session.Protocol_Functions = | |
| 266 | List( | |
| 267 | Markup.Build_Session_Finished.name -> build_session_finished, | |
| 268 | Markup.Loading_Theory.name -> loading_theory, | |
| 269 | Markup.EXPORT -> export_, | |
| 270 | fun(Markup.Theory_Timing.name, theory_timings, Markup.Theory_Timing.unapply), | |
| 271 | fun(Markup.Session_Timing.name, session_timings, Markup.Session_Timing.unapply), | |
| 272 | fun(Markup.Task_Statistics.name, task_statistics, Markup.Task_Statistics.unapply)) | |
| 273 | }) | |
| 274 | ||
| 275 |           session.command_timings += Session.Consumer("command_timings") {
 | |
| 276 | case Session.Command_Timing(props) => | |
| 277 |               for {
 | |
| 278 | elapsed <- Markup.Elapsed.unapply(props) | |
| 279 | elapsed_time = Time.seconds(elapsed) | |
| 280 |                 if elapsed_time.is_relevant && elapsed_time >= options.seconds("command_timing_threshold")
 | |
| 281 | } command_timings += props.filter(Markup.command_timing_property) | |
| 282 | } | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 283 | |
| 78372 | 284 |           session.runtime_statistics += Session.Consumer("ML_statistics") {
 | 
| 285 | case Session.Runtime_Statistics(props) => runtime_statistics += props | |
| 286 | } | |
| 287 | ||
| 288 |           session.finished_theories += Session.Consumer[Document.Snapshot]("finished_theories") {
 | |
| 289 | case snapshot => | |
| 290 |               if (!progress.stopped) {
 | |
| 291 |                 def export_(name: String, xml: XML.Body, compress: Boolean = true): Unit = {
 | |
| 292 |                   if (!progress.stopped) {
 | |
| 293 | val theory_name = snapshot.node_name.theory | |
| 294 | val args = | |
| 295 | Protocol.Export.Args(theory_name = theory_name, name = name, compress = compress) | |
| 296 | val body = Bytes(Symbol.encode(YXML.string_of_body(xml))) | |
| 297 | export_consumer.make_entry(session_name, args, body) | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 298 | } | 
| 78372 | 299 | } | 
| 300 | def export_text(name: String, text: String, compress: Boolean = true): Unit = | |
| 301 | export_(name, List(XML.Text(text)), compress = compress) | |
| 302 | ||
| 303 |                 for (command <- snapshot.snippet_command) {
 | |
| 304 | export_text(Export.DOCUMENT_ID, command.id.toString, compress = false) | |
| 305 | } | |
| 306 | ||
| 307 | export_text(Export.FILES, | |
| 308 | cat_lines(snapshot.node_files.map(name => File.symbolic_path(name.path))), | |
| 309 | compress = false) | |
| 310 | ||
| 311 |                 for ((blob_name, i) <- snapshot.node_files.tail.zipWithIndex) {
 | |
| 312 | val xml = snapshot.switch(blob_name).xml_markup() | |
| 313 | export_(Export.MARKUP + (i + 1), xml) | |
| 314 | } | |
| 315 | export_(Export.MARKUP, snapshot.xml_markup()) | |
| 316 | export_(Export.MESSAGES, snapshot.messages.map(_._1)) | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 317 | } | 
| 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 318 | } | 
| 78372 | 319 | |
| 320 |           session.all_messages += Session.Consumer[Any]("build_session_output") {
 | |
| 321 | case msg: Prover.Output => | |
| 322 | val message = msg.message | |
| 323 | if (msg.is_system) resources.log(Protocol.message_text(message)) | |
| 324 | ||
| 325 |               if (msg.is_stdout) {
 | |
| 326 | stdout ++= Symbol.encode(XML.content(message)) | |
| 327 | } | |
| 328 |               else if (msg.is_stderr) {
 | |
| 329 | stderr ++= Symbol.encode(XML.content(message)) | |
| 330 | } | |
| 331 |               else if (msg.is_exit) {
 | |
| 332 | val err = | |
| 333 | "Prover terminated" + | |
| 334 |                     (msg.properties match {
 | |
| 335 | case Markup.Process_Result(result) => ": " + result.print_rc | |
| 336 | case _ => "" | |
| 337 | }) | |
| 338 | Build_Session_Errors(List(err)) | |
| 339 | } | |
| 340 | case _ => | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 341 | } | 
| 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 342 | |
| 78372 | 343 | build_context.session_setup(session_name, session) | 
| 344 | ||
| 345 |           val eval_main = Command_Line.ML_tool("Isabelle_Process.init_build ()" :: eval_store)
 | |
| 346 | ||
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 347 | |
| 78372 | 348 | /* process */ | 
| 349 | ||
| 350 | val process = | |
| 351 | Isabelle_Process.start(options, session, session_background, session_heaps, | |
| 352 | use_prelude = use_prelude, eval_main = eval_main, cwd = info.dir.file, env = env) | |
| 353 | ||
| 354 | val timeout_request: Option[Event_Timer.Request] = | |
| 355 | if (info.timeout_ignored) None | |
| 356 |             else Some(Event_Timer.request(Time.now() + info.timeout) { process.terminate() })
 | |
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 357 | |
| 78372 | 358 | val build_errors = | 
| 359 |             Isabelle_Thread.interrupt_handler(_ => process.terminate()) {
 | |
| 360 |               Exn.capture { process.await_startup() } match {
 | |
| 361 | case Exn.Res(_) => | |
| 362 | val resources_yxml = resources.init_session_yxml | |
| 363 | val encode_options: XML.Encode.T[Options] = | |
| 364 | options => session.prover_options(options).encode | |
| 365 | val args_yxml = | |
| 366 | YXML.string_of_body( | |
| 367 |                       {
 | |
| 368 | import XML.Encode._ | |
| 369 | pair(string, list(pair(encode_options, list(pair(string, properties)))))( | |
| 370 | (session_name, info.theories)) | |
| 371 | }) | |
| 372 |                   session.protocol_command("build_session", resources_yxml, args_yxml)
 | |
| 373 | Build_Session_Errors.result | |
| 374 | case Exn.Exn(exn) => Exn.Res(List(Exn.message(exn))) | |
| 375 | } | |
| 376 | } | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 377 | |
| 78372 | 378 | val result0 = | 
| 379 |             Isabelle_Thread.interrupt_handler(_ => process.terminate()) { process.await_shutdown() }
 | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 380 | |
| 78372 | 381 | val was_timeout = | 
| 382 |             timeout_request match {
 | |
| 383 | case None => false | |
| 384 | case Some(request) => !request.cancel() | |
| 385 | } | |
| 386 | ||
| 387 | session.stop() | |
| 388 | ||
| 389 | val export_errors = | |
| 390 | export_consumer.shutdown(close = true).map(Output.error_message_text) | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 391 | |
| 78372 | 392 | val (document_output, document_errors) = | 
| 393 |             try {
 | |
| 78674 | 394 |               if (Exn.is_res(build_errors) && result0.ok && info.documents.nonEmpty) {
 | 
| 78379 | 395 |                 using(Export.open_database_context(store, server = server)) { database_context =>
 | 
| 78372 | 396 | val documents = | 
| 397 |                     using(database_context.open_session(session_background)) {
 | |
| 398 | session_context => | |
| 399 | Document_Build.build_documents( | |
| 400 | Document_Build.context(session_context, progress = progress), | |
| 401 | output_sources = info.document_output, | |
| 402 | output_pdf = info.document_output) | |
| 403 | } | |
| 404 | using(database_context.open_database(session_name, output = true))(session_database => | |
| 405 | documents.foreach(_.write(session_database.db, session_name))) | |
| 406 | (documents.flatMap(_.log_lines), Nil) | |
| 407 | } | |
| 77484 | 408 | } | 
| 78372 | 409 | else (Nil, Nil) | 
| 410 | } | |
| 411 |             catch {
 | |
| 412 | case exn: Document_Build.Build_Error => (exn.log_lines, exn.log_errors) | |
| 413 | case Exn.Interrupt.ERROR(msg) => (Nil, List(msg)) | |
| 414 | } | |
| 77485 | 415 | |
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 416 | |
| 78372 | 417 | /* process result */ | 
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 418 | |
| 78372 | 419 |           val result1 = {
 | 
| 420 | val theory_timing = | |
| 421 | theory_timings.iterator.flatMap( | |
| 422 |                 {
 | |
| 423 | case props @ Markup.Name(name) => Some(name -> props) | |
| 424 | case _ => None | |
| 425 | }).toMap | |
| 426 | val used_theory_timings = | |
| 427 |               for { (name, _) <- session_background.base.used_theories }
 | |
| 428 | yield theory_timing.getOrElse(name.theory, Markup.Name(name.theory)) | |
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 429 | |
| 78372 | 430 | val more_output = | 
| 431 | Library.trim_line(stdout.toString) :: | |
| 432 | command_timings.toList.map(Protocol.Command_Timing_Marker.apply) ::: | |
| 433 | used_theory_timings.map(Protocol.Theory_Timing_Marker.apply) ::: | |
| 434 | session_timings.toList.map(Protocol.Session_Timing_Marker.apply) ::: | |
| 435 | runtime_statistics.toList.map(Protocol.ML_Statistics_Marker.apply) ::: | |
| 436 | task_statistics.toList.map(Protocol.Task_Statistics_Marker.apply) ::: | |
| 437 | document_output | |
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 438 | |
| 78372 | 439 | result0.output(more_output) | 
| 440 | .error(Library.trim_line(stderr.toString)) | |
| 441 | .errors_rc(export_errors ::: document_errors) | |
| 442 | } | |
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 443 | |
| 78372 | 444 | val result2 = | 
| 445 |             build_errors match {
 | |
| 446 | case Exn.Res(build_errs) => | |
| 447 | val errs = build_errs ::: document_errors | |
| 448 |                 if (errs.nonEmpty) {
 | |
| 449 | result1.error_rc.output( | |
| 450 | errs.flatMap(s => split_lines(Output.error_message_text(s))) ::: | |
| 451 | errs.map(Protocol.Error_Message_Marker.apply)) | |
| 452 | } | |
| 453 | else if (progress.stopped && result1.ok) result1.copy(rc = Process_Result.RC.interrupt) | |
| 454 | else result1 | |
| 455 | case Exn.Exn(Exn.Interrupt()) => | |
| 456 | if (result1.ok) result1.copy(rc = Process_Result.RC.interrupt) | |
| 457 | else result1 | |
| 458 | case Exn.Exn(exn) => throw exn | |
| 459 | } | |
| 460 | ||
| 461 | val process_result = | |
| 462 | if (result2.ok) result2 | |
| 463 |             else if (was_timeout) result2.error(Output.error_message_text("Timeout")).timeout_rc
 | |
| 464 |             else if (result2.interrupted) result2.error(Output.error_message_text("Interrupt"))
 | |
| 465 | else result2 | |
| 466 | ||
| 467 | ||
| 468 | /* output heap */ | |
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 469 | |
| 78372 | 470 |           val output_shasum = {
 | 
| 471 | val heap = store.output_heap(session_name) | |
| 472 |             if (process_result.ok && store_heap && heap.is_file) {
 | |
| 473 |               val slice = Space.MiB(options.real("build_database_slice")).bytes
 | |
| 474 | val digest = ML_Heap.store(database_server, session_name, heap, slice) | |
| 475 | SHA1.shasum(digest, session_name) | |
| 476 | } | |
| 477 | else SHA1.no_shasum | |
| 478 | } | |
| 479 | ||
| 480 | val log_lines = process_result.out_lines.filterNot(Protocol_Message.Marker.test) | |
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 481 | |
| 78372 | 482 | val build_log = | 
| 78985 
24e686fe043e
clarified Log_File.cache: reuse existing Store.cache / Build_Log.Store.cache;
 wenzelm parents: 
78842diff
changeset | 483 | Build_Log.Log_File(session_name, process_result.out_lines, cache = store.cache). | 
| 78372 | 484 | parse_session_info( | 
| 485 | command_timings = true, | |
| 486 | theory_timings = true, | |
| 487 | ml_statistics = true, | |
| 488 | task_statistics = true) | |
| 489 | ||
| 490 | // write log file | |
| 491 |           if (process_result.ok) {
 | |
| 492 | File.write_gzip(store.output_log_gz(session_name), terminate_lines(log_lines)) | |
| 493 | } | |
| 494 | else File.write(store.output_log(session_name), terminate_lines(log_lines)) | |
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 495 | |
| 78372 | 496 | // write database | 
| 78374 | 497 | def write_info(db: SQL.Database): Unit = | 
| 78372 | 498 | store.write_session_info(db, session_name, session_sources, | 
| 499 | build_log = | |
| 500 |                 if (process_result.timeout) build_log.error("Timeout") else build_log,
 | |
| 501 | build = | |
| 502 | Store.Build_Info( | |
| 503 | sources = sources_shasum, | |
| 504 | input_heaps = input_shasum, | |
| 505 | output_heap = output_shasum, | |
| 506 | process_result.rc, | |
| 78374 | 507 | build_context.build_uuid)) | 
| 78529 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78435diff
changeset | 508 | |
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78435diff
changeset | 509 | val valid = | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78435diff
changeset | 510 | if (progress.stopped_local) false | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78435diff
changeset | 511 |             else {
 | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78435diff
changeset | 512 |               database_server match {
 | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78435diff
changeset | 513 | case Some(db) => write_info(db) | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78435diff
changeset | 514 | case None => using(store.open_database(session_name, output = true))(write_info) | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78435diff
changeset | 515 | } | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78435diff
changeset | 516 | true | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78435diff
changeset | 517 | } | 
| 78372 | 518 | |
| 519 | // messages | |
| 520 | process_result.err_lines.foreach(progress.echo(_)) | |
| 521 | ||
| 522 |           if (process_result.ok) {
 | |
| 523 | val props = build_log.session_timing | |
| 524 | val threads = Markup.Session_Timing.Threads.unapply(props) getOrElse 1 | |
| 525 | val timing = Markup.Timing_Properties.get(props) | |
| 526 | progress.echo( | |
| 527 |               "Timing " + session_name + " (" + threads + " threads, " + timing.message_factor + ")",
 | |
| 528 | verbose = true) | |
| 529 | progress.echo( | |
| 530 |               "Finished " + session_name + " (" + process_result.timing.message_resources + ")")
 | |
| 531 | } | |
| 532 |           else {
 | |
| 533 | progress.echo( | |
| 534 | session_name + " FAILED (see also \"isabelle build_log -H Error " + session_name + "\")") | |
| 535 |             if (!process_result.interrupted) {
 | |
| 536 |               val tail = info.options.int("process_output_tail")
 | |
| 537 | val suffix = if (tail == 0) log_lines else log_lines.drop(log_lines.length - tail max 0) | |
| 538 |               val prefix = if (log_lines.length == suffix.length) Nil else List("...")
 | |
| 539 | progress.echo(Library.trim_line(cat_lines(prefix ::: suffix))) | |
| 540 | } | |
| 541 | } | |
| 542 | ||
| 78530 | 543 | if (valid) Some(Result(process_result.copy(out_lines = log_lines), output_shasum)) | 
| 544 | else None | |
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 545 | } | 
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 546 | } | 
| 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 547 | |
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 548 | override def cancel(): Unit = future_result.cancel() | 
| 77298 | 549 | override def is_finished: Boolean = future_result.is_finished | 
| 78530 | 550 | override def join: Option[Result] = future_result.join | 
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 551 | } | 
| 72848 
d5d0e36eda16
read theory with PIDE markup from session database;
 wenzelm parents: 
72844diff
changeset | 552 | } |