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