| author | wenzelm | 
| Fri, 23 Dec 2022 22:51:47 +0100 | |
| changeset 76767 | 540cd80c5af2 | 
| parent 76670 | b04d45bebbc5 | 
| child 76855 | 5efc770dd727 | 
| 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, | 
| 76198 
fb4215da4919
clarified presentation_sessions: work with partial results;
 wenzelm parents: 
76197diff
changeset | 142 | results: Map[String, (Option[Process_Result], Sessions.Info)], | 
| 76230 | 143 | val presentation_sessions: Browser_Info.Config => List[String] | 
| 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, | 
| 75393 | 191 | session_setup: (String, Session) => Unit = (_, _) => () | 
| 192 |   ): Results = {
 | |
| 71677 | 193 | val build_options = | 
| 71679 
eeaa4021f080
proper "editor_tracing_messages=0" as in "isabelle dump";
 wenzelm parents: 
71677diff
changeset | 194 | options + | 
| 
eeaa4021f080
proper "editor_tracing_messages=0" as in "isabelle dump";
 wenzelm parents: 
71677diff
changeset | 195 | "completion_limit=0" + | 
| 
eeaa4021f080
proper "editor_tracing_messages=0" as in "isabelle dump";
 wenzelm parents: 
71677diff
changeset | 196 | "editor_tracing_messages=0" + | 
| 72728 
caa182bdab7a
clarified options: batch-build has pide_reports disabled by default (requires significant resources);
 wenzelm parents: 
72693diff
changeset | 197 |         ("pide_reports=" + options.bool("build_pide_reports"))
 | 
| 51220 | 198 | |
| 69854 
cc0b3e177b49
system option "system_heaps" supersedes various command-line options for "system build mode";
 wenzelm parents: 
69811diff
changeset | 199 | val store = Sessions.store(build_options) | 
| 66745 | 200 | |
| 69369 
6ecc85955e04
prefer "Isabelle DejaVu Sans", even for headless batch-build (session_graph.pdf);
 wenzelm parents: 
68957diff
changeset | 201 | Isabelle_Fonts.init() | 
| 
6ecc85955e04
prefer "Isabelle DejaVu Sans", even for headless batch-build (session_graph.pdf);
 wenzelm parents: 
68957diff
changeset | 202 | |
| 66745 | 203 | |
| 204 | /* session selection and dependencies */ | |
| 65422 | 205 | |
| 66961 | 206 | val full_sessions = | 
| 67026 | 207 | Sessions.load_structure(build_options, dirs = dirs, select_dirs = select_dirs, infos = infos) | 
| 73012 
238ddf525da4
clarified HTML presentation, e.g. avoid bulky jobs like HOL or HOL-Analysis in applications;
 wenzelm parents: 
72993diff
changeset | 208 | 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 | 209 | |
| 75393 | 210 |     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 | 211 | val digests = | 
| 71669 | 212 | full_sessions(session_name).meta_digest :: | 
| 75741 | 213 | deps.session_sources(session_name) ::: | 
| 71669 | 214 | deps.imported_sources(session_name) | 
| 72654 | 215 | 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 | 216 | } | 
| 66745 | 217 | |
| 75948 | 218 |     val build_deps = {
 | 
| 66745 | 219 | val deps0 = | 
| 71896 
ce06d6456cc8
clarified signature --- fit within limit of 22 arguments;
 wenzelm parents: 
71895diff
changeset | 220 | Sessions.deps(full_sessions.selection(selection), | 
| 66962 
e1bde71bace6
clarified signature: global_theories is always required;
 wenzelm parents: 
66961diff
changeset | 221 | progress = progress, inlined_files = true, verbose = verbose, | 
| 
e1bde71bace6
clarified signature: global_theories is always required;
 wenzelm parents: 
66961diff
changeset | 222 | list_files = list_files, check_keywords = check_keywords).check_errors | 
| 66745 | 223 | |
| 66841 | 224 |       if (soft_build && !fresh_build) {
 | 
| 66745 | 225 | val outdated = | 
| 68204 | 226 | 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 | 227 |             store.try_open_database(name) match {
 | 
| 68214 | 228 | case Some(db) => | 
| 68216 | 229 |                 using(db)(store.read_build(_, name)) match {
 | 
| 66745 | 230 | case Some(build) | 
| 66747 | 231 | if build.ok && build.sources == sources_stamp(deps0, name) => None | 
| 66745 | 232 | case _ => Some(name) | 
| 233 | } | |
| 234 | case None => Some(name) | |
| 235 | }) | |
| 68204 | 236 | |
| 237 | 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 | 238 | progress = progress, inlined_files = true).check_errors | 
| 66745 | 239 | } | 
| 68204 | 240 | else deps0 | 
| 66745 | 241 | } | 
| 242 | ||
| 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 {
 | 
| 75948 | 249 | (_, base) <- build_deps.session_bases.iterator | 
| 75741 | 250 | (path, _) <- base.session_sources.iterator | 
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 251 | } yield path).toList | 
| 76670 
b04d45bebbc5
discontinued somewhat pointless dependency: avoid illusion of extra accuracy (see also 09fb749d1a1e and 0f750a6dc754);
 wenzelm parents: 
