38 name == root_name || name == "README" || name == "index" || name == "bib" |
37 name == root_name || name == "README" || name == "index" || name == "bib" |
39 |
38 |
40 |
39 |
41 /* ROOTS file format */ |
40 /* ROOTS file format */ |
42 |
41 |
43 class File_Format extends isabelle.File_Format |
42 class File_Format extends isabelle.File_Format { |
44 { |
|
45 val format_name: String = roots_name |
43 val format_name: String = roots_name |
46 val file_ext = "" |
44 val file_ext = "" |
47 |
45 |
48 override def detect(name: String): Boolean = |
46 override def detect(name: String): Boolean = |
49 Thy_Header.split_file_name(name) match { |
47 Thy_Header.split_file_name(name) match { |
73 known_loaded_files: Map[String, List[Path]] = Map.empty, |
71 known_loaded_files: Map[String, List[Path]] = Map.empty, |
74 overall_syntax: Outer_Syntax = Outer_Syntax.empty, |
72 overall_syntax: Outer_Syntax = Outer_Syntax.empty, |
75 imported_sources: List[(Path, SHA1.Digest)] = Nil, |
73 imported_sources: List[(Path, SHA1.Digest)] = Nil, |
76 sources: List[(Path, SHA1.Digest)] = Nil, |
74 sources: List[(Path, SHA1.Digest)] = Nil, |
77 session_graph_display: Graph_Display.Graph = Graph_Display.empty_graph, |
75 session_graph_display: Graph_Display.Graph = Graph_Display.empty_graph, |
78 errors: List[String] = Nil) |
76 errors: List[String] = Nil |
79 { |
77 ) { |
80 override def toString: String = |
78 override def toString: String = |
81 "Sessions.Base(loaded_theories = " + loaded_theories.size + |
79 "Sessions.Base(loaded_theories = " + loaded_theories.size + |
82 ", used_theories = " + used_theories.length + ")" |
80 ", used_theories = " + used_theories.length + ")" |
83 |
81 |
84 def theory_qualifier(name: String): String = |
82 def theory_qualifier(name: String): String = |
98 |
96 |
99 def node_syntax(nodes: Document.Nodes, name: Document.Node.Name): Outer_Syntax = |
97 def node_syntax(nodes: Document.Nodes, name: Document.Node.Name): Outer_Syntax = |
100 nodes(name).syntax orElse loaded_theory_syntax(name) getOrElse overall_syntax |
98 nodes(name).syntax orElse loaded_theory_syntax(name) getOrElse overall_syntax |
101 } |
99 } |
102 |
100 |
103 sealed case class Deps(sessions_structure: Structure, session_bases: Map[String, Base]) |
101 sealed case class Deps(sessions_structure: Structure, session_bases: Map[String, Base]) { |
104 { |
|
105 override def toString: String = "Sessions.Deps(" + sessions_structure + ")" |
102 override def toString: String = "Sessions.Deps(" + sessions_structure + ")" |
106 |
103 |
107 def is_empty: Boolean = session_bases.isEmpty |
104 def is_empty: Boolean = session_bases.isEmpty |
108 def apply(name: String): Base = session_bases(name) |
105 def apply(name: String): Base = session_bases(name) |
109 def get(name: String): Option[Base] = session_bases.get(name) |
106 def get(name: String): Option[Base] = session_bases.get(name) |
128 case errs => error(cat_lines(errs)) |
125 case errs => error(cat_lines(errs)) |
129 } |
126 } |
130 } |
127 } |
131 |
128 |
132 def deps(sessions_structure: Structure, |
129 def deps(sessions_structure: Structure, |
133 progress: Progress = new Progress, |
130 progress: Progress = new Progress, |
134 inlined_files: Boolean = false, |
131 inlined_files: Boolean = false, |
135 verbose: Boolean = false, |
132 verbose: Boolean = false, |
136 list_files: Boolean = false, |
133 list_files: Boolean = false, |
137 check_keywords: Set[String] = Set.empty): Deps = |
134 check_keywords: Set[String] = Set.empty |
138 { |
135 ): Deps = { |
139 var cache_sources = Map.empty[JFile, SHA1.Digest] |
136 var cache_sources = Map.empty[JFile, SHA1.Digest] |
140 def check_sources(paths: List[Path]): List[(Path, SHA1.Digest)] = |
137 def check_sources(paths: List[Path]): List[(Path, SHA1.Digest)] = { |
141 { |
|
142 for { |
138 for { |
143 path <- paths |
139 path <- paths |
144 file = path.file |
140 file = path.file |
145 if cache_sources.isDefinedAt(file) || file.isFile |
141 if cache_sources.isDefinedAt(file) || file.isFile |
146 } |
142 } |
202 if (check_keywords.nonEmpty) { |
198 if (check_keywords.nonEmpty) { |
203 Check_Keywords.check_keywords( |
199 Check_Keywords.check_keywords( |
204 progress, overall_syntax.keywords, check_keywords, theory_files) |
200 progress, overall_syntax.keywords, check_keywords, theory_files) |
205 } |
201 } |
206 |
202 |
207 val session_graph_display: Graph_Display.Graph = |
203 val session_graph_display: Graph_Display.Graph = { |
208 { |
|
209 def session_node(name: String): Graph_Display.Node = |
204 def session_node(name: String): Graph_Display.Node = |
210 Graph_Display.Node("[" + name + "]", "session." + name) |
205 Graph_Display.Node("[" + name + "]", "session." + name) |
211 |
206 |
212 def node(name: Document.Node.Name): Graph_Display.Node = |
207 def node(name: Document.Node.Name): Graph_Display.Node = { |
213 { |
|
214 val qualifier = deps_base.theory_qualifier(name) |
208 val qualifier = deps_base.theory_qualifier(name) |
215 if (qualifier == info.name) |
209 if (qualifier == info.name) |
216 Graph_Display.Node(name.theory_base_name, "theory." + name.theory) |
210 Graph_Display.Node(name.theory_base_name, "theory." + name.theory) |
217 else session_node(qualifier) |
211 else session_node(qualifier) |
218 } |
212 } |
249 dependencies.entries.iterator.map(entry => entry.name.theory -> entry). |
243 dependencies.entries.iterator.map(entry => entry.name.theory -> entry). |
250 foldLeft(deps_base.known_theories)(_ + _) |
244 foldLeft(deps_base.known_theories)(_ + _) |
251 |
245 |
252 val known_loaded_files = deps_base.known_loaded_files ++ loaded_files |
246 val known_loaded_files = deps_base.known_loaded_files ++ loaded_files |
253 |
247 |
254 val import_errors = |
248 val import_errors = { |
255 { |
|
256 val known_sessions = |
249 val known_sessions = |
257 sessions_structure.imports_requirements(List(session_name)).toSet |
250 sessions_structure.imports_requirements(List(session_name)).toSet |
258 for { |
251 for { |
259 name <- dependencies.theories |
252 name <- dependencies.theories |
260 qualifier = deps_base.theory_qualifier(name) |
253 qualifier = deps_base.theory_qualifier(name) |
370 sealed case class Base_Info( |
362 sealed case class Base_Info( |
371 session: String, |
363 session: String, |
372 sessions_structure: Structure, |
364 sessions_structure: Structure, |
373 errors: List[String], |
365 errors: List[String], |
374 base: Base, |
366 base: Base, |
375 infos: List[Info]) |
367 infos: List[Info] |
376 { |
368 ) { |
377 def check: Base_Info = if (errors.isEmpty) this else error(cat_lines(errors)) |
369 def check: Base_Info = if (errors.isEmpty) this else error(cat_lines(errors)) |
378 } |
370 } |
379 |
371 |
380 def base_info(options: Options, |
372 def base_info(options: Options, |
381 session: String, |
373 session: String, |
382 progress: Progress = new Progress, |
374 progress: Progress = new Progress, |
383 dirs: List[Path] = Nil, |
375 dirs: List[Path] = Nil, |
384 include_sessions: List[String] = Nil, |
376 include_sessions: List[String] = Nil, |
385 session_ancestor: Option[String] = None, |
377 session_ancestor: Option[String] = None, |
386 session_requirements: Boolean = false): Base_Info = |
378 session_requirements: Boolean = false |
387 { |
379 ): Base_Info = { |
388 val full_sessions = load_structure(options, dirs = dirs) |
380 val full_sessions = load_structure(options, dirs = dirs) |
389 |
381 |
390 val selected_sessions = |
382 val selected_sessions = |
391 full_sessions.selection(Selection(sessions = session :: session_ancestor.toList)) |
383 full_sessions.selection(Selection(sessions = session :: session_ancestor.toList)) |
392 val info = selected_sessions(session) |
384 val info = selected_sessions(session) |
472 theories: List[(Options, List[(String, Position.T)])], |
464 theories: List[(Options, List[(String, Position.T)])], |
473 global_theories: List[String], |
465 global_theories: List[String], |
474 document_theories: List[(String, Position.T)], |
466 document_theories: List[(String, Position.T)], |
475 document_files: List[(Path, Path)], |
467 document_files: List[(Path, Path)], |
476 export_files: List[(Path, Int, List[String])], |
468 export_files: List[(Path, Int, List[String])], |
477 meta_digest: SHA1.Digest) |
469 meta_digest: SHA1.Digest |
478 { |
470 ) { |
479 def chapter_session: String = chapter + "/" + name |
471 def chapter_session: String = chapter + "/" + name |
480 |
472 |
481 def relative_path(info1: Info): String = |
473 def relative_path(info1: Info): String = |
482 if (name == info1.name) "" |
474 if (name == info1.name) "" |
483 else if (chapter == info1.chapter) "../" + info1.name + "/" |
475 else if (chapter == info1.chapter) "../" + info1.name + "/" |
484 else "../../" + info1.chapter_session + "/" |
476 else "../../" + info1.chapter_session + "/" |
485 |
477 |
486 def deps: List[String] = parent.toList ::: imports |
478 def deps: List[String] = parent.toList ::: imports |
487 |
479 |
488 def deps_base(session_bases: String => Base): Base = |
480 def deps_base(session_bases: String => Base): Base = { |
489 { |
|
490 val parent_base = session_bases(parent.getOrElse("")) |
481 val parent_base = session_bases(parent.getOrElse("")) |
491 val imports_bases = imports.map(session_bases) |
482 val imports_bases = imports.map(session_bases) |
492 parent_base.copy( |
483 parent_base.copy( |
493 known_theories = |
484 known_theories = |
494 (for { |
485 (for { |
512 case "" | "false" => false |
503 case "" | "false" => false |
513 case "pdf" | "true" => true |
504 case "pdf" | "true" => true |
514 case doc => error("Bad document specification " + quote(doc)) |
505 case doc => error("Bad document specification " + quote(doc)) |
515 } |
506 } |
516 |
507 |
517 def document_variants: List[Document_Build.Document_Variant] = |
508 def document_variants: List[Document_Build.Document_Variant] = { |
518 { |
|
519 val variants = |
509 val variants = |
520 Library.space_explode(':', options.string("document_variants")). |
510 Library.space_explode(':', options.string("document_variants")). |
521 map(Document_Build.Document_Variant.parse) |
511 map(Document_Build.Document_Variant.parse) |
522 |
512 |
523 val dups = Library.duplicates(variants.map(_.name)) |
513 val dups = Library.duplicates(variants.map(_.name)) |
524 if (dups.nonEmpty) error("Duplicate document variants: " + commas_quote(dups)) |
514 if (dups.nonEmpty) error("Duplicate document variants: " + commas_quote(dups)) |
525 |
515 |
526 variants |
516 variants |
527 } |
517 } |
528 |
518 |
529 def documents: List[Document_Build.Document_Variant] = |
519 def documents: List[Document_Build.Document_Variant] = { |
530 { |
|
531 val variants = document_variants |
520 val variants = document_variants |
532 if (!document_enabled || document_files.isEmpty) Nil else variants |
521 if (!document_enabled || document_files.isEmpty) Nil else variants |
533 } |
522 } |
534 |
523 |
535 def document_output: Option[Path] = |
524 def document_output: Option[Path] = |
551 |
540 |
552 def is_afp: Boolean = chapter == AFP.chapter |
541 def is_afp: Boolean = chapter == AFP.chapter |
553 def is_afp_bulky: Boolean = is_afp && groups.exists(AFP.groups_bulky.contains) |
542 def is_afp_bulky: Boolean = is_afp && groups.exists(AFP.groups_bulky.contains) |
554 } |
543 } |
555 |
544 |
556 def make_info(options: Options, dir_selected: Boolean, dir: Path, chapter: String, |
545 def make_info( |
557 entry: Session_Entry): Info = |
546 options: Options, |
558 { |
547 dir_selected: Boolean, |
|
548 dir: Path, |
|
549 chapter: String, |
|
550 entry: Session_Entry |
|
551 ): Info = { |
559 try { |
552 try { |
560 val name = entry.name |
553 val name = entry.name |
561 |
554 |
562 if (exclude_session(name)) error("Bad session name") |
555 if (exclude_session(name)) error("Bad session name") |
563 if (is_pure(name) && entry.parent.isDefined) error("Illegal parent session") |
556 if (is_pure(name) && entry.parent.isDefined) error("Illegal parent session") |
627 all_sessions: Boolean = false, |
619 all_sessions: Boolean = false, |
628 base_sessions: List[String] = Nil, |
620 base_sessions: List[String] = Nil, |
629 exclude_session_groups: List[String] = Nil, |
621 exclude_session_groups: List[String] = Nil, |
630 exclude_sessions: List[String] = Nil, |
622 exclude_sessions: List[String] = Nil, |
631 session_groups: List[String] = Nil, |
623 session_groups: List[String] = Nil, |
632 sessions: List[String] = Nil) |
624 sessions: List[String] = Nil |
633 { |
625 ) { |
634 def ++ (other: Selection): Selection = |
626 def ++ (other: Selection): Selection = |
635 Selection( |
627 Selection( |
636 requirements = requirements || other.requirements, |
628 requirements = requirements || other.requirements, |
637 all_sessions = all_sessions || other.all_sessions, |
629 all_sessions = all_sessions || other.all_sessions, |
638 base_sessions = Library.merge(base_sessions, other.base_sessions), |
630 base_sessions = Library.merge(base_sessions, other.base_sessions), |
640 exclude_sessions = Library.merge(exclude_sessions, other.exclude_sessions), |
632 exclude_sessions = Library.merge(exclude_sessions, other.exclude_sessions), |
641 session_groups = Library.merge(session_groups, other.session_groups), |
633 session_groups = Library.merge(session_groups, other.session_groups), |
642 sessions = Library.merge(sessions, other.sessions)) |
634 sessions = Library.merge(sessions, other.sessions)) |
643 } |
635 } |
644 |
636 |
645 object Structure |
637 object Structure { |
646 { |
|
647 val empty: Structure = make(Nil) |
638 val empty: Structure = make(Nil) |
648 |
639 |
649 def make(infos: List[Info]): Structure = |
640 def make(infos: List[Info]): Structure = { |
650 { |
641 def add_edges( |
651 def add_edges(graph: Graph[String, Info], kind: String, edges: Info => Iterable[String]) |
642 graph: Graph[String, Info], |
652 : Graph[String, Info] = |
643 kind: String, |
653 { |
644 edges: Info => Iterable[String] |
654 def add_edge(pos: Position.T, name: String, g: Graph[String, Info], parent: String) = |
645 ) : Graph[String, Info] = { |
655 { |
646 def add_edge(pos: Position.T, name: String, g: Graph[String, Info], parent: String) = { |
656 if (!g.defined(parent)) |
647 if (!g.defined(parent)) |
657 error("Bad " + kind + " session " + quote(parent) + " for " + |
648 error("Bad " + kind + " session " + quote(parent) + " for " + |
658 quote(name) + Position.here(pos)) |
649 quote(name) + Position.here(pos)) |
659 |
650 |
660 try { g.add_edge_acyclic(parent, name) } |
651 try { g.add_edge_acyclic(parent, name) } |
723 session_positions, session_directories, global_theories, build_graph, imports_graph) |
714 session_positions, session_directories, global_theories, build_graph, imports_graph) |
724 } |
715 } |
725 } |
716 } |
726 |
717 |
727 final class Structure private[Sessions]( |
718 final class Structure private[Sessions]( |
728 val session_positions: List[(String, Position.T)], |
719 val session_positions: List[(String, Position.T)], |
729 val session_directories: Map[JFile, String], |
720 val session_directories: Map[JFile, String], |
730 val global_theories: Map[String, String], |
721 val global_theories: Map[String, String], |
731 val build_graph: Graph[String, Info], |
722 val build_graph: Graph[String, Info], |
732 val imports_graph: Graph[String, Info]) |
723 val imports_graph: Graph[String, Info] |
733 { |
724 ) { |
734 sessions_structure => |
725 sessions_structure => |
735 |
726 |
736 def bootstrap: Base = |
727 def bootstrap: Base = |
737 Base( |
728 Base( |
738 session_directories = session_directories, |
729 session_directories = session_directories, |
757 def get(name: String): Option[Info] = if (defined(name)) Some(apply(name)) else None |
748 def get(name: String): Option[Info] = if (defined(name)) Some(apply(name)) else None |
758 |
749 |
759 def theory_qualifier(name: String): String = |
750 def theory_qualifier(name: String): String = |
760 global_theories.getOrElse(name, Long_Name.qualifier(name)) |
751 global_theories.getOrElse(name, Long_Name.qualifier(name)) |
761 |
752 |
762 def check_sessions(names: List[String]): Unit = |
753 def check_sessions(names: List[String]): Unit = { |
763 { |
|
764 val bad_sessions = SortedSet(names.filterNot(defined): _*).toList |
754 val bad_sessions = SortedSet(names.filterNot(defined): _*).toList |
765 if (bad_sessions.nonEmpty) |
755 if (bad_sessions.nonEmpty) |
766 error("Undefined session(s): " + commas_quote(bad_sessions)) |
756 error("Undefined session(s): " + commas_quote(bad_sessions)) |
767 } |
757 } |
768 |
758 |
769 def check_sessions(sel: Selection): Unit = |
759 def check_sessions(sel: Selection): Unit = |
770 check_sessions(sel.base_sessions ::: sel.exclude_sessions ::: sel.sessions) |
760 check_sessions(sel.base_sessions ::: sel.exclude_sessions ::: sel.sessions) |
771 |
761 |
772 private def selected(graph: Graph[String, Info], sel: Selection): List[String] = |
762 private def selected(graph: Graph[String, Info], sel: Selection): List[String] = { |
773 { |
|
774 check_sessions(sel) |
763 check_sessions(sel) |
775 |
764 |
776 val select_group = sel.session_groups.toSet |
765 val select_group = sel.session_groups.toSet |
777 val select_session = sel.sessions.toSet ++ imports_graph.all_succs(sel.base_sessions) |
766 val select_session = sel.sessions.toSet ++ imports_graph.all_succs(sel.base_sessions) |
778 |
767 |
787 |
776 |
788 if (sel.requirements) (graph.all_preds(selected0).toSet -- selected0).toList |
777 if (sel.requirements) (graph.all_preds(selected0).toSet -- selected0).toList |
789 else selected0 |
778 else selected0 |
790 } |
779 } |
791 |
780 |
792 def selection(sel: Selection): Structure = |
781 def selection(sel: Selection): Structure = { |
793 { |
|
794 check_sessions(sel) |
782 check_sessions(sel) |
795 |
783 |
796 val excluded = |
784 val excluded = { |
797 { |
|
798 val exclude_group = sel.exclude_session_groups.toSet |
785 val exclude_group = sel.exclude_session_groups.toSet |
799 val exclude_group_sessions = |
786 val exclude_group_sessions = |
800 (for { |
787 (for { |
801 (name, (info, _)) <- imports_graph.iterator |
788 (name, (info, _)) <- imports_graph.iterator |
802 if imports_graph.get_node(name).groups.exists(exclude_group) |
789 if imports_graph.get_node(name).groups.exists(exclude_group) |
803 } yield name).toList |
790 } yield name).toList |
804 imports_graph.all_succs(exclude_group_sessions ::: sel.exclude_sessions).toSet |
791 imports_graph.all_succs(exclude_group_sessions ::: sel.exclude_sessions).toSet |
805 } |
792 } |
806 |
793 |
807 def restrict(graph: Graph[String, Info]): Graph[String, Info] = |
794 def restrict(graph: Graph[String, Info]): Graph[String, Info] = { |
808 { |
|
809 val sessions = graph.all_preds(selected(graph, sel)).filterNot(excluded) |
795 val sessions = graph.all_preds(selected(graph, sel)).filterNot(excluded) |
810 graph.restrict(graph.all_preds(sessions).toSet) |
796 graph.restrict(graph.all_preds(sessions).toSet) |
811 } |
797 } |
812 |
798 |
813 new Structure( |
799 new Structure( |
820 def selection_deps( |
806 def selection_deps( |
821 selection: Selection, |
807 selection: Selection, |
822 progress: Progress = new Progress, |
808 progress: Progress = new Progress, |
823 loading_sessions: Boolean = false, |
809 loading_sessions: Boolean = false, |
824 inlined_files: Boolean = false, |
810 inlined_files: Boolean = false, |
825 verbose: Boolean = false): Deps = |
811 verbose: Boolean = false |
826 { |
812 ): Deps = { |
827 val deps = |
813 val deps = |
828 Sessions.deps(sessions_structure.selection(selection), |
814 Sessions.deps(sessions_structure.selection(selection), |
829 progress = progress, inlined_files = inlined_files, verbose = verbose) |
815 progress = progress, inlined_files = inlined_files, verbose = verbose) |
830 |
816 |
831 if (loading_sessions) { |
817 if (loading_sessions) { |
902 imports: List[String], |
888 imports: List[String], |
903 directories: List[String], |
889 directories: List[String], |
904 theories: List[(List[Options.Spec], List[((String, Position.T), Boolean)])], |
890 theories: List[(List[Options.Spec], List[((String, Position.T), Boolean)])], |
905 document_theories: List[(String, Position.T)], |
891 document_theories: List[(String, Position.T)], |
906 document_files: List[(String, String)], |
892 document_files: List[(String, String)], |
907 export_files: List[(String, Int, List[String])]) extends Entry |
893 export_files: List[(String, Int, List[String])] |
908 { |
894 ) extends Entry { |
909 def theories_no_position: List[(List[Options.Spec], List[(String, Boolean)])] = |
895 def theories_no_position: List[(List[Options.Spec], List[(String, Boolean)])] = |
910 theories.map({ case (a, b) => (a, b.map({ case ((c, _), d) => (c, d) })) }) |
896 theories.map({ case (a, b) => (a, b.map({ case ((c, _), d) => (c, d) })) }) |
911 def document_theories_no_position: List[String] = |
897 def document_theories_no_position: List[String] = |
912 document_theories.map(_._1) |
898 document_theories.map(_._1) |
913 } |
899 } |
914 |
900 |
915 private object Parser extends Options.Parser |
901 private object Parser extends Options.Parser { |
916 { |
902 private val chapter: Parser[Chapter] = { |
917 private val chapter: Parser[Chapter] = |
|
918 { |
|
919 val chapter_name = atom("chapter name", _.is_name) |
903 val chapter_name = atom("chapter name", _.is_name) |
920 |
904 |
921 command(CHAPTER) ~! chapter_name ^^ { case _ ~ a => Chapter(a) } |
905 command(CHAPTER) ~! chapter_name ^^ { case _ ~ a => Chapter(a) } |
922 } |
906 } |
923 |
907 |
924 private val session_entry: Parser[Session_Entry] = |
908 private val session_entry: Parser[Session_Entry] = { |
925 { |
|
926 val option = |
909 val option = |
927 option_name ~ opt($$$("=") ~! option_value ^^ { case _ ~ x => x }) ^^ |
910 option_name ~ opt($$$("=") ~! option_value ^^ { case _ ~ x => x }) ^^ |
928 { case x ~ y => (x, y) } |
911 { case x ~ y => (x, y) } |
929 val options = $$$("[") ~> rep1sep(option, $$$(",")) <~ $$$("]") |
912 val options = $$$("[") ~> rep1sep(option, $$$(",")) <~ $$$("]") |
930 |
913 |
967 rep(export_files)))) ^^ |
950 rep(export_files)))) ^^ |
968 { case _ ~ ((a, pos) ~ b ~ c ~ (_ ~ (d ~ e ~ f ~ g ~ h ~ i ~ j ~ k ~ l))) => |
951 { case _ ~ ((a, pos) ~ b ~ c ~ (_ ~ (d ~ e ~ f ~ g ~ h ~ i ~ j ~ k ~ l))) => |
969 Session_Entry(pos, a, b, c, d, e, f, g, h, i, j, k, l) } |
952 Session_Entry(pos, a, b, c, d, e, f, g, h, i, j, k, l) } |
970 } |
953 } |
971 |
954 |
972 def parse_root(path: Path): List[Entry] = |
955 def parse_root(path: Path): List[Entry] = { |
973 { |
|
974 val toks = Token.explode(root_syntax.keywords, File.read(path)) |
956 val toks = Token.explode(root_syntax.keywords, File.read(path)) |
975 val start = Token.Pos.file(path.implode) |
957 val start = Token.Pos.file(path.implode) |
976 |
958 |
977 parse_all(rep(chapter | session_entry), Token.reader(toks, start)) match { |
959 parse_all(rep(chapter | session_entry), Token.reader(toks, start)) match { |
978 case Success(result, _) => result |
960 case Success(result, _) => result |
985 |
967 |
986 def parse_root_entries(path: Path): List[Session_Entry] = |
968 def parse_root_entries(path: Path): List[Session_Entry] = |
987 for (entry <- Parser.parse_root(path) if entry.isInstanceOf[Session_Entry]) |
969 for (entry <- Parser.parse_root(path) if entry.isInstanceOf[Session_Entry]) |
988 yield entry.asInstanceOf[Session_Entry] |
970 yield entry.asInstanceOf[Session_Entry] |
989 |
971 |
990 def read_root(options: Options, select: Boolean, path: Path): List[Info] = |
972 def read_root(options: Options, select: Boolean, path: Path): List[Info] = { |
991 { |
|
992 var entry_chapter = UNSORTED |
973 var entry_chapter = UNSORTED |
993 val infos = new mutable.ListBuffer[Info] |
974 val infos = new mutable.ListBuffer[Info] |
994 parse_root(path).foreach { |
975 parse_root(path).foreach { |
995 case Chapter(name) => entry_chapter = name |
976 case Chapter(name) => entry_chapter = name |
996 case entry: Session_Entry => |
977 case entry: Session_Entry => |
997 infos += make_info(options, select, path.dir, entry_chapter, entry) |
978 infos += make_info(options, select, path.dir, entry_chapter, entry) |
998 } |
979 } |
999 infos.toList |
980 infos.toList |
1000 } |
981 } |
1001 |
982 |
1002 def parse_roots(roots: Path): List[String] = |
983 def parse_roots(roots: Path): List[String] = { |
1003 { |
|
1004 for { |
984 for { |
1005 line <- split_lines(File.read(roots)) |
985 line <- split_lines(File.read(roots)) |
1006 if !(line == "" || line.startsWith("#")) |
986 if !(line == "" || line.startsWith("#")) |
1007 } yield line |
987 } yield line |
1008 } |
988 } |
1015 |
995 |
1016 def check_session_dir(dir: Path): Path = |
996 def check_session_dir(dir: Path): Path = |
1017 if (is_session_dir(dir)) File.pwd() + dir.expand |
997 if (is_session_dir(dir)) File.pwd() + dir.expand |
1018 else error("Bad session root directory (missing ROOT or ROOTS): " + dir.expand.toString) |
998 else error("Bad session root directory (missing ROOT or ROOTS): " + dir.expand.toString) |
1019 |
999 |
1020 def directories(dirs: List[Path], select_dirs: List[Path]): List[(Boolean, Path)] = |
1000 def directories(dirs: List[Path], select_dirs: List[Path]): List[(Boolean, Path)] = { |
1021 { |
|
1022 val default_dirs = Components.directories().filter(is_session_dir) |
1001 val default_dirs = Components.directories().filter(is_session_dir) |
1023 for { (select, dir) <- (default_dirs ::: dirs).map((false, _)) ::: select_dirs.map((true, _)) } |
1002 for { (select, dir) <- (default_dirs ::: dirs).map((false, _)) ::: select_dirs.map((true, _)) } |
1024 yield (select, dir.canonical) |
1003 yield (select, dir.canonical) |
1025 } |
1004 } |
1026 |
1005 |
1027 def load_structure(options: Options, |
1006 def load_structure( |
|
1007 options: Options, |
1028 dirs: List[Path] = Nil, |
1008 dirs: List[Path] = Nil, |
1029 select_dirs: List[Path] = Nil, |
1009 select_dirs: List[Path] = Nil, |
1030 infos: List[Info] = Nil): Structure = |
1010 infos: List[Info] = Nil |
1031 { |
1011 ): Structure = { |
1032 def load_dir(select: Boolean, dir: Path): List[(Boolean, Path)] = |
1012 def load_dir(select: Boolean, dir: Path): List[(Boolean, Path)] = |
1033 load_root(select, dir) ::: load_roots(select, dir) |
1013 load_root(select, dir) ::: load_roots(select, dir) |
1034 |
1014 |
1035 def load_root(select: Boolean, dir: Path): List[(Boolean, Path)] = |
1015 def load_root(select: Boolean, dir: Path): List[(Boolean, Path)] = { |
1036 { |
|
1037 val root = dir + ROOT |
1016 val root = dir + ROOT |
1038 if (root.is_file) List((select, root)) else Nil |
1017 if (root.is_file) List((select, root)) else Nil |
1039 } |
1018 } |
1040 |
1019 |
1041 def load_roots(select: Boolean, dir: Path): List[(Boolean, Path)] = |
1020 def load_roots(select: Boolean, dir: Path): List[(Boolean, Path)] = { |
1042 { |
|
1043 val roots = dir + ROOTS |
1021 val roots = dir + ROOTS |
1044 if (roots.is_file) { |
1022 if (roots.is_file) { |
1045 for { |
1023 for { |
1046 entry <- parse_roots(roots) |
1024 entry <- parse_roots(roots) |
1047 dir1 = |
1025 dir1 = |
1077 |
1055 |
1078 |
1056 |
1079 /* Isabelle tool wrapper */ |
1057 /* Isabelle tool wrapper */ |
1080 |
1058 |
1081 val isabelle_tool = Isabelle_Tool("sessions", "explore structure of Isabelle sessions", |
1059 val isabelle_tool = Isabelle_Tool("sessions", "explore structure of Isabelle sessions", |
1082 Scala_Project.here, args => |
1060 Scala_Project.here, args => { |
1083 { |
|
1084 var base_sessions: List[String] = Nil |
1061 var base_sessions: List[String] = Nil |
1085 var select_dirs: List[Path] = Nil |
1062 var select_dirs: List[Path] = Nil |
1086 var requirements = false |
1063 var requirements = false |
1087 var exclude_session_groups: List[String] = Nil |
1064 var exclude_session_groups: List[String] = Nil |
1088 var all_sessions = false |
1065 var all_sessions = false |
1135 |
1112 |
1136 /** heap file with SHA1 digest **/ |
1113 /** heap file with SHA1 digest **/ |
1137 |
1114 |
1138 private val sha1_prefix = "SHA1:" |
1115 private val sha1_prefix = "SHA1:" |
1139 |
1116 |
1140 def read_heap_digest(heap: Path): Option[String] = |
1117 def read_heap_digest(heap: Path): Option[String] = { |
1141 { |
|
1142 if (heap.is_file) { |
1118 if (heap.is_file) { |
1143 using(FileChannel.open(heap.java_path, StandardOpenOption.READ))(file => |
1119 using(FileChannel.open(heap.java_path, StandardOpenOption.READ))(file => { |
1144 { |
|
1145 val len = file.size |
1120 val len = file.size |
1146 val n = sha1_prefix.length + SHA1.digest_length |
1121 val n = sha1_prefix.length + SHA1.digest_length |
1147 if (len >= n) { |
1122 if (len >= n) { |
1148 file.position(len - n) |
1123 file.position(len - n) |
1149 |
1124 |
1208 val table = SQL.Table("isabelle_session_info", build_log_columns ::: build_columns) |
1182 val table = SQL.Table("isabelle_session_info", build_log_columns ::: build_columns) |
1209 } |
1183 } |
1210 |
1184 |
1211 class Database_Context private[Sessions]( |
1185 class Database_Context private[Sessions]( |
1212 val store: Sessions.Store, |
1186 val store: Sessions.Store, |
1213 database_server: Option[SQL.Database]) extends AutoCloseable |
1187 database_server: Option[SQL.Database] |
1214 { |
1188 ) extends AutoCloseable { |
1215 def cache: Term.Cache = store.cache |
1189 def cache: Term.Cache = store.cache |
1216 |
1190 |
1217 def close(): Unit = database_server.foreach(_.close()) |
1191 def close(): Unit = database_server.foreach(_.close()) |
1218 |
1192 |
1219 def output_database[A](session: String)(f: SQL.Database => A): A = |
1193 def output_database[A](session: String)(f: SQL.Database => A): A = |
1231 case None => None |
1205 case None => None |
1232 } |
1206 } |
1233 } |
1207 } |
1234 |
1208 |
1235 def read_export( |
1209 def read_export( |
1236 sessions: List[String], theory_name: String, name: String): Option[Export.Entry] = |
1210 sessions: List[String], |
1237 { |
1211 theory_name: String, |
|
1212 name: String |
|
1213 ): Option[Export.Entry] = { |
1238 val attempts = |
1214 val attempts = |
1239 database_server match { |
1215 database_server match { |
1240 case Some(db) => |
1216 case Some(db) => |
1241 sessions.view.map(session_name => |
1217 sessions.view.map(session_name => |
1242 Export.read_entry(db, store.cache, session_name, theory_name, name)) |
1218 Export.read_entry(db, store.cache, session_name, theory_name, name)) |
1254 def get_export( |
1230 def get_export( |
1255 session_hierarchy: List[String], theory_name: String, name: String): Export.Entry = |
1231 session_hierarchy: List[String], theory_name: String, name: String): Export.Entry = |
1256 read_export(session_hierarchy, theory_name, name) getOrElse |
1232 read_export(session_hierarchy, theory_name, name) getOrElse |
1257 Export.empty_entry(theory_name, name) |
1233 Export.empty_entry(theory_name, name) |
1258 |
1234 |
1259 override def toString: String = |
1235 override def toString: String = { |
1260 { |
|
1261 val s = |
1236 val s = |
1262 database_server match { |
1237 database_server match { |
1263 case Some(db) => db.toString |
1238 case Some(db) => db.toString |
1264 case None => "input_dirs = " + store.input_dirs.map(_.absolute).mkString(", ") |
1239 case None => "input_dirs = " + store.input_dirs.map(_.absolute).mkString(", ") |
1265 } |
1240 } |
1345 ssh_close = true) |
1319 ssh_close = true) |
1346 |
1320 |
1347 def open_database_context(): Database_Context = |
1321 def open_database_context(): Database_Context = |
1348 new Database_Context(store, if (database_server) Some(open_database_server()) else None) |
1322 new Database_Context(store, if (database_server) Some(open_database_server()) else None) |
1349 |
1323 |
1350 def try_open_database(name: String, output: Boolean = false): Option[SQL.Database] = |
1324 def try_open_database(name: String, output: Boolean = false): Option[SQL.Database] = { |
1351 { |
|
1352 def check(db: SQL.Database): Option[SQL.Database] = |
1325 def check(db: SQL.Database): Option[SQL.Database] = |
1353 if (output || session_info_exists(db)) Some(db) else { db.close(); None } |
1326 if (output || session_info_exists(db)) Some(db) else { db.close(); None } |
1354 |
1327 |
1355 if (database_server) check(open_database_server()) |
1328 if (database_server) check(open_database_server()) |
1356 else if (output) Some(SQLite.open_database(output_database(name))) |
1329 else if (output) Some(SQLite.open_database(output_database(name))) |
1365 |
1338 |
1366 def open_database(name: String, output: Boolean = false): SQL.Database = |
1339 def open_database(name: String, output: Boolean = false): SQL.Database = |
1367 try_open_database(name, output = output) getOrElse |
1340 try_open_database(name, output = output) getOrElse |
1368 error("Missing build database for session " + quote(name)) |
1341 error("Missing build database for session " + quote(name)) |
1369 |
1342 |
1370 def clean_output(name: String): (Boolean, Boolean) = |
1343 def clean_output(name: String): (Boolean, Boolean) = { |
1371 { |
|
1372 val relevant_db = |
1344 val relevant_db = |
1373 database_server && |
1345 database_server && { |
1374 { |
|
1375 try_open_database(name) match { |
1346 try_open_database(name) match { |
1376 case Some(db) => |
1347 case Some(db) => |
1377 try { |
1348 try { |
1378 db.transaction { |
1349 db.transaction { |
1379 val relevant_db = session_info_defined(db, name) |
1350 val relevant_db = session_info_defined(db, name) |
1401 |
1372 |
1402 /* SQL database content */ |
1373 /* SQL database content */ |
1403 |
1374 |
1404 def read_bytes(db: SQL.Database, name: String, column: SQL.Column): Bytes = |
1375 def read_bytes(db: SQL.Database, name: String, column: SQL.Column): Bytes = |
1405 db.using_statement(Session_Info.table.select(List(column), |
1376 db.using_statement(Session_Info.table.select(List(column), |
1406 Session_Info.session_name.where_equal(name)))(stmt => |
1377 Session_Info.session_name.where_equal(name)))(stmt => { |
1407 { |
|
1408 val res = stmt.execute_query() |
1378 val res = stmt.execute_query() |
1409 if (!res.next()) Bytes.empty else res.bytes(column) |
1379 if (!res.next()) Bytes.empty else res.bytes(column) |
1410 }) |
1380 }) |
1411 |
1381 |
1412 def read_properties(db: SQL.Database, name: String, column: SQL.Column): List[Properties.T] = |
1382 def read_properties(db: SQL.Database, name: String, column: SQL.Column): List[Properties.T] = |
1413 Properties.uncompress(read_bytes(db, name, column), cache = cache) |
1383 Properties.uncompress(read_bytes(db, name, column), cache = cache) |
1414 |
1384 |
1415 |
1385 |
1416 /* session info */ |
1386 /* session info */ |
1417 |
1387 |
1418 def init_session_info(db: SQL.Database, name: String): Unit = |
1388 def init_session_info(db: SQL.Database, name: String): Unit = { |
1419 { |
|
1420 db.transaction { |
1389 db.transaction { |
1421 db.create_table(Session_Info.table) |
1390 db.create_table(Session_Info.table) |
1422 db.using_statement( |
1391 db.using_statement( |
1423 Session_Info.table.delete(Session_Info.session_name.where_equal(name)))(_.execute()) |
1392 Session_Info.table.delete(Session_Info.session_name.where_equal(name)))(_.execute()) |
1424 |
1393 |
1431 Document_Build.Data.table.delete( |
1400 Document_Build.Data.table.delete( |
1432 Document_Build.Data.session_name.where_equal(name)))(_.execute()) |
1401 Document_Build.Data.session_name.where_equal(name)))(_.execute()) |
1433 } |
1402 } |
1434 } |
1403 } |
1435 |
1404 |
1436 def session_info_exists(db: SQL.Database): Boolean = |
1405 def session_info_exists(db: SQL.Database): Boolean = { |
1437 { |
|
1438 val tables = db.tables |
1406 val tables = db.tables |
1439 tables.contains(Session_Info.table.name) && |
1407 tables.contains(Session_Info.table.name) && |
1440 tables.contains(Export.Data.table.name) |
1408 tables.contains(Export.Data.table.name) |
1441 } |
1409 } |
1442 |
1410 |
1443 def session_info_defined(db: SQL.Database, name: String): Boolean = |
1411 def session_info_defined(db: SQL.Database, name: String): Boolean = |
1444 db.transaction { |
1412 db.transaction { |
1445 session_info_exists(db) && |
1413 session_info_exists(db) && { |
1446 { |
|
1447 db.using_statement( |
1414 db.using_statement( |
1448 Session_Info.table.select(List(Session_Info.session_name), |
1415 Session_Info.table.select(List(Session_Info.session_name), |
1449 Session_Info.session_name.where_equal(name)))(stmt => stmt.execute_query().next()) |
1416 Session_Info.session_name.where_equal(name)))(stmt => stmt.execute_query().next()) |
1450 } |
1417 } |
1451 } |
1418 } |
1452 |
1419 |
1453 def write_session_info( |
1420 def write_session_info( |
1454 db: SQL.Database, |
1421 db: SQL.Database, |
1455 name: String, |
1422 name: String, |
1456 build_log: Build_Log.Session_Info, |
1423 build_log: Build_Log.Session_Info, |
1457 build: Build.Session_Info): Unit = |
1424 build: Build.Session_Info |
1458 { |
1425 ): Unit = { |
1459 db.transaction { |
1426 db.transaction { |
1460 db.using_statement(Session_Info.table.insert())(stmt => |
1427 db.using_statement(Session_Info.table.insert())(stmt => { |
1461 { |
|
1462 stmt.string(1) = name |
1428 stmt.string(1) = name |
1463 stmt.bytes(2) = Properties.encode(build_log.session_timing) |
1429 stmt.bytes(2) = Properties.encode(build_log.session_timing) |
1464 stmt.bytes(3) = Properties.compress(build_log.command_timings, cache = cache.xz) |
1430 stmt.bytes(3) = Properties.compress(build_log.command_timings, cache = cache.xz) |
1465 stmt.bytes(4) = Properties.compress(build_log.theory_timings, cache = cache.xz) |
1431 stmt.bytes(4) = Properties.compress(build_log.theory_timings, cache = cache.xz) |
1466 stmt.bytes(5) = Properties.compress(build_log.ml_statistics, cache = cache.xz) |
1432 stmt.bytes(5) = Properties.compress(build_log.ml_statistics, cache = cache.xz) |
1494 read_theory_timings(db, name).flatMap(Markup.Name.unapply) |
1460 read_theory_timings(db, name).flatMap(Markup.Name.unapply) |
1495 |
1461 |
1496 def read_errors(db: SQL.Database, name: String): List[String] = |
1462 def read_errors(db: SQL.Database, name: String): List[String] = |
1497 Build_Log.uncompress_errors(read_bytes(db, name, Session_Info.errors), cache = cache) |
1463 Build_Log.uncompress_errors(read_bytes(db, name, Session_Info.errors), cache = cache) |
1498 |
1464 |
1499 def read_build(db: SQL.Database, name: String): Option[Build.Session_Info] = |
1465 def read_build(db: SQL.Database, name: String): Option[Build.Session_Info] = { |
1500 { |
|
1501 if (db.tables.contains(Session_Info.table.name)) { |
1466 if (db.tables.contains(Session_Info.table.name)) { |
1502 db.using_statement(Session_Info.table.select(Session_Info.build_columns, |
1467 db.using_statement(Session_Info.table.select(Session_Info.build_columns, |
1503 Session_Info.session_name.where_equal(name)))(stmt => |
1468 Session_Info.session_name.where_equal(name)))(stmt => { |
1504 { |
|
1505 val res = stmt.execute_query() |
1469 val res = stmt.execute_query() |
1506 if (!res.next()) None |
1470 if (!res.next()) None |
1507 else { |
1471 else { |
1508 Some( |
1472 Some( |
1509 Build.Session_Info( |
1473 Build.Session_Info( |