summaryrefslogtreecommitdiffstats
path: root/ocamlbuild/display.ml
diff options
context:
space:
mode:
Diffstat (limited to 'ocamlbuild/display.ml')
-rw-r--r--ocamlbuild/display.ml385
1 files changed, 385 insertions, 0 deletions
diff --git a/ocamlbuild/display.ml b/ocamlbuild/display.ml
new file mode 100644
index 000000000..b1f13ee39
--- /dev/null
+++ b/ocamlbuild/display.ml
@@ -0,0 +1,385 @@
+(***********************************************************************)
+(* ocamlbuild *)
+(* *)
+(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *)
+(* *)
+(* Copyright 2007 Institut National de Recherche en Informatique et *)
+(* en Automatique. All rights reserved. This file is distributed *)
+(* under the terms of the Q Public License version 1.0. *)
+(* *)
+(***********************************************************************)
+
+(* $Id$ *)
+(* Original author: Berke Durak *)
+(* Display *)
+open My_std;;
+
+open My_unix;;
+
+let fp = Printf.fprintf;;
+
+(*** ANSI *)
+module ANSI =
+ struct
+ let up oc n = fp oc "\027[%dA" n;;
+ let clear_to_eol oc () = fp oc "\027[K";;
+ let bol oc () = fp oc "\r";;
+ let get_columns () =
+ try
+ int_of_string (String.chomp (My_unix.run_and_read "tput cols"))
+ with
+ | Failure _ -> 80
+ end
+;;
+(* ***)
+(*** tagline_description *)
+type tagline_description = (string * char) list;;
+(* ***)
+(*** sophisticated_display *)
+type sophisticated_display = {
+ ds_channel : out_channel; (** Channel for writing *)
+ ds_start_time : float; (** When was compilation started *)
+ mutable ds_last_update : float; (** When was the display last updated *)
+ mutable ds_last_target : string; (** Last target built *)
+ mutable ds_last_cached : bool; (** Was the last target cached or really built ? *)
+ mutable ds_last_tags : Tags.t; (** Tags of the last command *)
+ mutable ds_changed : bool; (** Does the tag line need recomputing ? *)
+ ds_update_interval : float; (** Minimum interval between updates *)
+ ds_columns : int; (** Number of columns in dssplay *)
+ mutable ds_jobs : int; (** Number of jobs launched or cached *)
+ mutable ds_jobs_cached : int; (** Number of jobs cached *)
+ ds_tagline : string; (** Current tagline *)
+ mutable ds_seen_tags : Tags.t; (** Tags that we have encountered *)
+ ds_pathname_length : int; (** How much space for displaying pathnames ? *)
+ ds_tld : tagline_description; (** Description for the tagline *)
+};;
+(* ***)
+(*** display_line, display *)
+type display_line =
+| Classic
+| Sophisticated of sophisticated_display
+
+type display = {
+ di_log_level : int;
+ di_log_channel : (Format.formatter * out_channel) option;
+ di_channel : out_channel;
+ di_formatter : Format.formatter;
+ di_display_line : display_line;
+ mutable di_finished : bool;
+}
+;;
+(* ***)
+(*** various defaults *)
+let default_update_interval = 0.05;;
+let default_tagline_description = [
+ "ocaml", 'O';
+ "native", 'N';
+ "byte", 'B';
+ "program", 'P';
+ "pp", 'R';
+ "debug", 'D';
+ "interf", 'I';
+ "link", 'L';
+];;
+
+(* NOT including spaces *)
+let countdown_chars = 8;;
+let jobs_chars = 3;;
+let jobs_cached_chars = 5;;
+let dots = "...";;
+let start_target = "STARTING";;
+let finish_target = "FINISHED";;
+let ticker_chars = 3;;
+let ticker_period = 0.25;;
+let ticker_animation = [|
+ "\\";
+ "|";
+ "/";
+ "-";
+|];;
+let cached = "*";;
+let uncached = " ";;
+let cache_chars = 1;;
+(* ***)
+(*** create_tagline *)
+let create_tagline description = String.make (List.length description) '-';;
+(* ***)
+(*** create *)
+let create
+ ?(channel=stdout)
+ ?(mode:[`Classic|`Sophisticated] = `Sophisticated)
+ ?columns:(_columns=75)
+ ?(description = default_tagline_description)
+ ?log_file
+ ?(log_level=1)
+ ()
+ =
+ let log_channel =
+ match log_file with
+ | None -> None
+ | Some fn ->
+ let oc = open_out_gen [Open_text; Open_wronly; Open_creat; Open_trunc] 0o644 fn in
+ let f = Format.formatter_of_out_channel oc in
+ Format.fprintf f "*** Starting build.\n";
+ Some (f, oc)
+ in
+
+ let display_line =
+ match mode with
+ | `Classic -> Classic
+ | `Sophisticated ->
+ (* We assume Unix is not degraded. *)
+ let n = ANSI.get_columns () in
+ let tag_chars = List.length description in
+ Sophisticated
+ { ds_channel = stdout;
+ ds_start_time = gettimeofday ();
+ ds_last_update = 0.0;
+ ds_last_target = start_target;
+ ds_last_tags = Tags.empty;
+ ds_last_cached = false;
+ ds_changed = false;
+ ds_update_interval = default_update_interval;
+ ds_columns = n;
+ ds_jobs = 0;
+ ds_jobs_cached = 0;
+ ds_tagline = create_tagline description;
+ ds_seen_tags = Tags.empty;
+ ds_pathname_length = n -
+ (countdown_chars + 1 + jobs_chars + 1 + jobs_cached_chars + 1 +
+ cache_chars + 1 + tag_chars + 1 + ticker_chars + 2);
+ ds_tld = description }
+ in
+ { di_log_level = log_level;
+ di_log_channel = log_channel;
+ di_channel = channel;
+ di_formatter = Format.formatter_of_out_channel channel;
+ di_display_line = display_line;
+ di_finished = false }
+;;
+(* ***)
+(*** print_time *)
+let print_time oc t =
+ let t = int_of_float t in
+ let s = t mod 60 in
+ let m = (t / 60) mod 60 in
+ let h = t / 3600 in
+ fp oc "%02d:%02d:%02d" h m s
+;;
+(* ***)
+(*** print_shortened_pathname *)
+let print_shortened_pathname length oc u =
+ assert(length >= 3);
+ let m = String.length u in
+ if m <= length then
+ begin
+ output_string oc u;
+ fp oc "%*s" (length - m) ""
+ end
+ else
+ begin
+ let n = String.length dots in
+ let k = length - n in
+ output_string oc dots;
+ output oc u (m - k) k;
+ end
+(* ***)
+(*** Layout
+
+00000000001111111111222222222233333333334444444444555555555566666666667777777777
+01234567890123456789012345678901234567890123456789012345678901234567890123456789
+HH MM SS XXXX PATHNAME
+00:12:31 32 ( 26) ...lp4Filters/Camlp4LocationStripper.cmo * OBn-------------
+| | | | | \ tags
+| | | \ last target built \ cached ?
+| | |
+| | \ number of jobs cached
+| \ number of jobs
+\ elapsed time
+cmo mllib
+***)
+(*** redraw_sophisticated *)
+let redraw_sophisticated ds =
+ let t = gettimeofday () in
+ let oc = ds.ds_channel in
+ let dt = t -. ds.ds_start_time in
+ ds.ds_last_update <- t;
+ fp oc "%a" ANSI.bol ();
+ let ticker_phase = (abs (int_of_float (ceil (dt /. ticker_period)))) mod (Array.length ticker_animation) in
+ let ticker = ticker_animation.(ticker_phase) in
+ fp oc "%a %-4d (%-4d) %a %s %s %s"
+ print_time dt
+ ds.ds_jobs
+ ds.ds_jobs_cached
+ (print_shortened_pathname ds.ds_pathname_length) ds.ds_last_target
+ (if ds.ds_last_cached then cached else uncached)
+ ds.ds_tagline
+ ticker;
+ fp oc "%a%!" ANSI.clear_to_eol ()
+;;
+(* ***)
+(*** redraw *)
+let redraw = function
+ | Classic -> ()
+ | Sophisticated ds -> redraw_sophisticated ds
+;;
+(* ***)
+(*** finish_sophisticated *)
+let finish_sophisticated ?(how=`Success) ds =
+ let t = gettimeofday () in
+ let oc = ds.ds_channel in
+ let dt = t -. ds.ds_start_time in
+ match how with
+ | `Success|`Error ->
+ fp oc "%a" ANSI.bol ();
+ fp oc "%s %d target%s (%d cached) in %a."
+ (if how = `Error then
+ "Compilation unsuccessful after building"
+ else
+ "Finished,")
+ ds.ds_jobs
+ (if ds.ds_jobs = 1 then "" else "s")
+ ds.ds_jobs_cached
+ print_time dt;
+ fp oc "%a\n%!" ANSI.clear_to_eol ()
+ | `Quiet ->
+ fp oc "%a%a%!" ANSI.bol () ANSI.clear_to_eol ();
+;;
+(* ***)
+(*** sophisticated_display *)
+let sophisticated_display ds f =
+ fp ds.ds_channel "%a%a%!" ANSI.bol () ANSI.clear_to_eol ();
+ f ds.ds_channel
+;;
+(* ***)
+(*** call_if *)
+let call_if log_channel f =
+ match log_channel with
+ | None -> ()
+ | Some x -> f x
+;;
+(* ***)
+(*** display *)
+let display di f =
+ call_if di.di_log_channel (fun (_, oc) -> f oc);
+ match di.di_display_line with
+ | Classic -> f di.di_channel
+ | Sophisticated ds -> sophisticated_display ds f
+;;
+(* ***)
+(*** finish *)
+let finish ?(how=`Success) di =
+ if not di.di_finished then begin
+ di.di_finished <- true;
+ call_if di.di_log_channel
+ begin fun (fmt, oc) ->
+ Format.fprintf fmt "# Compilation %ssuccessful.@." (if how = `Error then "un" else "");
+ close_out oc
+ end;
+ match di.di_display_line with
+ | Classic -> ()
+ | Sophisticated ds -> finish_sophisticated ~how ds
+ end
+;;
+(* ***)
+(*** update_tagline_from_tags *)
+let update_tagline_from_tags ds =
+ let tagline = ds.ds_tagline in
+ let tags = ds.ds_last_tags in
+ let rec loop i = function
+ | [] ->
+ for j = i to String.length tagline - 1 do
+ tagline.[j] <- '-'
+ done
+ | (tag, c) :: rest ->
+ if Tags.mem tag tags then
+ tagline.[i] <- Char.uppercase c
+ else
+ if Tags.mem tag ds.ds_seen_tags then
+ tagline.[i] <- Char.lowercase c
+ else
+ tagline.[i] <- '-';
+ loop (i + 1) rest
+ in
+ loop 0 ds.ds_tld;
+;;
+(* ***)
+(*** update_sophisticated *)
+let update_sophisticated ds =
+ let t = gettimeofday () in
+ let dt = t -. ds.ds_last_update in
+ if dt > ds.ds_update_interval then
+ begin
+ if ds.ds_changed then
+ begin
+ update_tagline_from_tags ds;
+ ds.ds_changed <- false
+ end;
+ redraw_sophisticated ds
+ end
+ else
+ ()
+;;
+(* ***)
+(*** set_target_sophisticated *)
+let set_target_sophisticated ds target tags cached =
+ ds.ds_changed <- true;
+ ds.ds_last_target <- target;
+ ds.ds_last_tags <- tags;
+ ds.ds_jobs <- 1 + ds.ds_jobs;
+ if cached then ds.ds_jobs_cached <- 1 + ds.ds_jobs_cached;
+ ds.ds_last_cached <- cached;
+ ds.ds_seen_tags <- Tags.union ds.ds_seen_tags ds.ds_last_tags;
+ update_sophisticated ds
+;;
+
+let print_tags f tags =
+ let first = ref true in
+ Tags.iter begin fun tag ->
+ if !first then begin
+ first := false;
+ Format.fprintf f "%s" tag
+ end else Format.fprintf f ", %s" tag
+ end tags
+;;
+(* ***)
+(*** update *)
+let update di =
+ match di.di_display_line with
+ | Classic -> ()
+ | Sophisticated ds -> update_sophisticated ds
+;;
+(* ***)
+(*** event *)
+let event di ?(pretend=false) command target tags =
+ call_if di.di_log_channel
+ (fun (fmt, _) ->
+ Format.fprintf fmt "# Target: %s, tags: { %a }\n" target print_tags tags;
+ Format.fprintf fmt "%s%s@." command (if pretend then " # cached" else ""));
+ match di.di_display_line with
+ | Classic ->
+ if pretend then
+ (if di.di_log_level >= 2 then Format.fprintf di.di_formatter "[cache hit] %s\n%!" command)
+ else
+ (if di.di_log_level >= 1 then Format.fprintf di.di_formatter "%s\n%!" command)
+ | Sophisticated ds ->
+ set_target_sophisticated ds target tags pretend;
+ update_sophisticated ds
+;;
+(* ***)
+(*** dprintf *)
+let dprintf ?(log_level=1) di fmt =
+ if log_level > di.di_log_level then Discard_printf.discard_printf fmt else
+ match di.di_display_line with
+ | Classic -> Format.fprintf di.di_formatter fmt
+ | Sophisticated _ ->
+ if log_level < 0 then
+ begin
+ display di ignore;
+ Format.fprintf di.di_formatter fmt
+ end
+ else
+ match di.di_log_channel with
+ | Some (f, _) -> Format.fprintf f fmt
+ | None -> Discard_printf.discard_printf fmt
+(* ***)