7 signature CACHE_IO = |
7 signature CACHE_IO = |
8 sig |
8 sig |
9 (*IO wrapper*) |
9 (*IO wrapper*) |
10 val with_tmp_file: string -> (Path.T -> 'a) -> 'a |
10 val with_tmp_file: string -> (Path.T -> 'a) -> 'a |
11 val with_tmp_dir: string -> (Path.T -> 'a) -> 'a |
11 val with_tmp_dir: string -> (Path.T -> 'a) -> 'a |
12 val run: (Path.T -> Path.T -> string) -> string -> |
12 type result = { |
13 {output: string list, redirected_output: string list, return_code: int} |
13 output: string list, |
|
14 redirected_output: string list, |
|
15 return_code: int} |
|
16 val run: (Path.T -> Path.T -> string) -> string -> result |
14 |
17 |
15 (*cache*) |
18 (*cache*) |
16 type cache |
19 type cache |
17 val make: Path.T -> cache |
20 val make: Path.T -> cache |
18 val cache_path_of: cache -> Path.T |
21 val cache_path_of: cache -> Path.T |
19 val lookup: cache -> string -> (string list * string list) option * string |
22 val lookup: cache -> string -> result option * string |
20 val run_and_cache: cache -> string -> (Path.T -> Path.T -> string) -> |
23 val run_and_cache: cache -> string -> (Path.T -> Path.T -> string) -> |
21 string -> string list * string list |
24 string -> result |
22 val run_cached: cache -> (Path.T -> Path.T -> string) -> string -> |
25 val run_cached: cache -> (Path.T -> Path.T -> string) -> string -> result |
23 string list * string list |
|
24 end |
26 end |
25 |
27 |
26 structure Cache_IO : CACHE_IO = |
28 structure Cache_IO : CACHE_IO = |
27 struct |
29 struct |
28 |
30 |
42 val path = File.tmp_path (Path.explode (name ^ serial_string ())) |
44 val path = File.tmp_path (Path.explode (name ^ serial_string ())) |
43 val _ = File.mkdir path |
45 val _ = File.mkdir path |
44 val x = Exn.capture f path |
46 val x = Exn.capture f path |
45 val _ = try File.rm_tree path |
47 val _ = try File.rm_tree path |
46 in Exn.release x end |
48 in Exn.release x end |
|
49 |
|
50 type result = { |
|
51 output: string list, |
|
52 redirected_output: string list, |
|
53 return_code: int} |
47 |
54 |
48 fun run make_cmd str = |
55 fun run make_cmd str = |
49 with_tmp_file cache_io_prefix (fn in_path => |
56 with_tmp_file cache_io_prefix (fn in_path => |
50 with_tmp_file cache_io_prefix (fn out_path => |
57 with_tmp_file cache_io_prefix (fn out_path => |
51 let |
58 let |
96 fun load line (i, xsp) = |
103 fun load line (i, xsp) = |
97 if i < p then (i+1, xsp) |
104 if i < p then (i+1, xsp) |
98 else if i < p + len1 then (i+1, apfst (cons line) xsp) |
105 else if i < p + len1 then (i+1, apfst (cons line) xsp) |
99 else if i < p + len2 then (i+1, apsnd (cons line) xsp) |
106 else if i < p + len2 then (i+1, apsnd (cons line) xsp) |
100 else (i, xsp) |
107 else (i, xsp) |
101 in pairself rev (snd (File.fold_lines load cache_path (1, ([], [])))) end |
108 val (out, err) = |
|
109 pairself rev (snd (File.fold_lines load cache_path (1, ([], [])))) |
|
110 in {output=err, redirected_output=out, return_code=0} end |
102 |
111 |
103 fun lookup (Cache {path=cache_path, table}) str = |
112 fun lookup (Cache {path=cache_path, table}) str = |
104 let val key = SHA1.digest str |
113 let val key = SHA1.digest str |
105 in |
114 in |
106 (case Symtab.lookup (snd (Synchronized.value table)) key of |
115 (case Symtab.lookup (snd (Synchronized.value table)) key of |
108 | SOME pos => (SOME (load_cached_result cache_path pos), key)) |
117 | SOME pos => (SOME (load_cached_result cache_path pos), key)) |
109 end |
118 end |
110 |
119 |
111 fun run_and_cache (Cache {path=cache_path, table}) key make_cmd str = |
120 fun run_and_cache (Cache {path=cache_path, table}) key make_cmd str = |
112 let |
121 let |
113 val {output=err, redirected_output=out, ...} = run make_cmd str |
122 val {output=err, redirected_output=out, return_code} = run make_cmd str |
114 val res = (out, err) |
123 val (l1, l2) = pairself length (out, err) |
115 val (l1, l2) = pairself length res |
|
116 val header = key ^ " " ^ string_of_int l1 ^ " " ^ string_of_int l2 |
124 val header = key ^ " " ^ string_of_int l1 ^ " " ^ string_of_int l2 |
117 val lines = map (suffix "\n") (header :: out @ err) |
125 val lines = map (suffix "\n") (header :: out @ err) |
118 |
126 |
119 val _ = Synchronized.change table (fn (p, tab) => |
127 val _ = Synchronized.change table (fn (p, tab) => |
120 if Symtab.defined tab key then (p, tab) |
128 if Symtab.defined tab key then (p, tab) |
121 else |
129 else |
122 let val _ = File.append_list cache_path lines |
130 let val _ = File.append_list cache_path lines |
123 in (p+l1+l2+1, Symtab.update (key, (p+1, l1, l2)) tab) end) |
131 in (p+l1+l2+1, Symtab.update (key, (p+1, l1, l2)) tab) end) |
124 in res end |
132 in {output=err, redirected_output=out, return_code=return_code} end |
125 |
133 |
126 fun run_cached cache make_cmd str = |
134 fun run_cached cache make_cmd str = |
127 (case lookup cache str of |
135 (case lookup cache str of |
128 (NONE, key) => run_and_cache cache key make_cmd str |
136 (NONE, key) => run_and_cache cache key make_cmd str |
129 | (SOME output, _) => output) |
137 | (SOME output, _) => output) |