xref: /aosp_15_r20/development/vndk/tools/sourcedr/ninja/ninja.py (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
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