Skip to content
This repository
tag: v1495
Fetching contributors…

Cannot retrieve contributors at this time

file 364 lines (285 sloc) 10.349 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
(*
Copyright © 2011 MLstate

This file is part of OPA.

OPA is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License, version 3, as published by
the Free Software Foundation.

OPA is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
more details.

You should have received a copy of the GNU Affero General Public License
along with OPA. If not, see <http://www.gnu.org/licenses/>.
*)
(* THIS FILE HAS A DOCUMENTED MLI *)

type at_exit = { at_exit : 'a. int -> 'a }
let main_at_exit = ref { at_exit = (fun i -> exit i) }
let exit code = !main_at_exit.at_exit code

(* Default formatter for OManager is stderr *)
let oformatter = ref Format.err_formatter
let odescr = Unix.stderr
  (* don't use [odescr] for outputting because you would
* have troubles with order of output since the formatter
* and the file descriptor have different buffers
* it is just used for setting a default behavior for colors *)

type ('params,'output) oformat = ('params, Format.formatter, unit, 'output) format4

module Color = struct
  module Arg = Base.Arg
  let set_color b = Format.pp_set_tags !oformatter b

  let () = set_color (Unix.isatty odescr)
    (* by default, color on a terminal
* no color if stderr is redirected to a file *)

  let console_set_color b =
    Ansi.set_ignore_color (not b) ;
    set_color b ;
    ()

  let args = [ "--no-color", Arg.Unit (fun () -> console_set_color false), " Disable any ANSI escape in the output (default for non ttys)"
             ; "--force-color", Arg.Unit (fun () -> console_set_color true), " Enable ANSI escape in the compiler output (default for ttys)" ]

  (* the state allows us to nest escapes *)
  type state =
      { reversed : string
      ; crossed : string
      ; underlined : string
      ; intensity : string
      ; foreground : string
      ; background : string
      ; old_state : state
      ; undo : string } (* the escapes to restore old_state *)

  let rec default_state =
    { reversed = "" (* not reversed *)
    ; crossed = "" (* not crossed *)
    ; underlined = "" (* not underlined *)
    ; intensity = "" (* normal intensity *)
    ; foreground = "" (* default foreground *)
    ; background = "" (* default background *)
    ; old_state = default_state (* shouldn't be used *)
    ; undo = "" } (* since old_state is default_state, you don't have anything to undo to restore it *)
  let default_state_of state =
    { reversed = ""
    ; crossed = ""
    ; underlined = ""
    ; intensity = ""
    ; foreground = ""
    ; background = ""
    ; old_state = state
    ; undo = "" }

  (* the current state *)
  let state = ref default_state

  (* the function that restores the previous state *)
  let undo () =
    let s = !state.undo in
    state := !state.old_state;
    s

  (* a bunch of functions to update the stack *)
  let fg s =
    let state' = !state in
    state := {state' with foreground = s; undo = state'.foreground; old_state = state'};
    s
  let bg s =
    let state' = !state in
    state := {state' with background = s; undo = state'.background; old_state = state'};
    s
  let reverse () =
    let state' = !state in
    let reversed = state'.reversed in
    let reverse = "" in
    let unreverse = "" in
    let s = if reversed == reverse then unreverse else reverse in
    (* if you try to reverse the colors when they are already reversed, you get the normal colors *)
    state := {state' with reversed = s; undo = state'.reversed; old_state = state'};
    s
  let cross () =
    let state' = !state in
    let s = "" in
    state := {state' with crossed = s; undo = state'.crossed; old_state = state'};
    s
  let underline () =
    let state' = !state in
    let s = "" in
    state := {state' with underlined = s; undo = state'.underlined; old_state = state'};
    s
  let intensity s =
    let state' = !state in
    state := {state' with intensity = s; undo = state'.intensity; old_state = state'};
    s
  let nop () =
    state := {!state with undo = ""; old_state = !state};
    ""

  (* a brutal way to restore a state: just concatenate all its ansi escapes *)
  let make_state { reversed = reversed
                 ; crossed = crossed
                 ; underlined = underlined
                 ; intensity = intensity
                 ; foreground = foreground
                 ; background = background } =
    reversed ^ crossed ^ underlined ^ intensity ^ background ^ foreground
  let full_undo () =
    let state' = !state in
    state := {(default_state_of state') with undo = make_state state' };
    ""

  (* the strings that are accepted inside @{<blabla> *)
  let color = function
    | "reset" -> full_undo ()
    | "reverse" -> reverse ()
    | "cross" -> cross ()
    | "underline" -> underline ()
    | "dim" -> intensity ""
    | "bright" -> intensity ""
    | "normal" -> intensity ""

    | "black" -> fg ""
    | "red" -> fg ""
    | "green" -> fg ""
    | "yellow" -> fg ""
    | "blue" -> fg ""
    | "magenta" -> fg ""
    | "cyan" -> fg ""
    | "white" -> fg ""
    | "default" -> fg ""

    | "Black" -> bg ""
    | "Red" -> bg ""
    | "Green" -> bg ""
    | "Yellow" -> bg ""
    | "Blue" -> bg ""
    | "Magenta" -> bg ""
    | "Cyan" -> bg ""
    | "White" -> bg ""
    | "Default" -> bg ""

    | _ -> nop ()

  (* allowing people to give several comma separated tags in one @{<...>@} *)
  let delim = Str.regexp "[ \t]*,[ \t]*"
  let mark_open s =
    let sl = Str.split delim s in
    String.concat "" (List.map color sl)
  let mark_close s =
    let sl = Str.split delim s in
    String.concat "" (List.map (fun _ -> undo ()) sl)

  let color_tags =
    { Format.mark_open_tag = mark_open
    ; Format.mark_close_tag = mark_close
    ; Format.print_open_tag = ignore
    ; Format.print_close_tag = ignore }

  let () =
    #<If:OMANAGER_DEBUG>()#<Else>
    Format.pp_set_formatter_tag_functions !oformatter color_tags
    #<End>
end

let printf fmt = Format.fprintf !oformatter fmt

let kfprintf = fun f fmt -> Format.kfprintf f !oformatter fmt

let ifprintf fmt = Format.ifprintf !oformatter fmt

module Verbose = struct

  let verb = ref false
  let quiet = ref false

  let set_verbose v = verb := v

  let is_verbose () = !verb

  let verbose fmt =

    if !verb then
      printf ("@{<blue>"^^fmt^^"@}@.")
    else
      ifprintf fmt

  let set_quiet q = quiet := q

  let is_quiet () = !quiet

  let unquiet fmt =

    if not !quiet then
      printf ("@{<blue>"^^fmt^^"@}@.")
    else
      ifprintf fmt
end

module WarnSet =
  BaseSet.Make(struct
            type t = WarningClass.wclass
            let compare = compare
          end)

module Error = struct

  let warn_set = ref WarnSet.empty

  let add_warn_error warn =
    warn_set := WarnSet.add warn !warn_set

  let error_fmt fmt = ("@{<red>Error@}@\n"^^fmt^^"@.")

  let print_public_error fmt =
    printf (error_fmt fmt)

  let public fmt =
    kfprintf (fun _ -> exit 1) (error_fmt fmt)

  let public_error = ref false
  let s_public fmt =
    kfprintf (fun _ -> public_error := true) (error_fmt fmt)

  let print_internal_error = print_public_error

  let internal_fmt fmt =
    ("@{<red>Internal Error@}@\n"^^fmt^^"@.")

  let quit_internal _ =
    if Printexc.backtrace_status ()
    then failwith "OManager.i_error: backtrace"
    else exit 2

  let internal fmt =
    kfprintf quit_internal (internal_fmt fmt)

  let internal_error = ref false
  let s_internal fmt =
    kfprintf (fun _ -> internal_error := true) (internal_fmt fmt)

  let flush () =
    if not (WarnSet.is_empty !warn_set) then (
      let _, msg =
        WarnSet.fold
          (fun w (pre,acc) ->
             ",", Printf.sprintf "%s%s '%s'" acc pre (WarningClass.get_name w))
          !warn_set ("","Fatal warning:") in
      s_public "%s\n" msg;
    );
    printf "%!";
    if !public_error then exit 10;
    if !internal_error then quit_internal ();
    ()
end

module Warning = struct

  let warning ~wclass fmt =
    if WarningClass.is_warn wclass then
      (if WarningClass.is_warn_error wclass then
         Error.add_warn_error wclass;
       printf ("@{<yellow>Warning %s@}@\n"^^fmt^^"@.")
                 (WarningClass.get_name wclass))
    else ifprintf fmt

end

let set_color = Color.console_set_color

let set_verbose = Verbose.set_verbose

let is_verbose = Verbose.is_verbose

let set_quiet = Verbose.set_quiet

let is_quiet = Verbose.is_quiet

let verbose = Verbose.verbose

let unquiet = Verbose.unquiet

let error = Error.public

let serror = Error.s_public

let i_error = Error.internal

let i_serror = Error.s_internal

let flush_errors = Error.flush

let warning = Warning.warning

let warn_error_status () = WarnSet.elements !Error.warn_set

module CompilerAsLib =
struct
  let set_stderr stderr =
    (* odescr is just for having a default behavior for colors *)
    oformatter := stderr

  let set_stdout _ = assert false

  let at_exit at_exit =
    main_at_exit := at_exit
 end

let apologies () =
  printf "Our apologies, an @{<bright>internal error@} has stopped the process@\nYou may get support, and contribute to the OPA development by @{<bright>reporting@} this problem to MLstate@\n"


let this_is_tool ?(force=false) tool =
  let pp = if force then printf else verbose in
  pp "This is @{<bright>%s@} version @{<bright>%s@}: (c) @{<bright>MLstate@} %s"
    tool BuildInfos.version_id BuildInfos.year ;
  if force then printf "@\n" else ()

module Arg =
struct
  module Arg = Base.Arg (* Base.Arg *)
  (* todo: hide printf with --quiet *)
  let options = Color.args @ [

    "--verbose",
    Arg.Unit (fun () -> Verbose.set_verbose true),
    " Compiler is more verbose (print some logs)" ;

    "--quiet",
    Arg.Unit (fun () -> Verbose.set_verbose false; Verbose.set_quiet true),
    " The compiler is quiet (less logs)"

  ]

  let version tool =
    "--version",
    Arg.Unit (fun () -> this_is_tool ~force:true tool; exit 0),
    " Print version and exit"
end
Something went wrong with that request. Please try again.