| author | wenzelm | 
| Sun, 02 Feb 2025 17:05:06 +0100 | |
| changeset 82052 | 0295cacff486 | 
| parent 81414 | ed4ff84e9b21 | 
| child 82706 | e9b9af6da795 | 
| permissions | -rw-r--r-- | 
| 79502 | 1 | /* Title: Pure/Build/build_job.scala | 
| 72662 | 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 | 
| 79887 
17220dc05991
revert most parts of 0e79fa88cab6: somewhat ambitious attempt to move towards "editing" builds via added/canceled workers;
 wenzelm parents: 
79718diff
changeset | 16 | def join: Build_Job.Result = Build_Job.no_result | 
| 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) | 
| 79887 
17220dc05991
revert most parts of 0e79fa88cab6: somewhat ambitious attempt to move towards "editing" builds via added/canceled workers;
 wenzelm parents: 
79718diff
changeset | 21 | val no_result: Result = Result(Process_Result.undefined, SHA1.no_shasum) | 
| 78530 | 22 | |
| 23 | ||
| 77396 | 24 | /* build session */ | 
| 25 | ||
| 77474 | 26 | def start_session( | 
| 78421 | 27 | build_context: Build.Context, | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78213diff
changeset | 28 | session_context: Session_Context, | 
| 77505 | 29 | progress: Progress, | 
| 30 | log: Logger, | |
| 78372 | 31 | server: SSH.Server, | 
| 77474 | 32 | session_background: Sessions.Background, | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78213diff
changeset | 33 | sources_shasum: SHA1.Shasum, | 
| 77474 | 34 | input_shasum: SHA1.Shasum, | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78213diff
changeset | 35 | node_info: Host.Node_Info, | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78213diff
changeset | 36 | store_heap: Boolean | 
| 77505 | 37 |   ): Session_Job = {
 | 
| 78372 | 38 | 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 | 39 | session_background, sources_shasum, input_shasum, node_info, store_heap) | 
| 77505 | 40 | } | 
| 77474 | 41 | |
| 77442 | 42 |   object Session_Context {
 | 
| 43 | def load( | |
| 78374 | 44 | 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 | 45 | build_uuid: String, | 
| 77444 
0c704aba71e3
clarified signature: reduce explicit access to static Sessions.Structure;
 wenzelm parents: 
77442diff
changeset | 46 | name: String, | 
| 
0c704aba71e3
clarified signature: reduce explicit access to static Sessions.Structure;
 wenzelm parents: 
77442diff
changeset | 47 | deps: List[String], | 
| 
0c704aba71e3
clarified signature: reduce explicit access to static Sessions.Structure;
 wenzelm parents: 
77442diff
changeset | 48 | ancestors: List[String], | 
| 77610 
3b09ae9e40cb
clarified session prefs (or "options" within the database);
 wenzelm parents: 
77587diff
changeset | 49 | session_prefs: String, | 
| 77458 | 50 | sources_shasum: SHA1.Shasum, | 
| 77442 | 51 | timeout: Time, | 
| 78178 | 52 | store: Store, | 
| 78374 | 53 | progress: Progress = new Progress | 
| 77442 | 54 |     ): Session_Context = {
 | 
| 77450 | 55 | def default: Session_Context = | 
| 77496 | 56 | Session_Context( | 
| 77610 
3b09ae9e40cb
clarified session prefs (or "options" within the database);
 wenzelm parents: 
77587diff
changeset | 57 | name, deps, ancestors, session_prefs, sources_shasum, timeout, | 
| 
3b09ae9e40cb
clarified session prefs (or "options" within the database);
 wenzelm parents: 
77587diff
changeset | 58 | Time.zero, Bytes.empty, build_uuid) | 
| 77444 
0c704aba71e3
clarified signature: reduce explicit access to static Sessions.Structure;
 wenzelm parents: 
77442diff
changeset | 59 | |
| 78374 | 60 |       def read(db: SQL.Database): Session_Context = {
 | 
| 61 |         def ignore_error(msg: String) = {
 | |
| 62 | progress.echo_warning( | |
| 63 | "Ignoring bad database " + db + " for session " + quote(name) + | |
| 64 | if_proper(msg, ":\n" + msg)) | |
| 65 | default | |
| 66 | } | |
| 67 |         try {
 | |
| 68 | val command_timings = store.read_command_timings(db, name) | |
| 69 | val elapsed = | |
| 70 |             store.read_session_timing(db, name) match {
 | |
| 71 | case Markup.Elapsed(s) => Time.seconds(s) | |
| 72 | case _ => Time.zero | |
| 73 | } | |
| 74 | new Session_Context( | |
| 75 | name, deps, ancestors, session_prefs, sources_shasum, timeout, | |
| 76 | elapsed, command_timings, build_uuid) | |
| 77 | } | |
| 78 |         catch {
 | |
| 79 | case ERROR(msg) => ignore_error(msg) | |
| 80 | case exn: java.lang.Error => ignore_error(Exn.message(exn)) | |
| 81 |           case _: XML.Error => ignore_error("XML.Error")
 | |
| 82 | } | |
| 83 | } | |
| 84 | ||
| 85 |       database_server match {
 | |
| 86 | case Some(db) => if (store.session_info_exists(db)) read(db) else default | |
| 87 | case None => using_option(store.try_open_database(name))(read) getOrElse default | |
| 77442 | 88 | } | 
| 89 | } | |
| 90 | } | |
| 91 | ||
| 77496 | 92 | sealed case class Session_Context( | 
| 93 | name: String, | |
| 94 | deps: List[String], | |
| 95 | ancestors: List[String], | |
| 77610 
3b09ae9e40cb
clarified session prefs (or "options" within the database);
 wenzelm parents: 
77587diff
changeset | 96 | session_prefs: String, | 
| 77496 | 97 | sources_shasum: SHA1.Shasum, | 
| 98 | timeout: Time, | |
| 99 | old_time: Time, | |
| 100 | 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 | 101 | build_uuid: String | 
| 80270 | 102 | ) extends Name.T | 
| 77442 | 103 | |
| 77474 | 104 | class Session_Job private[Build_Job]( | 
| 78421 | 105 | build_context: Build.Context, | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78213diff
changeset | 106 | session_context: Session_Context, | 
| 77505 | 107 | progress: Progress, | 
| 108 | log: Logger, | |
| 78372 | 109 | server: SSH.Server, | 
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 110 | session_background: Sessions.Background, | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78213diff
changeset | 111 | sources_shasum: SHA1.Shasum, | 
| 77460 
ccca589d7027
tuned signature: support general Build_Job instances;
 wenzelm parents: 
77458diff
changeset | 112 | input_shasum: SHA1.Shasum, | 
| 78237 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78213diff
changeset | 113 | node_info: Host.Node_Info, | 
| 
c2c59de57df9
clarified static Build_Process.Context vs. dynamic Build_Process.State;
 wenzelm parents: 
78213diff
changeset | 114 | store_heap: Boolean | 
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 115 |   ) extends Build_Job {
 | 
| 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 116 | 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 | 117 | |
| 79887 
17220dc05991
revert most parts of 0e79fa88cab6: somewhat ambitious attempt to move towards "editing" builds via added/canceled workers;
 wenzelm parents: 
79718diff
changeset | 118 | private val future_result: Future[Result] = | 
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 119 |       Future.thread("build", uninterruptible = true) {
 | 
| 78359 
cb0a90df4871
prefer asynchronous operations: reduce time spent within synchronized_database("Build_Process.start_job");
 wenzelm parents: 
78280diff
changeset | 120 | 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 | 121 | 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 | 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 =>
 | 
| 125 | 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 | 126 | |
| 78372 | 127 | val session_sources = | 
| 128 | 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 | 129 | |
| 78372 | 130 | val env = | 
| 131 | Isabelle_System.settings( | |
| 132 |               List("ISABELLE_ML_DEBUGGER" -> options.bool("ML_debugger").toString))
 | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 133 | |
| 78372 | 134 | val session_heaps = | 
| 135 |             session_background.info.parent match {
 | |
| 136 | case None => Nil | |
| 137 | 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 | 138 | } | 
| 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 139 | |
| 78372 | 140 | 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 | 141 | |
| 78372 | 142 | val eval_store = | 
| 143 |             if (store_heap) {
 | |
| 144 |               (if (info.theories.nonEmpty) List("ML_Heap.share_common_data ()") else Nil) :::
 | |
| 145 |               List("ML_Heap.save_child " +
 | |
| 146 | ML_Syntax.print_string_bytes(File.platform_path(store.output_heap(session_name)))) | |
| 147 | } | |
| 148 | else Nil | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 149 | |
| 78372 | 150 | def session_blobs(node_name: Document.Node.Name): List[(Command.Blob, Document.Blobs.Item)] = | 
| 151 |             session_background.base.theory_load_commands.get(node_name.theory) match {
 | |
| 152 | case None => Nil | |
| 153 | case Some(spans) => | |
| 154 | val syntax = session_background.base.theory_syntax(node_name) | |
| 155 | val master_dir = Path.explode(node_name.master_dir) | |
| 156 | for (span <- spans; file <- span.loaded_files(syntax).files) | |
| 157 |                   yield {
 | |
| 158 | val src_path = Path.explode(file) | |
| 159 | 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 | 160 | |
| 78372 | 161 | val bytes = session_sources(blob_name.node).bytes | 
| 162 | val text = bytes.text | |
| 163 | val chunk = Symbol.Text_Chunk(text) | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 164 | |
| 78372 | 165 | Command.Blob(blob_name, src_path, Some((SHA1.digest(bytes), chunk))) -> | 
| 166 | Document.Blobs.Item(bytes, text, chunk, changed = false) | |
| 167 | } | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 168 | } | 
| 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 169 | |
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 170 | |
| 78372 | 171 | /* session */ | 
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 172 | |
| 78372 | 173 | val resources = | 
| 174 | new Resources(session_background, log = log, | |
| 175 | command_timings = | |
| 176 | Properties.uncompress(session_context.old_command_timings_blob, cache = store.cache)) | |
| 77485 | 177 | |
| 78372 | 178 | val session = | 
| 179 |             new Session(options, resources) {
 | |
| 180 | override val cache: Term.Cache = store.cache | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 181 | |
| 78372 | 182 | override def build_blobs_info(node_name: Document.Node.Name): Command.Blobs_Info = | 
| 183 | Command.Blobs_Info.make(session_blobs(node_name)) | |
| 184 | ||
| 185 | override def build_blobs(node_name: Document.Node.Name): Document.Blobs = | |
| 186 | Document.Blobs.make(session_blobs(node_name)) | |
| 187 | } | |
| 188 | ||
| 189 |           object Build_Session_Errors {
 | |
| 190 | private val promise: Promise[List[String]] = Future.promise | |
| 191 | ||
| 192 | def result: Exn.Result[List[String]] = promise.join_result | |
| 193 | def cancel(): Unit = promise.cancel() | |
| 194 |             def apply(errs: List[String]): Unit = {
 | |
| 195 |               try { promise.fulfill(errs) }
 | |
| 196 |               catch { case _: IllegalStateException => }
 | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 197 | } | 
| 
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 | |
| 78372 | 200 | val export_consumer = | 
| 201 | Export.consumer(store.open_database(session_name, output = true, server = server), | |
| 202 | store.cache, progress = progress) | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 203 | |
| 78372 | 204 | val stdout = new StringBuilder(1000) | 
| 205 | val stderr = new StringBuilder(1000) | |
| 206 | val command_timings = new mutable.ListBuffer[Properties.T] | |
| 207 | val theory_timings = new mutable.ListBuffer[Properties.T] | |
| 208 | val session_timings = new mutable.ListBuffer[Properties.T] | |
| 209 | val runtime_statistics = new mutable.ListBuffer[Properties.T] | |
| 210 | val task_statistics = new mutable.ListBuffer[Properties.T] | |
| 211 | ||
| 212 | def fun( | |
| 213 | name: String, | |
| 214 | acc: mutable.ListBuffer[Properties.T], | |
| 215 | unapply: Properties.T => Option[Properties.T] | |
| 216 |           ): (String, Session.Protocol_Function) = {
 | |
| 217 | name -> ((msg: Prover.Protocol_Output) => | |
| 218 |               unapply(msg.properties) match {
 | |
| 219 | case Some(props) => acc += props; true | |
| 220 | case _ => false | |
| 221 | }) | |
| 77485 | 222 | } | 
| 223 | ||
| 78372 | 224 |           session.init_protocol_handler(new Session.Protocol_Handler {
 | 
| 225 | override def exit(): Unit = Build_Session_Errors.cancel() | |
| 226 | ||
| 227 |               private def build_session_finished(msg: Prover.Protocol_Output): Boolean = {
 | |
| 228 | val (rc, errors) = | |
| 229 |                   try {
 | |
| 230 |                     val (rc, errs) = {
 | |
| 231 | import XML.Decode._ | |
| 80463 | 232 | pair(int, list(self))(Symbol.decode_yxml(msg.text)) | 
| 78372 | 233 | } | 
| 234 | val errors = | |
| 235 |                       for (err <- errs) yield {
 | |
| 80872 | 236 | Pretty.string_of(err, metric = Symbol.Metric, pure = true) | 
| 78372 | 237 | } | 
| 238 | (rc, errors) | |
| 239 | } | |
| 240 |                   catch { case ERROR(err) => (Process_Result.RC.failure, List(err)) }
 | |
| 241 | ||
| 80462 
7a1f9e571046
clarified signature: more explicit XML.Body types, more uniform Symbol.encode_yxml;
 wenzelm parents: 
80437diff
changeset | 242 |                 session.protocol_command("Prover.stop", XML.Encode.int(rc))
 | 
| 78372 | 243 | Build_Session_Errors(errors) | 
| 244 | true | |
| 245 | } | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 246 | |
| 78372 | 247 | private def loading_theory(msg: Prover.Protocol_Output): Boolean = | 
| 248 |                 msg.properties match {
 | |
| 249 | case Markup.Loading_Theory(Markup.Name(name)) => | |
| 250 | progress.theory(Progress.Theory(name, session = session_name)) | |
| 251 | false | |
| 252 | case _ => false | |
| 253 | } | |
| 254 | ||
| 255 | private def export_(msg: Prover.Protocol_Output): Boolean = | |
| 256 |                 msg.properties match {
 | |
| 257 | case Protocol.Export(args) => | |
| 258 | export_consumer.make_entry(session_name, args, msg.chunk) | |
| 259 | true | |
| 260 | case _ => false | |
| 261 | } | |
| 262 | ||
| 263 | override val functions: Session.Protocol_Functions = | |
| 264 | List( | |
| 265 | Markup.Build_Session_Finished.name -> build_session_finished, | |
| 266 | Markup.Loading_Theory.name -> loading_theory, | |
| 267 | Markup.EXPORT -> export_, | |
| 268 | fun(Markup.Theory_Timing.name, theory_timings, Markup.Theory_Timing.unapply), | |
| 269 | fun(Markup.Session_Timing.name, session_timings, Markup.Session_Timing.unapply), | |
| 270 | fun(Markup.Task_Statistics.name, task_statistics, Markup.Task_Statistics.unapply)) | |
| 271 | }) | |
| 272 | ||
| 273 |           session.command_timings += Session.Consumer("command_timings") {
 | |
| 274 | case Session.Command_Timing(props) => | |
| 275 |               for {
 | |
| 276 | elapsed <- Markup.Elapsed.unapply(props) | |
| 277 | elapsed_time = Time.seconds(elapsed) | |
| 79692 | 278 | if elapsed_time.is_relevant && | 
| 279 |                    elapsed_time >= options.seconds("command_timing_threshold")
 | |
| 78372 | 280 | } command_timings += props.filter(Markup.command_timing_property) | 
| 281 | } | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 282 | |
| 78372 | 283 |           session.runtime_statistics += Session.Consumer("ML_statistics") {
 | 
| 284 | case Session.Runtime_Statistics(props) => runtime_statistics += props | |
| 285 | } | |
| 286 | ||
| 287 |           session.finished_theories += Session.Consumer[Document.Snapshot]("finished_theories") {
 | |
| 288 | case snapshot => | |
| 289 |               if (!progress.stopped) {
 | |
| 290 |                 def export_(name: String, xml: XML.Body, compress: Boolean = true): Unit = {
 | |
| 291 |                   if (!progress.stopped) {
 | |
| 292 | val theory_name = snapshot.node_name.theory | |
| 293 | val args = | |
| 79692 | 294 | Protocol.Export.Args( | 
| 295 | theory_name = theory_name, name = name, compress = compress) | |
| 80437 
2c07b9b2f9f4
minor performance tuning: more direct Bytes with Symbol.encode;
 wenzelm parents: 
80270diff
changeset | 296 | val body = YXML.bytes_of_body(xml, recode = Symbol.encode) | 
| 78372 | 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 | ||
| 81414 
ed4ff84e9b21
Document.Snapshot: support for multiple snippet_commands;
 wenzelm parents: 
80872diff
changeset | 303 |                 for (command <- snapshot.snippet_commands) {
 | 
| 78372 | 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, | |
| 80224 
db92e0b6a11a
clarified signature: prefer symbolic isabelle.Path over physical java.io.File;
 wenzelm parents: 
80121diff
changeset | 352 | use_prelude = use_prelude, eval_main = eval_main, cwd = info.dir, env = env) | 
| 78372 | 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(_) => | |
| 80462 
7a1f9e571046
clarified signature: more explicit XML.Body types, more uniform Symbol.encode_yxml;
 wenzelm parents: 
80437diff
changeset | 362 | val resources_xml = resources.init_session_xml | 
| 78372 | 363 | val encode_options: XML.Encode.T[Options] = | 
| 364 | options => session.prover_options(options).encode | |
| 80462 
7a1f9e571046
clarified signature: more explicit XML.Body types, more uniform Symbol.encode_yxml;
 wenzelm parents: 
80437diff
changeset | 365 | val args_xml = | 
| 
7a1f9e571046
clarified signature: more explicit XML.Body types, more uniform Symbol.encode_yxml;
 wenzelm parents: 
80437diff
changeset | 366 |                     {
 | 
| 
7a1f9e571046
clarified signature: more explicit XML.Body types, more uniform Symbol.encode_yxml;
 wenzelm parents: 
80437diff
changeset | 367 | import XML.Encode._ | 
| 
7a1f9e571046
clarified signature: more explicit XML.Body types, more uniform Symbol.encode_yxml;
 wenzelm parents: 
80437diff
changeset | 368 | pair(string, list(pair(encode_options, list(pair(string, properties)))))( | 
| 
7a1f9e571046
clarified signature: more explicit XML.Body types, more uniform Symbol.encode_yxml;
 wenzelm parents: 
80437diff
changeset | 369 | (session_name, info.theories)) | 
| 
7a1f9e571046
clarified signature: more explicit XML.Body types, more uniform Symbol.encode_yxml;
 wenzelm parents: 
80437diff
changeset | 370 | } | 
| 
7a1f9e571046
clarified signature: more explicit XML.Body types, more uniform Symbol.encode_yxml;
 wenzelm parents: 
80437diff
changeset | 371 |                   session.protocol_command("build_session", resources_xml, args_xml)
 | 
| 78372 | 372 | Build_Session_Errors.result | 
| 373 | case Exn.Exn(exn) => Exn.Res(List(Exn.message(exn))) | |
| 374 | } | |
| 375 | } | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 376 | |
| 78372 | 377 | val result0 = | 
| 378 |             Isabelle_Thread.interrupt_handler(_ => process.terminate()) { process.await_shutdown() }
 | |
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 379 | |
| 78372 | 380 | val was_timeout = | 
| 381 |             timeout_request match {
 | |
| 382 | case None => false | |
| 383 | case Some(request) => !request.cancel() | |
| 384 | } | |
| 385 | ||
| 386 | session.stop() | |
| 387 | ||
| 388 | val export_errors = | |
| 389 | 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 | 390 | |
| 78372 | 391 | val (document_output, document_errors) = | 
| 392 |             try {
 | |
| 78674 | 393 |               if (Exn.is_res(build_errors) && result0.ok && info.documents.nonEmpty) {
 | 
| 78379 | 394 |                 using(Export.open_database_context(store, server = server)) { database_context =>
 | 
| 78372 | 395 | val documents = | 
| 396 |                     using(database_context.open_session(session_background)) {
 | |
| 397 | session_context => | |
| 398 | Document_Build.build_documents( | |
| 399 | Document_Build.context(session_context, progress = progress), | |
| 400 | output_sources = info.document_output, | |
| 401 | output_pdf = info.document_output) | |
| 402 | } | |
| 79692 | 403 | using(database_context.open_database(session_name, output = true))( | 
| 404 | session_database => | |
| 405 | documents.foreach(_.write(session_database.db, session_name))) | |
| 78372 | 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 | } | |
| 79692 | 453 |                 else if (progress.stopped && result1.ok) {
 | 
| 454 | result1.copy(rc = Process_Result.RC.interrupt) | |
| 455 | } | |
| 78372 | 456 | else result1 | 
| 457 | case Exn.Exn(Exn.Interrupt()) => | |
| 458 | if (result1.ok) result1.copy(rc = Process_Result.RC.interrupt) | |
| 459 | else result1 | |
| 460 | case Exn.Exn(exn) => throw exn | |
| 461 | } | |
| 462 | ||
| 463 | val process_result = | |
| 464 | if (result2.ok) result2 | |
| 465 |             else if (was_timeout) result2.error(Output.error_message_text("Timeout")).timeout_rc
 | |
| 466 |             else if (result2.interrupted) result2.error(Output.error_message_text("Interrupt"))
 | |
| 467 | else result2 | |
| 468 | ||
| 79691 
d298c5b65d8e
clarified store_session: heap requires process_result.ok, but log_db is always stored;
 wenzelm parents: 
79682diff
changeset | 469 | val store_session = | 
| 
d298c5b65d8e
clarified store_session: heap requires process_result.ok, but log_db is always stored;
 wenzelm parents: 
79682diff
changeset | 470 | store.output_session(session_name, store_heap = process_result.ok && store_heap) | 
| 
d298c5b65d8e
clarified store_session: heap requires process_result.ok, but log_db is always stored;
 wenzelm parents: 
79682diff
changeset | 471 | |
| 78372 | 472 | |
| 473 | /* output heap */ | |
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 474 | |
| 79682 
1fa1b32b0379
build local log_db, with store/restore via optional database server;
 wenzelm parents: 
79678diff
changeset | 475 | val output_shasum = | 
| 
1fa1b32b0379
build local log_db, with store/restore via optional database server;
 wenzelm parents: 
79678diff
changeset | 476 |             store_session.heap match {
 | 
| 79691 
d298c5b65d8e
clarified store_session: heap requires process_result.ok, but log_db is always stored;
 wenzelm parents: 
79682diff
changeset | 477 | case Some(path) => SHA1.shasum(ML_Heap.write_file_digest(path), session_name) | 
| 
d298c5b65d8e
clarified store_session: heap requires process_result.ok, but log_db is always stored;
 wenzelm parents: 
79682diff
changeset | 478 | case None => SHA1.no_shasum | 
| 78372 | 479 | } | 
| 480 | ||
| 481 | 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 | 482 | |
| 78372 | 483 | val build_log = | 
| 78985 
24e686fe043e
clarified Log_File.cache: reuse existing Store.cache / Build_Log.Store.cache;
 wenzelm parents: 
78842diff
changeset | 484 | Build_Log.Log_File(session_name, process_result.out_lines, cache = store.cache). | 
| 78372 | 485 | parse_session_info( | 
| 486 | command_timings = true, | |
| 487 | theory_timings = true, | |
| 488 | ml_statistics = true, | |
| 489 | task_statistics = true) | |
| 490 | ||
| 491 | // write log file | |
| 492 |           if (process_result.ok) {
 | |
| 493 | File.write_gzip(store.output_log_gz(session_name), terminate_lines(log_lines)) | |
| 494 | } | |
| 495 | 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 | 496 | |
| 78372 | 497 | // write database | 
| 78374 | 498 | def write_info(db: SQL.Database): Unit = | 
| 78372 | 499 | store.write_session_info(db, session_name, session_sources, | 
| 500 | build_log = | |
| 501 |                 if (process_result.timeout) build_log.error("Timeout") else build_log,
 | |
| 502 | build = | |
| 503 | Store.Build_Info( | |
| 504 | sources = sources_shasum, | |
| 505 | input_heaps = input_shasum, | |
| 506 | output_heap = output_shasum, | |
| 507 | process_result.rc, | |
| 78374 | 508 | build_context.build_uuid)) | 
| 78529 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78435diff
changeset | 509 | |
| 79887 
17220dc05991
revert most parts of 0e79fa88cab6: somewhat ambitious attempt to move towards "editing" builds via added/canceled workers;
 wenzelm parents: 
79718diff
changeset | 510 |           database_server match {
 | 
| 
17220dc05991
revert most parts of 0e79fa88cab6: somewhat ambitious attempt to move towards "editing" builds via added/canceled workers;
 wenzelm parents: 
79718diff
changeset | 511 | case Some(db) => write_info(db) | 
| 
17220dc05991
revert most parts of 0e79fa88cab6: somewhat ambitious attempt to move towards "editing" builds via added/canceled workers;
 wenzelm parents: 
79718diff
changeset | 512 | case None => using(store.open_database(session_name, output = true))(write_info) | 
| 
17220dc05991
revert most parts of 0e79fa88cab6: somewhat ambitious attempt to move towards "editing" builds via added/canceled workers;
 wenzelm parents: 
79718diff
changeset | 513 | } | 
| 78372 | 514 | |
| 80121 | 515 | store.in_heaps_database( | 
| 516 | List(store_session), database_server, server = server, progress = progress) | |
| 79682 
1fa1b32b0379
build local log_db, with store/restore via optional database server;
 wenzelm parents: 
79678diff
changeset | 517 | |
| 78372 | 518 | // messages | 
| 519 | process_result.err_lines.foreach(progress.echo(_)) | |
| 520 | ||
| 521 |           if (process_result.ok) {
 | |
| 522 | val props = build_log.session_timing | |
| 523 | val threads = Markup.Session_Timing.Threads.unapply(props) getOrElse 1 | |
| 524 | val timing = Markup.Timing_Properties.get(props) | |
| 525 | progress.echo( | |
| 526 |               "Timing " + session_name + " (" + threads + " threads, " + timing.message_factor + ")",
 | |
| 527 | verbose = true) | |
| 528 | progress.echo( | |
| 529 |               "Finished " + session_name + " (" + process_result.timing.message_resources + ")")
 | |
| 530 | } | |
| 531 |           else {
 | |
| 532 | progress.echo( | |
| 79692 | 533 | session_name + " FAILED (see also \"isabelle build_log -H Error " + | 
| 534 | session_name + "\")") | |
| 78372 | 535 |             if (!process_result.interrupted) {
 | 
| 536 |               val tail = info.options.int("process_output_tail")
 | |
| 79692 | 537 | val suffix = | 
| 538 | if (tail == 0) log_lines else log_lines.drop(log_lines.length - tail max 0) | |
| 539 | val prefix = | |
| 540 |                 if (log_lines.length == suffix.length) Nil else List("...")
 | |
| 78372 | 541 | progress.echo(Library.trim_line(cat_lines(prefix ::: suffix))) | 
| 542 | } | |
| 543 | } | |
| 544 | ||
| 79887 
17220dc05991
revert most parts of 0e79fa88cab6: somewhat ambitious attempt to move towards "editing" builds via added/canceled workers;
 wenzelm parents: 
79718diff
changeset | 545 | Result(process_result.copy(out_lines = log_lines), output_shasum) | 
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 546 | } | 
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 547 | } | 
| 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 548 | |
| 77486 
032c76e04475
clarified execution context: main work happens within Future.thread;
 wenzelm parents: 
77485diff
changeset | 549 | override def cancel(): Unit = future_result.cancel() | 
| 77298 | 550 | override def is_finished: Boolean = future_result.is_finished | 
| 79887 
17220dc05991
revert most parts of 0e79fa88cab6: somewhat ambitious attempt to move towards "editing" builds via added/canceled workers;
 wenzelm parents: 
79718diff
changeset | 551 | override def join: Result = future_result.join | 
| 77297 
226a880d0423
clarified types: support a variety of Build_Job instances;
 wenzelm parents: 
77294diff
changeset | 552 | } | 
| 72848 
d5d0e36eda16
read theory with PIDE markup from session database;
 wenzelm parents: 
72844diff
changeset | 553 | } |