76669diff
changeset | 252 |       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 | 253 | case Nil => | 
| 
b04d45bebbc5
discontinued somewhat pointless dependency: avoid illusion of extra accuracy (see also 09fb749d1a1e and 0f750a6dc754);
 wenzelm parents: 
76669diff
changeset | 254 | case unknown_files => | 
| 
b04d45bebbc5
discontinued somewhat pointless dependency: avoid illusion of extra accuracy (see also 09fb749d1a1e and 0f750a6dc754);
 wenzelm parents: 
76669diff
changeset | 255 |           progress.echo_warning("Unknown files (not part of the underlying Mercurial repository):" +
 | 
| 
b04d45bebbc5
discontinued somewhat pointless dependency: avoid illusion of extra accuracy (see also 09fb749d1a1e and 0f750a6dc754);
 wenzelm parents: 
76669diff
changeset | 256 |             unknown_files.map(path => path.expand.implode).sorted.mkString("\n  ", "\n  ", ""))
 | 
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 257 | } | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 258 | } | 
| 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 259 | |
| 51220 | 260 | |
| 261 | /* main build process */ | |
| 262 | ||
| 76669 | 263 | val queue = Queue(progress, build_deps.sessions_structure, store) | 
| 65289 | 264 | |
| 68219 | 265 | store.prepare_output_dir() | 
| 48373 | 266 | |
| 48595 | 267 |     if (clean_build) {
 | 
| 73012 
238ddf525da4
clarified HTML presentation, e.g. avoid bulky jobs like HOL or HOL-Analysis in applications;
 wenzelm parents: 
72993diff
changeset | 268 |       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 | 269 | val (relevant, ok) = store.clean_output(name) | 
| 
8fc4e3d1df86
clarified store.clean_output: cleanup user_output_dir even in system_mode;
 wenzelm parents: 
68219diff
changeset | 270 |         if (relevant) {
 | 
| 
8fc4e3d1df86
clarified store.clean_output: cleanup user_output_dir even in system_mode;
 wenzelm parents: 
68219diff
changeset | 271 |           if (ok) progress.echo("Cleaned " + name)
 | 
| 
8fc4e3d1df86
clarified store.clean_output: cleanup user_output_dir even in system_mode;
 wenzelm parents: 
68219diff
changeset | 272 | else progress.echo(name + " FAILED to clean") | 
| 
8fc4e3d1df86
clarified store.clean_output: cleanup user_output_dir even in system_mode;
 wenzelm parents: 
68219diff
changeset | 273 | } | 
| 48595 | 274 | } | 
| 275 | } | |
| 276 | ||
| 48425 | 277 | // scheduler loop | 
| 63082 
6af03422535a
expose Sessions.Info in Build.Results
 Lars Hupel <lars.hupel@mytum.de> parents: 
62946diff
changeset | 278 | case class Result( | 
| 66594 | 279 | current: Boolean, | 
| 68213 | 280 | heap_digest: Option[String], | 
| 66594 | 281 | process: Option[Process_Result], | 
| 75393 | 282 | info: Sessions.Info | 
| 283 |     ) {
 | |
| 62402 | 284 | def ok: Boolean = | 
| 285 |         process match {
 | |
| 286 | case None => false | |
| 74306 | 287 | case Some(res) => res.ok | 
| 62402 | 288 | } | 
| 289 | } | |
| 48639 
675988e64bf9
store parent heap stamp as well -- needs to be propagated through the build hierarchy;
 wenzelm parents: 
