Skip to content

Commit

Permalink
Respect OCAML_COLOR environment variable for deciding whether to use …
Browse files Browse the repository at this point in the history
…colors

Since 4.03, OCaml supports coloring its messages to standard output and standard
error, depending on the "-color" argument ({always,never,auto}).  This commit
adds support for the environment variable "OCAML_COLOR" (which value can as well
be {always,never,auto}).

The command line argument "-color" takes precedence, "OCAML_COLOR" is only
taken into consideration if no "-color" is provided.

The motivation for this is that the user should have control over coloring
OCaml's output messages.  OCamlbuild, a widely used build tool executes OCaml
not under a tty (and OCaml does not colorize errors and warnings), which lead
various packages use `color(always)` in their `_tags` files, which breaks with
other (non-interactive) programs (i.e.  editor helpers).

Further discussion was done at ocaml/ocamlbuild#87 and
#1098.
  • Loading branch information
hannesm authored and gasche committed Mar 15, 2017
1 parent 0903941 commit d377215
Show file tree
Hide file tree
Showing 13 changed files with 68 additions and 12 deletions.
7 changes: 7 additions & 0 deletions Changes
Expand Up @@ -115,6 +115,13 @@ Next major version (4.05.0):
spent in subprocesses like preprocessors
(Valentin Gatien-Baron, review by Gabriel Scherer)

- GPR#1098: the compiler now takes the boolean "OCAML_COLOR" environment
variable into account if "-color" is not provided. This allows users
to override the default behaviour without modifying invocations of ocaml
manually.
(Hannes Mehnert, Guillaume Bury,
review by Daniel Bünzli, Gabriel Scherer, Damien Doligez)

### Standard library:

- PR#6975, GPR#902: Truncate function added to stdlib Buffer module
Expand Down
2 changes: 1 addition & 1 deletion driver/compenv.ml
Expand Up @@ -338,7 +338,7 @@ let read_one_param ppf position name v =
(Warnings.Bad_env_variable ("OCAMLPARAM",
"bad value for \"color\", \
(expected \"auto\", \"always\" or \"never\")"))
| Some setting -> color := setting
| Some setting -> color := Some setting
end

| "intf-suffix" -> Config.interface_suffix := v
Expand Down
15 changes: 15 additions & 0 deletions driver/compmisc.ml
Expand Up @@ -60,3 +60,18 @@ let initial_env () =
List.fold_left (fun env m ->
open_implicit_module m env
) env (!implicit_modules @ List.rev !Clflags.open_modules)


let read_color_env ppf =
try
match Clflags.parse_color_setting (Sys.getenv "OCAML_COLOR") with
| None ->
Location.print_warning Location.none ppf
(Warnings.Bad_env_variable
("OCAML_COLOR",
"expected \"auto\", \"always\" or \"never\""));
| Some x -> match !Clflags.color with
| None -> Clflags.color := Some x
| Some _ -> ()
with
Not_found -> ()
2 changes: 2 additions & 0 deletions driver/compmisc.mli
Expand Up @@ -15,3 +15,5 @@

val init_path : ?dir:string -> bool -> unit
val initial_env : unit -> Env.t

