xref: /aosp_15_r20/external/protobuf/objectivec/DevTools/pddm.py (revision 1b3f573f81763fcece89efc2b6a5209149e44ab8)
1*1b3f573fSAndroid Build Coastguard Worker#! /usr/bin/env python3
2*1b3f573fSAndroid Build Coastguard Worker#
3*1b3f573fSAndroid Build Coastguard Worker# Protocol Buffers - Google's data interchange format
4*1b3f573fSAndroid Build Coastguard Worker# Copyright 2015 Google Inc.  All rights reserved.
5*1b3f573fSAndroid Build Coastguard Worker# https://developers.google.com/protocol-buffers/
6*1b3f573fSAndroid Build Coastguard Worker#
7*1b3f573fSAndroid Build Coastguard Worker# Redistribution and use in source and binary forms, with or without
8*1b3f573fSAndroid Build Coastguard Worker# modification, are permitted provided that the following conditions are
9*1b3f573fSAndroid Build Coastguard Worker# met:
10*1b3f573fSAndroid Build Coastguard Worker#
11*1b3f573fSAndroid Build Coastguard Worker#     * Redistributions of source code must retain the above copyright
12*1b3f573fSAndroid Build Coastguard Worker# notice, this list of conditions and the following disclaimer.
13*1b3f573fSAndroid Build Coastguard Worker#     * Redistributions in binary form must reproduce the above
14*1b3f573fSAndroid Build Coastguard Worker# copyright notice, this list of conditions and the following disclaimer
15*1b3f573fSAndroid Build Coastguard Worker# in the documentation and/or other materials provided with the
16*1b3f573fSAndroid Build Coastguard Worker# distribution.
17*1b3f573fSAndroid Build Coastguard Worker#     * Neither the name of Google Inc. nor the names of its
18*1b3f573fSAndroid Build Coastguard Worker# contributors may be used to endorse or promote products derived from
19*1b3f573fSAndroid Build Coastguard Worker# this software without specific prior written permission.
20*1b3f573fSAndroid Build Coastguard Worker#
21*1b3f573fSAndroid Build Coastguard Worker# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22*1b3f573fSAndroid Build Coastguard Worker# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23*1b3f573fSAndroid Build Coastguard Worker# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24*1b3f573fSAndroid Build Coastguard Worker# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25*1b3f573fSAndroid Build Coastguard Worker# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26*1b3f573fSAndroid Build Coastguard Worker# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27*1b3f573fSAndroid Build Coastguard Worker# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28*1b3f573fSAndroid Build Coastguard Worker# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29*1b3f573fSAndroid Build Coastguard Worker# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30*1b3f573fSAndroid Build Coastguard Worker# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31*1b3f573fSAndroid Build Coastguard Worker# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32*1b3f573fSAndroid Build Coastguard Worker
33*1b3f573fSAndroid Build Coastguard Worker"""PDDM - Poor Developers' Debug-able Macros
34*1b3f573fSAndroid Build Coastguard Worker
35*1b3f573fSAndroid Build Coastguard WorkerA simple markup that can be added in comments of source so they can then be
36*1b3f573fSAndroid Build Coastguard Workerexpanded out into code. Most of this could be done with CPP macros, but then
37*1b3f573fSAndroid Build Coastguard Workerdevelopers can't really step through them in the debugger, this way they are
38*1b3f573fSAndroid Build Coastguard Workerexpanded to the same code, but you can debug them.
39*1b3f573fSAndroid Build Coastguard Worker
40*1b3f573fSAndroid Build Coastguard WorkerAny file can be processed, but the syntax is designed around a C based compiler.
41*1b3f573fSAndroid Build Coastguard WorkerProcessed lines start with "//%".  There are three types of sections you can
42*1b3f573fSAndroid Build Coastguard Workercreate: Text (left alone), Macro Definitions, and Macro Expansions.  There is
43*1b3f573fSAndroid Build Coastguard Workerno order required between definitions and expansions, all definitions are read
44*1b3f573fSAndroid Build Coastguard Workerbefore any expansions are processed (thus, if desired, definitions can be put
45*1b3f573fSAndroid Build Coastguard Workerat the end of the file to keep them out of the way of the code).
46*1b3f573fSAndroid Build Coastguard Worker
47*1b3f573fSAndroid Build Coastguard WorkerMacro Definitions are started with "//%PDDM-DEFINE Name(args)" and all lines
48*1b3f573fSAndroid Build Coastguard Workerafterwards that start with "//%" are included in the definition.  Multiple
49*1b3f573fSAndroid Build Coastguard Workermacros can be defined in one block by just using a another "//%PDDM-DEFINE"
50*1b3f573fSAndroid Build Coastguard Workerline to start the next macro.  Optionally, a macro can be ended with
51*1b3f573fSAndroid Build Coastguard Worker"//%PDDM-DEFINE-END", this can be useful when you want to make it clear that
52*1b3f573fSAndroid Build Coastguard Workertrailing blank lines are included in the macro.  You can also end a definition
53*1b3f573fSAndroid Build Coastguard Workerwith an expansion.
54*1b3f573fSAndroid Build Coastguard Worker
55*1b3f573fSAndroid Build Coastguard WorkerMacro Expansions are started by single lines containing
56*1b3f573fSAndroid Build Coastguard Worker"//%PDDM-EXPAND Name(args)" and then with "//%PDDM-EXPAND-END" or another
57*1b3f573fSAndroid Build Coastguard Workerexpansions.  All lines in-between are replaced by the result of the expansion.
58*1b3f573fSAndroid Build Coastguard WorkerThe first line of the expansion is always a blank like just for readability.
59*1b3f573fSAndroid Build Coastguard Worker
60*1b3f573fSAndroid Build Coastguard WorkerExpansion itself is pretty simple, one macro can invoke another macro, but
61*1b3f573fSAndroid Build Coastguard Workeryou cannot nest the invoke of a macro in another macro (i.e. - can't do
62*1b3f573fSAndroid Build Coastguard Worker"foo(bar(a))", but you can define foo(a) and bar(b) where bar invokes foo()
63*1b3f573fSAndroid Build Coastguard Workerwithin its expansion.
64*1b3f573fSAndroid Build Coastguard Worker
65*1b3f573fSAndroid Build Coastguard WorkerWhen macros are expanded, the arg references can also add "$O" suffix to the
66*1b3f573fSAndroid Build Coastguard Workername (i.e. - "NAME$O") to specify an option to be applied. The options are:
67*1b3f573fSAndroid Build Coastguard Worker
68*1b3f573fSAndroid Build Coastguard Worker    $S - Replace each character in the value with a space.
69*1b3f573fSAndroid Build Coastguard Worker    $l - Lowercase the first letter of the value.
70*1b3f573fSAndroid Build Coastguard Worker    $L - Lowercase the whole value.
71*1b3f573fSAndroid Build Coastguard Worker    $u - Uppercase the first letter of the value.
72*1b3f573fSAndroid Build Coastguard Worker    $U - Uppercase the whole value.
73*1b3f573fSAndroid Build Coastguard Worker
74*1b3f573fSAndroid Build Coastguard WorkerWithin a macro you can use ## to cause things to get joined together after
75*1b3f573fSAndroid Build Coastguard Workerexpansion (i.e. - "a##b" within a macro will become "ab").
76*1b3f573fSAndroid Build Coastguard Worker
77*1b3f573fSAndroid Build Coastguard WorkerExample:
78*1b3f573fSAndroid Build Coastguard Worker
79*1b3f573fSAndroid Build Coastguard Worker    int foo(MyEnum x) {
80*1b3f573fSAndroid Build Coastguard Worker    switch (x) {
81*1b3f573fSAndroid Build Coastguard Worker    //%PDDM-EXPAND case(Enum_Left, 1)
82*1b3f573fSAndroid Build Coastguard Worker    //%PDDM-EXPAND case(Enum_Center, 2)
83*1b3f573fSAndroid Build Coastguard Worker    //%PDDM-EXPAND case(Enum_Right, 3)
84*1b3f573fSAndroid Build Coastguard Worker    //%PDDM-EXPAND-END
85*1b3f573fSAndroid Build Coastguard Worker    }
86*1b3f573fSAndroid Build Coastguard Worker
87*1b3f573fSAndroid Build Coastguard Worker    //%PDDM-DEFINE case(_A, _B)
88*1b3f573fSAndroid Build Coastguard Worker    //%  case _A:
89*1b3f573fSAndroid Build Coastguard Worker    //%    return _B;
90*1b3f573fSAndroid Build Coastguard Worker
91*1b3f573fSAndroid Build Coastguard Worker  A macro ends at the start of the next one, or an optional %PDDM-DEFINE-END
92*1b3f573fSAndroid Build Coastguard Worker  can be used to avoid adding extra blank lines/returns (or make it clear when
93*1b3f573fSAndroid Build Coastguard Worker  it is desired).
94*1b3f573fSAndroid Build Coastguard Worker
95*1b3f573fSAndroid Build Coastguard Worker  One macro can invoke another by simply using its name NAME(ARGS). You cannot
96*1b3f573fSAndroid Build Coastguard Worker  nest an invoke inside another (i.e. - NAME1(NAME2(ARGS)) isn't supported).
97*1b3f573fSAndroid Build Coastguard Worker
98*1b3f573fSAndroid Build Coastguard Worker  Within a macro you can use ## to cause things to get joined together after
99*1b3f573fSAndroid Build Coastguard Worker  processing (i.e. - "a##b" within a macro will become "ab").
100*1b3f573fSAndroid Build Coastguard Worker
101*1b3f573fSAndroid Build Coastguard Worker
102*1b3f573fSAndroid Build Coastguard Worker"""
103*1b3f573fSAndroid Build Coastguard Worker
104*1b3f573fSAndroid Build Coastguard Workerimport optparse
105*1b3f573fSAndroid Build Coastguard Workerimport os
106*1b3f573fSAndroid Build Coastguard Workerimport re
107*1b3f573fSAndroid Build Coastguard Workerimport sys
108*1b3f573fSAndroid Build Coastguard Worker
109*1b3f573fSAndroid Build Coastguard Worker
110*1b3f573fSAndroid Build Coastguard Worker# Regex for macro definition.
111*1b3f573fSAndroid Build Coastguard Worker_MACRO_RE = re.compile(r'(?P<name>\w+)\((?P<args>.*?)\)')
112*1b3f573fSAndroid Build Coastguard Worker# Regex for macro's argument definition.
113*1b3f573fSAndroid Build Coastguard Worker_MACRO_ARG_NAME_RE = re.compile(r'^\w+$')
114*1b3f573fSAndroid Build Coastguard Worker
115*1b3f573fSAndroid Build Coastguard Worker# Line inserted after each EXPAND.
116*1b3f573fSAndroid Build Coastguard Worker_GENERATED_CODE_LINE = (
117*1b3f573fSAndroid Build Coastguard Worker  '// This block of code is generated, do not edit it directly.'
118*1b3f573fSAndroid Build Coastguard Worker)
119*1b3f573fSAndroid Build Coastguard Worker
120*1b3f573fSAndroid Build Coastguard Worker
121*1b3f573fSAndroid Build Coastguard Workerdef _MacroRefRe(macro_names):
122*1b3f573fSAndroid Build Coastguard Worker  # Takes in a list of macro names and makes a regex that will match invokes
123*1b3f573fSAndroid Build Coastguard Worker  # of those macros.
124*1b3f573fSAndroid Build Coastguard Worker  return re.compile(r'\b(?P<macro_ref>(?P<name>(%s))\((?P<args>.*?)\))' %
125*1b3f573fSAndroid Build Coastguard Worker                    '|'.join(macro_names))
126*1b3f573fSAndroid Build Coastguard Worker
127*1b3f573fSAndroid Build Coastguard Worker
128*1b3f573fSAndroid Build Coastguard Workerdef _MacroArgRefRe(macro_arg_names):
129*1b3f573fSAndroid Build Coastguard Worker  # Takes in a list of macro arg names and makes a regex that will match
130*1b3f573fSAndroid Build Coastguard Worker  # uses of those args.
131*1b3f573fSAndroid Build Coastguard Worker  return re.compile(r'\b(?P<name>(%s))(\$(?P<option>.))?\b' %
132*1b3f573fSAndroid Build Coastguard Worker                    '|'.join(macro_arg_names))
133*1b3f573fSAndroid Build Coastguard Worker
134*1b3f573fSAndroid Build Coastguard Worker
135*1b3f573fSAndroid Build Coastguard Workerclass PDDMError(Exception):
136*1b3f573fSAndroid Build Coastguard Worker  """Error thrown by pddm."""
137*1b3f573fSAndroid Build Coastguard Worker
138*1b3f573fSAndroid Build Coastguard Worker  def __init__(self, message="Error"):
139*1b3f573fSAndroid Build Coastguard Worker    self.message = message
140*1b3f573fSAndroid Build Coastguard Worker    super().__init__(self.message)
141*1b3f573fSAndroid Build Coastguard Worker
142*1b3f573fSAndroid Build Coastguard Worker
143*1b3f573fSAndroid Build Coastguard Workerclass MacroCollection(object):
144*1b3f573fSAndroid Build Coastguard Worker  """Hold a set of macros and can resolve/expand them."""
145*1b3f573fSAndroid Build Coastguard Worker
146*1b3f573fSAndroid Build Coastguard Worker  def __init__(self, a_file=None):
147*1b3f573fSAndroid Build Coastguard Worker    """Initializes the collection.
148*1b3f573fSAndroid Build Coastguard Worker
149*1b3f573fSAndroid Build Coastguard Worker    Args:
150*1b3f573fSAndroid Build Coastguard Worker      a_file: The file like stream to parse.
151*1b3f573fSAndroid Build Coastguard Worker
152*1b3f573fSAndroid Build Coastguard Worker    Raises:
153*1b3f573fSAndroid Build Coastguard Worker      PDDMError if there are any issues.
154*1b3f573fSAndroid Build Coastguard Worker    """
155*1b3f573fSAndroid Build Coastguard Worker    self._macros = dict()
156*1b3f573fSAndroid Build Coastguard Worker    if a_file:
157*1b3f573fSAndroid Build Coastguard Worker      self.ParseInput(a_file)
158*1b3f573fSAndroid Build Coastguard Worker
159*1b3f573fSAndroid Build Coastguard Worker  class MacroDefinition(object):
160*1b3f573fSAndroid Build Coastguard Worker    """Holds a macro definition."""
161*1b3f573fSAndroid Build Coastguard Worker
162*1b3f573fSAndroid Build Coastguard Worker    def __init__(self, name, arg_names):
163*1b3f573fSAndroid Build Coastguard Worker      self._name = name
164*1b3f573fSAndroid Build Coastguard Worker      self._args = tuple(arg_names)
165*1b3f573fSAndroid Build Coastguard Worker      self._body = ''
166*1b3f573fSAndroid Build Coastguard Worker      self._needNewLine = False
167*1b3f573fSAndroid Build Coastguard Worker
168*1b3f573fSAndroid Build Coastguard Worker    def AppendLine(self, line):
169*1b3f573fSAndroid Build Coastguard Worker      if self._needNewLine:
170*1b3f573fSAndroid Build Coastguard Worker        self._body += '\n'
171*1b3f573fSAndroid Build Coastguard Worker      self._body += line
172*1b3f573fSAndroid Build Coastguard Worker      self._needNewLine = not line.endswith('\n')
173*1b3f573fSAndroid Build Coastguard Worker
174*1b3f573fSAndroid Build Coastguard Worker    @property
175*1b3f573fSAndroid Build Coastguard Worker    def name(self):
176*1b3f573fSAndroid Build Coastguard Worker      return self._name
177*1b3f573fSAndroid Build Coastguard Worker
178*1b3f573fSAndroid Build Coastguard Worker    @property
179*1b3f573fSAndroid Build Coastguard Worker    def args(self):
180*1b3f573fSAndroid Build Coastguard Worker      return self._args
181*1b3f573fSAndroid Build Coastguard Worker
182*1b3f573fSAndroid Build Coastguard Worker    @property
183*1b3f573fSAndroid Build Coastguard Worker    def body(self):
184*1b3f573fSAndroid Build Coastguard Worker      return self._body
185*1b3f573fSAndroid Build Coastguard Worker
186*1b3f573fSAndroid Build Coastguard Worker  def ParseInput(self, a_file):
187*1b3f573fSAndroid Build Coastguard Worker    """Consumes input extracting definitions.
188*1b3f573fSAndroid Build Coastguard Worker
189*1b3f573fSAndroid Build Coastguard Worker    Args:
190*1b3f573fSAndroid Build Coastguard Worker      a_file: The file like stream to parse.
191*1b3f573fSAndroid Build Coastguard Worker
192*1b3f573fSAndroid Build Coastguard Worker    Raises:
193*1b3f573fSAndroid Build Coastguard Worker      PDDMError if there are any issues.
194*1b3f573fSAndroid Build Coastguard Worker    """
195*1b3f573fSAndroid Build Coastguard Worker    input_lines = a_file.read().splitlines()
196*1b3f573fSAndroid Build Coastguard Worker    self.ParseLines(input_lines)
197*1b3f573fSAndroid Build Coastguard Worker
198*1b3f573fSAndroid Build Coastguard Worker  def ParseLines(self, input_lines):
199*1b3f573fSAndroid Build Coastguard Worker    """Parses list of lines.
200*1b3f573fSAndroid Build Coastguard Worker
201*1b3f573fSAndroid Build Coastguard Worker    Args:
202*1b3f573fSAndroid Build Coastguard Worker      input_lines: A list of strings of input to parse (no newlines on the
203*1b3f573fSAndroid Build Coastguard Worker                   strings).
204*1b3f573fSAndroid Build Coastguard Worker
205*1b3f573fSAndroid Build Coastguard Worker    Raises:
206*1b3f573fSAndroid Build Coastguard Worker      PDDMError if there are any issues.
207*1b3f573fSAndroid Build Coastguard Worker    """
208*1b3f573fSAndroid Build Coastguard Worker    current_macro = None
209*1b3f573fSAndroid Build Coastguard Worker    for line in input_lines:
210*1b3f573fSAndroid Build Coastguard Worker      if line.startswith('PDDM-'):
211*1b3f573fSAndroid Build Coastguard Worker        directive = line.split(' ', 1)[0]
212*1b3f573fSAndroid Build Coastguard Worker        if directive == 'PDDM-DEFINE':
213*1b3f573fSAndroid Build Coastguard Worker          name, args = self._ParseDefineLine(line)
214*1b3f573fSAndroid Build Coastguard Worker          if self._macros.get(name):
215*1b3f573fSAndroid Build Coastguard Worker            raise PDDMError('Attempt to redefine macro: "%s"' % line)
216*1b3f573fSAndroid Build Coastguard Worker          current_macro = self.MacroDefinition(name, args)
217*1b3f573fSAndroid Build Coastguard Worker          self._macros[name] = current_macro
218*1b3f573fSAndroid Build Coastguard Worker          continue
219*1b3f573fSAndroid Build Coastguard Worker        if directive == 'PDDM-DEFINE-END':
220*1b3f573fSAndroid Build Coastguard Worker          if not current_macro:
221*1b3f573fSAndroid Build Coastguard Worker            raise PDDMError('Got DEFINE-END directive without an active macro:'
222*1b3f573fSAndroid Build Coastguard Worker                            ' "%s"' % line)
223*1b3f573fSAndroid Build Coastguard Worker          current_macro = None
224*1b3f573fSAndroid Build Coastguard Worker          continue
225*1b3f573fSAndroid Build Coastguard Worker        raise PDDMError('Hit a line with an unknown directive: "%s"' % line)
226*1b3f573fSAndroid Build Coastguard Worker
227*1b3f573fSAndroid Build Coastguard Worker      if current_macro:
228*1b3f573fSAndroid Build Coastguard Worker        current_macro.AppendLine(line)
229*1b3f573fSAndroid Build Coastguard Worker        continue
230*1b3f573fSAndroid Build Coastguard Worker
231*1b3f573fSAndroid Build Coastguard Worker      # Allow blank lines between macro definitions.
232*1b3f573fSAndroid Build Coastguard Worker      if line.strip() == '':
233*1b3f573fSAndroid Build Coastguard Worker        continue
234*1b3f573fSAndroid Build Coastguard Worker
235*1b3f573fSAndroid Build Coastguard Worker      raise PDDMError('Hit a line that wasn\'t a directive and no open macro'
236*1b3f573fSAndroid Build Coastguard Worker                      ' definition: "%s"' % line)
237*1b3f573fSAndroid Build Coastguard Worker
238*1b3f573fSAndroid Build Coastguard Worker  def _ParseDefineLine(self, input_line):
239*1b3f573fSAndroid Build Coastguard Worker    assert input_line.startswith('PDDM-DEFINE')
240*1b3f573fSAndroid Build Coastguard Worker    line = input_line[12:].strip()
241*1b3f573fSAndroid Build Coastguard Worker    match = _MACRO_RE.match(line)
242*1b3f573fSAndroid Build Coastguard Worker    # Must match full line
243*1b3f573fSAndroid Build Coastguard Worker    if match is None or match.group(0) != line:
244*1b3f573fSAndroid Build Coastguard Worker      raise PDDMError('Failed to parse macro definition: "%s"' % input_line)
245*1b3f573fSAndroid Build Coastguard Worker    name = match.group('name')
246*1b3f573fSAndroid Build Coastguard Worker    args_str = match.group('args').strip()
247*1b3f573fSAndroid Build Coastguard Worker    args = []
248*1b3f573fSAndroid Build Coastguard Worker    if args_str:
249*1b3f573fSAndroid Build Coastguard Worker      for part in args_str.split(','):
250*1b3f573fSAndroid Build Coastguard Worker        arg = part.strip()
251*1b3f573fSAndroid Build Coastguard Worker        if arg == '':
252*1b3f573fSAndroid Build Coastguard Worker          raise PDDMError('Empty arg name in macro definition: "%s"'
253*1b3f573fSAndroid Build Coastguard Worker                          % input_line)
254*1b3f573fSAndroid Build Coastguard Worker        if not _MACRO_ARG_NAME_RE.match(arg):
255*1b3f573fSAndroid Build Coastguard Worker          raise PDDMError('Invalid arg name "%s" in macro definition: "%s"'
256*1b3f573fSAndroid Build Coastguard Worker                          % (arg, input_line))
257*1b3f573fSAndroid Build Coastguard Worker        if arg in args:
258*1b3f573fSAndroid Build Coastguard Worker          raise PDDMError('Arg name "%s" used more than once in macro'
259*1b3f573fSAndroid Build Coastguard Worker                          ' definition: "%s"' % (arg, input_line))
260*1b3f573fSAndroid Build Coastguard Worker        args.append(arg)
261*1b3f573fSAndroid Build Coastguard Worker    return (name, tuple(args))
262*1b3f573fSAndroid Build Coastguard Worker
263*1b3f573fSAndroid Build Coastguard Worker  def Expand(self, macro_ref_str):
264*1b3f573fSAndroid Build Coastguard Worker    """Expands the macro reference.
265*1b3f573fSAndroid Build Coastguard Worker
266*1b3f573fSAndroid Build Coastguard Worker    Args:
267*1b3f573fSAndroid Build Coastguard Worker      macro_ref_str: String of a macro reference (i.e. foo(a, b)).
268*1b3f573fSAndroid Build Coastguard Worker
269*1b3f573fSAndroid Build Coastguard Worker    Returns:
270*1b3f573fSAndroid Build Coastguard Worker      The text from the expansion.
271*1b3f573fSAndroid Build Coastguard Worker
272*1b3f573fSAndroid Build Coastguard Worker    Raises:
273*1b3f573fSAndroid Build Coastguard Worker      PDDMError if there are any issues.
274*1b3f573fSAndroid Build Coastguard Worker    """
275*1b3f573fSAndroid Build Coastguard Worker    match = _MACRO_RE.match(macro_ref_str)
276*1b3f573fSAndroid Build Coastguard Worker    if match is None or match.group(0) != macro_ref_str:
277*1b3f573fSAndroid Build Coastguard Worker      raise PDDMError('Failed to parse macro reference: "%s"' % macro_ref_str)
278*1b3f573fSAndroid Build Coastguard Worker    if match.group('name') not in self._macros:
279*1b3f573fSAndroid Build Coastguard Worker      raise PDDMError('No macro named "%s".' % match.group('name'))
280*1b3f573fSAndroid Build Coastguard Worker    return self._Expand(match, [], macro_ref_str)
281*1b3f573fSAndroid Build Coastguard Worker
282*1b3f573fSAndroid Build Coastguard Worker  def _FormatStack(self, macro_ref_stack):
283*1b3f573fSAndroid Build Coastguard Worker    result = ''
284*1b3f573fSAndroid Build Coastguard Worker    for _, macro_ref in reversed(macro_ref_stack):
285*1b3f573fSAndroid Build Coastguard Worker      result += '\n...while expanding "%s".' % macro_ref
286*1b3f573fSAndroid Build Coastguard Worker    return result
287*1b3f573fSAndroid Build Coastguard Worker
288*1b3f573fSAndroid Build Coastguard Worker  def _Expand(self, macro_ref_match, macro_stack, macro_ref_str=None):
289*1b3f573fSAndroid Build Coastguard Worker    if macro_ref_str is None:
290*1b3f573fSAndroid Build Coastguard Worker      macro_ref_str = macro_ref_match.group('macro_ref')
291*1b3f573fSAndroid Build Coastguard Worker    name = macro_ref_match.group('name')
292*1b3f573fSAndroid Build Coastguard Worker    for prev_name, prev_macro_ref in macro_stack:
293*1b3f573fSAndroid Build Coastguard Worker      if name == prev_name:
294*1b3f573fSAndroid Build Coastguard Worker        raise PDDMError('Found macro recursion, invoking "%s":%s' %
295*1b3f573fSAndroid Build Coastguard Worker                        (macro_ref_str, self._FormatStack(macro_stack)))
296*1b3f573fSAndroid Build Coastguard Worker    macro = self._macros[name]
297*1b3f573fSAndroid Build Coastguard Worker    args_str = macro_ref_match.group('args').strip()
298*1b3f573fSAndroid Build Coastguard Worker    args = []
299*1b3f573fSAndroid Build Coastguard Worker    if args_str or len(macro.args):
300*1b3f573fSAndroid Build Coastguard Worker      args = [x.strip() for x in args_str.split(',')]
301*1b3f573fSAndroid Build Coastguard Worker    if len(args) != len(macro.args):
302*1b3f573fSAndroid Build Coastguard Worker      raise PDDMError('Expected %d args, got: "%s".%s' %
303*1b3f573fSAndroid Build Coastguard Worker                      (len(macro.args), macro_ref_str,
304*1b3f573fSAndroid Build Coastguard Worker                       self._FormatStack(macro_stack)))
305*1b3f573fSAndroid Build Coastguard Worker    # Replace args usages.
306*1b3f573fSAndroid Build Coastguard Worker    result = self._ReplaceArgValues(macro, args, macro_ref_str, macro_stack)
307*1b3f573fSAndroid Build Coastguard Worker    # Expand any macro invokes.
308*1b3f573fSAndroid Build Coastguard Worker    new_macro_stack = macro_stack + [(name, macro_ref_str)]
309*1b3f573fSAndroid Build Coastguard Worker    while True:
310*1b3f573fSAndroid Build Coastguard Worker      eval_result = self._EvalMacrosRefs(result, new_macro_stack)
311*1b3f573fSAndroid Build Coastguard Worker      # Consume all ## directives to glue things together.
312*1b3f573fSAndroid Build Coastguard Worker      eval_result = eval_result.replace('##', '')
313*1b3f573fSAndroid Build Coastguard Worker      if eval_result == result:
314*1b3f573fSAndroid Build Coastguard Worker        break
315*1b3f573fSAndroid Build Coastguard Worker      result = eval_result
316*1b3f573fSAndroid Build Coastguard Worker    return result
317*1b3f573fSAndroid Build Coastguard Worker
318*1b3f573fSAndroid Build Coastguard Worker  def _ReplaceArgValues(self,
319*1b3f573fSAndroid Build Coastguard Worker                        macro, arg_values, macro_ref_to_report, macro_stack):
320*1b3f573fSAndroid Build Coastguard Worker    if len(arg_values) == 0:
321*1b3f573fSAndroid Build Coastguard Worker      # Nothing to do
322*1b3f573fSAndroid Build Coastguard Worker      return macro.body
323*1b3f573fSAndroid Build Coastguard Worker    assert len(arg_values) == len(macro.args)
324*1b3f573fSAndroid Build Coastguard Worker    args = dict(list(zip(macro.args, arg_values)))
325*1b3f573fSAndroid Build Coastguard Worker
326*1b3f573fSAndroid Build Coastguard Worker    def _lookupArg(match):
327*1b3f573fSAndroid Build Coastguard Worker      val = args[match.group('name')]
328*1b3f573fSAndroid Build Coastguard Worker      opt = match.group('option')
329*1b3f573fSAndroid Build Coastguard Worker      if opt:
330*1b3f573fSAndroid Build Coastguard Worker        if opt == 'S':  # Spaces for the length
331*1b3f573fSAndroid Build Coastguard Worker          return ' ' * len(val)
332*1b3f573fSAndroid Build Coastguard Worker        elif opt == 'l':  # Lowercase first character
333*1b3f573fSAndroid Build Coastguard Worker          if val:
334*1b3f573fSAndroid Build Coastguard Worker            return val[0].lower() + val[1:]
335*1b3f573fSAndroid Build Coastguard Worker          else:
336*1b3f573fSAndroid Build Coastguard Worker            return val
337*1b3f573fSAndroid Build Coastguard Worker        elif opt == 'L':  # All Lowercase
338*1b3f573fSAndroid Build Coastguard Worker          return val.lower()
339*1b3f573fSAndroid Build Coastguard Worker        elif opt == 'u':  # Uppercase first character
340*1b3f573fSAndroid Build Coastguard Worker          if val:
341*1b3f573fSAndroid Build Coastguard Worker            return val[0].upper() + val[1:]
342*1b3f573fSAndroid Build Coastguard Worker          else:
343*1b3f573fSAndroid Build Coastguard Worker            return val
344*1b3f573fSAndroid Build Coastguard Worker        elif opt == 'U':  # All Uppercase
345*1b3f573fSAndroid Build Coastguard Worker          return val.upper()
346*1b3f573fSAndroid Build Coastguard Worker        else:
347*1b3f573fSAndroid Build Coastguard Worker          raise PDDMError('Unknown arg option "%s$%s" while expanding "%s".%s'
348*1b3f573fSAndroid Build Coastguard Worker                          % (match.group('name'), match.group('option'),
349*1b3f573fSAndroid Build Coastguard Worker                             macro_ref_to_report,
350*1b3f573fSAndroid Build Coastguard Worker                             self._FormatStack(macro_stack)))
351*1b3f573fSAndroid Build Coastguard Worker      return val
352*1b3f573fSAndroid Build Coastguard Worker    # Let the regex do the work!
353*1b3f573fSAndroid Build Coastguard Worker    macro_arg_ref_re = _MacroArgRefRe(macro.args)
354*1b3f573fSAndroid Build Coastguard Worker    return macro_arg_ref_re.sub(_lookupArg, macro.body)
355*1b3f573fSAndroid Build Coastguard Worker
356*1b3f573fSAndroid Build Coastguard Worker  def _EvalMacrosRefs(self, text, macro_stack):
357*1b3f573fSAndroid Build Coastguard Worker    macro_ref_re = _MacroRefRe(list(self._macros.keys()))
358*1b3f573fSAndroid Build Coastguard Worker
359*1b3f573fSAndroid Build Coastguard Worker    def _resolveMacro(match):
360*1b3f573fSAndroid Build Coastguard Worker      return self._Expand(match, macro_stack)
361*1b3f573fSAndroid Build Coastguard Worker    return macro_ref_re.sub(_resolveMacro, text)
362*1b3f573fSAndroid Build Coastguard Worker
363*1b3f573fSAndroid Build Coastguard Worker
364*1b3f573fSAndroid Build Coastguard Workerclass SourceFile(object):
365*1b3f573fSAndroid Build Coastguard Worker  """Represents a source file with PDDM directives in it."""
366*1b3f573fSAndroid Build Coastguard Worker
367*1b3f573fSAndroid Build Coastguard Worker  def __init__(self, a_file, import_resolver=None):
368*1b3f573fSAndroid Build Coastguard Worker    """Initializes the file reading in the file.
369*1b3f573fSAndroid Build Coastguard Worker
370*1b3f573fSAndroid Build Coastguard Worker    Args:
371*1b3f573fSAndroid Build Coastguard Worker      a_file: The file to read in.
372*1b3f573fSAndroid Build Coastguard Worker      import_resolver: a function that given a path will return a stream for
373*1b3f573fSAndroid Build Coastguard Worker        the contents.
374*1b3f573fSAndroid Build Coastguard Worker
375*1b3f573fSAndroid Build Coastguard Worker    Raises:
376*1b3f573fSAndroid Build Coastguard Worker      PDDMError if there are any issues.
377*1b3f573fSAndroid Build Coastguard Worker    """
378*1b3f573fSAndroid Build Coastguard Worker    self._sections = []
379*1b3f573fSAndroid Build Coastguard Worker    self._original_content = a_file.read()
380*1b3f573fSAndroid Build Coastguard Worker    self._import_resolver = import_resolver
381*1b3f573fSAndroid Build Coastguard Worker    self._processed_content = None
382*1b3f573fSAndroid Build Coastguard Worker
383*1b3f573fSAndroid Build Coastguard Worker  class SectionBase(object):
384*1b3f573fSAndroid Build Coastguard Worker
385*1b3f573fSAndroid Build Coastguard Worker    def __init__(self, first_line_num):
386*1b3f573fSAndroid Build Coastguard Worker      self._lines = []
387*1b3f573fSAndroid Build Coastguard Worker      self._first_line_num = first_line_num
388*1b3f573fSAndroid Build Coastguard Worker
389*1b3f573fSAndroid Build Coastguard Worker    def TryAppend(self, line, line_num):
390*1b3f573fSAndroid Build Coastguard Worker      """Try appending a line.
391*1b3f573fSAndroid Build Coastguard Worker
392*1b3f573fSAndroid Build Coastguard Worker      Args:
393*1b3f573fSAndroid Build Coastguard Worker        line: The line to append.
394*1b3f573fSAndroid Build Coastguard Worker        line_num: The number of the line.
395*1b3f573fSAndroid Build Coastguard Worker
396*1b3f573fSAndroid Build Coastguard Worker      Returns:
397*1b3f573fSAndroid Build Coastguard Worker        A tuple of (SUCCESS, CAN_ADD_MORE).  If SUCCESS if False, the line
398*1b3f573fSAndroid Build Coastguard Worker        wasn't append.  If SUCCESS is True, then CAN_ADD_MORE is True/False to
399*1b3f573fSAndroid Build Coastguard Worker        indicate if more lines can be added after this one.
400*1b3f573fSAndroid Build Coastguard Worker      """
401*1b3f573fSAndroid Build Coastguard Worker      assert False, "subclass should have overridden"
402*1b3f573fSAndroid Build Coastguard Worker      return (False, False)
403*1b3f573fSAndroid Build Coastguard Worker
404*1b3f573fSAndroid Build Coastguard Worker    def HitEOF(self):
405*1b3f573fSAndroid Build Coastguard Worker      """Called when the EOF was reached for for a given section."""
406*1b3f573fSAndroid Build Coastguard Worker      pass
407*1b3f573fSAndroid Build Coastguard Worker
408*1b3f573fSAndroid Build Coastguard Worker    def BindMacroCollection(self, macro_collection):
409*1b3f573fSAndroid Build Coastguard Worker      """Binds the chunk to a macro collection.
410*1b3f573fSAndroid Build Coastguard Worker
411*1b3f573fSAndroid Build Coastguard Worker      Args:
412*1b3f573fSAndroid Build Coastguard Worker        macro_collection: The collection to bind too.
413*1b3f573fSAndroid Build Coastguard Worker      """
414*1b3f573fSAndroid Build Coastguard Worker      pass
415*1b3f573fSAndroid Build Coastguard Worker
416*1b3f573fSAndroid Build Coastguard Worker    def Append(self, line):
417*1b3f573fSAndroid Build Coastguard Worker      self._lines.append(line)
418*1b3f573fSAndroid Build Coastguard Worker
419*1b3f573fSAndroid Build Coastguard Worker    @property
420*1b3f573fSAndroid Build Coastguard Worker    def lines(self):
421*1b3f573fSAndroid Build Coastguard Worker      return self._lines
422*1b3f573fSAndroid Build Coastguard Worker
423*1b3f573fSAndroid Build Coastguard Worker    @property
424*1b3f573fSAndroid Build Coastguard Worker    def num_lines_captured(self):
425*1b3f573fSAndroid Build Coastguard Worker      return len(self._lines)
426*1b3f573fSAndroid Build Coastguard Worker
427*1b3f573fSAndroid Build Coastguard Worker    @property
428*1b3f573fSAndroid Build Coastguard Worker    def first_line_num(self):
429*1b3f573fSAndroid Build Coastguard Worker      return self._first_line_num
430*1b3f573fSAndroid Build Coastguard Worker
431*1b3f573fSAndroid Build Coastguard Worker    @property
432*1b3f573fSAndroid Build Coastguard Worker    def first_line(self):
433*1b3f573fSAndroid Build Coastguard Worker      if not self._lines:
434*1b3f573fSAndroid Build Coastguard Worker        return ''
435*1b3f573fSAndroid Build Coastguard Worker      return self._lines[0]
436*1b3f573fSAndroid Build Coastguard Worker
437*1b3f573fSAndroid Build Coastguard Worker    @property
438*1b3f573fSAndroid Build Coastguard Worker    def text(self):
439*1b3f573fSAndroid Build Coastguard Worker      return '\n'.join(self.lines) + '\n'
440*1b3f573fSAndroid Build Coastguard Worker
441*1b3f573fSAndroid Build Coastguard Worker  class TextSection(SectionBase):
442*1b3f573fSAndroid Build Coastguard Worker    """Text section that is echoed out as is."""
443*1b3f573fSAndroid Build Coastguard Worker
444*1b3f573fSAndroid Build Coastguard Worker    def TryAppend(self, line, line_num):
445*1b3f573fSAndroid Build Coastguard Worker      if line.startswith('//%PDDM'):
446*1b3f573fSAndroid Build Coastguard Worker        return (False, False)
447*1b3f573fSAndroid Build Coastguard Worker      self.Append(line)
448*1b3f573fSAndroid Build Coastguard Worker      return (True, True)
449*1b3f573fSAndroid Build Coastguard Worker
450*1b3f573fSAndroid Build Coastguard Worker  class ExpansionSection(SectionBase):
451*1b3f573fSAndroid Build Coastguard Worker    """Section that is the result of an macro expansion."""
452*1b3f573fSAndroid Build Coastguard Worker
453*1b3f573fSAndroid Build Coastguard Worker    def __init__(self, first_line_num):
454*1b3f573fSAndroid Build Coastguard Worker      SourceFile.SectionBase.__init__(self, first_line_num)
455*1b3f573fSAndroid Build Coastguard Worker      self._macro_collection = None
456*1b3f573fSAndroid Build Coastguard Worker
457*1b3f573fSAndroid Build Coastguard Worker    def TryAppend(self, line, line_num):
458*1b3f573fSAndroid Build Coastguard Worker      if line.startswith('//%PDDM'):
459*1b3f573fSAndroid Build Coastguard Worker        directive = line.split(' ', 1)[0]
460*1b3f573fSAndroid Build Coastguard Worker        if directive == '//%PDDM-EXPAND':
461*1b3f573fSAndroid Build Coastguard Worker          self.Append(line)
462*1b3f573fSAndroid Build Coastguard Worker          return (True, True)
463*1b3f573fSAndroid Build Coastguard Worker        if directive == '//%PDDM-EXPAND-END':
464*1b3f573fSAndroid Build Coastguard Worker          assert self.num_lines_captured > 0
465*1b3f573fSAndroid Build Coastguard Worker          return (True, False)
466*1b3f573fSAndroid Build Coastguard Worker        raise PDDMError('Ran into directive ("%s", line %d) while in "%s".' %
467*1b3f573fSAndroid Build Coastguard Worker                        (directive, line_num, self.first_line))
468*1b3f573fSAndroid Build Coastguard Worker      # Eat other lines.
469*1b3f573fSAndroid Build Coastguard Worker      return (True, True)
470*1b3f573fSAndroid Build Coastguard Worker
471*1b3f573fSAndroid Build Coastguard Worker    def HitEOF(self):
472*1b3f573fSAndroid Build Coastguard Worker      raise PDDMError('Hit the end of the file while in "%s".' %
473*1b3f573fSAndroid Build Coastguard Worker                      self.first_line)
474*1b3f573fSAndroid Build Coastguard Worker
475*1b3f573fSAndroid Build Coastguard Worker    def BindMacroCollection(self, macro_collection):
476*1b3f573fSAndroid Build Coastguard Worker      self._macro_collection = macro_collection
477*1b3f573fSAndroid Build Coastguard Worker
478*1b3f573fSAndroid Build Coastguard Worker    @property
479*1b3f573fSAndroid Build Coastguard Worker    def lines(self):
480*1b3f573fSAndroid Build Coastguard Worker      captured_lines = SourceFile.SectionBase.lines.fget(self)
481*1b3f573fSAndroid Build Coastguard Worker      directive_len = len('//%PDDM-EXPAND')
482*1b3f573fSAndroid Build Coastguard Worker      result = []
483*1b3f573fSAndroid Build Coastguard Worker      for line in captured_lines:
484*1b3f573fSAndroid Build Coastguard Worker        result.append(line)
485*1b3f573fSAndroid Build Coastguard Worker        if self._macro_collection:
486*1b3f573fSAndroid Build Coastguard Worker          # Always add a blank line, seems to read better. (If need be, add an
487*1b3f573fSAndroid Build Coastguard Worker          # option to the EXPAND to indicate if this should be done.)
488*1b3f573fSAndroid Build Coastguard Worker          result.extend([_GENERATED_CODE_LINE, '// clang-format off', ''])
489*1b3f573fSAndroid Build Coastguard Worker          macro = line[directive_len:].strip()
490*1b3f573fSAndroid Build Coastguard Worker          try:
491*1b3f573fSAndroid Build Coastguard Worker            expand_result = self._macro_collection.Expand(macro)
492*1b3f573fSAndroid Build Coastguard Worker            # Since expansions are line oriented, strip trailing whitespace
493*1b3f573fSAndroid Build Coastguard Worker            # from the lines.
494*1b3f573fSAndroid Build Coastguard Worker            lines = [x.rstrip() for x in expand_result.split('\n')]
495*1b3f573fSAndroid Build Coastguard Worker            lines.append('// clang-format on')
496*1b3f573fSAndroid Build Coastguard Worker            result.append('\n'.join(lines))
497*1b3f573fSAndroid Build Coastguard Worker          except PDDMError as e:
498*1b3f573fSAndroid Build Coastguard Worker            raise PDDMError('%s\n...while expanding "%s" from the section'
499*1b3f573fSAndroid Build Coastguard Worker                            ' that started:\n   Line %d: %s' %
500*1b3f573fSAndroid Build Coastguard Worker                            (e.message, macro,
501*1b3f573fSAndroid Build Coastguard Worker                             self.first_line_num, self.first_line))
502*1b3f573fSAndroid Build Coastguard Worker
503*1b3f573fSAndroid Build Coastguard Worker      # Add the ending marker.
504*1b3f573fSAndroid Build Coastguard Worker      if len(captured_lines) == 1:
505*1b3f573fSAndroid Build Coastguard Worker        result.append('//%%PDDM-EXPAND-END %s' %
506*1b3f573fSAndroid Build Coastguard Worker                      captured_lines[0][directive_len:].strip())
507*1b3f573fSAndroid Build Coastguard Worker      else:
508*1b3f573fSAndroid Build Coastguard Worker        result.append('//%%PDDM-EXPAND-END (%s expansions)' %
509*1b3f573fSAndroid Build Coastguard Worker                      len(captured_lines))
510*1b3f573fSAndroid Build Coastguard Worker      return result
511*1b3f573fSAndroid Build Coastguard Worker
512*1b3f573fSAndroid Build Coastguard Worker  class DefinitionSection(SectionBase):
513*1b3f573fSAndroid Build Coastguard Worker    """Section containing macro definitions"""
514*1b3f573fSAndroid Build Coastguard Worker
515*1b3f573fSAndroid Build Coastguard Worker    def TryAppend(self, line, line_num):
516*1b3f573fSAndroid Build Coastguard Worker      if not line.startswith('//%'):
517*1b3f573fSAndroid Build Coastguard Worker        return (False, False)
518*1b3f573fSAndroid Build Coastguard Worker      if line.startswith('//%PDDM'):
519*1b3f573fSAndroid Build Coastguard Worker        directive = line.split(' ', 1)[0]
520*1b3f573fSAndroid Build Coastguard Worker        if directive == "//%PDDM-EXPAND":
521*1b3f573fSAndroid Build Coastguard Worker          return False, False
522*1b3f573fSAndroid Build Coastguard Worker        if directive not in ('//%PDDM-DEFINE', '//%PDDM-DEFINE-END'):
523*1b3f573fSAndroid Build Coastguard Worker          raise PDDMError('Ran into directive ("%s", line %d) while in "%s".' %
524*1b3f573fSAndroid Build Coastguard Worker                          (directive, line_num, self.first_line))
525*1b3f573fSAndroid Build Coastguard Worker      self.Append(line)
526*1b3f573fSAndroid Build Coastguard Worker      return (True, True)
527*1b3f573fSAndroid Build Coastguard Worker
528*1b3f573fSAndroid Build Coastguard Worker    def BindMacroCollection(self, macro_collection):
529*1b3f573fSAndroid Build Coastguard Worker      if macro_collection:
530*1b3f573fSAndroid Build Coastguard Worker        try:
531*1b3f573fSAndroid Build Coastguard Worker          # Parse the lines after stripping the prefix.
532*1b3f573fSAndroid Build Coastguard Worker          macro_collection.ParseLines([x[3:] for x in self.lines])
533*1b3f573fSAndroid Build Coastguard Worker        except PDDMError as e:
534*1b3f573fSAndroid Build Coastguard Worker          raise PDDMError('%s\n...while parsing section that started:\n'
535*1b3f573fSAndroid Build Coastguard Worker                          '  Line %d: %s' %
536*1b3f573fSAndroid Build Coastguard Worker                          (e.message, self.first_line_num, self.first_line))
537*1b3f573fSAndroid Build Coastguard Worker
538*1b3f573fSAndroid Build Coastguard Worker  class ImportDefinesSection(SectionBase):
539*1b3f573fSAndroid Build Coastguard Worker    """Section containing an import of PDDM-DEFINES from an external file."""
540*1b3f573fSAndroid Build Coastguard Worker
541*1b3f573fSAndroid Build Coastguard Worker    def __init__(self, first_line_num, import_resolver):
542*1b3f573fSAndroid Build Coastguard Worker      SourceFile.SectionBase.__init__(self, first_line_num)
543*1b3f573fSAndroid Build Coastguard Worker      self._import_resolver = import_resolver
544*1b3f573fSAndroid Build Coastguard Worker
545*1b3f573fSAndroid Build Coastguard Worker    def TryAppend(self, line, line_num):
546*1b3f573fSAndroid Build Coastguard Worker      if not line.startswith('//%PDDM-IMPORT-DEFINES '):
547*1b3f573fSAndroid Build Coastguard Worker        return (False, False)
548*1b3f573fSAndroid Build Coastguard Worker      assert self.num_lines_captured == 0
549*1b3f573fSAndroid Build Coastguard Worker      self.Append(line)
550*1b3f573fSAndroid Build Coastguard Worker      return (True, False)
551*1b3f573fSAndroid Build Coastguard Worker
552*1b3f573fSAndroid Build Coastguard Worker    def BindMacroCollection(self, macro_colletion):
553*1b3f573fSAndroid Build Coastguard Worker      if not macro_colletion:
554*1b3f573fSAndroid Build Coastguard Worker        return
555*1b3f573fSAndroid Build Coastguard Worker      if self._import_resolver is None:
556*1b3f573fSAndroid Build Coastguard Worker        raise PDDMError('Got an IMPORT-DEFINES without a resolver (line %d):'
557*1b3f573fSAndroid Build Coastguard Worker                        ' "%s".' % (self.first_line_num, self.first_line))
558*1b3f573fSAndroid Build Coastguard Worker      import_name = self.first_line.split(' ', 1)[1].strip()
559*1b3f573fSAndroid Build Coastguard Worker      imported_file = self._import_resolver(import_name)
560*1b3f573fSAndroid Build Coastguard Worker      if imported_file is None:
561*1b3f573fSAndroid Build Coastguard Worker        raise PDDMError('Resolver failed to find "%s" (line %d):'
562*1b3f573fSAndroid Build Coastguard Worker                        ' "%s".' %
563*1b3f573fSAndroid Build Coastguard Worker                        (import_name, self.first_line_num, self.first_line))
564*1b3f573fSAndroid Build Coastguard Worker      try:
565*1b3f573fSAndroid Build Coastguard Worker        imported_src_file = SourceFile(imported_file, self._import_resolver)
566*1b3f573fSAndroid Build Coastguard Worker        imported_src_file._ParseFile()
567*1b3f573fSAndroid Build Coastguard Worker        for section in imported_src_file._sections:
568*1b3f573fSAndroid Build Coastguard Worker          section.BindMacroCollection(macro_colletion)
569*1b3f573fSAndroid Build Coastguard Worker      except PDDMError as e:
570*1b3f573fSAndroid Build Coastguard Worker        raise PDDMError('%s\n...while importing defines:\n'
571*1b3f573fSAndroid Build Coastguard Worker                        '  Line %d: %s' %
572*1b3f573fSAndroid Build Coastguard Worker                        (e.message, self.first_line_num, self.first_line))
573*1b3f573fSAndroid Build Coastguard Worker
574*1b3f573fSAndroid Build Coastguard Worker  def _ParseFile(self):
575*1b3f573fSAndroid Build Coastguard Worker    self._sections = []
576*1b3f573fSAndroid Build Coastguard Worker    lines = self._original_content.splitlines()
577*1b3f573fSAndroid Build Coastguard Worker    cur_section = None
578*1b3f573fSAndroid Build Coastguard Worker    for line_num, line in enumerate(lines, 1):
579*1b3f573fSAndroid Build Coastguard Worker      if not cur_section:
580*1b3f573fSAndroid Build Coastguard Worker        cur_section = self._MakeSection(line, line_num)
581*1b3f573fSAndroid Build Coastguard Worker      was_added, accept_more = cur_section.TryAppend(line, line_num)
582*1b3f573fSAndroid Build Coastguard Worker      if not was_added:
583*1b3f573fSAndroid Build Coastguard Worker        cur_section = self._MakeSection(line, line_num)
584*1b3f573fSAndroid Build Coastguard Worker        was_added, accept_more = cur_section.TryAppend(line, line_num)
585*1b3f573fSAndroid Build Coastguard Worker        assert was_added
586*1b3f573fSAndroid Build Coastguard Worker      if not accept_more:
587*1b3f573fSAndroid Build Coastguard Worker        cur_section = None
588*1b3f573fSAndroid Build Coastguard Worker
589*1b3f573fSAndroid Build Coastguard Worker    if cur_section:
590*1b3f573fSAndroid Build Coastguard Worker      cur_section.HitEOF()
591*1b3f573fSAndroid Build Coastguard Worker
592*1b3f573fSAndroid Build Coastguard Worker  def _MakeSection(self, line, line_num):
593*1b3f573fSAndroid Build Coastguard Worker    if not line.startswith('//%PDDM'):
594*1b3f573fSAndroid Build Coastguard Worker      section = self.TextSection(line_num)
595*1b3f573fSAndroid Build Coastguard Worker    else:
596*1b3f573fSAndroid Build Coastguard Worker      directive = line.split(' ', 1)[0]
597*1b3f573fSAndroid Build Coastguard Worker      if directive == '//%PDDM-EXPAND':
598*1b3f573fSAndroid Build Coastguard Worker        section = self.ExpansionSection(line_num)
599*1b3f573fSAndroid Build Coastguard Worker      elif directive == '//%PDDM-DEFINE':
600*1b3f573fSAndroid Build Coastguard Worker        section = self.DefinitionSection(line_num)
601*1b3f573fSAndroid Build Coastguard Worker      elif directive == '//%PDDM-IMPORT-DEFINES':
602*1b3f573fSAndroid Build Coastguard Worker        section = self.ImportDefinesSection(line_num, self._import_resolver)
603*1b3f573fSAndroid Build Coastguard Worker      else:
604*1b3f573fSAndroid Build Coastguard Worker        raise PDDMError('Unexpected line %d: "%s".' % (line_num, line))
605*1b3f573fSAndroid Build Coastguard Worker    self._sections.append(section)
606*1b3f573fSAndroid Build Coastguard Worker    return section
607*1b3f573fSAndroid Build Coastguard Worker
608*1b3f573fSAndroid Build Coastguard Worker  def ProcessContent(self, strip_expansion=False):
609*1b3f573fSAndroid Build Coastguard Worker    """Processes the file contents."""
610*1b3f573fSAndroid Build Coastguard Worker    self._ParseFile()
611*1b3f573fSAndroid Build Coastguard Worker    if strip_expansion:
612*1b3f573fSAndroid Build Coastguard Worker      # Without a collection the expansions become blank, removing them.
613*1b3f573fSAndroid Build Coastguard Worker      collection = None
614*1b3f573fSAndroid Build Coastguard Worker    else:
615*1b3f573fSAndroid Build Coastguard Worker      collection = MacroCollection()
616*1b3f573fSAndroid Build Coastguard Worker    for section in self._sections:
617*1b3f573fSAndroid Build Coastguard Worker      section.BindMacroCollection(collection)
618*1b3f573fSAndroid Build Coastguard Worker    result = ''
619*1b3f573fSAndroid Build Coastguard Worker    for section in self._sections:
620*1b3f573fSAndroid Build Coastguard Worker      result += section.text
621*1b3f573fSAndroid Build Coastguard Worker    self._processed_content = result
622*1b3f573fSAndroid Build Coastguard Worker
623*1b3f573fSAndroid Build Coastguard Worker  @property
624*1b3f573fSAndroid Build Coastguard Worker  def original_content(self):
625*1b3f573fSAndroid Build Coastguard Worker    return self._original_content
626*1b3f573fSAndroid Build Coastguard Worker
627*1b3f573fSAndroid Build Coastguard Worker  @property
628*1b3f573fSAndroid Build Coastguard Worker  def processed_content(self):
629*1b3f573fSAndroid Build Coastguard Worker    return self._processed_content
630*1b3f573fSAndroid Build Coastguard Worker
631*1b3f573fSAndroid Build Coastguard Worker
632*1b3f573fSAndroid Build Coastguard Workerdef main(args):
633*1b3f573fSAndroid Build Coastguard Worker  usage = '%prog [OPTIONS] PATH ...'
634*1b3f573fSAndroid Build Coastguard Worker  description = (
635*1b3f573fSAndroid Build Coastguard Worker      'Processes PDDM directives in the given paths and write them back out.'
636*1b3f573fSAndroid Build Coastguard Worker  )
637*1b3f573fSAndroid Build Coastguard Worker  parser = optparse.OptionParser(usage=usage, description=description)
638*1b3f573fSAndroid Build Coastguard Worker  parser.add_option('--dry-run',
639*1b3f573fSAndroid Build Coastguard Worker                    default=False, action='store_true',
640*1b3f573fSAndroid Build Coastguard Worker                    help='Don\'t write back to the file(s), just report if the'
641*1b3f573fSAndroid Build Coastguard Worker                    ' contents needs an update and exit with a value of 1.')
642*1b3f573fSAndroid Build Coastguard Worker  parser.add_option('--verbose',
643*1b3f573fSAndroid Build Coastguard Worker                    default=False, action='store_true',
644*1b3f573fSAndroid Build Coastguard Worker                    help='Reports is a file is already current.')
645*1b3f573fSAndroid Build Coastguard Worker  parser.add_option('--collapse',
646*1b3f573fSAndroid Build Coastguard Worker                    default=False, action='store_true',
647*1b3f573fSAndroid Build Coastguard Worker                    help='Removes all the generated code.')
648*1b3f573fSAndroid Build Coastguard Worker  opts, extra_args = parser.parse_args(args)
649*1b3f573fSAndroid Build Coastguard Worker
650*1b3f573fSAndroid Build Coastguard Worker  if not extra_args:
651*1b3f573fSAndroid Build Coastguard Worker    parser.error('Need at least one file to process')
652*1b3f573fSAndroid Build Coastguard Worker
653*1b3f573fSAndroid Build Coastguard Worker  result = 0
654*1b3f573fSAndroid Build Coastguard Worker  for a_path in extra_args:
655*1b3f573fSAndroid Build Coastguard Worker    if not os.path.exists(a_path):
656*1b3f573fSAndroid Build Coastguard Worker      sys.stderr.write('ERROR: File not found: %s\n' % a_path)
657*1b3f573fSAndroid Build Coastguard Worker      return 100
658*1b3f573fSAndroid Build Coastguard Worker
659*1b3f573fSAndroid Build Coastguard Worker    def _ImportResolver(name):
660*1b3f573fSAndroid Build Coastguard Worker      # resolve based on the file being read.
661*1b3f573fSAndroid Build Coastguard Worker      a_dir = os.path.dirname(a_path)
662*1b3f573fSAndroid Build Coastguard Worker      import_path = os.path.join(a_dir, name)
663*1b3f573fSAndroid Build Coastguard Worker      if not os.path.exists(import_path):
664*1b3f573fSAndroid Build Coastguard Worker        return None
665*1b3f573fSAndroid Build Coastguard Worker      return open(import_path, 'r')
666*1b3f573fSAndroid Build Coastguard Worker
667*1b3f573fSAndroid Build Coastguard Worker    with open(a_path, 'r') as f:
668*1b3f573fSAndroid Build Coastguard Worker      src_file = SourceFile(f, _ImportResolver)
669*1b3f573fSAndroid Build Coastguard Worker
670*1b3f573fSAndroid Build Coastguard Worker    try:
671*1b3f573fSAndroid Build Coastguard Worker      src_file.ProcessContent(strip_expansion=opts.collapse)
672*1b3f573fSAndroid Build Coastguard Worker    except PDDMError as e:
673*1b3f573fSAndroid Build Coastguard Worker      sys.stderr.write('ERROR: %s\n...While processing "%s"\n' %
674*1b3f573fSAndroid Build Coastguard Worker                       (e.message, a_path))
675*1b3f573fSAndroid Build Coastguard Worker      return 101
676*1b3f573fSAndroid Build Coastguard Worker
677*1b3f573fSAndroid Build Coastguard Worker    if src_file.processed_content != src_file.original_content:
678*1b3f573fSAndroid Build Coastguard Worker      if not opts.dry_run:
679*1b3f573fSAndroid Build Coastguard Worker        print('Updating for "%s".' % a_path)
680*1b3f573fSAndroid Build Coastguard Worker        with open(a_path, 'w') as f:
681*1b3f573fSAndroid Build Coastguard Worker          f.write(src_file.processed_content)
682*1b3f573fSAndroid Build Coastguard Worker      else:
683*1b3f573fSAndroid Build Coastguard Worker        # Special result to indicate things need updating.
684*1b3f573fSAndroid Build Coastguard Worker        print('Update needed for "%s".' % a_path)
685*1b3f573fSAndroid Build Coastguard Worker        result = 1
686*1b3f573fSAndroid Build Coastguard Worker    elif opts.verbose:
687*1b3f573fSAndroid Build Coastguard Worker      print('No update for "%s".' % a_path)
688*1b3f573fSAndroid Build Coastguard Worker
689*1b3f573fSAndroid Build Coastguard Worker  return result
690*1b3f573fSAndroid Build Coastguard Worker
691*1b3f573fSAndroid Build Coastguard Worker
692*1b3f573fSAndroid Build Coastguard Workerif __name__ == '__main__':
693*1b3f573fSAndroid Build Coastguard Worker  sys.exit(main(sys.argv[1:]))
694