Reverted to correct commit this time.
[lldb.git] / libclc / build / ninja_syntax.py
1 #!/usr/bin/python
2
3 """Python module for generating .ninja files.
4
5 Note that this is emphatically not a required piece of Ninja; it's
6 just a helpful utility for build-file-generation systems that already
7 use Python.
8 """
9
10 import textwrap
11
12 class 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
105 def 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('$', '$$')