| author | wenzelm | 
| Tue, 05 Jul 2022 13:12:04 +0200 | |
| changeset 75654 | 21164fd15e3d | 
| parent 75394 | 42267c650205 | 
| child 75725 | cc711d229815 | 
| 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 | ||
| 73364 | 11 | import scala.collection.immutable.SortedSet | 
| 48340 
6f4fc030882a
allow explicit specification of additional session directories;
 wenzelm parents: 
48339diff
changeset | 12 | import scala.annotation.tailrec | 
| 48337 
9c7f8e5805b4
cumulate semantic Session_Info, based on syntactic Session_Entry;
 wenzelm parents: 
48336diff
changeset | 13 | |
| 48335 | 14 | |
| 75393 | 15 | object Build {
 | 
| 62631 | 16 | /** auxiliary **/ | 
| 48424 | 17 | |
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 18 | /* persistent build info */ | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 19 | |
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 20 | sealed case class Session_Info( | 
| 66749 
0445cfaf6132
more compact (second-order) digest for 10^2..10^3 source files, with slightly increased risk of collisions;
 wenzelm parents: 
66747diff
changeset | 21 | sources: String, | 
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 22 | input_heaps: List[String], | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 23 | output_heap: Option[String], | 
| 75393 | 24 | return_code: Int | 
| 25 |   ) {
 | |
| 66747 | 26 | def ok: Boolean = return_code == 0 | 
| 27 | } | |
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 28 | |
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 29 | |
| 65289 | 30 | /* queue with scheduling information */ | 
| 48676 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 31 | |
| 75393 | 32 |   private object Queue {
 | 
| 68086 
9e1c670301b8
cleanup session output before starting build job;
 wenzelm parents: 
67852diff
changeset | 33 | type Timings = (List[Properties.T], Double) | 
| 
9e1c670301b8
cleanup session output before starting build job;
 wenzelm parents: 
67852diff
changeset | 34 | |
| 75393 | 35 |     def load_timings(progress: Progress, store: Sessions.Store, session_name: String): Timings = {
 | 
| 68086 
9e1c670301b8
cleanup session output before starting build job;
 wenzelm parents: 
67852diff
changeset | 36 | val no_timings: Timings = (Nil, 0.0) | 
| 65289 | 37 | |
| 72634 
5cea0993ee4f
clarified access to single database server vs. collection of database files;
 wenzelm parents: 
72624diff
changeset | 38 |       store.try_open_database(session_name) match {
 | 
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 39 | case None => no_timings | 
| 68214 | 40 | case Some(db) => | 
| 75393 | 41 |           def ignore_error(msg: String) = {
 | 
| 68214 | 42 |             progress.echo_warning("Ignoring bad database " + db + (if (msg == "") "" else "\n" + msg))
 | 
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 43 | no_timings | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 44 | } | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 45 |           try {
 | 
| 71669 | 46 | val command_timings = store.read_command_timings(db, session_name) | 
| 68214 | 47 | val session_timing = | 
| 71669 | 48 |               store.read_session_timing(db, session_name) match {
 | 
| 68214 | 49 | case Markup.Elapsed(t) => t | 
| 50 | case _ => 0.0 | |
| 51 | } | |
| 52 | (command_timings, session_timing) | |
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 53 | } | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 54 |           catch {
 | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 55 | case ERROR(msg) => ignore_error(msg) | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 56 | 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 | 57 |             case _: XML.Error => ignore_error("")
 | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 58 | } | 
| 73367 | 59 |           finally { db.close() }
 | 
| 65289 | 60 | } | 
| 61 | } | |
| 62 | ||
| 75393 | 63 | def make_session_timing( | 
| 64 | sessions_structure: Sessions.Structure, | |
| 65 | timing: Map[String, Double] | |
| 66 |     ) : Map[String, Double] = {
 | |
| 68731 | 67 | val maximals = sessions_structure.build_graph.maximals.toSet | 
| 75393 | 68 |       def desc_timing(session_name: String): Double = {
 | 
| 71669 | 69 | if (maximals.contains(session_name)) timing(session_name) | 
| 68487 | 70 |         else {
 | 
| 71669 | 71 | val descendants = sessions_structure.build_descendants(List(session_name)).toSet | 
| 68731 | 72 | val g = sessions_structure.build_graph.restrict(descendants) | 
| 75394 | 73 |           (0.0 :: g.maximals.flatMap { desc =>
 | 
| 68487 | 74 | val ps = g.all_preds(List(desc)) | 
| 71895 | 75 | if (ps.exists(p => !timing.isDefinedAt(p))) None | 
| 68487 | 76 | else Some(ps.map(timing(_)).sum) | 
| 75394 | 77 | }).max | 
| 68487 | 78 | } | 
| 68486 
6984a55f3cba
clarified queue ordering: take session descendants into account, notably for "slow" AFP sessions;
 wenzelm parents: 
68331diff
changeset | 79 | } | 
| 
6984a55f3cba
clarified queue ordering: take session descendants into account, notably for "slow" AFP sessions;
 wenzelm parents: 
68331diff
changeset | 80 | timing.keySet.iterator.map(name => (name -> desc_timing(name))).toMap.withDefaultValue(0.0) | 
| 
6984a55f3cba
clarified queue ordering: take session descendants into account, notably for "slow" AFP sessions;
 wenzelm parents: 
68331diff
changeset | 81 | } | 
| 
6984a55f3cba
clarified queue ordering: take session descendants into account, notably for "slow" AFP sessions;
 wenzelm parents: 
68331diff
changeset | 82 | |
| 75393 | 83 | def apply( | 
| 84 | progress: Progress, | |
| 85 | sessions_structure: Sessions.Structure, | |
| 86 | store: Sessions.Store | |
| 87 |     ) : Queue = {
 | |
| 68731 | 88 | val graph = sessions_structure.build_graph | 
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 89 | val names = graph.keys | 
| 51220 | 90 | |
| 65831 | 91 | val timings = names.map(name => (name, load_timings(progress, store, name))) | 
| 51220 | 92 | val command_timings = | 
| 68486 
6984a55f3cba
clarified queue ordering: take session descendants into account, notably for "slow" AFP sessions;
 wenzelm parents: 
68331diff
changeset | 93 |         timings.map({ case (name, (ts, _)) => (name, ts) }).toMap.withDefaultValue(Nil)
 | 
| 51220 | 94 | val session_timing = | 
| 68731 | 95 | make_session_timing(sessions_structure, | 
| 96 |           timings.map({ case (name, (_, t)) => (name, t) }).toMap)
 | |
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 97 | |
| 75393 | 98 |       object Ordering extends scala.math.Ordering[String] {
 | 
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 99 | def compare(name1: String, name2: String): Int = | 
| 73365 | 100 |           session_timing(name2) compare session_timing(name1) match {
 | 
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 101 | case 0 => | 
| 68731 | 102 |               sessions_structure(name2).timeout compare sessions_structure(name1).timeout match {
 | 
| 68486 
6984a55f3cba
clarified queue ordering: take session descendants into account, notably for "slow" AFP sessions;
 wenzelm parents: 
68331diff
changeset | 103 | case 0 => name1 compare name2 | 
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 104 | case ord => ord | 
| 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 105 | } | 
| 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 106 | case ord => ord | 
| 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 107 | } | 
| 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 108 | } | 
| 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 109 | |
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 110 | 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 | 111 | } | 
| 
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 | |
| 65314 | 114 | private class Queue( | 
| 62631 | 115 | graph: Graph[String, Sessions.Info], | 
| 51220 | 116 | order: SortedSet[String], | 
| 75393 | 117 | val command_timings: String => List[Properties.T] | 
| 118 |   ) {
 | |
| 48676 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 119 | 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 | 120 | |
| 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 121 | 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 | 122 | |
| 51227 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 123 | def - (name: String): Queue = | 
| 73365 | 124 | new Queue(graph.del_node(name), order - name, command_timings) | 
| 48676 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 125 | |
| 75393 | 126 |     def dequeue(skip: String => Boolean): Option[(String, Sessions.Info)] = {
 | 
| 73365 | 127 | val it = order.iterator.dropWhile(name => skip(name) || !graph.is_minimal(name)) | 
| 73344 | 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 | |
| 62631 | 134 | |
| 62641 | 135 | /** build with results **/ | 
| 48424 | 136 | |
| 75393 | 137 |   class Results private[Build](results: Map[String, (Option[Process_Result], Sessions.Info)]) {
 | 
| 62403 | 138 | def sessions: Set[String] = results.keySet | 
| 72673 
8ff7a0e394f9
clarified PDF/HTML presentation, based on pdf blobs from session database (e.g. from earlier builds);
 wenzelm parents: 
72662diff
changeset | 139 | def infos: List[Sessions.Info] = results.values.map(_._2).toList | 
| 63082 
6af03422535a
expose Sessions.Info in Build.Results
 Lars Hupel <lars.hupel@mytum.de> parents: 
62946diff
changeset | 140 | 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 | 141 | def apply(name: String): Process_Result = results(name)._1.getOrElse(Process_Result(1)) | 
| 74701 
2bc24136bdeb
clarified order: prefer bottom-up construction of partial content;
 wenzelm parents: 
74700diff
changeset | 142 | def get_info(name: String): Option[Sessions.Info] = results.get(name).map(_._2) | 
| 
2bc24136bdeb
clarified order: prefer bottom-up construction of partial content;
 wenzelm parents: 
74700diff
changeset | 143 | def info(name: String): Sessions.Info = get_info(name).get | 
| 71601 | 144 | val rc: Int = | 
| 73359 | 145 |       results.iterator.map({ case (_, (Some(r), _)) => r.rc case (_, (None, _)) => 1 }).
 | 
| 74306 | 146 | foldLeft(Process_Result.RC.ok)(_ max _) | 
| 147 | def ok: Boolean = rc == Process_Result.RC.ok | |
| 62406 | 148 | |
| 149 | override def toString: String = rc.toString | |
| 62403 | 150 | } | 
| 151 | ||
| 72021 | 152 | def session_finished(session_name: String, process_result: Process_Result): String = | 
| 153 |     "Finished " + session_name + " (" + process_result.timing.message_resources + ")"
 | |
| 72013 | 154 | |
| 75393 | 155 |   def session_timing(session_name: String, build_log: Build_Log.Session_Info): String = {
 | 
| 72021 | 156 | val props = build_log.session_timing | 
| 157 | val threads = Markup.Session_Timing.Threads.unapply(props) getOrElse 1 | |
| 74782 | 158 | val timing = Markup.Timing_Properties.get(props) | 
| 72015 | 159 |     "Timing " + session_name + " (" + threads + " threads, " + timing.message_factor + ")"
 | 
| 72021 | 160 | } | 
| 72018 | 161 | |
| 62641 | 162 | def build( | 
| 50404 
898cac1dad5e
avoid startup within GUI thread -- it is only required later for dialog;
 wenzelm parents: 
50367diff
changeset | 163 | options: Options, | 
| 71981 | 164 | selection: Sessions.Selection = Sessions.Selection.empty, | 
| 72652 | 165 | presentation: Presentation.Context = Presentation.Context.none, | 
| 71726 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71718diff
changeset | 166 | progress: Progress = new Progress, | 
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 167 | check_unknown_files: Boolean = false, | 
| 48511 
37999ee01156
remove old output heaps, to ensure that result is valid wrt. check_stamps;
 wenzelm parents: 
48509diff
changeset | 168 | build_heap: Boolean = false, | 
| 48595 | 169 | clean_build: Boolean = false, | 
| 56890 | 170 | dirs: List[Path] = Nil, | 
| 171 | select_dirs: List[Path] = Nil, | |
| 66968 
9991671c98aa
allow to augment session context via explicit session infos;
 wenzelm parents: 
66962diff
changeset | 172 | infos: List[Sessions.Info] = Nil, | 
| 64265 | 173 | numa_shuffling: Boolean = false, | 
| 48509 | 174 | max_jobs: Int = 1, | 
| 48903 | 175 | list_files: Boolean = false, | 
| 59891 
9ce697050455
added isabelle build option -k, for fast off-line checking of theory sources;
 wenzelm parents: 
59811diff
changeset | 176 | check_keywords: Set[String] = Set.empty, | 
| 66841 | 177 | fresh_build: Boolean = false, | 
| 48509 | 178 | no_build: Boolean = false, | 
| 66745 | 179 | soft_build: Boolean = false, | 
| 48509 | 180 | verbose: Boolean = false, | 
| 73805 
b73777a0c076
allow build session setup, e.g. for protocol handlers;
 wenzelm parents: 
73804diff
changeset | 181 | export_files: Boolean = false, | 
| 75393 | 182 | session_setup: (String, Session) => Unit = (_, _) => () | 
| 183 |   ): Results = {
 | |
| 71677 | 184 | val build_options = | 
| 71679 
eeaa4021f080
proper "editor_tracing_messages=0" as in "isabelle dump";
 wenzelm parents: 
71677diff
changeset | 185 | options + | 
| 
eeaa4021f080
proper "editor_tracing_messages=0" as in "isabelle dump";
 wenzelm parents: 
71677diff
changeset | 186 | "completion_limit=0" + | 
| 
eeaa4021f080
proper "editor_tracing_messages=0" as in "isabelle dump";
 wenzelm parents: 
71677diff
changeset | 187 | "editor_tracing_messages=0" + | 
| 72728 
caa182bdab7a
clarified options: batch-build has pide_reports disabled by default (requires significant resources);
 wenzelm parents: 
72693diff
changeset | 188 |         ("pide_reports=" + options.bool("build_pide_reports"))
 | 
| 51220 | 189 | |
| 69854 
cc0b3e177b49
system option "system_heaps" supersedes various command-line options for "system build mode";
 wenzelm parents: 
69811diff
changeset | 190 | val store = Sessions.store(build_options) | 
| 66745 | 191 | |
| 69369 
6ecc85955e04
prefer "Isabelle DejaVu Sans", even for headless batch-build (session_graph.pdf);
 wenzelm parents: 
68957diff
changeset | 192 | Isabelle_Fonts.init() | 
| 
6ecc85955e04
prefer "Isabelle DejaVu Sans", even for headless batch-build (session_graph.pdf);
 wenzelm parents: 
68957diff
changeset | 193 | |
| 66745 | 194 | |
| 195 | /* session selection and dependencies */ | |
| 65422 | 196 | |
| 66961 | 197 | val full_sessions = | 
| 67026 | 198 | Sessions.load_structure(build_options, dirs = dirs, select_dirs = select_dirs, infos = infos) | 
| 65422 | 199 | |
| 73012 
238ddf525da4
clarified HTML presentation, e.g. avoid bulky jobs like HOL or HOL-Analysis in applications;
 wenzelm parents: 
72993diff
changeset | 200 | val full_sessions_selection = full_sessions.imports_selection(selection) | 
| 74808 | 201 | val full_sessions_selected = full_sessions_selection.toSet | 
| 73012 
238ddf525da4
clarified HTML presentation, e.g. avoid bulky jobs like HOL or HOL-Analysis in applications;
 wenzelm parents: 
72993diff
changeset | 202 | |
| 75393 | 203 |     def sources_stamp(deps: Sessions.Deps, session_name: String): String = {
 | 
| 66749 
0445cfaf6132
more compact (second-order) digest for 10^2..10^3 source files, with slightly increased risk of collisions;
 wenzelm parents: 
66747diff
changeset | 204 | val digests = | 
| 71669 | 205 | full_sessions(session_name).meta_digest :: | 
| 206 | deps.sources(session_name) ::: | |
| 207 | deps.imported_sources(session_name) | |
| 72654 | 208 | SHA1.digest_set(digests).toString | 
| 66749 
0445cfaf6132
more compact (second-order) digest for 10^2..10^3 source files, with slightly increased risk of collisions;
 wenzelm parents: 
66747diff
changeset | 209 | } | 
| 66745 | 210 | |
| 75393 | 211 |     val deps = {
 | 
| 66745 | 212 | val deps0 = | 
| 71896 
ce06d6456cc8
clarified signature --- fit within limit of 22 arguments;
 wenzelm parents: 
71895diff
changeset | 213 | Sessions.deps(full_sessions.selection(selection), | 
| 66962 
e1bde71bace6
clarified signature: global_theories is always required;
 wenzelm parents: 
66961diff
changeset | 214 | progress = progress, inlined_files = true, verbose = verbose, | 
| 
e1bde71bace6
clarified signature: global_theories is always required;
 wenzelm parents: 
66961diff
changeset | 215 | list_files = list_files, check_keywords = check_keywords).check_errors | 
| 66745 | 216 | |
| 66841 | 217 |       if (soft_build && !fresh_build) {
 | 
| 66745 | 218 | val outdated = | 
| 68204 | 219 | deps0.sessions_structure.build_topological_order.flatMap(name => | 
| 72634 
5cea0993ee4f
clarified access to single database server vs. collection of database files;
 wenzelm parents: 
72624diff
changeset | 220 |             store.try_open_database(name) match {
 | 
| 68214 | 221 | case Some(db) => | 
| 68216 | 222 |                 using(db)(store.read_build(_, name)) match {
 | 
| 66745 | 223 | case Some(build) | 
| 66747 | 224 | if build.ok && build.sources == sources_stamp(deps0, name) => None | 
| 66745 | 225 | case _ => Some(name) | 
| 226 | } | |
| 227 | case None => Some(name) | |
| 228 | }) | |
| 68204 | 229 | |
| 230 | Sessions.deps(full_sessions.selection(Sessions.Selection(sessions = outdated)), | |
| 70671 
cb1776c8e216
clarified signature: retain global session information, unaffected by later restriction;
 wenzelm parents: 
70509diff
changeset | 231 | progress = progress, inlined_files = true).check_errors | 
| 66745 | 232 | } | 
| 68204 | 233 | else deps0 | 
| 66745 | 234 | } | 
| 235 | ||
| 74800 | 236 | val presentation_sessions = | 
| 237 |       (for {
 | |
| 74813 
2ad892ac749a
present only selected session theories (as in Isabelle2021), in contrast to 2bc24136bdeb, eb89b3a37826;
 wenzelm parents: 
74808diff
changeset | 238 | session_name <- deps.sessions_structure.build_topological_order.iterator | 
| 74808 | 239 | info <- deps.sessions_structure.get(session_name) | 
| 240 | if full_sessions_selected(session_name) && presentation.enabled(info) } | |
| 74800 | 241 | yield info).toList | 
| 242 | ||
| 66745 | 243 | |
| 244 | /* check unknown files */ | |
| 48504 
21dfd6c04482
actually check source vs. target stamps, based on information from log files;
 wenzelm parents: 
48494diff
changeset | 245 | |
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 246 |     if (check_unknown_files) {
 | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 247 | val source_files = | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 248 |         (for {
 | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 249 | (_, base) <- deps.session_bases.iterator | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 250 | (path, _) <- base.sources.iterator | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 251 | } yield path).toList | 
| 67098 | 252 |       val exclude_files = List(Path.explode("$POLYML_EXE")).map(_.canonical_file)
 | 
| 253 | val unknown_files = | |
| 67782 
7e223a05e6d8
clarified notion of unknown files: ignore files outside of a Mercurial repository;
 wenzelm parents: 
67493diff
changeset | 254 | Mercurial.check_files(source_files)._2. | 
| 67098 | 255 | filterNot(path => exclude_files.contains(path.canonical_file)) | 
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 256 |       if (unknown_files.nonEmpty) {
 | 
| 67782 
7e223a05e6d8
clarified notion of unknown files: ignore files outside of a Mercurial repository;
 wenzelm parents: 
67493diff
changeset | 257 |         progress.echo_warning("Unknown files (not part of the underlying Mercurial repository):" +
 | 
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 258 |           unknown_files.map(path => path.expand.implode).sorted.mkString("\n  ", "\n  ", ""))
 | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 259 | } | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 260 | } | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 261 | |
| 51220 | 262 | |
| 263 | /* main build process */ | |
| 264 | ||
| 68204 | 265 | val queue = Queue(progress, deps.sessions_structure, store) | 
| 65289 | 266 | |
| 68219 | 267 | store.prepare_output_dir() | 
| 48373 | 268 | |
| 48595 | 269 |     if (clean_build) {
 | 
| 73012 
238ddf525da4
clarified HTML presentation, e.g. avoid bulky jobs like HOL or HOL-Analysis in applications;
 wenzelm parents: 
72993diff
changeset | 270 |       for (name <- full_sessions.imports_descendants(full_sessions_selection)) {
 | 
| 68220 
8fc4e3d1df86
clarified store.clean_output: cleanup user_output_dir even in system_mode;
 wenzelm parents: 
68219diff
changeset | 271 | val (relevant, ok) = store.clean_output(name) | 
| 
8fc4e3d1df86
clarified store.clean_output: cleanup user_output_dir even in system_mode;
 wenzelm parents: 
68219diff
changeset | 272 |         if (relevant) {
 | 
| 
8fc4e3d1df86
clarified store.clean_output: cleanup user_output_dir even in system_mode;
 wenzelm parents: 
68219diff
changeset | 273 |           if (ok) progress.echo("Cleaned " + name)
 | 
| 
8fc4e3d1df86
clarified store.clean_output: cleanup user_output_dir even in system_mode;
 wenzelm parents: 
68219diff
changeset | 274 | else progress.echo(name + " FAILED to clean") | 
| 
8fc4e3d1df86
clarified store.clean_output: cleanup user_output_dir even in system_mode;
 wenzelm parents: 
68219diff
changeset | 275 | } | 
| 48595 | 276 | } | 
| 277 | } | |
| 278 | ||
| 48425 | 279 | // scheduler loop | 
| 63082 
6af03422535a
expose Sessions.Info in Build.Results
 Lars Hupel <lars.hupel@mytum.de> parents: 
62946diff
changeset | 280 | case class Result( | 
| 66594 | 281 | current: Boolean, | 
| 68213 | 282 | heap_digest: Option[String], | 
| 66594 | 283 | process: Option[Process_Result], | 
| 75393 | 284 | info: Sessions.Info | 
| 285 |     ) {
 | |
| 62402 | 286 | def ok: Boolean = | 
| 287 |         process match {
 | |
| 288 | case None => false | |
| 74306 | 289 | case Some(res) => res.ok | 
| 62402 | 290 | } | 
| 291 | } | |
| 48639 
675988e64bf9
store parent heap stamp as well -- needs to be propagated through the build hierarchy;
 wenzelm parents: 
48626diff
changeset | 292 | |
| 73340 | 293 | def sleep(): Unit = | 
| 73702 
7202e12cb324
tuned signature --- following hints by IntelliJ IDEA;
 wenzelm parents: 
73522diff
changeset | 294 |       Isabelle_Thread.interrupt_handler(_ => progress.stop()) { Time.seconds(0.5).sleep() }
 | 
| 50366 | 295 | |
| 73777 
52e43a93d51f
clarified system_log: make this work independently of the particular "isabelle build" command-line (e.g. "isabelle mirabelle");
 wenzelm parents: 
73774diff
changeset | 296 | val log = | 
| 
52e43a93d51f
clarified system_log: make this work independently of the particular "isabelle build" command-line (e.g. "isabelle mirabelle");
 wenzelm parents: 
73774diff
changeset | 297 |       build_options.string("system_log") match {
 | 
| 
52e43a93d51f
clarified system_log: make this work independently of the particular "isabelle build" command-line (e.g. "isabelle mirabelle");
 wenzelm parents: 
73774diff
changeset | 298 | case "" => No_Logger | 
| 74827 
c1b5d6e6ff74
clarified system option standard values: avoid oddities like "isabelle build -o document_output" producing directories named "true";
 wenzelm parents: 
74818diff
changeset | 299 | case "-" => Logger.make(progress) | 
| 73777 
52e43a93d51f
clarified system_log: make this work independently of the particular "isabelle build" command-line (e.g. "isabelle mirabelle");
 wenzelm parents: 
73774diff
changeset | 300 | case log_file => Logger.make(Some(Path.explode(log_file))) | 
| 
52e43a93d51f
clarified system_log: make this work independently of the particular "isabelle build" command-line (e.g. "isabelle mirabelle");
 wenzelm parents: 
73774diff
changeset | 301 | } | 
| 
52e43a93d51f
clarified system_log: make this work independently of the particular "isabelle build" command-line (e.g. "isabelle mirabelle");
 wenzelm parents: 
73774diff
changeset | 302 | |
| 64265 | 303 | val numa_nodes = new NUMA.Nodes(numa_shuffling) | 
| 304 | ||
| 48425 | 305 | @tailrec def loop( | 
| 48676 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 306 | pending: Queue, | 
| 72662 | 307 | running: Map[String, (List[String], Build_Job)], | 
| 75393 | 308 | results: Map[String, Result] | 
| 309 |     ): Map[String, Result] = {
 | |
| 64265 | 310 | def used_node(i: Int): Boolean = | 
| 311 | running.iterator.exists( | |
| 312 |           { case (_, (_, job)) => job.numa_node.isDefined && job.numa_node.get == i })
 | |
| 313 | ||
| 48425 | 314 | if (pending.is_empty) results | 
| 51253 | 315 |       else {
 | 
| 71726 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71718diff
changeset | 316 |         if (progress.stopped) {
 | 
| 73367 | 317 | for ((_, (_, job)) <- running) job.terminate() | 
| 71726 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71718diff
changeset | 318 | } | 
| 51253 | 319 | |
| 48674 | 320 |         running.find({ case (_, (_, job)) => job.is_finished }) match {
 | 
| 71669 | 321 | case Some((session_name, (input_heaps, job))) => | 
| 50367 | 322 |             //{{{ finish job
 | 
| 48424 | 323 | |
| 68217 | 324 | val (process_result, heap_digest) = job.join | 
| 48373 | 325 | |
| 71623 
b3bddebe44ca
clarified signature: more explicit type Protocol_Message.Marker;
 wenzelm parents: 
71614diff
changeset | 326 | val log_lines = process_result.out_lines.filterNot(Protocol_Message.Marker.test) | 
| 75393 | 327 |             val process_result_tail = {
 | 
| 62409 
e391528eff3b
proper option process_output_tail, more generous default;
 wenzelm parents: 
62406diff
changeset | 328 |               val tail = job.info.options.int("process_output_tail")
 | 
| 62632 | 329 | process_result.copy( | 
| 330 | out_lines = | |
| 71669 | 331 | "(see also " + store.output_log(session_name).file.toString + ")" :: | 
| 65294 | 332 | (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 | 333 | } | 
| 
13a0f537e232
retain tail out_lines as printed, but not the whole log content;
 wenzelm parents: 
62403diff
changeset | 334 | |
| 72017 | 335 | val build_log = | 
| 336 | Build_Log.Log_File(session_name, process_result.out_lines). | |
| 337 | parse_session_info( | |
| 338 | command_timings = true, | |
| 339 | theory_timings = true, | |
| 340 | ml_statistics = true, | |
| 341 | task_statistics = true) | |
| 66666 
1a620647285c
clarified messages: after writing all files (see also 27f90319a499 and 57c85c83c11b);
 wenzelm parents: 
66595diff
changeset | 342 | |
| 
1a620647285c
clarified messages: after writing all files (see also 27f90319a499 and 57c85c83c11b);
 wenzelm parents: 
66595diff
changeset | 343 | // write log file | 
| 68217 | 344 |             if (process_result.ok) {
 | 
| 71669 | 345 | File.write_gzip(store.output_log_gz(session_name), terminate_lines(log_lines)) | 
| 68217 | 346 | } | 
| 71669 | 347 | else File.write(store.output_log(session_name), terminate_lines(log_lines)) | 
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 348 | |
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 349 | // write database | 
| 72017 | 350 | using(store.open_database(session_name, output = true))(db => | 
| 351 | store.write_session_info(db, session_name, | |
| 352 | build_log = | |
| 353 |                   if (process_result.timeout) build_log.error("Timeout") else build_log,
 | |
| 354 | build = | |
| 355 | Session_Info(sources_stamp(deps, session_name), input_heaps, heap_digest, | |
| 356 | process_result.rc))) | |
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 357 | |
| 66666 
1a620647285c
clarified messages: after writing all files (see also 27f90319a499 and 57c85c83c11b);
 wenzelm parents: 
66595diff
changeset | 358 | // messages | 
| 71601 | 359 | process_result.err_lines.foreach(progress.echo) | 
| 66666 
1a620647285c
clarified messages: after writing all files (see also 27f90319a499 and 57c85c83c11b);
 wenzelm parents: 
66595diff
changeset | 360 | |
| 72013 | 361 |             if (process_result.ok) {
 | 
| 72022 
45865bb06182
clarified message --- as in former ML version (see 940195fbb282);
 wenzelm parents: 
72021diff
changeset | 362 | if (verbose) progress.echo(session_timing(session_name, build_log)) | 
| 72021 | 363 | progress.echo(session_finished(session_name, process_result)) | 
| 72013 | 364 | } | 
| 66666 
1a620647285c
clarified messages: after writing all files (see also 27f90319a499 and 57c85c83c11b);
 wenzelm parents: 
66595diff
changeset | 365 |             else {
 | 
| 71669 | 366 | progress.echo(session_name + " FAILED") | 
| 66666 
1a620647285c
clarified messages: after writing all files (see also 27f90319a499 and 57c85c83c11b);
 wenzelm parents: 
66595diff
changeset | 367 | if (!process_result.interrupted) progress.echo(process_result_tail.out) | 
| 
1a620647285c
clarified messages: after writing all files (see also 27f90319a499 and 57c85c83c11b);
 wenzelm parents: 
66595diff
changeset | 368 | } | 
| 
1a620647285c
clarified messages: after writing all files (see also 27f90319a499 and 57c85c83c11b);
 wenzelm parents: 
66595diff
changeset | 369 | |
| 71669 | 370 | loop(pending - session_name, running - session_name, | 
| 371 | results + | |
| 372 | (session_name -> Result(false, heap_digest, Some(process_result_tail), job.info))) | |
| 50367 | 373 | //}}} | 
| 60215 | 374 | case None if running.size < (max_jobs max 1) => | 
| 50367 | 375 |             //{{{ check/start next job
 | 
| 71601 | 376 |             pending.dequeue(running.isDefinedAt) match {
 | 
| 71669 | 377 | case Some((session_name, info)) => | 
| 66848 | 378 | val ancestor_results = | 
| 71669 | 379 | deps.sessions_structure.build_requirements(List(session_name)). | 
| 380 | filterNot(_ == session_name).map(results(_)) | |
| 68213 | 381 | val ancestor_heaps = ancestor_results.flatMap(_.heap_digest) | 
| 62628 | 382 | |
| 71669 | 383 | val do_store = | 
| 384 | build_heap || Sessions.is_pure(session_name) || queue.is_inner(session_name) | |
| 48547 | 385 | |
| 75393 | 386 |                 val (current, heap_digest) = {
 | 
| 72634 
5cea0993ee4f
clarified access to single database server vs. collection of database files;
 wenzelm parents: 
72624diff
changeset | 387 |                   store.try_open_database(session_name) match {
 | 
| 68212 
5a59fded83c7
clarified heap vs. database operations: discontinued correlation of directory;
 wenzelm parents: 
68209diff
changeset | 388 | case Some(db) => | 
| 71669 | 389 |                       using(db)(store.read_build(_, session_name)) match {
 | 
| 68216 | 390 | case Some(build) => | 
| 71669 | 391 | val heap_digest = store.find_heap_digest(session_name) | 
| 68216 | 392 | val current = | 
| 393 | !fresh_build && | |
| 394 | build.ok && | |
| 71669 | 395 | build.sources == sources_stamp(deps, session_name) && | 
| 68216 | 396 | build.input_heaps == ancestor_heaps && | 
| 397 | build.output_heap == heap_digest && | |
| 71614 | 398 | !(do_store && heap_digest.isEmpty) | 
| 68216 | 399 | (current, heap_digest) | 
| 400 | case None => (false, None) | |
| 401 | } | |
| 62636 | 402 | case None => (false, None) | 
| 48504 
21dfd6c04482
actually check source vs. target stamps, based on information from log files;
 wenzelm parents: 
48494diff
changeset | 403 | } | 
| 48547 | 404 | } | 
| 62628 | 405 | val all_current = current && ancestor_results.forall(_.current) | 
| 48528 
784c6f63d79c
proper all_current, which regards parent status as well;
 wenzelm parents: 
48511diff
changeset | 406 | |
| 48547 | 407 | if (all_current) | 
| 71669 | 408 | loop(pending - session_name, running, | 
| 409 | results + | |
| 410 | (session_name -> Result(true, heap_digest, Some(Process_Result(0)), info))) | |
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 411 |                 else if (no_build) {
 | 
| 71894 | 412 | progress.echo_if(verbose, "Skipping " + session_name + " ...") | 
| 71669 | 413 | loop(pending - session_name, running, | 
| 414 | results + | |
| 415 | (session_name -> Result(false, heap_digest, Some(Process_Result(1)), info))) | |
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 416 | } | 
| 62628 | 417 |                 else if (ancestor_results.forall(_.ok) && !progress.stopped) {
 | 
| 71669 | 418 | progress.echo((if (do_store) "Building " else "Running ") + session_name + " ...") | 
| 68086 
9e1c670301b8
cleanup session output before starting build job;
 wenzelm parents: 
67852diff
changeset | 419 | |
| 71669 | 420 | store.clean_output(session_name) | 
| 421 | using(store.open_database(session_name, output = true))( | |
| 422 | store.init_session_info(_, session_name)) | |
| 68086 
9e1c670301b8
cleanup session output before starting build job;
 wenzelm parents: 
67852diff
changeset | 423 | |
| 71601 | 424 | val numa_node = numa_nodes.next(used_node) | 
| 51220 | 425 | val job = | 
| 72693 | 426 | new Build_Job(progress, session_name, info, deps, store, do_store, | 
| 73805 
b73777a0c076
allow build session setup, e.g. for protocol handlers;
 wenzelm parents: 
73804diff
changeset | 427 | log, session_setup, numa_node, queue.command_timings(session_name)) | 
| 71669 | 428 | loop(pending, running + (session_name -> (ancestor_heaps, job)), results) | 
| 48547 | 429 | } | 
| 430 |                 else {
 | |
| 71669 | 431 | progress.echo(session_name + " CANCELLED") | 
| 432 | loop(pending - session_name, running, | |
| 433 | results + (session_name -> Result(false, heap_digest, None, info))) | |
| 48547 | 434 | } | 
| 435 | case None => sleep(); loop(pending, running, results) | |
| 48425 | 436 | } | 
| 50367 | 437 | ///}}} | 
| 48425 | 438 | case None => sleep(); loop(pending, running, results) | 
| 48373 | 439 | } | 
| 51253 | 440 | } | 
| 48425 | 441 | } | 
| 442 | ||
| 51220 | 443 | |
| 444 | /* build results */ | |
| 445 | ||
| 75393 | 446 |     val results = {
 | 
| 72673 
8ff7a0e394f9
clarified PDF/HTML presentation, based on pdf blobs from session database (e.g. from earlier builds);
 wenzelm parents: 
72662diff
changeset | 447 | val results0 = | 
| 
8ff7a0e394f9
clarified PDF/HTML presentation, based on pdf blobs from session database (e.g. from earlier builds);
 wenzelm parents: 
72662diff
changeset | 448 |         if (deps.is_empty) {
 | 
| 
8ff7a0e394f9
clarified PDF/HTML presentation, based on pdf blobs from session database (e.g. from earlier builds);
 wenzelm parents: 
72662diff
changeset | 449 |           progress.echo_warning("Nothing to build")
 | 
| 
8ff7a0e394f9
clarified PDF/HTML presentation, based on pdf blobs from session database (e.g. from earlier builds);
 wenzelm parents: 
72662diff
changeset | 450 | Map.empty[String, Result] | 
| 
8ff7a0e394f9
clarified PDF/HTML presentation, based on pdf blobs from session database (e.g. from earlier builds);
 wenzelm parents: 
72662diff
changeset | 451 | } | 
| 
8ff7a0e394f9
clarified PDF/HTML presentation, based on pdf blobs from session database (e.g. from earlier builds);
 wenzelm parents: 
72662diff
changeset | 452 |         else Isabelle_Thread.uninterruptible { loop(queue, Map.empty, Map.empty) }
 | 
| 48583 | 453 | |
| 64265 | 454 | new Results( | 
| 455 | (for ((name, result) <- results0.iterator) | |
| 456 | yield (name, (result.process, result.info))).toMap) | |
| 72673 
8ff7a0e394f9
clarified PDF/HTML presentation, based on pdf blobs from session database (e.g. from earlier builds);
 wenzelm parents: 
72662diff
changeset | 457 | } | 
| 62641 | 458 | |
| 69811 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 459 |     if (export_files) {
 | 
| 71896 
ce06d6456cc8
clarified signature --- fit within limit of 22 arguments;
 wenzelm parents: 
71895diff
changeset | 460 |       for (name <- full_sessions.imports_selection(selection).iterator if results(name).ok) {
 | 
| 69811 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 461 | val info = results.info(name) | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 462 |         if (info.export_files.nonEmpty) {
 | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 463 |           progress.echo("Exporting " + info.name + " ...")
 | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 464 |           for ((dir, prune, pats) <- info.export_files) {
 | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 465 | Export.export_files(store, name, info.dir + dir, | 
| 71726 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71718diff
changeset | 466 | progress = if (verbose) progress else new Progress, | 
| 69811 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 467 | export_prune = prune, | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 468 | export_patterns = pats) | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 469 | } | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 470 | } | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 471 | } | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 472 | } | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 473 | |
| 74306 | 474 |     if (results.rc != Process_Result.RC.ok && (verbose || !no_build)) {
 | 
| 62641 | 475 | val unfinished = | 
| 476 |         (for {
 | |
| 477 | name <- results.sessions.iterator | |
| 478 | if !results(name).ok | |
| 479 | } yield name).toList.sorted | |
| 480 |       progress.echo("Unfinished session(s): " + commas(unfinished))
 | |
| 481 | } | |
| 482 | ||
| 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 | 483 | |
| 72673 
8ff7a0e394f9
clarified PDF/HTML presentation, based on pdf blobs from session database (e.g. from earlier builds);
 wenzelm parents: 
72662diff
changeset | 484 | /* PDF/HTML presentation */ | 
| 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 | 485 | |
| 72677 | 486 |     if (!no_build && !progress.stopped && results.ok) {
 | 
| 74698 | 487 |       if (presentation_sessions.nonEmpty) {
 | 
| 72676 | 488 | val presentation_dir = presentation.dir(store) | 
| 489 |         progress.echo("Presentation in " + presentation_dir.absolute)
 | |
| 74709 | 490 | Presentation.update_root(presentation_dir) | 
| 74701 
2bc24136bdeb
clarified order: prefer bottom-up construction of partial content;
 wenzelm parents: 
74700diff
changeset | 491 | |
| 
2bc24136bdeb
clarified order: prefer bottom-up construction of partial content;
 wenzelm parents: 
74700diff
changeset | 492 |         for ((chapter, infos) <- presentation_sessions.groupBy(_.chapter).iterator) {
 | 
| 
2bc24136bdeb
clarified order: prefer bottom-up construction of partial content;
 wenzelm parents: 
74700diff
changeset | 493 | val entries = infos.map(info => (info.name, info.description)) | 
| 74709 | 494 | Presentation.update_chapter(presentation_dir, chapter, entries) | 
| 74701 
2bc24136bdeb
clarified order: prefer bottom-up construction of partial content;
 wenzelm parents: 
74700diff
changeset | 495 | } | 
| 72676 | 496 | |
| 75394 | 497 |         using(store.open_database_context()) { db_context =>
 | 
| 74818 
3064e165c660
clarified HTML_Context.theory_exports: prefer value-oriented parallelism;
 wenzelm parents: 
74815diff
changeset | 498 | val exports = | 
| 
3064e165c660
clarified HTML_Context.theory_exports: prefer value-oriented parallelism;
 wenzelm parents: 
74815diff
changeset | 499 | Presentation.read_exports(presentation_sessions.map(_.name), deps, db_context) | 
| 74798 
507f50dbeb79
just one Presentation.State for all sessions: avoid duplication of already presented theories (very slow) and cached theory export (not very slow);
 wenzelm parents: 
74782diff
changeset | 500 | |
| 75394 | 501 |           Par_List.map({ (session: String) =>
 | 
| 72683 | 502 | progress.expose_interrupt() | 
| 74770 
32c2587cda4f
clarified HTML_Context: more explicit directory structure;
 wenzelm parents: 
74767diff
changeset | 503 |             progress.echo("Presenting " + session + " ...")
 | 
| 
32c2587cda4f
clarified HTML_Context: more explicit directory structure;
 wenzelm parents: 
74767diff
changeset | 504 | |
| 
32c2587cda4f
clarified HTML_Context: more explicit directory structure;
 wenzelm parents: 
74767diff
changeset | 505 | val html_context = | 
| 
32c2587cda4f
clarified HTML_Context: more explicit directory structure;
 wenzelm parents: 
74767diff
changeset | 506 |               new Presentation.HTML_Context {
 | 
| 
32c2587cda4f
clarified HTML_Context: more explicit directory structure;
 wenzelm parents: 
74767diff
changeset | 507 | override def root_dir: Path = presentation_dir | 
| 
32c2587cda4f
clarified HTML_Context: more explicit directory structure;
 wenzelm parents: 
74767diff
changeset | 508 | override def theory_session(name: Document.Node.Name): Sessions.Info = | 
| 
32c2587cda4f
clarified HTML_Context: more explicit directory structure;
 wenzelm parents: 
74767diff
changeset | 509 | deps.sessions_structure(deps(session).theory_qualifier(name)) | 
| 74828 
46c7fafbea3d
more compact data during presentation: Entity_Context.Theory_Export instead of full Export_Theory.Theory;
 wenzelm parents: 
74827diff
changeset | 510 | override def theory_exports: Theory_Exports = exports | 
| 74770 
32c2587cda4f
clarified HTML_Context: more explicit directory structure;
 wenzelm parents: 
74767diff
changeset | 511 | } | 
| 72962 | 512 | Presentation.session_html( | 
| 74770 
32c2587cda4f
clarified HTML_Context: more explicit directory structure;
 wenzelm parents: 
74767diff
changeset | 513 | session, deps, db_context, progress = progress, | 
| 74818 
3064e165c660
clarified HTML_Context.theory_exports: prefer value-oriented parallelism;
 wenzelm parents: 
74815diff
changeset | 514 | verbose = verbose, html_context = html_context, | 
| 74770 
32c2587cda4f
clarified HTML_Context: more explicit directory structure;
 wenzelm parents: 
74767diff
changeset | 515 | Presentation.elements1) | 
| 74818 
3064e165c660
clarified HTML_Context.theory_exports: prefer value-oriented parallelism;
 wenzelm parents: 
74815diff
changeset | 516 | }, presentation_sessions.map(_.name)) | 
| 75394 | 517 | } | 
| 72676 | 518 | } | 
| 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 | 519 | } | 
| 
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 | 520 | |
| 62641 | 521 | results | 
| 48341 | 522 | } | 
| 523 | ||
| 524 | ||
| 62833 | 525 | /* Isabelle tool wrapper */ | 
| 48341 | 526 | |
| 72763 | 527 |   val isabelle_tool = Isabelle_Tool("build", "build and manage Isabelle sessions",
 | 
| 75394 | 528 | Scala_Project.here, | 
| 529 |     { args =>
 | |
| 530 |       val build_options = Word.explode(Isabelle_System.getenv("ISABELLE_BUILD_OPTIONS"))
 | |
| 62590 | 531 | |
| 75394 | 532 | var base_sessions: List[String] = Nil | 
| 533 | var select_dirs: List[Path] = Nil | |
| 534 | var numa_shuffling = false | |
| 535 | var presentation = Presentation.Context.none | |
| 536 | var requirements = false | |
| 537 | var soft_build = false | |
| 538 | var exclude_session_groups: List[String] = Nil | |
| 539 | var all_sessions = false | |
| 540 | var build_heap = false | |
| 541 | var clean_build = false | |
| 542 | var dirs: List[Path] = Nil | |
| 543 | var export_files = false | |
| 544 | var fresh_build = false | |
| 545 | var session_groups: List[String] = Nil | |
| 546 | var max_jobs = 1 | |
| 547 | var check_keywords: Set[String] = Set.empty | |
| 548 | var list_files = false | |
| 549 | var no_build = false | |
| 550 | var options = Options.init(opts = build_options) | |
| 551 | var verbose = false | |
| 552 | var exclude_sessions: List[String] = Nil | |
| 62590 | 553 | |
| 75394 | 554 |       val getopts = Getopts("""
 | 
| 62590 | 555 | Usage: isabelle build [OPTIONS] [SESSIONS ...] | 
| 556 | ||
| 557 | Options are: | |
| 66737 
2edc0c42c883
option -B for "isabelle build" and "isabelle imports";
 wenzelm parents: 
66736diff
changeset | 558 | -B NAME include session NAME and all descendants | 
| 62590 | 559 | -D DIR include session directory and select its sessions | 
| 64265 | 560 | -N cyclic shuffling of NUMA CPU nodes (performance tuning) | 
| 72648 | 561 |     -P DIR       enable HTML/PDF presentation in directory (":" for default)
 | 
| 71807 | 562 | -R refer to requirements of selected sessions | 
| 66745 | 563 | -S soft build: only observe changes of sources, not heap images | 
| 62590 | 564 | -X NAME exclude sessions from group NAME and all descendants | 
| 565 | -a select all sessions | |
| 566 | -b build heap images | |
| 567 | -c clean build | |
| 568 | -d DIR include session directory | |
| 69811 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 569 | -e export files from session specification into file-system | 
| 66841 | 570 | -f fresh build | 
| 62590 | 571 | -g NAME select session group NAME | 
| 572 | -j INT maximum number of parallel jobs (default 1) | |
| 573 | -k KEYWORD check theory sources for conflicts with proposed keywords | |
| 574 | -l list session source files | |
| 575 | -n no build -- test dependencies only | |
| 576 | -o OPTION override Isabelle system OPTION (via NAME=VAL or NAME) | |
| 577 | -v verbose | |
| 578 | -x NAME exclude session NAME and all descendants | |
| 579 | ||
| 62596 | 580 | Build and manage Isabelle sessions, depending on implicit settings: | 
| 581 | ||
| 73736 | 582 | """ + Library.indent_lines(2, Build_Log.Settings.show()) + "\n", | 
| 75394 | 583 | "B:" -> (arg => base_sessions = base_sessions ::: List(arg)), | 
| 584 | "D:" -> (arg => select_dirs = select_dirs ::: List(Path.explode(arg))), | |
| 585 | "N" -> (_ => numa_shuffling = true), | |
| 586 | "P:" -> (arg => presentation = Presentation.Context.make(arg)), | |
| 587 | "R" -> (_ => requirements = true), | |
| 588 | "S" -> (_ => soft_build = true), | |
| 589 | "X:" -> (arg => exclude_session_groups = exclude_session_groups ::: List(arg)), | |
| 590 | "a" -> (_ => all_sessions = true), | |
| 591 | "b" -> (_ => build_heap = true), | |
| 592 | "c" -> (_ => clean_build = true), | |
| 593 | "d:" -> (arg => dirs = dirs ::: List(Path.explode(arg))), | |
| 594 | "e" -> (_ => export_files = true), | |
| 595 | "f" -> (_ => fresh_build = true), | |
| 596 | "g:" -> (arg => session_groups = session_groups ::: List(arg)), | |
| 597 | "j:" -> (arg => max_jobs = Value.Int.parse(arg)), | |
| 598 | "k:" -> (arg => check_keywords = check_keywords + arg), | |
| 599 | "l" -> (_ => list_files = true), | |
| 600 | "n" -> (_ => no_build = true), | |
| 601 | "o:" -> (arg => options = options + arg), | |
| 602 | "v" -> (_ => verbose = true), | |
| 603 | "x:" -> (arg => exclude_sessions = exclude_sessions ::: List(arg))) | |
| 62590 | 604 | |
| 75394 | 605 | val sessions = getopts(args) | 
| 62590 | 606 | |
| 75394 | 607 | val progress = new Console_Progress(verbose = verbose) | 
| 62590 | 608 | |
| 75394 | 609 | val start_date = Date.now() | 
| 64140 | 610 | |
| 75394 | 611 |       if (verbose) {
 | 
| 612 | progress.echo( | |
| 613 | "Started at " + Build_Log.print_date(start_date) + | |
| 614 |             " (" + Isabelle_System.getenv("ML_IDENTIFIER") + " on " + Isabelle_System.hostname() +")")
 | |
| 615 | progress.echo(Build_Log.Settings.show() + "\n") | |
| 616 | } | |
| 62590 | 617 | |
| 75394 | 618 | val results = | 
| 619 |         progress.interrupt_handler {
 | |
| 620 | build(options, | |
| 621 | selection = Sessions.Selection( | |
| 622 | requirements = requirements, | |
| 623 | all_sessions = all_sessions, | |
| 624 | base_sessions = base_sessions, | |
| 625 | exclude_session_groups = exclude_session_groups, | |
| 626 | exclude_sessions = exclude_sessions, | |
| 627 | session_groups = session_groups, | |
| 628 | sessions = sessions), | |
| 629 | presentation = presentation, | |
| 630 | progress = progress, | |
| 631 | check_unknown_files = Mercurial.is_repository(Path.ISABELLE_HOME), | |
| 632 | build_heap = build_heap, | |
| 633 | clean_build = clean_build, | |
| 634 | dirs = dirs, | |
| 635 | select_dirs = select_dirs, | |
| 636 | numa_shuffling = NUMA.enabled_warning(progress, numa_shuffling), | |
| 637 | max_jobs = max_jobs, | |
| 638 | list_files = list_files, | |
| 639 | check_keywords = check_keywords, | |
| 640 | fresh_build = fresh_build, | |
| 641 | no_build = no_build, | |
| 642 | soft_build = soft_build, | |
| 643 | verbose = verbose, | |
| 644 | export_files = export_files) | |
| 645 | } | |
| 646 | val end_date = Date.now() | |
| 647 | val elapsed_time = end_date.time - start_date.time | |
| 648 | ||
| 649 |       if (verbose) {
 | |
| 650 |         progress.echo("\nFinished at " + Build_Log.print_date(end_date))
 | |
| 62833 | 651 | } | 
| 62590 | 652 | |
| 75394 | 653 | val total_timing = | 
| 654 | results.sessions.iterator.map(a => results(a).timing).foldLeft(Timing.zero)(_ + _). | |
| 655 | copy(elapsed = elapsed_time) | |
| 656 | progress.echo(total_timing.message_resources) | |
| 62590 | 657 | |
| 75394 | 658 | sys.exit(results.rc) | 
| 659 | }) | |
| 68305 | 660 | |
| 661 | ||
| 662 | /* build logic image */ | |
| 663 | ||
| 664 | def build_logic(options: Options, logic: String, | |
| 71726 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71718diff
changeset | 665 | progress: Progress = new Progress, | 
| 68305 | 666 | build_heap: Boolean = false, | 
| 667 | dirs: List[Path] = Nil, | |
| 70791 | 668 | fresh: Boolean = false, | 
| 75393 | 669 | strict: Boolean = false | 
| 670 |   ): Int = {
 | |
| 71896 
ce06d6456cc8
clarified signature --- fit within limit of 22 arguments;
 wenzelm parents: 
71895diff
changeset | 671 | val selection = Sessions.Selection.session(logic) | 
| 69540 | 672 | val rc = | 
| 71981 | 673 | if (!fresh && build(options, selection = selection, | 
| 74306 | 674 | build_heap = build_heap, no_build = true, dirs = dirs).ok) Process_Result.RC.ok | 
| 69540 | 675 |       else {
 | 
| 676 |         progress.echo("Build started for Isabelle/" + logic + " ...")
 | |
| 71981 | 677 | Build.build(options, selection = selection, progress = progress, | 
| 678 | build_heap = build_heap, fresh_build = fresh, dirs = dirs).rc | |
| 69540 | 679 | } | 
| 74306 | 680 |     if (strict && rc != Process_Result.RC.ok) error("Failed to build Isabelle/" + logic) else rc
 | 
| 68305 | 681 | } | 
| 48276 | 682 | } |