48626diff
changeset | 290 | |
| 73340 | 291 | def sleep(): Unit = | 
| 76476 | 292 |       Isabelle_Thread.interrupt_handler(_ => progress.stop()) {
 | 
| 293 |         build_options.seconds("editor_input_delay").sleep()
 | |
| 294 | } | |
| 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 = | |
| 75948 | 355 | 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 | 356 | process_result.rc, UUID.random().toString))) | 
| 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 = | 
| 76669 | 379 | build_deps.sessions_structure.build_requirements(List(session_name)). | 
| 71669 | 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 && | |
| 75948 | 395 | build.sources == sources_stamp(build_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 | |
| 76201 
9d1819c28f67
clarified conditions: no_build is ok for presentation if "all_current" holds;
 wenzelm parents: 
76200diff
changeset | 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))) | |
| 76201 
9d1819c28f67
clarified conditions: no_build is ok for presentation if "all_current" holds;
 wenzelm parents: 
76200diff
changeset | 411 | } | 
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 412 |                 else if (no_build) {
 | 
| 71894 | 413 | progress.echo_if(verbose, "Skipping " + session_name + " ...") | 
| 71669 | 414 | loop(pending - session_name, running, | 
| 415 | results + | |
| 416 | (session_name -> Result(false, heap_digest, Some(Process_Result(1)), info))) | |
| 48678 
ff27af15530c
queue ordering by descending outdegree and timeout;
 wenzelm parents: 
48676diff
changeset | 417 | } | 
| 62628 | 418 |                 else if (ancestor_results.forall(_.ok) && !progress.stopped) {
 | 
| 71669 | 419 | progress.echo((if (do_store) "Building " else "Running ") + session_name + " ...") | 
| 68086 
9e1c670301b8
cleanup session output before starting build job;
 wenzelm parents: 
67852diff
changeset | 420 | |
| 71669 | 421 | store.clean_output(session_name) | 
| 422 | using(store.open_database(session_name, output = true))( | |
| 423 | store.init_session_info(_, session_name)) | |
| 68086 
9e1c670301b8
cleanup session output before starting build job;
 wenzelm parents: 
67852diff
changeset | 424 | |
| 71601 | 425 | val numa_node = numa_nodes.next(used_node) | 
| 51220 | 426 | val job = | 
| 76668 | 427 | 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 | 428 | log, session_setup, numa_node, queue.command_timings(session_name)) | 
| 71669 | 429 | loop(pending, running + (session_name -> (ancestor_heaps, job)), results) | 
| 48547 | 430 | } | 
| 431 |                 else {
 | |
| 71669 | 432 | progress.echo(session_name + " CANCELLED") | 
| 433 | loop(pending - session_name, running, | |
| 434 | results + (session_name -> Result(false, heap_digest, None, info))) | |
| 48547 | 435 | } | 
| 436 | case None => sleep(); loop(pending, running, results) | |
| 48425 | 437 | } | 
| 50367 | 438 | ///}}} | 
| 48425 | 439 | case None => sleep(); loop(pending, running, results) | 
| 48373 | 440 | } | 
| 51253 | 441 | } | 
| 48425 | 442 | } | 
| 443 | ||
| 51220 | 444 | |
| 445 | /* build results */ | |
| 446 | ||
| 75393 | 447 |     val results = {
 | 
| 76200 | 448 | val build_results = | 
| 75948 | 449 |         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 | 450 |           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 | 451 | 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 | 452 | } | 
| 
8ff7a0e394f9
clarified PDF/HTML presentation, based on pdf blobs from session database (e.g. from earlier builds);
 wenzelm parents: 
72662diff
changeset | 453 |         else Isabelle_Thread.uninterruptible { loop(queue, Map.empty, Map.empty) }
 | 
| 48583 | 454 | |
| 76200 | 455 | val results = | 
| 456 | (for ((name, result) <- build_results.iterator) | |
| 76196 | 457 | yield (name, (result.process, result.info))).toMap | 
| 458 | ||
| 76230 | 459 | def presentation_sessions(config: Browser_Info.Config): List[String] = | 
| 76198 
fb4215da4919
clarified presentation_sessions: work with partial results;
 wenzelm parents: 
