1582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu"""Source List Parser 2582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 3582b5d869c0f05814d4d567636a743d3fdddf431Chia-I WuThe syntax of a source list file is a very small subset of GNU Make. These 4582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wufeatures are supported 5582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 64c15a77f27204620ed35d97d75e521ca982e1cc5José Fonseca operators: =, +=, := 7582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu line continuation 8582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu non-nested variable expansion 9582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu comment 10582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 11582b5d869c0f05814d4d567636a743d3fdddf431Chia-I WuThe goal is to allow Makefile's and SConscript's to share source listing. 12582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu""" 13582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 14582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wuclass SourceListParser(object): 15582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu def __init__(self): 16ea8dcfc90d5abbf699cd64be4dccd1e69fe82d75José Fonseca self.symbol_table = {} 17582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu self._reset() 18582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 19582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu def _reset(self, filename=None): 20582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu self.filename = filename 21582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 22582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu self.line_no = 1 23582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu self.line_cont = '' 24582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 25582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu def _error(self, msg): 26582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu raise RuntimeError('%s:%d: %s' % (self.filename, self.line_no, msg)) 27582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 28582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu def _next_dereference(self, val, cur): 29582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu """Locate the next $(...) in value.""" 30582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu deref_pos = val.find('$', cur) 31582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu if deref_pos < 0: 32582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu return (-1, -1) 33582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu elif val[deref_pos + 1] != '(': 34582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu self._error('non-variable dereference') 35582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 36582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu deref_end = val.find(')', deref_pos + 2) 37582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu if deref_end < 0: 38582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu self._error('unterminated variable dereference') 39582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 40582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu return (deref_pos, deref_end + 1) 41582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 42582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu def _expand_value(self, val): 43582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu """Perform variable expansion.""" 44582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu expanded = '' 45582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu cur = 0 46582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu while True: 47582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu deref_pos, deref_end = self._next_dereference(val, cur) 48582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu if deref_pos < 0: 49582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu expanded += val[cur:] 50582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu break 51582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 52582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu sym = val[(deref_pos + 2):(deref_end - 1)] 53582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu expanded += val[cur:deref_pos] + self.symbol_table[sym] 54582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu cur = deref_end 55582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 56582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu return expanded 57582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 58582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu def _parse_definition(self, line): 59582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu """Parse a variable definition line.""" 60582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu op_pos = line.find('=') 61582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu op_end = op_pos + 1 62582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu if op_pos < 0: 63582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu self._error('not a variable definition') 64582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 654c15a77f27204620ed35d97d75e521ca982e1cc5José Fonseca if op_pos > 0: 66ea606ee7b49d130fdedd5a707e79fc9e37ba280cJosé Fonseca if line[op_pos - 1] in [':', '+', '?']: 674c15a77f27204620ed35d97d75e521ca982e1cc5José Fonseca op_pos -= 1 68582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu else: 694c15a77f27204620ed35d97d75e521ca982e1cc5José Fonseca self._error('only =, :=, and += are supported') 70582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 71582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu # set op, sym, and val 72582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu op = line[op_pos:op_end] 73582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu sym = line[:op_pos].strip() 74582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu val = self._expand_value(line[op_end:].lstrip()) 75582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 764c15a77f27204620ed35d97d75e521ca982e1cc5José Fonseca if op in ('=', ':='): 77582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu self.symbol_table[sym] = val 78582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu elif op == '+=': 79582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu self.symbol_table[sym] += ' ' + val 80ea606ee7b49d130fdedd5a707e79fc9e37ba280cJosé Fonseca elif op == '?=': 81ea606ee7b49d130fdedd5a707e79fc9e37ba280cJosé Fonseca if sym not in self.symbol_table: 82ea606ee7b49d130fdedd5a707e79fc9e37ba280cJosé Fonseca self.symbol_table[sym] = val 83582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 84582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu def _parse_line(self, line): 85582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu """Parse a source list line.""" 86582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu # more lines to come 87582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu if line and line[-1] == '\\': 88582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu # spaces around "\\\n" are replaced by a single space 89582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu if self.line_cont: 90582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu self.line_cont += line[:-1].strip() + ' ' 91582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu else: 92582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu self.line_cont = line[:-1].rstrip() + ' ' 93582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu return 0 94582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 95582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu # combine with previous lines 96582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu if self.line_cont: 97582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu line = self.line_cont + line.lstrip() 98582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu self.line_cont = '' 99582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 100582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu if line: 101582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu begins_with_tab = (line[0] == '\t') 102582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 103582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu line = line.lstrip() 104582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu if line[0] != '#': 105582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu if begins_with_tab: 106582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu self._error('recipe line not supported') 107582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu else: 108582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu self._parse_definition(line) 109582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 110582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu return 1 111582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 112582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu def parse(self, filename): 113582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu """Parse a source list file.""" 114582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu if self.filename != filename: 115582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu fp = open(filename) 116582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu lines = fp.read().splitlines() 117582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu fp.close() 118582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 119582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu try: 120582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu self._reset(filename) 121582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu for line in lines: 122582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu self.line_no += self._parse_line(line) 123582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu except: 124582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu self._reset() 125582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu raise 126582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu 127582b5d869c0f05814d4d567636a743d3fdddf431Chia-I Wu return self.symbol_table 128ea8dcfc90d5abbf699cd64be4dccd1e69fe82d75José Fonseca 129ea8dcfc90d5abbf699cd64be4dccd1e69fe82d75José Fonseca def add_symbol(self, name, value): 130ea8dcfc90d5abbf699cd64be4dccd1e69fe82d75José Fonseca self.symbol_table[name] = value 131