191 def pide_document(snapshot: Document.Snapshot): XML.Body = |
188 def pide_document(snapshot: Document.Snapshot): XML.Body = |
192 make_html(snapshot.markup_to_XML(Text.Range.full, document_elements)) |
189 make_html(snapshot.markup_to_XML(Text.Range.full, document_elements)) |
193 |
190 |
194 |
191 |
195 |
192 |
196 /** make document source **/ |
193 /** build documents **/ |
197 |
194 |
198 def write_tex_index(dir: Path, names: List[Document.Node.Name]) |
195 val session_tex_path = Path.explode("session.tex") |
199 { |
196 val session_graph_path = Path.explode("session_graph.pdf") |
200 val path = dir + Path.explode("session.tex") |
197 |
201 val text = names.map(name => "\\input{" + name.theory_base_name + ".tex}\n\n").mkString |
198 def tex_name(name: Document.Node.Name): String = name.theory_base_name + ".tex" |
202 File.write(path, text) |
199 def document_tex_name(name: Document.Node.Name): String = "document/" + tex_name(name) |
203 } |
200 |
204 |
201 def isabelletags(tags: List[String]): String = |
205 |
202 Library.terminate_lines( |
206 |
|
207 /** build document **/ |
|
208 |
|
209 val document_format = "pdf" |
|
210 |
|
211 val default_document_name = "document" |
|
212 def default_document_dir(name: String): Path = Path.explode("output") + Path.basic(name) |
|
213 |
|
214 def document_tags(tags: List[String]): String = |
|
215 { |
|
216 cat_lines( |
|
217 tags.map(tag => |
203 tags.map(tag => |
218 tag.toList match { |
204 tag.toList match { |
219 case '/' :: cs => "\\isafoldtag{" + cs.mkString + "}" |
205 case '/' :: cs => "\\isafoldtag{" + cs.mkString + "}" |
220 case '-' :: cs => "\\isadroptag{" + cs.mkString + "}" |
206 case '-' :: cs => "\\isadroptag{" + cs.mkString + "}" |
221 case '+' :: cs => "\\isakeeptag{" + cs.mkString + "}" |
207 case '+' :: cs => "\\isakeeptag{" + cs.mkString + "}" |
222 case cs => "\\isakeeptag{" + cs.mkString + "}" |
208 case cs => "\\isakeeptag{" + cs.mkString + "}" |
223 })) + "\n" |
209 })) |
224 } |
210 |
225 |
211 def build_documents( |
226 def build_document( |
212 session: String, |
227 document_name: String = default_document_name, |
213 deps: Sessions.Deps, |
228 document_dir: Option[Path] = None, |
214 store: Sessions.Store, |
229 tags: List[String] = Nil) |
215 progress: Progress = new Progress, |
230 { |
216 verbose: Boolean = false, |
231 val document_target = Path.parent + Path.explode(document_name).ext(document_format) |
217 verbose_latex: Boolean = false): List[(String, Bytes)] = |
232 |
218 { |
233 val dir = document_dir getOrElse default_document_dir(document_name) |
219 /* session info */ |
234 if (!dir.is_dir) error("Bad document output directory " + dir) |
220 |
235 |
221 val info = deps.sessions_structure(session) |
236 val root_name = |
222 val options = info.options |
|
223 val base = deps(session) |
|
224 val graph_pdf = graphview.Graph_File.make_pdf(options, base.session_graph_display) |
|
225 |
|
226 |
|
227 /* prepare document directory */ |
|
228 |
|
229 def prepare_dir(dir: Path, doc_name: String, doc_tags: List[String]): (Path, String) = |
237 { |
230 { |
238 val root1 = "root_" + document_name |
231 val doc_dir = dir + Path.basic(doc_name) |
239 if ((dir + Path.explode(root1).ext("tex")).is_file) root1 else "root" |
232 Isabelle_System.make_directory(doc_dir) |
240 } |
233 |
241 |
234 Isabelle_System.bash("isabelle latex -o sty", cwd = doc_dir.file).check |
242 |
235 File.write(doc_dir + Path.explode("isabelletags.sty"), isabelletags(doc_tags)) |
243 /* bash scripts */ |
236 for ((base_dir, src) <- info.document_files) File.copy_base(info.dir + base_dir, src, doc_dir) |
244 |
237 Bytes.write(doc_dir + session_graph_path, graph_pdf) |
245 def root_bash(ext: String): String = Bash.string(root_name + "." + ext) |
238 |
246 |
239 File.write(doc_dir + session_tex_path, |
247 def latex_bash(fmt: String, ext: String = "tex"): String = |
240 Library.terminate_lines( |
248 "isabelle latex -o " + Bash.string(fmt) + " " + Bash.string(root_name + "." + ext) |
241 base.session_theories.map(name => "\\input{" + tex_name(name) + "}"))) |
249 |
242 |
250 def bash(script: String): Process_Result = |
243 using(store.open_database(session))(db => |
251 Isabelle_System.bash(script, cwd = dir.file) |
244 for (name <- base.session_theories) { |
252 |
245 val tex = |
253 |
246 Export.read_entry(db, session, name.theory, document_tex_name(name)) match { |
254 /* prepare document */ |
247 case Some(entry) => entry.uncompressed(cache = store.xz_cache) |
255 |
248 case None => Bytes.empty |
256 File.write(dir + Path.explode("isabelletags.sty"), document_tags(tags)) |
249 } |
257 |
250 Bytes.write(doc_dir + Path.basic(tex_name(name)), tex) |
258 List("log", "blg").foreach(ext => (dir + Path.explode(root_name).ext(ext)).file.delete) |
251 }) |
259 |
252 |
260 val result = |
253 val root1 = "root_" + doc_name |
261 if ((dir + Path.explode("build")).is_file) { |
254 val root = if ((doc_dir + Path.explode(root1).tex).is_file) root1 else "root" |
262 bash("./build " + Bash.string(document_format) + " " + Bash.string(document_name)) |
255 |
|
256 (doc_dir, root) |
|
257 } |
|
258 |
|
259 |
|
260 /* produce documents */ |
|
261 |
|
262 val doc_output = info.document_output |
|
263 |
|
264 val docs = |
|
265 for ((doc_name, doc_tags) <- info.documents) |
|
266 yield { |
|
267 Isabelle_System.with_tmp_dir("document")(tmp_dir => |
|
268 { |
|
269 val (doc_dir, root) = prepare_dir(tmp_dir, doc_name, doc_tags) |
|
270 doc_output.foreach(prepare_dir(_, doc_name, doc_tags)) |
|
271 |
|
272 |
|
273 // bash scripts |
|
274 |
|
275 def root_bash(ext: String): String = Bash.string(root + "." + ext) |
|
276 |
|
277 def latex_bash(fmt: String = "pdf", ext: String = "tex"): String = |
|
278 "isabelle latex -o " + Bash.string(fmt) + " " + Bash.string(root + "." + ext) |
|
279 |
|
280 def bash(items: String*): Process_Result = |
|
281 progress.bash(items.mkString(" && "), cwd = doc_dir.file, echo = verbose_latex) |
|
282 |
|
283 |
|
284 // prepare document |
|
285 |
|
286 List("log", "blg").foreach(ext => (doc_dir + Path.explode(root).ext(ext)).file.delete) |
|
287 |
|
288 val result = |
|
289 if ((doc_dir + Path.explode("build")).is_file) { |
|
290 bash("./build pdf " + Bash.string(doc_name)) |
|
291 } |
|
292 else { |
|
293 bash( |
|
294 latex_bash("sty"), |
|
295 latex_bash(), |
|
296 "{ [ ! -f " + root_bash("bib") + " ] || " + latex_bash("bbl") + "; }", |
|
297 "{ [ ! -f " + root_bash("idx") + " ] || " + latex_bash("idx") + "; }", |
|
298 latex_bash(), |
|
299 latex_bash()) |
|
300 } |
|
301 |
|
302 |
|
303 // result |
|
304 |
|
305 val root_pdf = Path.basic(root).pdf |
|
306 val result_path = doc_dir + root_pdf |
|
307 |
|
308 if (!result.ok) { |
|
309 cat_error( |
|
310 Library.trim_line(result.err), |
|
311 cat_lines(Latex.latex_errors(doc_dir, root) ::: Bibtex.bibtex_errors(doc_dir, root)), |
|
312 "Failed to build document " + quote(doc_name)) |
|
313 } |
|
314 else if (!result_path.is_file) { |
|
315 error("Bad document result: expected to find " + root_pdf) |
|
316 } |
|
317 else doc_name -> Bytes.read(result_path) |
|
318 }) |
263 } |
319 } |
264 else { |
320 |
265 bash( |
321 def output(echo: Boolean, dir: Path) |
266 List( |
322 { |
267 latex_bash("sty"), |
323 Isabelle_System.make_directory(dir) |
268 latex_bash(document_format), |
324 for ((name, pdf) <- docs) { |
269 "{ [ ! -f " + root_bash("bib") + " ] || " + latex_bash("bbl") + "; }", |
325 val path = dir + Path.basic(name).pdf |
270 "{ [ ! -f " + root_bash("idx") + " ] || " + latex_bash("idx") + "; }", |
326 Bytes.write(path, pdf) |
271 latex_bash(document_format), |
327 if (echo) progress.echo_document(path) |
272 latex_bash(document_format)).mkString(" && ")) |
|
273 } |
328 } |
274 |
329 } |
275 |
330 |
276 /* result */ |
331 if (info.options.bool("browser_info") || doc_output.isEmpty) { |
277 |
332 output(verbose, store.browser_info + Path.basic(info.chapter) + Path.basic(session)) |
278 if (!result.ok) { |
333 } |
279 cat_error( |
334 |
280 Library.trim_line(result.err), |
335 doc_output.foreach(output(true, _)) |
281 cat_lines(Latex.latex_errors(dir, root_name) ::: Bibtex.bibtex_errors(dir, root_name)), |
336 |
282 "Failed to build document in " + File.path(dir.absolute_file)) |
337 docs |
283 } |
|
284 |
|
285 bash("if [ -f " + root_bash(document_format) + " ]; then cp -f " + |
|
286 root_bash(document_format) + " " + File.bash_path(document_target) + "; fi").check |
|
287 } |
338 } |
288 |
339 |
289 |
340 |
290 /* Isabelle tool wrapper */ |
341 /* Isabelle tool wrapper */ |
291 |
342 |
292 val isabelle_tool = |
343 val isabelle_tool = |
293 Isabelle_Tool("document", "prepare theory session document", args => |
344 Isabelle_Tool("document", "prepare session theory document", args => |
294 { |
345 { |
295 var document_dir: Option[Path] = None |
346 var verbose_latex = false |
296 var document_name = default_document_name |
347 var dirs: List[Path] = Nil |
297 var tags: List[String] = Nil |
348 var no_build = false |
298 |
349 var options = Options.init() |
299 val getopts = Getopts(""" |
350 var verbose_build = false |
300 Usage: isabelle document [OPTIONS] |
351 |
|
352 val getopts = Getopts( |
|
353 """ |
|
354 Usage: isabelle document [OPTIONS] SESSION |
301 |
355 |
302 Options are: |
356 Options are: |
303 -d DIR document output directory (default """ + |
357 -O set option "document_output", relative to current directory |
304 default_document_dir(default_document_name) + """) |
358 -V verbose latex |
305 -n NAME document name (default """ + quote(default_document_name) + """) |
359 -d DIR include session directory |
306 -t TAGS markup for tagged regions |
360 -n no build of session |
307 |
361 -o OPTION override Isabelle system OPTION (via NAME=VAL or NAME) |
308 Prepare the theory session document in document directory, producing the |
362 -v verbose build |
309 specified output format. |
363 |
|
364 Prepare the theory document of a session. |
310 """, |
365 """, |
311 "d:" -> (arg => document_dir = Some(Path.explode(arg))), |
366 "O:" -> (arg => options += ("document_output=" + Path.explode(arg).absolute.implode)), |
312 "n:" -> (arg => document_name = arg), |
367 "V" -> (_ => verbose_latex = true), |
313 "t:" -> (arg => tags = space_explode(',', arg))) |
368 "d:" -> (arg => dirs = dirs ::: List(Path.explode(arg))), |
314 |
369 "n" -> (_ => no_build = true), |
315 val more_args = getopts(args) |
370 "o:" -> (arg => options = options + arg), |
316 if (more_args.nonEmpty) getopts.usage() |
371 "v" -> (_ => verbose_build = true)) |
317 |
372 |
318 try { |
373 val more_args = getopts(args) |
319 build_document(document_dir = document_dir, document_name = document_name, tags = tags) |
374 val session = |
320 } |
375 more_args match { |
321 catch { case ERROR(msg) => Output.writeln(msg); sys.exit(2) } |
376 case List(a) => a |
322 }) |
377 case _ => getopts.usage() |
|
378 } |
|
379 |
|
380 val progress = new Console_Progress(verbose = verbose_build) |
|
381 val store = Sessions.store(options) |
|
382 |
|
383 progress.interrupt_handler { |
|
384 if (!no_build) { |
|
385 val res = |
|
386 Build.build(options, selection = Sessions.Selection.session(session), |
|
387 dirs = dirs, progress = progress, verbose = verbose_build) |
|
388 if (!res.ok) error("Failed to build session " + quote(session)) |
|
389 } |
|
390 |
|
391 val deps = |
|
392 Sessions.load_structure(options + "document=pdf", dirs = dirs). |
|
393 selection_deps(Sessions.Selection.session(session)) |
|
394 |
|
395 build_documents(session, deps, store, progress = progress, |
|
396 verbose = true, verbose_latex = verbose_latex) |
|
397 } |
|
398 }) |
323 } |
399 } |