| author | wenzelm | 
| Sat, 08 Apr 2017 21:35:04 +0200 | |
| changeset 65444 | 1f5821b19741 | 
| parent 65441 | 9425e4d8bdb6 | 
| child 65456 | 31e8a86971a8 | 
| permissions | -rw-r--r-- | 
| 50686 | 1 | /* Title: Pure/Tools/build.scala | 
| 48276 | 2 | Author: Makarius | 
| 57923 | 3 | Options: :folding=explicit: | 
| 48276 | 4 | |
| 5 | Build and manage Isabelle sessions. | |
| 6 | */ | |
| 7 | ||
| 8 | package isabelle | |
| 9 | ||
| 10 | ||
| 48548 | 11 | import java.io.{BufferedInputStream, FileInputStream,
 | 
| 48494 | 12 | BufferedReader, InputStreamReader, IOException} | 
| 13 | import java.util.zip.GZIPInputStream | |
| 48335 | 14 | |
| 48676 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 15 | import scala.collection.SortedSet | 
| 51397 
03b586ee5930
support for 'chapter' specifications within session ROOT;
 wenzelm parents: 
51300diff
changeset | 16 | import scala.collection.mutable | 
| 48340 
6f4fc030882a
allow explicit specification of additional session directories;
 wenzelm parents: 
48339diff
changeset | 17 | import scala.annotation.tailrec | 
| 48337 
9c7f8e5805b4
cumulate semantic Session_Info, based on syntactic Session_Entry;
 wenzelm parents: 
