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