| author | wenzelm | 
| Sun, 22 Sep 2024 16:12:15 +0200 | |
| changeset 80923 | 6c9628a116cc | 
| parent 80886 | 5d562dd387ae | 
| child 81414 | ed4ff84e9b21 | 
| permissions | -rw-r--r-- | 
| 79502 | 1 | /* Title: Pure/Build/build.scala | 
| 48276 | 2 | Author: Makarius | 
| 57923 | 3 | Options: :folding=explicit: | 
| 48276 | 4 | |
| 77553 | 5 | Command-line tools to build and manage Isabelle sessions. | 
| 48276 | 6 | */ | 
| 7 | ||
| 8 | package isabelle | |
| 9 | ||
| 77553 | 10 | import scala.collection.mutable | 
| 11 | import scala.util.matching.Regex | |
| 12 | ||
| 48276 | 13 | |
| 75393 | 14 | object Build {
 | 
| 77553 | 15 | /** "isabelle build" **/ | 
| 16 | ||
| 78232 | 17 | /* options */ | 
| 18 | ||
| 19 | def hostname(options: Options): String = | |
| 20 |     Isabelle_System.hostname(options.string("build_hostname"))
 | |
| 21 | ||
| 22 |   def engine_name(options: Options): String = options.string("build_engine")
 | |
| 23 | ||
| 24 | ||
| 25 | ||
| 78421 | 26 | /* context */ | 
| 27 | ||
| 28 | sealed case class Context( | |
| 29 | store: Store, | |
| 79643 | 30 | deps: isabelle.Sessions.Deps, | 
| 79642 | 31 | engine: Engine = Engine.Default, | 
| 78421 | 32 | afp_root: Option[Path] = None, | 
| 33 | build_hosts: List[Build_Cluster.Host] = Nil, | |
| 34 |     ml_platform: String = Isabelle_System.getenv("ML_PLATFORM"),
 | |
| 35 | hostname: String = Isabelle_System.hostname(), | |
| 36 | numa_shuffling: Boolean = false, | |
| 80109 
dbcd6dc7f70f
back to static numa_nodes (reverting part of c2c59de57df9);
 wenzelm parents: 
80056diff
changeset | 37 | numa_nodes: List[Int] = Nil, | 
| 79710 
32ca3d1283de
clarified signature: Build_Process tells how to clean sessions;
 wenzelm parents: 
79709diff
changeset | 38 | clean_sessions: List[String] = Nil, | 
| 80118 | 39 | store_heap: Boolean = false, | 
| 78421 | 40 | fresh_build: Boolean = false, | 
| 41 | no_build: Boolean = false, | |
| 42 | session_setup: (String, Session) => Unit = (_, _) => (), | |
| 79717 | 43 | build_uuid: String = UUID.random_string(), | 
| 79811 
d9fc2cc37694
more robust build_start for master and workers (via database);
 wenzelm parents: 
79810diff
changeset | 44 | build_start: Option[Date] = None, | 
| 79644 | 45 | jobs: Int = 0, | 
| 78421 | 46 | master: Boolean = false | 
| 47 |   ) {
 | |
| 48 | def build_options: Options = store.options | |
| 49 | ||
| 79643 | 50 | def sessions_structure: isabelle.Sessions.Structure = deps.sessions_structure | 
| 78421 | 51 | |
| 79645 
7a1153c95bf9
clarifier worker vs. master, which may coincide for local build;
 wenzelm parents: 
79644diff
changeset | 52 | def worker: Boolean = jobs > 0 | 
| 
7a1153c95bf9
clarifier worker vs. master, which may coincide for local build;
 wenzelm parents: 
79644diff
changeset | 53 | |
| 
7a1153c95bf9
clarifier worker vs. master, which may coincide for local build;
 wenzelm parents: 
79644diff
changeset | 54 | override def toString: String = | 
| 
7a1153c95bf9
clarifier worker vs. master, which may coincide for local build;
 wenzelm parents: 
79644diff
changeset | 55 | "Build.Context(build_uuid = " + quote(build_uuid) + | 
| 
7a1153c95bf9
clarifier worker vs. master, which may coincide for local build;
 wenzelm parents: 
79644diff
changeset | 56 | if_proper(worker, ", worker = true") + | 
| 
7a1153c95bf9
clarifier worker vs. master, which may coincide for local build;
 wenzelm parents: 
79644diff
changeset | 57 | if_proper(master, ", master = true") + ")" | 
| 78421 | 58 | } | 
| 59 | ||
| 60 | ||
| 77330 
47eb96592aa2
support alternative build engines, via system option "build_engine";
 wenzelm parents: 
77326diff
changeset | 61 | /* results */ | 
| 48424 | 62 | |
| 77311 | 63 |   object Results {
 | 
| 78440 
126a12483c67
support for Build_Cluster.Session.init (rsync + Admin/init);
 wenzelm parents: 
78437diff
changeset | 64 | def apply( | 
| 
126a12483c67
support for Build_Cluster.Session.init (rsync + Admin/init);
 wenzelm parents: 
78437diff
changeset | 65 | context: Context, | 
| 
126a12483c67
support for Build_Cluster.Session.init (rsync + Admin/init);
 wenzelm parents: 
78437diff
changeset | 66 | results: Map[String, Process_Result] = Map.empty, | 
| 
126a12483c67
support for Build_Cluster.Session.init (rsync + Admin/init);
 wenzelm parents: 
78437diff
changeset | 67 | other_rc: Int = Process_Result.RC.ok | 
| 
126a12483c67
support for Build_Cluster.Session.init (rsync + Admin/init);
 wenzelm parents: 
78437diff
changeset | 68 |     ): Results = {
 | 
| 79643 | 69 | new Results(context.store, context.deps, results, other_rc) | 
| 78440 
126a12483c67
support for Build_Cluster.Session.init (rsync + Admin/init);
 wenzelm parents: 
78437diff
changeset | 70 | } | 
| 77311 | 71 | } | 
| 72 | ||
| 73 | class Results private( | |
| 78178 | 74 | val store: Store, | 
| 76209 | 75 | val deps: Sessions.Deps, | 
| 78440 
126a12483c67
support for Build_Cluster.Session.init (rsync + Admin/init);
 wenzelm parents: 
78437diff
changeset | 76 | results: Map[String, Process_Result], | 
| 
126a12483c67
support for Build_Cluster.Session.init (rsync + Admin/init);
 wenzelm parents: 
78437diff
changeset | 77 | other_rc: Int | 
| 76198 
fb4215da4919
clarified presentation_sessions: work with partial results;
 wenzelm parents: 
76197diff
changeset | 78 |   ) {
 | 
| 76206 
769a7cd5a16a
clarified signature: re-use store/cache from build results;
 wenzelm parents: 
76202diff
changeset | 79 | def cache: Term.Cache = store.cache | 
| 
769a7cd5a16a
clarified signature: re-use store/cache from build results;
 wenzelm parents: 
76202diff
changeset | 80 | |
| 77311 | 81 | def sessions_ok: List[String] = | 
| 80116 | 82 | List.from( | 
| 83 |         for {
 | |
| 84 | name <- deps.sessions_structure.build_topological_order.iterator | |
| 85 | result <- results.get(name) if result.ok | |
| 86 | } yield name) | |
| 77311 | 87 | |
| 77250 
22016642d6af
clarified data structure: use static info from deps, not dynamic results;
 wenzelm parents: 
77246diff
changeset | 88 | def info(name: String): Sessions.Info = deps.sessions_structure(name) | 
| 62403 | 89 | def sessions: Set[String] = results.keySet | 
| 77253 
792dad9cb04f
clarified data structure: absorb Option[Process_Result] into Process_Result, e.g. to simplify database storage;
 wenzelm parents: 
77252diff
changeset | 90 | def cancelled(name: String): Boolean = !results(name).defined | 
| 
792dad9cb04f
clarified data structure: absorb Option[Process_Result] into Process_Result, e.g. to simplify database storage;
 wenzelm parents: 
77252diff
changeset | 91 | def apply(name: String): Process_Result = results(name).strict | 
| 78440 
126a12483c67
support for Build_Cluster.Session.init (rsync + Admin/init);
 wenzelm parents: 
78437diff
changeset | 92 | |
| 
126a12483c67
support for Build_Cluster.Session.init (rsync + Admin/init);
 wenzelm parents: 
78437diff
changeset | 93 | val rc: Int = | 
| 
126a12483c67
support for Build_Cluster.Session.init (rsync + Admin/init);
 wenzelm parents: 
78437diff
changeset | 94 | Process_Result.RC.merge(other_rc, | 
| 
126a12483c67
support for Build_Cluster.Session.init (rsync + Admin/init);
 wenzelm parents: 
78437diff
changeset | 95 | Process_Result.RC.merge(results.valuesIterator.map(_.strict.rc))) | 
| 74306 | 96 | def ok: Boolean = rc == Process_Result.RC.ok | 
| 62406 | 97 | |
| 78566 
a04277e3b313
tuned message: failure may stem from build_cluster init;
 wenzelm parents: 
78562diff
changeset | 98 | lazy val unfinished: List[String] = sessions.iterator.filterNot(apply(_).ok).toList.sorted | 
| 76199 | 99 | |
| 62406 | 100 | override def toString: String = rc.toString | 
| 62403 | 101 | } | 
| 102 | ||
| 77330 
47eb96592aa2
support alternative build engines, via system option "build_engine";
 wenzelm parents: 
77326diff
changeset | 103 | |
| 
47eb96592aa2
support alternative build engines, via system option "build_engine";
 wenzelm parents: 
77326diff
changeset | 104 | /* engine */ | 
| 
47eb96592aa2
support alternative build engines, via system option "build_engine";
 wenzelm parents: 
77326diff
changeset | 105 | |
| 78232 | 106 |   object Engine {
 | 
| 107 | lazy val services: List[Engine] = | |
| 108 | Isabelle_System.make_services(classOf[Engine]) | |
| 109 | ||
| 110 | def apply(name: String): Engine = | |
| 111 |       services.find(_.name == name).getOrElse(error("Bad build engine " + quote(name)))
 | |
| 79642 | 112 | |
| 113 |     class Default extends Engine("") { override def toString: String = "<default>" }
 | |
| 114 | object Default extends Default | |
| 78232 | 115 | } | 
| 116 | ||
| 77411 
149cc77f7348
clafified signature: simplify object-oriented reuse;
 wenzelm parents: 
77384diff
changeset | 117 |   class Engine(val name: String) extends Isabelle_System.Service {
 | 
| 79705 | 118 | engine => | 
| 119 | ||
| 77330 
47eb96592aa2
support alternative build engines, via system option "build_engine";
 wenzelm parents: 
77326diff
changeset | 120 | override def toString: String = name | 
| 78232 | 121 | |
| 79614 | 122 |     def build_options(options: Options, build_cluster: Boolean = false): Options = {
 | 
| 78401 | 123 | val options1 = options + "completion_limit=0" + "editor_tracing_messages=0" | 
| 80160 
ead20482da9c
build_cluster always uses build_database_server for now -- despite 1fa1b32b0379: its local/remote storage model often leads to incoherent state;
 wenzelm parents: 
80128diff
changeset | 124 | if (build_cluster) options1 + "build_database" + "build_database_server" + "build_log_verbose" else options1 // FIXME tmp: build_database_server | 
| 78401 | 125 | } | 
| 78232 | 126 | |
| 78412 | 127 | final def build_store(options: Options, | 
| 79614 | 128 | build_cluster: Boolean = false, | 
| 78412 | 129 | cache: Term.Cache = Term.Cache.make() | 
| 130 |     ): Store = {
 | |
| 79705 | 131 | val build_options = engine.build_options(options, build_cluster = build_cluster) | 
| 132 | val store = Store(build_options, build_cluster = build_cluster, cache = cache) | |
| 78412 | 133 |       Isabelle_System.make_directory(store.output_dir + Path.basic("log"))
 | 
| 134 | Isabelle_Fonts.init() | |
| 135 | store | |
| 136 | } | |
| 137 | ||
| 78436 
5f5f909206bb
clarified signature: more "object-oriented" style;
 wenzelm parents: 
78435diff
changeset | 138 | def open_build_process( | 
| 78421 | 139 | build_context: Context, | 
| 78372 | 140 | build_progress: Progress, | 
| 141 | server: SSH.Server | |
| 142 | ): Build_Process = new Build_Process(build_context, build_progress, server) | |
| 78232 | 143 | |
| 78436 
5f5f909206bb
clarified signature: more "object-oriented" style;
 wenzelm parents: 
78435diff
changeset | 144 | final def run_build_process( | 
| 78421 | 145 | context: Context, | 
| 78372 | 146 | progress: Progress, | 
| 147 | server: SSH.Server | |
| 78412 | 148 |     ): Results = {
 | 
| 78232 | 149 |       Isabelle_Thread.uninterruptible {
 | 
| 79710 
32ca3d1283de
clarified signature: Build_Process tells how to clean sessions;
 wenzelm parents: 
79709diff
changeset | 150 |         using(open_build_process(context, progress, server)) { build_process =>
 | 
| 
32ca3d1283de
clarified signature: Build_Process tells how to clean sessions;
 wenzelm parents: 
79709diff
changeset | 151 | build_process.prepare() | 
| 
32ca3d1283de
clarified signature: Build_Process tells how to clean sessions;
 wenzelm parents: 
79709diff
changeset | 152 | build_process.run() | 
| 
32ca3d1283de
clarified signature: Build_Process tells how to clean sessions;
 wenzelm parents: 
79709diff
changeset | 153 | } | 
| 78232 | 154 | } | 
| 78412 | 155 | } | 
| 77330 
47eb96592aa2
support alternative build engines, via system option "build_engine";
 wenzelm parents: 
77326diff
changeset | 156 | } | 
| 
47eb96592aa2
support alternative build engines, via system option "build_engine";
 wenzelm parents: 
77326diff
changeset | 157 | |
| 
47eb96592aa2
support alternative build engines, via system option "build_engine";
 wenzelm parents: 
77326diff
changeset | 158 | |
| 77558 | 159 | |
| 160 | /* build */ | |
| 161 | ||
| 62641 | 162 | def build( | 
| 50404 
898cac1dad5e
avoid startup within GUI thread -- it is only required later for dialog;
 wenzelm parents: 
50367diff
changeset | 163 | options: Options, | 
| 78401 | 164 | build_hosts: List[Build_Cluster.Host] = Nil, | 
| 71981 | 165 | selection: Sessions.Selection = Sessions.Selection.empty, | 
| 75942 
603852abed8f
clarified names: Browser_Info.Config vs. Browser_Info.Context;
 wenzelm parents: 
75941diff
changeset | 166 | browser_info: Browser_Info.Config = Browser_Info.Config.none, | 
| 71726 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71718diff
changeset | 167 | progress: Progress = new Progress, | 
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 168 | check_unknown_files: Boolean = false, | 
| 48511 
37999ee01156
remove old output heaps, to ensure that result is valid wrt. check_stamps;
 wenzelm parents: 
48509diff
changeset | 169 | build_heap: Boolean = false, | 
| 48595 | 170 | clean_build: Boolean = false, | 
| 78418 
bc62be4144e6
added option -A for AFP root, following "isabelle sync";
 wenzelm parents: 
78412diff
changeset | 171 | afp_root: Option[Path] = None, | 
| 56890 | 172 | dirs: List[Path] = Nil, | 
| 173 | select_dirs: List[Path] = Nil, | |
| 66968 
9991671c98aa
allow to augment session context via explicit session infos;
 wenzelm parents: 
66962diff
changeset | 174 | infos: List[Sessions.Info] = Nil, | 
| 64265 | 175 | numa_shuffling: Boolean = false, | 
| 79616 | 176 | max_jobs: Option[Int] = None, | 
| 48903 | 177 | list_files: Boolean = false, | 
| 66841 | 178 | fresh_build: Boolean = false, | 
| 48509 | 179 | no_build: Boolean = false, | 
| 66745 | 180 | soft_build: Boolean = false, | 
| 73805 
b73777a0c076
allow build session setup, e.g. for protocol handlers;
 wenzelm parents: 
73804diff
changeset | 181 | export_files: Boolean = false, | 
| 76927 
da13da82f6f9
treat update_options as part of Sessions.Info meta_digest, for proper re-build of updated sessions;
 wenzelm parents: 
76919diff
changeset | 182 | augment_options: String => List[Options.Spec] = _ => Nil, | 
| 77559 
4ad322ee6025
clarified signature: support all arguments of Sessions.store();
 wenzelm parents: 
77558diff
changeset | 183 | session_setup: (String, Session) => Unit = (_, _) => (), | 
| 
4ad322ee6025
clarified signature: support all arguments of Sessions.store();
 wenzelm parents: 
77558diff
changeset | 184 | cache: Term.Cache = Term.Cache.make() | 
| 75393 | 185 |   ): Results = {
 | 
| 79707 | 186 | val engine = Engine(engine_name(options)) | 
| 187 | val store = engine.build_store(options, build_cluster = build_hosts.nonEmpty, cache = cache) | |
| 77557 | 188 | val build_options = store.options | 
| 69369 
6ecc85955e04
prefer "Isabelle DejaVu Sans", even for headless batch-build (session_graph.pdf);
 wenzelm parents: 
68957diff
changeset | 189 | |
| 78372 | 190 |     using(store.open_server()) { server =>
 | 
| 66745 | 191 | |
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 192 | /* session selection and dependencies */ | 
| 65422 | 193 | |
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 194 | val full_sessions = | 
| 80056 | 195 | Sessions.load_structure(build_options, dirs = AFP.main_dirs(afp_root) ::: dirs, | 
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 196 | select_dirs = select_dirs, infos = infos, augment_options = augment_options) | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 197 | val full_sessions_selection = full_sessions.imports_selection(selection) | 
| 73012 
238ddf525da4
clarified HTML presentation, e.g. avoid bulky jobs like HOL or HOL-Analysis in applications;
 wenzelm parents: 
72993diff
changeset | 198 | |
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 199 |       val build_deps = {
 | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 200 | val deps0 = | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 201 | Sessions.deps(full_sessions.selection(selection), progress = progress, | 
| 80886 
5d562dd387ae
discontinued "isabelle build -k": superseded by admin-tool "isabelle check_keywords";
 wenzelm parents: 
80817diff
changeset | 202 | inlined_files = true, list_files = list_files).check_errors | 
| 66745 | 203 | |
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 204 |         if (soft_build && !fresh_build) {
 | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 205 | val outdated = | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 206 | deps0.sessions_structure.build_topological_order.flatMap(name => | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 207 |               store.try_open_database(name, server = server) match {
 | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 208 | case Some(db) => | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 209 |                   using(db)(store.read_build(_, name)) match {
 | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 210 | case Some(build) if build.ok => | 
| 80128 | 211 | val sources_shasum = deps0.sources_shasum(name) | 
| 212 | val thorough = deps0.sessions_structure(name).build_thorough | |
| 213 | if (Sessions.eq_sources(thorough, build.sources, sources_shasum)) None | |
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 214 | else Some(name) | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 215 | case _ => Some(name) | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 216 | } | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 217 | case None => Some(name) | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 218 | }) | 
| 68204 | 219 | |
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 220 | Sessions.deps(full_sessions.selection(Sessions.Selection(sessions = outdated)), | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 221 | progress = progress, inlined_files = true).check_errors | 
| 78372 | 222 | } | 
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 223 | else deps0 | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 224 | } | 
| 66745 | 225 | |
| 226 | ||
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 227 | /* check unknown files */ | 
| 78372 | 228 | |
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 229 |       if (check_unknown_files) {
 | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 230 | val source_files = | 
| 80116 | 231 | List.from( | 
| 232 |             for {
 | |
| 233 | (_, base) <- build_deps.session_bases.iterator | |
| 234 | (path, _) <- base.session_sources.iterator | |
| 235 | } yield path) | |
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 236 |         Mercurial.check_files(source_files)._2 match {
 | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 237 | case Nil => | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 238 | case unknown_files => | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 239 | progress.echo_warning( | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 240 | "Unknown files (not part of the underlying Mercurial repository):" + | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 241 |               unknown_files.map(File.standard_path).sorted.mkString("\n  ", "\n  ", ""))
 | 
| 78372 | 242 | } | 
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 243 | } | 
| 78372 | 244 | |
| 245 | ||
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 246 | /* build process and results */ | 
| 78372 | 247 | |
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 248 | val clean_sessions = | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 249 | if (clean_build) full_sessions.imports_descendants(full_sessions_selection) else Nil | 
| 79710 
32ca3d1283de
clarified signature: Build_Process tells how to clean sessions;
 wenzelm parents: 
79709diff
changeset | 250 | |
| 80109 
dbcd6dc7f70f
back to static numa_nodes (reverting part of c2c59de57df9);
 wenzelm parents: 
80056diff
changeset | 251 | val numa_nodes = Host.numa_nodes(enabled = numa_shuffling) | 
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 252 | val build_context = | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 253 | Context(store, build_deps, engine = engine, afp_root = afp_root, | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 254 | build_hosts = build_hosts, hostname = hostname(build_options), | 
| 80118 | 255 | clean_sessions = clean_sessions, store_heap = build_heap, | 
| 80109 
dbcd6dc7f70f
back to static numa_nodes (reverting part of c2c59de57df9);
 wenzelm parents: 
80056diff
changeset | 256 | numa_shuffling = numa_shuffling, numa_nodes = numa_nodes, | 
| 
dbcd6dc7f70f
back to static numa_nodes (reverting part of c2c59de57df9);
 wenzelm parents: 
80056diff
changeset | 257 | fresh_build = fresh_build, no_build = no_build, session_setup = session_setup, | 
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 258 | jobs = max_jobs.getOrElse(if (build_hosts.nonEmpty) 0 else 1), master = true) | 
| 48504 
21dfd6c04482
actually check source vs. target stamps, based on information from log files;
 wenzelm parents: 
48494diff
changeset | 259 | |
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 260 | val results = engine.run_build_process(build_context, progress, server) | 
| 78372 | 261 | |
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 262 |       if (export_files) {
 | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 263 |         for (name <- full_sessions_selection.iterator if results(name).ok) {
 | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 264 | val info = results.info(name) | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 265 |           if (info.export_files.nonEmpty) {
 | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 266 |             progress.echo("Exporting " + info.name + " ...")
 | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 267 |             for ((dir, prune, pats) <- info.export_files) {
 | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 268 | Export.export_files(store, name, info.dir + dir, | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 269 | progress = if (progress.verbose) progress else new Progress, | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 270 | export_prune = prune, | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 271 | export_patterns = pats) | 
| 78372 | 272 | } | 
| 273 | } | |
| 274 | } | |
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 275 | } | 
| 78372 | 276 | |
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 277 | val presentation_sessions = | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 278 | results.sessions_ok.filter(name => browser_info.enabled(results.info(name))) | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 279 |       if (presentation_sessions.nonEmpty && !progress.stopped) {
 | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 280 | Browser_Info.build(browser_info, results.store, results.deps, presentation_sessions, | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 281 | progress = progress, server = server) | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 282 | } | 
| 78372 | 283 | |
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 284 |       if (results.unfinished.nonEmpty && (progress.verbose || !no_build)) {
 | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 285 |         progress.echo("Unfinished session(s): " + commas(results.unfinished))
 | 
| 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 286 | } | 
| 78372 | 287 | |
| 79715 
e59d93da9109
removed unused database_server (amending 32ca3d1283de);
 wenzelm parents: 
79710diff
changeset | 288 | results | 
| 65832 
2fb85623c386
implicitly check for unknown files (not part of a Mercurial repository);
 wenzelm parents: 
65831diff
changeset | 289 | } | 
| 48341 | 290 | } | 
| 291 | ||
| 292 | ||
| 77555 | 293 | /* build logic image */ | 
| 294 | ||
| 295 | def build_logic(options: Options, logic: String, | |
| 296 | progress: Progress = new Progress, | |
| 297 | build_heap: Boolean = false, | |
| 298 | dirs: List[Path] = Nil, | |
| 299 | fresh: Boolean = false, | |
| 300 | strict: Boolean = false | |
| 301 |   ): Int = {
 | |
| 302 | val selection = Sessions.Selection.session(logic) | |
| 303 | val rc = | |
| 304 | if (!fresh && build(options, selection = selection, | |
| 305 | build_heap = build_heap, no_build = true, dirs = dirs).ok) Process_Result.RC.ok | |
| 306 |       else {
 | |
| 307 |         progress.echo("Build started for Isabelle/" + logic + " ...")
 | |
| 308 | build(options, selection = selection, progress = progress, | |
| 309 | build_heap = build_heap, fresh_build = fresh, dirs = dirs).rc | |
| 310 | } | |
| 311 |     if (strict && rc != Process_Result.RC.ok) error("Failed to build Isabelle/" + logic) else rc
 | |
| 312 | } | |
| 313 | ||
| 314 | ||
| 79629 | 315 | /* Isabelle tool wrappers */ | 
| 48341 | 316 | |
| 77553 | 317 |   val isabelle_tool1 = Isabelle_Tool("build", "build and manage Isabelle sessions",
 | 
| 75394 | 318 | Scala_Project.here, | 
| 319 |     { args =>
 | |
| 78418 
bc62be4144e6
added option -A for AFP root, following "isabelle sync";
 wenzelm parents: 
78412diff
changeset | 320 | var afp_root: Option[Path] = None | 
| 78405 | 321 | val base_sessions = new mutable.ListBuffer[String] | 
| 322 | val select_dirs = new mutable.ListBuffer[Path] | |
| 78401 | 323 | val build_hosts = new mutable.ListBuffer[Build_Cluster.Host] | 
| 75394 | 324 | var numa_shuffling = false | 
| 75942 
603852abed8f
clarified names: Browser_Info.Config vs. Browser_Info.Context;
 wenzelm parents: 
75941diff
changeset | 325 | var browser_info = Browser_Info.Config.none | 
| 75394 | 326 | var requirements = false | 
| 327 | var soft_build = false | |
| 78405 | 328 | val exclude_session_groups = new mutable.ListBuffer[String] | 
| 75394 | 329 | var all_sessions = false | 
| 330 | var build_heap = false | |
| 331 | var clean_build = false | |
| 78405 | 332 | val dirs = new mutable.ListBuffer[Path] | 
| 75394 | 333 | var export_files = false | 
| 334 | var fresh_build = false | |
| 78405 | 335 | val session_groups = new mutable.ListBuffer[String] | 
| 79616 | 336 | var max_jobs: Option[Int] = None | 
| 75394 | 337 | var list_files = false | 
| 338 | var no_build = false | |
| 77628 | 339 | var options = Options.init(specs = Options.Spec.ISABELLE_BUILD_OPTIONS) | 
| 75394 | 340 | var verbose = false | 
| 78405 | 341 | val exclude_sessions = new mutable.ListBuffer[String] | 
| 62590 | 342 | |
| 75394 | 343 |       val getopts = Getopts("""
 | 
| 62590 | 344 | Usage: isabelle build [OPTIONS] [SESSIONS ...] | 
| 345 | ||
| 346 | Options are: | |
| 78418 
bc62be4144e6
added option -A for AFP root, following "isabelle sync";
 wenzelm parents: 
78412diff
changeset | 347 |     -A ROOT      include AFP with given root directory (":" for """ + AFP.BASE.implode + """)
 | 
| 66737 
2edc0c42c883
option -B for "isabelle build" and "isabelle imports";
 wenzelm parents: 
66736diff
changeset | 348 | -B NAME include session NAME and all descendants | 
| 62590 | 349 | -D DIR include session directory and select its sessions | 
| 79615 | 350 | -H HOSTS additional cluster host specifications of the form | 
| 351 | NAMES:PARAMETERS (separated by commas) | |
| 64265 | 352 | -N cyclic shuffling of NUMA CPU nodes (performance tuning) | 
| 72648 | 353 |     -P DIR       enable HTML/PDF presentation in directory (":" for default)
 | 
| 71807 | 354 | -R refer to requirements of selected sessions | 
| 66745 | 355 | -S soft build: only observe changes of sources, not heap images | 
| 62590 | 356 | -X NAME exclude sessions from group NAME and all descendants | 
| 357 | -a select all sessions | |
| 358 | -b build heap images | |
| 359 | -c clean build | |
| 360 | -d DIR include session directory | |
| 69811 
18f61ce86425
clarified 'export_files' in session ROOT: require explicit "isabelle build -e";
 wenzelm parents: 
69777diff
changeset | 361 | -e export files from session specification into file-system | 
| 66841 | 362 | -f fresh build | 
| 62590 | 363 | -g NAME select session group NAME | 
| 79647 | 364 | -j INT maximum number of parallel jobs | 
| 365 | (default: 1 for local build, 0 for build cluster) | |
| 62590 | 366 | -k KEYWORD check theory sources for conflicts with proposed keywords | 
| 367 | -l list session source files | |
| 77554 
4465d9dff448
clarified terminology of "session build database", while "build database" is the one underlying Build_Process;
 wenzelm parents: 
77553diff
changeset | 368 | -n no build -- take existing session build databases | 
| 62590 | 369 | -o OPTION override Isabelle system OPTION (via NAME=VAL or NAME) | 
| 370 | -v verbose | |
| 371 | -x NAME exclude session NAME and all descendants | |
| 372 | ||
| 77647 | 373 | Build and manage Isabelle sessions: ML heaps, session databases, documents. | 
| 62596 | 374 | |
| 79615 | 375 | Parameters for cluster host specifications (-H), apart from system options: | 
| 78408 
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
 wenzelm parents: 
78405diff
changeset | 376 | """ + Library.indent_lines(4, Build_Cluster.Host.parameters.print()) + | 
| 
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
 wenzelm parents: 
78405diff
changeset | 377 | """ | 
| 
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
 wenzelm parents: 
78405diff
changeset | 378 | |
| 77647 | 379 | Notable system options: see "isabelle options -l -t build" | 
| 380 | ||
| 381 | Notable system settings: | |
| 78408 
092f1e435b3a
proper Build_Cluster.Host.parse for parameters and system options;
 wenzelm parents: 
78405diff
changeset | 382 | """ + Library.indent_lines(4, Build_Log.Settings.show()) + "\n", | 
| 78418 
bc62be4144e6
added option -A for AFP root, following "isabelle sync";
 wenzelm parents: 
78412diff
changeset | 383 | "A:" -> (arg => afp_root = Some(if (arg == ":") AFP.BASE else Path.explode(arg))), | 
| 78405 | 384 | "B:" -> (arg => base_sessions += arg), | 
| 385 | "D:" -> (arg => select_dirs += Path.explode(arg)), | |
| 78945 
73162a487f94
build cluster host specifications are based on registry entries (table prefix "host");
 wenzelm parents: 
78916diff
changeset | 386 | "H:" -> (arg => build_hosts ++= Build_Cluster.Host.parse(Registry.global, arg)), | 
| 75394 | 387 | "N" -> (_ => numa_shuffling = true), | 
| 75942 
603852abed8f
clarified names: Browser_Info.Config vs. Browser_Info.Context;
 wenzelm parents: 
75941diff
changeset | 388 | "P:" -> (arg => browser_info = Browser_Info.Config.make(arg)), | 
| 75394 | 389 | "R" -> (_ => requirements = true), | 
| 390 | "S" -> (_ => soft_build = true), | |
| 78405 | 391 | "X:" -> (arg => exclude_session_groups += arg), | 
| 75394 | 392 | "a" -> (_ => all_sessions = true), | 
| 393 | "b" -> (_ => build_heap = true), | |
| 394 | "c" -> (_ => clean_build = true), | |
| 78405 | 395 | "d:" -> (arg => dirs += Path.explode(arg)), | 
| 75394 | 396 | "e" -> (_ => export_files = true), | 
| 397 | "f" -> (_ => fresh_build = true), | |
| 78405 | 398 | "g:" -> (arg => session_groups += arg), | 
| 79616 | 399 | "j:" -> (arg => max_jobs = Some(Value.Nat.parse(arg))), | 
| 75394 | 400 | "l" -> (_ => list_files = true), | 
| 401 | "n" -> (_ => no_build = true), | |
| 402 | "o:" -> (arg => options = options + arg), | |
| 403 | "v" -> (_ => verbose = true), | |
| 78405 | 404 | "x:" -> (arg => exclude_sessions += arg)) | 
| 62590 | 405 | |
| 75394 | 406 | val sessions = getopts(args) | 
| 62590 | 407 | |
| 75394 | 408 | val progress = new Console_Progress(verbose = verbose) | 
| 62590 | 409 | |
| 77521 
5642de4d225d
clarified signature: manage "verbose" flag via "progress";
 wenzelm parents: 
77505diff
changeset | 410 | progress.echo( | 
| 78895 | 411 | "Started at " + Build_Log.print_date(progress.start) + | 
| 77719 | 412 |           " (" + Isabelle_System.ml_identifier() + " on " + hostname(options) +")",
 | 
| 77521 
5642de4d225d
clarified signature: manage "verbose" flag via "progress";
 wenzelm parents: 
77505diff
changeset | 413 | verbose = true) | 
| 
5642de4d225d
clarified signature: manage "verbose" flag via "progress";
 wenzelm parents: 
77505diff
changeset | 414 | progress.echo(Build_Log.Settings.show() + "\n", verbose = true) | 
| 62590 | 415 | |
| 75394 | 416 | val results = | 
| 417 |         progress.interrupt_handler {
 | |
| 418 | build(options, | |
| 419 | selection = Sessions.Selection( | |
| 420 | requirements = requirements, | |
| 421 | all_sessions = all_sessions, | |
| 78405 | 422 | base_sessions = base_sessions.toList, | 
| 423 | exclude_session_groups = exclude_session_groups.toList, | |
| 424 | exclude_sessions = exclude_sessions.toList, | |
| 425 | session_groups = session_groups.toList, | |
| 75394 | 426 | sessions = sessions), | 
| 75942 
603852abed8f
clarified names: Browser_Info.Config vs. Browser_Info.Context;
 wenzelm parents: 
75941diff
changeset | 427 | browser_info = browser_info, | 
| 75394 | 428 | progress = progress, | 
| 429 | check_unknown_files = Mercurial.is_repository(Path.ISABELLE_HOME), | |
| 430 | build_heap = build_heap, | |
| 431 | clean_build = clean_build, | |
| 78437 | 432 | afp_root = afp_root, | 
| 78405 | 433 | dirs = dirs.toList, | 
| 434 | select_dirs = select_dirs.toList, | |
| 77477 | 435 | numa_shuffling = Host.numa_check(progress, numa_shuffling), | 
| 75394 | 436 | max_jobs = max_jobs, | 
| 437 | list_files = list_files, | |
| 438 | fresh_build = fresh_build, | |
| 439 | no_build = no_build, | |
| 440 | soft_build = soft_build, | |
| 78401 | 441 | export_files = export_files, | 
| 442 | build_hosts = build_hosts.toList) | |
| 75394 | 443 | } | 
| 79810 
4b23abde5d0b
more accurate progress.now(), notably for Database_Progress;
 wenzelm parents: 
79717diff
changeset | 444 | val stop_date = progress.now() | 
| 79819 | 445 | val elapsed_time = stop_date - progress.start | 
| 75394 | 446 | |
| 77545 | 447 |       progress.echo("\nFinished at " + Build_Log.print_date(stop_date), verbose = true)
 | 
| 62590 | 448 | |
| 75394 | 449 | val total_timing = | 
| 450 | results.sessions.iterator.map(a => results(a).timing).foldLeft(Timing.zero)(_ + _). | |
| 451 | copy(elapsed = elapsed_time) | |
| 452 | progress.echo(total_timing.message_resources) | |
| 62590 | 453 | |
| 75394 | 454 | sys.exit(results.rc) | 
| 455 | }) | |
| 68305 | 456 | |
| 457 | ||
| 77553 | 458 | |
| 78562 | 459 | /** build cluster management **/ | 
| 77557 | 460 | |
| 78219 | 461 | /* identified builds */ | 
| 462 | ||
| 78368 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 463 | def read_builds(build_database: Option[SQL.Database]): List[Build_Process.Build] = | 
| 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 464 |     build_database match {
 | 
| 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 465 | case None => Nil | 
| 78634 | 466 | case Some(db) => Build_Process.read_builds(db) | 
| 78219 | 467 | } | 
| 468 | ||
| 78368 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 469 | def print_builds(build_database: Option[SQL.Database], builds: List[Build_Process.Build]): String = | 
| 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 470 |   {
 | 
| 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 471 | val print_database = | 
| 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 472 |       build_database match {
 | 
| 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 473 | case None => "" | 
| 78634 | 474 | case Some(db) => " (database " + db + ")" | 
| 78368 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 475 | } | 
| 78640 | 476 | if (builds.isEmpty) "No build processes" + print_database | 
| 78641 | 477 | else "Build processes" + print_database + builds.map(build => "\n " + build.print).mkString | 
| 78368 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 478 | } | 
| 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 479 | |
| 78410 | 480 | def find_builds( | 
| 78368 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 481 | build_database: Option[SQL.Database], | 
| 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 482 | build_id: String, | 
| 78221 | 483 | builds: List[Build_Process.Build] | 
| 78410 | 484 |   ): Build_Process.Build = {
 | 
| 78368 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 485 |     (build_id, builds.length) match {
 | 
| 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 486 | case (UUID(_), _) if builds.exists(_.build_uuid == build_id) => | 
| 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 487 | builds.find(_.build_uuid == build_id).get | 
| 78410 | 488 |       case ("", 1) => builds.head
 | 
| 78368 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 489 |       case ("", 0) => error(print_builds(build_database, builds))
 | 
| 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 490 | case _ => | 
| 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 491 |         cat_error("Cannot identify build process " + quote(build_id),
 | 
| 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 492 | print_builds(build_database, builds)) | 
| 78219 | 493 | } | 
| 78410 | 494 | } | 
| 78219 | 495 | |
| 496 | ||
| 78562 | 497 | /* "isabelle build_process" */ | 
| 498 | ||
| 499 | def build_process( | |
| 500 | options: Options, | |
| 79676 
0cac7e3634d0
more explicit build_cluster flag to guard open_build_database server;
 wenzelm parents: 
79647diff
changeset | 501 | build_cluster: Boolean = false, | 
| 78562 | 502 | list_builds: Boolean = false, | 
| 78636 | 503 | remove_builds: Boolean = false, | 
| 504 | force: Boolean = false, | |
| 78562 | 505 | progress: Progress = new Progress | 
| 506 |   ): Unit = {
 | |
| 79707 | 507 | val engine = Engine(engine_name(options)) | 
| 508 | val store = engine.build_store(options, build_cluster = build_cluster) | |
| 78562 | 509 | |
| 510 |     using(store.open_server()) { server =>
 | |
| 511 |       using_optional(store.maybe_open_build_database(server = server)) { build_database =>
 | |
| 78636 | 512 | def print(builds: List[Build_Process.Build]): Unit = | 
| 513 | if (list_builds) progress.echo(print_builds(build_database, builds)) | |
| 78562 | 514 | |
| 78636 | 515 |         build_database match {
 | 
| 516 | case None => print(Nil) | |
| 79847 | 517 | case Some(db) if remove_builds && force => | 
| 518 |             db.transaction {
 | |
| 519 | val tables0 = | |
| 520 | ML_Heap.private_data.tables.list ::: | |
| 521 | Store.private_data.tables.list ::: | |
| 79873 | 522 | Database_Progress.private_data.tables.list ::: | 
| 79847 | 523 | Build_Process.private_data.tables.list | 
| 524 | val tables = tables0.filter(t => db.exists_table(t.name)).sortBy(_.name) | |
| 525 |               if (tables.nonEmpty) {
 | |
| 526 |                 progress.echo("Removing tables " + commas_quote(tables.map(_.name)) + " ...")
 | |
| 527 | db.execute_statement(SQL.MULTI(tables.map(db.destroy))) | |
| 528 | } | |
| 529 | } | |
| 78636 | 530 | case Some(db) => | 
| 531 |             Build_Process.private_data.transaction_lock(db, create = true, label = "build_process") {
 | |
| 532 | val builds = Build_Process.private_data.read_builds(db) | |
| 533 | print(builds) | |
| 79847 | 534 |               if (remove_builds) {
 | 
| 535 | val remove = builds.flatMap(_.active_build_uuid) | |
| 536 |                 if (remove.nonEmpty) {
 | |
| 78636 | 537 |                   progress.echo("Removing " + commas(remove) + " ...")
 | 
| 538 | Build_Process.private_data.remove_builds(db, remove) | |
| 539 | print(Build_Process.private_data.read_builds(db)) | |
| 540 | } | |
| 541 | } | |
| 542 | } | |
| 543 | } | |
| 78562 | 544 | } | 
| 545 | } | |
| 546 | } | |
| 547 | ||
| 548 |   val isabelle_tool2 = Isabelle_Tool("build_process", "manage session build process",
 | |
| 549 | Scala_Project.here, | |
| 550 |     { args =>
 | |
| 79676 
0cac7e3634d0
more explicit build_cluster flag to guard open_build_database server;
 wenzelm parents: 
79647diff
changeset | 551 | var build_cluster = false | 
| 78636 | 552 | var force = false | 
| 78562 | 553 | var list_builds = false | 
| 554 | var options = | |
| 79682 
1fa1b32b0379
build local log_db, with store/restore via optional database server;
 wenzelm parents: 
79679diff
changeset | 555 | Options.init(specs = | 
| 
1fa1b32b0379
build local log_db, with store/restore via optional database server;
 wenzelm parents: 
79679diff
changeset | 556 |           Options.Spec.ISABELLE_BUILD_OPTIONS ::: List(Options.Spec.make("build_database")))
 | 
| 78636 | 557 | var remove_builds = false | 
| 78562 | 558 | |
| 559 |       val getopts = Getopts("""
 | |
| 560 | Usage: isabelle build_process [OPTIONS] | |
| 561 | ||
| 562 | Options are: | |
| 79676 
0cac7e3634d0
more explicit build_cluster flag to guard open_build_database server;
 wenzelm parents: 
79647diff
changeset | 563 | -C build cluster mode (database server) | 
| 78636 | 564 | -f extra force for option -r | 
| 78562 | 565 | -l list build processes | 
| 566 | -o OPTION override Isabelle system OPTION (via NAME=VAL or NAME) | |
| 78636 | 567 | -r remove data from build processes: inactive processes (default) | 
| 568 | or all processes (option -f) | |
| 79700 | 569 | |
| 570 | Manage Isabelle build process, notably distributed build cluster (option -C). | |
| 78562 | 571 | """, | 
| 79679 
ba2c43592f35
proper treatment of "isabelle build_process -C" (amending 0cac7e3634d0);
 wenzelm parents: 
79676diff
changeset | 572 | "C" -> (_ => build_cluster = true), | 
| 78636 | 573 | "f" -> (_ => force = true), | 
| 78562 | 574 | "l" -> (_ => list_builds = true), | 
| 78636 | 575 | "o:" -> (arg => options = options + arg), | 
| 576 | "r" -> (_ => remove_builds = true)) | |
| 78562 | 577 | |
| 578 | val more_args = getopts(args) | |
| 579 | if (more_args.nonEmpty) getopts.usage() | |
| 580 | ||
| 581 | val progress = new Console_Progress() | |
| 582 | ||
| 79676 
0cac7e3634d0
more explicit build_cluster flag to guard open_build_database server;
 wenzelm parents: 
79647diff
changeset | 583 | build_process(options, build_cluster = build_cluster, list_builds = list_builds, | 
| 
0cac7e3634d0
more explicit build_cluster flag to guard open_build_database server;
 wenzelm parents: 
79647diff
changeset | 584 | remove_builds = remove_builds, force = force, progress = progress) | 
| 78562 | 585 | }) | 
| 586 | ||
| 587 | ||
| 588 | ||
| 589 | /* "isabelle build_worker" */ | |
| 77557 | 590 | |
| 78444 
3d1746a716fa
clarified signature: Build_Cluster.Session.build_context;
 wenzelm parents: 
78440diff
changeset | 591 | def build_worker_command( | 
| 
3d1746a716fa
clarified signature: Build_Cluster.Session.build_context;
 wenzelm parents: 
78440diff
changeset | 592 | host: Build_Cluster.Host, | 
| 
3d1746a716fa
clarified signature: Build_Cluster.Session.build_context;
 wenzelm parents: 
78440diff
changeset | 593 | ssh: SSH.System = SSH.Local, | 
| 78558 | 594 | build_options: List[Options.Spec] = Nil, | 
| 78444 
3d1746a716fa
clarified signature: Build_Cluster.Session.build_context;
 wenzelm parents: 
78440diff
changeset | 595 | build_id: String = "", | 
| 
3d1746a716fa
clarified signature: Build_Cluster.Session.build_context;
 wenzelm parents: 
78440diff
changeset | 596 | isabelle_home: Path = Path.current, | 
| 
3d1746a716fa
clarified signature: Build_Cluster.Session.build_context;
 wenzelm parents: 
78440diff
changeset | 597 | dirs: List[Path] = Nil, | 
| 78637 | 598 | quiet: Boolean = false, | 
| 78444 
3d1746a716fa
clarified signature: Build_Cluster.Session.build_context;
 wenzelm parents: 
78440diff
changeset | 599 | verbose: Boolean = false | 
| 
3d1746a716fa
clarified signature: Build_Cluster.Session.build_context;
 wenzelm parents: 
78440diff
changeset | 600 |   ): String = {
 | 
| 78916 | 601 |     val options = build_options ::: Options.Spec.eq("build_hostname", host.name) :: host.options
 | 
| 79624 | 602 | ssh.bash_path(Isabelle_Tool.exe(isabelle_home)) + " build_worker" + | 
| 78444 
3d1746a716fa
clarified signature: Build_Cluster.Session.build_context;
 wenzelm parents: 
78440diff
changeset | 603 | if_proper(build_id, " -B " + Bash.string(build_id)) + | 
| 
3d1746a716fa
clarified signature: Build_Cluster.Session.build_context;
 wenzelm parents: 
78440diff
changeset | 604 | dirs.map(dir => " -d " + ssh.bash_path(dir)).mkString + | 
| 78560 | 605 | if_proper(host.numa, " -N") + " -j" + host.jobs + | 
| 78915 
90756ad4d8d7
more accurate treatment of surrounding whitespace;
 wenzelm parents: 
78908diff
changeset | 606 | Options.Spec.bash_strings(options, bg = true) + | 
| 78637 | 607 | if_proper(quiet, " -q") + | 
| 78444 
3d1746a716fa
clarified signature: Build_Cluster.Session.build_context;
 wenzelm parents: 
78440diff
changeset | 608 | if_proper(verbose, " -v") | 
| 
3d1746a716fa
clarified signature: Build_Cluster.Session.build_context;
 wenzelm parents: 
78440diff
changeset | 609 | } | 
| 
3d1746a716fa
clarified signature: Build_Cluster.Session.build_context;
 wenzelm parents: 
78440diff
changeset | 610 | |
| 77557 | 611 | def build_worker( | 
| 612 | options: Options, | |
| 78410 | 613 | build_id: String = "", | 
| 77557 | 614 | progress: Progress = new Progress, | 
| 615 | dirs: List[Path] = Nil, | |
| 616 | numa_shuffling: Boolean = false, | |
| 79616 | 617 | max_jobs: Option[Int] = None | 
| 78568 | 618 |   ): Results = {
 | 
| 79707 | 619 | val engine = Engine(engine_name(options)) | 
| 620 | val store = engine.build_store(options, build_cluster = true) | |
| 77557 | 621 | val build_options = store.options | 
| 622 | ||
| 78372 | 623 |     using(store.open_server()) { server =>
 | 
| 624 |       using_optional(store.maybe_open_build_database(server = server)) { build_database =>
 | |
| 625 | val builds = read_builds(build_database) | |
| 78368 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 626 | |
| 78634 | 627 | val build_master = find_builds(build_database, build_id, builds.filter(_.active)) | 
| 78368 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 628 | |
| 80196 | 629 | val more_dirs = List(Path.ISABELLE_HOME + Sync.DIRS).filter(Sessions.is_session_dir(_)) | 
| 630 | ||
| 78562 | 631 | val sessions_structure = | 
| 80196 | 632 | Sessions.load_structure(build_options, dirs = more_dirs ::: dirs). | 
| 78562 | 633 | selection(Sessions.Selection(sessions = build_master.sessions)) | 
| 78219 | 634 | |
| 78562 | 635 | val build_deps = | 
| 636 | Sessions.deps(sessions_structure, progress = progress, inlined_files = true).check_errors | |
| 78219 | 637 | |
| 78562 | 638 | val build_context = | 
| 80196 | 639 | Context(store, build_deps, engine = engine, hostname = hostname(build_options), | 
| 640 | numa_shuffling = numa_shuffling, build_uuid = build_master.build_uuid, | |
| 641 | build_start = Some(build_master.start), jobs = max_jobs.getOrElse(1)) | |
| 78219 | 642 | |
| 79707 | 643 | engine.run_build_process(build_context, progress, server) | 
| 78368 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 644 | } | 
| 
6689b4c07bba
clarified signature: proper Scala function for command-line tool;
 wenzelm parents: 
78280diff
changeset | 645 | } | 
| 77557 | 646 | } | 
| 647 | ||
| 78562 | 648 |   val isabelle_tool3 = Isabelle_Tool("build_worker", "start worker for session build process",
 | 
| 77557 | 649 | Scala_Project.here, | 
| 650 |     { args =>
 | |
| 78219 | 651 | var build_id = "" | 
| 77557 | 652 | var numa_shuffling = false | 
| 78405 | 653 | val dirs = new mutable.ListBuffer[Path] | 
| 79616 | 654 | var max_jobs: Option[Int] = None | 
| 78219 | 655 | var options = | 
| 79682 
1fa1b32b0379
build local log_db, with store/restore via optional database server;
 wenzelm parents: 
79679diff
changeset | 656 | Options.init(specs = | 
| 
1fa1b32b0379
build local log_db, with store/restore via optional database server;
 wenzelm parents: 
79679diff
changeset | 657 |           Options.Spec.ISABELLE_BUILD_OPTIONS ::: List(Options.Spec.make("build_database")))
 | 
| 78637 | 658 | var quiet = false | 
| 77557 | 659 | var verbose = false | 
| 660 | ||
| 661 |       val getopts = Getopts("""
 | |
| 78219 | 662 | Usage: isabelle build_worker [OPTIONS] | 
| 77557 | 663 | |
| 664 | Options are: | |
| 78411 | 665 | -B UUID existing UUID for build process (default: from database) | 
| 77557 | 666 | -N cyclic shuffling of NUMA CPU nodes (performance tuning) | 
| 667 | -d DIR include session directory | |
| 668 | -j INT maximum number of parallel jobs (default 1) | |
| 669 | -o OPTION override Isabelle system OPTION (via NAME=VAL or NAME) | |
| 78637 | 670 | -q quiet mode: no progress | 
| 77557 | 671 | -v verbose | 
| 77647 | 672 | """, | 
| 78410 | 673 | "B:" -> (arg => build_id = arg), | 
| 77557 | 674 | "N" -> (_ => numa_shuffling = true), | 
| 78405 | 675 | "d:" -> (arg => dirs += Path.explode(arg)), | 
| 79616 | 676 | "j:" -> (arg => max_jobs = Some(Value.Nat.parse(arg))), | 
| 77557 | 677 | "o:" -> (arg => options = options + arg), | 
| 78637 | 678 | "q" -> (_ => quiet = true), | 
| 77557 | 679 | "v" -> (_ => verbose = true)) | 
| 680 | ||
| 681 | val more_args = getopts(args) | |
| 682 | if (more_args.nonEmpty) getopts.usage() | |
| 683 | ||
| 78637 | 684 | val progress = | 
| 685 |         if (quiet && verbose) new Progress { override def verbose: Boolean = true }
 | |
| 686 | else if (quiet) new Progress | |
| 687 | else new Console_Progress(verbose = verbose) | |
| 77557 | 688 | |
| 80527 
f6a9271cb177
ignore error code for "isabelle worker" (in contrast to eff08c3f89fe): avoid confusing "failed to work" messages via Build_Cluster.start;
 wenzelm parents: 
80480diff
changeset | 689 |       progress.interrupt_handler {
 | 
| 
f6a9271cb177
ignore error code for "isabelle worker" (in contrast to eff08c3f89fe): avoid confusing "failed to work" messages via Build_Cluster.start;
 wenzelm parents: 
80480diff
changeset | 690 | build_worker(options, | 
| 
f6a9271cb177
ignore error code for "isabelle worker" (in contrast to eff08c3f89fe): avoid confusing "failed to work" messages via Build_Cluster.start;
 wenzelm parents: 
80480diff
changeset | 691 | build_id = build_id, | 
| 
f6a9271cb177
ignore error code for "isabelle worker" (in contrast to eff08c3f89fe): avoid confusing "failed to work" messages via Build_Cluster.start;
 wenzelm parents: 
80480diff
changeset | 692 | progress = progress, | 
| 
f6a9271cb177
ignore error code for "isabelle worker" (in contrast to eff08c3f89fe): avoid confusing "failed to work" messages via Build_Cluster.start;
 wenzelm parents: 
80480diff
changeset | 693 | dirs = dirs.toList, | 
| 
f6a9271cb177
ignore error code for "isabelle worker" (in contrast to eff08c3f89fe): avoid confusing "failed to work" messages via Build_Cluster.start;
 wenzelm parents: 
80480diff
changeset | 694 | numa_shuffling = Host.numa_check(progress, numa_shuffling), | 
| 
f6a9271cb177
ignore error code for "isabelle worker" (in contrast to eff08c3f89fe): avoid confusing "failed to work" messages via Build_Cluster.start;
 wenzelm parents: 
80480diff
changeset | 695 | max_jobs = max_jobs) | 
| 
f6a9271cb177
ignore error code for "isabelle worker" (in contrast to eff08c3f89fe): avoid confusing "failed to work" messages via Build_Cluster.start;
 wenzelm parents: 
80480diff
changeset | 696 | } | 
| 77557 | 697 | }) | 
| 698 | ||
| 699 | ||
| 700 | ||
| 77563 | 701 | /** "isabelle build_log" **/ | 
| 77553 | 702 | |
| 703 | /* theory markup/messages from session database */ | |
| 704 | ||
| 705 | def read_theory( | |
| 706 | theory_context: Export.Theory_Context, | |
| 707 | unicode_symbols: Boolean = false | |
| 708 |   ): Option[Document.Snapshot] = {
 | |
| 80447 
325907d85977
minor performance tuning: allow recode operation during YXML parsing;
 wenzelm parents: 
80350diff
changeset | 709 | def decode(str: String): String = Symbol.output(unicode_symbols, str) | 
| 77553 | 710 | |
| 711 | def read(name: String): Export.Entry = theory_context(name, permissive = true) | |
| 712 | ||
| 713 | def read_xml(name: String): XML.Body = | |
| 80480 
972f7a4cdc0e
clarified YXML.Source: more direct support for String and Bytes, instead of CharSequence;
 wenzelm parents: 
80447diff
changeset | 714 | YXML.parse_body(read(name).bytes, recode = decode, cache = theory_context.cache) | 
| 77553 | 715 | |
| 78178 | 716 | def read_source_file(name: String): Store.Source_File = | 
| 77553 | 717 | theory_context.session_context.source_file(name) | 
| 718 | ||
| 719 |     for {
 | |
| 720 | id <- theory_context.document_id() | |
| 721 | (thy_file, blobs_files) <- theory_context.files(permissive = true) | |
| 722 | } | |
| 723 |     yield {
 | |
| 724 | val master_dir = | |
| 725 | Path.explode(Url.strip_base_name(thy_file).getOrElse( | |
| 726 |           error("Cannot determine theory master directory: " + quote(thy_file))))
 | |
| 727 | ||
| 728 | val blobs = | |
| 729 |         blobs_files.map { name =>
 | |
| 730 | val path = Path.explode(name) | |
| 731 | val src_path = File.relative_path(master_dir, path).getOrElse(path) | |
| 732 | ||
| 733 | val file = read_source_file(name) | |
| 734 | val bytes = file.bytes | |
| 80447 
325907d85977
minor performance tuning: allow recode operation during YXML parsing;
 wenzelm parents: 
80350diff
changeset | 735 | val text = decode(bytes.text) | 
| 77553 | 736 | val chunk = Symbol.Text_Chunk(text) | 
| 737 | ||
| 738 | Command.Blob(Document.Node.Name(name), src_path, Some((file.digest, chunk))) -> | |
| 739 | Document.Blobs.Item(bytes, text, chunk, changed = false) | |
| 740 | } | |
| 741 | ||
| 80447 
325907d85977
minor performance tuning: allow recode operation during YXML parsing;
 wenzelm parents: 
80350diff
changeset | 742 | val thy_source = decode(read_source_file(thy_file).bytes.text) | 
| 77553 | 743 | val thy_xml = read_xml(Export.MARKUP) | 
| 744 | val blobs_xml = | |
| 745 | for (i <- (1 to blobs.length).toList) | |
| 746 | yield read_xml(Export.MARKUP + i) | |
| 747 | ||
| 748 | val markups_index = Command.Markup_Index.make(blobs.map(_._1)) | |
| 749 | val markups = | |
| 750 | Command.Markups.make( | |
| 751 | for ((index, xml) <- markups_index.zip(thy_xml :: blobs_xml)) | |
| 752 | yield index -> Markup_Tree.from_XML(xml)) | |
| 753 | ||
| 754 | val results = | |
| 755 | Command.Results.make( | |
| 78592 | 756 | for (case elem@XML.Elem(Markup(_, Markup.Serial(i)), _) <- read_xml(Export.MESSAGES)) | 
| 77553 | 757 | yield i -> elem) | 
| 758 | ||
| 759 | val command = | |
| 760 | Command.unparsed(thy_source, theory = true, id = id, | |
| 761 | node_name = Document.Node.Name(thy_file, theory = theory_context.theory), | |
| 762 | blobs_info = Command.Blobs_Info.make(blobs), | |
| 763 | markups = markups, results = results) | |
| 764 | ||
| 765 | val doc_blobs = Document.Blobs.make(blobs) | |
| 766 | ||
| 767 | Document.State.init.snippet(command, doc_blobs) | |
| 768 | } | |
| 769 | } | |
| 770 | ||
| 771 | ||
| 772 | /* print messages */ | |
| 773 | ||
| 774 | def print_log( | |
| 775 | options: Options, | |
| 776 | sessions: List[String], | |
| 777 | theories: List[String] = Nil, | |
| 778 | message_head: List[Regex] = Nil, | |
| 779 | message_body: List[Regex] = Nil, | |
| 780 | progress: Progress = new Progress, | |
| 781 | margin: Double = Pretty.default_margin, | |
| 782 | breakgain: Double = Pretty.default_breakgain, | |
| 783 | metric: Pretty.Metric = Symbol.Metric, | |
| 784 | unicode_symbols: Boolean = false | |
| 785 |   ): Unit = {
 | |
| 78178 | 786 | val store = Store(options) | 
| 77553 | 787 | val session = new Session(options, Resources.bootstrap) | 
| 788 | ||
| 789 | def check(filter: List[Regex], make_string: => String): Boolean = | |
| 790 |       filter.isEmpty || {
 | |
| 80817 | 791 | val s = Protocol_Message.clean_output(make_string) | 
| 792 | filter.forall(r => r.findFirstIn(Protocol_Message.clean_output(s)).nonEmpty) | |
| 77553 | 793 | } | 
| 794 | ||
| 795 |     def print(session_name: String): Unit = {
 | |
| 796 |       using(Export.open_session_context0(store, session_name)) { session_context =>
 | |
| 797 | val result = | |
| 798 |           for {
 | |
| 799 | db <- session_context.session_db() | |
| 800 | theories = store.read_theories(db, session_name) | |
| 801 | errors = store.read_errors(db, session_name) | |
| 802 | info <- store.read_build(db, session_name) | |
| 803 | } yield (theories, errors, info.return_code) | |
| 804 |         result match {
 | |
| 805 | case None => store.error_database(session_name) | |
| 806 | case Some((used_theories, errors, rc)) => | |
| 807 |             theories.filterNot(used_theories.toSet) match {
 | |
| 808 | case Nil => | |
| 809 |               case bad => error("Unknown theories " + commas_quote(bad))
 | |
| 810 | } | |
| 811 | val print_theories = | |
| 812 | if (theories.isEmpty) used_theories else used_theories.filter(theories.toSet) | |
| 813 | ||
| 814 |             for (thy <- print_theories) {
 | |
| 815 | val thy_heading = "\nTheory " + quote(thy) + " (in " + session_name + ")" + ":" | |
| 816 | ||
| 78280 | 817 |               Build.read_theory(session_context.theory(thy), unicode_symbols = unicode_symbols) match {
 | 
| 77553 | 818 | case None => progress.echo(thy_heading + " MISSING") | 
| 819 | case Some(snapshot) => | |
| 820 | val rendering = new Rendering(snapshot, options, session) | |
| 821 | val messages = | |
| 822 | rendering.text_messages(Text.Range.full) | |
| 823 | .filter(message => progress.verbose || Protocol.is_exported(message.info)) | |
| 824 |                   if (messages.nonEmpty) {
 | |
| 825 | val line_document = Line.Document(snapshot.node.source) | |
| 826 | val buffer = new mutable.ListBuffer[String] | |
| 827 |                     for (Text.Info(range, elem) <- messages) {
 | |
| 828 | val line = line_document.position(range.start).line1 | |
| 829 | val pos = Position.Line_File(line, snapshot.node_name.node) | |
| 830 | def message_text: String = | |
| 831 | Protocol.message_text(elem, heading = true, pos = pos, | |
| 832 | margin = margin, breakgain = breakgain, metric = metric) | |
| 833 | val ok = | |
| 834 | check(message_head, Protocol.message_heading(elem, pos)) && | |
| 80807 | 835 | check(message_body, Pretty.unformatted_string_of(List(elem))) | 
| 77553 | 836 | if (ok) buffer += message_text | 
| 837 | } | |
| 838 |                     if (buffer.nonEmpty) {
 | |
| 839 | progress.echo(thy_heading) | |
| 840 | buffer.foreach(progress.echo(_)) | |
| 841 | } | |
| 842 | } | |
| 843 | } | |
| 844 | } | |
| 845 | ||
| 846 |             if (errors.nonEmpty) {
 | |
| 847 | val msg = Symbol.output(unicode_symbols, cat_lines(errors)) | |
| 848 |               progress.echo("\nBuild errors:\n" + Output.error_message_text(msg))
 | |
| 849 | } | |
| 850 |             if (rc != Process_Result.RC.ok) {
 | |
| 851 |               progress.echo("\n" + Process_Result.RC.print_long(rc))
 | |
| 852 | } | |
| 853 | } | |
| 854 | } | |
| 855 | } | |
| 856 | ||
| 857 | val errors = new mutable.ListBuffer[String] | |
| 858 |     for (session_name <- sessions) {
 | |
| 78705 
fde0b195cb7d
clarified signature: avoid association with potentially dangerous Exn.capture;
 wenzelm parents: 
78643diff
changeset | 859 |       Exn.result(print(session_name)) match {
 | 
| 77553 | 860 | case Exn.Res(_) => | 
| 861 | case Exn.Exn(exn) => errors += Exn.message(exn) | |
| 862 | } | |
| 863 | } | |
| 864 | if (errors.nonEmpty) error(cat_lines(errors.toList)) | |
| 865 | } | |
| 866 | ||
| 867 | ||
| 79629 | 868 | /* Isabelle tool wrapper */ | 
| 77553 | 869 | |
| 78562 | 870 |   val isabelle_tool4 = Isabelle_Tool("build_log", "print messages from session build database",
 | 
| 77553 | 871 | Scala_Project.here, | 
| 872 |     { args =>
 | |
| 873 | /* arguments */ | |
| 874 | ||
| 78405 | 875 | val message_head = new mutable.ListBuffer[Regex] | 
| 876 | val message_body = new mutable.ListBuffer[Regex] | |
| 77553 | 877 | var unicode_symbols = false | 
| 78405 | 878 | val theories = new mutable.ListBuffer[String] | 
| 77553 | 879 | var margin = Pretty.default_margin | 
| 880 | var options = Options.init() | |
| 881 | var verbose = false | |
| 882 | ||
| 883 |       val getopts = Getopts("""
 | |
| 77563 | 884 | Usage: isabelle build_log [OPTIONS] [SESSIONS ...] | 
| 77553 | 885 | |
| 886 | Options are: | |
| 887 | -H REGEX filter messages by matching against head | |
| 888 | -M REGEX filter messages by matching against body | |
| 889 | -T NAME restrict to given theories (multiple options possible) | |
| 890 | -U output Unicode symbols | |
| 891 | -m MARGIN margin for pretty printing (default: """ + margin + """) | |
| 892 | -o OPTION override Isabelle system OPTION (via NAME=VAL or NAME) | |
| 893 | -v print all messages, including information etc. | |
| 894 | ||
| 77554 
4465d9dff448
clarified terminology of "session build database", while "build database" is the one underlying Build_Process;
 wenzelm parents: 
77553diff
changeset | 895 | Print messages from the session build database of the given sessions, | 
| 
4465d9dff448
clarified terminology of "session build database", while "build database" is the one underlying Build_Process;
 wenzelm parents: 
77553diff
changeset | 896 | without any checks against current sources nor session structure: results | 
| 
4465d9dff448
clarified terminology of "session build database", while "build database" is the one underlying Build_Process;
 wenzelm parents: 
77553diff
changeset | 897 | from old sessions or failed builds can be printed as well. | 
| 77553 | 898 | |
| 899 | Multiple options -H and -M are conjunctive: all given patterns need to | |
| 900 | match. Patterns match any substring, but ^ or $ may be used to match the | |
| 901 | start or end explicitly. | |
| 902 | """, | |
| 78405 | 903 | "H:" -> (arg => message_head += arg.r), | 
| 904 | "M:" -> (arg => message_body += arg.r), | |
| 905 | "T:" -> (arg => theories += arg), | |
| 77553 | 906 | "U" -> (_ => unicode_symbols = true), | 
| 907 | "m:" -> (arg => margin = Value.Double.parse(arg)), | |
| 908 | "o:" -> (arg => options = options + arg), | |
| 909 | "v" -> (_ => verbose = true)) | |
| 910 | ||
| 911 | val sessions = getopts(args) | |
| 912 | ||
| 913 | val progress = new Console_Progress(verbose = verbose) | |
| 914 | ||
| 915 |       if (sessions.isEmpty) progress.echo_warning("No sessions to print")
 | |
| 916 |       else {
 | |
| 78405 | 917 | print_log(options, sessions, theories = theories.toList, message_head = message_head.toList, | 
| 918 | message_body = message_body.toList, margin = margin, progress = progress, | |
| 77553 | 919 | unicode_symbols = unicode_symbols) | 
| 920 | } | |
| 921 | }) | |
| 48276 | 922 | } |