48336diff
changeset | 18 | |
| 48335 | 19 | |
| 48276 | 20 | object Build | 
| 21 | {
 | |
| 62631 | 22 | /** auxiliary **/ | 
| 48424 | 23 | |
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 24 | /* persistent build info */ | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 25 | |
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 26 | sealed case class Session_Info( | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 27 | sources: List[String], | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 28 | input_heaps: List[String], | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 29 | output_heap: Option[String], | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 30 | return_code: Int) | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 31 | |
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 32 | |
| 65289 | 33 | /* queue with scheduling information */ | 
| 48676 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 34 | |
| 62631 | 35 | private object Queue | 
| 48676 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 36 |   {
 | 
| 65289 | 37 | def load_timings(store: Sessions.Store, name: String): (List[Properties.T], Double) = | 
| 38 |     {
 | |
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 39 | val no_timings: (List[Properties.T], Double) = (Nil, 0.0) | 
| 65289 | 40 | |
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 41 |       store.find_database(name) match {
 | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 42 | case None => no_timings | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 43 | case Some(database) => | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 44 | def ignore_error(msg: String) = | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 45 |           {
 | 
| 65365 | 46 |             Output.warning("Ignoring bad database: " +
 | 
| 47 | database.expand + (if (msg == "") "" else "\n" + msg)) | |
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 48 | no_timings | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 49 | } | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 50 |           try {
 | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 51 | using(SQLite.open_database(database))(db => | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 52 |             {
 | 
| 65320 
52861eebf58d
access table via session_name: db may in principle contain multiple entries;
 wenzelm parents: 
65318diff
changeset | 53 | val build_log = store.read_build_log(db, name, command_timings = true) | 
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 54 | val session_timing = Markup.Elapsed.unapply(build_log.session_timing) getOrElse 0.0 | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 55 | (build_log.command_timings, session_timing) | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 56 | }) | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 57 | } | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 58 |           catch {
 | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 59 | case ERROR(msg) => ignore_error(msg) | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 60 | case exn: java.lang.Error => ignore_error(Exn.message(exn)) | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 61 |             case _: XML.Error => ignore_error("")
 | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 62 | } | 
| 65289 | 63 | } | 
| 64 | } | |
| 65 | ||
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 66 | def apply(sessions: Sessions.T, store: Sessions.Store): Queue = | 
| 48676 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 67 |     {
 | 
| 65420 
695d4e22345a
support for static session imports, without affect build hierarchy;
 wenzelm parents: 
65419diff
changeset | 68 | val graph = sessions.build_graph | 
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 69 | val names = graph.keys | 
| 51220 | 70 | |
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 71 | val timings = names.map(name => (name, load_timings(store, name))) | 
| 51220 | 72 | val command_timings = | 
| 73 |         Map(timings.map({ case (name, (ts, _)) => (name, ts) }): _*).withDefaultValue(Nil)
 | |
| 74 | val session_timing = | |
| 75 |         Map(timings.map({ case (name, (_, t)) => (name, t) }): _*).withDefaultValue(0.0)
 | |
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 76 | |
| 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 77 | def outdegree(name: String): Int = graph.imm_succs(name).size | 
| 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 78 | |
| 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 79 | object Ordering extends scala.math.Ordering[String] | 
| 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 80 |       {
 | 
| 51227 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 81 | def compare_timing(name1: String, name2: String): Int = | 
| 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 82 |         {
 | 
| 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 83 | val t1 = session_timing(name1) | 
| 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 84 | val t2 = session_timing(name2) | 
| 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 85 | if (t1 == 0.0 || t2 == 0.0) 0 | 
| 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 86 | else t1 compare t2 | 
| 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 87 | } | 
| 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 88 | |
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 89 | def compare(name1: String, name2: String): Int = | 
| 51229 
6e40d0bb89e3
prefer outdegree in comparison again (cf. 88c96e836ed6) -- NB: big jobs might hide behind small ones in this naive queuing scheme;
 wenzelm parents: 
51227diff
changeset | 90 |           outdegree(name2) compare outdegree(name1) match {
 | 
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 91 | case 0 => | 
| 51229 
6e40d0bb89e3
prefer outdegree in comparison again (cf. 88c96e836ed6) -- NB: big jobs might hide behind small ones in this naive queuing scheme;
 wenzelm parents: 
51227diff
changeset | 92 |               compare_timing(name2, name1) match {
 | 
| 51220 | 93 | case 0 => | 
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 94 |                   sessions(name2).timeout compare sessions(name1).timeout match {
 | 
| 51220 | 95 | case 0 => name1 compare name2 | 
| 96 | case ord => ord | |
| 97 | } | |
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 98 | case ord => ord | 
| 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 99 | } | 
| 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 100 | case ord => ord | 
| 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 101 | } | 
| 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 102 | } | 
| 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 103 | |
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 104 | new Queue(graph, SortedSet(names: _*)(Ordering), command_timings) | 
| 48676 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 105 | } | 
| 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 106 | } | 
| 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 107 | |
| 65314 | 108 | private class Queue( | 
| 62631 | 109 | graph: Graph[String, Sessions.Info], | 
| 51220 | 110 | order: SortedSet[String], | 
| 111 | val command_timings: String => List[Properties.T]) | |
| 48676 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 112 |   {
 | 
| 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 113 | def is_inner(name: String): Boolean = !graph.is_maximal(name) | 
| 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 114 | |
| 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 115 | def is_empty: Boolean = graph.is_empty | 
| 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 116 | |
| 51227 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 117 | def - (name: String): Queue = | 
| 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 118 | new Queue(graph.del_node(name), | 
| 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 119 | order - name, // FIXME scala-2.10.0 TreeSet problem!? | 
| 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 120 | command_timings) | 
| 48676 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 121 | |
| 62631 | 122 | def dequeue(skip: String => Boolean): Option[(String, Sessions.Info)] = | 
| 48676 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 123 |     {
 | 
| 51227 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 124 | val it = order.iterator.dropWhile(name => | 
| 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 125 | skip(name) | 
| 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 126 | || !graph.defined(name) // FIXME scala-2.10.0 TreeSet problem!? | 
| 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 127 | || !graph.is_minimal(name)) | 
| 48680 | 128 |       if (it.hasNext) { val name = it.next; Some((name, graph.get_node(name))) }
 | 
| 48676 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 129 | else None | 
| 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 130 | } | 
| 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 131 | } | 
| 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 132 | |
| 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 133 | |
| 65313 | 134 | /* PIDE protocol handler */ | 
| 135 | ||
| 136 | class Handler(progress: Progress, session: Session, session_name: String) | |
| 137 | extends Session.Protocol_Handler | |
| 138 |   {
 | |
| 139 | val result_error: Promise[String] = Future.promise | |
| 140 | ||
| 141 |     override def exit() { result_error.cancel }
 | |
| 142 | ||
| 143 | private def build_session_finished(msg: Prover.Protocol_Output): Boolean = | |
| 144 |     {
 | |
| 145 | val error_message = | |
| 65344 
b99283eed13c
clarified YXML vs. symbol encoding: operate on whole message;
 wenzelm parents: 
65320diff
changeset | 146 |         try { Pretty.string_of(Symbol.decode_yxml(msg.text)) }
 | 
| 65313 | 147 |         catch { case ERROR(msg) => msg }
 | 
| 148 | result_error.fulfill(error_message) | |
| 149 | session.send_stop() | |
| 150 | true | |
| 151 | } | |
| 152 | ||
| 153 | private def loading_theory(msg: Prover.Protocol_Output): Boolean = | |
| 154 |       msg.properties match {
 | |
| 155 | case Markup.Loading_Theory(name) => | |
| 156 | progress.theory(session_name, name) | |
| 157 | true | |
| 158 | case _ => false | |
| 159 | } | |
| 160 | ||
| 161 | val functions = | |
| 162 | List( | |
| 163 | Markup.BUILD_SESSION_FINISHED -> build_session_finished _, | |
| 164 | Markup.LOADING_THEORY -> loading_theory _) | |
| 165 | } | |
| 166 | ||
| 167 | ||
| 65308 | 168 | /* job: running prover process */ | 
| 48341 | 169 | |
| 65308 | 170 | private class Job(progress: Progress, | 
| 171 | name: String, | |
| 172 | val info: Sessions.Info, | |
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 173 | sessions: Sessions.T, | 
| 65313 | 174 | deps: Sessions.Deps, | 
| 65308 | 175 | store: Sessions.Store, | 
| 176 | do_output: Boolean, | |
| 177 | verbose: Boolean, | |
| 178 | pide: Boolean, | |
| 179 | val numa_node: Option[Int], | |
| 180 | command_timings: List[Properties.T]) | |
| 48418 | 181 |   {
 | 
| 62633 | 182 | val output = store.output_dir + Path.basic(name) | 
| 48674 | 183 | def output_path: Option[Path] = if (do_output) Some(output) else None | 
| 62633 | 184 | output.file.delete | 
| 48674 | 185 | |
| 65312 | 186 | val options = | 
| 187 |       numa_node match {
 | |
| 188 | case None => info.options | |
| 189 |         case Some(n) => info.options.string("ML_process_policy") = NUMA.policy(n)
 | |
| 190 | } | |
| 191 | ||
| 59445 | 192 |     private val graph_file = Isabelle_System.tmp_file("session_graph", "pdf")
 | 
| 65313 | 193 |     try { isabelle.graphview.Graph_File.write(options, graph_file, deps(name).session_graph) }
 | 
| 59445 | 194 |     catch { case ERROR(_) => /*error should be exposed in ML*/ }
 | 
| 195 | ||
| 62573 | 196 | private val future_result: Future[Process_Result] = | 
| 61559 
313eca3fa847
more direct task future implementation, with proper cancel operation;
 wenzelm parents: 
61556diff
changeset | 197 |       Future.thread("build") {
 | 
| 65308 | 198 |         val parent = info.parent.getOrElse("")
 | 
| 65441 | 199 | val base = deps(name) | 
| 65308 | 200 | val args_yxml = | 
| 201 | YXML.string_of_body( | |
| 62944 
3ee643c5ed00
more standard session build process, including browser_info;
 wenzelm parents: 
62902diff
changeset | 202 |             {
 | 
| 
3ee643c5ed00
more standard session build process, including browser_info;
 wenzelm parents: 
62902diff
changeset | 203 | import XML.Encode._ | 
| 
3ee643c5ed00
more standard session build process, including browser_info;
 wenzelm parents: 
62902diff
changeset | 204 | pair(list(pair(string, int)), pair(list(properties), pair(bool, pair(bool, | 
| 
3ee643c5ed00
more standard session build process, including browser_info;
 wenzelm parents: 
62902diff
changeset | 205 | pair(Path.encode, pair(list(pair(Path.encode, Path.encode)), pair(string, | 
| 65307 | 206 | pair(string, pair(string, pair(string, pair(Path.encode, | 
| 65431 
4a3e6cda3b94
provide session base for "isabelle build" and "isabelle console" ML process;
 wenzelm parents: 
65422diff
changeset | 207 | pair(list(pair(Options.encode, list(string))), | 
| 65441 | 208 | pair(list(string), | 
| 209 | pair(list(pair(string, string)), list(pair(string, string))))))))))))))))( | |
| 62944 
3ee643c5ed00
more standard session build process, including browser_info;
 wenzelm parents: 
62902diff
changeset | 210 | (Symbol.codes, (command_timings, (do_output, (verbose, | 
| 
3ee643c5ed00
more standard session build process, including browser_info;
 wenzelm parents: 
62902diff
changeset | 211 | (store.browser_info, (info.document_files, (File.standard_path(graph_file), | 
| 65307 | 212 | (parent, (info.chapter, (name, (Path.current, | 
| 65431 
4a3e6cda3b94
provide session base for "isabelle build" and "isabelle console" ML process;
 wenzelm parents: 
65422diff
changeset | 213 | (info.theories, | 
| 65441 | 214 | (base.global_theories.toList, | 
| 215 | (base.dest_loaded_theories, base.dest_known_theories))))))))))))))) | |
| 65308 | 216 | }) | 
| 217 | ||
| 218 | val env = | |
| 219 | Isabelle_System.settings() + | |
| 65312 | 220 |             ("ISABELLE_ML_DEBUGGER" -> options.bool("ML_debugger").toString)
 | 
| 65308 | 221 | |
| 222 | def save_heap: String = | |
| 223 | "ML_Heap.share_common_data (); ML_Heap.save_child " + | |
| 224 | ML_Syntax.print_string0(File.platform_path(output)) | |
| 62944 
3ee643c5ed00
more standard session build process, including browser_info;
 wenzelm parents: 
62902diff
changeset | 225 | |
| 65360 | 226 |         if (pide && !Sessions.is_pure(name)) {
 | 
| 65441 | 227 | val resources = new Resources(deps(parent), default_qualifier = name) | 
| 65313 | 228 | val session = new Session(options, resources) | 
| 229 | val handler = new Handler(progress, session, name) | |
| 65315 | 230 | session.init_protocol_handler(handler) | 
| 65313 | 231 | |
| 65317 | 232 | val session_result = Future.promise[Process_Result] | 
| 65313 | 233 | |
| 234 | Isabelle_Process.start(session, options, logic = parent, | |
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 235 | cwd = info.dir.file, env = env, sessions = Some(sessions), store = store, | 
| 65313 | 236 | phase_changed = | 
| 237 |             {
 | |
| 238 |               case Session.Ready => session.protocol_command("build_session", args_yxml)
 | |
| 65317 | 239 | case Session.Terminated(result) => session_result.fulfill(result) | 
| 65313 | 240 | case _ => | 
| 241 | }) | |
| 242 | ||
| 65317 | 243 | val result = session_result.join | 
| 65313 | 244 |           handler.result_error.join match {
 | 
| 65317 | 245 | case "" => result | 
| 246 | case msg => | |
| 247 | result.copy( | |
| 248 | rc = result.rc max 1, | |
| 249 | out_lines = result.out_lines ::: split_lines(Output.error_text(msg))) | |
| 65313 | 250 | } | 
| 65308 | 251 | } | 
| 252 |         else {
 | |
| 253 |           val args_file = Isabelle_System.tmp_file("build")
 | |
| 254 | File.write(args_file, args_yxml) | |
| 255 | ||
| 256 | val eval = | |
| 257 |             "Command_Line.tool0 (fn () => (" +
 | |
| 258 | "Build.build " + ML_Syntax.print_string0(File.standard_path(args_file)) + | |
| 259 | (if (do_output) "; " + save_heap else "") + "));" | |
| 64265 | 260 | |
| 65308 | 261 | val process = | 
| 65360 | 262 |             if (Sessions.is_pure(name)) {
 | 
| 65312 | 263 | ML_Process(options, raw_ml_system = true, cwd = info.dir.file, | 
| 65308 | 264 | args = | 
| 265 |                   (for ((root, _) <- Thy_Header.ml_roots) yield List("--use", root)).flatten :::
 | |
| 266 |                   List("--eval", eval),
 | |
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 267 | env = env, sessions = Some(sessions), store = store, | 
| 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 268 | cleanup = () => args_file.delete) | 
| 65308 | 269 | } | 
| 270 |             else {
 | |
| 65312 | 271 |               ML_Process(options, parent, List("--eval", eval), cwd = info.dir.file,
 | 
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 272 | env = env, sessions = Some(sessions), store = store, | 
| 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 273 | cleanup = () => args_file.delete) | 
| 65308 | 274 | } | 
| 64265 | 275 | |
| 65308 | 276 | process.result( | 
| 277 | progress_stdout = (line: String) => | |
| 278 |               Library.try_unprefix("\floading_theory = ", line) match {
 | |
| 279 | case Some(theory) => progress.theory(name, theory) | |
| 280 | case None => | |
| 281 | }, | |
| 282 | progress_limit = | |
| 65312 | 283 |               options.int("process_output_limit") match {
 | 
| 65308 | 284 | case 0 => None | 
| 285 | case m => Some(m * 1000000L) | |
| 286 | }, | |
| 287 | strict = false) | |
| 288 | } | |
| 50845 | 289 | } | 
| 48674 | 290 | |
| 62572 | 291 | def terminate: Unit = future_result.cancel | 
| 292 | def is_finished: Boolean = future_result.is_finished | |
| 48674 | 293 | |
| 62569 | 294 | @volatile private var was_timeout = false | 
| 56779 | 295 | private val timeout_request: Option[Event_Timer.Request] = | 
| 296 |     {
 | |
| 62569 | 297 | if (info.timeout > Time.zero) | 
| 298 |         Some(Event_Timer.request(Time.now() + info.timeout) { terminate; was_timeout = true })
 | |
| 48674 | 299 | else None | 
| 56779 | 300 | } | 
| 48674 | 301 | |
| 62400 | 302 | def join: Process_Result = | 
| 50845 | 303 |     {
 | 
| 62572 | 304 | val result = future_result.join | 
| 50845 | 305 | |
| 62944 
3ee643c5ed00
more standard session build process, including browser_info;
 wenzelm parents: 
62902diff
changeset | 306 | if (result.ok) | 
| 62633 | 307 | Present.finish(progress, store.browser_info, graph_file, info, name) | 
| 61372 | 308 | |
| 59445 | 309 | graph_file.delete | 
| 56779 | 310 | timeout_request.foreach(_.cancel) | 
| 48674 | 311 | |
| 62572 | 312 |       if (result.interrupted) {
 | 
| 313 |         if (was_timeout) result.error(Output.error_text("Timeout")).was_timeout
 | |
| 314 |         else result.error(Output.error_text("Interrupt"))
 | |
| 52063 
fd533ac64390
timeout counts as regular error, with rc = 1 (cf. special Exn.Interrupt vs. regular TimeLimit.TimeOut in Isabelle/ML);
 wenzelm parents: 
51987diff
changeset | 315 | } | 
| 62572 | 316 | else result | 
| 48674 | 317 | } | 
| 48364 | 318 | } | 
| 319 | ||
| 48424 | 320 | |
| 62631 | 321 | |
| 62641 | 322 | /** build with results **/ | 
| 48424 | 323 | |
| 63996 | 324 | class Results private[Build](results: Map[String, (Option[Process_Result], Sessions.Info)]) | 
| 62403 | 325 |   {
 | 
| 326 | def sessions: Set[String] = results.keySet | |
| 63082 
6af03422535a
expose Sessions.Info in Build.Results
 Lars Hupel <lars.hupel@mytum.de> parents: 
62946diff
changeset | 327 | def cancelled(name: String): Boolean = results(name)._1.isEmpty | 
| 
6af03422535a
expose Sessions.Info in Build.Results
 Lars Hupel <lars.hupel@mytum.de> parents: 
62946diff
changeset | 328 | def apply(name: String): Process_Result = results(name)._1.getOrElse(Process_Result(1)) | 
| 
6af03422535a
expose Sessions.Info in Build.Results
 Lars Hupel <lars.hupel@mytum.de> parents: 
62946diff
changeset | 329 | def info(name: String): Sessions.Info = results(name)._2 | 
| 65253 | 330 | val rc = | 
| 331 | (0 /: results.iterator.map( | |
| 332 |         { case (_, (Some(r), _)) => r.rc case (_, (None, _)) => 1 }))(_ max _)
 | |
| 62641 | 333 | def ok: Boolean = rc == 0 | 
| 62406 | 334 | |
| 335 | override def toString: String = rc.toString | |
| 62403 | 336 | } | 
| 337 | ||
| 62641 | 338 | def build( | 
| 50404 
898cac1dad5e
avoid startup within GUI thread -- it is only required later for dialog;
 wenzelm parents: 
50367diff
changeset | 339 | options: Options, | 
| 64909 | 340 | progress: Progress = No_Progress, | 
| 48511 
37999ee01156
remove old output heaps, to ensure that result is valid wrt. check_stamps;
 wenzelm parents: 
48509diff
changeset | 341 | build_heap: Boolean = false, | 
| 48595 | 342 | clean_build: Boolean = false, | 
| 56890 | 343 | dirs: List[Path] = Nil, | 
| 344 | select_dirs: List[Path] = Nil, | |
| 64265 | 345 | numa_shuffling: Boolean = false, | 
| 48509 | 346 | max_jobs: Int = 1, | 
| 48903 | 347 | list_files: Boolean = false, | 
| 59891 
9ce697050455
added isabelle build option -k, for fast off-line checking of theory sources;
 wenzelm parents: 
59811diff
changeset | 348 | check_keywords: Set[String] = Set.empty, | 
| 48509 | 349 | no_build: Boolean = false, | 
| 350 | system_mode: Boolean = false, | |
| 351 | verbose: Boolean = false, | |
| 65308 | 352 | pide: Boolean = false, | 
| 63224 | 353 | requirements: Boolean = false, | 
| 354 | all_sessions: Boolean = false, | |
| 355 | exclude_session_groups: List[String] = Nil, | |
| 59892 
2a616319c171
added isabelle build option -x, to exclude sessions;
 wenzelm parents: 
59891diff
changeset | 356 | exclude_sessions: List[String] = Nil, | 
| 63224 | 357 | session_groups: List[String] = Nil, | 
| 65422 | 358 | sessions: List[String] = Nil, | 
| 359 | selection: Sessions.Selection = Sessions.Selection.empty): Results = | |
| 63224 | 360 |   {
 | 
| 361 | /* session selection and dependencies */ | |
| 51220 | 362 | |
| 62714 | 363 |     val build_options = options.int.update("completion_limit", 0).bool.update("ML_statistics", true)
 | 
| 65422 | 364 | |
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 365 | val full_sessions = Sessions.load(build_options, dirs, select_dirs) | 
| 65422 | 366 | |
| 367 | val (selected, selected_sessions) = | |
| 368 | full_sessions.selection( | |
| 369 | Sessions.Selection(requirements, all_sessions, exclude_session_groups, | |
| 370 | exclude_sessions, session_groups, sessions) + selection) | |
| 371 | ||
| 65251 | 372 | val deps = | 
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 373 | Sessions.deps(selected_sessions, progress = progress, inlined_files = true, | 
| 65406 | 374 | verbose = verbose, list_files = list_files, check_keywords = check_keywords, | 
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 375 | global_theories = full_sessions.global_theories) | 
| 48368 | 376 | |
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 377 | def sources_stamp(name: String): List[String] = | 
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 378 | (selected_sessions(name).meta_digest :: deps.sources(name)).map(_.toString).sorted | 
| 48504 
21dfd6c04482
actually check source vs. target stamps, based on information from log files;
 wenzelm parents: 
48494diff
changeset | 379 | |
| 51220 | 380 | |
| 381 | /* main build process */ | |
| 382 | ||
| 65289 | 383 | val store = Sessions.store(system_mode) | 
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 384 | val queue = Queue(selected_sessions, store) | 
| 65289 | 385 | |
| 62632 | 386 | store.prepare_output() | 
| 48373 | 387 | |
| 48595 | 388 | // optional cleanup | 
| 389 |     if (clean_build) {
 | |
| 65420 
695d4e22345a
support for static session imports, without affect build hierarchy;
 wenzelm parents: 
65419diff
changeset | 390 |       for (name <- full_sessions.build_descendants(selected)) {
 | 
| 48595 | 391 | val files = | 
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 392 | List(Path.basic(name), store.database(name), store.log(name), store.log_gz(name)). | 
| 62632 | 393 | map(store.output_dir + _).filter(_.is_file) | 
| 59319 | 394 |         if (files.nonEmpty) progress.echo("Cleaning " + name + " ...")
 | 
| 50366 | 395 | if (!files.forall(p => p.file.delete)) progress.echo(name + " FAILED to delete") | 
| 48595 | 396 | } | 
| 397 | } | |
| 398 | ||
| 48425 | 399 | // scheduler loop | 
| 63082 
6af03422535a
expose Sessions.Info in Build.Results
 Lars Hupel <lars.hupel@mytum.de> parents: 
62946diff
changeset | 400 | case class Result( | 
| 
6af03422535a
expose Sessions.Info in Build.Results
 Lars Hupel <lars.hupel@mytum.de> parents: 
62946diff
changeset | 401 | current: Boolean, heap_stamp: Option[String], | 
| 
6af03422535a
expose Sessions.Info in Build.Results
 Lars Hupel <lars.hupel@mytum.de> parents: 
62946diff
changeset | 402 | process: Option[Process_Result], info: Sessions.Info) | 
| 62402 | 403 |     {
 | 
| 404 | def ok: Boolean = | |
| 405 |         process match {
 | |
| 406 | case None => false | |
| 407 | case Some(res) => res.rc == 0 | |
| 408 | } | |
| 409 | } | |
| 48639 
675988e64bf9
store parent heap stamp as well -- needs to be propagated through the build hierarchy;
 wenzelm parents: 
48626diff
changeset | 410 | |
| 56837 
5a598f1eecfd
more robust interrupt handling for Scala_Console, which uses JVM Thread.interrupt instead of POSIX SIGINT;
 wenzelm parents: 
56831diff
changeset | 411 | def sleep() | 
| 
5a598f1eecfd
more robust interrupt handling for Scala_Console, which uses JVM Thread.interrupt instead of POSIX SIGINT;
 wenzelm parents: 
56831diff
changeset | 412 |     {
 | 
| 
5a598f1eecfd
more robust interrupt handling for Scala_Console, which uses JVM Thread.interrupt instead of POSIX SIGINT;
 wenzelm parents: 
56831diff
changeset | 413 |       try { Thread.sleep(500) }
 | 
| 56861 | 414 |       catch { case Exn.Interrupt() => Exn.Interrupt.impose() }
 | 
| 56837 
5a598f1eecfd
more robust interrupt handling for Scala_Console, which uses JVM Thread.interrupt instead of POSIX SIGINT;
 wenzelm parents: 
56831diff
changeset | 415 | } | 
| 50366 | 416 | |
| 64265 | 417 | val numa_nodes = new NUMA.Nodes(numa_shuffling) | 
| 418 | ||
| 48425 | 419 | @tailrec def loop( | 
| 48676 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 420 | pending: Queue, | 
| 62628 | 421 | running: Map[String, (List[String], Job)], | 
| 48639 
675988e64bf9
store parent heap stamp as well -- needs to be propagated through the build hierarchy;
 wenzelm parents: 
48626diff
changeset | 422 | results: Map[String, Result]): Map[String, Result] = | 
| 48425 | 423 |     {
 | 
| 64265 | 424 | def used_node(i: Int): Boolean = | 
| 425 | running.iterator.exists( | |
| 426 |           { case (_, (_, job)) => job.numa_node.isDefined && job.numa_node.get == i })
 | |
| 427 | ||
| 48425 | 428 | if (pending.is_empty) results | 
| 51253 | 429 |       else {
 | 
| 430 | if (progress.stopped) | |
| 431 | for ((_, (_, job)) <- running) job.terminate | |
| 432 | ||
| 48674 | 433 |         running.find({ case (_, (_, job)) => job.is_finished }) match {
 | 
| 62628 | 434 | case Some((name, (input_heaps, job))) => | 
| 50367 | 435 |             //{{{ finish job
 | 
| 48424 | 436 | |
| 62401 | 437 | val process_result = job.join | 
| 62573 | 438 | process_result.err_lines.foreach(progress.echo(_)) | 
| 439 | if (process_result.ok) | |
| 440 |               progress.echo("Finished " + name + " (" + process_result.timing.message_resources + ")")
 | |
| 48373 | 441 | |
| 65294 | 442 |             val log_lines = process_result.out_lines.filterNot(_.startsWith("\f"))
 | 
| 62404 
13a0f537e232
retain tail out_lines as printed, but not the whole log content;
 wenzelm parents: 
62403diff
changeset | 443 | val process_result_tail = | 
| 
13a0f537e232
retain tail out_lines as printed, but not the whole log content;
 wenzelm parents: 
62403diff
changeset | 444 |             {
 | 
| 62409 
e391528eff3b
proper option process_output_tail, more generous default;
 wenzelm parents: 
62406diff
changeset | 445 |               val tail = job.info.options.int("process_output_tail")
 | 
| 62632 | 446 | process_result.copy( | 
| 447 | out_lines = | |
| 65278 | 448 | "(see also " + (store.output_dir + store.log(name)).file.toString + ")" :: | 
| 65294 | 449 | (if (tail == 0) log_lines else log_lines.drop(log_lines.length - tail max 0))) | 
| 62404 
13a0f537e232
retain tail out_lines as printed, but not the whole log content;
 wenzelm parents: 
62403diff
changeset | 450 | } | 
| 
13a0f537e232
retain tail out_lines as printed, but not the whole log content;
 wenzelm parents: 
62403diff
changeset | 451 | |
| 62636 | 452 | val heap_stamp = | 
| 62401 | 453 |               if (process_result.ok) {
 | 
| 65278 | 454 | (store.output_dir + store.log(name)).file.delete | 
| 62636 | 455 | val heap_stamp = | 
| 62704 
478b49f0d726
proper SHA1 digest as annex to heap file: Poly/ML reads precise segment length;
 wenzelm parents: 
62643diff
changeset | 456 | for (path <- job.output_path if path.is_file) | 
| 
478b49f0d726
proper SHA1 digest as annex to heap file: Poly/ML reads precise segment length;
 wenzelm parents: 
62643diff
changeset | 457 | yield Sessions.write_heap_digest(path) | 
| 48639 
675988e64bf9
store parent heap stamp as well -- needs to be propagated through the build hierarchy;
 wenzelm parents: 
48626diff
changeset | 458 | |
| 65294 | 459 | File.write_gzip(store.output_dir + store.log_gz(name), terminate_lines(log_lines)) | 
| 48639 
675988e64bf9
store parent heap stamp as well -- needs to be propagated through the build hierarchy;
 wenzelm parents: 
48626diff
changeset | 460 | |
| 62636 | 461 | heap_stamp | 
| 48639 
675988e64bf9
store parent heap stamp as well -- needs to be propagated through the build hierarchy;
 wenzelm parents: 
48626diff
changeset | 462 | } | 
| 
675988e64bf9
store parent heap stamp as well -- needs to be propagated through the build hierarchy;
 wenzelm parents: 
48626diff
changeset | 463 |               else {
 | 
| 62632 | 464 | (store.output_dir + Path.basic(name)).file.delete | 
| 65278 | 465 | (store.output_dir + store.log_gz(name)).file.delete | 
| 48639 
675988e64bf9
store parent heap stamp as well -- needs to be propagated through the build hierarchy;
 wenzelm parents: 
48626diff
changeset | 466 | |
| 65294 | 467 | File.write(store.output_dir + store.log(name), terminate_lines(log_lines)) | 
| 50366 | 468 | progress.echo(name + " FAILED") | 
| 62404 
13a0f537e232
retain tail out_lines as printed, but not the whole log content;
 wenzelm parents: 
62403diff
changeset | 469 | if (!process_result.interrupted) progress.echo(process_result_tail.out) | 
| 48639 
675988e64bf9
store parent heap stamp as well -- needs to be propagated through the build hierarchy;
 wenzelm parents: 
48626diff
changeset | 470 | |
| 62636 | 471 | None | 
| 48639 
675988e64bf9
store parent heap stamp as well -- needs to be propagated through the build hierarchy;
 wenzelm parents: 
48626diff
changeset | 472 | } | 
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 473 | |
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 474 | // write database | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 475 |             {
 | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 476 | val database = store.output_dir + store.database(name) | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 477 | database.file.delete | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 478 | |
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 479 | using(SQLite.open_database(database))(db => | 
| 65318 
342efc382558
eliminated somewhat redundant inlined name (despite a7aa17a1f721);
 wenzelm parents: 
65317diff
changeset | 480 | store.write_session_info(db, name, | 
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 481 | build_log = | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 482 | Build_Log.Log_File(name, process_result.out_lines). | 
| 65318 
342efc382558
eliminated somewhat redundant inlined name (despite a7aa17a1f721);
 wenzelm parents: 
65317diff
changeset | 483 | parse_session_info( | 
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 484 | command_timings = true, ml_statistics = true, task_statistics = true), | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 485 | build = | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 486 | Session_Info(sources_stamp(name), input_heaps, heap_stamp, process_result.rc))) | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 487 | } | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 488 | |
| 50707 
5b2bf7611662
maintain session index on Scala side, for more determistic results;
 wenzelm parents: 
50686diff
changeset | 489 | loop(pending - name, running - name, | 
| 63082 
6af03422535a
expose Sessions.Info in Build.Results
 Lars Hupel <lars.hupel@mytum.de> parents: 
62946diff
changeset | 490 | results + (name -> Result(false, heap_stamp, Some(process_result_tail), job.info))) | 
| 50367 | 491 | //}}} | 
| 60215 | 492 | case None if running.size < (max_jobs max 1) => | 
| 50367 | 493 |             //{{{ check/start next job
 | 
| 48547 | 494 |             pending.dequeue(running.isDefinedAt(_)) match {
 | 
| 495 | case Some((name, info)) => | |
| 65420 
695d4e22345a
support for static session imports, without affect build hierarchy;
 wenzelm parents: 
65419diff
changeset | 496 | val ancestor_results = selected_sessions.build_ancestors(name).map(results(_)) | 
| 62636 | 497 | val ancestor_heaps = ancestor_results.flatMap(_.heap_stamp) | 
| 62628 | 498 | |
| 65360 | 499 | val do_output = build_heap || Sessions.is_pure(name) || queue.is_inner(name) | 
| 48547 | 500 | |
| 62636 | 501 | val (current, heap_stamp) = | 
| 48547 | 502 |                 {
 | 
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 503 |                   store.find_database_heap(name) match {
 | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 504 | case Some((database, heap_stamp)) => | 
| 65320 
52861eebf58d
access table via session_name: db may in principle contain multiple entries;
 wenzelm parents: 
65318diff
changeset | 505 |                       using(SQLite.open_database(database))(store.read_build(_, name)) match {
 | 
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 506 | case Some(build) => | 
| 62628 | 507 | val current = | 
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 508 | build.sources == sources_stamp(name) && | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 509 | build.input_heaps == ancestor_heaps && | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 510 | build.output_heap == heap_stamp && | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 511 | !(do_output && heap_stamp.isEmpty) && | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 512 | build.return_code == 0 | 
| 62636 | 513 | (current, heap_stamp) | 
| 514 | case None => (false, None) | |
| 48547 | 515 | } | 
| 62636 | 516 | case None => (false, None) | 
| 48504 
21dfd6c04482
actually check source vs. target stamps, based on information from log files;
 wenzelm parents: 
48494diff
changeset | 517 | } | 
| 48547 | 518 | } | 
| 62628 | 519 | val all_current = current && ancestor_results.forall(_.current) | 
| 48528 
784c6f63d79c
proper all_current, which regards parent status as well;
 wenzelm parents: 
48511diff
changeset | 520 | |
| 48547 | 521 | if (all_current) | 
| 62402 | 522 | loop(pending - name, running, | 
| 63082 
6af03422535a
expose Sessions.Info in Build.Results
 Lars Hupel <lars.hupel@mytum.de> parents: 
62946diff
changeset | 523 | results + (name -> Result(true, heap_stamp, Some(Process_Result(0)), info))) | 
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 524 |                 else if (no_build) {
 | 
| 50366 | 525 |                   if (verbose) progress.echo("Skipping " + name + " ...")
 | 
| 62402 | 526 | loop(pending - name, running, | 
| 63082 
6af03422535a
expose Sessions.Info in Build.Results
 Lars Hupel <lars.hupel@mytum.de> parents: 
62946diff
changeset | 527 | results + (name -> Result(false, heap_stamp, Some(Process_Result(1)), info))) | 
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 528 | } | 
| 62628 | 529 |                 else if (ancestor_results.forall(_.ok) && !progress.stopped) {
 | 
| 64265 | 530 | val numa_node = numa_nodes.next(used_node(_)) | 
| 50366 | 531 | progress.echo((if (do_output) "Building " else "Running ") + name + " ...") | 
| 51220 | 532 | val job = | 
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 533 | new Job(progress, name, info, selected_sessions, deps, store, do_output, | 
| 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 534 | verbose, pide, numa_node, queue.command_timings(name)) | 
| 62628 | 535 | loop(pending, running + (name -> (ancestor_heaps, job)), results) | 
| 48547 | 536 | } | 
| 537 |                 else {
 | |
| 50366 | 538 | progress.echo(name + " CANCELLED") | 
| 65253 | 539 | loop(pending - name, running, | 
| 540 | results + (name -> Result(false, heap_stamp, None, info))) | |
| 48547 | 541 | } | 
| 542 | case None => sleep(); loop(pending, running, results) | |
| 48425 | 543 | } | 
| 50367 | 544 | ///}}} | 
| 48425 | 545 | case None => sleep(); loop(pending, running, results) | 
| 48373 | 546 | } | 
| 51253 | 547 | } | 
| 48425 | 548 | } | 
| 549 | ||
| 51220 | 550 | |
| 551 | /* build results */ | |
| 552 | ||
| 62641 | 553 | val results0 = | 
| 48583 | 554 |       if (deps.is_empty) {
 | 
| 56782 
433cf57550fa
more systematic Isabelle output, like in classic Isabelle/ML (without markup);
 wenzelm parents: 
56780diff
changeset | 555 |         progress.echo(Output.warning_text("Nothing to build"))
 | 
| 50707 
5b2bf7611662
maintain session index on Scala side, for more determistic results;
 wenzelm parents: 
50686diff
changeset | 556 | Map.empty[String, Result] | 
| 48583 | 557 | } | 
| 558 | else loop(queue, Map.empty, Map.empty) | |
| 559 | ||
| 62641 | 560 | val results = | 
| 64265 | 561 | new Results( | 
| 562 | (for ((name, result) <- results0.iterator) | |
| 563 | yield (name, (result.process, result.info))).toMap) | |
| 62641 | 564 | |
| 565 |     if (results.rc != 0 && (verbose || !no_build)) {
 | |
| 566 | val unfinished = | |
| 567 |         (for {
 | |
| 568 | name <- results.sessions.iterator | |
| 569 | if !results(name).ok | |
| 570 | } yield name).toList.sorted | |
| 571 |       progress.echo("Unfinished session(s): " + commas(unfinished))
 | |
| 572 | } | |
| 573 | ||
| 51418 
7b8ce8403340
more accurate handling of global browser info at the very end (without races), subject to no_build and info.browser_info;
 wenzelm parents: 
51402diff
changeset | 574 | |
| 
7b8ce8403340
more accurate handling of global browser info at the very end (without races), subject to no_build and info.browser_info;
 wenzelm parents: 
51402diff
changeset | 575 | /* global browser info */ | 
| 
7b8ce8403340
more accurate handling of global browser info at the very end (without races), subject to no_build and info.browser_info;
 wenzelm parents: 
51402diff
changeset | 576 | |
| 
7b8ce8403340
more accurate handling of global browser info at the very end (without races), subject to no_build and info.browser_info;
 wenzelm parents: 
51402diff
changeset | 577 |     if (!no_build) {
 | 
| 
7b8ce8403340
more accurate handling of global browser info at the very end (without races), subject to no_build and info.browser_info;
 wenzelm parents: 
51402diff
changeset | 578 | val browser_chapters = | 
| 
7b8ce8403340
more accurate handling of global browser info at the very end (without races), subject to no_build and info.browser_info;
 wenzelm parents: 
51402diff
changeset | 579 |         (for {
 | 
| 62641 | 580 | (name, result) <- results0.iterator | 
| 62944 
3ee643c5ed00
more standard session build process, including browser_info;
 wenzelm parents: 
62902diff
changeset | 581 | if result.ok | 
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 582 | info = full_sessions(name) | 
| 51418 
7b8ce8403340
more accurate handling of global browser info at the very end (without races), subject to no_build and info.browser_info;
 wenzelm parents: 
51402diff
changeset | 583 |           if info.options.bool("browser_info")
 | 
| 
7b8ce8403340
more accurate handling of global browser info at the very end (without races), subject to no_build and info.browser_info;
 wenzelm parents: 
51402diff
changeset | 584 | } yield (info.chapter, (name, info.description))).toList.groupBy(_._1). | 
| 
7b8ce8403340
more accurate handling of global browser info at the very end (without races), subject to no_build and info.browser_info;
 wenzelm parents: 
51402diff
changeset | 585 |             map({ case (chapter, es) => (chapter, es.map(_._2)) }).filterNot(_._2.isEmpty)
 | 
| 
7b8ce8403340
more accurate handling of global browser info at the very end (without races), subject to no_build and info.browser_info;
 wenzelm parents: 
51402diff
changeset | 586 | |
| 
7b8ce8403340
more accurate handling of global browser info at the very end (without races), subject to no_build and info.browser_info;
 wenzelm parents: 
51402diff
changeset | 587 | for ((chapter, entries) <- browser_chapters) | 
| 62632 | 588 | Present.update_chapter_index(store.browser_info, chapter, entries) | 
| 51418 
7b8ce8403340
more accurate handling of global browser info at the very end (without races), subject to no_build and info.browser_info;
 wenzelm parents: 
51402diff
changeset | 589 | |
| 62632 | 590 | if (browser_chapters.nonEmpty) Present.make_global_index(store.browser_info) | 
| 51418 
7b8ce8403340
more accurate handling of global browser info at the very end (without races), subject to no_build and info.browser_info;
 wenzelm parents: 
51402diff
changeset | 591 | } | 
| 
7b8ce8403340
more accurate handling of global browser info at the very end (without races), subject to no_build and info.browser_info;
 wenzelm parents: 
51402diff
changeset | 592 | |
| 62641 | 593 | results | 
| 48341 | 594 | } | 
| 595 | ||
| 596 | ||
| 62833 | 597 | /* Isabelle tool wrapper */ | 
| 48341 | 598 | |
| 62833 | 599 |   val isabelle_tool = Isabelle_Tool("build", "build and manage Isabelle sessions", args =>
 | 
| 48341 | 600 |   {
 | 
| 62833 | 601 |     val build_options = Word.explode(Isabelle_System.getenv("ISABELLE_BUILD_OPTIONS"))
 | 
| 62590 | 602 | |
| 62833 | 603 | var select_dirs: List[Path] = Nil | 
| 64265 | 604 | var numa_shuffling = false | 
| 65308 | 605 | var pide = false | 
| 62833 | 606 | var requirements = false | 
| 607 | var exclude_session_groups: List[String] = Nil | |
| 608 | var all_sessions = false | |
| 609 | var build_heap = false | |
| 610 | var clean_build = false | |
| 611 | var dirs: List[Path] = Nil | |
| 612 | var session_groups: List[String] = Nil | |
| 613 | var max_jobs = 1 | |
| 614 | var check_keywords: Set[String] = Set.empty | |
| 615 | var list_files = false | |
| 616 | var no_build = false | |
| 617 | var options = (Options.init() /: build_options)(_ + _) | |
| 618 | var system_mode = false | |
| 619 | var verbose = false | |
| 620 | var exclude_sessions: List[String] = Nil | |
| 62590 | 621 | |
| 62833 | 622 |     val getopts = Getopts("""
 | 
| 62590 | 623 | Usage: isabelle build [OPTIONS] [SESSIONS ...] | 
| 624 | ||
| 625 | Options are: | |
| 626 | -D DIR include session directory and select its sessions | |
| 64265 | 627 | -N cyclic shuffling of NUMA CPU nodes (performance tuning) | 
| 65308 | 628 | -P build via PIDE protocol | 
| 62590 | 629 | -R operate on requirements of selected sessions | 
| 630 | -X NAME exclude sessions from group NAME and all descendants | |
| 631 | -a select all sessions | |
| 632 | -b build heap images | |
| 633 | -c clean build | |
| 634 | -d DIR include session directory | |
| 635 | -g NAME select session group NAME | |
| 636 | -j INT maximum number of parallel jobs (default 1) | |
| 637 | -k KEYWORD check theory sources for conflicts with proposed keywords | |
| 638 | -l list session source files | |
| 639 | -n no build -- test dependencies only | |
| 640 | -o OPTION override Isabelle system OPTION (via NAME=VAL or NAME) | |
| 641 | -s system build mode: produce output in ISABELLE_HOME | |
| 642 | -v verbose | |
| 643 | -x NAME exclude session NAME and all descendants | |
| 644 | ||
| 62596 | 645 | Build and manage Isabelle sessions, depending on implicit settings: | 
| 646 | ||
| 64455 | 647 | """ + Library.prefix_lines("  ", Build_Log.Settings.show()) + "\n",
 | 
| 62833 | 648 | "D:" -> (arg => select_dirs = select_dirs ::: List(Path.explode(arg))), | 
| 64265 | 649 | "N" -> (_ => numa_shuffling = true), | 
| 65308 | 650 | "P" -> (_ => pide = true), | 
| 62833 | 651 | "R" -> (_ => requirements = true), | 
| 652 | "X:" -> (arg => exclude_session_groups = exclude_session_groups ::: List(arg)), | |
| 653 | "a" -> (_ => all_sessions = true), | |
| 654 | "b" -> (_ => build_heap = true), | |
| 655 | "c" -> (_ => clean_build = true), | |
| 656 | "d:" -> (arg => dirs = dirs ::: List(Path.explode(arg))), | |
| 657 | "g:" -> (arg => session_groups = session_groups ::: List(arg)), | |
| 63805 | 658 | "j:" -> (arg => max_jobs = Value.Int.parse(arg)), | 
| 62833 | 659 | "k:" -> (arg => check_keywords = check_keywords + arg), | 
| 660 | "l" -> (_ => list_files = true), | |
| 661 | "n" -> (_ => no_build = true), | |
| 662 | "o:" -> (arg => options = options + arg), | |
| 663 | "s" -> (_ => system_mode = true), | |
| 664 | "v" -> (_ => verbose = true), | |
| 665 | "x:" -> (arg => exclude_sessions = exclude_sessions ::: List(arg))) | |
| 62590 | 666 | |
| 62833 | 667 | val sessions = getopts(args) | 
| 62590 | 668 | |
| 64115 | 669 | val progress = new Console_Progress(verbose = verbose) | 
| 62590 | 670 | |
| 64140 | 671 | val start_date = Date.now() | 
| 672 | ||
| 62833 | 673 |     if (verbose) {
 | 
| 674 | progress.echo( | |
| 64155 | 675 | "Started at " + Build_Log.print_date(start_date) + | 
| 64140 | 676 |           " (" + Isabelle_System.getenv("ML_IDENTIFIER") + " on " + Isabelle_System.hostname() +")")
 | 
| 64081 | 677 | progress.echo(Build_Log.Settings.show() + "\n") | 
| 62833 | 678 | } | 
| 62590 | 679 | |
| 62833 | 680 | val results = | 
| 681 |       progress.interrupt_handler {
 | |
| 63224 | 682 | build(options, progress, | 
| 683 | build_heap = build_heap, | |
| 684 | clean_build = clean_build, | |
| 685 | dirs = dirs, | |
| 686 | select_dirs = select_dirs, | |
| 64265 | 687 | numa_shuffling = NUMA.enabled_warning(numa_shuffling), | 
| 63224 | 688 | max_jobs = max_jobs, | 
| 689 | list_files = list_files, | |
| 690 | check_keywords = check_keywords, | |
| 691 | no_build = no_build, | |
| 692 | system_mode = system_mode, | |
| 693 | verbose = verbose, | |
| 65308 | 694 | pide = pide, | 
| 63224 | 695 | requirements = requirements, | 
| 696 | all_sessions = all_sessions, | |
| 697 | exclude_session_groups = exclude_session_groups, | |
| 698 | exclude_sessions = exclude_sessions, | |
| 699 | session_groups = session_groups, | |
| 700 | sessions = sessions) | |
| 62833 | 701 | } | 
| 64140 | 702 | val end_date = Date.now() | 
| 703 | val elapsed_time = end_date.time - start_date.time | |
| 62590 | 704 | |
| 62833 | 705 |     if (verbose) {
 | 
| 64155 | 706 |       progress.echo("\nFinished at " + Build_Log.print_date(end_date))
 | 
| 62833 | 707 | } | 
| 62590 | 708 | |
| 62833 | 709 | val total_timing = | 
| 710 | (Timing.zero /: results.sessions.iterator.map(a => results(a).timing))(_ + _). | |
| 711 | copy(elapsed = elapsed_time) | |
| 712 | progress.echo(total_timing.message_resources) | |
| 62590 | 713 | |
| 62833 | 714 | sys.exit(results.rc) | 
| 715 | }) | |
| 48276 | 716 | } |