val read_color_env : Format.formatter -> unit
5 changes: 3 additions & 2 deletions driver/main.ml
Expand Up @@ -104,9 +104,9 @@ module Options = Main_args.Make_bytecomp_options (struct
let _warn_error = (Warnings.parse_options true)
let _warn_help = Warnings.help_warnings
let _color option =
begin match Clflags.parse_color_setting option with
begin match parse_color_setting option with
| None -> ()
| Some setting -> Clflags.color := setting
| Some setting -> color := Some setting
end
let _where = print_standard_library
let _verbose = set verbose
Expand All @@ -130,6 +130,7 @@ let main () =
try
readenv ppf Before_args;
Clflags.parse_arguments anonymous usage;
Compmisc.read_color_env ppf;
begin try
Compenv.process_deferred_actions
(ppf,
Expand Down
5 changes: 3 additions & 2 deletions driver/optmain.ml
Expand Up @@ -187,9 +187,9 @@ module Options = Main_args.Make_optcomp_options (struct
let _warn_error s = Warnings.parse_options true s
let _warn_help = Warnings.help_warnings
let _color option =
begin match Clflags.parse_color_setting option with
begin match parse_color_setting option with
| None -> ()
| Some setting -> Clflags.color := setting
| Some setting -> color := Some setting
end
let _where () = print_standard_library ()

Expand Down Expand Up @@ -238,6 +238,7 @@ let main () =
readenv ppf Before_args;
Clflags.add_arguments __LOC__ (Arch.command_line_options @ Options.list);
Clflags.parse_arguments anonymous usage;
Compmisc.read_color_env ppf;
if !gprofile && not Config.profiling then
fatal "Profiling with \"gprof\" is not supported on this platform.";
begin try
Expand Down
3 changes: 3 additions & 0 deletions man/ocamlc.m
Expand Up @@ -282,6 +282,9 @@ use heuristics to enable colors only if the output supports them (an
checks that the "TERM" environment variable exists and is
not empty or "dumb", and that isatty(stderr) holds.

The environment variable "OCAML_COLOR" is considered if \-color is not
provided. Its values are auto/always/never as above.

.TP
.B \-compat\-32
Check that the generated bytecode executable can run on 32-bit
Expand Down
24 changes: 24 additions & 0 deletions man/ocamlopt.m
Expand Up @@ -217,6 +217,30 @@ Dump detailed information about the compilation (types, bindings,
causes the C linker to search for C libraries in
directory
.IR dir .
.TP
.BI \-color \ mode
Enable or disable colors in compiler messages (especially warnings and errors).
The following modes are supported:

.B auto
use heuristics to enable colors only if the output supports them (an
ANSI-compatible tty terminal);

.B always
enable colors unconditionally;

.B never
disable color output.

The default setting is
.B auto,
and the current heuristic
checks that the "TERM" environment variable exists and is
not empty or "dumb", and that isatty(stderr) holds.

The environment variable "OCAML_COLOR" is considered if \-color is not
provided. Its values are auto/always/never as above.

.TP
.B \-compact
Optimize the produced code for space rather than for time. This
Expand Down
3 changes: 3 additions & 0 deletions manual/manual/cmds/unified-options.etex
Expand Up @@ -131,6 +131,9 @@ The following modes are supported:
The default setting is 'auto', and the current heuristic
checks that the "TERM" environment variable exists and is
not empty or "dumb", and that 'isatty(stderr)' holds.

The environment variable "OCAML_COLOR" is considered if "-color" is not
provided. Its values are auto/always/never as above.
}%notop

\comp{%
Expand Down
2 changes: 1 addition & 1 deletion utils/clflags.ml
Expand Up @@ -358,7 +358,7 @@ let parse_color_setting = function
| "always" -> Some Misc.Color.Always
| "never" -> Some Misc.Color.Never
| _ -> None
let color = ref Misc.Color.Auto ;; (* -color *)
let color = ref None ;; (* -color *)

let unboxed_types = ref false

Expand Down
2 changes: 1 addition & 1 deletion utils/clflags.mli
Expand Up @@ -201,7 +201,7 @@ val dumped_pass : string -> bool
val set_dumped_pass : string -> bool -> unit

val parse_color_setting : string -> Misc.Color.setting option
val color : Misc.Color.setting ref
val color : Misc.Color.setting option ref

val unboxed_types : bool ref

Expand Down
8 changes: 4 additions & 4 deletions utils/misc.ml
Expand Up @@ -601,10 +601,10 @@ module Color = struct
Format.set_mark_tags true;
List.iter set_color_tag_handling formatter_l;
color_enabled := (match o with
| Always -> true
| Auto -> should_enable_color ()
| Never -> false
)
| Some Always -> true
| Some Auto -> should_enable_color ()
| Some Never -> false
| None -> should_enable_color ())
);
()
end
Expand Down
2 changes: 1 addition & 1 deletion utils/misc.mli
Expand Up @@ -277,7 +277,7 @@ module Color : sig

type setting = Auto | Always | Never

val setup : setting -> unit
val setup : setting option -> unit
(* [setup opt] will enable or disable color handling on standard formatters
according to the value of color setting [opt].
Only the first call to this function has an effect. *)
Expand Down

0 comments on commit d377215

Please sign in to comment.