From 982de2ff79a1d862026763bcff17582ebeef5a5d Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Tue, 24 Jan 2012 15:56:15 -0800 Subject: rearrange query/browse output to be more sensible --- src/browse.py | 131 ++++++++++++++++++++++++++++++++++------------------------ src/ninja.cc | 40 ++++++++++-------- 2 files changed, 100 insertions(+), 71 deletions(-) diff --git a/src/browse.py b/src/browse.py index ca95197..17e67cf 100755 --- a/src/browse.py +++ b/src/browse.py @@ -24,36 +24,66 @@ import BaseHTTPServer import subprocess import sys import webbrowser +from collections import namedtuple -def match_strip(prefix, line): +Node = namedtuple('Node', ['inputs', 'rule', 'target', 'outputs']) + +# Ideally we'd allow you to navigate to a build edge or a build node, +# with appropriate views for each. But there's no way to *name* a build +# edge so we can only display nodes. +# +# For a given node, it has at most one input edge, which has n +# different inputs. This becomes node.inputs. (We leave out the +# outputs of the input edge due to what follows.) The node can have +# multiple dependent output edges. Rather than attempting to display +# those, they are summarized by taking the union of all their outputs. +# +# This means there's no single view that shows you all inputs and outputs +# of an edge. But I think it's less confusing than alternatives. + +def match_strip(line, prefix): if not line.startswith(prefix): - print prefix, line - assert line.startswith(prefix) - return line[len(prefix):] + return (False, line) + return (True, line[len(prefix):]) def parse(text): - lines = text.split('\n') - node = lines.pop(0) - node = node[:-1] # strip trailing colon - - input = [] - if lines and lines[0].startswith(' input:'): - input.append(match_strip(' input: ', lines.pop(0))) - while lines and lines[0].startswith(' '): - input.append(lines.pop(0).strip()) + lines = iter(text.split('\n')) + target = None + rule = None + inputs = [] outputs = [] - while lines: - output = [] - output.append(match_strip(' output: ', lines.pop(0))) - while lines and lines[0].startswith(' '): - output.append(lines.pop(0).strip()) - outputs.append(output) - - return (node, input, outputs) -def generate_html(data): - node, input, outputs = data + try: + target = lines.next()[:-1] # strip trailing colon + + line = lines.next() + (match, rule) = match_strip(line, ' input: ') + if match: + (match, line) = match_strip(lines.next(), ' ') + while match: + type = None + (match, line) = match_strip(line, '| ') + if match: + type = 'implicit' + (match, line) = match_strip(line, '|| ') + if match: + type = 'order-only' + inputs.append((line, type)) + (match, line) = match_strip(lines.next(), ' ') + + match, _ = match_strip(line, ' outputs:') + if match: + (match, line) = match_strip(lines.next(), ' ') + while match: + outputs.append(line) + (match, line) = match_strip(lines.next(), ' ') + except StopIteration: + pass + + return Node(inputs, rule, target, outputs) + +def generate_html(node): print ''' ''' - print '' - - print '' - print '' - - print '
' - print '

%s

' % node - print '
' - if input: - print '

built by rule: %s

' % input[0] - if len(input) > 0: - print 'inputs:' - print '
    ' - for i in input[1:]: - print '
  • %s
  • ' % (i, i) - print '
' - else: - print '

no input rule

' - print '
 ' - print '

dependents

' - for output in outputs: - print '%s' % output[0] - print '
    ' - for i in output[1:]: - print '
  • %s
  • ' % (i, i) - print '
' - print '
' + + print '

%s

' % node.target + + if node.inputs: + print '

target is built using rule %s of

' % node.rule + if len(node.inputs) > 0: + print '
' + for input, type in sorted(node.inputs): + extra = '' + if type: + extra = ' (%s)' % type + print '%s%s
' % (input, input, extra) + print '
' + + if node.outputs: + print '

dependent edges build:

' + print '
' + for output in sorted(node.outputs): + print '%s
' % (output, output) + print '
' def ninja_dump(target): proc = subprocess.Popen([sys.argv[1], '-t', 'query', target], diff --git a/src/ninja.cc b/src/ninja.cc index 4e750f4..1822e54 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -224,24 +224,7 @@ int ToolQuery(Globals* globals, int argc, char* argv[]) { } for (int i = 0; i < argc; ++i) { Node* node = globals->state->LookupNode(argv[i]); - if (node) { - printf("%s:\n", argv[i]); - if (node->in_edge()) { - printf(" input: %s\n", node->in_edge()->rule_->name().c_str()); - for (vector::iterator in = node->in_edge()->inputs_.begin(); - in != node->in_edge()->inputs_.end(); ++in) { - printf(" %s\n", (*in)->path().c_str()); - } - } - for (vector::const_iterator edge = node->out_edges().begin(); - edge != node->out_edges().end(); ++edge) { - printf(" output: %s\n", (*edge)->rule_->name().c_str()); - for (vector::iterator out = (*edge)->outputs_.begin(); - out != (*edge)->outputs_.end(); ++out) { - printf(" %s\n", (*out)->path().c_str()); - } - } - } else { + if (!node) { Node* suggestion = globals->state->SpellcheckNode(argv[i]); if (suggestion) { printf("%s unknown, did you mean %s?\n", @@ -251,6 +234,27 @@ int ToolQuery(Globals* globals, int argc, char* argv[]) { } return 1; } + + printf("%s:\n", argv[i]); + if (Edge* edge = node->in_edge()) { + printf(" input: %s\n", edge->rule_->name().c_str()); + for (int in = 0; in < (int)edge->inputs_.size(); in++) { + const char* label = ""; + if (edge->is_implicit(in)) + label = "| "; + else if (edge->is_order_only(in)) + label = "|| "; + printf(" %s%s\n", label, edge->inputs_[in]->path().c_str()); + } + } + printf(" outputs:\n"); + for (vector::const_iterator edge = node->out_edges().begin(); + edge != node->out_edges().end(); ++edge) { + for (vector::iterator out = (*edge)->outputs_.begin(); + out != (*edge)->outputs_.end(); ++out) { + printf(" %s\n", (*out)->path().c_str()); + } + } } return 0; } -- cgit v0.12