src/Tools/Find_Facts/src/elm.scala
changeset 81770 f54881ce5cf3
parent 81765 eb40020efda7
child 81775 1a02f32f7d20
equal deleted inserted replaced
81769:dfb6c4a15ef4 81770:f54881ce5cf3
       
     1 /*  Author:     Fabian Huch, TU Muenchen
       
     2 
       
     3 Elm module for Isabelle.
       
     4 */
       
     5 
       
     6 package isabelle.find_facts
       
     7 
       
     8 
       
     9 import isabelle._
       
    10 
       
    11 import java.io.{File => JFile}
       
    12 
       
    13 import scala.jdk.CollectionConverters._
       
    14 
       
    15 import org.jsoup.nodes.Element
       
    16 
       
    17 
       
    18 object Elm {
       
    19   private lazy val exec = Path.explode("$ISABELLE_ELM_HOME/elm").expand
       
    20 
       
    21   object Project {
       
    22     def apply(
       
    23       name: String,
       
    24       dir: Path,
       
    25       main: Path = Path.explode("src/Main.elm"),
       
    26       output: Path = Path.explode("index.html"),
       
    27       head: XML.Body = Nil
       
    28     ): Project = {
       
    29       if (!dir.is_dir) error("Project directory does not exist: " + dir)
       
    30       val main_file = dir + main
       
    31       if (!main_file.is_file) error("Main elm file does not exist: " + main_file)
       
    32       new Project(name, dir, main, dir + output, head)
       
    33     }
       
    34   }
       
    35 
       
    36   class Project private(name: String, dir: Path, main: Path, output: Path, head: XML.Body) {
       
    37     val definition = JSON.parse(File.read(dir + Path.basic("elm.json")))
       
    38     val src_dirs =
       
    39       JSON.strings(definition, "source-directories").getOrElse(
       
    40         error("Missing source directories in elm.json"))
       
    41 
       
    42     def sources: List[JFile] =
       
    43       for {
       
    44         src_dir <- src_dirs
       
    45         path = dir + Path.explode(src_dir)
       
    46         file <- File.find_files(path.file, _.getName.endsWith(".elm"))
       
    47       } yield file
       
    48 
       
    49     def sources_shasum: SHA1.Shasum = {
       
    50       val meta_info = SHA1.shasum_meta_info(SHA1.digest(JSON.Format(definition)))
       
    51       val head_digest = SHA1.shasum(SHA1.digest(XML.string_of_body(head)), "head")
       
    52       val source_digest =
       
    53         SHA1.shasum_sorted(for (file <- sources) yield SHA1.digest(file) -> file.getCanonicalPath)
       
    54       meta_info ::: head_digest ::: source_digest
       
    55     }
       
    56 
       
    57     def get_digest: SHA1.Digest =
       
    58       Exn.capture {
       
    59         val html = HTML.parse_document(File.read(output))
       
    60         val elem = html.head.getElementsByTag("meta").attr("name", "shasum")
       
    61         Library.the_single(elem.eachAttr("content").asScala.toList)
       
    62       } match {
       
    63         case Exn.Res(s) => SHA1.fake_digest(s)
       
    64         case _ => SHA1.digest_empty
       
    65       }
       
    66 
       
    67     def build_html(progress: Progress): String = {
       
    68       val digest = sources_shasum.digest
       
    69       if (digest == get_digest) File.read(output)
       
    70       else {
       
    71         progress.echo("### Building " + name + " (" + output.canonical.implode + ") ...")
       
    72 
       
    73         val cmd =
       
    74           File.bash_path(exec) + " make " + File.bash_path(main) + " --optimize --output=" + output
       
    75         val res = Isabelle_System.bash(cmd, cwd = dir)
       
    76 
       
    77         if (!res.ok) {
       
    78           progress.echo(res.err)
       
    79           error("Failed to compile Elm sources")
       
    80         }
       
    81 
       
    82         val file = HTML.parse_document(File.read(output))
       
    83         file.head.appendChild(
       
    84           Element("meta").attr("name", "shasum").attr("content", digest.toString))
       
    85         file.head.append(XML.string_of_body(head))
       
    86         val html = file.html
       
    87         File.write(output, html)
       
    88 
       
    89         html
       
    90       }
       
    91     }
       
    92   }
       
    93 }