| author | wenzelm | 
| Sun, 15 Jan 2023 20:38:27 +0100 | |
| changeset 76992 | 11c0747a87fc | 
| parent 76927 | da13da82f6f9 | 
| child 77177 | 76180e429491 | 
| 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], | 
| 75967 
ff164add75cd
maintain "uuid" column in session build database, to identity the original build process uniquely;
 wenzelm parents: 
75948diff
changeset | 24 | return_code: Int, | 
| 
ff164add75cd
maintain "uuid" column in session build database, to identity the original build process uniquely;
 wenzelm parents: 
75948diff
changeset | 25 | uuid: String | 
| 75393 | 26 |   ) {
 | 
| 66747 | 27 | def ok: Boolean = return_code == 0 | 
| 28 | } | |
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 29 | |
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 30 | |
| 65289 | 31 | /* queue with scheduling information */ | 
| 48676 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 32 | |
| 75393 | 33 |   private object Queue {
 | 
| 68086 
9e1c670301b8
cleanup session output before starting build job;
 wenzelm parents: 
67852diff
changeset | 34 | type Timings = (List[Properties.T], Double) | 
| 
9e1c670301b8
cleanup session output before starting build job;
 wenzelm parents: 
67852diff
changeset | 35 | |
| 75393 | 36 |     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 | 37 | val no_timings: Timings = (Nil, 0.0) | 
| 65289 | 38 | |
| 72634 
5cea0993ee4f
clarified access to single database server vs. collection of database files;
 wenzelm parents: 
72624diff
changeset | 39 |       store.try_open_database(session_name) match {
 | 
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 40 | case None => no_timings | 
| 68214 | 41 | case Some(db) => | 
| 75393 | 42 |           def ignore_error(msg: String) = {
 | 
| 75793 | 43 |             progress.echo_warning("Ignoring bad database " + db +
 | 
| 44 | " for session " + quote(session_name) + (if (msg == "") "" else ":\n" + msg)) | |
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 45 | no_timings | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 46 | } | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 47 |           try {
 | 
| 71669 | 48 | val command_timings = store.read_command_timings(db, session_name) | 
| 68214 | 49 | val session_timing = | 
| 71669 | 50 |               store.read_session_timing(db, session_name) match {
 | 
| 68214 | 51 | case Markup.Elapsed(t) => t | 
| 52 | case _ => 0.0 | |
| 53 | } | |
| 54 | (command_timings, session_timing) | |
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 55 | } | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 56 |           catch {
 | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 57 | case ERROR(msg) => ignore_error(msg) | 
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 58 | case exn: java.lang.Error => ignore_error(Exn.message(exn)) | 
| 75783 | 59 |             case _: XML.Error => ignore_error("XML.Error")
 | 
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 60 | } | 
| 73367 | 61 |           finally { db.close() }
 | 
| 65289 | 62 | } | 
| 63 | } | |
| 64 | ||
| 75393 | 65 | def make_session_timing( | 
| 66 | sessions_structure: Sessions.Structure, | |
| 67 | timing: Map[String, Double] | |
| 68 |     ) : Map[String, Double] = {
 | |
| 68731 | 69 | val maximals = sessions_structure.build_graph.maximals.toSet | 
| 75393 | 70 |       def desc_timing(session_name: String): Double = {
 | 
| 71669 | 71 | if (maximals.contains(session_name)) timing(session_name) | 
| 68487 | 72 |         else {
 | 
| 71669 | 73 | val descendants = sessions_structure.build_descendants(List(session_name)).toSet | 
| 68731 | 74 | val g = sessions_structure.build_graph.restrict(descendants) | 
| 75394 | 75 |           (0.0 :: g.maximals.flatMap { desc =>
 | 
| 68487 | 76 | val ps = g.all_preds(List(desc)) | 
| 71895 | 77 | if (ps.exists(p => !timing.isDefinedAt(p))) None | 
| 68487 | 78 | else Some(ps.map(timing(_)).sum) | 
| 75394 | 79 | }).max | 
| 68487 | 80 | } | 
| 68486 
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 | 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 | 83 | } | 
| 
6984a55f3cba
clarified queue ordering: take session descendants into account, notably for "slow" AFP sessions;
 wenzelm parents: 
68331diff
changeset | 84 | |
| 75393 | 85 | def apply( | 
| 86 | progress: Progress, | |
| 87 | sessions_structure: Sessions.Structure, | |
| 88 | store: Sessions.Store | |
| 89 |     ) : Queue = {
 | |
| 68731 | 90 | val graph = sessions_structure.build_graph | 
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 91 | val names = graph.keys | 
| 51220 | 92 | |
| 65831 | 93 | val timings = names.map(name => (name, load_timings(progress, store, name))) | 
| 51220 | 94 | val command_timings = | 
| 68486 
6984a55f3cba
clarified queue ordering: take session descendants into account, notably for "slow" AFP sessions;
 wenzelm parents: 
68331diff
changeset | 95 |         timings.map({ case (name, (ts, _)) => (name, ts) }).toMap.withDefaultValue(Nil)
 | 
| 51220 | 96 | val session_timing = | 
| 68731 | 97 | make_session_timing(sessions_structure, | 
| 98 |           timings.map({ case (name, (_, t)) => (name, t) }).toMap)
 | |
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 99 | |
| 75393 | 100 |       object Ordering extends scala.math.Ordering[String] {
 | 
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 101 | def compare(name1: String, name2: String): Int = | 
| 73365 | 102 |           session_timing(name2) compare session_timing(name1) match {
 | 
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 103 | case 0 => | 
| 68731 | 104 |               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 | 105 | case 0 => name1 compare name2 | 
| 48678 
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 | case ord => ord | 
| 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 109 | } | 
| 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 110 | } | 
| 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 111 | |
| 65415 
8cd54b18b68b
clarified signature: tree structure is not essential;
 wenzelm parents: 
65406diff
changeset | 112 | 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 | 113 | } | 
| 
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 | |
| 65314 | 116 | private class Queue( | 
| 62631 | 117 | graph: Graph[String, Sessions.Info], | 
| 51220 | 118 | order: SortedSet[String], | 
| 75393 | 119 | val command_timings: String => List[Properties.T] | 
| 120 |   ) {
 | |
| 48676 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 121 | 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 | 122 | |
| 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 123 | 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 | 124 | |
| 51227 
88c96e836ed6
prefer comparison of session timing, if this is known already;
 wenzelm parents: 
51223diff
changeset | 125 | def - (name: String): Queue = | 
| 73365 | 126 | 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 | 127 | |
| 75393 | 128 |     def dequeue(skip: String => Boolean): Option[(String, Sessions.Info)] = {
 | 
| 73365 | 129 | val it = order.iterator.dropWhile(name => skip(name) || !graph.is_minimal(name)) | 
| 73344 | 130 |       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 | 131 | else None | 
| 
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 | } | 
| 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 134 | |
| 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 135 | |
| 62631 | 136 | |
| 62641 | 137 | /** build with results **/ | 
| 48424 | 138 | |
| 76198 
fb4215da4919
clarified presentation_sessions: work with partial results;
 wenzelm parents: 
76197diff
changeset | 139 | class Results private[Build]( | 
| 76206 
769a7cd5a16a
clarified signature: re-use store/cache from build results;
 wenzelm parents: 
76202diff
changeset | 140 | val store: Sessions.Store, | 
| 76209 | 141 | val deps: Sessions.Deps, | 
| 76901 | 142 | val sessions_ok: List[String], | 
| 143 | results: Map[String, (Option[Process_Result], Sessions.Info)] | |
| 76198 
fb4215da4919
clarified presentation_sessions: work with partial results;
 wenzelm parents: 
76197diff
changeset | 144 |   ) {
 | 
| 76206 
769a7cd5a16a
clarified signature: re-use store/cache from build results;
 wenzelm parents: 
76202diff
changeset | 145 | def cache: Term.Cache = store.cache | 
| 
769a7cd5a16a
clarified signature: re-use store/cache from build results;
 wenzelm parents: 
76202diff
changeset | 146 | |
| 62403 | 147 | def sessions: Set[String] = results.keySet | 
| 63082 
6af03422535a
expose Sessions.Info in Build.Results
 Lars Hupel <lars.hupel@mytum.de> parents: 
62946diff
changeset | 148 | def cancelled(name: String): Boolean = results(name)._1.isEmpty | 
| 76197 | 149 | def info(name: String): Sessions.Info = results(name)._2 | 
| 63082 
6af03422535a
expose Sessions.Info in Build.Results
 Lars Hupel <lars.hupel@mytum.de> parents: 
62946diff
changeset | 150 | def apply(name: String): Process_Result = results(name)._1.getOrElse(Process_Result(1)) | 
| 71601 | 151 | val rc: Int = | 
| 73359 | 152 |       results.iterator.map({ case (_, (Some(r), _)) => r.rc case (_, (None, _)) => 1 }).
 | 
| 74306 | 153 | foldLeft(Process_Result.RC.ok)(_ max _) | 
| 154 | def ok: Boolean = rc == Process_Result.RC.ok | |
| 62406 | 155 | |
| 76202 | 156 | def unfinished: List[String] = sessions.iterator.filterNot(apply(_).ok).toList.sorted | 
| 76199 | 157 | |
| 62406 | 158 | override def toString: String = rc.toString | 
| 62403 | 159 | } | 
| 160 | ||
| 72021 | 161 | def session_finished(session_name: String, process_result: Process_Result): String = | 
| 162 |     "Finished " + session_name + " (" + process_result.timing.message_resources + ")"
 | |
| 72013 | 163 | |
| 75393 | 164 |   def session_timing(session_name: String, build_log: Build_Log.Session_Info): String = {
 | 
| 72021 | 165 | val props = build_log.session_timing | 
| 166 | val threads = Markup.Session_Timing.Threads.unapply(props) getOrElse 1 | |
| 74782 | 167 | val timing = Markup.Timing_Properties.get(props) | 
| 72015 | 168 |     "Timing " + session_name + " (" + threads + " threads, " + timing.message_factor + ")"
 | 
| 72021 | 169 | } | 
| 72018 | 170 | |
| 62641 | 171 | def build( | 
| 50404 
898cac1dad5e
avoid startup within GUI thread -- it is only required later for dialog;
 wenzelm parents: 
50367diff
changeset | 172 | options: Options, | 
| 71981 | 173 | selection: Sessions.Selection = Sessions.Selection.empty, | 
| 75942 
603852abed8f
clarified names: Browser_Info.Config vs. Browser_Info.Context;
 wenzelm parents: 
75941diff
changeset | 174 | browser_info: Browser_Info.Config = Browser_Info.Config.none, | 
| 71726 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71718diff
changeset | 175 | progress: Progress = new Progress, | 
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 176 | check_unknown_files: Boolean = false, | 
| 48511 
37999ee01156
remove old output heaps, to ensure that result is valid wrt. check_stamps;
 wenzelm parents: 
48509diff
changeset | 177 | build_heap: Boolean = false, | 
| 48595 | 178 | clean_build: Boolean = false, | 
| 56890 | 179 | dirs: List[Path] = Nil, | 
| 180 | select_dirs: List[Path] = Nil, | |
| 66968 
9991671c98aa
allow to augment session context via explicit session infos;
 wenzelm parents: 
66962diff
changeset | 181 | infos: List[Sessions.Info] = Nil, | 
| 64265 | 182 | numa_shuffling: Boolean = false, | 
| 48509 | 183 | max_jobs: Int = 1, | 
| 48903 | 184 | list_files: Boolean = false, | 
| 59891 
9ce697050455
added isabelle build option -k, for fast off-line checking of theory sources;
 wenzelm parents: 
59811diff
changeset | 185 | check_keywords: Set[String] = Set.empty, | 
| 66841 | 186 | fresh_build: Boolean = false, | 
| 48509 | 187 | no_build: Boolean = false, | 
| 66745 | 188 | soft_build: Boolean = false, | 
| 48509 | 189 | verbose: Boolean = false, | 
| 73805 
b73777a0c076
allow build session setup, e.g. for protocol handlers;
 wenzelm parents: 
73804diff
changeset | 190 | export_files: Boolean = false, | 
| 76927 
da13da82f6f9
treat update_options as part of Sessions.Info meta_digest, for proper re-build of updated sessions;
 wenzelm parents: 
76919diff
changeset | 191 | augment_options: String => List[Options.Spec] = _ => Nil, | 
| 75393 | 192 | session_setup: (String, Session) => Unit = (_, _) => () | 
| 193 |   ): Results = {
 | |
| 71677 | 194 | val build_options = | 
| 71679 
eeaa4021f080
proper "editor_tracing_messages=0" as in "isabelle dump";
 wenzelm parents: 
71677diff
changeset | 195 | options + | 
| 
eeaa4021f080
proper "editor_tracing_messages=0" as in "isabelle dump";
 wenzelm parents: 
71677diff
changeset | 196 | "completion_limit=0" + | 
| 
eeaa4021f080
proper "editor_tracing_messages=0" as in "isabelle dump";
 wenzelm parents: 
71677diff
changeset | 197 | "editor_tracing_messages=0" + | 
| 72728 
caa182bdab7a
clarified options: batch-build has pide_reports disabled by default (requires significant resources);
 wenzelm parents: 
72693diff
changeset | 198 |         ("pide_reports=" + options.bool("build_pide_reports"))
 | 
| 51220 | 199 | |
| 69854 
cc0b3e177b49
system option "system_heaps" supersedes various command-line options for "system build mode";
 wenzelm parents: 
69811diff
changeset | 200 | val store = Sessions.store(build_options) | 
| 66745 | 201 | |
| 69369 
6ecc85955e04
prefer "Isabelle DejaVu Sans", even for headless batch-build (session_graph.pdf);
 wenzelm parents: 
68957diff
changeset | 202 | Isabelle_Fonts.init() | 
| 
6ecc85955e04
prefer "Isabelle DejaVu Sans", even for headless batch-build (session_graph.pdf);
 wenzelm parents: 
68957diff
changeset | 203 | |
| 66745 | 204 | |
| 205 | /* session selection and dependencies */ | |
| 65422 | 206 | |
| 66961 | 207 | val full_sessions = | 
| 76927 
da13da82f6f9
treat update_options as part of Sessions.Info meta_digest, for proper re-build of updated sessions;
 wenzelm parents: 
76919diff
changeset | 208 | Sessions.load_structure(build_options, dirs = dirs, select_dirs = select_dirs, infos = infos, | 
| 
da13da82f6f9
treat update_options as part of Sessions.Info meta_digest, for proper re-build of updated sessions;
 wenzelm parents: 
76919diff
changeset | 209 | augment_options = augment_options) | 
| 73012 
238ddf525da4
clarified HTML presentation, e.g. avoid bulky jobs like HOL or HOL-Analysis in applications;
 wenzelm parents: 
72993diff
changeset | 210 | val full_sessions_selection = full_sessions.imports_selection(selection) | 
| 
238ddf525da4
clarified HTML presentation, e.g. avoid bulky jobs like HOL or HOL-Analysis in applications;
 wenzelm parents: 
72993diff
changeset | 211 | |
| 75393 | 212 |     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 | 213 | val digests = | 
| 71669 | 214 | full_sessions(session_name).meta_digest :: | 
| 75741 | 215 | deps.session_sources(session_name) ::: | 
| 71669 | 216 | deps.imported_sources(session_name) | 
| 72654 | 217 | 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 | 218 | } | 
| 66745 | 219 | |
| 75948 | 220 |     val build_deps = {
 | 
| 66745 | 221 | val deps0 = | 
| 71896 
ce06d6456cc8
clarified signature --- fit within limit of 22 arguments;
 wenzelm parents: 
71895diff
changeset | 222 | Sessions.deps(full_sessions.selection(selection), | 
| 66962 
e1bde71bace6
clarified signature: global_theories is always required;
 wenzelm parents: 
66961diff
changeset | 223 | progress = progress, inlined_files = true, verbose = verbose, | 
| 
e1bde71bace6
clarified signature: global_theories is always required;
 wenzelm parents: 
66961diff
changeset | 224 | list_files = list_files, check_keywords = check_keywords).check_errors | 
| 66745 | 225 | |
| 66841 | 226 |       if (soft_build && !fresh_build) {
 | 
| 66745 | 227 | val outdated = | 
| 68204 | 228 | 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 | 229 |             store.try_open_database(name) match {
 | 
| 68214 | 230 | case Some(db) => | 
| 68216 | 231 |                 using(db)(store.read_build(_, name)) match {
 | 
| 66745 | 232 | case Some(build) | 
| 66747 | 233 | if build.ok && build.sources == sources_stamp(deps0, name) => None | 
| 66745 | 234 | case _ => Some(name) | 
| 235 | } | |
| 236 | case None => Some(name) | |
| 237 | }) | |
| 68204 | 238 | |
| 239 | 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 | 240 | progress = progress, inlined_files = true).check_errors | 
| 66745 | 241 | } | 
| 68204 | 242 | else deps0 | 
| 66745 | 243 | } | 
| 244 | ||
| 245 | ||
| 246 | /* check unknown files */ | |
| 48504 
21dfd6c04482
actually check source vs. target stamps, based on information from log files;
 wenzelm parents: 
