1 (***********************************************************************)
4 (* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *)
6 (* Copyright 2007 Institut National de Recherche en Informatique et *)
7 (* en Automatique. All rights reserved. This file is distributed *)
8 (* under the terms of the Q Public License version 1.0. *)
10 (***********************************************************************)
13 (* Original author: Berke Durak *)
19 let fp = Printf.fprintf
;;
24 let up oc n
= fp oc
"\027[%dA" n
;;
25 let clear_to_eol oc
() = fp oc
"\027[K";;
26 let bol oc
() = fp oc
"\r";;
29 int_of_string
(String.chomp
(My_unix.run_and_read
"tput cols"))
35 (*** tagline_description *)
36 type tagline_description
= (string * char
) list
;;
38 (*** sophisticated_display *)
39 type sophisticated_display
= {
40 ds_channel
: out_channel
; (** Channel for writing *)
41 ds_start_time
: float; (** When was compilation started *)
42 mutable ds_last_update
: float; (** When was the display last updated *)
43 mutable ds_last_target
: string; (** Last target built *)
44 mutable ds_last_cached
: bool; (** Was the last target cached or really built ? *)
45 mutable ds_last_tags
: Tags.t
; (** Tags of the last command *)
46 mutable ds_changed
: bool; (** Does the tag line need recomputing ? *)
47 ds_update_interval
: float; (** Minimum interval between updates *)
48 ds_columns
: int; (** Number of columns in dssplay *)
49 mutable ds_jobs
: int; (** Number of jobs launched or cached *)
50 mutable ds_jobs_cached
: int; (** Number of jobs cached *)
51 ds_tagline
: string; (** Current tagline *)
52 mutable ds_seen_tags
: Tags.t
; (** Tags that we have encountered *)
53 ds_pathname_length
: int; (** How much space for displaying pathnames ? *)
54 ds_tld
: tagline_description
; (** Description for the tagline *)
57 (*** display_line, display *)
60 | Sophisticated
of sophisticated_display
64 di_log_channel
: (Format.formatter
* out_channel
) option;
65 di_channel
: out_channel
;
66 di_formatter
: Format.formatter
;
67 di_display_line
: display_line
;
68 mutable di_finished
: bool;
72 (*** various defaults *)
73 let default_update_interval = 0.05;;
74 let default_tagline_description = [
85 (* NOT including spaces *)
86 let countdown_chars = 8;;
88 let jobs_cached_chars = 5;;
90 let start_target = "STARTING";;
91 let finish_target = "FINISHED";;
92 let ticker_chars = 3;;
93 let ticker_period = 0.25;;
94 let ticker_animation = [|
102 let cache_chars = 1;;
104 (*** create_tagline *)
105 let create_tagline description
= String.make
(List.length description
) '
-'
;;
110 ?
(mode
:[`Classic
|`Sophisticated
] = `Sophisticated
)
111 ?columns
:(_columns
=75)
112 ?
(description
= default_tagline_description)
121 let oc = open_out_gen
[Open_text
; Open_wronly
; Open_creat
; Open_trunc
] 0o644 fn
in
122 let f = Format.formatter_of_out_channel
oc in
123 Format.fprintf
f "### Starting build.\n";
129 | `Classic
-> Classic
131 (* We assume Unix is not degraded. *)
132 let n = ANSI.get_columns () in
133 let tag_chars = List.length description
in
135 { ds_channel
= stdout
;
136 ds_start_time
= gettimeofday
();
137 ds_last_update
= 0.0;
138 ds_last_target
= start_target;
139 ds_last_tags
= Tags.empty
;
140 ds_last_cached
= false;
142 ds_update_interval
= default_update_interval;
146 ds_tagline
= create_tagline description
;
147 ds_seen_tags
= Tags.empty
;
148 ds_pathname_length
= n -
149 (countdown_chars + 1 + jobs_chars + 1 + jobs_cached_chars + 1 +
150 cache_chars + 1 + tag_chars + 1 + ticker_chars + 2);
151 ds_tld
= description
}
153 { di_log_level
= log_level
;
154 di_log_channel
= log_channel;
155 di_channel
= channel
;
156 di_formatter
= Format.formatter_of_out_channel channel
;
157 di_display_line
= display_line;
158 di_finished
= false }
162 let print_time oc t
=
163 let t = int_of_float
t in
165 let m = (t / 60) mod 60 in
167 fp oc "%02d:%02d:%02d" h m s
170 (*** print_shortened_pathname *)
171 let print_shortened_pathname length
oc u
=
173 let m = String.length u
in
177 fp oc "%*s" (length
- m) ""
181 let n = String.length
dots in
182 let k = length
- n in
183 output_string
oc dots;
184 output
oc u
(m - k) k;
189 00000000001111111111222222222233333333334444444444555555555566666666667777777777
190 01234567890123456789012345678901234567890123456789012345678901234567890123456789
191 HH MM SS XXXX PATHNAME
192 00:12:31 32 ( 26) ...lp4Filters/Camlp4LocationStripper.cmo * OBn-------------
194 | | | \ last target built \ cached ?
196 | | \ number of jobs cached
201 (*** redraw_sophisticated *)
202 let redraw_sophisticated ds
=
203 let t = gettimeofday
() in
204 let oc = ds
.ds_channel
in
205 let dt = t -. ds
.ds_start_time
in
206 ds
.ds_last_update
<- t;
207 fp oc "%a" ANSI.bol ();
208 let ticker_phase = (abs
(int_of_float
(ceil
(dt /. ticker_period)))) mod (Array.length
ticker_animation) in
209 let ticker = ticker_animation.(ticker_phase) in
210 fp oc "%a %-4d (%-4d) %a %s %s %s"
214 (print_shortened_pathname ds
.ds_pathname_length
) ds
.ds_last_target
215 (if ds
.ds_last_cached
then cached else uncached)
218 fp oc "%a%!" ANSI.clear_to_eol ()
222 let redraw = function
224 | Sophisticated ds
-> redraw_sophisticated ds
227 (*** finish_sophisticated *)
228 let finish_sophisticated ?
(how
=`Success
) ds
=
229 let t = gettimeofday
() in
230 let oc = ds
.ds_channel
in
231 let dt = t -. ds
.ds_start_time
in
234 fp oc "%a" ANSI.bol ();
235 fp oc "%s %d target%s (%d cached) in %a."
236 (if how
= `Error
then
237 "Compilation unsuccessful after building"
241 (if ds
.ds_jobs
= 1 then "" else "s")
244 fp oc "%a\n%!" ANSI.clear_to_eol ()
246 fp oc "%a%a%!" ANSI.bol () ANSI.clear_to_eol ();
249 (*** sophisticated_display *)
250 let sophisticated_display ds
f =
251 fp ds
.ds_channel
"%a%a%!" ANSI.bol () ANSI.clear_to_eol ();
256 let call_if log_channel f =
257 match log_channel with
264 call_if di
.di_log_channel
(fun (_
, oc) -> f oc);
265 match di
.di_display_line
with
266 | Classic
-> f di
.di_channel
267 | Sophisticated ds
-> sophisticated_display ds
f
271 let finish ?
(how
=`Success
) di
=
272 if not di
.di_finished
then begin
273 di
.di_finished
<- true;
274 call_if di
.di_log_channel
275 begin fun (fmt
, oc) ->
276 Format.fprintf fmt
"# Compilation %ssuccessful.@." (if how
= `Error
then "un" else "");
279 match di
.di_display_line
with
281 | Sophisticated ds
-> finish_sophisticated ~how ds
285 (*** update_tagline_from_tags *)
286 let update_tagline_from_tags ds
=
287 let tagline = ds
.ds_tagline
in
288 let tags = ds
.ds_last_tags
in
289 let rec loop i
= function
291 for j
= i
to String.length
tagline - 1 do
294 | (tag
, c
) :: rest
->
295 if Tags.mem tag
tags then
296 tagline.[i
] <- Char.uppercase c
298 if Tags.mem tag ds
.ds_seen_tags
then
299 tagline.[i
] <- Char.lowercase c
307 (*** update_sophisticated *)
308 let update_sophisticated ds
=
309 let t = gettimeofday
() in
310 let dt = t -. ds
.ds_last_update
in
311 if dt > ds
.ds_update_interval
then
313 if ds
.ds_changed
then
315 update_tagline_from_tags ds
;
316 ds
.ds_changed
<- false
318 redraw_sophisticated ds
324 (*** set_target_sophisticated *)
325 let set_target_sophisticated ds target
tags cached =
326 ds
.ds_changed
<- true;
327 ds
.ds_last_target
<- target
;
328 ds
.ds_last_tags
<- tags;
329 ds
.ds_jobs
<- 1 + ds
.ds_jobs
;
330 if cached then ds
.ds_jobs_cached
<- 1 + ds
.ds_jobs_cached
;
331 ds
.ds_last_cached
<- cached;
332 ds
.ds_seen_tags
<- Tags.union ds
.ds_seen_tags ds
.ds_last_tags
;
333 update_sophisticated ds
336 let print_tags f tags =
337 let first = ref true in
338 Tags.iter
begin fun tag
->
341 Format.fprintf
f "%s" tag
342 end else Format.fprintf
f ", %s" tag
348 match di
.di_display_line
with
350 | Sophisticated ds
-> update_sophisticated ds
354 let event di ?
(pretend
=false) command target
tags =
355 call_if di
.di_log_channel
357 Format.fprintf fmt
"# Target: %s, tags: { %a }\n" target
print_tags tags;
358 Format.fprintf fmt
"%s%s@." command
(if pretend
then " # cached" else ""));
359 match di
.di_display_line
with
362 (if di
.di_log_level
>= 2 then Format.fprintf di
.di_formatter
"[cache hit] %s\n%!" command
)
364 (if di
.di_log_level
>= 1 then Format.fprintf di
.di_formatter
"%s\n%!" command
)
365 | Sophisticated ds
->
366 set_target_sophisticated ds target
tags pretend
;
367 update_sophisticated ds
371 let dprintf ?
(log_level
=1) di fmt
=
372 if log_level
> di
.di_log_level
then Discard_printf.discard_printf fmt
else
373 match di
.di_display_line
with
374 | Classic
-> Format.fprintf di
.di_formatter fmt
376 if log_level
< 0 then
379 Format.fprintf di
.di_formatter fmt
382 match di
.di_log_channel
with
383 | Some
(f, _
) -> Format.fprintf
f fmt
384 | None
-> Discard_printf.discard_printf fmt