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 {
}
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],
}
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",
}
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;
}