author | wenzelm |
Mon, 25 Mar 2019 14:19:26 +0100 | |
changeset 69973 | a3e3be17dca5 |
parent 69787 | 60b5a4731695 |
child 69974 | 916726680a66 |
permissions | -rw-r--r-- |
66820 | 1 |
/* Title: Pure/Admin/afp.scala |
2 |
Author: Makarius |
|
3 |
||
4 |
Administrative support for the Archive of Formal Proofs. |
|
5 |
*/ |
|
6 |
||
7 |
package isabelle |
|
8 |
||
9 |
||
10 |
object AFP |
|
11 |
{ |
|
69787
60b5a4731695
clarified URL -- avoid odd certificate problem with api.media.atlassian.com;
wenzelm
parents:
69693
diff
changeset
|
12 |
val repos_source = "https://isabelle.sketis.net/repos/afp-devel" |
66854 | 13 |
|
69693 | 14 |
val groups: Map[String, String] = |
15 |
Map("large" -> "full 64-bit memory model or word arithmetic required", |
|
16 |
"slow" -> "CPU time much higher than 60min (on mid-range hardware)", |
|
17 |
"very_slow" -> "elapsed time of many hours (on high-end hardware)") |
|
18 |
||
19 |
def groups_bulky: List[String] = List("large", "slow") |
|
20 |
||
66824 | 21 |
def init(options: Options, base_dir: Path = Path.explode("$AFP_BASE")): AFP = |
22 |
new AFP(options, base_dir) |
|
66820 | 23 |
|
69973 | 24 |
|
25 |
/* metadata */ |
|
26 |
||
27 |
object Metadata |
|
28 |
{ |
|
29 |
private val Section = """^\[(\S+)\]\s*$""".r |
|
30 |
private val Property = """^(\S+)\s*=(.*)$""".r |
|
31 |
private val Extra_Line = """^\s+(.*)$""".r |
|
32 |
private val Blank_Line = """^\s*$""".r |
|
33 |
||
34 |
def read(metadata_file: Path): Map[String, Metadata] = |
|
35 |
{ |
|
36 |
var result = Map.empty[String, Metadata] |
|
37 |
var section = "" |
|
38 |
var props = List.empty[Properties.Entry] |
|
39 |
||
40 |
def flush() |
|
41 |
{ |
|
42 |
if (section != "") result += (section -> new Metadata(props.reverse)) |
|
43 |
section = "" |
|
44 |
props = Nil |
|
45 |
} |
|
46 |
||
47 |
for ((line, i) <- split_lines(File.read(metadata_file)).zipWithIndex) |
|
48 |
{ |
|
49 |
def err(msg: String): Nothing = |
|
50 |
error(msg + Position.here(Position.Line_File(i + 1, metadata_file.expand.implode))) |
|
51 |
||
52 |
line match { |
|
53 |
case Section(name) => flush(); section = name |
|
54 |
case Property(a, b) => |
|
55 |
if (section == "") err("Property without a section") |
|
56 |
props = (a -> b.trim) :: props |
|
57 |
case Extra_Line(line) => |
|
58 |
props match { |
|
59 |
case Nil => err("Extra line without a property") |
|
60 |
case (a, b) :: rest => props = (a, b + "\n" + line.trim) :: rest |
|
61 |
} |
|
62 |
case Blank_Line() => |
|
63 |
case _ => err("Bad input") |
|
64 |
} |
|
65 |
} |
|
66 |
||
67 |
flush() |
|
68 |
result |
|
69 |
} |
|
70 |
} |
|
71 |
||
72 |
class Metadata private[AFP](properties: Properties.T) |
|
73 |
{ |
|
74 |
override def toString: String = |
|
75 |
properties.map({ case (a, b) => a + "=" + b }).mkString("Metadata(", ", ", ")") |
|
76 |
||
77 |
def is_empty: Boolean = properties.isEmpty |
|
78 |
} |
|
79 |
||
80 |
||
81 |
/* entries */ |
|
82 |
||
83 |
sealed case class Entry(name: String, metadata: Metadata, sessions: List[String]) |
|
66820 | 84 |
} |
85 |
||
66824 | 86 |
class AFP private(options: Options, val base_dir: Path) |
66820 | 87 |
{ |
66821 | 88 |
override def toString: String = base_dir.expand.toString |
89 |
||
66820 | 90 |
val main_dir: Path = base_dir + Path.explode("thys") |
91 |
||
66821 | 92 |
|
69973 | 93 |
/* entries with metadata and sessions */ |
66821 | 94 |
|
66820 | 95 |
val entries: List[AFP.Entry] = |
69973 | 96 |
{ |
97 |
val entry_metadata = AFP.Metadata.read(base_dir + Path.explode("metadata/metadata")) |
|
98 |
||
99 |
val entries = |
|
100 |
(for (name <- Sessions.parse_roots(main_dir + Sessions.ROOTS)) yield { |
|
101 |
val metadata = |
|
102 |
entry_metadata.getOrElse(name, error("Entry without metadata: " + quote(name))) |
|
103 |
val sessions = |
|
104 |
Sessions.parse_root_entries(main_dir + Path.explode(name) + Sessions.ROOT).map(_.name) |
|
105 |
AFP.Entry(name, metadata, sessions) |
|
106 |
}).sortBy(_.name) |
|
107 |
||
108 |
val entry_set = entries.iterator.map(_.name).toSet |
|
109 |
val extra_metadata = |
|
110 |
(for ((name, _) <- entry_metadata.iterator if !entry_set(name)) yield name).toList.sorted |
|
111 |
if (extra_metadata.nonEmpty) error("Meta data without entry: " + commas_quote(extra_metadata)) |
|
112 |
||
113 |
entries |
|
114 |
} |
|
66820 | 115 |
|
66821 | 116 |
val sessions: List[String] = entries.flatMap(_.sessions) |
66824 | 117 |
|
67052 | 118 |
val sessions_structure: Sessions.Structure = |
67026 | 119 |
Sessions.load_structure(options, dirs = List(main_dir)). |
67025
961285f581e6
clarifified selection: always wrt. build_graph structure;
wenzelm
parents:
66861
diff
changeset
|
120 |
selection(Sessions.Selection(sessions = sessions.toList)) |
66821 | 121 |
|
122 |
||
66824 | 123 |
/* dependency graph */ |
66821 | 124 |
|
66824 | 125 |
private def sessions_deps(entry: AFP.Entry): List[String] = |
126 |
entry.sessions.flatMap(sessions_structure.imports_graph.imm_preds(_)).distinct.sorted |
|
127 |
||
66852
d20a668b394e
entries_graph requires acyclic graph, but lazy val allows forming the AFP object nonetheless;
wenzelm
parents:
66832
diff
changeset
|
128 |
lazy val entries_graph: Graph[String, Unit] = |
66821 | 129 |
{ |
130 |
val session_entries = |
|
66831
29ea2b900a05
tuned: each session has at most one defining entry;
wenzelm
parents:
66824
diff
changeset
|
131 |
(Map.empty[String, String] /: entries) { |
29ea2b900a05
tuned: each session has at most one defining entry;
wenzelm
parents:
66824
diff
changeset
|
132 |
case (m1, e) => (m1 /: e.sessions) { case (m2, s) => m2 + (s -> e.name) } |
66821 | 133 |
} |
66831
29ea2b900a05
tuned: each session has at most one defining entry;
wenzelm
parents:
66824
diff
changeset
|
134 |
(Graph.empty[String, Unit] /: entries) { case (g, entry) => |
29ea2b900a05
tuned: each session has at most one defining entry;
wenzelm
parents:
66824
diff
changeset
|
135 |
val e1 = entry.name |
66832 | 136 |
(g.default_node(e1, ()) /: sessions_deps(entry)) { case (g1, s) => |
137 |
(g1 /: session_entries.get(s).filterNot(_ == e1)) { case (g2, e2) => |
|
66852
d20a668b394e
entries_graph requires acyclic graph, but lazy val allows forming the AFP object nonetheless;
wenzelm
parents:
66832
diff
changeset
|
138 |
try { g2.default_node(e2, ()).add_edge_acyclic(e2, e1) } |
d20a668b394e
entries_graph requires acyclic graph, but lazy val allows forming the AFP object nonetheless;
wenzelm
parents:
66832
diff
changeset
|
139 |
catch { |
d20a668b394e
entries_graph requires acyclic graph, but lazy val allows forming the AFP object nonetheless;
wenzelm
parents:
66832
diff
changeset
|
140 |
case exn: Graph.Cycles[_] => |
d20a668b394e
entries_graph requires acyclic graph, but lazy val allows forming the AFP object nonetheless;
wenzelm
parents:
66832
diff
changeset
|
141 |
error(cat_lines(exn.cycles.map(cycle => |
d20a668b394e
entries_graph requires acyclic graph, but lazy val allows forming the AFP object nonetheless;
wenzelm
parents:
66832
diff
changeset
|
142 |
"Cyclic dependency of " + cycle.map(c => quote(c.toString)).mkString(" via ") + |
d20a668b394e
entries_graph requires acyclic graph, but lazy val allows forming the AFP object nonetheless;
wenzelm
parents:
66832
diff
changeset
|
143 |
" due to session " + quote(s)))) |
66832 | 144 |
} |
66821 | 145 |
} |
146 |
} |
|
66824 | 147 |
} |
66821 | 148 |
} |
149 |
||
66824 | 150 |
def entries_graph_display: Graph_Display.Graph = |
151 |
Graph_Display.make_graph(entries_graph) |
|
66823 | 152 |
|
66824 | 153 |
def entries_json_text: String = |
154 |
(for (entry <- entries.iterator) yield { |
|
155 |
val distrib_deps = sessions_deps(entry).filterNot(sessions.contains(_)) |
|
156 |
val afp_deps = entries_graph.imm_preds(entry.name).toList |
|
157 |
""" |
|
66821 | 158 |
{""" + JSON.Format(entry.name) + """: |
159 |
{"distrib_deps": """ + JSON.Format(distrib_deps) + """, |
|
160 |
"afp_deps": """ + JSON.Format(afp_deps) + """ |
|
161 |
} |
|
162 |
}""" |
|
66824 | 163 |
}).mkString("[", ", ", "\n]\n") |
66861
f6676691ef8a
partition AFP sessions according to structure, which happens to cut it roughly into equal parts;
wenzelm
parents:
66854
diff
changeset
|
164 |
|
f6676691ef8a
partition AFP sessions according to structure, which happens to cut it roughly into equal parts;
wenzelm
parents:
66854
diff
changeset
|
165 |
|
f6676691ef8a
partition AFP sessions according to structure, which happens to cut it roughly into equal parts;
wenzelm
parents:
66854
diff
changeset
|
166 |
/* partition sessions */ |
f6676691ef8a
partition AFP sessions according to structure, which happens to cut it roughly into equal parts;
wenzelm
parents:
66854
diff
changeset
|
167 |
|
67817 | 168 |
val force_partition1: List[String] = List("Category3", "HOL-ODE") |
67776 | 169 |
|
66861
f6676691ef8a
partition AFP sessions according to structure, which happens to cut it roughly into equal parts;
wenzelm
parents:
66854
diff
changeset
|
170 |
def partition(n: Int): List[String] = |
f6676691ef8a
partition AFP sessions according to structure, which happens to cut it roughly into equal parts;
wenzelm
parents:
66854
diff
changeset
|
171 |
n match { |
f6676691ef8a
partition AFP sessions according to structure, which happens to cut it roughly into equal parts;
wenzelm
parents:
66854
diff
changeset
|
172 |
case 0 => Nil |
f6676691ef8a
partition AFP sessions according to structure, which happens to cut it roughly into equal parts;
wenzelm
parents:
66854
diff
changeset
|
173 |
case 1 | 2 => |
f6676691ef8a
partition AFP sessions according to structure, which happens to cut it roughly into equal parts;
wenzelm
parents:
66854
diff
changeset
|
174 |
val graph = sessions_structure.build_graph.restrict(sessions.toSet) |
67817 | 175 |
val force_part1 = |
176 |
graph.all_preds(graph.all_succs(force_partition1.filter(graph.defined(_)))).toSet |
|
177 |
val (part1, part2) = graph.keys.partition(a => force_part1(a) || graph.is_isolated(a)) |
|
178 |
if (n == 1) part1 else part2 |
|
66861
f6676691ef8a
partition AFP sessions according to structure, which happens to cut it roughly into equal parts;
wenzelm
parents:
66854
diff
changeset
|
179 |
case _ => error("Bad AFP partition: " + n + " (should be 0, 1, 2)") |
f6676691ef8a
partition AFP sessions according to structure, which happens to cut it roughly into equal parts;
wenzelm
parents:
66854
diff
changeset
|
180 |
} |
66820 | 181 |
} |