From 1ab2cae66f685a6c5d61dd200b5d4f386ec2c937 Mon Sep 17 00:00:00 2001 From: Ross Younger Date: Mon, 21 Aug 2017 09:15:24 +1200 Subject: [PATCH 1/4] Basic safe and switchable output colourisation --- muddled/colourize.py | 130 +++++++++++++++++++++++++++++++++++++++++++ muddled/commands.py | 3 +- 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 muddled/colourize.py diff --git a/muddled/colourize.py b/muddled/colourize.py new file mode 100644 index 0000000..b5444b2 --- /dev/null +++ b/muddled/colourize.py @@ -0,0 +1,130 @@ +# -*- coding: utf-8 -*- +""" +Output colourisation +Inspired by colorgcc, this makes long gcc output easier on the eye. +(Why yes, I do know about gcc 4.9's ability to colour to the terminal. But in +this muddled world, gcc is not outputting to a tty, so doesn't colourise. To +force it, you'd have to set a cflag in every last package, so it's much better +to do this centrally. Doing it this way also picks up gcc-like errors from +other tools.) +""" + +import os +import re +import sys +from collections import defaultdict +# Requires colorama to be able to colour, but won't fall over if it's not there. +# - 'apt install python-colorama' or 'pip install colorama' +try: + from colorama import Fore, Back, Style + + colour = { + # These are all the styles we might set + 'reset': Style.RESET_ALL, + 'filename': Fore.WHITE + Style.BRIGHT, + 'line_num': Fore.WHITE + Style.BRIGHT, + 'line_pos': Fore.WHITE + Style.BRIGHT, + 'quoted': Fore.WHITE + Style.BRIGHT, + + 'error': Fore.RED + Style.BRIGHT, + 'warning': Fore.MAGENTA + Style.BRIGHT, + 'note': Fore.CYAN, + 'intro': Fore.CYAN, + 'introquote': Fore.CYAN, + } + have_colour = True +except ImportError: + # Quietly fall back to uncoloured behaviour + colour = defaultdict(lambda:'') + have_colour = False + +def wrap_colour(string, col): + ''' Colourises a string ''' + return colour[col] + string + colour['reset'] + +def colour_quote(string, inside): + ''' colourises things found between quotes and smart quotes + @inside@ is a member of the colour dict, it is applied to whatever is found inside quotes + ''' + # blah blah `thing to highlight' blah + string = re.sub(r'`([^\']+)\'', '`' + colour[inside] + r'\1' + colour['reset'] + "'", string) + # blah blah ‘smart quoted thing’ blah + string = re.sub(r'‘([^’]+)’', '‘' + colour[inside] + r'\1' + colour['reset'] + '’', string) + # sometimes gcc doesn't even bother with the backquote when muddle is capturing the output? + string = re.sub(r'\'([^\']+)\'', '\'' + colour[inside] + r'\1' + colour['reset'] + "'", string) + return string + +# map keywords to colours +gcc_keywords = { + 'error:' : 'error', + 'warning:' : 'warning', + 'note:' : 'note', + 'instantiated from' : 'intro', + 'undefined reference' : 'error', + 'multiple definition of' : 'error', + } + +def keyword_map(string, mapdict): + ''' Colourises keywords in a string ''' + for k in mapdict: + string = string.replace(k, wrap_colour(k, mapdict[k])) + return string + +def colour_filename(filename): + ''' Part-colourises a filename. + (Muddle paths are often quite long and dangly, and we generally aren't + interested in all of them.) + ''' + parts = filename.split('/') + # We'll colour up to 3 components. + limit = min(3, len(parts)) + for i in range(-limit,0): + parts[i] = wrap_colour(parts[i], 'filename') + return '/'.join(parts) + +def colour_gcc(reresult): + ''' Colourises gcc and gcc-like output. + @reresult@ is a regexp match. + groups 1 and 2 are filename and line number. + group 3 is the position in the line, may not be present. + group 4 is the rest of the line - scan for keywords and smart quotes. + ''' + (filename, lineno, linepos, msg) = reresult.group(1,2,3,4) + if linepos is not None: + linepos = wrap_colour(linepos, 'line_pos') + ':' + else: + linepos = '' + msg = keyword_map(msg, gcc_keywords) + msg = colour_quote(msg, 'quoted') + return colour_filename(filename) + ':' + wrap_colour(lineno, 'line_num') + ':' + linepos + msg + +def colour_one_line(l): + # Deal with most gcc and gcc-like output, including linker errors: + result = re.search(r'^(.+?\.[^:/ ]+):([0-9]+):([0-9]+)?:?(.*)$', l) # file:line[:pos]:msg + if result: + return colour_gcc(result) + # Otherwise, look for introductions: In function 'int blah(void)': + return colour_quote(l, 'introquote') + +def colourize(msg): + ''' Colourizes a multi-line message ''' + sw = os.getenv('MUDDLE_COLOURISE', 'auto') + if sw=='auto': + sw = '1' if sys.stdout.isatty() else '0' + if not (sw.lower() in ('yes','y','true','1')): + # we're not enabled, do nothing + return msg + output = [] + if not have_colour: + return str(msg) + ''' + +(muddle says: By the way, I would have colourised this output but you don\'t + have the colorama package installed. + `apt install python-colorama' or `pip install colorama' to make it work. + Or set MUDDLE_COLOURISE=no in your environment to shut this message up.)''' + + lines = str(msg).splitlines(True) + output = [] + for l in lines: + output.append( colour_one_line(l.strip()) +'\n') + return ''.join(output) diff --git a/muddled/commands.py b/muddled/commands.py index fa095fd..57a16eb 100644 --- a/muddled/commands.py +++ b/muddled/commands.py @@ -42,6 +42,7 @@ import muddled.version_control as version_control import muddled.docreport +from muddled.colourize import colourize from muddled.db import Database, InstructionFile from muddled.depend import Label, label_list_to_string from muddled.utils import GiveUp, MuddleBug, Unsupported, \ @@ -848,7 +849,7 @@ def build_labels(builder, to_build): for lbl in to_build: builder.build_label(lbl) except GiveUp,e: - raise GiveUp("Can't build %s - %s"%(str(lbl), e)) + raise GiveUp("Can't build %s - %s"%(str(lbl), colourize(e))) # ============================================================================= # Actual commands From 6a18557c9dd4e17df0f0f7a202bb56ad31c55739 Mon Sep 17 00:00:00 2001 From: Ross Younger Date: Mon, 21 Aug 2017 09:36:47 +1200 Subject: [PATCH 2/4] colourize: Add HTML output styling for use by CI systems --- muddled/colourize.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/muddled/colourize.py b/muddled/colourize.py index b5444b2..4768fae 100644 --- a/muddled/colourize.py +++ b/muddled/colourize.py @@ -12,7 +12,8 @@ import os import re import sys -from collections import defaultdict +from collections import defaultdict,Container + # Requires colorama to be able to colour, but won't fall over if it's not there. # - 'apt install python-colorama' or 'pip install colorama' try: @@ -38,6 +39,18 @@ colour = defaultdict(lambda:'') have_colour = False +if os.getenv('MUDDLE_COLOURISE') == 'html': + # Alternate mode for HTML output.. wrap text up in CSS classes + class htmlcolour(Container): + def __getitem__(self, key): + if key=='reset': + return '' + return ''%key + def __contains__(self): + return True + colour = htmlcolour() + have_colour = True + def wrap_colour(string, col): ''' Colourises a string ''' return colour[col] + string + colour['reset'] @@ -111,7 +124,7 @@ def colourize(msg): sw = os.getenv('MUDDLE_COLOURISE', 'auto') if sw=='auto': sw = '1' if sys.stdout.isatty() else '0' - if not (sw.lower() in ('yes','y','true','1')): + if not (sw.lower() in ('yes','y','true','1','html')): # we're not enabled, do nothing return msg output = [] From 56582fb8f8681c9ebe24bed59a7f68ccea5f0d31 Mon Sep 17 00:00:00 2001 From: Ross Younger Date: Mon, 21 Aug 2017 09:46:10 +1200 Subject: [PATCH 3/4] Document colourisation --- docs/jottings.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/jottings.txt b/docs/jottings.txt index 4154c91..47939d8 100644 --- a/docs/jottings.txt +++ b/docs/jottings.txt @@ -540,3 +540,21 @@ dictionaries and so on into the main domain. We can then discard the old Builder, since they're no longer of any interest. .. vim: set filetype=rst tabstop=8 softtabstop=2 shiftwidth=2 expandtab: + +Colourised output +================= + +Since gcc tends to output in colour these days, wouldn't it would be nice +if you could get the same colour coding from muddle when things go wrong? +Well now you can, and it's enabled by default. To make it work, all you +need to do is ``apt install python-colorama`` or ``pip install colorama``. + +Colourisation is controlled by the ``MUDDLE_COLOURISE`` environment +variable. The known values are: + +* ``auto`` (default): Detect whether muddle is running in a tty and colourise +if so. +* ``yes``: Always colourise +* ``no``: Never colourise +* ``html``: Always colourise for HTML. This is for use with Continuous +Integration systems. From c9e5bc75c1f2794c20a45bfcf5c9daf4ef0d14d8 Mon Sep 17 00:00:00 2001 From: Ross Younger Date: Tue, 14 Nov 2017 07:38:56 +1300 Subject: [PATCH 4/4] visdep: Give goals a different colour to aid in picking them out amongst a myriad of dependencies --- sandbox/visualise-dependencies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sandbox/visualise-dependencies.py b/sandbox/visualise-dependencies.py index bd8c285..cb07d3d 100755 --- a/sandbox/visualise-dependencies.py +++ b/sandbox/visualise-dependencies.py @@ -232,7 +232,7 @@ def process(args): raise GiveUp("None of the given goals %s is a target"%(map(str, goals))) for g in full_goals: - Node(g, isGoal=True, extras="shape=parallelogram") + Node(g, isGoal=True, extras="shape=parallelogram, fillcolor=gold") # color=green fillcolor=green style=filled...? for g in full_goals: