1*90c8c64dSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*90c8c64dSAndroid Build Coastguard Worker 3*90c8c64dSAndroid Build Coastguard Worker"""Ninja File Parser. 4*90c8c64dSAndroid Build Coastguard Worker""" 5*90c8c64dSAndroid Build Coastguard Worker 6*90c8c64dSAndroid Build Coastguard Workerfrom __future__ import print_function 7*90c8c64dSAndroid Build Coastguard Worker 8*90c8c64dSAndroid Build Coastguard Workerimport argparse 9*90c8c64dSAndroid Build Coastguard Workerimport collections 10*90c8c64dSAndroid Build Coastguard Workerimport os 11*90c8c64dSAndroid Build Coastguard Workerimport re 12*90c8c64dSAndroid Build Coastguard Workerimport struct 13*90c8c64dSAndroid Build Coastguard Workerimport sys 14*90c8c64dSAndroid Build Coastguard Worker 15*90c8c64dSAndroid Build Coastguard Workertry: 16*90c8c64dSAndroid Build Coastguard Worker import cPickle as pickle # Python 2 17*90c8c64dSAndroid Build Coastguard Workerexcept ImportError: 18*90c8c64dSAndroid Build Coastguard Worker import pickle # Python 3 19*90c8c64dSAndroid Build Coastguard Worker 20*90c8c64dSAndroid Build Coastguard Workertry: 21*90c8c64dSAndroid Build Coastguard Worker from cStringIO import StringIO # Python 2 22*90c8c64dSAndroid Build Coastguard Workerexcept ImportError: 23*90c8c64dSAndroid Build Coastguard Worker from io import StringIO # Python 3 24*90c8c64dSAndroid Build Coastguard Worker 25*90c8c64dSAndroid Build Coastguard Workertry: 26*90c8c64dSAndroid Build Coastguard Worker from sys import intern 27*90c8c64dSAndroid Build Coastguard Workerexcept ImportError: 28*90c8c64dSAndroid Build Coastguard Worker pass # In Python 2, intern() is a built-in function. 29*90c8c64dSAndroid Build Coastguard Worker 30*90c8c64dSAndroid Build Coastguard Workerif sys.version_info < (3,): 31*90c8c64dSAndroid Build Coastguard Worker # Wrap built-in open() function to ignore encoding in Python 2. 32*90c8c64dSAndroid Build Coastguard Worker _builtin_open = open 33*90c8c64dSAndroid Build Coastguard Worker def open(path, mode, encoding=None): 34*90c8c64dSAndroid Build Coastguard Worker return _builtin_open(path, mode) 35*90c8c64dSAndroid Build Coastguard Worker 36*90c8c64dSAndroid Build Coastguard Worker # Replace built-in zip() function with itertools.izip 37*90c8c64dSAndroid Build Coastguard Worker from itertools import izip as zip 38*90c8c64dSAndroid Build Coastguard Worker 39*90c8c64dSAndroid Build Coastguard Worker 40*90c8c64dSAndroid Build Coastguard Workerclass EvalEnv(dict): 41*90c8c64dSAndroid Build Coastguard Worker __slots__ = ('parent') 42*90c8c64dSAndroid Build Coastguard Worker 43*90c8c64dSAndroid Build Coastguard Worker 44*90c8c64dSAndroid Build Coastguard Worker def __init__(self, *args, **kwargs): 45*90c8c64dSAndroid Build Coastguard Worker super(EvalEnv, self).__init__(*args, **kwargs) 46*90c8c64dSAndroid Build Coastguard Worker self.parent = None 47*90c8c64dSAndroid Build Coastguard Worker 48*90c8c64dSAndroid Build Coastguard Worker 49*90c8c64dSAndroid Build Coastguard Worker def get_recursive(self, key, default=None): 50*90c8c64dSAndroid Build Coastguard Worker try: 51*90c8c64dSAndroid Build Coastguard Worker return self[key] 52*90c8c64dSAndroid Build Coastguard Worker except KeyError: 53*90c8c64dSAndroid Build Coastguard Worker if self.parent: 54*90c8c64dSAndroid Build Coastguard Worker return self.parent.get_recursive(key, default) 55*90c8c64dSAndroid Build Coastguard Worker return default 56*90c8c64dSAndroid Build Coastguard Worker 57*90c8c64dSAndroid Build Coastguard Worker 58*90c8c64dSAndroid Build Coastguard Workerclass BuildEvalEnv(EvalEnv): 59*90c8c64dSAndroid Build Coastguard Worker __slots__ = ('_build_env', '_rule_env') 60*90c8c64dSAndroid Build Coastguard Worker 61*90c8c64dSAndroid Build Coastguard Worker 62*90c8c64dSAndroid Build Coastguard Worker def __init__(self, build_env, rule_env): 63*90c8c64dSAndroid Build Coastguard Worker self._build_env = build_env 64*90c8c64dSAndroid Build Coastguard Worker self._rule_env = rule_env 65*90c8c64dSAndroid Build Coastguard Worker 66*90c8c64dSAndroid Build Coastguard Worker 67*90c8c64dSAndroid Build Coastguard Worker def get_recursive(self, key, default=None): 68*90c8c64dSAndroid Build Coastguard Worker try: 69*90c8c64dSAndroid Build Coastguard Worker return self._build_env[key] 70*90c8c64dSAndroid Build Coastguard Worker except KeyError: 71*90c8c64dSAndroid Build Coastguard Worker pass 72*90c8c64dSAndroid Build Coastguard Worker 73*90c8c64dSAndroid Build Coastguard Worker if self._rule_env: 74*90c8c64dSAndroid Build Coastguard Worker try: 75*90c8c64dSAndroid Build Coastguard Worker return self._rule_env[key] 76*90c8c64dSAndroid Build Coastguard Worker except KeyError: 77*90c8c64dSAndroid Build Coastguard Worker pass 78*90c8c64dSAndroid Build Coastguard Worker 79*90c8c64dSAndroid Build Coastguard Worker if self._build_env.parent: 80*90c8c64dSAndroid Build Coastguard Worker return self._build_env.parent.get_recursive(key, default) 81*90c8c64dSAndroid Build Coastguard Worker return default 82*90c8c64dSAndroid Build Coastguard Worker 83*90c8c64dSAndroid Build Coastguard Worker 84*90c8c64dSAndroid Build Coastguard Workerclass EvalError(ValueError): 85*90c8c64dSAndroid Build Coastguard Worker """Exceptions for ``EvalString`` evalution errors.""" 86*90c8c64dSAndroid Build Coastguard Worker pass 87*90c8c64dSAndroid Build Coastguard Worker 88*90c8c64dSAndroid Build Coastguard Worker 89*90c8c64dSAndroid Build Coastguard Workerclass EvalCircularError(EvalError): 90*90c8c64dSAndroid Build Coastguard Worker """Exception for circular substitution in ``EvalString``.""" 91*90c8c64dSAndroid Build Coastguard Worker 92*90c8c64dSAndroid Build Coastguard Worker 93*90c8c64dSAndroid Build Coastguard Worker def __init__(self, expanded_vars): 94*90c8c64dSAndroid Build Coastguard Worker super(EvalCircularError, self).__init__( 95*90c8c64dSAndroid Build Coastguard Worker 'circular evaluation: ' + ' -> '.join(expanded_vars)) 96*90c8c64dSAndroid Build Coastguard Worker 97*90c8c64dSAndroid Build Coastguard Worker 98*90c8c64dSAndroid Build Coastguard Workerclass EvalString(tuple): 99*90c8c64dSAndroid Build Coastguard Worker """Strings with variables to be substituted.""" 100*90c8c64dSAndroid Build Coastguard Worker 101*90c8c64dSAndroid Build Coastguard Worker 102*90c8c64dSAndroid Build Coastguard Worker def __bool__(self): 103*90c8c64dSAndroid Build Coastguard Worker """Check whether this is an empty string.""" 104*90c8c64dSAndroid Build Coastguard Worker return len(self) > 1 105*90c8c64dSAndroid Build Coastguard Worker 106*90c8c64dSAndroid Build Coastguard Worker 107*90c8c64dSAndroid Build Coastguard Worker def __nonzero__(self): 108*90c8c64dSAndroid Build Coastguard Worker """Check whether this is an empty string (Python2).""" 109*90c8c64dSAndroid Build Coastguard Worker return self.__bool__() 110*90c8c64dSAndroid Build Coastguard Worker 111*90c8c64dSAndroid Build Coastguard Worker 112*90c8c64dSAndroid Build Coastguard Worker def create_iters(self): 113*90c8c64dSAndroid Build Coastguard Worker """Create descriptors and segments iterators.""" 114*90c8c64dSAndroid Build Coastguard Worker curr_iter = iter(self) 115*90c8c64dSAndroid Build Coastguard Worker descs = next(curr_iter) 116*90c8c64dSAndroid Build Coastguard Worker return zip(descs, curr_iter) 117*90c8c64dSAndroid Build Coastguard Worker 118*90c8c64dSAndroid Build Coastguard Worker 119*90c8c64dSAndroid Build Coastguard Workerdef _eval_string(s, env, expanded_vars, result_buf): 120*90c8c64dSAndroid Build Coastguard Worker """Evaluate each segments in ``EvalString`` and write result to the 121*90c8c64dSAndroid Build Coastguard Worker given StringIO buffer. 122*90c8c64dSAndroid Build Coastguard Worker 123*90c8c64dSAndroid Build Coastguard Worker Args: 124*90c8c64dSAndroid Build Coastguard Worker env: A ``dict`` that maps a name to ``EvalString`` object. 125*90c8c64dSAndroid Build Coastguard Worker expanded_vars: A ``list`` that keeps the variable under evaluation. 126*90c8c64dSAndroid Build Coastguard Worker result_buf: Output buffer. 127*90c8c64dSAndroid Build Coastguard Worker """ 128*90c8c64dSAndroid Build Coastguard Worker if type(s) is str: 129*90c8c64dSAndroid Build Coastguard Worker result_buf.write(s) 130*90c8c64dSAndroid Build Coastguard Worker return 131*90c8c64dSAndroid Build Coastguard Worker 132*90c8c64dSAndroid Build Coastguard Worker for desc, seg in s.create_iters(): 133*90c8c64dSAndroid Build Coastguard Worker if desc == 't': 134*90c8c64dSAndroid Build Coastguard Worker # Append raw text 135*90c8c64dSAndroid Build Coastguard Worker result_buf.write(seg) 136*90c8c64dSAndroid Build Coastguard Worker else: 137*90c8c64dSAndroid Build Coastguard Worker # Substitute variables 138*90c8c64dSAndroid Build Coastguard Worker varname = seg 139*90c8c64dSAndroid Build Coastguard Worker if varname in expanded_vars: 140*90c8c64dSAndroid Build Coastguard Worker raise EvalCircularError(expanded_vars + [varname]) 141*90c8c64dSAndroid Build Coastguard Worker expanded_vars.append(varname) 142*90c8c64dSAndroid Build Coastguard Worker try: 143*90c8c64dSAndroid Build Coastguard Worker next_es = env.get_recursive(varname) 144*90c8c64dSAndroid Build Coastguard Worker if next_es: 145*90c8c64dSAndroid Build Coastguard Worker _eval_string(next_es, env, expanded_vars, result_buf) 146*90c8c64dSAndroid Build Coastguard Worker finally: 147*90c8c64dSAndroid Build Coastguard Worker expanded_vars.pop() 148*90c8c64dSAndroid Build Coastguard Worker 149*90c8c64dSAndroid Build Coastguard Worker 150*90c8c64dSAndroid Build Coastguard Workerdef eval_string(s, env): 151*90c8c64dSAndroid Build Coastguard Worker """Evaluate a ``str`` or ``EvalString`` in an environment. 152*90c8c64dSAndroid Build Coastguard Worker 153*90c8c64dSAndroid Build Coastguard Worker Args: 154*90c8c64dSAndroid Build Coastguard Worker env: A ``dict`` that maps a name to an ``EvalString`` object. 155*90c8c64dSAndroid Build Coastguard Worker 156*90c8c64dSAndroid Build Coastguard Worker Returns: 157*90c8c64dSAndroid Build Coastguard Worker str: The result of evaluation. 158*90c8c64dSAndroid Build Coastguard Worker 159*90c8c64dSAndroid Build Coastguard Worker Raises: 160*90c8c64dSAndroid Build Coastguard Worker EvalNameError: Unknown variable name occurs. 161*90c8c64dSAndroid Build Coastguard Worker EvalCircularError: Circular variable substitution occurs. 162*90c8c64dSAndroid Build Coastguard Worker """ 163*90c8c64dSAndroid Build Coastguard Worker expanded_vars = [] 164*90c8c64dSAndroid Build Coastguard Worker result_buf = StringIO() 165*90c8c64dSAndroid Build Coastguard Worker _eval_string(s, env, expanded_vars, result_buf) 166*90c8c64dSAndroid Build Coastguard Worker return result_buf.getvalue() 167*90c8c64dSAndroid Build Coastguard Worker 168*90c8c64dSAndroid Build Coastguard Worker 169*90c8c64dSAndroid Build Coastguard Workerdef eval_path_strings(strs, env): 170*90c8c64dSAndroid Build Coastguard Worker """Evalute a list of ``EvalString`` in an environment and normalize paths. 171*90c8c64dSAndroid Build Coastguard Worker 172*90c8c64dSAndroid Build Coastguard Worker Args: 173*90c8c64dSAndroid Build Coastguard Worker strs: A list of ``EvalString`` which should be treated as paths. 174*90c8c64dSAndroid Build Coastguard Worker env: A ``dict`` that maps a name to an ``EvalString`` object. 175*90c8c64dSAndroid Build Coastguard Worker 176*90c8c64dSAndroid Build Coastguard Worker Returns: 177*90c8c64dSAndroid Build Coastguard Worker The list of evaluated strings. 178*90c8c64dSAndroid Build Coastguard Worker """ 179*90c8c64dSAndroid Build Coastguard Worker return [intern(os.path.normpath(eval_string(s, env))) for s in strs] 180*90c8c64dSAndroid Build Coastguard Worker 181*90c8c64dSAndroid Build Coastguard Worker 182*90c8c64dSAndroid Build Coastguard Workerclass EvalStringBuilder(object): 183*90c8c64dSAndroid Build Coastguard Worker def __init__(self): 184*90c8c64dSAndroid Build Coastguard Worker self._segs = [''] 185*90c8c64dSAndroid Build Coastguard Worker 186*90c8c64dSAndroid Build Coastguard Worker 187*90c8c64dSAndroid Build Coastguard Worker def append_raw(self, text): 188*90c8c64dSAndroid Build Coastguard Worker descs = self._segs[0] 189*90c8c64dSAndroid Build Coastguard Worker if descs and descs[-1] == 't': 190*90c8c64dSAndroid Build Coastguard Worker self._segs[-1] += text 191*90c8c64dSAndroid Build Coastguard Worker else: 192*90c8c64dSAndroid Build Coastguard Worker self._segs[0] += 't' 193*90c8c64dSAndroid Build Coastguard Worker self._segs.append(text) 194*90c8c64dSAndroid Build Coastguard Worker return self 195*90c8c64dSAndroid Build Coastguard Worker 196*90c8c64dSAndroid Build Coastguard Worker 197*90c8c64dSAndroid Build Coastguard Worker def append_var(self, varname): 198*90c8c64dSAndroid Build Coastguard Worker self._segs[0] += 'v' 199*90c8c64dSAndroid Build Coastguard Worker self._segs.append(varname) 200*90c8c64dSAndroid Build Coastguard Worker return self 201*90c8c64dSAndroid Build Coastguard Worker 202*90c8c64dSAndroid Build Coastguard Worker 203*90c8c64dSAndroid Build Coastguard Worker def getvalue(self): 204*90c8c64dSAndroid Build Coastguard Worker return EvalString(intern(seg) for seg in self._segs) 205*90c8c64dSAndroid Build Coastguard Worker 206*90c8c64dSAndroid Build Coastguard Worker 207*90c8c64dSAndroid Build Coastguard Workerclass Build(object): 208*90c8c64dSAndroid Build Coastguard Worker __slots__ = ('explicit_outs', 'implicit_outs', 'rule', 'explicit_ins', 209*90c8c64dSAndroid Build Coastguard Worker 'implicit_ins', 'prerequisites', 'bindings', 210*90c8c64dSAndroid Build Coastguard Worker 'depfile_implicit_ins') 211*90c8c64dSAndroid Build Coastguard Worker 212*90c8c64dSAndroid Build Coastguard Worker 213*90c8c64dSAndroid Build Coastguard Workerclass Rule(object): 214*90c8c64dSAndroid Build Coastguard Worker __slots__ = ('name', 'bindings') 215*90c8c64dSAndroid Build Coastguard Worker 216*90c8c64dSAndroid Build Coastguard Worker 217*90c8c64dSAndroid Build Coastguard Workerclass Pool(object): 218*90c8c64dSAndroid Build Coastguard Worker __slots__ = ('name', 'bindings') 219*90c8c64dSAndroid Build Coastguard Worker 220*90c8c64dSAndroid Build Coastguard Worker 221*90c8c64dSAndroid Build Coastguard Workerclass Default(object): 222*90c8c64dSAndroid Build Coastguard Worker __slots__ = ('outs') 223*90c8c64dSAndroid Build Coastguard Worker 224*90c8c64dSAndroid Build Coastguard Worker 225*90c8c64dSAndroid Build Coastguard WorkerToken = collections.namedtuple('Token', 'kind line column value') 226*90c8c64dSAndroid Build Coastguard Worker 227*90c8c64dSAndroid Build Coastguard Worker 228*90c8c64dSAndroid Build Coastguard Workerclass TK(object): 229*90c8c64dSAndroid Build Coastguard Worker """Token ID enumerations.""" 230*90c8c64dSAndroid Build Coastguard Worker 231*90c8c64dSAndroid Build Coastguard Worker # Trivial tokens 232*90c8c64dSAndroid Build Coastguard Worker EOF = 0 233*90c8c64dSAndroid Build Coastguard Worker COMMENT = 1 234*90c8c64dSAndroid Build Coastguard Worker NEWLINE = 2 235*90c8c64dSAndroid Build Coastguard Worker SPACE = 3 236*90c8c64dSAndroid Build Coastguard Worker ESC_NEWLINE = 4 237*90c8c64dSAndroid Build Coastguard Worker IDENT = 5 238*90c8c64dSAndroid Build Coastguard Worker PIPE2 = 6 239*90c8c64dSAndroid Build Coastguard Worker PIPE = 7 240*90c8c64dSAndroid Build Coastguard Worker COLON = 8 241*90c8c64dSAndroid Build Coastguard Worker ASSIGN = 9 242*90c8c64dSAndroid Build Coastguard Worker 243*90c8c64dSAndroid Build Coastguard Worker # Non-trivial tokens 244*90c8c64dSAndroid Build Coastguard Worker PATH = 10 245*90c8c64dSAndroid Build Coastguard Worker STRING = 11 246*90c8c64dSAndroid Build Coastguard Worker 247*90c8c64dSAndroid Build Coastguard Worker 248*90c8c64dSAndroid Build Coastguard Workerclass TokenMatcher(object): 249*90c8c64dSAndroid Build Coastguard Worker def __init__(self, patterns): 250*90c8c64dSAndroid Build Coastguard Worker self._matcher = re.compile('|'.join('(' + p + ')' for k, p in patterns)) 251*90c8c64dSAndroid Build Coastguard Worker self._kinds = [k for k, p in patterns] 252*90c8c64dSAndroid Build Coastguard Worker 253*90c8c64dSAndroid Build Coastguard Worker 254*90c8c64dSAndroid Build Coastguard Worker def match(self, buf, pos): 255*90c8c64dSAndroid Build Coastguard Worker match = self._matcher.match(buf, pos) 256*90c8c64dSAndroid Build Coastguard Worker if not match: 257*90c8c64dSAndroid Build Coastguard Worker return None 258*90c8c64dSAndroid Build Coastguard Worker return (self._kinds[match.lastindex - 1], match.start(), match.end()) 259*90c8c64dSAndroid Build Coastguard Worker 260*90c8c64dSAndroid Build Coastguard Worker 261*90c8c64dSAndroid Build Coastguard Workerclass ParseError(ValueError): 262*90c8c64dSAndroid Build Coastguard Worker def __init__(self, path, line, column, reason=None): 263*90c8c64dSAndroid Build Coastguard Worker self.path = path 264*90c8c64dSAndroid Build Coastguard Worker self.line = line 265*90c8c64dSAndroid Build Coastguard Worker self.column = column 266*90c8c64dSAndroid Build Coastguard Worker self.reason = reason 267*90c8c64dSAndroid Build Coastguard Worker 268*90c8c64dSAndroid Build Coastguard Worker 269*90c8c64dSAndroid Build Coastguard Worker def __repr__(self): 270*90c8c64dSAndroid Build Coastguard Worker s = 'ParseError: {}:{}:{}'.format(self.path, self.line, self.column) 271*90c8c64dSAndroid Build Coastguard Worker if self.reason: 272*90c8c64dSAndroid Build Coastguard Worker s += ': ' + self.reason 273*90c8c64dSAndroid Build Coastguard Worker return s 274*90c8c64dSAndroid Build Coastguard Worker 275*90c8c64dSAndroid Build Coastguard Worker 276*90c8c64dSAndroid Build Coastguard Workerclass Lexer(object): 277*90c8c64dSAndroid Build Coastguard Worker def __init__(self, lines_iterable, path='<stdin>', encoding='utf-8'): 278*90c8c64dSAndroid Build Coastguard Worker self.encoding = encoding 279*90c8c64dSAndroid Build Coastguard Worker self.path = path 280*90c8c64dSAndroid Build Coastguard Worker 281*90c8c64dSAndroid Build Coastguard Worker self._line_iter = iter(lines_iterable) 282*90c8c64dSAndroid Build Coastguard Worker self._line_buf = None 283*90c8c64dSAndroid Build Coastguard Worker self._line = 0 284*90c8c64dSAndroid Build Coastguard Worker self._line_pos = 0 285*90c8c64dSAndroid Build Coastguard Worker self._line_end = 0 286*90c8c64dSAndroid Build Coastguard Worker 287*90c8c64dSAndroid Build Coastguard Worker self._line_start = True 288*90c8c64dSAndroid Build Coastguard Worker 289*90c8c64dSAndroid Build Coastguard Worker self._next_token = None 290*90c8c64dSAndroid Build Coastguard Worker self._next_pos = None 291*90c8c64dSAndroid Build Coastguard Worker 292*90c8c64dSAndroid Build Coastguard Worker 293*90c8c64dSAndroid Build Coastguard Worker def raise_error(self, reason=None): 294*90c8c64dSAndroid Build Coastguard Worker raise ParseError(self.path, self._line, self._line_pos + 1, reason) 295*90c8c64dSAndroid Build Coastguard Worker 296*90c8c64dSAndroid Build Coastguard Worker 297*90c8c64dSAndroid Build Coastguard Worker def _read_next_line(self): 298*90c8c64dSAndroid Build Coastguard Worker try: 299*90c8c64dSAndroid Build Coastguard Worker self._line_buf = next(self._line_iter) 300*90c8c64dSAndroid Build Coastguard Worker self._line_pos = 0 301*90c8c64dSAndroid Build Coastguard Worker self._line_end = len(self._line_buf) 302*90c8c64dSAndroid Build Coastguard Worker self._line += 1 303*90c8c64dSAndroid Build Coastguard Worker return True 304*90c8c64dSAndroid Build Coastguard Worker except StopIteration: 305*90c8c64dSAndroid Build Coastguard Worker self._line_buf = None 306*90c8c64dSAndroid Build Coastguard Worker return False 307*90c8c64dSAndroid Build Coastguard Worker 308*90c8c64dSAndroid Build Coastguard Worker 309*90c8c64dSAndroid Build Coastguard Worker def _ensure_line(self): 310*90c8c64dSAndroid Build Coastguard Worker if self._line_buf and self._line_pos < self._line_end: 311*90c8c64dSAndroid Build Coastguard Worker return True 312*90c8c64dSAndroid Build Coastguard Worker return self._read_next_line() 313*90c8c64dSAndroid Build Coastguard Worker 314*90c8c64dSAndroid Build Coastguard Worker _COMMENT_MATCHER = re.compile(r'[ \t]*(?:#[^\n]*)?(?=\n)') 315*90c8c64dSAndroid Build Coastguard Worker 316*90c8c64dSAndroid Build Coastguard Worker 317*90c8c64dSAndroid Build Coastguard Worker def _ensure_non_comment_line(self): 318*90c8c64dSAndroid Build Coastguard Worker if not self._ensure_line(): 319*90c8c64dSAndroid Build Coastguard Worker return False 320*90c8c64dSAndroid Build Coastguard Worker # Match comments or spaces 321*90c8c64dSAndroid Build Coastguard Worker match = self._COMMENT_MATCHER.match(self._line_buf) 322*90c8c64dSAndroid Build Coastguard Worker if not match: 323*90c8c64dSAndroid Build Coastguard Worker return True 324*90c8c64dSAndroid Build Coastguard Worker # Move the cursor to the newline character 325*90c8c64dSAndroid Build Coastguard Worker self._line_pos = match.end() 326*90c8c64dSAndroid Build Coastguard Worker return True 327*90c8c64dSAndroid Build Coastguard Worker 328*90c8c64dSAndroid Build Coastguard Worker _SPACE_MATCHER = re.compile(r'[ \t]+') 329*90c8c64dSAndroid Build Coastguard Worker 330*90c8c64dSAndroid Build Coastguard Worker 331*90c8c64dSAndroid Build Coastguard Worker def _skip_space(self): 332*90c8c64dSAndroid Build Coastguard Worker match = self._SPACE_MATCHER.match(self._line_buf, self._line_pos) 333*90c8c64dSAndroid Build Coastguard Worker if match: 334*90c8c64dSAndroid Build Coastguard Worker self._line_pos = match.end() 335*90c8c64dSAndroid Build Coastguard Worker 336*90c8c64dSAndroid Build Coastguard Worker _SIMPLE_TOKEN_MATCHER = TokenMatcher([ 337*90c8c64dSAndroid Build Coastguard Worker (TK.COMMENT, r'#[^\n]*'), 338*90c8c64dSAndroid Build Coastguard Worker (TK.NEWLINE, r'[\r\n]'), 339*90c8c64dSAndroid Build Coastguard Worker (TK.SPACE, r'[ \t]+'), 340*90c8c64dSAndroid Build Coastguard Worker (TK.ESC_NEWLINE, r'\$[\r\n]'), 341*90c8c64dSAndroid Build Coastguard Worker (TK.IDENT, r'[\w_.-]+'), 342*90c8c64dSAndroid Build Coastguard Worker (TK.PIPE2, r'\|\|'), 343*90c8c64dSAndroid Build Coastguard Worker (TK.PIPE, r'\|'), 344*90c8c64dSAndroid Build Coastguard Worker (TK.COLON, r':'), 345*90c8c64dSAndroid Build Coastguard Worker (TK.ASSIGN, r'='), 346*90c8c64dSAndroid Build Coastguard Worker ]) 347*90c8c64dSAndroid Build Coastguard Worker 348*90c8c64dSAndroid Build Coastguard Worker 349*90c8c64dSAndroid Build Coastguard Worker def peek(self): 350*90c8c64dSAndroid Build Coastguard Worker if self._next_token is not None: 351*90c8c64dSAndroid Build Coastguard Worker return self._next_token 352*90c8c64dSAndroid Build Coastguard Worker while True: 353*90c8c64dSAndroid Build Coastguard Worker if not self._ensure_non_comment_line(): 354*90c8c64dSAndroid Build Coastguard Worker return Token(TK.EOF, self._line, self._line_pos + 1, '') 355*90c8c64dSAndroid Build Coastguard Worker 356*90c8c64dSAndroid Build Coastguard Worker match = self._SIMPLE_TOKEN_MATCHER.match( 357*90c8c64dSAndroid Build Coastguard Worker self._line_buf, self._line_pos) 358*90c8c64dSAndroid Build Coastguard Worker if not match: 359*90c8c64dSAndroid Build Coastguard Worker return None 360*90c8c64dSAndroid Build Coastguard Worker kind, start, end = match 361*90c8c64dSAndroid Build Coastguard Worker 362*90c8c64dSAndroid Build Coastguard Worker # Skip comments and spaces 363*90c8c64dSAndroid Build Coastguard Worker if ((kind == TK.SPACE and not self._line_start) or 364*90c8c64dSAndroid Build Coastguard Worker (kind == TK.ESC_NEWLINE) or 365*90c8c64dSAndroid Build Coastguard Worker (kind == TK.COMMENT)): 366*90c8c64dSAndroid Build Coastguard Worker self._line_pos = end 367*90c8c64dSAndroid Build Coastguard Worker continue 368*90c8c64dSAndroid Build Coastguard Worker 369*90c8c64dSAndroid Build Coastguard Worker # Save the peaked token 370*90c8c64dSAndroid Build Coastguard Worker token = Token(kind, self._line, self._line_pos + 1, 371*90c8c64dSAndroid Build Coastguard Worker self._line_buf[start:end]) 372*90c8c64dSAndroid Build Coastguard Worker self._next_token = token 373*90c8c64dSAndroid Build Coastguard Worker self._next_pos = end 374*90c8c64dSAndroid Build Coastguard Worker return token 375*90c8c64dSAndroid Build Coastguard Worker 376*90c8c64dSAndroid Build Coastguard Worker 377*90c8c64dSAndroid Build Coastguard Worker def lex(self): 378*90c8c64dSAndroid Build Coastguard Worker token = self.peek() 379*90c8c64dSAndroid Build Coastguard Worker if not token: 380*90c8c64dSAndroid Build Coastguard Worker self.raise_error() 381*90c8c64dSAndroid Build Coastguard Worker self._line_start = token.kind == TK.NEWLINE 382*90c8c64dSAndroid Build Coastguard Worker self._line_pos = self._next_pos 383*90c8c64dSAndroid Build Coastguard Worker self._next_token = None 384*90c8c64dSAndroid Build Coastguard Worker self._next_pos = None 385*90c8c64dSAndroid Build Coastguard Worker return token 386*90c8c64dSAndroid Build Coastguard Worker 387*90c8c64dSAndroid Build Coastguard Worker 388*90c8c64dSAndroid Build Coastguard Worker def lex_match(self, match_set): 389*90c8c64dSAndroid Build Coastguard Worker token = self.lex() 390*90c8c64dSAndroid Build Coastguard Worker if token.kind not in match_set: 391*90c8c64dSAndroid Build Coastguard Worker self.raise_error() 392*90c8c64dSAndroid Build Coastguard Worker return token 393*90c8c64dSAndroid Build Coastguard Worker 394*90c8c64dSAndroid Build Coastguard Worker 395*90c8c64dSAndroid Build Coastguard Worker class STR_TK(object): 396*90c8c64dSAndroid Build Coastguard Worker END = 0 397*90c8c64dSAndroid Build Coastguard Worker CHARS = 1 398*90c8c64dSAndroid Build Coastguard Worker ESC_CHAR = 2 399*90c8c64dSAndroid Build Coastguard Worker ESC_NEWLINE = 3 400*90c8c64dSAndroid Build Coastguard Worker VAR = 4 401*90c8c64dSAndroid Build Coastguard Worker CURVE_VAR = 5 402*90c8c64dSAndroid Build Coastguard Worker 403*90c8c64dSAndroid Build Coastguard Worker 404*90c8c64dSAndroid Build Coastguard Worker _PATH_TOKEN_MATCHER = TokenMatcher([ 405*90c8c64dSAndroid Build Coastguard Worker (STR_TK.END, r'[ \t\n|:]'), 406*90c8c64dSAndroid Build Coastguard Worker (STR_TK.CHARS, r'[^ \t\n|:$]+'), 407*90c8c64dSAndroid Build Coastguard Worker (STR_TK.ESC_CHAR, r'\$[^\n{\w_-]'), 408*90c8c64dSAndroid Build Coastguard Worker (STR_TK.ESC_NEWLINE, r'\$\n[ \t]*'), 409*90c8c64dSAndroid Build Coastguard Worker (STR_TK.VAR, r'\$[\w_-]+'), 410*90c8c64dSAndroid Build Coastguard Worker (STR_TK.CURVE_VAR, r'\$\{[\w_.-]+\}'), 411*90c8c64dSAndroid Build Coastguard Worker ]) 412*90c8c64dSAndroid Build Coastguard Worker 413*90c8c64dSAndroid Build Coastguard Worker 414*90c8c64dSAndroid Build Coastguard Worker _STR_TOKEN_MATCHER = TokenMatcher([ 415*90c8c64dSAndroid Build Coastguard Worker (STR_TK.END, r'\n+'), 416*90c8c64dSAndroid Build Coastguard Worker (STR_TK.CHARS, r'[^\n$]+'), 417*90c8c64dSAndroid Build Coastguard Worker (STR_TK.ESC_CHAR, r'\$[^\n{\w_-]'), 418*90c8c64dSAndroid Build Coastguard Worker (STR_TK.ESC_NEWLINE, r'\$\n[ \t]*'), 419*90c8c64dSAndroid Build Coastguard Worker (STR_TK.VAR, r'\$[\w_-]+'), 420*90c8c64dSAndroid Build Coastguard Worker (STR_TK.CURVE_VAR, r'\$\{[\w_.-]+\}'), 421*90c8c64dSAndroid Build Coastguard Worker ]) 422*90c8c64dSAndroid Build Coastguard Worker 423*90c8c64dSAndroid Build Coastguard Worker 424*90c8c64dSAndroid Build Coastguard Worker def _lex_string_or_path(self, matcher, result_kind): 425*90c8c64dSAndroid Build Coastguard Worker self._ensure_line() 426*90c8c64dSAndroid Build Coastguard Worker self._skip_space() 427*90c8c64dSAndroid Build Coastguard Worker 428*90c8c64dSAndroid Build Coastguard Worker start_line = self._line 429*90c8c64dSAndroid Build Coastguard Worker start_column = self._line_pos + 1 430*90c8c64dSAndroid Build Coastguard Worker 431*90c8c64dSAndroid Build Coastguard Worker builder = EvalStringBuilder() 432*90c8c64dSAndroid Build Coastguard Worker 433*90c8c64dSAndroid Build Coastguard Worker while True: 434*90c8c64dSAndroid Build Coastguard Worker if not self._ensure_line(): 435*90c8c64dSAndroid Build Coastguard Worker break 436*90c8c64dSAndroid Build Coastguard Worker 437*90c8c64dSAndroid Build Coastguard Worker match = matcher.match(self._line_buf, self._line_pos) 438*90c8c64dSAndroid Build Coastguard Worker if not match: 439*90c8c64dSAndroid Build Coastguard Worker self.raise_error('unknown character sequence') 440*90c8c64dSAndroid Build Coastguard Worker 441*90c8c64dSAndroid Build Coastguard Worker kind, start, end = match 442*90c8c64dSAndroid Build Coastguard Worker if kind == self.STR_TK.END: 443*90c8c64dSAndroid Build Coastguard Worker break 444*90c8c64dSAndroid Build Coastguard Worker 445*90c8c64dSAndroid Build Coastguard Worker self._line_pos = end 446*90c8c64dSAndroid Build Coastguard Worker 447*90c8c64dSAndroid Build Coastguard Worker if kind == self.STR_TK.CHARS: 448*90c8c64dSAndroid Build Coastguard Worker builder.append_raw(self._line_buf[start:end]) 449*90c8c64dSAndroid Build Coastguard Worker elif kind == self.STR_TK.ESC_CHAR: 450*90c8c64dSAndroid Build Coastguard Worker ch = self._line_buf[start + 1] 451*90c8c64dSAndroid Build Coastguard Worker if ch in ' \t:$': 452*90c8c64dSAndroid Build Coastguard Worker builder.append_raw(ch) 453*90c8c64dSAndroid Build Coastguard Worker else: 454*90c8c64dSAndroid Build Coastguard Worker self.raise_error('bad escape sequence') 455*90c8c64dSAndroid Build Coastguard Worker elif kind == self.STR_TK.ESC_NEWLINE: 456*90c8c64dSAndroid Build Coastguard Worker if not self._read_next_line(): 457*90c8c64dSAndroid Build Coastguard Worker break 458*90c8c64dSAndroid Build Coastguard Worker self._skip_space() 459*90c8c64dSAndroid Build Coastguard Worker elif kind == self.STR_TK.VAR: 460*90c8c64dSAndroid Build Coastguard Worker builder.append_var(self._line_buf[start + 1 : end]) 461*90c8c64dSAndroid Build Coastguard Worker else: 462*90c8c64dSAndroid Build Coastguard Worker assert kind == self.STR_TK.CURVE_VAR 463*90c8c64dSAndroid Build Coastguard Worker builder.append_var(self._line_buf[start + 2 : end - 1]) 464*90c8c64dSAndroid Build Coastguard Worker 465*90c8c64dSAndroid Build Coastguard Worker self._next_token = None 466*90c8c64dSAndroid Build Coastguard Worker return Token(result_kind, start_line, start_column, builder.getvalue()) 467*90c8c64dSAndroid Build Coastguard Worker 468*90c8c64dSAndroid Build Coastguard Worker 469*90c8c64dSAndroid Build Coastguard Worker def lex_path(self): 470*90c8c64dSAndroid Build Coastguard Worker return self._lex_string_or_path(self._PATH_TOKEN_MATCHER, TK.PATH) 471*90c8c64dSAndroid Build Coastguard Worker 472*90c8c64dSAndroid Build Coastguard Worker 473*90c8c64dSAndroid Build Coastguard Worker def lex_string(self): 474*90c8c64dSAndroid Build Coastguard Worker return self._lex_string_or_path(self._STR_TOKEN_MATCHER, TK.STRING) 475*90c8c64dSAndroid Build Coastguard Worker 476*90c8c64dSAndroid Build Coastguard Worker 477*90c8c64dSAndroid Build Coastguard WorkerManifest = collections.namedtuple('Manifest', 'builds rules pools defaults') 478*90c8c64dSAndroid Build Coastguard Worker 479*90c8c64dSAndroid Build Coastguard Worker 480*90c8c64dSAndroid Build Coastguard Workerclass Parser(object): 481*90c8c64dSAndroid Build Coastguard Worker """Ninja Manifest Parser 482*90c8c64dSAndroid Build Coastguard Worker 483*90c8c64dSAndroid Build Coastguard Worker This parser parses ninja-build manifest files, such as:: 484*90c8c64dSAndroid Build Coastguard Worker 485*90c8c64dSAndroid Build Coastguard Worker cflags = -Wall 486*90c8c64dSAndroid Build Coastguard Worker 487*90c8c64dSAndroid Build Coastguard Worker pool cc_pool 488*90c8c64dSAndroid Build Coastguard Worker depth = 1 489*90c8c64dSAndroid Build Coastguard Worker 490*90c8c64dSAndroid Build Coastguard Worker rule cc 491*90c8c64dSAndroid Build Coastguard Worker command = gcc -c -o $out $in $cflags $extra_cflags 492*90c8c64dSAndroid Build Coastguard Worker pool = cc_pool 493*90c8c64dSAndroid Build Coastguard Worker 494*90c8c64dSAndroid Build Coastguard Worker build test.o : cc test.c 495*90c8c64dSAndroid Build Coastguard Worker extra_cflags = -Werror 496*90c8c64dSAndroid Build Coastguard Worker 497*90c8c64dSAndroid Build Coastguard Worker default test.o 498*90c8c64dSAndroid Build Coastguard Worker 499*90c8c64dSAndroid Build Coastguard Worker Example: 500*90c8c64dSAndroid Build Coastguard Worker >>> manifest = Parser().parse('build.ninja', 'utf-8') 501*90c8c64dSAndroid Build Coastguard Worker >>> print(manifest.builds) 502*90c8c64dSAndroid Build Coastguard Worker 503*90c8c64dSAndroid Build Coastguard Worker """ 504*90c8c64dSAndroid Build Coastguard Worker 505*90c8c64dSAndroid Build Coastguard Worker 506*90c8c64dSAndroid Build Coastguard Worker def __init__(self, base_dir=None): 507*90c8c64dSAndroid Build Coastguard Worker if base_dir is None: 508*90c8c64dSAndroid Build Coastguard Worker self._base_dir = os.getcwd() 509*90c8c64dSAndroid Build Coastguard Worker else: 510*90c8c64dSAndroid Build Coastguard Worker self._base_dir = base_dir 511*90c8c64dSAndroid Build Coastguard Worker 512*90c8c64dSAndroid Build Coastguard Worker # File context 513*90c8c64dSAndroid Build Coastguard Worker self._context = [] 514*90c8c64dSAndroid Build Coastguard Worker self._lexer = None 515*90c8c64dSAndroid Build Coastguard Worker self._env = None 516*90c8c64dSAndroid Build Coastguard Worker 517*90c8c64dSAndroid Build Coastguard Worker # Intermediate results 518*90c8c64dSAndroid Build Coastguard Worker self._builds = [] 519*90c8c64dSAndroid Build Coastguard Worker self._rules = [] 520*90c8c64dSAndroid Build Coastguard Worker self._pools = [] 521*90c8c64dSAndroid Build Coastguard Worker self._defaults = [] 522*90c8c64dSAndroid Build Coastguard Worker 523*90c8c64dSAndroid Build Coastguard Worker self._rules_dict = {} 524*90c8c64dSAndroid Build Coastguard Worker 525*90c8c64dSAndroid Build Coastguard Worker 526*90c8c64dSAndroid Build Coastguard Worker def _push_context(self, lexer, env): 527*90c8c64dSAndroid Build Coastguard Worker """Push a parsing file context. 528*90c8c64dSAndroid Build Coastguard Worker 529*90c8c64dSAndroid Build Coastguard Worker Args: 530*90c8c64dSAndroid Build Coastguard Worker lexer: Lexer for the associated file. 531*90c8c64dSAndroid Build Coastguard Worker env: Environment for global variable bindings. 532*90c8c64dSAndroid Build Coastguard Worker """ 533*90c8c64dSAndroid Build Coastguard Worker 534*90c8c64dSAndroid Build Coastguard Worker self._context.append((self._lexer, self._env)) 535*90c8c64dSAndroid Build Coastguard Worker self._lexer = lexer 536*90c8c64dSAndroid Build Coastguard Worker self._env = env 537*90c8c64dSAndroid Build Coastguard Worker 538*90c8c64dSAndroid Build Coastguard Worker 539*90c8c64dSAndroid Build Coastguard Worker def _pop_context(self): 540*90c8c64dSAndroid Build Coastguard Worker """Push a parsing file context.""" 541*90c8c64dSAndroid Build Coastguard Worker 542*90c8c64dSAndroid Build Coastguard Worker current_context = (self._lexer, self._env) 543*90c8c64dSAndroid Build Coastguard Worker self._lexer, self._env = self._context.pop() 544*90c8c64dSAndroid Build Coastguard Worker return current_context 545*90c8c64dSAndroid Build Coastguard Worker 546*90c8c64dSAndroid Build Coastguard Worker 547*90c8c64dSAndroid Build Coastguard Worker def parse(self, path, encoding, depfile=None): 548*90c8c64dSAndroid Build Coastguard Worker """Parse a ninja-build manifest file. 549*90c8c64dSAndroid Build Coastguard Worker 550*90c8c64dSAndroid Build Coastguard Worker Args: 551*90c8c64dSAndroid Build Coastguard Worker path (str): Input file path to be parsed. 552*90c8c64dSAndroid Build Coastguard Worker encoding (str): Input file encoding. 553*90c8c64dSAndroid Build Coastguard Worker 554*90c8c64dSAndroid Build Coastguard Worker Returns: 555*90c8c64dSAndroid Build Coastguard Worker Manifest: Parsed manifest for the given ninja-build manifest file. 556*90c8c64dSAndroid Build Coastguard Worker """ 557*90c8c64dSAndroid Build Coastguard Worker 558*90c8c64dSAndroid Build Coastguard Worker self._parse_internal(path, encoding, EvalEnv()) 559*90c8c64dSAndroid Build Coastguard Worker if depfile: 560*90c8c64dSAndroid Build Coastguard Worker self.parse_dep_file(depfile, encoding) 561*90c8c64dSAndroid Build Coastguard Worker return Manifest(self._builds, self._rules, self._pools, self._defaults) 562*90c8c64dSAndroid Build Coastguard Worker 563*90c8c64dSAndroid Build Coastguard Worker 564*90c8c64dSAndroid Build Coastguard Worker def _parse_internal(self, path, encoding, env): 565*90c8c64dSAndroid Build Coastguard Worker path = os.path.join(self._base_dir, path) 566*90c8c64dSAndroid Build Coastguard Worker with open(path, 'r', encoding=encoding) as fp: 567*90c8c64dSAndroid Build Coastguard Worker self._push_context(Lexer(fp, path, encoding), env) 568*90c8c64dSAndroid Build Coastguard Worker try: 569*90c8c64dSAndroid Build Coastguard Worker self._parse_all_top_level_stmts() 570*90c8c64dSAndroid Build Coastguard Worker finally: 571*90c8c64dSAndroid Build Coastguard Worker self._pop_context() 572*90c8c64dSAndroid Build Coastguard Worker 573*90c8c64dSAndroid Build Coastguard Worker 574*90c8c64dSAndroid Build Coastguard Worker def _parse_all_top_level_stmts(self): 575*90c8c64dSAndroid Build Coastguard Worker """Parse all top-level statements in a file.""" 576*90c8c64dSAndroid Build Coastguard Worker while self._parse_top_level_stmt(): 577*90c8c64dSAndroid Build Coastguard Worker pass 578*90c8c64dSAndroid Build Coastguard Worker 579*90c8c64dSAndroid Build Coastguard Worker 580*90c8c64dSAndroid Build Coastguard Worker def _parse_top_level_stmt(self): 581*90c8c64dSAndroid Build Coastguard Worker """Parse a top level statement.""" 582*90c8c64dSAndroid Build Coastguard Worker 583*90c8c64dSAndroid Build Coastguard Worker token = self._lexer.peek() 584*90c8c64dSAndroid Build Coastguard Worker if not token: 585*90c8c64dSAndroid Build Coastguard Worker # An unexpected non-trivial token occurs. Raise an error. 586*90c8c64dSAndroid Build Coastguard Worker self._lexer.raise_error() 587*90c8c64dSAndroid Build Coastguard Worker 588*90c8c64dSAndroid Build Coastguard Worker if token.kind == TK.EOF: 589*90c8c64dSAndroid Build Coastguard Worker return False 590*90c8c64dSAndroid Build Coastguard Worker elif token.kind == TK.NEWLINE: 591*90c8c64dSAndroid Build Coastguard Worker self._lexer.lex() 592*90c8c64dSAndroid Build Coastguard Worker elif token.kind == TK.IDENT: 593*90c8c64dSAndroid Build Coastguard Worker ident = token.value 594*90c8c64dSAndroid Build Coastguard Worker if ident == 'rule': 595*90c8c64dSAndroid Build Coastguard Worker self._parse_rule_stmt() 596*90c8c64dSAndroid Build Coastguard Worker elif ident == 'build': 597*90c8c64dSAndroid Build Coastguard Worker self._parse_build_stmt() 598*90c8c64dSAndroid Build Coastguard Worker elif ident == 'default': 599*90c8c64dSAndroid Build Coastguard Worker self._parse_default_stmt() 600*90c8c64dSAndroid Build Coastguard Worker elif ident == 'pool': 601*90c8c64dSAndroid Build Coastguard Worker self._parse_pool_stmt() 602*90c8c64dSAndroid Build Coastguard Worker elif ident in {'subninja', 'include'}: 603*90c8c64dSAndroid Build Coastguard Worker self._parse_include_stmt() 604*90c8c64dSAndroid Build Coastguard Worker else: 605*90c8c64dSAndroid Build Coastguard Worker self._parse_global_binding_stmt() 606*90c8c64dSAndroid Build Coastguard Worker else: 607*90c8c64dSAndroid Build Coastguard Worker # An unexpected trivial token occurs. Raise an error. 608*90c8c64dSAndroid Build Coastguard Worker self._lexer.raise_error() 609*90c8c64dSAndroid Build Coastguard Worker return True 610*90c8c64dSAndroid Build Coastguard Worker 611*90c8c64dSAndroid Build Coastguard Worker 612*90c8c64dSAndroid Build Coastguard Worker def _parse_path_list(self, end_set): 613*90c8c64dSAndroid Build Coastguard Worker """Parse a list of paths.""" 614*90c8c64dSAndroid Build Coastguard Worker 615*90c8c64dSAndroid Build Coastguard Worker result = [] 616*90c8c64dSAndroid Build Coastguard Worker while True: 617*90c8c64dSAndroid Build Coastguard Worker token = self._lexer.peek() 618*90c8c64dSAndroid Build Coastguard Worker if token: 619*90c8c64dSAndroid Build Coastguard Worker if token.kind in end_set: 620*90c8c64dSAndroid Build Coastguard Worker break 621*90c8c64dSAndroid Build Coastguard Worker elif token.kind != TK.IDENT: 622*90c8c64dSAndroid Build Coastguard Worker self._lexer.raise_error() 623*90c8c64dSAndroid Build Coastguard Worker 624*90c8c64dSAndroid Build Coastguard Worker token = self._lexer.lex_path() 625*90c8c64dSAndroid Build Coastguard Worker result.append(token.value) 626*90c8c64dSAndroid Build Coastguard Worker return result 627*90c8c64dSAndroid Build Coastguard Worker 628*90c8c64dSAndroid Build Coastguard Worker 629*90c8c64dSAndroid Build Coastguard Worker def _parse_binding_stmt(self): 630*90c8c64dSAndroid Build Coastguard Worker """Parse a variable binding statement. 631*90c8c64dSAndroid Build Coastguard Worker 632*90c8c64dSAndroid Build Coastguard Worker Example: 633*90c8c64dSAndroid Build Coastguard Worker IDENT = STRING 634*90c8c64dSAndroid Build Coastguard Worker """ 635*90c8c64dSAndroid Build Coastguard Worker key = self._lexer.lex_match({TK.IDENT}).value 636*90c8c64dSAndroid Build Coastguard Worker self._lexer.lex_match({TK.ASSIGN}) 637*90c8c64dSAndroid Build Coastguard Worker token = self._lexer.lex_string() 638*90c8c64dSAndroid Build Coastguard Worker value = token.value 639*90c8c64dSAndroid Build Coastguard Worker self._lexer.lex_match({TK.NEWLINE, TK.EOF}) 640*90c8c64dSAndroid Build Coastguard Worker return (key, value) 641*90c8c64dSAndroid Build Coastguard Worker 642*90c8c64dSAndroid Build Coastguard Worker 643*90c8c64dSAndroid Build Coastguard Worker def _parse_global_binding_stmt(self): 644*90c8c64dSAndroid Build Coastguard Worker """Parse a global variable binding statement. 645*90c8c64dSAndroid Build Coastguard Worker 646*90c8c64dSAndroid Build Coastguard Worker Example: 647*90c8c64dSAndroid Build Coastguard Worker IDENT = STRING 648*90c8c64dSAndroid Build Coastguard Worker """ 649*90c8c64dSAndroid Build Coastguard Worker 650*90c8c64dSAndroid Build Coastguard Worker key, value = self._parse_binding_stmt() 651*90c8c64dSAndroid Build Coastguard Worker value = eval_string(value, self._env) 652*90c8c64dSAndroid Build Coastguard Worker self._env[key] = value 653*90c8c64dSAndroid Build Coastguard Worker 654*90c8c64dSAndroid Build Coastguard Worker 655*90c8c64dSAndroid Build Coastguard Worker def _parse_local_binding_block(self): 656*90c8c64dSAndroid Build Coastguard Worker """Parse several local variable bindings. 657*90c8c64dSAndroid Build Coastguard Worker 658*90c8c64dSAndroid Build Coastguard Worker Example: 659*90c8c64dSAndroid Build Coastguard Worker SPACE IDENT1 = STRING1 660*90c8c64dSAndroid Build Coastguard Worker SPACE IDENT2 = STRING2 661*90c8c64dSAndroid Build Coastguard Worker """ 662*90c8c64dSAndroid Build Coastguard Worker result = EvalEnv() 663*90c8c64dSAndroid Build Coastguard Worker while True: 664*90c8c64dSAndroid Build Coastguard Worker token = self._lexer.peek() 665*90c8c64dSAndroid Build Coastguard Worker if not token or token.kind != TK.SPACE: 666*90c8c64dSAndroid Build Coastguard Worker break 667*90c8c64dSAndroid Build Coastguard Worker self._lexer.lex() 668*90c8c64dSAndroid Build Coastguard Worker key, value = self._parse_binding_stmt() 669*90c8c64dSAndroid Build Coastguard Worker result[key] = value 670*90c8c64dSAndroid Build Coastguard Worker return result 671*90c8c64dSAndroid Build Coastguard Worker 672*90c8c64dSAndroid Build Coastguard Worker 673*90c8c64dSAndroid Build Coastguard Worker def _parse_build_stmt(self): 674*90c8c64dSAndroid Build Coastguard Worker """Parse `build` statement. 675*90c8c64dSAndroid Build Coastguard Worker 676*90c8c64dSAndroid Build Coastguard Worker Example: 677*90c8c64dSAndroid Build Coastguard Worker build PATH1 PATH2 | PATH3 PATH4 : IDENT PATH5 PATH6 | $ 678*90c8c64dSAndroid Build Coastguard Worker PATH7 PATH8 || PATH9 PATH10 679*90c8c64dSAndroid Build Coastguard Worker SPACE IDENT1 = STRING1 680*90c8c64dSAndroid Build Coastguard Worker SPACE IDENT2 = STRING2 681*90c8c64dSAndroid Build Coastguard Worker """ 682*90c8c64dSAndroid Build Coastguard Worker 683*90c8c64dSAndroid Build Coastguard Worker token = self._lexer.lex_match({TK.IDENT}) 684*90c8c64dSAndroid Build Coastguard Worker assert token.value == 'build' 685*90c8c64dSAndroid Build Coastguard Worker 686*90c8c64dSAndroid Build Coastguard Worker build = Build() 687*90c8c64dSAndroid Build Coastguard Worker 688*90c8c64dSAndroid Build Coastguard Worker # Parse explicit outs 689*90c8c64dSAndroid Build Coastguard Worker explicit_outs = self._parse_path_list({TK.PIPE, TK.COLON}) 690*90c8c64dSAndroid Build Coastguard Worker 691*90c8c64dSAndroid Build Coastguard Worker # Parse implicit outs 692*90c8c64dSAndroid Build Coastguard Worker token = self._lexer.peek() 693*90c8c64dSAndroid Build Coastguard Worker if token.kind == TK.PIPE: 694*90c8c64dSAndroid Build Coastguard Worker self._lexer.lex() 695*90c8c64dSAndroid Build Coastguard Worker implicit_outs = self._parse_path_list({TK.COLON}) 696*90c8c64dSAndroid Build Coastguard Worker else: 697*90c8c64dSAndroid Build Coastguard Worker implicit_outs = tuple() 698*90c8c64dSAndroid Build Coastguard Worker 699*90c8c64dSAndroid Build Coastguard Worker self._lexer.lex_match({TK.COLON}) 700*90c8c64dSAndroid Build Coastguard Worker 701*90c8c64dSAndroid Build Coastguard Worker # Parse rule name for this build statement 702*90c8c64dSAndroid Build Coastguard Worker build.rule = self._lexer.lex_match({TK.IDENT}).value 703*90c8c64dSAndroid Build Coastguard Worker try: 704*90c8c64dSAndroid Build Coastguard Worker rule_env = self._rules_dict[build.rule].bindings 705*90c8c64dSAndroid Build Coastguard Worker except KeyError: 706*90c8c64dSAndroid Build Coastguard Worker if build.rule != 'phony': 707*90c8c64dSAndroid Build Coastguard Worker self._lexer.raise_error('undeclared rule name') 708*90c8c64dSAndroid Build Coastguard Worker rule_env = self._env 709*90c8c64dSAndroid Build Coastguard Worker 710*90c8c64dSAndroid Build Coastguard Worker # Parse explicit ins 711*90c8c64dSAndroid Build Coastguard Worker explicit_ins = self._parse_path_list( 712*90c8c64dSAndroid Build Coastguard Worker {TK.PIPE, TK.PIPE2, TK.NEWLINE, TK.EOF}) 713*90c8c64dSAndroid Build Coastguard Worker 714*90c8c64dSAndroid Build Coastguard Worker # Parse implicit ins 715*90c8c64dSAndroid Build Coastguard Worker token = self._lexer.peek() 716*90c8c64dSAndroid Build Coastguard Worker if token.kind == TK.PIPE: 717*90c8c64dSAndroid Build Coastguard Worker self._lexer.lex() 718*90c8c64dSAndroid Build Coastguard Worker implicit_ins = self._parse_path_list({TK.PIPE2, TK.NEWLINE, TK.EOF}) 719*90c8c64dSAndroid Build Coastguard Worker else: 720*90c8c64dSAndroid Build Coastguard Worker implicit_ins = tuple() 721*90c8c64dSAndroid Build Coastguard Worker 722*90c8c64dSAndroid Build Coastguard Worker # Parse order-only prerequisites 723*90c8c64dSAndroid Build Coastguard Worker token = self._lexer.peek() 724*90c8c64dSAndroid Build Coastguard Worker if token.kind == TK.PIPE2: 725*90c8c64dSAndroid Build Coastguard Worker self._lexer.lex() 726*90c8c64dSAndroid Build Coastguard Worker prerequisites = self._parse_path_list({TK.NEWLINE, TK.EOF}) 727*90c8c64dSAndroid Build Coastguard Worker else: 728*90c8c64dSAndroid Build Coastguard Worker prerequisites = tuple() 729*90c8c64dSAndroid Build Coastguard Worker 730*90c8c64dSAndroid Build Coastguard Worker self._lexer.lex_match({TK.NEWLINE, TK.EOF}) 731*90c8c64dSAndroid Build Coastguard Worker 732*90c8c64dSAndroid Build Coastguard Worker # Parse local bindings 733*90c8c64dSAndroid Build Coastguard Worker bindings = self._parse_local_binding_block() 734*90c8c64dSAndroid Build Coastguard Worker bindings.parent = self._env 735*90c8c64dSAndroid Build Coastguard Worker if bindings: 736*90c8c64dSAndroid Build Coastguard Worker build.bindings = bindings 737*90c8c64dSAndroid Build Coastguard Worker else: 738*90c8c64dSAndroid Build Coastguard Worker # Don't keep the empty ``dict`` object if there are no bindings 739*90c8c64dSAndroid Build Coastguard Worker build.bindings = None 740*90c8c64dSAndroid Build Coastguard Worker 741*90c8c64dSAndroid Build Coastguard Worker # Evaluate all paths 742*90c8c64dSAndroid Build Coastguard Worker env = BuildEvalEnv(bindings, rule_env) 743*90c8c64dSAndroid Build Coastguard Worker 744*90c8c64dSAndroid Build Coastguard Worker build.explicit_outs = eval_path_strings(explicit_outs, env) 745*90c8c64dSAndroid Build Coastguard Worker build.implicit_outs = eval_path_strings(implicit_outs, env) 746*90c8c64dSAndroid Build Coastguard Worker build.explicit_ins = eval_path_strings(explicit_ins, env) 747*90c8c64dSAndroid Build Coastguard Worker build.implicit_ins = eval_path_strings(implicit_ins, env) 748*90c8c64dSAndroid Build Coastguard Worker build.prerequisites = eval_path_strings(prerequisites, env) 749*90c8c64dSAndroid Build Coastguard Worker build.depfile_implicit_ins = tuple() 750*90c8c64dSAndroid Build Coastguard Worker 751*90c8c64dSAndroid Build Coastguard Worker self._builds.append(build) 752*90c8c64dSAndroid Build Coastguard Worker 753*90c8c64dSAndroid Build Coastguard Worker 754*90c8c64dSAndroid Build Coastguard Worker def _parse_rule_stmt(self): 755*90c8c64dSAndroid Build Coastguard Worker """Parse a `rule` statement. 756*90c8c64dSAndroid Build Coastguard Worker 757*90c8c64dSAndroid Build Coastguard Worker Example: 758*90c8c64dSAndroid Build Coastguard Worker rule IDENT 759*90c8c64dSAndroid Build Coastguard Worker SPACE IDENT1 = STRING1 760*90c8c64dSAndroid Build Coastguard Worker SPACE IDENT2 = STRING2 761*90c8c64dSAndroid Build Coastguard Worker """ 762*90c8c64dSAndroid Build Coastguard Worker 763*90c8c64dSAndroid Build Coastguard Worker token = self._lexer.lex_match({TK.IDENT}) 764*90c8c64dSAndroid Build Coastguard Worker assert token.value == 'rule' 765*90c8c64dSAndroid Build Coastguard Worker 766*90c8c64dSAndroid Build Coastguard Worker rule = Rule() 767*90c8c64dSAndroid Build Coastguard Worker rule.name = self._lexer.lex_match({TK.IDENT}).value 768*90c8c64dSAndroid Build Coastguard Worker self._lexer.lex_match({TK.NEWLINE, TK.EOF}) 769*90c8c64dSAndroid Build Coastguard Worker rule.bindings = self._parse_local_binding_block() 770*90c8c64dSAndroid Build Coastguard Worker 771*90c8c64dSAndroid Build Coastguard Worker self._rules.append(rule) 772*90c8c64dSAndroid Build Coastguard Worker self._rules_dict[rule.name] = rule 773*90c8c64dSAndroid Build Coastguard Worker 774*90c8c64dSAndroid Build Coastguard Worker 775*90c8c64dSAndroid Build Coastguard Worker def _parse_default_stmt(self): 776*90c8c64dSAndroid Build Coastguard Worker """Parse a `default` statement. 777*90c8c64dSAndroid Build Coastguard Worker 778*90c8c64dSAndroid Build Coastguard Worker Example: 779*90c8c64dSAndroid Build Coastguard Worker default PATH1 PATH2 PATH3 780*90c8c64dSAndroid Build Coastguard Worker """ 781*90c8c64dSAndroid Build Coastguard Worker 782*90c8c64dSAndroid Build Coastguard Worker token = self._lexer.lex_match({TK.IDENT}) 783*90c8c64dSAndroid Build Coastguard Worker assert token.value == 'default' 784*90c8c64dSAndroid Build Coastguard Worker 785*90c8c64dSAndroid Build Coastguard Worker default = Default() 786*90c8c64dSAndroid Build Coastguard Worker outs = self._parse_path_list({TK.NEWLINE, TK.EOF}) 787*90c8c64dSAndroid Build Coastguard Worker default.outs = eval_path_strings(outs, self._env) 788*90c8c64dSAndroid Build Coastguard Worker 789*90c8c64dSAndroid Build Coastguard Worker self._lexer.lex_match({TK.NEWLINE, TK.EOF}) 790*90c8c64dSAndroid Build Coastguard Worker 791*90c8c64dSAndroid Build Coastguard Worker self._defaults.append(default) 792*90c8c64dSAndroid Build Coastguard Worker 793*90c8c64dSAndroid Build Coastguard Worker 794*90c8c64dSAndroid Build Coastguard Worker def _parse_pool_stmt(self): 795*90c8c64dSAndroid Build Coastguard Worker """Parse a `pool` statement. 796*90c8c64dSAndroid Build Coastguard Worker 797*90c8c64dSAndroid Build Coastguard Worker Example: 798*90c8c64dSAndroid Build Coastguard Worker pool IDENT 799*90c8c64dSAndroid Build Coastguard Worker SPACE IDENT1 = STRING1 800*90c8c64dSAndroid Build Coastguard Worker SPACE IDENT2 = STRING2 801*90c8c64dSAndroid Build Coastguard Worker """ 802*90c8c64dSAndroid Build Coastguard Worker token = self._lexer.lex_match({TK.IDENT}) 803*90c8c64dSAndroid Build Coastguard Worker assert token.value == 'pool' 804*90c8c64dSAndroid Build Coastguard Worker 805*90c8c64dSAndroid Build Coastguard Worker pool = Pool() 806*90c8c64dSAndroid Build Coastguard Worker 807*90c8c64dSAndroid Build Coastguard Worker token = self._lexer.lex() 808*90c8c64dSAndroid Build Coastguard Worker assert token.kind == TK.IDENT 809*90c8c64dSAndroid Build Coastguard Worker pool.name = token.value 810*90c8c64dSAndroid Build Coastguard Worker 811*90c8c64dSAndroid Build Coastguard Worker self._lexer.lex_match({TK.NEWLINE, TK.EOF}) 812*90c8c64dSAndroid Build Coastguard Worker 813*90c8c64dSAndroid Build Coastguard Worker pool.bindings = self._parse_local_binding_block() 814*90c8c64dSAndroid Build Coastguard Worker 815*90c8c64dSAndroid Build Coastguard Worker self._pools.append(pool) 816*90c8c64dSAndroid Build Coastguard Worker 817*90c8c64dSAndroid Build Coastguard Worker 818*90c8c64dSAndroid Build Coastguard Worker def _parse_include_stmt(self): 819*90c8c64dSAndroid Build Coastguard Worker """Parse an `include` or `subninja` statement. 820*90c8c64dSAndroid Build Coastguard Worker 821*90c8c64dSAndroid Build Coastguard Worker Example: 822*90c8c64dSAndroid Build Coastguard Worker include PATH 823*90c8c64dSAndroid Build Coastguard Worker subninja PATH 824*90c8c64dSAndroid Build Coastguard Worker """ 825*90c8c64dSAndroid Build Coastguard Worker 826*90c8c64dSAndroid Build Coastguard Worker token = self._lexer.lex_match({TK.IDENT}) 827*90c8c64dSAndroid Build Coastguard Worker assert token.value in {'include', 'subninja'} 828*90c8c64dSAndroid Build Coastguard Worker wrap_env = token.value == 'subninja' 829*90c8c64dSAndroid Build Coastguard Worker 830*90c8c64dSAndroid Build Coastguard Worker token = self._lexer.lex_path() 831*90c8c64dSAndroid Build Coastguard Worker path = eval_string(token.value, self._env) # XXX: Check lookup order 832*90c8c64dSAndroid Build Coastguard Worker self._lexer.lex_match({TK.NEWLINE, TK.EOF}) 833*90c8c64dSAndroid Build Coastguard Worker 834*90c8c64dSAndroid Build Coastguard Worker if wrap_env: 835*90c8c64dSAndroid Build Coastguard Worker env = EvalEnv() 836*90c8c64dSAndroid Build Coastguard Worker env.parent = self._env 837*90c8c64dSAndroid Build Coastguard Worker else: 838*90c8c64dSAndroid Build Coastguard Worker env = self._env 839*90c8c64dSAndroid Build Coastguard Worker self._parse_internal(path, self._lexer.encoding, env) 840*90c8c64dSAndroid Build Coastguard Worker 841*90c8c64dSAndroid Build Coastguard Worker 842*90c8c64dSAndroid Build Coastguard Worker def parse_dep_file(self, path, encoding): 843*90c8c64dSAndroid Build Coastguard Worker depfile = DepFileParser().parse(path, encoding) 844*90c8c64dSAndroid Build Coastguard Worker for build in self._builds: 845*90c8c64dSAndroid Build Coastguard Worker depfile_implicit_ins = set() 846*90c8c64dSAndroid Build Coastguard Worker for explicit_out in build.explicit_outs: 847*90c8c64dSAndroid Build Coastguard Worker deps = depfile.get(explicit_out) 848*90c8c64dSAndroid Build Coastguard Worker if deps: 849*90c8c64dSAndroid Build Coastguard Worker depfile_implicit_ins.update(deps.implicit_ins) 850*90c8c64dSAndroid Build Coastguard Worker build.depfile_implicit_ins = tuple(sorted(depfile_implicit_ins)) 851*90c8c64dSAndroid Build Coastguard Worker 852*90c8c64dSAndroid Build Coastguard Worker 853*90c8c64dSAndroid Build Coastguard Workerclass DepFileError(ValueError): 854*90c8c64dSAndroid Build Coastguard Worker pass 855*90c8c64dSAndroid Build Coastguard Worker 856*90c8c64dSAndroid Build Coastguard Worker 857*90c8c64dSAndroid Build Coastguard Workerclass DepFileRecord(object): 858*90c8c64dSAndroid Build Coastguard Worker __slots__ = ('id', 'explicit_out', 'mtime', 'implicit_ins') 859*90c8c64dSAndroid Build Coastguard Worker 860*90c8c64dSAndroid Build Coastguard Worker 861*90c8c64dSAndroid Build Coastguard Worker def __init__(self, id, explicit_out, mtime, implicit_ins): 862*90c8c64dSAndroid Build Coastguard Worker self.id = id 863*90c8c64dSAndroid Build Coastguard Worker self.explicit_out = explicit_out 864*90c8c64dSAndroid Build Coastguard Worker self.mtime = mtime 865*90c8c64dSAndroid Build Coastguard Worker self.implicit_ins = implicit_ins 866*90c8c64dSAndroid Build Coastguard Worker 867*90c8c64dSAndroid Build Coastguard Worker 868*90c8c64dSAndroid Build Coastguard Workerclass DepFileParser(object): 869*90c8c64dSAndroid Build Coastguard Worker """Ninja deps log parser which parses ``.ninja_deps`` file. 870*90c8c64dSAndroid Build Coastguard Worker """ 871*90c8c64dSAndroid Build Coastguard Worker 872*90c8c64dSAndroid Build Coastguard Worker 873*90c8c64dSAndroid Build Coastguard Worker def __init__(self): 874*90c8c64dSAndroid Build Coastguard Worker self._deps = [] 875*90c8c64dSAndroid Build Coastguard Worker self._paths = [] 876*90c8c64dSAndroid Build Coastguard Worker self._path_deps = {} 877*90c8c64dSAndroid Build Coastguard Worker 878*90c8c64dSAndroid Build Coastguard Worker 879*90c8c64dSAndroid Build Coastguard Worker def parse(self, path, encoding): 880*90c8c64dSAndroid Build Coastguard Worker with open(path, 'rb') as fp: 881*90c8c64dSAndroid Build Coastguard Worker return self._parse(fp, encoding) 882*90c8c64dSAndroid Build Coastguard Worker 883*90c8c64dSAndroid Build Coastguard Worker 884*90c8c64dSAndroid Build Coastguard Worker @staticmethod 885*90c8c64dSAndroid Build Coastguard Worker def _unpack_uint32(buf): 886*90c8c64dSAndroid Build Coastguard Worker return struct.unpack('<I', buf)[0] 887*90c8c64dSAndroid Build Coastguard Worker 888*90c8c64dSAndroid Build Coastguard Worker 889*90c8c64dSAndroid Build Coastguard Worker @staticmethod 890*90c8c64dSAndroid Build Coastguard Worker def _unpack_uint32_iter(buf): 891*90c8c64dSAndroid Build Coastguard Worker for p in struct.iter_unpack('<I', buf): 892*90c8c64dSAndroid Build Coastguard Worker yield p[0] 893*90c8c64dSAndroid Build Coastguard Worker 894*90c8c64dSAndroid Build Coastguard Worker 895*90c8c64dSAndroid Build Coastguard Worker if sys.version_info < (3,): 896*90c8c64dSAndroid Build Coastguard Worker @staticmethod 897*90c8c64dSAndroid Build Coastguard Worker def _extract_path(s, encoding): 898*90c8c64dSAndroid Build Coastguard Worker pos = len(s) 899*90c8c64dSAndroid Build Coastguard Worker count = 3 900*90c8c64dSAndroid Build Coastguard Worker while count > 0 and pos > 0 and s[pos - 1] == b'\0': 901*90c8c64dSAndroid Build Coastguard Worker pos -= 1 902*90c8c64dSAndroid Build Coastguard Worker count -= 1 903*90c8c64dSAndroid Build Coastguard Worker return intern(s[0:pos]) 904*90c8c64dSAndroid Build Coastguard Worker else: 905*90c8c64dSAndroid Build Coastguard Worker @staticmethod 906*90c8c64dSAndroid Build Coastguard Worker def _extract_path(s, encoding): 907*90c8c64dSAndroid Build Coastguard Worker pos = len(s) 908*90c8c64dSAndroid Build Coastguard Worker count = 3 909*90c8c64dSAndroid Build Coastguard Worker while count > 0 and pos > 0 and s[pos - 1] == 0: 910*90c8c64dSAndroid Build Coastguard Worker pos -= 1 911*90c8c64dSAndroid Build Coastguard Worker count -= 1 912*90c8c64dSAndroid Build Coastguard Worker return intern(s[0:pos].decode(encoding)) 913*90c8c64dSAndroid Build Coastguard Worker 914*90c8c64dSAndroid Build Coastguard Worker 915*90c8c64dSAndroid Build Coastguard Worker def _get_path(self, index): 916*90c8c64dSAndroid Build Coastguard Worker try: 917*90c8c64dSAndroid Build Coastguard Worker return self._paths[index] 918*90c8c64dSAndroid Build Coastguard Worker except IndexError: 919*90c8c64dSAndroid Build Coastguard Worker raise DepFileError('path index overflow') 920*90c8c64dSAndroid Build Coastguard Worker 921*90c8c64dSAndroid Build Coastguard Worker 922*90c8c64dSAndroid Build Coastguard Worker def _parse(self, fp, encoding): 923*90c8c64dSAndroid Build Coastguard Worker # Check the magic word 924*90c8c64dSAndroid Build Coastguard Worker if fp.readline() != b'# ninjadeps\n': 925*90c8c64dSAndroid Build Coastguard Worker raise DepFileError('bad magic word') 926*90c8c64dSAndroid Build Coastguard Worker 927*90c8c64dSAndroid Build Coastguard Worker # Check the file format version 928*90c8c64dSAndroid Build Coastguard Worker version = self._unpack_uint32(fp.read(4)) 929*90c8c64dSAndroid Build Coastguard Worker if version != 3: 930*90c8c64dSAndroid Build Coastguard Worker raise DepFileError('unsupported deps log version: ' + str(version)) 931*90c8c64dSAndroid Build Coastguard Worker 932*90c8c64dSAndroid Build Coastguard Worker # Read the records 933*90c8c64dSAndroid Build Coastguard Worker MAX_RECORD_SIZE = (1 << 19) - 1 934*90c8c64dSAndroid Build Coastguard Worker while True: 935*90c8c64dSAndroid Build Coastguard Worker buf = fp.read(4) 936*90c8c64dSAndroid Build Coastguard Worker if not buf: 937*90c8c64dSAndroid Build Coastguard Worker break 938*90c8c64dSAndroid Build Coastguard Worker 939*90c8c64dSAndroid Build Coastguard Worker record_size = self._unpack_uint32(buf) 940*90c8c64dSAndroid Build Coastguard Worker is_dep = bool(record_size >> 31) 941*90c8c64dSAndroid Build Coastguard Worker record_size &= (1 << 31) - 1 942*90c8c64dSAndroid Build Coastguard Worker 943*90c8c64dSAndroid Build Coastguard Worker if record_size > MAX_RECORD_SIZE: 944*90c8c64dSAndroid Build Coastguard Worker raise DepFileError('record size overflow') 945*90c8c64dSAndroid Build Coastguard Worker 946*90c8c64dSAndroid Build Coastguard Worker if is_dep: 947*90c8c64dSAndroid Build Coastguard Worker if record_size % 4 != 0 or record_size < 8: 948*90c8c64dSAndroid Build Coastguard Worker raise DepFileError('corrupted deps record') 949*90c8c64dSAndroid Build Coastguard Worker 950*90c8c64dSAndroid Build Coastguard Worker buf = fp.read(record_size) 951*90c8c64dSAndroid Build Coastguard Worker 952*90c8c64dSAndroid Build Coastguard Worker dep_iter = self._unpack_uint32_iter(buf) 953*90c8c64dSAndroid Build Coastguard Worker 954*90c8c64dSAndroid Build Coastguard Worker idx = len(self._deps) 955*90c8c64dSAndroid Build Coastguard Worker explicit_out = self._get_path(next(dep_iter)) 956*90c8c64dSAndroid Build Coastguard Worker mtime = next(dep_iter) 957*90c8c64dSAndroid Build Coastguard Worker implicit_ins = [self._get_path(p) for p in dep_iter] 958*90c8c64dSAndroid Build Coastguard Worker 959*90c8c64dSAndroid Build Coastguard Worker deps = DepFileRecord(idx, explicit_out, mtime, implicit_ins) 960*90c8c64dSAndroid Build Coastguard Worker 961*90c8c64dSAndroid Build Coastguard Worker old_deps = self._path_deps.get(explicit_out) 962*90c8c64dSAndroid Build Coastguard Worker if not old_deps: 963*90c8c64dSAndroid Build Coastguard Worker self._deps.append(deps) 964*90c8c64dSAndroid Build Coastguard Worker self._path_deps[explicit_out] = deps 965*90c8c64dSAndroid Build Coastguard Worker elif old_deps.mtime > deps.mtime: 966*90c8c64dSAndroid Build Coastguard Worker self._deps.append(None) 967*90c8c64dSAndroid Build Coastguard Worker else: 968*90c8c64dSAndroid Build Coastguard Worker self._deps[old_deps.id] = None 969*90c8c64dSAndroid Build Coastguard Worker self._deps.append(deps) 970*90c8c64dSAndroid Build Coastguard Worker self._path_deps[explicit_out] = deps 971*90c8c64dSAndroid Build Coastguard Worker else: 972*90c8c64dSAndroid Build Coastguard Worker if record_size < 4: 973*90c8c64dSAndroid Build Coastguard Worker raise DepFileError('corrupted path record') 974*90c8c64dSAndroid Build Coastguard Worker buf = fp.read(record_size - 4) 975*90c8c64dSAndroid Build Coastguard Worker path = self._extract_path(buf, encoding) 976*90c8c64dSAndroid Build Coastguard Worker buf = fp.read(4) 977*90c8c64dSAndroid Build Coastguard Worker checksum = 0xffffffff ^ self._unpack_uint32(buf) 978*90c8c64dSAndroid Build Coastguard Worker if len(self._paths) != checksum: 979*90c8c64dSAndroid Build Coastguard Worker raise DepFileError('bad path record checksum') 980*90c8c64dSAndroid Build Coastguard Worker self._paths.append(path) 981*90c8c64dSAndroid Build Coastguard Worker 982*90c8c64dSAndroid Build Coastguard Worker return self._path_deps 983*90c8c64dSAndroid Build Coastguard Worker 984*90c8c64dSAndroid Build Coastguard Worker 985*90c8c64dSAndroid Build Coastguard Workerdef _parse_args(): 986*90c8c64dSAndroid Build Coastguard Worker """Parse command line options.""" 987*90c8c64dSAndroid Build Coastguard Worker 988*90c8c64dSAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 989*90c8c64dSAndroid Build Coastguard Worker subparsers = parser.add_subparsers(dest='command') 990*90c8c64dSAndroid Build Coastguard Worker 991*90c8c64dSAndroid Build Coastguard Worker def _register_input_file_args(parser): 992*90c8c64dSAndroid Build Coastguard Worker parser.add_argument('input_file', help='input ninja file') 993*90c8c64dSAndroid Build Coastguard Worker parser.add_argument('--ninja-deps', help='.ninja_deps file') 994*90c8c64dSAndroid Build Coastguard Worker parser.add_argument('--cwd', help='working directory for ninja') 995*90c8c64dSAndroid Build Coastguard Worker parser.add_argument('--encoding', default='utf-8', 996*90c8c64dSAndroid Build Coastguard Worker help='ninja file encoding') 997*90c8c64dSAndroid Build Coastguard Worker 998*90c8c64dSAndroid Build Coastguard Worker # dump sub-command 999*90c8c64dSAndroid Build Coastguard Worker parser_dump = subparsers.add_parser('dump', help='dump dependency graph') 1000*90c8c64dSAndroid Build Coastguard Worker _register_input_file_args(parser_dump) 1001*90c8c64dSAndroid Build Coastguard Worker parser_dump.add_argument('-o', '--output', help='output file') 1002*90c8c64dSAndroid Build Coastguard Worker 1003*90c8c64dSAndroid Build Coastguard Worker # pickle sub-command 1004*90c8c64dSAndroid Build Coastguard Worker parser_pickle = subparsers.add_parser( 1005*90c8c64dSAndroid Build Coastguard Worker 'pickle', help='serialize dependency graph with pickle') 1006*90c8c64dSAndroid Build Coastguard Worker _register_input_file_args(parser_pickle) 1007*90c8c64dSAndroid Build Coastguard Worker parser_pickle.add_argument('-o', '--output', required=True, 1008*90c8c64dSAndroid Build Coastguard Worker help='output file') 1009*90c8c64dSAndroid Build Coastguard Worker 1010*90c8c64dSAndroid Build Coastguard Worker # Parse arguments and check sub-command 1011*90c8c64dSAndroid Build Coastguard Worker args = parser.parse_args() 1012*90c8c64dSAndroid Build Coastguard Worker if args.command is None: 1013*90c8c64dSAndroid Build Coastguard Worker parser.print_help() 1014*90c8c64dSAndroid Build Coastguard Worker sys.exit(1) 1015*90c8c64dSAndroid Build Coastguard Worker 1016*90c8c64dSAndroid Build Coastguard Worker return args 1017*90c8c64dSAndroid Build Coastguard Worker 1018*90c8c64dSAndroid Build Coastguard Worker 1019*90c8c64dSAndroid Build Coastguard Workerdef load_manifest_from_args(args): 1020*90c8c64dSAndroid Build Coastguard Worker """Load the input manifest specified by command line options.""" 1021*90c8c64dSAndroid Build Coastguard Worker 1022*90c8c64dSAndroid Build Coastguard Worker input_file = args.input_file 1023*90c8c64dSAndroid Build Coastguard Worker 1024*90c8c64dSAndroid Build Coastguard Worker # If the input file name ends with `.pickle`, load it with pickle.load(). 1025*90c8c64dSAndroid Build Coastguard Worker if input_file.endswith('.pickle'): 1026*90c8c64dSAndroid Build Coastguard Worker with open(input_file, 'rb') as pickle_file: 1027*90c8c64dSAndroid Build Coastguard Worker return pickle.load(pickle_file) 1028*90c8c64dSAndroid Build Coastguard Worker 1029*90c8c64dSAndroid Build Coastguard Worker # Parse the ninja file 1030*90c8c64dSAndroid Build Coastguard Worker return Parser(args.cwd).parse(args.input_file, args.encoding, 1031*90c8c64dSAndroid Build Coastguard Worker args.ninja_deps) 1032*90c8c64dSAndroid Build Coastguard Worker 1033*90c8c64dSAndroid Build Coastguard Worker 1034*90c8c64dSAndroid Build Coastguard Workerdef dump_manifest(manifest, file): 1035*90c8c64dSAndroid Build Coastguard Worker """Dump a manifest to a text file.""" 1036*90c8c64dSAndroid Build Coastguard Worker 1037*90c8c64dSAndroid Build Coastguard Worker for rule in manifest.rules: 1038*90c8c64dSAndroid Build Coastguard Worker print('rule', rule.name, file=file) 1039*90c8c64dSAndroid Build Coastguard Worker 1040*90c8c64dSAndroid Build Coastguard Worker for build in manifest.builds: 1041*90c8c64dSAndroid Build Coastguard Worker print('build', file=file) 1042*90c8c64dSAndroid Build Coastguard Worker for path in build.explicit_outs: 1043*90c8c64dSAndroid Build Coastguard Worker print(' explicit_out:', path, file=file) 1044*90c8c64dSAndroid Build Coastguard Worker for path in build.implicit_outs: 1045*90c8c64dSAndroid Build Coastguard Worker print(' implicit_out:', path, file=file) 1046*90c8c64dSAndroid Build Coastguard Worker for path in build.explicit_ins: 1047*90c8c64dSAndroid Build Coastguard Worker print(' explicit_in:', path, file=file) 1048*90c8c64dSAndroid Build Coastguard Worker for path in build.implicit_ins: 1049*90c8c64dSAndroid Build Coastguard Worker print(' implicit_in:', path, file=file) 1050*90c8c64dSAndroid Build Coastguard Worker for path in build.prerequisites: 1051*90c8c64dSAndroid Build Coastguard Worker print(' prerequisites:', path, file=file) 1052*90c8c64dSAndroid Build Coastguard Worker for path in build.depfile_implicit_ins: 1053*90c8c64dSAndroid Build Coastguard Worker print(' depfile_implicit_in:', path, file=file) 1054*90c8c64dSAndroid Build Coastguard Worker 1055*90c8c64dSAndroid Build Coastguard Worker for pool in manifest.pools: 1056*90c8c64dSAndroid Build Coastguard Worker print('pool', pool.name, file=file) 1057*90c8c64dSAndroid Build Coastguard Worker 1058*90c8c64dSAndroid Build Coastguard Worker for default in manifest.defaults: 1059*90c8c64dSAndroid Build Coastguard Worker print('default', file=file) 1060*90c8c64dSAndroid Build Coastguard Worker for path in default.outs: 1061*90c8c64dSAndroid Build Coastguard Worker print(' out:', path, file=file) 1062*90c8c64dSAndroid Build Coastguard Worker 1063*90c8c64dSAndroid Build Coastguard Worker 1064*90c8c64dSAndroid Build Coastguard Workerdef command_dump_main(args): 1065*90c8c64dSAndroid Build Coastguard Worker """Main function for the dump sub-command""" 1066*90c8c64dSAndroid Build Coastguard Worker if args.output is None: 1067*90c8c64dSAndroid Build Coastguard Worker dump_manifest(load_manifest_from_args(args), sys.stdout) 1068*90c8c64dSAndroid Build Coastguard Worker else: 1069*90c8c64dSAndroid Build Coastguard Worker with open(args.output, 'w') as output_file: 1070*90c8c64dSAndroid Build Coastguard Worker dump_manifest(load_manifest_from_args(args), output_file) 1071*90c8c64dSAndroid Build Coastguard Worker 1072*90c8c64dSAndroid Build Coastguard Worker 1073*90c8c64dSAndroid Build Coastguard Workerdef command_pickle_main(args): 1074*90c8c64dSAndroid Build Coastguard Worker """Main function for the pickle sub-command""" 1075*90c8c64dSAndroid Build Coastguard Worker with open(args.output, 'wb') as output_file: 1076*90c8c64dSAndroid Build Coastguard Worker pickle.dump(load_manifest_from_args(args), output_file) 1077*90c8c64dSAndroid Build Coastguard Worker 1078*90c8c64dSAndroid Build Coastguard Worker 1079*90c8c64dSAndroid Build Coastguard Workerdef main(): 1080*90c8c64dSAndroid Build Coastguard Worker """Main function for the executable""" 1081*90c8c64dSAndroid Build Coastguard Worker args = _parse_args() 1082*90c8c64dSAndroid Build Coastguard Worker if args.command == 'dump': 1083*90c8c64dSAndroid Build Coastguard Worker command_dump_main(args) 1084*90c8c64dSAndroid Build Coastguard Worker elif args.command == 'pickle': 1085*90c8c64dSAndroid Build Coastguard Worker command_pickle_main(args) 1086*90c8c64dSAndroid Build Coastguard Worker else: 1087*90c8c64dSAndroid Build Coastguard Worker raise KeyError('unknown command ' + args.command) 1088*90c8c64dSAndroid Build Coastguard Worker 1089*90c8c64dSAndroid Build Coastguard Worker 1090*90c8c64dSAndroid Build Coastguard Workerif __name__ == '__main__': 1091*90c8c64dSAndroid Build Coastguard Worker import ninja 1092*90c8c64dSAndroid Build Coastguard Worker ninja.main() 1093