76197diff
changeset | 460 |         (for {
 | 
| 76669 | 461 | name <- build_deps.sessions_structure.build_topological_order.iterator | 
| 76200 | 462 | result <- build_results.get(name) | 
| 76230 | 463 | if result.ok && config.enabled(result.info) | 
| 76198 
fb4215da4919
clarified presentation_sessions: work with partial results;
 wenzelm parents: 
76197diff
changeset | 464 | } yield name).toList | 
| 
fb4215da4919
clarified presentation_sessions: work with partial results;
 wenzelm parents: 
76197diff
changeset | 465 | |
| 76209 | 466 | new Results(store, build_deps, results, presentation_sessions) | 
| 72673 
8ff7a0e394f9
clarified PDF/HTML presentation, based on pdf blobs from session database (e.g. from earlier builds);
 wenzelm parents: 
72662diff
changeset | 467 | } | 
| 62641 | 468 | |
| 69811 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 469 |     if (export_files) {
 | 
| 76196 | 470 |       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 | 471 | val info = results.info(name) | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 472 |         if (info.export_files.nonEmpty) {
 | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 473 |           progress.echo("Exporting " + info.name + " ...")
 | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 474 |           for ((dir, prune, pats) <- info.export_files) {
 | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 475 | Export.export_files(store, name, info.dir + dir, | 
| 71726 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71718diff
changeset | 476 | progress = if (verbose) progress else new Progress, | 
| 69811 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 477 | export_prune = prune, | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 478 | export_patterns = pats) | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 479 | } | 
| 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 480 | } | 
| 
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 | |
| 76230 | 484 | val presentation_sessions = results.presentation_sessions(browser_info) | 
| 485 |     if (presentation_sessions.nonEmpty && !progress.stopped) {
 | |
| 486 | Browser_Info.build(browser_info, results.store, results.deps, presentation_sessions, | |
| 76198 
fb4215da4919
clarified presentation_sessions: work with partial results;
 wenzelm parents: 
76197diff
changeset | 487 | progress = progress, verbose = verbose) | 
| 
fb4215da4919
clarified presentation_sessions: work with partial results;
 wenzelm parents: 
76197diff
changeset | 488 | } | 
| 
fb4215da4919
clarified presentation_sessions: work with partial results;
 wenzelm parents: 
76197diff
changeset | 489 | |
| 76196 | 490 |     if (!results.ok && (verbose || !no_build)) {
 | 
| 76199 | 491 |       progress.echo("Unfinished session(s): " + commas(results.unfinished))
 | 
| 62641 | 492 | } | 
| 493 | ||
| 494 | results | |
| 48341 | 495 | } | 
| 496 | ||
| 497 | ||
| 62833 | 498 | /* Isabelle tool wrapper */ | 
| 48341 | 499 | |
| 72763 | 500 |   val isabelle_tool = Isabelle_Tool("build", "build and manage Isabelle sessions",
 | 
| 75394 | 501 | Scala_Project.here, | 
| 502 |     { args =>
 | |
| 503 |       val build_options = Word.explode(Isabelle_System.getenv("ISABELLE_BUILD_OPTIONS"))
 | |
| 62590 | 504 | |
| 75394 | 505 | var base_sessions: List[String] = Nil | 
| 506 | var select_dirs: List[Path] = Nil | |
| 507 | var numa_shuffling = false | |
| 75942 
603852abed8f
clarified names: Browser_Info.Config vs. Browser_Info.Context;
 wenzelm parents: 
75941diff
changeset | 508 | var browser_info = Browser_Info.Config.none | 
| 75394 | 509 | var requirements = false | 
| 510 | var soft_build = false | |
| 511 | var exclude_session_groups: List[String] = Nil | |
| 512 | var all_sessions = false | |
| 513 | var build_heap = false | |
| 514 | var clean_build = false | |
| 515 | var dirs: List[Path] = Nil | |
| 516 | var export_files = false | |
| 517 | var fresh_build = false | |
| 518 | var session_groups: List[String] = Nil | |
| 519 | var max_jobs = 1 | |
| 520 | var check_keywords: Set[String] = Set.empty | |
| 521 | var list_files = false | |
| 522 | var no_build = false | |
| 523 | var options = Options.init(opts = build_options) | |
| 524 | var verbose = false | |
| 525 | var exclude_sessions: List[String] = Nil | |
| 62590 | 526 | |
| 75394 | 527 |       val getopts = Getopts("""
 | 
| 62590 | 528 | Usage: isabelle build [OPTIONS] [SESSIONS ...] | 
| 529 | ||
| 530 | Options are: | |
| 66737 
2edc0c42c883
option -B for "isabelle build" and "isabelle imports";
 wenzelm parents: 
66736diff
changeset | 531 | -B NAME include session NAME and all descendants | 
| 62590 | 532 | -D DIR include session directory and select its sessions | 
| 64265 | 533 | -N cyclic shuffling of NUMA CPU nodes (performance tuning) | 
| 72648 | 534 |     -P DIR       enable HTML/PDF presentation in directory (":" for default)
 | 
| 71807 | 535 | -R refer to requirements of selected sessions | 
| 66745 | 536 | -S soft build: only observe changes of sources, not heap images | 
| 62590 | 537 | -X NAME exclude sessions from group NAME and all descendants | 
| 538 | -a select all sessions | |
| 539 | -b build heap images | |
| 540 | -c clean build | |
| 541 | -d DIR include session directory | |
| 69811 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 542 | -e export files from session specification into file-system | 
| 66841 | 543 | -f fresh build | 
| 62590 | 544 | -g NAME select session group NAME | 
| 545 | -j INT maximum number of parallel jobs (default 1) | |
| 546 | -k KEYWORD check theory sources for conflicts with proposed keywords | |
| 547 | -l list session source files | |
| 548 | -n no build -- test dependencies only | |
| 549 | -o OPTION override Isabelle system OPTION (via NAME=VAL or NAME) | |
| 550 | -v verbose | |
| 551 | -x NAME exclude session NAME and all descendants | |
| 552 | ||
| 62596 | 553 | Build and manage Isabelle sessions, depending on implicit settings: | 
| 554 | ||
| 73736 | 555 | """ + Library.indent_lines(2, Build_Log.Settings.show()) + "\n", | 
| 75394 | 556 | "B:" -> (arg => base_sessions = base_sessions ::: List(arg)), | 
| 557 | "D:" -> (arg => select_dirs = select_dirs ::: List(Path.explode(arg))), | |
| 558 | "N" -> (_ => numa_shuffling = true), | |
| 75942 
603852abed8f
clarified names: Browser_Info.Config vs. Browser_Info.Context;
 wenzelm parents: 
75941diff
changeset | 559 | "P:" -> (arg => browser_info = Browser_Info.Config.make(arg)), | 
| 75394 | 560 | "R" -> (_ => requirements = true), | 
| 561 | "S" -> (_ => soft_build = true), | |
| 562 | "X:" -> (arg => exclude_session_groups = exclude_session_groups ::: List(arg)), | |
| 563 | "a" -> (_ => all_sessions = true), | |
| 564 | "b" -> (_ => build_heap = true), | |
| 565 | "c" -> (_ => clean_build = true), | |
| 566 | "d:" -> (arg => dirs = dirs ::: List(Path.explode(arg))), | |
| 567 | "e" -> (_ => export_files = true), | |
| 568 | "f" -> (_ => fresh_build = true), | |
| 569 | "g:" -> (arg => session_groups = session_groups ::: List(arg)), | |
| 570 | "j:" -> (arg => max_jobs = Value.Int.parse(arg)), | |
| 571 | "k:" -> (arg => check_keywords = check_keywords + arg), | |
| 572 | "l" -> (_ => list_files = true), | |
| 573 | "n" -> (_ => no_build = true), | |
| 574 | "o:" -> (arg => options = options + arg), | |
| 575 | "v" -> (_ => verbose = true), | |
| 576 | "x:" -> (arg => exclude_sessions = exclude_sessions ::: List(arg))) | |
| 62590 | 577 | |
| 75394 | 578 | val sessions = getopts(args) | 
| 62590 | 579 | |
| 75394 | 580 | val progress = new Console_Progress(verbose = verbose) | 
| 62590 | 581 | |
| 75394 | 582 | val start_date = Date.now() | 
| 64140 | 583 | |
| 75394 | 584 |       if (verbose) {
 | 
| 585 | progress.echo( | |
| 586 | "Started at " + Build_Log.print_date(start_date) + | |
| 587 |             " (" + Isabelle_System.getenv("ML_IDENTIFIER") + " on " + Isabelle_System.hostname() +")")
 | |
| 588 | progress.echo(Build_Log.Settings.show() + "\n") | |
| 589 | } | |
| 62590 | 590 | |
| 75394 | 591 | val results = | 
| 592 |         progress.interrupt_handler {
 | |
| 593 | build(options, | |
| 594 | selection = Sessions.Selection( | |
| 595 | requirements = requirements, | |
| 596 | all_sessions = all_sessions, | |
| 597 | base_sessions = base_sessions, | |
| 598 | exclude_session_groups = exclude_session_groups, | |
| 599 | exclude_sessions = exclude_sessions, | |
| 600 | session_groups = session_groups, | |
| 601 | sessions = sessions), | |
| 75942 
603852abed8f
clarified names: Browser_Info.Config vs. Browser_Info.Context;
 wenzelm parents: 
75941diff
changeset | 602 | browser_info = browser_info, | 
| 75394 | 603 | progress = progress, | 
| 604 | check_unknown_files = Mercurial.is_repository(Path.ISABELLE_HOME), | |
| 605 | build_heap = build_heap, | |
| 606 | clean_build = clean_build, | |
| 607 | dirs = dirs, | |
| 608 | select_dirs = select_dirs, | |
| 609 | numa_shuffling = NUMA.enabled_warning(progress, numa_shuffling), | |
| 610 | max_jobs = max_jobs, | |
| 611 | list_files = list_files, | |
| 612 | check_keywords = check_keywords, | |
| 613 | fresh_build = fresh_build, | |
| 614 | no_build = no_build, | |
| 615 | soft_build = soft_build, | |
| 616 | verbose = verbose, | |
| 617 | export_files = export_files) | |
| 618 | } | |
| 619 | val end_date = Date.now() | |
| 620 | val elapsed_time = end_date.time - start_date.time | |
| 621 | ||
| 622 |       if (verbose) {
 | |
| 623 |         progress.echo("\nFinished at " + Build_Log.print_date(end_date))
 | |
| 62833 | 624 | } | 
| 62590 | 625 | |
| 75394 | 626 | val total_timing = | 
| 627 | results.sessions.iterator.map(a => results(a).timing).foldLeft(Timing.zero)(_ + _). | |
| 628 | copy(elapsed = elapsed_time) | |
| 629 | progress.echo(total_timing.message_resources) | |
| 62590 | 630 | |
| 75394 | 631 | sys.exit(results.rc) | 
| 632 | }) | |
| 68305 | 633 | |
| 634 | ||
| 635 | /* build logic image */ | |
| 636 | ||
| 637 | def build_logic(options: Options, logic: String, | |
| 71726 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71718diff
changeset | 638 | progress: Progress = new Progress, | 
| 68305 | 639 | build_heap: Boolean = false, | 
| 640 | dirs: List[Path] = Nil, | |
| 70791 | 641 | fresh: Boolean = false, | 
| 75393 | 642 | strict: Boolean = false | 
| 643 |   ): Int = {
 | |
| 71896 
ce06d6456cc8
clarified signature --- fit within limit of 22 arguments;
 wenzelm parents: 
71895diff
changeset | 644 | val selection = Sessions.Selection.session(logic) | 
| 69540 | 645 | val rc = | 
| 71981 | 646 | if (!fresh && build(options, selection = selection, | 
| 74306 | 647 | build_heap = build_heap, no_build = true, dirs = dirs).ok) Process_Result.RC.ok | 
| 69540 | 648 |       else {
 | 
| 649 |         progress.echo("Build started for Isabelle/" + logic + " ...")
 | |
| 71981 | 650 | Build.build(options, selection = selection, progress = progress, | 
| 651 | build_heap = build_heap, fresh_build = fresh, dirs = dirs).rc | |
| 69540 | 652 | } | 
| 74306 | 653 |     if (strict && rc != Process_Result.RC.ok) error("Failed to build Isabelle/" + logic) else rc
 | 
| 68305 | 654 | } | 
| 48276 | 655 | } |