src/Pure/Thy/present.scala
changeset 72574 d892f6d66402
parent 72565 ed5b907bbf50
child 72576 913407dad883
equal deleted inserted replaced
72573:a085a1a89388 72574:d892f6d66402
    75 
    75 
    76 
    76 
    77   /* finish session */
    77   /* finish session */
    78 
    78 
    79   def finish(
    79   def finish(
    80     progress: Progress,
       
    81     browser_info: Path,
    80     browser_info: Path,
    82     graph_file: JFile,
    81     graph_pdf: Bytes,
    83     info: Sessions.Info,
    82     info: Sessions.Info,
    84     name: String)
    83     name: String)
    85   {
    84   {
    86     val session_prefix = browser_info + Path.basic(info.chapter) + Path.basic(name)
    85     val session_prefix = browser_info + Path.basic(info.chapter) + Path.basic(name)
    87     val session_fonts = session_prefix + Path.explode("fonts")
    86     val session_fonts = session_prefix + Path.explode("fonts")
    88 
    87 
    89     if (info.options.bool("browser_info")) {
    88     if (info.options.bool("browser_info")) {
    90       Isabelle_System.make_directory(session_fonts)
    89       Isabelle_System.make_directory(session_fonts)
    91 
    90 
    92       val session_graph = session_prefix + Path.basic("session_graph.pdf")
    91       Bytes.write(session_prefix + session_graph_path, graph_pdf)
    93       File.copy(graph_file, session_graph.file)
       
    94       Isabelle_System.chmod("a+r", session_graph)
       
    95 
    92 
    96       HTML.write_isabelle_css(session_prefix)
    93       HTML.write_isabelle_css(session_prefix)
    97 
    94 
    98       for (entry <- Isabelle_Fonts.fonts(hidden = true))
    95       for (entry <- Isabelle_Fonts.fonts(hidden = true))
    99         File.copy(entry.path, session_fonts)
    96         File.copy(entry.path, session_fonts)
   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 }