xref: /aosp_15_r20/development/vndk/tools/sourcedr/blueprint/blueprint.py (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1*90c8c64dSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*90c8c64dSAndroid Build Coastguard Worker
3*90c8c64dSAndroid Build Coastguard Worker#
4*90c8c64dSAndroid Build Coastguard Worker# Copyright (C) 2018 The Android Open Source Project
5*90c8c64dSAndroid Build Coastguard Worker#
6*90c8c64dSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
7*90c8c64dSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
8*90c8c64dSAndroid Build Coastguard Worker# You may obtain a copy of the License at
9*90c8c64dSAndroid Build Coastguard Worker#
10*90c8c64dSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
11*90c8c64dSAndroid Build Coastguard Worker#
12*90c8c64dSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
13*90c8c64dSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
14*90c8c64dSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15*90c8c64dSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
16*90c8c64dSAndroid Build Coastguard Worker# limitations under the License.
17*90c8c64dSAndroid Build Coastguard Worker#
18*90c8c64dSAndroid Build Coastguard Worker
19*90c8c64dSAndroid Build Coastguard Worker"""This module implements a Android.bp parser."""
20*90c8c64dSAndroid Build Coastguard Worker
21*90c8c64dSAndroid Build Coastguard Workerimport collections
22*90c8c64dSAndroid Build Coastguard Workerimport glob
23*90c8c64dSAndroid Build Coastguard Workerimport itertools
24*90c8c64dSAndroid Build Coastguard Workerimport os
25*90c8c64dSAndroid Build Coastguard Workerimport re
26*90c8c64dSAndroid Build Coastguard Workerimport sys
27*90c8c64dSAndroid Build Coastguard Worker
28*90c8c64dSAndroid Build Coastguard Worker
29*90c8c64dSAndroid Build Coastguard Worker#------------------------------------------------------------------------------
30*90c8c64dSAndroid Build Coastguard Worker# Python 2 compatibility
31*90c8c64dSAndroid Build Coastguard Worker#------------------------------------------------------------------------------
32*90c8c64dSAndroid Build Coastguard Worker
33*90c8c64dSAndroid Build Coastguard Workerif sys.version_info >= (3,):
34*90c8c64dSAndroid Build Coastguard Worker    py3_chr = chr  # pylint: disable=invalid-name
35*90c8c64dSAndroid Build Coastguard Workerelse:
36*90c8c64dSAndroid Build Coastguard Worker    def py3_chr(codepoint):
37*90c8c64dSAndroid Build Coastguard Worker        """Convert an integer character codepoint into a utf-8 string."""
38*90c8c64dSAndroid Build Coastguard Worker        return unichr(codepoint).encode('utf-8')
39*90c8c64dSAndroid Build Coastguard Worker
40*90c8c64dSAndroid Build Coastguard Workertry:
41*90c8c64dSAndroid Build Coastguard Worker    from enum import Enum
42*90c8c64dSAndroid Build Coastguard Workerexcept ImportError:
43*90c8c64dSAndroid Build Coastguard Worker    class _Enum(object):  # pylint: disable=too-few-public-methods
44*90c8c64dSAndroid Build Coastguard Worker        """A name-value pair for each enumeration."""
45*90c8c64dSAndroid Build Coastguard Worker
46*90c8c64dSAndroid Build Coastguard Worker        __slot__ = ('name', 'value')
47*90c8c64dSAndroid Build Coastguard Worker
48*90c8c64dSAndroid Build Coastguard Worker
49*90c8c64dSAndroid Build Coastguard Worker        def __init__(self, name, value):
50*90c8c64dSAndroid Build Coastguard Worker            """Create a name-value pair."""
51*90c8c64dSAndroid Build Coastguard Worker            self.name = name
52*90c8c64dSAndroid Build Coastguard Worker            self.value = value
53*90c8c64dSAndroid Build Coastguard Worker
54*90c8c64dSAndroid Build Coastguard Worker
55*90c8c64dSAndroid Build Coastguard Worker        def __repr__(self):
56*90c8c64dSAndroid Build Coastguard Worker            """Return the name of the enumeration."""
57*90c8c64dSAndroid Build Coastguard Worker            return self.name
58*90c8c64dSAndroid Build Coastguard Worker
59*90c8c64dSAndroid Build Coastguard Worker
60*90c8c64dSAndroid Build Coastguard Worker    class _EnumMeta(type):  # pylint: disable=too-few-public-methods
61*90c8c64dSAndroid Build Coastguard Worker        """Metaclass for Enum base class."""
62*90c8c64dSAndroid Build Coastguard Worker
63*90c8c64dSAndroid Build Coastguard Worker        def __new__(mcs, name, bases, attrs):
64*90c8c64dSAndroid Build Coastguard Worker            """Collects enumerations from attributes of the derived classes."""
65*90c8c64dSAndroid Build Coastguard Worker            enums = []
66*90c8c64dSAndroid Build Coastguard Worker            new_attrs = {'_enums': enums}
67*90c8c64dSAndroid Build Coastguard Worker
68*90c8c64dSAndroid Build Coastguard Worker            for key, value in attrs.iteritems():
69*90c8c64dSAndroid Build Coastguard Worker                if key.startswith('_'):
70*90c8c64dSAndroid Build Coastguard Worker                    new_attrs[key] = value
71*90c8c64dSAndroid Build Coastguard Worker                else:
72*90c8c64dSAndroid Build Coastguard Worker                    item = _Enum(key, value)
73*90c8c64dSAndroid Build Coastguard Worker                    enums.append(item)
74*90c8c64dSAndroid Build Coastguard Worker                    new_attrs[key] = item
75*90c8c64dSAndroid Build Coastguard Worker
76*90c8c64dSAndroid Build Coastguard Worker            return type.__new__(mcs, name, bases, new_attrs)
77*90c8c64dSAndroid Build Coastguard Worker
78*90c8c64dSAndroid Build Coastguard Worker
79*90c8c64dSAndroid Build Coastguard Worker        def __iter__(cls):
80*90c8c64dSAndroid Build Coastguard Worker            """Iterate the list of enumerations."""
81*90c8c64dSAndroid Build Coastguard Worker            return iter(cls._enums)
82*90c8c64dSAndroid Build Coastguard Worker
83*90c8c64dSAndroid Build Coastguard Worker
84*90c8c64dSAndroid Build Coastguard Worker    class Enum(object):  # pylint: disable=too-few-public-methods
85*90c8c64dSAndroid Build Coastguard Worker        """Enum base class."""
86*90c8c64dSAndroid Build Coastguard Worker        __metaclass__ = _EnumMeta
87*90c8c64dSAndroid Build Coastguard Worker
88*90c8c64dSAndroid Build Coastguard Worker
89*90c8c64dSAndroid Build Coastguard Worker#------------------------------------------------------------------------------
90*90c8c64dSAndroid Build Coastguard Worker# Lexer
91*90c8c64dSAndroid Build Coastguard Worker#------------------------------------------------------------------------------
92*90c8c64dSAndroid Build Coastguard Worker
93*90c8c64dSAndroid Build Coastguard Workerclass Token(Enum):  # pylint: disable=too-few-public-methods
94*90c8c64dSAndroid Build Coastguard Worker    """Token enumerations."""
95*90c8c64dSAndroid Build Coastguard Worker
96*90c8c64dSAndroid Build Coastguard Worker    EOF = 0
97*90c8c64dSAndroid Build Coastguard Worker
98*90c8c64dSAndroid Build Coastguard Worker    IDENT = 1
99*90c8c64dSAndroid Build Coastguard Worker    LPAREN = 2
100*90c8c64dSAndroid Build Coastguard Worker    RPAREN = 3
101*90c8c64dSAndroid Build Coastguard Worker    LBRACKET = 4
102*90c8c64dSAndroid Build Coastguard Worker    RBRACKET = 5
103*90c8c64dSAndroid Build Coastguard Worker    LBRACE = 6
104*90c8c64dSAndroid Build Coastguard Worker    RBRACE = 7
105*90c8c64dSAndroid Build Coastguard Worker    COLON = 8
106*90c8c64dSAndroid Build Coastguard Worker    ASSIGN = 9
107*90c8c64dSAndroid Build Coastguard Worker    ASSIGNPLUS = 10
108*90c8c64dSAndroid Build Coastguard Worker    PLUS = 11
109*90c8c64dSAndroid Build Coastguard Worker    COMMA = 12
110*90c8c64dSAndroid Build Coastguard Worker    STRING = 13
111*90c8c64dSAndroid Build Coastguard Worker    INTEGER = 14
112*90c8c64dSAndroid Build Coastguard Worker
113*90c8c64dSAndroid Build Coastguard Worker    COMMENT = 15
114*90c8c64dSAndroid Build Coastguard Worker    SPACE = 16
115*90c8c64dSAndroid Build Coastguard Worker
116*90c8c64dSAndroid Build Coastguard Worker
117*90c8c64dSAndroid Build Coastguard Workerclass LexerError(ValueError):
118*90c8c64dSAndroid Build Coastguard Worker    """Lexer error exception class."""
119*90c8c64dSAndroid Build Coastguard Worker
120*90c8c64dSAndroid Build Coastguard Worker    def __init__(self, buf, pos, message):
121*90c8c64dSAndroid Build Coastguard Worker        """Create a lexer error exception object."""
122*90c8c64dSAndroid Build Coastguard Worker        super(LexerError, self).__init__(message)
123*90c8c64dSAndroid Build Coastguard Worker        self.message = message
124*90c8c64dSAndroid Build Coastguard Worker        self.line, self.column = Lexer.compute_line_column(buf, pos)
125*90c8c64dSAndroid Build Coastguard Worker
126*90c8c64dSAndroid Build Coastguard Worker
127*90c8c64dSAndroid Build Coastguard Worker    def __str__(self):
128*90c8c64dSAndroid Build Coastguard Worker        """Convert lexer error to string representation."""
129*90c8c64dSAndroid Build Coastguard Worker        return 'LexerError: {}:{}: {}'.format(
130*90c8c64dSAndroid Build Coastguard Worker            self.line, self.column, self.message)
131*90c8c64dSAndroid Build Coastguard Worker
132*90c8c64dSAndroid Build Coastguard Worker
133*90c8c64dSAndroid Build Coastguard Workerclass Lexer(object):
134*90c8c64dSAndroid Build Coastguard Worker    """Lexer to tokenize the input string."""
135*90c8c64dSAndroid Build Coastguard Worker
136*90c8c64dSAndroid Build Coastguard Worker    def __init__(self, buf, offset=0, path=None):
137*90c8c64dSAndroid Build Coastguard Worker        """Tokenize the source code in buf starting from offset.
138*90c8c64dSAndroid Build Coastguard Worker
139*90c8c64dSAndroid Build Coastguard Worker        Args:
140*90c8c64dSAndroid Build Coastguard Worker            buf (string) The source code to be tokenized.
141*90c8c64dSAndroid Build Coastguard Worker            offset (int) The position to start.
142*90c8c64dSAndroid Build Coastguard Worker        """
143*90c8c64dSAndroid Build Coastguard Worker
144*90c8c64dSAndroid Build Coastguard Worker        self.buf = buf
145*90c8c64dSAndroid Build Coastguard Worker
146*90c8c64dSAndroid Build Coastguard Worker        self.start = None
147*90c8c64dSAndroid Build Coastguard Worker        self.end = offset
148*90c8c64dSAndroid Build Coastguard Worker        self.token = None
149*90c8c64dSAndroid Build Coastguard Worker        self.literal = None
150*90c8c64dSAndroid Build Coastguard Worker        self.path = path
151*90c8c64dSAndroid Build Coastguard Worker
152*90c8c64dSAndroid Build Coastguard Worker        self._next()
153*90c8c64dSAndroid Build Coastguard Worker
154*90c8c64dSAndroid Build Coastguard Worker
155*90c8c64dSAndroid Build Coastguard Worker    def consume(self, *tokens):
156*90c8c64dSAndroid Build Coastguard Worker        """Consume one or more token."""
157*90c8c64dSAndroid Build Coastguard Worker
158*90c8c64dSAndroid Build Coastguard Worker        for token in tokens:
159*90c8c64dSAndroid Build Coastguard Worker            if token == self.token:
160*90c8c64dSAndroid Build Coastguard Worker                self._next()
161*90c8c64dSAndroid Build Coastguard Worker            else:
162*90c8c64dSAndroid Build Coastguard Worker                raise LexerError(self.buf, self.start,
163*90c8c64dSAndroid Build Coastguard Worker                                 'unexpected token ' + self.token.name)
164*90c8c64dSAndroid Build Coastguard Worker
165*90c8c64dSAndroid Build Coastguard Worker
166*90c8c64dSAndroid Build Coastguard Worker    def _next(self):
167*90c8c64dSAndroid Build Coastguard Worker        """Read next non-comment non-space token."""
168*90c8c64dSAndroid Build Coastguard Worker
169*90c8c64dSAndroid Build Coastguard Worker        buf_len = len(self.buf)
170*90c8c64dSAndroid Build Coastguard Worker        while self.end < buf_len:
171*90c8c64dSAndroid Build Coastguard Worker            self.start = self.end
172*90c8c64dSAndroid Build Coastguard Worker            self.token, self.end, self.literal = self.lex(self.buf, self.start)
173*90c8c64dSAndroid Build Coastguard Worker            if self.token != Token.SPACE and self.token != Token.COMMENT:
174*90c8c64dSAndroid Build Coastguard Worker                return
175*90c8c64dSAndroid Build Coastguard Worker
176*90c8c64dSAndroid Build Coastguard Worker        self.start = self.end
177*90c8c64dSAndroid Build Coastguard Worker        self.token = Token.EOF
178*90c8c64dSAndroid Build Coastguard Worker        self.literal = None
179*90c8c64dSAndroid Build Coastguard Worker
180*90c8c64dSAndroid Build Coastguard Worker
181*90c8c64dSAndroid Build Coastguard Worker    @staticmethod
182*90c8c64dSAndroid Build Coastguard Worker    def compute_line_column(buf, pos):
183*90c8c64dSAndroid Build Coastguard Worker        """Compute the line number and the column number of a given position in
184*90c8c64dSAndroid Build Coastguard Worker        the buffer."""
185*90c8c64dSAndroid Build Coastguard Worker
186*90c8c64dSAndroid Build Coastguard Worker        prior = buf[0:pos]
187*90c8c64dSAndroid Build Coastguard Worker        newline_pos = prior.rfind('\n')
188*90c8c64dSAndroid Build Coastguard Worker        if newline_pos == -1:
189*90c8c64dSAndroid Build Coastguard Worker            return (1, pos + 1)
190*90c8c64dSAndroid Build Coastguard Worker        return (prior.count('\n') + 1, pos - newline_pos)
191*90c8c64dSAndroid Build Coastguard Worker
192*90c8c64dSAndroid Build Coastguard Worker
193*90c8c64dSAndroid Build Coastguard Worker    UNICODE_CHARS_PATTERN = re.compile('[^\\\\\\n"]+')
194*90c8c64dSAndroid Build Coastguard Worker
195*90c8c64dSAndroid Build Coastguard Worker
196*90c8c64dSAndroid Build Coastguard Worker    ESCAPE_CHAR_TABLE = {
197*90c8c64dSAndroid Build Coastguard Worker        'a': '\a', 'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t',
198*90c8c64dSAndroid Build Coastguard Worker        'v': '\v', '\\': '\\', '\'': '\'', '\"': '\"',
199*90c8c64dSAndroid Build Coastguard Worker    }
200*90c8c64dSAndroid Build Coastguard Worker
201*90c8c64dSAndroid Build Coastguard Worker
202*90c8c64dSAndroid Build Coastguard Worker    OCT_TABLE = {str(i) for i in range(8)}
203*90c8c64dSAndroid Build Coastguard Worker
204*90c8c64dSAndroid Build Coastguard Worker
205*90c8c64dSAndroid Build Coastguard Worker    @staticmethod
206*90c8c64dSAndroid Build Coastguard Worker    def decode_oct(buf, offset, start, end):
207*90c8c64dSAndroid Build Coastguard Worker        """Read characters from buf[start:end] and interpret them as an octal
208*90c8c64dSAndroid Build Coastguard Worker        integer."""
209*90c8c64dSAndroid Build Coastguard Worker
210*90c8c64dSAndroid Build Coastguard Worker        if end > len(buf):
211*90c8c64dSAndroid Build Coastguard Worker            raise LexerError(buf, offset, 'bad octal escape sequence')
212*90c8c64dSAndroid Build Coastguard Worker        try:
213*90c8c64dSAndroid Build Coastguard Worker            codepoint = int(buf[start:end], 8)
214*90c8c64dSAndroid Build Coastguard Worker        except ValueError:
215*90c8c64dSAndroid Build Coastguard Worker            raise LexerError(buf, offset, 'bad octal escape sequence')
216*90c8c64dSAndroid Build Coastguard Worker        if codepoint > 0xff:
217*90c8c64dSAndroid Build Coastguard Worker            raise LexerError(buf, offset, 'bad octal escape sequence')
218*90c8c64dSAndroid Build Coastguard Worker        return codepoint
219*90c8c64dSAndroid Build Coastguard Worker
220*90c8c64dSAndroid Build Coastguard Worker
221*90c8c64dSAndroid Build Coastguard Worker    @staticmethod
222*90c8c64dSAndroid Build Coastguard Worker    def decode_hex(buf, offset, start, end):
223*90c8c64dSAndroid Build Coastguard Worker        """Read characters from buf[start:end] and interpret them as a
224*90c8c64dSAndroid Build Coastguard Worker        hexadecimal integer."""
225*90c8c64dSAndroid Build Coastguard Worker
226*90c8c64dSAndroid Build Coastguard Worker        if end > len(buf):
227*90c8c64dSAndroid Build Coastguard Worker            raise LexerError(buf, offset, 'bad hex escape sequence')
228*90c8c64dSAndroid Build Coastguard Worker        try:
229*90c8c64dSAndroid Build Coastguard Worker            return int(buf[start:end], 16)
230*90c8c64dSAndroid Build Coastguard Worker        except ValueError:
231*90c8c64dSAndroid Build Coastguard Worker            raise LexerError(buf, offset, 'bad hex escape sequence')
232*90c8c64dSAndroid Build Coastguard Worker
233*90c8c64dSAndroid Build Coastguard Worker
234*90c8c64dSAndroid Build Coastguard Worker    @classmethod
235*90c8c64dSAndroid Build Coastguard Worker    def lex_interpreted_string(cls, buf, offset):
236*90c8c64dSAndroid Build Coastguard Worker        """Tokenize a golang interpreted string.
237*90c8c64dSAndroid Build Coastguard Worker
238*90c8c64dSAndroid Build Coastguard Worker        Args:
239*90c8c64dSAndroid Build Coastguard Worker            buf (str)    The source code buffer.
240*90c8c64dSAndroid Build Coastguard Worker            offset (int) The position to find a golang interpreted string
241*90c8c64dSAndroid Build Coastguard Worker                         literal.
242*90c8c64dSAndroid Build Coastguard Worker
243*90c8c64dSAndroid Build Coastguard Worker        Returns:
244*90c8c64dSAndroid Build Coastguard Worker            A tuple with the end of matched buffer and the interpreted string
245*90c8c64dSAndroid Build Coastguard Worker            literal.
246*90c8c64dSAndroid Build Coastguard Worker        """
247*90c8c64dSAndroid Build Coastguard Worker
248*90c8c64dSAndroid Build Coastguard Worker        buf_len = len(buf)
249*90c8c64dSAndroid Build Coastguard Worker        pos = offset + 1
250*90c8c64dSAndroid Build Coastguard Worker        literal = ''
251*90c8c64dSAndroid Build Coastguard Worker        while pos < buf_len:
252*90c8c64dSAndroid Build Coastguard Worker            # Match unicode characters
253*90c8c64dSAndroid Build Coastguard Worker            match = cls.UNICODE_CHARS_PATTERN.match(buf, pos)
254*90c8c64dSAndroid Build Coastguard Worker            if match:
255*90c8c64dSAndroid Build Coastguard Worker                literal += match.group(0)
256*90c8c64dSAndroid Build Coastguard Worker                pos = match.end()
257*90c8c64dSAndroid Build Coastguard Worker            # Read the next character
258*90c8c64dSAndroid Build Coastguard Worker            try:
259*90c8c64dSAndroid Build Coastguard Worker                char = buf[pos]
260*90c8c64dSAndroid Build Coastguard Worker            except IndexError:
261*90c8c64dSAndroid Build Coastguard Worker                raise LexerError(buf, pos,
262*90c8c64dSAndroid Build Coastguard Worker                                 'unclosed interpreted string literal')
263*90c8c64dSAndroid Build Coastguard Worker            if char == '\\':
264*90c8c64dSAndroid Build Coastguard Worker                # Escape sequences
265*90c8c64dSAndroid Build Coastguard Worker                try:
266*90c8c64dSAndroid Build Coastguard Worker                    char = buf[pos + 1]
267*90c8c64dSAndroid Build Coastguard Worker                except IndexError:
268*90c8c64dSAndroid Build Coastguard Worker                    raise LexerError(buf, pos, 'bad escape sequence')
269*90c8c64dSAndroid Build Coastguard Worker                if char in cls.OCT_TABLE:
270*90c8c64dSAndroid Build Coastguard Worker                    literal += chr(cls.decode_oct(buf, pos, pos + 1, pos + 4))
271*90c8c64dSAndroid Build Coastguard Worker                    pos += 4
272*90c8c64dSAndroid Build Coastguard Worker                elif char == 'x':
273*90c8c64dSAndroid Build Coastguard Worker                    literal += chr(cls.decode_hex(buf, pos, pos + 2, pos + 4))
274*90c8c64dSAndroid Build Coastguard Worker                    pos += 4
275*90c8c64dSAndroid Build Coastguard Worker                elif char == 'u':
276*90c8c64dSAndroid Build Coastguard Worker                    literal += py3_chr(
277*90c8c64dSAndroid Build Coastguard Worker                        cls.decode_hex(buf, pos, pos + 2, pos + 6))
278*90c8c64dSAndroid Build Coastguard Worker                    pos += 6
279*90c8c64dSAndroid Build Coastguard Worker                elif char == 'U':
280*90c8c64dSAndroid Build Coastguard Worker                    literal += py3_chr(
281*90c8c64dSAndroid Build Coastguard Worker                        cls.decode_hex(buf, pos, pos + 2, pos + 10))
282*90c8c64dSAndroid Build Coastguard Worker                    pos += 10
283*90c8c64dSAndroid Build Coastguard Worker                else:
284*90c8c64dSAndroid Build Coastguard Worker                    try:
285*90c8c64dSAndroid Build Coastguard Worker                        literal += cls.ESCAPE_CHAR_TABLE[char]
286*90c8c64dSAndroid Build Coastguard Worker                        pos += 2
287*90c8c64dSAndroid Build Coastguard Worker                    except KeyError:
288*90c8c64dSAndroid Build Coastguard Worker                        raise LexerError(buf, pos, 'bad escape sequence')
289*90c8c64dSAndroid Build Coastguard Worker                continue
290*90c8c64dSAndroid Build Coastguard Worker            if char == '"':
291*90c8c64dSAndroid Build Coastguard Worker                # End of string literal
292*90c8c64dSAndroid Build Coastguard Worker                return (pos + 1, literal)
293*90c8c64dSAndroid Build Coastguard Worker            raise LexerError(buf, pos, 'unclosed interpreted string literal')
294*90c8c64dSAndroid Build Coastguard Worker
295*90c8c64dSAndroid Build Coastguard Worker
296*90c8c64dSAndroid Build Coastguard Worker    @classmethod
297*90c8c64dSAndroid Build Coastguard Worker    def lex_string(cls, buf, offset):
298*90c8c64dSAndroid Build Coastguard Worker        """Tokenize a golang string literal.
299*90c8c64dSAndroid Build Coastguard Worker
300*90c8c64dSAndroid Build Coastguard Worker        Args:
301*90c8c64dSAndroid Build Coastguard Worker            buf (str)    The source code buffer.
302*90c8c64dSAndroid Build Coastguard Worker            offset (int) The position to find a golang string literal.
303*90c8c64dSAndroid Build Coastguard Worker
304*90c8c64dSAndroid Build Coastguard Worker        Returns:
305*90c8c64dSAndroid Build Coastguard Worker            A tuple with the end of matched buffer and the interpreted string
306*90c8c64dSAndroid Build Coastguard Worker            literal.
307*90c8c64dSAndroid Build Coastguard Worker        """
308*90c8c64dSAndroid Build Coastguard Worker
309*90c8c64dSAndroid Build Coastguard Worker        char = buf[offset]
310*90c8c64dSAndroid Build Coastguard Worker        if char == '`':
311*90c8c64dSAndroid Build Coastguard Worker            try:
312*90c8c64dSAndroid Build Coastguard Worker                end = buf.index('`', offset + 1)
313*90c8c64dSAndroid Build Coastguard Worker                return (end + 1, buf[offset + 1 : end])
314*90c8c64dSAndroid Build Coastguard Worker            except ValueError:
315*90c8c64dSAndroid Build Coastguard Worker                raise LexerError(buf, len(buf), 'unclosed raw string literal')
316*90c8c64dSAndroid Build Coastguard Worker        if char == '"':
317*90c8c64dSAndroid Build Coastguard Worker            return cls.lex_interpreted_string(buf, offset)
318*90c8c64dSAndroid Build Coastguard Worker        raise LexerError(buf, offset, 'no string literal start character')
319*90c8c64dSAndroid Build Coastguard Worker
320*90c8c64dSAndroid Build Coastguard Worker
321*90c8c64dSAndroid Build Coastguard Worker    LEXER_PATTERNS = (
322*90c8c64dSAndroid Build Coastguard Worker        (Token.IDENT, '[A-Za-z_][0-9A-Za-z_]*'),
323*90c8c64dSAndroid Build Coastguard Worker        (Token.LPAREN, '\\('),
324*90c8c64dSAndroid Build Coastguard Worker        (Token.RPAREN, '\\)'),
325*90c8c64dSAndroid Build Coastguard Worker        (Token.LBRACKET, '\\['),
326*90c8c64dSAndroid Build Coastguard Worker        (Token.RBRACKET, '\\]'),
327*90c8c64dSAndroid Build Coastguard Worker        (Token.LBRACE, '\\{'),
328*90c8c64dSAndroid Build Coastguard Worker        (Token.RBRACE, '\\}'),
329*90c8c64dSAndroid Build Coastguard Worker        (Token.COLON, ':'),
330*90c8c64dSAndroid Build Coastguard Worker        (Token.ASSIGN, '='),
331*90c8c64dSAndroid Build Coastguard Worker        (Token.ASSIGNPLUS, '\\+='),
332*90c8c64dSAndroid Build Coastguard Worker        (Token.PLUS, '\\+'),
333*90c8c64dSAndroid Build Coastguard Worker        (Token.COMMA, ','),
334*90c8c64dSAndroid Build Coastguard Worker        (Token.STRING, '["`]'),
335*90c8c64dSAndroid Build Coastguard Worker        (Token.INTEGER, '-{0,1}[0-9]+'),
336*90c8c64dSAndroid Build Coastguard Worker
337*90c8c64dSAndroid Build Coastguard Worker        (Token.COMMENT,
338*90c8c64dSAndroid Build Coastguard Worker         '/(?:(?:/[^\\n]*)|(?:\\*(?:(?:[^*]*)|(?:\\*+[^/*]))*\\*+/))'),
339*90c8c64dSAndroid Build Coastguard Worker        (Token.SPACE, '\\s+'),
340*90c8c64dSAndroid Build Coastguard Worker    )
341*90c8c64dSAndroid Build Coastguard Worker
342*90c8c64dSAndroid Build Coastguard Worker
343*90c8c64dSAndroid Build Coastguard Worker    LEXER_MATCHER = re.compile('|'.join(
344*90c8c64dSAndroid Build Coastguard Worker        '(' + pattern + ')' for _, pattern in LEXER_PATTERNS))
345*90c8c64dSAndroid Build Coastguard Worker
346*90c8c64dSAndroid Build Coastguard Worker
347*90c8c64dSAndroid Build Coastguard Worker    @classmethod
348*90c8c64dSAndroid Build Coastguard Worker    def lex(cls, buf, offset):
349*90c8c64dSAndroid Build Coastguard Worker        """Tokenize a token from buf[offset].
350*90c8c64dSAndroid Build Coastguard Worker
351*90c8c64dSAndroid Build Coastguard Worker        Args:
352*90c8c64dSAndroid Build Coastguard Worker            buf (string) The source code buffer.
353*90c8c64dSAndroid Build Coastguard Worker            offset (int) The position to find and tokenize a token.
354*90c8c64dSAndroid Build Coastguard Worker
355*90c8c64dSAndroid Build Coastguard Worker        Return:
356*90c8c64dSAndroid Build Coastguard Worker            A tuple with three elements.  The first element is the token id.
357*90c8c64dSAndroid Build Coastguard Worker            The second element is the end of the token.  The third element is
358*90c8c64dSAndroid Build Coastguard Worker            the value for strings or identifiers.
359*90c8c64dSAndroid Build Coastguard Worker        """
360*90c8c64dSAndroid Build Coastguard Worker
361*90c8c64dSAndroid Build Coastguard Worker        match = cls.LEXER_MATCHER.match(buf, offset)
362*90c8c64dSAndroid Build Coastguard Worker        if not match:
363*90c8c64dSAndroid Build Coastguard Worker            raise LexerError(buf, offset, 'unknown token')
364*90c8c64dSAndroid Build Coastguard Worker        token = cls.LEXER_PATTERNS[match.lastindex - 1][0]
365*90c8c64dSAndroid Build Coastguard Worker
366*90c8c64dSAndroid Build Coastguard Worker        if token == Token.STRING:
367*90c8c64dSAndroid Build Coastguard Worker            end, literal = cls.lex_string(buf, offset)
368*90c8c64dSAndroid Build Coastguard Worker        else:
369*90c8c64dSAndroid Build Coastguard Worker            end = match.end()
370*90c8c64dSAndroid Build Coastguard Worker            if token in {Token.IDENT, Token.INTEGER}:
371*90c8c64dSAndroid Build Coastguard Worker                literal = buf[offset:end]
372*90c8c64dSAndroid Build Coastguard Worker            else:
373*90c8c64dSAndroid Build Coastguard Worker                literal = None
374*90c8c64dSAndroid Build Coastguard Worker
375*90c8c64dSAndroid Build Coastguard Worker        return (token, end, literal)
376*90c8c64dSAndroid Build Coastguard Worker
377*90c8c64dSAndroid Build Coastguard Worker
378*90c8c64dSAndroid Build Coastguard Worker#------------------------------------------------------------------------------
379*90c8c64dSAndroid Build Coastguard Worker# AST
380*90c8c64dSAndroid Build Coastguard Worker#------------------------------------------------------------------------------
381*90c8c64dSAndroid Build Coastguard Worker
382*90c8c64dSAndroid Build Coastguard Workerclass Expr(object):  # pylint: disable=too-few-public-methods
383*90c8c64dSAndroid Build Coastguard Worker    """Base class for all expressions."""
384*90c8c64dSAndroid Build Coastguard Worker
385*90c8c64dSAndroid Build Coastguard Worker    def eval(self, env):
386*90c8c64dSAndroid Build Coastguard Worker        """Evaluate the expression under an environment."""
387*90c8c64dSAndroid Build Coastguard Worker        raise NotImplementedError()
388*90c8c64dSAndroid Build Coastguard Worker
389*90c8c64dSAndroid Build Coastguard Worker
390*90c8c64dSAndroid Build Coastguard Workerclass String(Expr, str):
391*90c8c64dSAndroid Build Coastguard Worker    """String constant literal."""
392*90c8c64dSAndroid Build Coastguard Worker
393*90c8c64dSAndroid Build Coastguard Worker    def eval(self, env):
394*90c8c64dSAndroid Build Coastguard Worker        """Evaluate the string expression under an environment."""
395*90c8c64dSAndroid Build Coastguard Worker        return self
396*90c8c64dSAndroid Build Coastguard Worker
397*90c8c64dSAndroid Build Coastguard Worker
398*90c8c64dSAndroid Build Coastguard Workerclass Bool(Expr):  # pylint: disable=too-few-public-methods
399*90c8c64dSAndroid Build Coastguard Worker    """Boolean constant literal."""
400*90c8c64dSAndroid Build Coastguard Worker
401*90c8c64dSAndroid Build Coastguard Worker    __slots__ = ('value',)
402*90c8c64dSAndroid Build Coastguard Worker
403*90c8c64dSAndroid Build Coastguard Worker
404*90c8c64dSAndroid Build Coastguard Worker    def __init__(self, value):
405*90c8c64dSAndroid Build Coastguard Worker        """Create a boolean constant literal."""
406*90c8c64dSAndroid Build Coastguard Worker        self.value = value
407*90c8c64dSAndroid Build Coastguard Worker
408*90c8c64dSAndroid Build Coastguard Worker
409*90c8c64dSAndroid Build Coastguard Worker    def __repr__(self):
410*90c8c64dSAndroid Build Coastguard Worker        """Convert a boolean constant literal to string representation."""
411*90c8c64dSAndroid Build Coastguard Worker        return repr(self.value)
412*90c8c64dSAndroid Build Coastguard Worker
413*90c8c64dSAndroid Build Coastguard Worker
414*90c8c64dSAndroid Build Coastguard Worker    def __bool__(self):
415*90c8c64dSAndroid Build Coastguard Worker        """Convert boolean constant literal to Python bool type."""
416*90c8c64dSAndroid Build Coastguard Worker        return self.value
417*90c8c64dSAndroid Build Coastguard Worker
418*90c8c64dSAndroid Build Coastguard Worker    __nonzero__ = __bool__
419*90c8c64dSAndroid Build Coastguard Worker
420*90c8c64dSAndroid Build Coastguard Worker
421*90c8c64dSAndroid Build Coastguard Worker    def __eq__(self, rhs):
422*90c8c64dSAndroid Build Coastguard Worker        """Compare whether two instances are equal."""
423*90c8c64dSAndroid Build Coastguard Worker        return self.value == rhs.value
424*90c8c64dSAndroid Build Coastguard Worker
425*90c8c64dSAndroid Build Coastguard Worker
426*90c8c64dSAndroid Build Coastguard Worker    def __hash__(self):
427*90c8c64dSAndroid Build Coastguard Worker        """Compute the hashed value."""
428*90c8c64dSAndroid Build Coastguard Worker        return hash(self.value)
429*90c8c64dSAndroid Build Coastguard Worker
430*90c8c64dSAndroid Build Coastguard Worker
431*90c8c64dSAndroid Build Coastguard Worker    def eval(self, env):
432*90c8c64dSAndroid Build Coastguard Worker        """Evaluate the boolean expression under an environment."""
433*90c8c64dSAndroid Build Coastguard Worker        return self
434*90c8c64dSAndroid Build Coastguard Worker
435*90c8c64dSAndroid Build Coastguard Worker
436*90c8c64dSAndroid Build Coastguard Workerclass Integer(Expr):  # pylint: disable=too-few-public-methods
437*90c8c64dSAndroid Build Coastguard Worker    """Integer constant literal."""
438*90c8c64dSAndroid Build Coastguard Worker
439*90c8c64dSAndroid Build Coastguard Worker    __slots__ = ('value',)
440*90c8c64dSAndroid Build Coastguard Worker
441*90c8c64dSAndroid Build Coastguard Worker
442*90c8c64dSAndroid Build Coastguard Worker    def __init__(self, value):
443*90c8c64dSAndroid Build Coastguard Worker        """Create an integer constant literal."""
444*90c8c64dSAndroid Build Coastguard Worker        self.value = value
445*90c8c64dSAndroid Build Coastguard Worker
446*90c8c64dSAndroid Build Coastguard Worker
447*90c8c64dSAndroid Build Coastguard Worker    def __repr__(self):
448*90c8c64dSAndroid Build Coastguard Worker        """Convert an integer constant literal to string representation."""
449*90c8c64dSAndroid Build Coastguard Worker        return repr(self.value)
450*90c8c64dSAndroid Build Coastguard Worker
451*90c8c64dSAndroid Build Coastguard Worker
452*90c8c64dSAndroid Build Coastguard Worker    def __bool__(self):
453*90c8c64dSAndroid Build Coastguard Worker        """Convert an integer constant literal to Python bool type."""
454*90c8c64dSAndroid Build Coastguard Worker        return bool(self.value)
455*90c8c64dSAndroid Build Coastguard Worker
456*90c8c64dSAndroid Build Coastguard Worker    __nonzero__ = __bool__
457*90c8c64dSAndroid Build Coastguard Worker
458*90c8c64dSAndroid Build Coastguard Worker
459*90c8c64dSAndroid Build Coastguard Worker    def __int__(self):
460*90c8c64dSAndroid Build Coastguard Worker        """Convert an integer constant literal to Python int type."""
461*90c8c64dSAndroid Build Coastguard Worker        return self.value
462*90c8c64dSAndroid Build Coastguard Worker
463*90c8c64dSAndroid Build Coastguard Worker
464*90c8c64dSAndroid Build Coastguard Worker    def __eq__(self, rhs):
465*90c8c64dSAndroid Build Coastguard Worker        """Compare whether two instances are equal."""
466*90c8c64dSAndroid Build Coastguard Worker        return self.value == rhs.value
467*90c8c64dSAndroid Build Coastguard Worker
468*90c8c64dSAndroid Build Coastguard Worker
469*90c8c64dSAndroid Build Coastguard Worker    def __hash__(self):
470*90c8c64dSAndroid Build Coastguard Worker        """Compute the hashed value."""
471*90c8c64dSAndroid Build Coastguard Worker        return hash(self.value)
472*90c8c64dSAndroid Build Coastguard Worker
473*90c8c64dSAndroid Build Coastguard Worker
474*90c8c64dSAndroid Build Coastguard Worker    def eval(self, env):
475*90c8c64dSAndroid Build Coastguard Worker        """Evaluate the integer expression under an environment."""
476*90c8c64dSAndroid Build Coastguard Worker        return self
477*90c8c64dSAndroid Build Coastguard Worker
478*90c8c64dSAndroid Build Coastguard Worker
479*90c8c64dSAndroid Build Coastguard Workerclass VarRef(Expr):  # pylint: disable=too-few-public-methods
480*90c8c64dSAndroid Build Coastguard Worker    """A reference to a variable."""
481*90c8c64dSAndroid Build Coastguard Worker
482*90c8c64dSAndroid Build Coastguard Worker    def __init__(self, name, value):
483*90c8c64dSAndroid Build Coastguard Worker        """Create a variable reference with a name and the value under static
484*90c8c64dSAndroid Build Coastguard Worker        scoping."""
485*90c8c64dSAndroid Build Coastguard Worker        self.name = name
486*90c8c64dSAndroid Build Coastguard Worker        self.value = value
487*90c8c64dSAndroid Build Coastguard Worker
488*90c8c64dSAndroid Build Coastguard Worker
489*90c8c64dSAndroid Build Coastguard Worker    def __repr__(self):
490*90c8c64dSAndroid Build Coastguard Worker        """Convert a variable reference to string representation."""
491*90c8c64dSAndroid Build Coastguard Worker        return self.name
492*90c8c64dSAndroid Build Coastguard Worker
493*90c8c64dSAndroid Build Coastguard Worker
494*90c8c64dSAndroid Build Coastguard Worker    def eval(self, env):
495*90c8c64dSAndroid Build Coastguard Worker        """Evaluate the identifier under an environment."""
496*90c8c64dSAndroid Build Coastguard Worker        if self.value is None:
497*90c8c64dSAndroid Build Coastguard Worker            return env[self.name].eval(env)
498*90c8c64dSAndroid Build Coastguard Worker        return self.value.eval(env)
499*90c8c64dSAndroid Build Coastguard Worker
500*90c8c64dSAndroid Build Coastguard Worker
501*90c8c64dSAndroid Build Coastguard Workerclass List(Expr, list):
502*90c8c64dSAndroid Build Coastguard Worker    """List expression."""
503*90c8c64dSAndroid Build Coastguard Worker
504*90c8c64dSAndroid Build Coastguard Worker    def eval(self, env):
505*90c8c64dSAndroid Build Coastguard Worker        """Evaluate list elements under an environment."""
506*90c8c64dSAndroid Build Coastguard Worker        return List(item.eval(env) for item in self)
507*90c8c64dSAndroid Build Coastguard Worker
508*90c8c64dSAndroid Build Coastguard Worker
509*90c8c64dSAndroid Build Coastguard Workerclass Dict(Expr, collections.OrderedDict):
510*90c8c64dSAndroid Build Coastguard Worker    """Dictionary expression."""
511*90c8c64dSAndroid Build Coastguard Worker
512*90c8c64dSAndroid Build Coastguard Worker    def __repr__(self):
513*90c8c64dSAndroid Build Coastguard Worker        attrs = ', '.join(key + ': ' + repr(value)
514*90c8c64dSAndroid Build Coastguard Worker                          for key, value in self.items())
515*90c8c64dSAndroid Build Coastguard Worker        return '{' + attrs + '}'
516*90c8c64dSAndroid Build Coastguard Worker
517*90c8c64dSAndroid Build Coastguard Worker
518*90c8c64dSAndroid Build Coastguard Worker    def eval(self, env):
519*90c8c64dSAndroid Build Coastguard Worker        """Evaluate dictionary values under an environment."""
520*90c8c64dSAndroid Build Coastguard Worker        return Dict((key, value.eval(env)) for key, value in self.items())
521*90c8c64dSAndroid Build Coastguard Worker
522*90c8c64dSAndroid Build Coastguard Worker
523*90c8c64dSAndroid Build Coastguard Workerclass Concat(Expr):  # pylint: disable=too-few-public-methods
524*90c8c64dSAndroid Build Coastguard Worker    """List/string/integer plus operator."""
525*90c8c64dSAndroid Build Coastguard Worker
526*90c8c64dSAndroid Build Coastguard Worker    __slots__ = ('lhs', 'rhs')
527*90c8c64dSAndroid Build Coastguard Worker
528*90c8c64dSAndroid Build Coastguard Worker
529*90c8c64dSAndroid Build Coastguard Worker    def __init__(self, lhs, rhs):
530*90c8c64dSAndroid Build Coastguard Worker        """Create a list/string/integer plus expression."""
531*90c8c64dSAndroid Build Coastguard Worker        self.lhs = lhs
532*90c8c64dSAndroid Build Coastguard Worker        self.rhs = rhs
533*90c8c64dSAndroid Build Coastguard Worker
534*90c8c64dSAndroid Build Coastguard Worker
535*90c8c64dSAndroid Build Coastguard Worker    def __repr__(self):
536*90c8c64dSAndroid Build Coastguard Worker        return '(' + repr(self.lhs) + ' + ' + repr(self.rhs) + ')'
537*90c8c64dSAndroid Build Coastguard Worker
538*90c8c64dSAndroid Build Coastguard Worker
539*90c8c64dSAndroid Build Coastguard Worker    def eval(self, env):
540*90c8c64dSAndroid Build Coastguard Worker        """Evaluate list/string/integer plus operator under an environment."""
541*90c8c64dSAndroid Build Coastguard Worker        lhs = self.lhs.eval(env)
542*90c8c64dSAndroid Build Coastguard Worker        rhs = self.rhs.eval(env)
543*90c8c64dSAndroid Build Coastguard Worker        if isinstance(lhs, List) and isinstance(rhs, List):
544*90c8c64dSAndroid Build Coastguard Worker            return List(itertools.chain(lhs, rhs))
545*90c8c64dSAndroid Build Coastguard Worker        if isinstance(lhs, String) and isinstance(rhs, String):
546*90c8c64dSAndroid Build Coastguard Worker            return String(lhs + rhs)
547*90c8c64dSAndroid Build Coastguard Worker        if isinstance(lhs, Integer) and isinstance(rhs, Integer):
548*90c8c64dSAndroid Build Coastguard Worker            return Integer(int(lhs) + int(rhs))
549*90c8c64dSAndroid Build Coastguard Worker        raise TypeError('bad plus operands')
550*90c8c64dSAndroid Build Coastguard Worker
551*90c8c64dSAndroid Build Coastguard Worker
552*90c8c64dSAndroid Build Coastguard Worker#------------------------------------------------------------------------------
553*90c8c64dSAndroid Build Coastguard Worker# Parser
554*90c8c64dSAndroid Build Coastguard Worker#------------------------------------------------------------------------------
555*90c8c64dSAndroid Build Coastguard Worker
556*90c8c64dSAndroid Build Coastguard Workerclass ParseError(ValueError):
557*90c8c64dSAndroid Build Coastguard Worker    """Parser error exception class."""
558*90c8c64dSAndroid Build Coastguard Worker
559*90c8c64dSAndroid Build Coastguard Worker    def __init__(self, lexer, message):
560*90c8c64dSAndroid Build Coastguard Worker        """Create a parser error exception object."""
561*90c8c64dSAndroid Build Coastguard Worker        super(ParseError, self).__init__(message)
562*90c8c64dSAndroid Build Coastguard Worker        self.message = message
563*90c8c64dSAndroid Build Coastguard Worker        self.line, self.column = \
564*90c8c64dSAndroid Build Coastguard Worker            Lexer.compute_line_column(lexer.buf, lexer.start)
565*90c8c64dSAndroid Build Coastguard Worker
566*90c8c64dSAndroid Build Coastguard Worker
567*90c8c64dSAndroid Build Coastguard Worker    def __str__(self):
568*90c8c64dSAndroid Build Coastguard Worker        """Convert parser error to string representation."""
569*90c8c64dSAndroid Build Coastguard Worker        return 'ParseError: {}:{}: {}'.format(
570*90c8c64dSAndroid Build Coastguard Worker            self.line, self.column, self.message)
571*90c8c64dSAndroid Build Coastguard Worker
572*90c8c64dSAndroid Build Coastguard Worker
573*90c8c64dSAndroid Build Coastguard Workerclass Parser(object):
574*90c8c64dSAndroid Build Coastguard Worker    """Parser to parse Android.bp files."""
575*90c8c64dSAndroid Build Coastguard Worker
576*90c8c64dSAndroid Build Coastguard Worker    def __init__(self, lexer, inherited_env=None):
577*90c8c64dSAndroid Build Coastguard Worker        """Initialize the parser with the lexer."""
578*90c8c64dSAndroid Build Coastguard Worker        self.lexer = lexer
579*90c8c64dSAndroid Build Coastguard Worker
580*90c8c64dSAndroid Build Coastguard Worker        self.var_defs = []
581*90c8c64dSAndroid Build Coastguard Worker        self.vars = {} if inherited_env is None else dict(inherited_env)
582*90c8c64dSAndroid Build Coastguard Worker        self.modules = []
583*90c8c64dSAndroid Build Coastguard Worker
584*90c8c64dSAndroid Build Coastguard Worker
585*90c8c64dSAndroid Build Coastguard Worker    def parse(self):
586*90c8c64dSAndroid Build Coastguard Worker        """Parse AST from tokens."""
587*90c8c64dSAndroid Build Coastguard Worker        lexer = self.lexer
588*90c8c64dSAndroid Build Coastguard Worker        while lexer.token != Token.EOF:
589*90c8c64dSAndroid Build Coastguard Worker            if lexer.token == Token.IDENT:
590*90c8c64dSAndroid Build Coastguard Worker                ident = self.parse_ident_lvalue()
591*90c8c64dSAndroid Build Coastguard Worker                if lexer.token in {Token.ASSIGN, Token.ASSIGNPLUS}:
592*90c8c64dSAndroid Build Coastguard Worker                    self.parse_assign(ident, lexer.token)
593*90c8c64dSAndroid Build Coastguard Worker                elif lexer.token in {Token.LBRACE, Token.LPAREN}:
594*90c8c64dSAndroid Build Coastguard Worker                    self.parse_module_definition(ident)
595*90c8c64dSAndroid Build Coastguard Worker                else:
596*90c8c64dSAndroid Build Coastguard Worker                    raise ParseError(lexer,
597*90c8c64dSAndroid Build Coastguard Worker                                     'unexpected token ' + lexer.token.name)
598*90c8c64dSAndroid Build Coastguard Worker            else:
599*90c8c64dSAndroid Build Coastguard Worker                raise ParseError(lexer, 'unexpected token ' + lexer.token.name)
600*90c8c64dSAndroid Build Coastguard Worker        lexer.consume(Token.EOF)
601*90c8c64dSAndroid Build Coastguard Worker
602*90c8c64dSAndroid Build Coastguard Worker
603*90c8c64dSAndroid Build Coastguard Worker    def create_var_ref(self, name):
604*90c8c64dSAndroid Build Coastguard Worker        """Create a variable reference."""
605*90c8c64dSAndroid Build Coastguard Worker        return VarRef(name, self.vars.get(name))
606*90c8c64dSAndroid Build Coastguard Worker
607*90c8c64dSAndroid Build Coastguard Worker
608*90c8c64dSAndroid Build Coastguard Worker    def define_var(self, name, value):
609*90c8c64dSAndroid Build Coastguard Worker        """Define a variable."""
610*90c8c64dSAndroid Build Coastguard Worker        self.var_defs.append((name, value))
611*90c8c64dSAndroid Build Coastguard Worker        self.vars[name] = value
612*90c8c64dSAndroid Build Coastguard Worker
613*90c8c64dSAndroid Build Coastguard Worker
614*90c8c64dSAndroid Build Coastguard Worker    def parse_assign(self, ident, assign_token):
615*90c8c64dSAndroid Build Coastguard Worker        """Parse an assignment statement."""
616*90c8c64dSAndroid Build Coastguard Worker        lexer = self.lexer
617*90c8c64dSAndroid Build Coastguard Worker        lexer.consume(assign_token)
618*90c8c64dSAndroid Build Coastguard Worker        value = self.parse_expression()
619*90c8c64dSAndroid Build Coastguard Worker        if assign_token == Token.ASSIGNPLUS:
620*90c8c64dSAndroid Build Coastguard Worker            value = Concat(self.create_var_ref(ident), value)
621*90c8c64dSAndroid Build Coastguard Worker        self.define_var(ident, value)
622*90c8c64dSAndroid Build Coastguard Worker
623*90c8c64dSAndroid Build Coastguard Worker
624*90c8c64dSAndroid Build Coastguard Worker    def parse_module_definition(self, module_ident):
625*90c8c64dSAndroid Build Coastguard Worker        """Parse a module definition."""
626*90c8c64dSAndroid Build Coastguard Worker        properties = self.parse_dict()
627*90c8c64dSAndroid Build Coastguard Worker        properties['_path'] = String(self.lexer.path)
628*90c8c64dSAndroid Build Coastguard Worker        self.modules.append((module_ident, properties))
629*90c8c64dSAndroid Build Coastguard Worker
630*90c8c64dSAndroid Build Coastguard Worker
631*90c8c64dSAndroid Build Coastguard Worker    def parse_ident_lvalue(self):
632*90c8c64dSAndroid Build Coastguard Worker        """Parse an identifier as an l-value."""
633*90c8c64dSAndroid Build Coastguard Worker        ident = self.lexer.literal
634*90c8c64dSAndroid Build Coastguard Worker        self.lexer.consume(Token.IDENT)
635*90c8c64dSAndroid Build Coastguard Worker        return ident
636*90c8c64dSAndroid Build Coastguard Worker
637*90c8c64dSAndroid Build Coastguard Worker
638*90c8c64dSAndroid Build Coastguard Worker    def parse_ident_rvalue(self):
639*90c8c64dSAndroid Build Coastguard Worker        """Parse an identifier as a r-value.
640*90c8c64dSAndroid Build Coastguard Worker
641*90c8c64dSAndroid Build Coastguard Worker        Returns:
642*90c8c64dSAndroid Build Coastguard Worker            Returns VarRef if the literal is not 'true' nor 'false'.
643*90c8c64dSAndroid Build Coastguard Worker
644*90c8c64dSAndroid Build Coastguard Worker            Returns Bool(true/false) if the literal is either 'true' or 'false'.
645*90c8c64dSAndroid Build Coastguard Worker        """
646*90c8c64dSAndroid Build Coastguard Worker        lexer = self.lexer
647*90c8c64dSAndroid Build Coastguard Worker        if lexer.literal in {'true', 'false'}:
648*90c8c64dSAndroid Build Coastguard Worker            result = Bool(lexer.literal == 'true')
649*90c8c64dSAndroid Build Coastguard Worker        else:
650*90c8c64dSAndroid Build Coastguard Worker            result = self.create_var_ref(lexer.literal)
651*90c8c64dSAndroid Build Coastguard Worker        lexer.consume(Token.IDENT)
652*90c8c64dSAndroid Build Coastguard Worker        return result
653*90c8c64dSAndroid Build Coastguard Worker
654*90c8c64dSAndroid Build Coastguard Worker
655*90c8c64dSAndroid Build Coastguard Worker    def parse_string(self):
656*90c8c64dSAndroid Build Coastguard Worker        """Parse a string."""
657*90c8c64dSAndroid Build Coastguard Worker        lexer = self.lexer
658*90c8c64dSAndroid Build Coastguard Worker        string = String(lexer.literal)
659*90c8c64dSAndroid Build Coastguard Worker        lexer.consume(Token.STRING)
660*90c8c64dSAndroid Build Coastguard Worker        return string
661*90c8c64dSAndroid Build Coastguard Worker
662*90c8c64dSAndroid Build Coastguard Worker
663*90c8c64dSAndroid Build Coastguard Worker    def parse_integer(self):
664*90c8c64dSAndroid Build Coastguard Worker        """Parse an integer."""
665*90c8c64dSAndroid Build Coastguard Worker        lexer = self.lexer
666*90c8c64dSAndroid Build Coastguard Worker        integer = Integer(int(lexer.literal))
667*90c8c64dSAndroid Build Coastguard Worker        lexer.consume(Token.INTEGER)
668*90c8c64dSAndroid Build Coastguard Worker        return integer
669*90c8c64dSAndroid Build Coastguard Worker
670*90c8c64dSAndroid Build Coastguard Worker
671*90c8c64dSAndroid Build Coastguard Worker    def parse_operand(self):
672*90c8c64dSAndroid Build Coastguard Worker        """Parse an operand."""
673*90c8c64dSAndroid Build Coastguard Worker        lexer = self.lexer
674*90c8c64dSAndroid Build Coastguard Worker        token = lexer.token
675*90c8c64dSAndroid Build Coastguard Worker        if token == Token.STRING:
676*90c8c64dSAndroid Build Coastguard Worker            return self.parse_string()
677*90c8c64dSAndroid Build Coastguard Worker        if token == Token.IDENT:
678*90c8c64dSAndroid Build Coastguard Worker            return self.parse_ident_rvalue()
679*90c8c64dSAndroid Build Coastguard Worker        if token == Token.INTEGER:
680*90c8c64dSAndroid Build Coastguard Worker            return self.parse_integer()
681*90c8c64dSAndroid Build Coastguard Worker        if token == Token.LBRACKET:
682*90c8c64dSAndroid Build Coastguard Worker            return self.parse_list()
683*90c8c64dSAndroid Build Coastguard Worker        if token == Token.LBRACE:
684*90c8c64dSAndroid Build Coastguard Worker            return self.parse_dict()
685*90c8c64dSAndroid Build Coastguard Worker        if token == Token.LPAREN:
686*90c8c64dSAndroid Build Coastguard Worker            lexer.consume(Token.LPAREN)
687*90c8c64dSAndroid Build Coastguard Worker            operand = self.parse_expression()
688*90c8c64dSAndroid Build Coastguard Worker            lexer.consume(Token.RPAREN)
689*90c8c64dSAndroid Build Coastguard Worker            return operand
690*90c8c64dSAndroid Build Coastguard Worker        raise ParseError(lexer, 'unexpected token ' + token.name)
691*90c8c64dSAndroid Build Coastguard Worker
692*90c8c64dSAndroid Build Coastguard Worker
693*90c8c64dSAndroid Build Coastguard Worker    def parse_expression(self):
694*90c8c64dSAndroid Build Coastguard Worker        """Parse an expression."""
695*90c8c64dSAndroid Build Coastguard Worker        lexer = self.lexer
696*90c8c64dSAndroid Build Coastguard Worker        expr = self.parse_operand()
697*90c8c64dSAndroid Build Coastguard Worker        while lexer.token == Token.PLUS:
698*90c8c64dSAndroid Build Coastguard Worker            lexer.consume(Token.PLUS)
699*90c8c64dSAndroid Build Coastguard Worker            expr = Concat(expr, self.parse_operand())
700*90c8c64dSAndroid Build Coastguard Worker        return expr
701*90c8c64dSAndroid Build Coastguard Worker
702*90c8c64dSAndroid Build Coastguard Worker
703*90c8c64dSAndroid Build Coastguard Worker    def parse_list(self):
704*90c8c64dSAndroid Build Coastguard Worker        """Parse a list."""
705*90c8c64dSAndroid Build Coastguard Worker        result = List()
706*90c8c64dSAndroid Build Coastguard Worker        lexer = self.lexer
707*90c8c64dSAndroid Build Coastguard Worker        lexer.consume(Token.LBRACKET)
708*90c8c64dSAndroid Build Coastguard Worker        while lexer.token != Token.RBRACKET:
709*90c8c64dSAndroid Build Coastguard Worker            result.append(self.parse_expression())
710*90c8c64dSAndroid Build Coastguard Worker            if lexer.token == Token.COMMA:
711*90c8c64dSAndroid Build Coastguard Worker                lexer.consume(Token.COMMA)
712*90c8c64dSAndroid Build Coastguard Worker        lexer.consume(Token.RBRACKET)
713*90c8c64dSAndroid Build Coastguard Worker        return result
714*90c8c64dSAndroid Build Coastguard Worker
715*90c8c64dSAndroid Build Coastguard Worker
716*90c8c64dSAndroid Build Coastguard Worker    def parse_dict(self):
717*90c8c64dSAndroid Build Coastguard Worker        """Parse a dict."""
718*90c8c64dSAndroid Build Coastguard Worker        result = Dict()
719*90c8c64dSAndroid Build Coastguard Worker        lexer = self.lexer
720*90c8c64dSAndroid Build Coastguard Worker
721*90c8c64dSAndroid Build Coastguard Worker        is_func_syntax = lexer.token == Token.LPAREN
722*90c8c64dSAndroid Build Coastguard Worker        if is_func_syntax:
723*90c8c64dSAndroid Build Coastguard Worker            lexer.consume(Token.LPAREN)
724*90c8c64dSAndroid Build Coastguard Worker        else:
725*90c8c64dSAndroid Build Coastguard Worker            lexer.consume(Token.LBRACE)
726*90c8c64dSAndroid Build Coastguard Worker
727*90c8c64dSAndroid Build Coastguard Worker        while lexer.token != Token.RBRACE and lexer.token != Token.RPAREN:
728*90c8c64dSAndroid Build Coastguard Worker            if lexer.token != Token.IDENT:
729*90c8c64dSAndroid Build Coastguard Worker                raise ParseError(lexer, 'unexpected token ' + lexer.token.name)
730*90c8c64dSAndroid Build Coastguard Worker            key = self.parse_ident_lvalue()
731*90c8c64dSAndroid Build Coastguard Worker
732*90c8c64dSAndroid Build Coastguard Worker            if lexer.token == Token.ASSIGN:
733*90c8c64dSAndroid Build Coastguard Worker                lexer.consume(Token.ASSIGN)
734*90c8c64dSAndroid Build Coastguard Worker            else:
735*90c8c64dSAndroid Build Coastguard Worker                lexer.consume(Token.COLON)
736*90c8c64dSAndroid Build Coastguard Worker
737*90c8c64dSAndroid Build Coastguard Worker            value = self.parse_expression()
738*90c8c64dSAndroid Build Coastguard Worker            result[key] = value
739*90c8c64dSAndroid Build Coastguard Worker
740*90c8c64dSAndroid Build Coastguard Worker            if lexer.token == Token.COMMA:
741*90c8c64dSAndroid Build Coastguard Worker                lexer.consume(Token.COMMA)
742*90c8c64dSAndroid Build Coastguard Worker
743*90c8c64dSAndroid Build Coastguard Worker        if is_func_syntax:
744*90c8c64dSAndroid Build Coastguard Worker            lexer.consume(Token.RPAREN)
745*90c8c64dSAndroid Build Coastguard Worker        else:
746*90c8c64dSAndroid Build Coastguard Worker            lexer.consume(Token.RBRACE)
747*90c8c64dSAndroid Build Coastguard Worker
748*90c8c64dSAndroid Build Coastguard Worker        return result
749*90c8c64dSAndroid Build Coastguard Worker
750*90c8c64dSAndroid Build Coastguard Worker
751*90c8c64dSAndroid Build Coastguard Workerclass RecursiveParser(object):
752*90c8c64dSAndroid Build Coastguard Worker    """This is a recursive parser which will parse blueprint files
753*90c8c64dSAndroid Build Coastguard Worker    recursively."""
754*90c8c64dSAndroid Build Coastguard Worker
755*90c8c64dSAndroid Build Coastguard Worker
756*90c8c64dSAndroid Build Coastguard Worker    # Default Blueprint file name
757*90c8c64dSAndroid Build Coastguard Worker    _DEFAULT_SUB_NAME = 'Android.bp'
758*90c8c64dSAndroid Build Coastguard Worker
759*90c8c64dSAndroid Build Coastguard Worker
760*90c8c64dSAndroid Build Coastguard Worker    def __init__(self):
761*90c8c64dSAndroid Build Coastguard Worker        """Initialize a recursive parser."""
762*90c8c64dSAndroid Build Coastguard Worker        self.visited = set()
763*90c8c64dSAndroid Build Coastguard Worker        self.modules = []
764*90c8c64dSAndroid Build Coastguard Worker
765*90c8c64dSAndroid Build Coastguard Worker
766*90c8c64dSAndroid Build Coastguard Worker    @staticmethod
767*90c8c64dSAndroid Build Coastguard Worker    def glob_sub_files(pattern, sub_file_name):
768*90c8c64dSAndroid Build Coastguard Worker        """List the sub file paths that match with the pattern with
769*90c8c64dSAndroid Build Coastguard Worker        wildcards."""
770*90c8c64dSAndroid Build Coastguard Worker
771*90c8c64dSAndroid Build Coastguard Worker        for path in glob.glob(pattern):
772*90c8c64dSAndroid Build Coastguard Worker            if os.path.isfile(path):
773*90c8c64dSAndroid Build Coastguard Worker                if os.path.basename(path) == sub_file_name:
774*90c8c64dSAndroid Build Coastguard Worker                    yield path
775*90c8c64dSAndroid Build Coastguard Worker            else:
776*90c8c64dSAndroid Build Coastguard Worker                sub_file_path = os.path.join(path, sub_file_name)
777*90c8c64dSAndroid Build Coastguard Worker                if os.path.isfile(sub_file_path):
778*90c8c64dSAndroid Build Coastguard Worker                    yield sub_file_path
779*90c8c64dSAndroid Build Coastguard Worker
780*90c8c64dSAndroid Build Coastguard Worker
781*90c8c64dSAndroid Build Coastguard Worker    @classmethod
782*90c8c64dSAndroid Build Coastguard Worker    def find_sub_files_from_env(cls, rootdir, env, use_subdirs,
783*90c8c64dSAndroid Build Coastguard Worker                                default_sub_name=_DEFAULT_SUB_NAME):
784*90c8c64dSAndroid Build Coastguard Worker        """Find the sub files from the names specified in build, subdirs, and
785*90c8c64dSAndroid Build Coastguard Worker        optional_subdirs."""
786*90c8c64dSAndroid Build Coastguard Worker
787*90c8c64dSAndroid Build Coastguard Worker        subs = []
788*90c8c64dSAndroid Build Coastguard Worker
789*90c8c64dSAndroid Build Coastguard Worker        if 'build' in env:
790*90c8c64dSAndroid Build Coastguard Worker            subs.extend(os.path.join(rootdir, filename)
791*90c8c64dSAndroid Build Coastguard Worker                        for filename in env['build'].eval(env))
792*90c8c64dSAndroid Build Coastguard Worker        if use_subdirs:
793*90c8c64dSAndroid Build Coastguard Worker            sub_name = env['subname'] if 'subname' in env else default_sub_name
794*90c8c64dSAndroid Build Coastguard Worker
795*90c8c64dSAndroid Build Coastguard Worker            if 'subdirs' in env:
796*90c8c64dSAndroid Build Coastguard Worker                for path in env['subdirs'].eval(env):
797*90c8c64dSAndroid Build Coastguard Worker                    subs.extend(cls.glob_sub_files(os.path.join(rootdir, path),
798*90c8c64dSAndroid Build Coastguard Worker                                                   sub_name))
799*90c8c64dSAndroid Build Coastguard Worker            if 'optional_subdirs' in env:
800*90c8c64dSAndroid Build Coastguard Worker                for path in env['optional_subdirs'].eval(env):
801*90c8c64dSAndroid Build Coastguard Worker                    subs.extend(cls.glob_sub_files(os.path.join(rootdir, path),
802*90c8c64dSAndroid Build Coastguard Worker                                                   sub_name))
803*90c8c64dSAndroid Build Coastguard Worker        return subs
804*90c8c64dSAndroid Build Coastguard Worker
805*90c8c64dSAndroid Build Coastguard Worker
806*90c8c64dSAndroid Build Coastguard Worker    @staticmethod
807*90c8c64dSAndroid Build Coastguard Worker    def _read_file(path, env):
808*90c8c64dSAndroid Build Coastguard Worker        """Read a blueprint file and return modules and the environment."""
809*90c8c64dSAndroid Build Coastguard Worker        with open(path, 'r') as bp_file:
810*90c8c64dSAndroid Build Coastguard Worker            content = bp_file.read()
811*90c8c64dSAndroid Build Coastguard Worker        parser = Parser(Lexer(content, path=path), env)
812*90c8c64dSAndroid Build Coastguard Worker        parser.parse()
813*90c8c64dSAndroid Build Coastguard Worker        return (parser.modules, parser.vars)
814*90c8c64dSAndroid Build Coastguard Worker
815*90c8c64dSAndroid Build Coastguard Worker
816*90c8c64dSAndroid Build Coastguard Worker    def _parse_file(self, path, env, evaluate):
817*90c8c64dSAndroid Build Coastguard Worker        """Parse a blueprint file and append to self.modules."""
818*90c8c64dSAndroid Build Coastguard Worker        modules, sub_env = self._read_file(path, env)
819*90c8c64dSAndroid Build Coastguard Worker        if evaluate:
820*90c8c64dSAndroid Build Coastguard Worker            modules = [(ident, attrs.eval(env)) for ident, attrs in modules]
821*90c8c64dSAndroid Build Coastguard Worker        self.modules += modules
822*90c8c64dSAndroid Build Coastguard Worker        return sub_env
823*90c8c64dSAndroid Build Coastguard Worker
824*90c8c64dSAndroid Build Coastguard Worker
825*90c8c64dSAndroid Build Coastguard Worker    def _parse_file_recursive(self, path, env, evaluate, use_subdirs):
826*90c8c64dSAndroid Build Coastguard Worker        """Parse a blueprint file and recursively."""
827*90c8c64dSAndroid Build Coastguard Worker
828*90c8c64dSAndroid Build Coastguard Worker        self.visited.add(path)
829*90c8c64dSAndroid Build Coastguard Worker
830*90c8c64dSAndroid Build Coastguard Worker        sub_env = self._parse_file(path, env, evaluate)
831*90c8c64dSAndroid Build Coastguard Worker
832*90c8c64dSAndroid Build Coastguard Worker        rootdir = os.path.dirname(path)
833*90c8c64dSAndroid Build Coastguard Worker
834*90c8c64dSAndroid Build Coastguard Worker        sub_file_paths = self.find_sub_files_from_env(rootdir, sub_env,
835*90c8c64dSAndroid Build Coastguard Worker                                                      use_subdirs)
836*90c8c64dSAndroid Build Coastguard Worker
837*90c8c64dSAndroid Build Coastguard Worker        sub_env.pop('build', None)
838*90c8c64dSAndroid Build Coastguard Worker        sub_env.pop('subdirs', None)
839*90c8c64dSAndroid Build Coastguard Worker        sub_env.pop('optional_subdirs', None)
840*90c8c64dSAndroid Build Coastguard Worker
841*90c8c64dSAndroid Build Coastguard Worker        for sub_file_path in sub_file_paths:
842*90c8c64dSAndroid Build Coastguard Worker            if sub_file_path not in self.visited:
843*90c8c64dSAndroid Build Coastguard Worker                self._parse_file_recursive(sub_file_path, sub_env, evaluate,
844*90c8c64dSAndroid Build Coastguard Worker                                           use_subdirs)
845*90c8c64dSAndroid Build Coastguard Worker        return sub_env
846*90c8c64dSAndroid Build Coastguard Worker
847*90c8c64dSAndroid Build Coastguard Worker
848*90c8c64dSAndroid Build Coastguard Worker    def _scan_and_parse_all_file_recursive(self, filename, path, env, evaluate):
849*90c8c64dSAndroid Build Coastguard Worker        """Scan all files with the specified name and parse them."""
850*90c8c64dSAndroid Build Coastguard Worker
851*90c8c64dSAndroid Build Coastguard Worker        rootdir = os.path.dirname(path)
852*90c8c64dSAndroid Build Coastguard Worker        assert rootdir, 'rootdir is empty but must be non-empty'
853*90c8c64dSAndroid Build Coastguard Worker
854*90c8c64dSAndroid Build Coastguard Worker        envs = [(rootdir, env)]
855*90c8c64dSAndroid Build Coastguard Worker        assert env is not None
856*90c8c64dSAndroid Build Coastguard Worker
857*90c8c64dSAndroid Build Coastguard Worker        # Scan directories for all blueprint files
858*90c8c64dSAndroid Build Coastguard Worker        for basedir, dirnames, filenames in os.walk(rootdir):
859*90c8c64dSAndroid Build Coastguard Worker            # Drop irrelevant environments
860*90c8c64dSAndroid Build Coastguard Worker            while not basedir.startswith(envs[-1][0]):
861*90c8c64dSAndroid Build Coastguard Worker                envs.pop()
862*90c8c64dSAndroid Build Coastguard Worker
863*90c8c64dSAndroid Build Coastguard Worker            # Filter sub directories
864*90c8c64dSAndroid Build Coastguard Worker            if '.out-dir' in filenames:
865*90c8c64dSAndroid Build Coastguard Worker                # Stop at OUT_DIR
866*90c8c64dSAndroid Build Coastguard Worker                dirnames[:] = []
867*90c8c64dSAndroid Build Coastguard Worker                continue
868*90c8c64dSAndroid Build Coastguard Worker            new_dirnames = []
869*90c8c64dSAndroid Build Coastguard Worker            for name in dirnames:
870*90c8c64dSAndroid Build Coastguard Worker                if name in {'.git', '.repo'}:
871*90c8c64dSAndroid Build Coastguard Worker                    continue
872*90c8c64dSAndroid Build Coastguard Worker                if basedir == rootdir and name == 'out':
873*90c8c64dSAndroid Build Coastguard Worker                    continue
874*90c8c64dSAndroid Build Coastguard Worker                new_dirnames.append(name)
875*90c8c64dSAndroid Build Coastguard Worker            dirnames[:] = new_dirnames
876*90c8c64dSAndroid Build Coastguard Worker
877*90c8c64dSAndroid Build Coastguard Worker            # Parse blueprint files
878*90c8c64dSAndroid Build Coastguard Worker            if filename in filenames:
879*90c8c64dSAndroid Build Coastguard Worker                try:
880*90c8c64dSAndroid Build Coastguard Worker                    path = os.path.join(basedir, filename)
881*90c8c64dSAndroid Build Coastguard Worker                    sys.stdout.flush()
882*90c8c64dSAndroid Build Coastguard Worker                    sub_env = self._parse_file_recursive(path, envs[-1][1],
883*90c8c64dSAndroid Build Coastguard Worker                                                         evaluate, False)
884*90c8c64dSAndroid Build Coastguard Worker                    assert sub_env is not None
885*90c8c64dSAndroid Build Coastguard Worker                    envs.append((basedir, sub_env))
886*90c8c64dSAndroid Build Coastguard Worker                except IOError:
887*90c8c64dSAndroid Build Coastguard Worker                    pass
888*90c8c64dSAndroid Build Coastguard Worker
889*90c8c64dSAndroid Build Coastguard Worker
890*90c8c64dSAndroid Build Coastguard Worker    def parse_file(self, path, env=None, evaluate=True,
891*90c8c64dSAndroid Build Coastguard Worker                   default_sub_name=_DEFAULT_SUB_NAME):
892*90c8c64dSAndroid Build Coastguard Worker        """Parse blueprint files recursively."""
893*90c8c64dSAndroid Build Coastguard Worker
894*90c8c64dSAndroid Build Coastguard Worker        if env is None:
895*90c8c64dSAndroid Build Coastguard Worker            env = {}
896*90c8c64dSAndroid Build Coastguard Worker
897*90c8c64dSAndroid Build Coastguard Worker        path = os.path.abspath(path)
898*90c8c64dSAndroid Build Coastguard Worker
899*90c8c64dSAndroid Build Coastguard Worker        sub_env = self._read_file(path, env)[1]
900*90c8c64dSAndroid Build Coastguard Worker
901*90c8c64dSAndroid Build Coastguard Worker        if 'subdirs' in sub_env or 'optional_subdirs' in sub_env:
902*90c8c64dSAndroid Build Coastguard Worker            self._parse_file_recursive(path, env, evaluate, True)
903*90c8c64dSAndroid Build Coastguard Worker        else:
904*90c8c64dSAndroid Build Coastguard Worker            self._scan_and_parse_all_file_recursive(
905*90c8c64dSAndroid Build Coastguard Worker                default_sub_name, path, env, evaluate)
906*90c8c64dSAndroid Build Coastguard Worker
907*90c8c64dSAndroid Build Coastguard Worker
908*90c8c64dSAndroid Build Coastguard Worker#------------------------------------------------------------------------------
909*90c8c64dSAndroid Build Coastguard Worker# Transformation
910*90c8c64dSAndroid Build Coastguard Worker#------------------------------------------------------------------------------
911*90c8c64dSAndroid Build Coastguard Worker
912*90c8c64dSAndroid Build Coastguard Workerdef _build_named_modules_dict(modules):
913*90c8c64dSAndroid Build Coastguard Worker    """Build a name-to-module dict."""
914*90c8c64dSAndroid Build Coastguard Worker    named_modules = {}
915*90c8c64dSAndroid Build Coastguard Worker    for i, (ident, attrs) in enumerate(modules):
916*90c8c64dSAndroid Build Coastguard Worker        name = attrs.get('name')
917*90c8c64dSAndroid Build Coastguard Worker        if name is not None:
918*90c8c64dSAndroid Build Coastguard Worker            named_modules[name] = [ident, attrs, i]
919*90c8c64dSAndroid Build Coastguard Worker    return named_modules
920*90c8c64dSAndroid Build Coastguard Worker
921*90c8c64dSAndroid Build Coastguard Worker
922*90c8c64dSAndroid Build Coastguard Workerdef _po_sorted_modules(modules, named_modules):
923*90c8c64dSAndroid Build Coastguard Worker    """Sort modules in post order."""
924*90c8c64dSAndroid Build Coastguard Worker    modules = [(ident, attrs, i) for i, (ident, attrs) in enumerate(modules)]
925*90c8c64dSAndroid Build Coastguard Worker
926*90c8c64dSAndroid Build Coastguard Worker    # Build module dependency graph.
927*90c8c64dSAndroid Build Coastguard Worker    edges = {}
928*90c8c64dSAndroid Build Coastguard Worker    for ident, attrs, module_id in modules:
929*90c8c64dSAndroid Build Coastguard Worker        defaults = attrs.get('defaults')
930*90c8c64dSAndroid Build Coastguard Worker        if defaults:
931*90c8c64dSAndroid Build Coastguard Worker            edges[module_id] = set(
932*90c8c64dSAndroid Build Coastguard Worker                named_modules[default][2] for default in defaults)
933*90c8c64dSAndroid Build Coastguard Worker
934*90c8c64dSAndroid Build Coastguard Worker    # Traverse module graph in post order.
935*90c8c64dSAndroid Build Coastguard Worker    post_order = []
936*90c8c64dSAndroid Build Coastguard Worker    visited = set()
937*90c8c64dSAndroid Build Coastguard Worker
938*90c8c64dSAndroid Build Coastguard Worker    def _traverse(module_id):
939*90c8c64dSAndroid Build Coastguard Worker        visited.add(module_id)
940*90c8c64dSAndroid Build Coastguard Worker        for next_module_id in edges.get(module_id, []):
941*90c8c64dSAndroid Build Coastguard Worker            if next_module_id not in visited:
942*90c8c64dSAndroid Build Coastguard Worker                _traverse(next_module_id)
943*90c8c64dSAndroid Build Coastguard Worker        post_order.append(modules[module_id])
944*90c8c64dSAndroid Build Coastguard Worker
945*90c8c64dSAndroid Build Coastguard Worker    for module_id in range(len(modules)):
946*90c8c64dSAndroid Build Coastguard Worker        if module_id not in visited:
947*90c8c64dSAndroid Build Coastguard Worker            _traverse(module_id)
948*90c8c64dSAndroid Build Coastguard Worker
949*90c8c64dSAndroid Build Coastguard Worker    return post_order
950*90c8c64dSAndroid Build Coastguard Worker
951*90c8c64dSAndroid Build Coastguard Worker
952*90c8c64dSAndroid Build Coastguard Workerdef evaluate_default(attrs, default_attrs):
953*90c8c64dSAndroid Build Coastguard Worker    """Add default attributes if the keys do not exist."""
954*90c8c64dSAndroid Build Coastguard Worker    for key, value in default_attrs.items():
955*90c8c64dSAndroid Build Coastguard Worker        if key not in attrs:
956*90c8c64dSAndroid Build Coastguard Worker            attrs[key] = value
957*90c8c64dSAndroid Build Coastguard Worker        else:
958*90c8c64dSAndroid Build Coastguard Worker            attrs_value = attrs[key]
959*90c8c64dSAndroid Build Coastguard Worker            if isinstance(value, Dict) and isinstance(attrs_value, Dict):
960*90c8c64dSAndroid Build Coastguard Worker                attrs[key] = evaluate_default(attrs_value, value)
961*90c8c64dSAndroid Build Coastguard Worker    return attrs
962*90c8c64dSAndroid Build Coastguard Worker
963*90c8c64dSAndroid Build Coastguard Worker
964*90c8c64dSAndroid Build Coastguard Workerdef evaluate_defaults(modules):
965*90c8c64dSAndroid Build Coastguard Worker    """Add default attributes to all modules if the keys do not exist."""
966*90c8c64dSAndroid Build Coastguard Worker    named_modules = _build_named_modules_dict(modules)
967*90c8c64dSAndroid Build Coastguard Worker    for ident, attrs, i in _po_sorted_modules(modules, named_modules):
968*90c8c64dSAndroid Build Coastguard Worker        for default in attrs.get('defaults', []):
969*90c8c64dSAndroid Build Coastguard Worker            attrs = evaluate_default(attrs, named_modules[default][1])
970*90c8c64dSAndroid Build Coastguard Worker        modules[i] = (ident, attrs)
971*90c8c64dSAndroid Build Coastguard Worker    return modules
972*90c8c64dSAndroid Build Coastguard Worker
973*90c8c64dSAndroid Build Coastguard Worker
974*90c8c64dSAndroid Build Coastguard Workerdef fill_module_namespaces(root_bp, modules):
975*90c8c64dSAndroid Build Coastguard Worker    """Collect soong_namespace definition and set a `_namespace` property to
976*90c8c64dSAndroid Build Coastguard Worker    each module definitions."""
977*90c8c64dSAndroid Build Coastguard Worker
978*90c8c64dSAndroid Build Coastguard Worker    # Collect all namespaces
979*90c8c64dSAndroid Build Coastguard Worker    rootdir = os.path.dirname(os.path.abspath(root_bp))
980*90c8c64dSAndroid Build Coastguard Worker    namespaces = {rootdir}
981*90c8c64dSAndroid Build Coastguard Worker    for ident, attrs in modules:
982*90c8c64dSAndroid Build Coastguard Worker        if ident == 'soong_namespace':
983*90c8c64dSAndroid Build Coastguard Worker            namespaces.add(os.path.dirname(attrs['_path']))
984*90c8c64dSAndroid Build Coastguard Worker
985*90c8c64dSAndroid Build Coastguard Worker    # Build a path matcher for module namespaces
986*90c8c64dSAndroid Build Coastguard Worker    namespaces = sorted(namespaces, reverse=True)
987*90c8c64dSAndroid Build Coastguard Worker    path_matcher = re.compile(
988*90c8c64dSAndroid Build Coastguard Worker        '|'.join('(' + re.escape(x) + '/.*)' for x in namespaces))
989*90c8c64dSAndroid Build Coastguard Worker
990*90c8c64dSAndroid Build Coastguard Worker    # Trim the root directory prefix
991*90c8c64dSAndroid Build Coastguard Worker    rootdir_prefix_len = len(rootdir) + 1
992*90c8c64dSAndroid Build Coastguard Worker    namespaces = [path[rootdir_prefix_len:] for path in namespaces]
993*90c8c64dSAndroid Build Coastguard Worker
994*90c8c64dSAndroid Build Coastguard Worker    # Fill in module namespaces
995*90c8c64dSAndroid Build Coastguard Worker    for ident, attrs in modules:
996*90c8c64dSAndroid Build Coastguard Worker        match = path_matcher.match(attrs['_path'])
997*90c8c64dSAndroid Build Coastguard Worker        attrs['_namespace'] = namespaces[match.lastindex - 1]
998*90c8c64dSAndroid Build Coastguard Worker
999*90c8c64dSAndroid Build Coastguard Worker    return modules
1000