1f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org# This file comes from 2f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org# https://github.com/martine/ninja/blob/master/misc/ninja_syntax.py 3f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org# Do not edit! Edit the upstream one instead. 4f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 5f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org"""Python module for generating .ninja files. 6f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 7f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.orgNote that this is emphatically not a required piece of Ninja; it's 8f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.orgjust a helpful utility for build-file-generation systems that already 9f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.orguse Python. 10f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org""" 11f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 12f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.orgimport textwrap 135e8f653193e6839055ca8fb77eafcc2ad968f206thakis@chromium.orgimport re 145e8f653193e6839055ca8fb77eafcc2ad968f206thakis@chromium.org 15a77d1c0e8a279cf297bc1a3cc4f4e1e449e6a8c6thakis@chromium.orgdef escape_path(word): 16a77d1c0e8a279cf297bc1a3cc4f4e1e449e6a8c6thakis@chromium.org return word.replace('$ ','$$ ').replace(' ','$ ').replace(':', '$:') 17f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 18f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.orgclass Writer(object): 19f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org def __init__(self, output, width=78): 20f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org self.output = output 21f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org self.width = width 22f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 23f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org def newline(self): 24f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org self.output.write('\n') 25f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 26f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org def comment(self, text): 27f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org for line in textwrap.wrap(text, self.width - 2): 28f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org self.output.write('# ' + line + '\n') 29f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 30f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org def variable(self, key, value, indent=0): 315e8f653193e6839055ca8fb77eafcc2ad968f206thakis@chromium.org if value is None: 325e8f653193e6839055ca8fb77eafcc2ad968f206thakis@chromium.org return 335e8f653193e6839055ca8fb77eafcc2ad968f206thakis@chromium.org if isinstance(value, list): 34fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org value = ' '.join(filter(None, value)) # Filter out empty strings. 35f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org self._line('%s = %s' % (key, value), indent) 36f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 37a46c8832ac3d33d9d2ef8b743e968a7cd603c4d8thakis@chromium.org def pool(self, name, depth): 38a46c8832ac3d33d9d2ef8b743e968a7cd603c4d8thakis@chromium.org self._line('pool %s' % name) 39a46c8832ac3d33d9d2ef8b743e968a7cd603c4d8thakis@chromium.org self.variable('depth', depth, indent=1) 40a46c8832ac3d33d9d2ef8b743e968a7cd603c4d8thakis@chromium.org 415e8f653193e6839055ca8fb77eafcc2ad968f206thakis@chromium.org def rule(self, name, command, description=None, depfile=None, 42a46c8832ac3d33d9d2ef8b743e968a7cd603c4d8thakis@chromium.org generator=False, pool=None, restat=False, rspfile=None, 43a46c8832ac3d33d9d2ef8b743e968a7cd603c4d8thakis@chromium.org rspfile_content=None, deps=None): 44f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org self._line('rule %s' % name) 45f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org self.variable('command', command, indent=1) 46f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org if description: 47f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org self.variable('description', description, indent=1) 48f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org if depfile: 49f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org self.variable('depfile', depfile, indent=1) 505e8f653193e6839055ca8fb77eafcc2ad968f206thakis@chromium.org if generator: 515e8f653193e6839055ca8fb77eafcc2ad968f206thakis@chromium.org self.variable('generator', '1', indent=1) 52a46c8832ac3d33d9d2ef8b743e968a7cd603c4d8thakis@chromium.org if pool: 53a46c8832ac3d33d9d2ef8b743e968a7cd603c4d8thakis@chromium.org self.variable('pool', pool, indent=1) 54ecb67d9bce659fa56a54ae962661ded5b94d6901thakis@chromium.org if restat: 55ecb67d9bce659fa56a54ae962661ded5b94d6901thakis@chromium.org self.variable('restat', '1', indent=1) 567c97342c8eba9aa2418a6381e876a7d2bd03df12scottmg@chromium.org if rspfile: 577c97342c8eba9aa2418a6381e876a7d2bd03df12scottmg@chromium.org self.variable('rspfile', rspfile, indent=1) 587c97342c8eba9aa2418a6381e876a7d2bd03df12scottmg@chromium.org if rspfile_content: 597c97342c8eba9aa2418a6381e876a7d2bd03df12scottmg@chromium.org self.variable('rspfile_content', rspfile_content, indent=1) 60a46c8832ac3d33d9d2ef8b743e968a7cd603c4d8thakis@chromium.org if deps: 61a46c8832ac3d33d9d2ef8b743e968a7cd603c4d8thakis@chromium.org self.variable('deps', deps, indent=1) 62f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 63f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org def build(self, outputs, rule, inputs=None, implicit=None, order_only=None, 64f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org variables=None): 65f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org outputs = self._as_list(outputs) 66f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org all_inputs = self._as_list(inputs)[:] 67a77d1c0e8a279cf297bc1a3cc4f4e1e449e6a8c6thakis@chromium.org out_outputs = list(map(escape_path, outputs)) 68a77d1c0e8a279cf297bc1a3cc4f4e1e449e6a8c6thakis@chromium.org all_inputs = list(map(escape_path, all_inputs)) 69f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 70f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org if implicit: 71a77d1c0e8a279cf297bc1a3cc4f4e1e449e6a8c6thakis@chromium.org implicit = map(escape_path, self._as_list(implicit)) 72f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org all_inputs.append('|') 735e8f653193e6839055ca8fb77eafcc2ad968f206thakis@chromium.org all_inputs.extend(implicit) 74f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org if order_only: 75a77d1c0e8a279cf297bc1a3cc4f4e1e449e6a8c6thakis@chromium.org order_only = map(escape_path, self._as_list(order_only)) 76f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org all_inputs.append('||') 775e8f653193e6839055ca8fb77eafcc2ad968f206thakis@chromium.org all_inputs.extend(order_only) 78f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 79a46c8832ac3d33d9d2ef8b743e968a7cd603c4d8thakis@chromium.org self._line('build %s: %s' % (' '.join(out_outputs), 80a46c8832ac3d33d9d2ef8b743e968a7cd603c4d8thakis@chromium.org ' '.join([rule] + all_inputs))) 81f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 82f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org if variables: 8397bd1d8f114f5ca2e6f5dcb543fb338d4c2c4263thakis@chromium.org if isinstance(variables, dict): 84a46c8832ac3d33d9d2ef8b743e968a7cd603c4d8thakis@chromium.org iterator = iter(variables.items()) 8597bd1d8f114f5ca2e6f5dcb543fb338d4c2c4263thakis@chromium.org else: 8697bd1d8f114f5ca2e6f5dcb543fb338d4c2c4263thakis@chromium.org iterator = iter(variables) 8797bd1d8f114f5ca2e6f5dcb543fb338d4c2c4263thakis@chromium.org 8897bd1d8f114f5ca2e6f5dcb543fb338d4c2c4263thakis@chromium.org for key, val in iterator: 89f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org self.variable(key, val, indent=1) 90f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 91f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org return outputs 92f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 933fbdb4103aef78dbf6935c48ffbc216240d46deeevan@chromium.org def include(self, path): 943fbdb4103aef78dbf6935c48ffbc216240d46deeevan@chromium.org self._line('include %s' % path) 953fbdb4103aef78dbf6935c48ffbc216240d46deeevan@chromium.org 963fbdb4103aef78dbf6935c48ffbc216240d46deeevan@chromium.org def subninja(self, path): 973fbdb4103aef78dbf6935c48ffbc216240d46deeevan@chromium.org self._line('subninja %s' % path) 983fbdb4103aef78dbf6935c48ffbc216240d46deeevan@chromium.org 995e8f653193e6839055ca8fb77eafcc2ad968f206thakis@chromium.org def default(self, paths): 1005e8f653193e6839055ca8fb77eafcc2ad968f206thakis@chromium.org self._line('default %s' % ' '.join(self._as_list(paths))) 1015e8f653193e6839055ca8fb77eafcc2ad968f206thakis@chromium.org 102fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org def _count_dollars_before_index(self, s, i): 103fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org """Returns the number of '$' characters right in front of s[i].""" 104fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org dollar_count = 0 105fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org dollar_index = i - 1 106fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org while dollar_index > 0 and s[dollar_index] == '$': 107fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org dollar_count += 1 108fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org dollar_index -= 1 109fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org return dollar_count 110fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org 111f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org def _line(self, text, indent=0): 112f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org """Write 'text' word-wrapped at self.width characters.""" 113f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org leading_space = ' ' * indent 11497bd1d8f114f5ca2e6f5dcb543fb338d4c2c4263thakis@chromium.org while len(leading_space) + len(text) > self.width: 115f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org # The text is too wide; wrap if possible. 116f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 117fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org # Find the rightmost space that would obey our width constraint and 118fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org # that's not an escaped space. 119f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org available_space = self.width - len(leading_space) - len(' $') 120fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org space = available_space 121fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org while True: 122fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org space = text.rfind(' ', 0, space) 123fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org if space < 0 or \ 124fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org self._count_dollars_before_index(text, space) % 2 == 0: 125fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org break 126f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 127fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org if space < 0: 128fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org # No such space; just use the first unescaped space we can find. 129fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org space = available_space - 1 130fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org while True: 131fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org space = text.find(' ', space + 1) 132fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org if space < 0 or \ 133fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org self._count_dollars_before_index(text, space) % 2 == 0: 1345e8f653193e6839055ca8fb77eafcc2ad968f206thakis@chromium.org break 135fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org if space < 0: 136fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org # Give up on breaking. 137fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org break 1385e8f653193e6839055ca8fb77eafcc2ad968f206thakis@chromium.org 139fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org self.output.write(leading_space + text[0:space] + ' $\n') 140fd06f3b34a9cd1b393c3f7ddf5d6d8e80dc2fa06thakis@chromium.org text = text[space+1:] 141f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 142f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org # Subsequent lines are continuations, so indent them. 143f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org leading_space = ' ' * (indent+2) 144f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 145f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org self.output.write(leading_space + text + '\n') 146f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 147f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org def _as_list(self, input): 148f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org if input is None: 149f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org return [] 150f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org if isinstance(input, list): 151f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org return input 152f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org return [input] 153f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 154f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org 155f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.orgdef escape(string): 156f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org """Escape a string such that it can be embedded into a Ninja file without 157f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org further interpretation.""" 158f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org assert '\n' not in string, 'Ninja syntax does not allow newlines' 159f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org # We only have one special metacharacter: '$'. 160f040c6194657a5972871a96cb8c3572e9296ef37evan@chromium.org return string.replace('$', '$$') 161