blob: 6e8a87c7406843b011b194df3f9e42c6787e5318 [file] [log] [blame]
Peter Collingbourned5395fb2012-01-08 22:09:58 +00001#!/usr/bin/python
2
3"""Python module for generating .ninja files.
4
5Note that this is emphatically not a required piece of Ninja; it's
6just a helpful utility for build-file-generation systems that already
7use Python.
8"""
9
10import textwrap
11
12class Writer(object):
13 def __init__(self, output, width=78):
14 self.output = output
15 self.width = width
16
17 def newline(self):
18 self.output.write('\n')
19
20 def comment(self, text):
21 for line in textwrap.wrap(text, self.width - 2):
22 self.output.write('# ' + line + '\n')
23
24 def variable(self, key, value, indent=0):
25 if value is None:
26 return
27 if isinstance(value, list):
28 value = ' '.join(value)
29 self._line('%s = %s' % (key, value), indent)
30
31 def rule(self, name, command, description=None, depfile=None,
32 generator=False):
33 self._line('rule %s' % name)
34 self.variable('command', command, indent=1)
35 if description:
36 self.variable('description', description, indent=1)
37 if depfile:
38 self.variable('depfile', depfile, indent=1)
39 if generator:
40 self.variable('generator', '1', indent=1)
41
42 def build(self, outputs, rule, inputs=None, implicit=None, order_only=None,
43 variables=None):
44 outputs = self._as_list(outputs)
45 all_inputs = self._as_list(inputs)[:]
46
47 if implicit:
48 all_inputs.append('|')
49 all_inputs.extend(self._as_list(implicit))
50 if order_only:
51 all_inputs.append('||')
52 all_inputs.extend(self._as_list(order_only))
53
54 self._line('build %s: %s %s' % (' '.join(outputs),
55 rule,
56 ' '.join(all_inputs)))
57
58 if variables:
59 for key, val in variables:
60 self.variable(key, val, indent=1)
61
62 return outputs
63
64 def include(self, path):
65 self._line('include %s' % path)
66
67 def subninja(self, path):
68 self._line('subninja %s' % path)
69
70 def default(self, paths):
71 self._line('default %s' % ' '.join(self._as_list(paths)))
72
73 def _line(self, text, indent=0):
74 """Write 'text' word-wrapped at self.width characters."""
75 leading_space = ' ' * indent
76 while len(text) > self.width:
77 # The text is too wide; wrap if possible.
78
79 # Find the rightmost space that would obey our width constraint.
80 available_space = self.width - len(leading_space) - len(' $')
81 space = text.rfind(' ', 0, available_space)
82 if space < 0:
83 # No such space; just use the first space we can find.
84 space = text.find(' ', available_space)
85 if space < 0:
86 # Give up on breaking.
87 break
88
89 self.output.write(leading_space + text[0:space] + ' $\n')
90 text = text[space+1:]
91
92 # Subsequent lines are continuations, so indent them.
93 leading_space = ' ' * (indent+2)
94
95 self.output.write(leading_space + text + '\n')
96
97 def _as_list(self, input):
98 if input is None:
99 return []
100 if isinstance(input, list):
101 return input
102 return [input]
103
104
105def escape(string):
106 """Escape a string such that it can be embedded into a Ninja file without
107 further interpretation."""
108 assert '\n' not in string, 'Ninja syntax does not allow newlines'
109 # We only have one special metacharacter: '$'.
110 return string.replace('$', '$$')