diff --git a/waflib/Logs.py b/waflib/Logs.py index bfc667fc..a7f41cf2 100644 --- a/waflib/Logs.py +++ b/waflib/Logs.py @@ -7,51 +7,18 @@ logging, colors, terminal width and pretty-print """ import os, re, traceback, sys +from waflib import ansiterm -_nocolor = os.environ.get('NOCOLOR', 'no') not in ('no', '0', 'false') -try: - if not _nocolor: - import waflib.ansiterm -except ImportError: - pass +if not os.environ.get('NOSYNC', False): + # synchronized output is nearly mandatory to prevent garbled output + if sys.stdout.isatty() and id(sys.stdout) == id(sys.__stdout__): + sys.stdout = ansiterm.AnsiTerm(sys.stdout) + os.environ['TERM'] = 'vt100' # <- not sure about this + if sys.stderr.isatty() and id(sys.stderr) == id(sys.__stderr__): + sys.stderr = ansiterm.AnsiTerm(sys.stderr) + os.environ['TERM'] = 'vt100' # <- not sure about this -try: - import threading -except ImportError: - if not 'JOBS' in os.environ: - # no threading :-( - os.environ['JOBS'] = '1' -else: - wlock = threading.Lock() - - class sync_stream(object): - def __init__(self, stream): - self.stream = stream - self.encoding = self.stream.encoding - - def write(self, txt): - try: - wlock.acquire() - self.stream.write(txt) - self.stream.flush() - finally: - wlock.release() - - def fileno(self): - return self.stream.fileno() - - def flush(self): - self.stream.flush() - - def isatty(self): - return self.stream.isatty() - - if not os.environ.get('NOSYNC', False): - if id(sys.stdout) == id(sys.__stdout__): - sys.stdout = sync_stream(sys.stdout) - sys.stderr = sync_stream(sys.stderr) - -import logging # import other modules only after +import logging # the logging module keeps holds reference on sys.stderr LOG_FORMAT = "%(asctime)s %(c1)s%(zone)s%(c2)s %(message)s" HOUR_FORMAT = "%H:%M:%S" @@ -73,15 +40,17 @@ colors_lst = { 'cursor_off' :'\x1b[?25l', } -got_tty = not os.environ.get('TERM', 'dumb') in ['dumb', 'emacs'] -if got_tty: - try: - got_tty = sys.stderr.isatty() and sys.stdout.isatty() - except AttributeError: - got_tty = False - -if (not got_tty and os.environ.get('TERM', 'dumb') != 'msys') or _nocolor: - colors_lst['USE'] = False +def enable_colors(level): + if level == 0: + colors_lst['USE'] = False + elif level == 1: + # and here we guess + term = os.environ.get('TERM', '') + if term in ['dumb', 'emacs']: + colors_lst['USE'] = False + pass + else: + colors_lst['USE'] = True def get_term_cols(): return 80 diff --git a/waflib/Options.py b/waflib/Options.py index 52a79cdc..9fd67965 100644 --- a/waflib/Options.py +++ b/waflib/Options.py @@ -14,6 +14,13 @@ as well as custom ones, used by the ``options`` wscript function. import os, tempfile, optparse, sys, re from waflib import Logs, Utils, Context +try: + import threading +except ImportError: + if not 'JOBS' in os.environ: + # no threading :-( + os.environ['JOBS'] = '1' + cmds = 'distclean configure build install clean uninstall check dist distcheck'.split() """ Constant representing the default waf commands displayed in:: @@ -51,6 +58,7 @@ class opt_parser(optparse.OptionParser): self.ctx = ctx jobs = ctx.jobs() + p('-c', '--color', dest='colors', default='auto', action='store', help='whether to use colors (always/auto/never) [default: auto]') p('-j', '--jobs', dest='jobs', default=jobs, type='int', help='amount of parallel jobs (%r)' % jobs) p('-k', '--keep', dest='keep', default=0, action='count', help='keep running happily even if errors are found') p('-v', '--verbose', dest='verbose', default=0, action='count', help='verbosity level -v -vv or -vvv [default: 0]') @@ -242,6 +250,15 @@ class OptionsContext(Context.Context): if options.verbose >= 1: self.load('errcheck') + try: + colors = {'always' : 2, 'auto' : 1, 'never' : 0}[options.colors] + except KeyError: + self.parser.error('Bad option "%s" for --color option' % options.colors) + else: + if os.environ.get('NOCOLOR', ''): + colors = 0 + Logs.enable_colors(colors) + def execute(self): """ See :py:func:`waflib.Context.Context.execute` diff --git a/waflib/ansiterm.py b/waflib/ansiterm.py index 864d7c43..74835ccd 100644 --- a/waflib/ansiterm.py +++ b/waflib/ansiterm.py @@ -11,12 +11,35 @@ console commands. """ -import sys, os, re, threading +import re +from waflib.Utils import threading + +wlock = threading.Lock() try: from ctypes import Structure, windll, c_short, c_ulong, c_int, byref, c_wchar except ImportError: - pass + class AnsiTerm(object): + def __init__(self, stream): + self.stream = stream + self.encoding = self.stream.encoding + + def write(self, txt): + try: + wlock.acquire() + self.stream.write(txt) + self.stream.flush() + finally: + wlock.release() + + def fileno(self): + return self.stream.fileno() + + def flush(self): + self.stream.flush() + + def isatty(self): + return self.stream.isatty() else: class COORD(Structure): @@ -37,7 +60,6 @@ else: _type = str to_int = lambda number, default: number and int(number) or default - wlock = threading.Lock() STD_OUTPUT_HANDLE = -11 STD_ERROR_HANDLE = -12 @@ -230,6 +252,8 @@ else: else: self.writeconsole(txt) else: + # no support for colors in the console, just output the text: + # eclipse or msys may be able to interpret the escape sequences self.stream.write(text) finally: wlock.release() @@ -265,8 +289,4 @@ else: def isatty(self): return self._isatty - if sys.stdout.isatty() or sys.stderr.isatty(): - sys.stderr = AnsiTerm(sys.stderr) - sys.stdout = AnsiTerm(sys.stdout) - os.environ['TERM'] = 'vt100'