19 val body = SQL.Column.bytes("body") |
24 val body = SQL.Column.bytes("body") |
20 |
25 |
21 val table = |
26 val table = |
22 SQL.Table("isabelle_exports", List(session_name, theory_name, name, compressed, body)) |
27 SQL.Table("isabelle_exports", List(session_name, theory_name, name, compressed, body)) |
23 |
28 |
24 def where_equal(session_name: String, theory_name: String): SQL.Source = |
29 def where_equal(session_name: String, theory_name: String = "", name: String = ""): SQL.Source = |
25 "WHERE " + Data.session_name.equal(session_name) + |
30 "WHERE " + Data.session_name.equal(session_name) + |
26 " AND " + Data.theory_name.equal(theory_name) |
31 (if (theory_name == "") "" else " AND " + Data.theory_name.equal(theory_name)) + |
|
32 (if (name == "") "" else " AND " + Data.name.equal(name)) |
|
33 } |
|
34 |
|
35 def read_name(db: SQL.Database, session_name: String, theory_name: String, name: String): Boolean = |
|
36 { |
|
37 val select = |
|
38 Data.table.select(List(Data.name), Data.where_equal(session_name, theory_name, name)) |
|
39 db.using_statement(select)(stmt => stmt.execute_query().next()) |
27 } |
40 } |
28 |
41 |
29 def read_names(db: SQL.Database, session_name: String, theory_name: String): List[String] = |
42 def read_names(db: SQL.Database, session_name: String, theory_name: String): List[String] = |
30 { |
43 { |
31 val select = Data.table.select(List(Data.name), Data.where_equal(session_name, theory_name)) |
44 val select = Data.table.select(List(Data.name), Data.where_equal(session_name, theory_name)) |
32 db.using_statement(select)(stmt => |
45 db.using_statement(select)(stmt => |
33 stmt.execute_query().iterator(res => res.string(Data.name)).toList) |
46 stmt.execute_query().iterator(res => res.string(Data.name)).toList) |
34 } |
47 } |
35 |
48 |
36 def read_name(db: SQL.Database, session_name: String, theory_name: String, name: String): Boolean = |
49 def read_theory_names(db: SQL.Database, session_name: String): List[(String, String)] = |
37 { |
50 { |
38 val select = |
51 val select = Data.table.select(List(Data.theory_name, Data.name), Data.where_equal(session_name)) |
39 Data.table.select(List(Data.name), |
52 db.using_statement(select)(stmt => |
40 Data.where_equal(session_name, theory_name) + " AND " + Data.name.equal(name)) |
53 stmt.execute_query().iterator(res => |
41 db.using_statement(select)(stmt => stmt.execute_query().next()) |
54 (res.string(Data.theory_name), res.string(Data.name))).toList) |
42 } |
55 } |
43 |
56 |
44 def message(msg: String, theory_name: String, name: String): String = |
57 def message(msg: String, theory_name: String, name: String): String = |
45 msg + " " + quote(name) + " for theory " + quote(theory_name) |
58 msg + " " + quote(name) + " for theory " + quote(theory_name) |
|
59 |
|
60 def compound_name(a: String, b: String): String = a + ":" + b |
46 |
61 |
47 sealed case class Entry( |
62 sealed case class Entry( |
48 session_name: String, |
63 session_name: String, |
49 theory_name: String, |
64 theory_name: String, |
50 name: String, |
65 name: String, |
51 compressed: Boolean, |
66 compressed: Boolean, |
52 body: Future[Bytes]) |
67 body: Future[Bytes]) |
53 { |
68 { |
54 override def toString: String = theory_name + ":" + name |
69 override def toString: String = compound_name(theory_name, name) |
55 |
70 |
56 def write(db: SQL.Database) |
71 def write(db: SQL.Database) |
57 { |
72 { |
58 val bytes = body.join |
73 val bytes = body.join |
59 db.using_statement(Data.table.insert())(stmt => |
74 db.using_statement(Data.table.insert())(stmt => |
64 stmt.bool(4) = compressed |
79 stmt.bool(4) = compressed |
65 stmt.bytes(5) = bytes |
80 stmt.bytes(5) = bytes |
66 stmt.execute() |
81 stmt.execute() |
67 }) |
82 }) |
68 } |
83 } |
|
84 |
|
85 def body_uncompressed: Bytes = |
|
86 if (compressed) body.join.uncompress() else body.join |
|
87 } |
|
88 |
|
89 def make_regex(pattern: String): Regex = |
|
90 { |
|
91 @tailrec def make(result: List[String], depth: Int, chs: List[Char]): Regex = |
|
92 chs match { |
|
93 case '*' :: '*' :: rest => make("[^:]*" :: result, depth, rest) |
|
94 case '*' :: rest => make("[^:/]*" :: result, depth, rest) |
|
95 case '?' :: rest => make("[^:/]" :: result, depth, rest) |
|
96 case '\\' :: c :: rest => make(("\\" + c) :: result, depth, rest) |
|
97 case '{' :: rest => make("(" :: result, depth + 1, rest) |
|
98 case ',' :: rest if depth > 0 => make("|" :: result, depth, rest) |
|
99 case '}' :: rest if depth > 0 => make(")" :: result, depth - 1, rest) |
|
100 case c :: rest if ".+()".contains(c) => make(("\\" + c) :: result, depth, rest) |
|
101 case c :: rest => make(c.toString :: result, depth, rest) |
|
102 case Nil => result.reverse.mkString.r |
|
103 } |
|
104 make(Nil, 0, pattern.toList) |
69 } |
105 } |
70 |
106 |
71 def make_entry(session_name: String, args: Markup.Export.Args, body: Bytes): Entry = |
107 def make_entry(session_name: String, args: Markup.Export.Args, body: Bytes): Entry = |
72 { |
108 { |
73 Entry(session_name, args.theory_name, args.name, args.compress, |
109 Entry(session_name, args.theory_name, args.name, args.compress, |
76 |
112 |
77 def read_entry(db: SQL.Database, session_name: String, theory_name: String, name: String): Entry = |
113 def read_entry(db: SQL.Database, session_name: String, theory_name: String, name: String): Entry = |
78 { |
114 { |
79 val select = |
115 val select = |
80 Data.table.select(List(Data.compressed, Data.body), |
116 Data.table.select(List(Data.compressed, Data.body), |
81 Data.where_equal(session_name, theory_name) + " AND " + Data.name.equal(name)) |
117 Data.where_equal(session_name, theory_name, name)) |
82 db.using_statement(select)(stmt => |
118 db.using_statement(select)(stmt => |
83 { |
119 { |
84 val res = stmt.execute_query() |
120 val res = stmt.execute_query() |
85 if (res.next()) { |
121 if (res.next()) { |
86 val compressed = res.bool(Data.compressed) |
122 val compressed = res.bool(Data.compressed) |
124 consumer.shutdown() |
160 consumer.shutdown() |
125 if (close) db.close() |
161 if (close) db.close() |
126 export_errors.value.reverse |
162 export_errors.value.reverse |
127 } |
163 } |
128 } |
164 } |
|
165 |
|
166 |
|
167 /* Isabelle tool wrapper */ |
|
168 |
|
169 val default_export_dir = Path.explode("export") |
|
170 |
|
171 val isabelle_tool = Isabelle_Tool("export", "retrieve theory exports", args => |
|
172 { |
|
173 /* arguments */ |
|
174 |
|
175 var export_dir = default_export_dir |
|
176 var dirs: List[Path] = Nil |
|
177 var export_list = false |
|
178 var no_build = false |
|
179 var options = Options.init() |
|
180 var system_mode = false |
|
181 var export_pattern = "" |
|
182 |
|
183 val getopts = Getopts(""" |
|
184 Usage: isabelle export [OPTIONS] SESSION |
|
185 |
|
186 Options are: |
|
187 -D DIR target directory for exported files (default: """ + default_export_dir + """) |
|
188 -d DIR include session directory |
|
189 -l list exports |
|
190 -n no build of session |
|
191 -o OPTION override Isabelle system OPTION (via NAME=VAL or NAME) |
|
192 -s system build mode for session image |
|
193 -x PATTERN extract files matching pattern (e.g. "*:**" for all) |
|
194 |
|
195 List or export theory exports for SESSION: named blobs produced by |
|
196 isabelle build. Option -l or -x is required. |
|
197 |
|
198 The PATTERN language resembles glob patterns in the shell, with ? and * |
|
199 (both excluding ":" and "/"), ** (excluding ":"), and [abc] or [^abc], |
|
200 and variants {pattern1,pattern2,pattern3}. |
|
201 """, |
|
202 "D:" -> (arg => export_dir = Path.explode(arg)), |
|
203 "d:" -> (arg => dirs = dirs ::: List(Path.explode(arg))), |
|
204 "l" -> (_ => export_list = true), |
|
205 "n" -> (_ => no_build = true), |
|
206 "o:" -> (arg => options = options + arg), |
|
207 "s" -> (_ => system_mode = true), |
|
208 "x:" -> (arg => export_pattern = arg)) |
|
209 |
|
210 val more_args = getopts(args) |
|
211 val session_name = |
|
212 more_args match { |
|
213 case List(session_name) if export_list || export_pattern != "" => session_name |
|
214 case _ => getopts.usage() |
|
215 } |
|
216 |
|
217 |
|
218 /* build */ |
|
219 |
|
220 val progress = new Console_Progress() |
|
221 |
|
222 if (!no_build && |
|
223 !Build.build(options, no_build = true, dirs = dirs, system_mode = system_mode, |
|
224 sessions = List(session_name)).ok) |
|
225 { |
|
226 progress.echo("Build started for Isabelle/" + session_name + " ...") |
|
227 progress.interrupt_handler { |
|
228 val res = |
|
229 Build.build(options, progress = progress, dirs = dirs, system_mode = system_mode, |
|
230 sessions = List(session_name)) |
|
231 if (!res.ok) sys.exit(res.rc) |
|
232 } |
|
233 } |
|
234 |
|
235 |
|
236 /* database */ |
|
237 |
|
238 val store = Sessions.store(system_mode) |
|
239 val database = |
|
240 store.find_database(session_name) match { |
|
241 case None => error("Missing database for session " + quote(session_name)) |
|
242 case Some(database) => database |
|
243 } |
|
244 |
|
245 using(SQLite.open_database(database))(db => |
|
246 { |
|
247 db.transaction { |
|
248 val export_names = read_theory_names(db, session_name) |
|
249 |
|
250 // list |
|
251 if (export_list) { |
|
252 (for ((theory_name, name) <- export_names) yield compound_name(theory_name, name)). |
|
253 sorted.foreach(Output.writeln(_, stdout = true)) |
|
254 } |
|
255 |
|
256 // export |
|
257 if (export_pattern != "") { |
|
258 val regex = make_regex(export_pattern) |
|
259 for { |
|
260 (theory_name, name) <- export_names |
|
261 if regex.pattern.matcher(compound_name(theory_name, name)).matches |
|
262 } { |
|
263 val entry = read_entry(db, session_name, theory_name, name) |
|
264 val path = export_dir + Path.basic(theory_name) + Path.explode(name) |
|
265 |
|
266 progress.echo("exporting " + path) |
|
267 Isabelle_System.mkdirs(path.dir) |
|
268 Bytes.write(path, entry.body_uncompressed) |
|
269 } |
|
270 } |
|
271 } |
|
272 }) |
|
273 }) |
129 } |
274 } |