|
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 } |