diff options
-rwxr-xr-x | src/browse.py | 131 | ||||
-rw-r--r-- | 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 '''<!DOCTYPE html> <style> body { @@ -63,47 +93,42 @@ body { } h1 { font-weight: normal; + font-size: 140%; text-align: center; margin: 0; } h2 { font-weight: normal; + font-size: 120%; } tt { font-family: WebKitHack, monospace; + white-space: nowrap; } -ul { - margin-top: 0; - padding-left: 20px; +.filelist { + -webkit-columns: auto 2; } </style>''' - print '<table width=500><tr><td colspan=3>' - print '<h1><tt>%s</tt></h1>' % node - print '</td></tr>' - - print '<tr><td valign=top>' - if input: - print '<h2>built by rule: <tt>%s</tt></h2>' % input[0] - if len(input) > 0: - print 'inputs:' - print '<ul>' - for i in input[1:]: - print '<li><tt><a href="?%s">%s</a></tt></li>' % (i, i) - print '</ul>' - else: - print '<h2>no input rule</h2>' - print '</td>' - print '<td width=50> </td>' - - print '<td valign=top>' - print '<h2>dependents</h2>' - for output in outputs: - print '<tt>%s</tt>' % output[0] - print '<ul>' - for i in output[1:]: - print '<li><tt><a href="?%s">%s</a></tt></li>' % (i, i) - print '</ul>' - print '</td></tr></table>' + + print '<h1><tt>%s</tt></h1>' % node.target + + if node.inputs: + print '<h2>target is built using rule <tt>%s</tt> of</h2>' % node.rule + if len(node.inputs) > 0: + print '<div class=filelist>' + for input, type in sorted(node.inputs): + extra = '' + if type: + extra = ' (%s)' % type + print '<tt><a href="?%s">%s</a>%s</tt><br>' % (input, input, extra) + print '</div>' + + if node.outputs: + print '<h2>dependent edges build:</h2>' + print '<div class=filelist>' + for output in sorted(node.outputs): + print '<tt><a href="?%s">%s</a></tt><br>' % (output, output) + print '</div>' 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<Node*>::iterator in = node->in_edge()->inputs_.begin(); - in != node->in_edge()->inputs_.end(); ++in) { - printf(" %s\n", (*in)->path().c_str()); - } - } - for (vector<Edge*>::const_iterator edge = node->out_edges().begin(); - edge != node->out_edges().end(); ++edge) { - printf(" output: %s\n", (*edge)->rule_->name().c_str()); - for (vector<Node*>::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<Edge*>::const_iterator edge = node->out_edges().begin(); + edge != node->out_edges().end(); ++edge) { + for (vector<Node*>::iterator out = (*edge)->outputs_.begin(); + out != (*edge)->outputs_.end(); ++out) { + printf(" %s\n", (*out)->path().c_str()); + } + } } return 0; } |