48494diff
changeset | 247 | |
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 248 |     if (check_unknown_files) {
 | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 249 | val source_files = | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 250 |         (for {
 | 
| 75948 | 251 | (_, base) <- build_deps.session_bases.iterator | 
| 75741 | 252 | (path, _) <- base.session_sources.iterator | 
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 253 | } yield path).toList | 
| 76670 
b04d45bebbc5
discontinued somewhat pointless dependency: avoid illusion of extra accuracy (see also 09fb749d1a1e and 0f750a6dc754);
 wenzelm parents: 
76669diff
changeset | 254 |       Mercurial.check_files(source_files)._2 match {
 | 
| 
b04d45bebbc5
discontinued somewhat pointless dependency: avoid illusion of extra accuracy (see also 09fb749d1a1e and 0f750a6dc754);
 wenzelm parents: 
76669diff
changeset | 255 | case Nil => | 
| 
b04d45bebbc5
discontinued somewhat pointless dependency: avoid illusion of extra accuracy (see also 09fb749d1a1e and 0f750a6dc754);
 wenzelm parents: 
76669diff
changeset | 256 | case unknown_files => | 
| 
b04d45bebbc5
discontinued somewhat pointless dependency: avoid illusion of extra accuracy (see also 09fb749d1a1e and 0f750a6dc754);
 wenzelm parents: 
76669diff
changeset | 257 |           progress.echo_warning("Unknown files (not part of the underlying Mercurial repository):" +
 | 
| 76883 | 258 |             unknown_files.map(File.standard_path).sorted.mkString("\n  ", "\n  ", ""))
 | 
| 65832 
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 | ||
| 76669 | 265 | val queue = Queue(progress, build_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 = | 
| 76476 | 294 |       Isabelle_Thread.interrupt_handler(_ => progress.stop()) {
 | 
| 295 |         build_options.seconds("editor_input_delay").sleep()
 | |
| 296 | } | |
| 50366 | 297 | |
| 73777 
52e43a93d51f
clarified system_log: make this work independently of the particular "isabelle build" command-line (e.g. "isabelle mirabelle");
 wenzelm parents: 
73774diff
changeset | 298 | 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 | 299 |       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 | 300 | 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 | 301 | 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 | 302 | 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 | 303 | } | 
| 
52e43a93d51f
clarified system_log: make this work independently of the particular "isabelle build" command-line (e.g. "isabelle mirabelle");
 wenzelm parents: 
73774diff
changeset | 304 | |
| 64265 | 305 | val numa_nodes = new NUMA.Nodes(numa_shuffling) | 
| 306 | ||
| 48425 | 307 | @tailrec def loop( | 
| 48676 
3ef82491cdd6
clarified Session_Tree (with proper integrity check) vs. Queue (with provision for alternative ordering);
 wenzelm parents: 
48675diff
changeset | 308 | pending: Queue, | 
| 72662 | 309 | running: Map[String, (List[String], Build_Job)], | 
| 75393 | 310 | results: Map[String, Result] | 
| 311 |     ): Map[String, Result] = {
 | |
| 64265 | 312 | def used_node(i: Int): Boolean = | 
| 313 | running.iterator.exists( | |
| 314 |           { case (_, (_, job)) => job.numa_node.isDefined && job.numa_node.get == i })
 | |
| 315 | ||
| 48425 | 316 | if (pending.is_empty) results | 
| 51253 | 317 |       else {
 | 
| 71726 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71718diff
changeset | 318 |         if (progress.stopped) {
 | 
| 73367 | 319 | for ((_, (_, job)) <- running) job.terminate() | 
| 71726 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71718diff
changeset | 320 | } | 
| 51253 | 321 | |
| 48674 | 322 |         running.find({ case (_, (_, job)) => job.is_finished }) match {
 | 
| 71669 | 323 | case Some((session_name, (input_heaps, job))) => | 
| 50367 | 324 |             //{{{ finish job
 | 
| 48424 | 325 | |
| 68217 | 326 | val (process_result, heap_digest) = job.join | 
| 48373 | 327 | |
| 71623 
b3bddebe44ca
clarified signature: more explicit type Protocol_Message.Marker;
 wenzelm parents: 
71614diff
changeset | 328 | val log_lines = process_result.out_lines.filterNot(Protocol_Message.Marker.test) | 
| 75393 | 329 |             val process_result_tail = {
 | 
| 62409 
e391528eff3b
proper option process_output_tail, more generous default;
 wenzelm parents: 
62406diff
changeset | 330 |               val tail = job.info.options.int("process_output_tail")
 | 
| 62632 | 331 | process_result.copy( | 
| 332 | out_lines = | |
| 71669 | 333 | "(see also " + store.output_log(session_name).file.toString + ")" :: | 
| 65294 | 334 | (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 | 335 | } | 
| 
13a0f537e232
retain tail out_lines as printed, but not the whole log content;
 wenzelm parents: 
62403diff
changeset | 336 | |
| 72017 | 337 | val build_log = | 
| 338 | Build_Log.Log_File(session_name, process_result.out_lines). | |
| 339 | parse_session_info( | |
| 340 | command_timings = true, | |
| 341 | theory_timings = true, | |
| 342 | ml_statistics = true, | |
| 343 | task_statistics = true) | |
| 66666 
1a620647285c
clarified messages: after writing all files (see also 27f90319a499 and 57c85c83c11b);
 wenzelm parents: 
66595diff
changeset | 344 | |
| 
1a620647285c
clarified messages: after writing all files (see also 27f90319a499 and 57c85c83c11b);
 wenzelm parents: 
66595diff
changeset | 345 | // write log file | 
| 68217 | 346 |             if (process_result.ok) {
 | 
| 71669 | 347 | File.write_gzip(store.output_log_gz(session_name), terminate_lines(log_lines)) | 
| 68217 | 348 | } | 
| 71669 | 349 | 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 | 350 | |
| 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 351 | // write database | 
| 72017 | 352 | using(store.open_database(session_name, output = true))(db => | 
| 76871 
a17f9ff37558
clarified session_sources (again, see also 9d0e6ea7aa68);
 wenzelm parents: 
76866diff
changeset | 353 | store.write_session_info(db, session_name, job.session_sources, | 
| 72017 | 354 | build_log = | 
| 355 |                   if (process_result.timeout) build_log.error("Timeout") else build_log,
 | |
| 356 | build = | |
| 75948 | 357 | Session_Info(sources_stamp(build_deps, session_name), input_heaps, heap_digest, | 
| 75967 
ff164add75cd
maintain "uuid" column in session build database, to identity the original build process uniquely;
 wenzelm parents: 
75948diff
changeset | 358 | process_result.rc, UUID.random().toString))) | 
| 65291 
57c85c83c11b
maintain persistent session info in SQLite database instead of log file;
 wenzelm parents: 
65289diff
changeset | 359 | |
| 66666 
1a620647285c
clarified messages: after writing all files (see also 27f90319a499 and 57c85c83c11b);
 wenzelm parents: 
66595diff
changeset | 360 | // messages | 
| 71601 | 361 | process_result.err_lines.foreach(progress.echo) | 
| 66666 
1a620647285c
clarified messages: after writing all files (see also 27f90319a499 and 57c85c83c11b);
 wenzelm parents: 
66595diff
changeset | 362 | |
| 72013 | 363 |             if (process_result.ok) {
 | 
| 72022 
45865bb06182
clarified message --- as in former ML version (see 940195fbb282);
 wenzelm parents: 
72021diff
changeset | 364 | if (verbose) progress.echo(session_timing(session_name, build_log)) | 
| 72021 | 365 | progress.echo(session_finished(session_name, process_result)) | 
| 72013 | 366 | } | 
| 66666 
1a620647285c
clarified messages: after writing all files (see also 27f90319a499 and 57c85c83c11b);
 wenzelm parents: 
66595diff
changeset | 367 |             else {
 | 
| 71669 | 368 | progress.echo(session_name + " FAILED") | 
| 66666 
1a620647285c
clarified messages: after writing all files (see also 27f90319a499 and 57c85c83c11b);
 wenzelm parents: 
66595diff
changeset | 369 | 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 | 370 | } | 
| 
1a620647285c
clarified messages: after writing all files (see also 27f90319a499 and 57c85c83c11b);
 wenzelm parents: 
66595diff
changeset | 371 | |
| 71669 | 372 | loop(pending - session_name, running - session_name, | 
| 373 | results + | |
| 374 | (session_name -> Result(false, heap_digest, Some(process_result_tail), job.info))) | |
| 50367 | 375 | //}}} | 
| 60215 | 376 | case None if running.size < (max_jobs max 1) => | 
| 50367 | 377 |             //{{{ check/start next job
 | 
| 71601 | 378 |             pending.dequeue(running.isDefinedAt) match {
 | 
| 71669 | 379 | case Some((session_name, info)) => | 
| 66848 | 380 | val ancestor_results = | 
| 76669 | 381 | build_deps.sessions_structure.build_requirements(List(session_name)). | 
| 71669 | 382 | filterNot(_ == session_name).map(results(_)) | 
| 68213 | 383 | val ancestor_heaps = ancestor_results.flatMap(_.heap_digest) | 
| 62628 | 384 | |
| 71669 | 385 | val do_store = | 
| 386 | build_heap || Sessions.is_pure(session_name) || queue.is_inner(session_name) | |
| 48547 | 387 | |
| 75393 | 388 |                 val (current, heap_digest) = {
 | 
| 72634 
5cea0993ee4f
clarified access to single database server vs. collection of database files;
 wenzelm parents: 
72624diff
changeset | 389 |                   store.try_open_database(session_name) match {
 | 
| 68212 
5a59fded83c7
clarified heap vs. database operations: discontinued correlation of directory;
 wenzelm parents: 
68209diff
changeset | 390 | case Some(db) => | 
| 71669 | 391 |                       using(db)(store.read_build(_, session_name)) match {
 | 
| 68216 | 392 | case Some(build) => | 
| 71669 | 393 | val heap_digest = store.find_heap_digest(session_name) | 
| 68216 | 394 | val current = | 
| 395 | !fresh_build && | |
| 396 | build.ok && | |
| 75948 | 397 | build.sources == sources_stamp(build_deps, session_name) && | 
| 68216 | 398 | build.input_heaps == ancestor_heaps && | 
| 399 | build.output_heap == heap_digest && | |
| 71614 | 400 | !(do_store && heap_digest.isEmpty) | 
| 68216 | 401 | (current, heap_digest) | 
| 402 | case None => (false, None) | |
| 403 | } | |
| 62636 | 404 | case None => (false, None) | 
| 48504 
21dfd6c04482
actually check source vs. target stamps, based on information from log files;
 wenzelm parents: 
48494diff
changeset | 405 | } | 
| 48547 | 406 | } | 
| 62628 | 407 | val all_current = current && ancestor_results.forall(_.current) | 
| 48528 
784c6f63d79c
proper all_current, which regards parent status as well;
 wenzelm parents: 
48511diff
changeset | 408 | |
| 76201 
9d1819c28f67
clarified conditions: no_build is ok for presentation if "all_current" holds;
 wenzelm parents: 
76200diff
changeset | 409 |                 if (all_current) {
 | 
| 71669 | 410 | loop(pending - session_name, running, | 
| 411 | results + | |
| 412 | (session_name -> Result(true, heap_digest, Some(Process_Result(0)), info))) | |
| 76201 
9d1819c28f67
clarified conditions: no_build is ok for presentation if "all_current" holds;
 wenzelm parents: 
76200diff
changeset | 413 | } | 
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 414 |                 else if (no_build) {
 | 
| 71894 | 415 | progress.echo_if(verbose, "Skipping " + session_name + " ...") | 
| 71669 | 416 | loop(pending - session_name, running, | 
| 417 | results + | |
| 418 | (session_name -> Result(false, heap_digest, Some(Process_Result(1)), info))) | |
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 419 | } | 
| 62628 | 420 |                 else if (ancestor_results.forall(_.ok) && !progress.stopped) {
 | 
| 71669 | 421 | progress.echo((if (do_store) "Building " else "Running ") + session_name + " ...") | 
| 68086 
9e1c670301b8
cleanup session output before starting build job;
 wenzelm parents: 
67852diff
changeset | 422 | |
| 71669 | 423 | store.clean_output(session_name) | 
| 76871 
a17f9ff37558
clarified session_sources (again, see also 9d0e6ea7aa68);
 wenzelm parents: 
76866diff
changeset | 424 | using(store.open_database(session_name, output = true))( | 
| 
a17f9ff37558
clarified session_sources (again, see also 9d0e6ea7aa68);
 wenzelm parents: 
76866diff
changeset | 425 | store.init_session_info(_, session_name)) | 
| 68086 
9e1c670301b8
cleanup session output before starting build job;
 wenzelm parents: 
67852diff
changeset | 426 | |
| 71601 | 427 | val numa_node = numa_nodes.next(used_node) | 
| 51220 | 428 | val job = | 
| 76871 
a17f9ff37558
clarified session_sources (again, see also 9d0e6ea7aa68);
 wenzelm parents: 
76866diff
changeset | 429 | new Build_Job(progress, build_deps.background(session_name), store, do_store, | 
| 73805 
b73777a0c076
allow build session setup, e.g. for protocol handlers;
 wenzelm parents: 
73804diff
changeset | 430 | log, session_setup, numa_node, queue.command_timings(session_name)) | 
| 71669 | 431 | loop(pending, running + (session_name -> (ancestor_heaps, job)), results) | 
| 48547 | 432 | } | 
| 433 |                 else {
 | |
| 71669 | 434 | progress.echo(session_name + " CANCELLED") | 
| 435 | loop(pending - session_name, running, | |
| 436 | results + (session_name -> Result(false, heap_digest, None, info))) | |
| 48547 | 437 | } | 
| 438 | case None => sleep(); loop(pending, running, results) | |
| 48425 | 439 | } | 
| 50367 | 440 | ///}}} | 
| 48425 | 441 | case None => sleep(); loop(pending, running, results) | 
| 48373 | 442 | } | 
| 51253 | 443 | } | 
| 48425 | 444 | } | 
| 445 | ||
| 51220 | 446 | |
| 447 | /* build results */ | |
| 448 | ||
| 75393 | 449 |     val results = {
 | 
| 76200 | 450 | val build_results = | 
| 75948 | 451 |         if (build_deps.is_empty) {
 | 
| 72673 
8ff7a0e394f9
clarified PDF/HTML presentation, based on pdf blobs from session database (e.g. from earlier builds);
 wenzelm parents: 
72662diff
changeset | 452 |           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 | 453 | 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 | 454 | } | 
| 
8ff7a0e394f9
clarified PDF/HTML presentation, based on pdf blobs from session database (e.g. from earlier builds);
 wenzelm parents: 
72662diff
changeset | 455 |         else Isabelle_Thread.uninterruptible { loop(queue, Map.empty, Map.empty) }
 | 
| 48583 | 456 | |
| 76901 | 457 | val sessions_ok: List[String] = | 
| 458 |         (for {
 | |
| 459 | name <- build_deps.sessions_structure.build_topological_order.iterator | |
| 460 | result <- build_results.get(name) | |
| 461 | if result.ok | |
| 462 | } yield name).toList | |
| 463 | ||
| 76200 | 464 | val results = | 
| 465 | (for ((name, result) <- build_results.iterator) | |
| 76196 | 466 | yield (name, (result.process, result.info))).toMap | 
| 467 | ||
| 76901 | 468 | new Results(store, build_deps, sessions_ok, results) | 
| 72673 
8ff7a0e394f9
clarified PDF/HTML presentation, based on pdf blobs from session database (e.g. from earlier builds);
 wenzelm parents: 
72662diff
changeset | 469 | } | 
| 62641 | 470 | |
| 69811 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 471 |     if (export_files) {
 | 
| 76196 | 472 |       for (name <- full_sessions_selection.iterator if results(name).ok) {
 | 
| 69811 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 473 | val info = results.info(name) | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 474 |         if (info.export_files.nonEmpty) {
 | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 475 |           progress.echo("Exporting " + info.name + " ...")
 | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 476 |           for ((dir, prune, pats) <- info.export_files) {
 | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 477 | Export.export_files(store, name, info.dir + dir, | 
| 71726 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71718diff
changeset | 478 | progress = if (verbose) progress else new Progress, | 
| 69811 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 479 | export_prune = prune, | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 480 | export_patterns = pats) | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 481 | } | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 482 | } | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 483 | } | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 484 | } | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 485 | |
| 76901 | 486 | val presentation_sessions = | 
| 487 | results.sessions_ok.filter(name => browser_info.enabled(results.info(name))) | |
| 76230 | 488 |     if (presentation_sessions.nonEmpty && !progress.stopped) {
 | 
| 489 | Browser_Info.build(browser_info, results.store, results.deps, presentation_sessions, | |
| 76198 
fb4215da4919
clarified presentation_sessions: work with partial results;
 wenzelm parents: 
76197diff
changeset | 490 | progress = progress, verbose = verbose) | 
| 
fb4215da4919
clarified presentation_sessions: work with partial results;
 wenzelm parents: 
76197diff
changeset | 491 | } | 
| 
fb4215da4919
clarified presentation_sessions: work with partial results;
 wenzelm parents: 
76197diff
changeset | 492 | |
| 76196 | 493 |     if (!results.ok && (verbose || !no_build)) {
 | 
| 76199 | 494 |       progress.echo("Unfinished session(s): " + commas(results.unfinished))
 | 
| 62641 | 495 | } | 
| 496 | ||
| 497 | results | |
| 48341 | 498 | } | 
| 499 | ||
| 500 | ||
| 62833 | 501 | /* Isabelle tool wrapper */ | 
| 48341 | 502 | |
| 72763 | 503 |   val isabelle_tool = Isabelle_Tool("build", "build and manage Isabelle sessions",
 | 
| 75394 | 504 | Scala_Project.here, | 
| 505 |     { args =>
 | |
| 506 |       val build_options = Word.explode(Isabelle_System.getenv("ISABELLE_BUILD_OPTIONS"))
 | |
| 62590 | 507 | |
| 75394 | 508 | var base_sessions: List[String] = Nil | 
| 509 | var select_dirs: List[Path] = Nil | |
| 510 | var numa_shuffling = false | |
| 75942 
603852abed8f
clarified names: Browser_Info.Config vs. Browser_Info.Context;
 wenzelm parents: 
75941diff
changeset | 511 | var browser_info = Browser_Info.Config.none | 
| 75394 | 512 | var requirements = false | 
| 513 | var soft_build = false | |
| 514 | var exclude_session_groups: List[String] = Nil | |
| 515 | var all_sessions = false | |
| 516 | var build_heap = false | |
| 517 | var clean_build = false | |
| 518 | var dirs: List[Path] = Nil | |
| 519 | var export_files = false | |
| 520 | var fresh_build = false | |
| 521 | var session_groups: List[String] = Nil | |
| 522 | var max_jobs = 1 | |
| 523 | var check_keywords: Set[String] = Set.empty | |
| 524 | var list_files = false | |
| 525 | var no_build = false | |
| 526 | var options = Options.init(opts = build_options) | |
| 527 | var verbose = false | |
| 528 | var exclude_sessions: List[String] = Nil | |
| 62590 | 529 | |
| 75394 | 530 |       val getopts = Getopts("""
 | 
| 62590 | 531 | Usage: isabelle build [OPTIONS] [SESSIONS ...] | 
| 532 | ||
| 533 | Options are: | |
| 66737 
2edc0c42c883
option -B for "isabelle build" and "isabelle imports";
 wenzelm parents: 
66736diff
changeset | 534 | -B NAME include session NAME and all descendants | 
| 62590 | 535 | -D DIR include session directory and select its sessions | 
| 64265 | 536 | -N cyclic shuffling of NUMA CPU nodes (performance tuning) | 
| 72648 | 537 |     -P DIR       enable HTML/PDF presentation in directory (":" for default)
 | 
| 71807 | 538 | -R refer to requirements of selected sessions | 
| 66745 | 539 | -S soft build: only observe changes of sources, not heap images | 
| 62590 | 540 | -X NAME exclude sessions from group NAME and all descendants | 
| 541 | -a select all sessions | |
| 542 | -b build heap images | |
| 543 | -c clean build | |
| 544 | -d DIR include session directory | |
| 69811 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 545 | -e export files from session specification into file-system | 
| 66841 | 546 | -f fresh build | 
| 62590 | 547 | -g NAME select session group NAME | 
| 548 | -j INT maximum number of parallel jobs (default 1) | |
| 549 | -k KEYWORD check theory sources for conflicts with proposed keywords | |
| 550 | -l list session source files | |
| 76919 | 551 | -n no build -- take existing build databases | 
| 62590 | 552 | -o OPTION override Isabelle system OPTION (via NAME=VAL or NAME) | 
| 553 | -v verbose | |
| 554 | -x NAME exclude session NAME and all descendants | |
| 555 | ||
| 62596 | 556 | Build and manage Isabelle sessions, depending on implicit settings: | 
| 557 | ||
| 73736 | 558 | """ + Library.indent_lines(2, Build_Log.Settings.show()) + "\n", | 
| 75394 | 559 | "B:" -> (arg => base_sessions = base_sessions ::: List(arg)), | 
| 560 | "D:" -> (arg => select_dirs = select_dirs ::: List(Path.explode(arg))), | |
| 561 | "N" -> (_ => numa_shuffling = true), | |
| 75942 
603852abed8f
clarified names: Browser_Info.Config vs. Browser_Info.Context;
 wenzelm parents: 
75941diff
changeset | 562 | "P:" -> (arg => browser_info = Browser_Info.Config.make(arg)), | 
| 75394 | 563 | "R" -> (_ => requirements = true), | 
| 564 | "S" -> (_ => soft_build = true), | |
| 565 | "X:" -> (arg => exclude_session_groups = exclude_session_groups ::: List(arg)), | |
| 566 | "a" -> (_ => all_sessions = true), | |
| 567 | "b" -> (_ => build_heap = true), | |
| 568 | "c" -> (_ => clean_build = true), | |
| 569 | "d:" -> (arg => dirs = dirs ::: List(Path.explode(arg))), | |
| 570 | "e" -> (_ => export_files = true), | |
| 571 | "f" -> (_ => fresh_build = true), | |
| 572 | "g:" -> (arg => session_groups = session_groups ::: List(arg)), | |
| 573 | "j:" -> (arg => max_jobs = Value.Int.parse(arg)), | |
| 574 | "k:" -> (arg => check_keywords = check_keywords + arg), | |
| 575 | "l" -> (_ => list_files = true), | |
| 576 | "n" -> (_ => no_build = true), | |
| 577 | "o:" -> (arg => options = options + arg), | |
| 578 | "v" -> (_ => verbose = true), | |
| 579 | "x:" -> (arg => exclude_sessions = exclude_sessions ::: List(arg))) | |
| 62590 | 580 | |
| 75394 | 581 | val sessions = getopts(args) | 
| 62590 | 582 | |
| 75394 | 583 | val progress = new Console_Progress(verbose = verbose) | 
| 62590 | 584 | |
| 75394 | 585 | val start_date = Date.now() | 
| 64140 | 586 | |
| 75394 | 587 |       if (verbose) {
 | 
| 588 | progress.echo( | |
| 589 | "Started at " + Build_Log.print_date(start_date) + | |
| 590 |             " (" + Isabelle_System.getenv("ML_IDENTIFIER") + " on " + Isabelle_System.hostname() +")")
 | |
| 591 | progress.echo(Build_Log.Settings.show() + "\n") | |
| 592 | } | |
| 62590 | 593 | |
| 75394 | 594 | val results = | 
| 595 |         progress.interrupt_handler {
 | |
| 596 | build(options, | |
| 597 | selection = Sessions.Selection( | |
| 598 | requirements = requirements, | |
| 599 | all_sessions = all_sessions, | |
| 600 | base_sessions = base_sessions, | |
| 601 | exclude_session_groups = exclude_session_groups, | |
| 602 | exclude_sessions = exclude_sessions, | |
| 603 | session_groups = session_groups, | |
| 604 | sessions = sessions), | |
| 75942 
603852abed8f
clarified names: Browser_Info.Config vs. Browser_Info.Context;
 wenzelm parents: 
75941diff
changeset | 605 | browser_info = browser_info, | 
| 75394 | 606 | progress = progress, | 
| 607 | check_unknown_files = Mercurial.is_repository(Path.ISABELLE_HOME), | |
| 608 | build_heap = build_heap, | |
| 609 | clean_build = clean_build, | |
| 610 | dirs = dirs, | |
| 611 | select_dirs = select_dirs, | |
| 612 | numa_shuffling = NUMA.enabled_warning(progress, numa_shuffling), | |
| 613 | max_jobs = max_jobs, | |
| 614 | list_files = list_files, | |
| 615 | check_keywords = check_keywords, | |
| 616 | fresh_build = fresh_build, | |
| 617 | no_build = no_build, | |
| 618 | soft_build = soft_build, | |
| 619 | verbose = verbose, | |
| 620 | export_files = export_files) | |
| 621 | } | |
| 622 | val end_date = Date.now() | |
| 623 | val elapsed_time = end_date.time - start_date.time | |
| 624 | ||
| 625 |       if (verbose) {
 | |
| 626 |         progress.echo("\nFinished at " + Build_Log.print_date(end_date))
 | |
| 62833 | 627 | } | 
| 62590 | 628 | |
| 75394 | 629 | val total_timing = | 
| 630 | results.sessions.iterator.map(a => results(a).timing).foldLeft(Timing.zero)(_ + _). | |
| 631 | copy(elapsed = elapsed_time) | |
| 632 | progress.echo(total_timing.message_resources) | |
| 62590 | 633 | |
| 75394 | 634 | sys.exit(results.rc) | 
| 635 | }) | |
| 68305 | 636 | |
| 637 | ||
| 638 | /* build logic image */ | |
| 639 | ||
| 640 | def build_logic(options: Options, logic: String, | |
| 71726 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71718diff
changeset | 641 | progress: Progress = new Progress, | 
| 68305 | 642 | build_heap: Boolean = false, | 
| 643 | dirs: List[Path] = Nil, | |
| 70791 | 644 | fresh: Boolean = false, | 
| 75393 | 645 | strict: Boolean = false | 
| 646 |   ): Int = {
 | |
| 71896 
ce06d6456cc8
clarified signature --- fit within limit of 22 arguments;
 wenzelm parents: 
71895diff
changeset | 647 | val selection = Sessions.Selection.session(logic) | 
| 69540 | 648 | val rc = | 
| 71981 | 649 | if (!fresh && build(options, selection = selection, | 
| 74306 | 650 | build_heap = build_heap, no_build = true, dirs = dirs).ok) Process_Result.RC.ok | 
| 69540 | 651 |       else {
 | 
| 652 |         progress.echo("Build started for Isabelle/" + logic + " ...")
 | |
| 71981 | 653 | Build.build(options, selection = selection, progress = progress, | 
| 654 | build_heap = build_heap, fresh_build = fresh, dirs = dirs).rc | |
| 69540 | 655 | } | 
| 74306 | 656 |     if (strict && rc != Process_Result.RC.ok) error("Failed to build Isabelle/" + logic) else rc
 | 
| 68305 | 657 | } | 
| 48276 | 658 | } |