Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -34,6 +34,9 @@ // For --build-id. enum class BuildIdKind { None, Fast, Md5, Sha1, Hexstring, Uuid }; +// For --color-diagnostics. +enum class ColorPolicy { Auto, Always, Never }; + // For --discard-{all,locals,none}. enum class DiscardPolicy { Default, All, Locals, None }; @@ -135,6 +138,7 @@ bool ZRelro; bool ExitEarly; bool ZWxneeded; + ColorPolicy ColorDiagnostics = ColorPolicy::Auto; DiscardPolicy Discard; SortSectionPolicy SortSection; StripPolicy Strip = StripPolicy::None; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -391,6 +391,24 @@ return Default; } +// Parse -color-diagnostics={auto,always,never} or -no-color-diagnostics. +static ColorPolicy getColorDiagnostics(opt::InputArgList &Args) { + auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_no_color_diagnostics); + if (!Arg) + return ColorPolicy::Auto; + if (Arg->getOption().getID() == OPT_no_color_diagnostics) + return ColorPolicy::Never; + + StringRef S = Arg->getValue(); + if (S == "auto") + return ColorPolicy::Auto; + if (S == "always") + return ColorPolicy::Always; + if (S != "never") + error("unknown -color-diagnostics value: " + S); + return ColorPolicy::Never; +} + static DiscardPolicy getDiscardOption(opt::InputArgList &Args) { auto *Arg = Args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none); @@ -485,6 +503,7 @@ Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition); Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic); Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions); + Config->ColorDiagnostics = getColorDiagnostics(Args); Config->Demangle = getArg(Args, OPT_demangle, OPT_no_demangle, true); Config->DisableVerify = Args.hasArg(OPT_disable_verify); Config->Discard = getDiscardOption(Args); Index: ELF/Error.cpp =================================================================== --- ELF/Error.cpp +++ ELF/Error.cpp @@ -13,6 +13,7 @@ #include "llvm/ADT/Twine.h" #include "llvm/Support/Error.h" #include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Process.h" #include "llvm/Support/raw_ostream.h" #include @@ -20,6 +21,7 @@ #include #endif +using namespace lld::elf; using namespace llvm; namespace lld { @@ -32,6 +34,26 @@ // but outs() or errs() are not thread-safe. We protect them using a mutex. static std::mutex Mu; +static bool useColor() { + if (Config->ColorDiagnostics == ColorPolicy::Always) + return true; + if (Config->ColorDiagnostics == ColorPolicy::Never) + return false; + return ErrorOS == &errs() && sys::Process::StandardErrHasColors(); +} + +static void print(StringRef S, raw_ostream::Colors C) { + if (useColor()) { + ErrorOS->changeColor(raw_ostream::WHITE, /*Bold=*/true); + *ErrorOS << Argv0 + ": "; + ErrorOS->changeColor(C, true); + *ErrorOS << S; + ErrorOS->resetColor(); + } else { + *ErrorOS << Argv0 + ": " << S; + } +} + void elf::log(const Twine &Msg) { std::lock_guard Lock(Mu); if (Config->Verbose) @@ -44,16 +66,19 @@ return; } std::lock_guard Lock(Mu); - *ErrorOS << Argv0 << ": warning: " << Msg << "\n"; + print("warning: ", raw_ostream::MAGENTA); + *ErrorOS << Msg << "\n"; } void elf::error(const Twine &Msg) { std::lock_guard Lock(Mu); if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) { - *ErrorOS << Argv0 << ": error: " << Msg << "\n"; + print("error: ", raw_ostream::RED); + *ErrorOS << Msg << "\n"; } else if (ErrorCount == Config->ErrorLimit) { - *ErrorOS << Argv0 << ": error: too many errors emitted, stopping now" + print("error: ", raw_ostream::RED); + *ErrorOS << "too many errors emitted, stopping now" << " (use -error-limit=0 to see all errors)\n"; if (Config->ExitEarly) exitLld(1); Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -39,6 +39,9 @@ def as_needed: F<"as-needed">, HelpText<"Only set DT_NEEDED for shared libraries if used">; +def color_diagnostics: S<"color-diagnostics">, + HelpText<"Use colors in diagnostics">; + def disable_new_dtags: F<"disable-new-dtags">, HelpText<"Disable new dynamic tags">; @@ -121,6 +124,9 @@ def no_as_needed: F<"no-as-needed">, HelpText<"Always DT_NEEDED for shared libraries">; +def no_color_diagnostics: F<"no-color-diagnostics">, + HelpText<"Do not use colors in diagnostics">; + def no_demangle: F<"no-demangle">, HelpText<"Do not demangle symbol names">; @@ -238,6 +244,7 @@ def alias_Bstatic_non_shared: F<"non_shared">, Alias; def alias_Bstatic_static: F<"static">, Alias; def alias_L__library_path: J<"library-path=">, Alias; +def alias_color_diagnostics: J<"color-diagnostics=">, Alias; def alias_discard_all_x: Flag<["-"], "x">, Alias; def alias_discard_locals_X: Flag<["-"], "X">, Alias; def alias_dynamic_list: J<"dynamic-list=">, Alias; Index: test/ELF/color-diagnostics.test =================================================================== --- /dev/null +++ test/ELF/color-diagnostics.test @@ -0,0 +1,12 @@ +# Windows command prompt doesn't suport ANSI escape sequnces. +# REQUIRES: shell + +# RUN: not ld.lld -color-diagnostics=always /nosuchfile 2>&1 \ +# RUN: | FileCheck -check-prefix=COLOR %s + +# COLOR: {{^.\[0;1;37m.*/ld.lld: .\[0;1;31merror: .\[0mcannot open /nosuchfile}} + +# RUN: not ld.lld -color-diagnostics=always -no-color-diagnostics /nosuchfile 2>&1 \ +# RUN: | FileCheck -check-prefix=NOCOLOR %s + +# NOCOLOR: ld.lld: error: cannot open /nosuchfile