xref: /aosp_15_r20/external/angle/build/gn_helpers.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1*8975f5c5SAndroid Build Coastguard Worker# Copyright 2014 The Chromium Authors
2*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
3*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file.
4*8975f5c5SAndroid Build Coastguard Worker
5*8975f5c5SAndroid Build Coastguard Worker"""Helper functions useful when writing scripts that integrate with GN.
6*8975f5c5SAndroid Build Coastguard Worker
7*8975f5c5SAndroid Build Coastguard WorkerThe main functions are ToGNString() and FromGNString(), to convert between
8*8975f5c5SAndroid Build Coastguard Workerserialized GN veriables and Python variables.
9*8975f5c5SAndroid Build Coastguard Worker
10*8975f5c5SAndroid Build Coastguard WorkerTo use in an arbitrary Python file in the build:
11*8975f5c5SAndroid Build Coastguard Worker
12*8975f5c5SAndroid Build Coastguard Worker  import os
13*8975f5c5SAndroid Build Coastguard Worker  import sys
14*8975f5c5SAndroid Build Coastguard Worker
15*8975f5c5SAndroid Build Coastguard Worker  sys.path.append(os.path.join(os.path.dirname(__file__),
16*8975f5c5SAndroid Build Coastguard Worker                               os.pardir, os.pardir, 'build'))
17*8975f5c5SAndroid Build Coastguard Worker  import gn_helpers
18*8975f5c5SAndroid Build Coastguard Worker
19*8975f5c5SAndroid Build Coastguard WorkerWhere the sequence of parameters to join is the relative path from your source
20*8975f5c5SAndroid Build Coastguard Workerfile to the build directory.
21*8975f5c5SAndroid Build Coastguard Worker"""
22*8975f5c5SAndroid Build Coastguard Worker
23*8975f5c5SAndroid Build Coastguard Workerimport json
24*8975f5c5SAndroid Build Coastguard Workerimport os
25*8975f5c5SAndroid Build Coastguard Workerimport re
26*8975f5c5SAndroid Build Coastguard Workerimport shutil
27*8975f5c5SAndroid Build Coastguard Workerimport sys
28*8975f5c5SAndroid Build Coastguard Worker
29*8975f5c5SAndroid Build Coastguard Worker
30*8975f5c5SAndroid Build Coastguard Worker_CHROMIUM_ROOT = os.path.abspath(
31*8975f5c5SAndroid Build Coastguard Worker    os.path.join(os.path.dirname(__file__), os.pardir))
32*8975f5c5SAndroid Build Coastguard Worker
33*8975f5c5SAndroid Build Coastguard WorkerBUILD_VARS_FILENAME = 'build_vars.json'
34*8975f5c5SAndroid Build Coastguard WorkerIMPORT_RE = re.compile(r'^import\("//(\S+)"\)')
35*8975f5c5SAndroid Build Coastguard Worker
36*8975f5c5SAndroid Build Coastguard Worker
37*8975f5c5SAndroid Build Coastguard Workerclass GNError(Exception):
38*8975f5c5SAndroid Build Coastguard Worker  pass
39*8975f5c5SAndroid Build Coastguard Worker
40*8975f5c5SAndroid Build Coastguard Worker
41*8975f5c5SAndroid Build Coastguard Worker# Computes ASCII code of an element of encoded Python 2 str / Python 3 bytes.
42*8975f5c5SAndroid Build Coastguard Worker_Ord = ord if sys.version_info.major < 3 else lambda c: c
43*8975f5c5SAndroid Build Coastguard Worker
44*8975f5c5SAndroid Build Coastguard Worker
45*8975f5c5SAndroid Build Coastguard Workerdef _TranslateToGnChars(s):
46*8975f5c5SAndroid Build Coastguard Worker  for decoded_ch in s.encode('utf-8'):  # str in Python 2, bytes in Python 3.
47*8975f5c5SAndroid Build Coastguard Worker    code = _Ord(decoded_ch)  # int
48*8975f5c5SAndroid Build Coastguard Worker    if code in (34, 36, 92):  # For '"', '$', or '\\'.
49*8975f5c5SAndroid Build Coastguard Worker      yield '\\' + chr(code)
50*8975f5c5SAndroid Build Coastguard Worker    elif 32 <= code < 127:
51*8975f5c5SAndroid Build Coastguard Worker      yield chr(code)
52*8975f5c5SAndroid Build Coastguard Worker    else:
53*8975f5c5SAndroid Build Coastguard Worker      yield '$0x%02X' % code
54*8975f5c5SAndroid Build Coastguard Worker
55*8975f5c5SAndroid Build Coastguard Worker
56*8975f5c5SAndroid Build Coastguard Workerdef ToGNString(value, pretty=False):
57*8975f5c5SAndroid Build Coastguard Worker  """Returns a stringified GN equivalent of a Python value.
58*8975f5c5SAndroid Build Coastguard Worker
59*8975f5c5SAndroid Build Coastguard Worker  Args:
60*8975f5c5SAndroid Build Coastguard Worker    value: The Python value to convert.
61*8975f5c5SAndroid Build Coastguard Worker    pretty: Whether to pretty print. If true, then non-empty lists are rendered
62*8975f5c5SAndroid Build Coastguard Worker        recursively with one item per line, with indents. Otherwise lists are
63*8975f5c5SAndroid Build Coastguard Worker        rendered without new line.
64*8975f5c5SAndroid Build Coastguard Worker  Returns:
65*8975f5c5SAndroid Build Coastguard Worker    The stringified GN equivalent to |value|.
66*8975f5c5SAndroid Build Coastguard Worker
67*8975f5c5SAndroid Build Coastguard Worker  Raises:
68*8975f5c5SAndroid Build Coastguard Worker    GNError: |value| cannot be printed to GN.
69*8975f5c5SAndroid Build Coastguard Worker  """
70*8975f5c5SAndroid Build Coastguard Worker
71*8975f5c5SAndroid Build Coastguard Worker  if sys.version_info.major < 3:
72*8975f5c5SAndroid Build Coastguard Worker    basestring_compat = basestring
73*8975f5c5SAndroid Build Coastguard Worker  else:
74*8975f5c5SAndroid Build Coastguard Worker    basestring_compat = str
75*8975f5c5SAndroid Build Coastguard Worker
76*8975f5c5SAndroid Build Coastguard Worker  # Emits all output tokens without intervening whitespaces.
77*8975f5c5SAndroid Build Coastguard Worker  def GenerateTokens(v, level):
78*8975f5c5SAndroid Build Coastguard Worker    if isinstance(v, basestring_compat):
79*8975f5c5SAndroid Build Coastguard Worker      yield '"' + ''.join(_TranslateToGnChars(v)) + '"'
80*8975f5c5SAndroid Build Coastguard Worker
81*8975f5c5SAndroid Build Coastguard Worker    elif isinstance(v, bool):
82*8975f5c5SAndroid Build Coastguard Worker      yield 'true' if v else 'false'
83*8975f5c5SAndroid Build Coastguard Worker
84*8975f5c5SAndroid Build Coastguard Worker    elif isinstance(v, int):
85*8975f5c5SAndroid Build Coastguard Worker      yield str(v)
86*8975f5c5SAndroid Build Coastguard Worker
87*8975f5c5SAndroid Build Coastguard Worker    elif isinstance(v, list):
88*8975f5c5SAndroid Build Coastguard Worker      yield '['
89*8975f5c5SAndroid Build Coastguard Worker      for i, item in enumerate(v):
90*8975f5c5SAndroid Build Coastguard Worker        if i > 0:
91*8975f5c5SAndroid Build Coastguard Worker          yield ','
92*8975f5c5SAndroid Build Coastguard Worker        for tok in GenerateTokens(item, level + 1):
93*8975f5c5SAndroid Build Coastguard Worker          yield tok
94*8975f5c5SAndroid Build Coastguard Worker      yield ']'
95*8975f5c5SAndroid Build Coastguard Worker
96*8975f5c5SAndroid Build Coastguard Worker    elif isinstance(v, dict):
97*8975f5c5SAndroid Build Coastguard Worker      if level > 0:
98*8975f5c5SAndroid Build Coastguard Worker        yield '{'
99*8975f5c5SAndroid Build Coastguard Worker      for key in sorted(v):
100*8975f5c5SAndroid Build Coastguard Worker        if not isinstance(key, basestring_compat):
101*8975f5c5SAndroid Build Coastguard Worker          raise GNError('Dictionary key is not a string.')
102*8975f5c5SAndroid Build Coastguard Worker        if not key or key[0].isdigit() or not key.replace('_', '').isalnum():
103*8975f5c5SAndroid Build Coastguard Worker          raise GNError('Dictionary key is not a valid GN identifier.')
104*8975f5c5SAndroid Build Coastguard Worker        yield key  # No quotations.
105*8975f5c5SAndroid Build Coastguard Worker        yield '='
106*8975f5c5SAndroid Build Coastguard Worker        for tok in GenerateTokens(v[key], level + 1):
107*8975f5c5SAndroid Build Coastguard Worker          yield tok
108*8975f5c5SAndroid Build Coastguard Worker      if level > 0:
109*8975f5c5SAndroid Build Coastguard Worker        yield '}'
110*8975f5c5SAndroid Build Coastguard Worker
111*8975f5c5SAndroid Build Coastguard Worker    else:  # Not supporting float: Add only when needed.
112*8975f5c5SAndroid Build Coastguard Worker      raise GNError('Unsupported type when printing to GN.')
113*8975f5c5SAndroid Build Coastguard Worker
114*8975f5c5SAndroid Build Coastguard Worker  can_start = lambda tok: tok and tok not in ',}]='
115*8975f5c5SAndroid Build Coastguard Worker  can_end = lambda tok: tok and tok not in ',{[='
116*8975f5c5SAndroid Build Coastguard Worker
117*8975f5c5SAndroid Build Coastguard Worker  # Adds whitespaces, trying to keep everything (except dicts) in 1 line.
118*8975f5c5SAndroid Build Coastguard Worker  def PlainGlue(gen):
119*8975f5c5SAndroid Build Coastguard Worker    prev_tok = None
120*8975f5c5SAndroid Build Coastguard Worker    for i, tok in enumerate(gen):
121*8975f5c5SAndroid Build Coastguard Worker      if i > 0:
122*8975f5c5SAndroid Build Coastguard Worker        if can_end(prev_tok) and can_start(tok):
123*8975f5c5SAndroid Build Coastguard Worker          yield '\n'  # New dict item.
124*8975f5c5SAndroid Build Coastguard Worker        elif prev_tok == '[' and tok == ']':
125*8975f5c5SAndroid Build Coastguard Worker          yield '  '  # Special case for [].
126*8975f5c5SAndroid Build Coastguard Worker        elif tok != ',':
127*8975f5c5SAndroid Build Coastguard Worker          yield ' '
128*8975f5c5SAndroid Build Coastguard Worker      yield tok
129*8975f5c5SAndroid Build Coastguard Worker      prev_tok = tok
130*8975f5c5SAndroid Build Coastguard Worker
131*8975f5c5SAndroid Build Coastguard Worker  # Adds whitespaces so non-empty lists can span multiple lines, with indent.
132*8975f5c5SAndroid Build Coastguard Worker  def PrettyGlue(gen):
133*8975f5c5SAndroid Build Coastguard Worker    prev_tok = None
134*8975f5c5SAndroid Build Coastguard Worker    level = 0
135*8975f5c5SAndroid Build Coastguard Worker    for i, tok in enumerate(gen):
136*8975f5c5SAndroid Build Coastguard Worker      if i > 0:
137*8975f5c5SAndroid Build Coastguard Worker        if can_end(prev_tok) and can_start(tok):
138*8975f5c5SAndroid Build Coastguard Worker          yield '\n' + '  ' * level  # New dict item.
139*8975f5c5SAndroid Build Coastguard Worker        elif tok == '=' or prev_tok in '=':
140*8975f5c5SAndroid Build Coastguard Worker          yield ' '  # Separator before and after '=', on same line.
141*8975f5c5SAndroid Build Coastguard Worker      if tok in ']}':
142*8975f5c5SAndroid Build Coastguard Worker        level -= 1
143*8975f5c5SAndroid Build Coastguard Worker      # Exclude '[]' and '{}' cases.
144*8975f5c5SAndroid Build Coastguard Worker      if int(prev_tok == '[') + int(tok == ']') == 1 or \
145*8975f5c5SAndroid Build Coastguard Worker         int(prev_tok == '{') + int(tok == '}') == 1:
146*8975f5c5SAndroid Build Coastguard Worker        yield '\n' + '  ' * level
147*8975f5c5SAndroid Build Coastguard Worker      yield tok
148*8975f5c5SAndroid Build Coastguard Worker      if tok in '[{':
149*8975f5c5SAndroid Build Coastguard Worker        level += 1
150*8975f5c5SAndroid Build Coastguard Worker      if tok == ',':
151*8975f5c5SAndroid Build Coastguard Worker        yield '\n' + '  ' * level
152*8975f5c5SAndroid Build Coastguard Worker      prev_tok = tok
153*8975f5c5SAndroid Build Coastguard Worker
154*8975f5c5SAndroid Build Coastguard Worker  token_gen = GenerateTokens(value, 0)
155*8975f5c5SAndroid Build Coastguard Worker  ret = ''.join((PrettyGlue if pretty else PlainGlue)(token_gen))
156*8975f5c5SAndroid Build Coastguard Worker  # Add terminating '\n' for dict |value| or multi-line output.
157*8975f5c5SAndroid Build Coastguard Worker  if isinstance(value, dict) or '\n' in ret:
158*8975f5c5SAndroid Build Coastguard Worker    return ret + '\n'
159*8975f5c5SAndroid Build Coastguard Worker  return ret
160*8975f5c5SAndroid Build Coastguard Worker
161*8975f5c5SAndroid Build Coastguard Worker
162*8975f5c5SAndroid Build Coastguard Workerdef FromGNString(input_string):
163*8975f5c5SAndroid Build Coastguard Worker  """Converts the input string from a GN serialized value to Python values.
164*8975f5c5SAndroid Build Coastguard Worker
165*8975f5c5SAndroid Build Coastguard Worker  For details on supported types see GNValueParser.Parse() below.
166*8975f5c5SAndroid Build Coastguard Worker
167*8975f5c5SAndroid Build Coastguard Worker  If your GN script did:
168*8975f5c5SAndroid Build Coastguard Worker    something = [ "file1", "file2" ]
169*8975f5c5SAndroid Build Coastguard Worker    args = [ "--values=$something" ]
170*8975f5c5SAndroid Build Coastguard Worker  The command line would look something like:
171*8975f5c5SAndroid Build Coastguard Worker    --values="[ \"file1\", \"file2\" ]"
172*8975f5c5SAndroid Build Coastguard Worker  Which when interpreted as a command line gives the value:
173*8975f5c5SAndroid Build Coastguard Worker    [ "file1", "file2" ]
174*8975f5c5SAndroid Build Coastguard Worker
175*8975f5c5SAndroid Build Coastguard Worker  You can parse this into a Python list using GN rules with:
176*8975f5c5SAndroid Build Coastguard Worker    input_values = FromGNValues(options.values)
177*8975f5c5SAndroid Build Coastguard Worker  Although the Python 'ast' module will parse many forms of such input, it
178*8975f5c5SAndroid Build Coastguard Worker  will not handle GN escaping properly, nor GN booleans. You should use this
179*8975f5c5SAndroid Build Coastguard Worker  function instead.
180*8975f5c5SAndroid Build Coastguard Worker
181*8975f5c5SAndroid Build Coastguard Worker
182*8975f5c5SAndroid Build Coastguard Worker  A NOTE ON STRING HANDLING:
183*8975f5c5SAndroid Build Coastguard Worker
184*8975f5c5SAndroid Build Coastguard Worker  If you just pass a string on the command line to your Python script, or use
185*8975f5c5SAndroid Build Coastguard Worker  string interpolation on a string variable, the strings will not be quoted:
186*8975f5c5SAndroid Build Coastguard Worker    str = "asdf"
187*8975f5c5SAndroid Build Coastguard Worker    args = [ str, "--value=$str" ]
188*8975f5c5SAndroid Build Coastguard Worker  Will yield the command line:
189*8975f5c5SAndroid Build Coastguard Worker    asdf --value=asdf
190*8975f5c5SAndroid Build Coastguard Worker  The unquoted asdf string will not be valid input to this function, which
191*8975f5c5SAndroid Build Coastguard Worker  accepts only quoted strings like GN scripts. In such cases, you can just use
192*8975f5c5SAndroid Build Coastguard Worker  the Python string literal directly.
193*8975f5c5SAndroid Build Coastguard Worker
194*8975f5c5SAndroid Build Coastguard Worker  The main use cases for this is for other types, in particular lists. When
195*8975f5c5SAndroid Build Coastguard Worker  using string interpolation on a list (as in the top example) the embedded
196*8975f5c5SAndroid Build Coastguard Worker  strings will be quoted and escaped according to GN rules so the list can be
197*8975f5c5SAndroid Build Coastguard Worker  re-parsed to get the same result.
198*8975f5c5SAndroid Build Coastguard Worker  """
199*8975f5c5SAndroid Build Coastguard Worker  parser = GNValueParser(input_string)
200*8975f5c5SAndroid Build Coastguard Worker  return parser.Parse()
201*8975f5c5SAndroid Build Coastguard Worker
202*8975f5c5SAndroid Build Coastguard Worker
203*8975f5c5SAndroid Build Coastguard Workerdef FromGNArgs(input_string):
204*8975f5c5SAndroid Build Coastguard Worker  """Converts a string with a bunch of gn arg assignments into a Python dict.
205*8975f5c5SAndroid Build Coastguard Worker
206*8975f5c5SAndroid Build Coastguard Worker  Given a whitespace-separated list of
207*8975f5c5SAndroid Build Coastguard Worker
208*8975f5c5SAndroid Build Coastguard Worker    <ident> = (integer | string | boolean | <list of the former>)
209*8975f5c5SAndroid Build Coastguard Worker
210*8975f5c5SAndroid Build Coastguard Worker  gn assignments, this returns a Python dict, i.e.:
211*8975f5c5SAndroid Build Coastguard Worker
212*8975f5c5SAndroid Build Coastguard Worker    FromGNArgs('foo=true\nbar=1\n') -> { 'foo': True, 'bar': 1 }.
213*8975f5c5SAndroid Build Coastguard Worker
214*8975f5c5SAndroid Build Coastguard Worker  Only simple types and lists supported; variables, structs, calls
215*8975f5c5SAndroid Build Coastguard Worker  and other, more complicated things are not.
216*8975f5c5SAndroid Build Coastguard Worker
217*8975f5c5SAndroid Build Coastguard Worker  This routine is meant to handle only the simple sorts of values that
218*8975f5c5SAndroid Build Coastguard Worker  arise in parsing --args.
219*8975f5c5SAndroid Build Coastguard Worker  """
220*8975f5c5SAndroid Build Coastguard Worker  parser = GNValueParser(input_string)
221*8975f5c5SAndroid Build Coastguard Worker  return parser.ParseArgs()
222*8975f5c5SAndroid Build Coastguard Worker
223*8975f5c5SAndroid Build Coastguard Worker
224*8975f5c5SAndroid Build Coastguard Workerdef UnescapeGNString(value):
225*8975f5c5SAndroid Build Coastguard Worker  """Given a string with GN escaping, returns the unescaped string.
226*8975f5c5SAndroid Build Coastguard Worker
227*8975f5c5SAndroid Build Coastguard Worker  Be careful not to feed with input from a Python parsing function like
228*8975f5c5SAndroid Build Coastguard Worker  'ast' because it will do Python unescaping, which will be incorrect when
229*8975f5c5SAndroid Build Coastguard Worker  fed into the GN unescaper.
230*8975f5c5SAndroid Build Coastguard Worker
231*8975f5c5SAndroid Build Coastguard Worker  Args:
232*8975f5c5SAndroid Build Coastguard Worker    value: Input string to unescape.
233*8975f5c5SAndroid Build Coastguard Worker  """
234*8975f5c5SAndroid Build Coastguard Worker  result = ''
235*8975f5c5SAndroid Build Coastguard Worker  i = 0
236*8975f5c5SAndroid Build Coastguard Worker  while i < len(value):
237*8975f5c5SAndroid Build Coastguard Worker    if value[i] == '\\':
238*8975f5c5SAndroid Build Coastguard Worker      if i < len(value) - 1:
239*8975f5c5SAndroid Build Coastguard Worker        next_char = value[i + 1]
240*8975f5c5SAndroid Build Coastguard Worker        if next_char in ('$', '"', '\\'):
241*8975f5c5SAndroid Build Coastguard Worker          # These are the escaped characters GN supports.
242*8975f5c5SAndroid Build Coastguard Worker          result += next_char
243*8975f5c5SAndroid Build Coastguard Worker          i += 1
244*8975f5c5SAndroid Build Coastguard Worker        else:
245*8975f5c5SAndroid Build Coastguard Worker          # Any other backslash is a literal.
246*8975f5c5SAndroid Build Coastguard Worker          result += '\\'
247*8975f5c5SAndroid Build Coastguard Worker    else:
248*8975f5c5SAndroid Build Coastguard Worker      result += value[i]
249*8975f5c5SAndroid Build Coastguard Worker    i += 1
250*8975f5c5SAndroid Build Coastguard Worker  return result
251*8975f5c5SAndroid Build Coastguard Worker
252*8975f5c5SAndroid Build Coastguard Worker
253*8975f5c5SAndroid Build Coastguard Workerdef _IsDigitOrMinus(char):
254*8975f5c5SAndroid Build Coastguard Worker  return char in '-0123456789'
255*8975f5c5SAndroid Build Coastguard Worker
256*8975f5c5SAndroid Build Coastguard Worker
257*8975f5c5SAndroid Build Coastguard Workerclass GNValueParser(object):
258*8975f5c5SAndroid Build Coastguard Worker  """Duplicates GN parsing of values and converts to Python types.
259*8975f5c5SAndroid Build Coastguard Worker
260*8975f5c5SAndroid Build Coastguard Worker  Normally you would use the wrapper function FromGNValue() below.
261*8975f5c5SAndroid Build Coastguard Worker
262*8975f5c5SAndroid Build Coastguard Worker  If you expect input as a specific type, you can also call one of the Parse*
263*8975f5c5SAndroid Build Coastguard Worker  functions directly. All functions throw GNError on invalid input.
264*8975f5c5SAndroid Build Coastguard Worker  """
265*8975f5c5SAndroid Build Coastguard Worker
266*8975f5c5SAndroid Build Coastguard Worker  def __init__(self, string, checkout_root=_CHROMIUM_ROOT):
267*8975f5c5SAndroid Build Coastguard Worker    self.input = string
268*8975f5c5SAndroid Build Coastguard Worker    self.cur = 0
269*8975f5c5SAndroid Build Coastguard Worker    self.checkout_root = checkout_root
270*8975f5c5SAndroid Build Coastguard Worker
271*8975f5c5SAndroid Build Coastguard Worker  def IsDone(self):
272*8975f5c5SAndroid Build Coastguard Worker    return self.cur == len(self.input)
273*8975f5c5SAndroid Build Coastguard Worker
274*8975f5c5SAndroid Build Coastguard Worker  def ReplaceImports(self):
275*8975f5c5SAndroid Build Coastguard Worker    """Replaces import(...) lines with the contents of the imports.
276*8975f5c5SAndroid Build Coastguard Worker
277*8975f5c5SAndroid Build Coastguard Worker    Recurses on itself until there are no imports remaining, in the case of
278*8975f5c5SAndroid Build Coastguard Worker    nested imports.
279*8975f5c5SAndroid Build Coastguard Worker    """
280*8975f5c5SAndroid Build Coastguard Worker    lines = self.input.splitlines()
281*8975f5c5SAndroid Build Coastguard Worker    if not any(line.startswith('import(') for line in lines):
282*8975f5c5SAndroid Build Coastguard Worker      return
283*8975f5c5SAndroid Build Coastguard Worker    for line in lines:
284*8975f5c5SAndroid Build Coastguard Worker      if not line.startswith('import('):
285*8975f5c5SAndroid Build Coastguard Worker        continue
286*8975f5c5SAndroid Build Coastguard Worker      regex_match = IMPORT_RE.match(line)
287*8975f5c5SAndroid Build Coastguard Worker      if not regex_match:
288*8975f5c5SAndroid Build Coastguard Worker        raise GNError('Not a valid import string: %s' % line)
289*8975f5c5SAndroid Build Coastguard Worker      import_path = os.path.join(self.checkout_root, regex_match.group(1))
290*8975f5c5SAndroid Build Coastguard Worker      with open(import_path) as f:
291*8975f5c5SAndroid Build Coastguard Worker        imported_args = f.read()
292*8975f5c5SAndroid Build Coastguard Worker      self.input = self.input.replace(line, imported_args)
293*8975f5c5SAndroid Build Coastguard Worker    # Call ourselves again if we've just replaced an import() with additional
294*8975f5c5SAndroid Build Coastguard Worker    # imports.
295*8975f5c5SAndroid Build Coastguard Worker    self.ReplaceImports()
296*8975f5c5SAndroid Build Coastguard Worker
297*8975f5c5SAndroid Build Coastguard Worker
298*8975f5c5SAndroid Build Coastguard Worker  def _ConsumeWhitespace(self):
299*8975f5c5SAndroid Build Coastguard Worker    while not self.IsDone() and self.input[self.cur] in ' \t\n':
300*8975f5c5SAndroid Build Coastguard Worker      self.cur += 1
301*8975f5c5SAndroid Build Coastguard Worker
302*8975f5c5SAndroid Build Coastguard Worker  def ConsumeCommentAndWhitespace(self):
303*8975f5c5SAndroid Build Coastguard Worker    self._ConsumeWhitespace()
304*8975f5c5SAndroid Build Coastguard Worker
305*8975f5c5SAndroid Build Coastguard Worker    # Consume each comment, line by line.
306*8975f5c5SAndroid Build Coastguard Worker    while not self.IsDone() and self.input[self.cur] == '#':
307*8975f5c5SAndroid Build Coastguard Worker      # Consume the rest of the comment, up until the end of the line.
308*8975f5c5SAndroid Build Coastguard Worker      while not self.IsDone() and self.input[self.cur] != '\n':
309*8975f5c5SAndroid Build Coastguard Worker        self.cur += 1
310*8975f5c5SAndroid Build Coastguard Worker      # Move the cursor to the next line (if there is one).
311*8975f5c5SAndroid Build Coastguard Worker      if not self.IsDone():
312*8975f5c5SAndroid Build Coastguard Worker        self.cur += 1
313*8975f5c5SAndroid Build Coastguard Worker
314*8975f5c5SAndroid Build Coastguard Worker      self._ConsumeWhitespace()
315*8975f5c5SAndroid Build Coastguard Worker
316*8975f5c5SAndroid Build Coastguard Worker  def Parse(self):
317*8975f5c5SAndroid Build Coastguard Worker    """Converts a string representing a printed GN value to the Python type.
318*8975f5c5SAndroid Build Coastguard Worker
319*8975f5c5SAndroid Build Coastguard Worker    See additional usage notes on FromGNString() above.
320*8975f5c5SAndroid Build Coastguard Worker
321*8975f5c5SAndroid Build Coastguard Worker    * GN booleans ('true', 'false') will be converted to Python booleans.
322*8975f5c5SAndroid Build Coastguard Worker
323*8975f5c5SAndroid Build Coastguard Worker    * GN numbers ('123') will be converted to Python numbers.
324*8975f5c5SAndroid Build Coastguard Worker
325*8975f5c5SAndroid Build Coastguard Worker    * GN strings (double-quoted as in '"asdf"') will be converted to Python
326*8975f5c5SAndroid Build Coastguard Worker      strings with GN escaping rules. GN string interpolation (embedded
327*8975f5c5SAndroid Build Coastguard Worker      variables preceded by $) are not supported and will be returned as
328*8975f5c5SAndroid Build Coastguard Worker      literals.
329*8975f5c5SAndroid Build Coastguard Worker
330*8975f5c5SAndroid Build Coastguard Worker    * GN lists ('[1, "asdf", 3]') will be converted to Python lists.
331*8975f5c5SAndroid Build Coastguard Worker
332*8975f5c5SAndroid Build Coastguard Worker    * GN scopes ('{ ... }') are not supported.
333*8975f5c5SAndroid Build Coastguard Worker
334*8975f5c5SAndroid Build Coastguard Worker    Raises:
335*8975f5c5SAndroid Build Coastguard Worker      GNError: Parse fails.
336*8975f5c5SAndroid Build Coastguard Worker    """
337*8975f5c5SAndroid Build Coastguard Worker    result = self._ParseAllowTrailing()
338*8975f5c5SAndroid Build Coastguard Worker    self.ConsumeCommentAndWhitespace()
339*8975f5c5SAndroid Build Coastguard Worker    if not self.IsDone():
340*8975f5c5SAndroid Build Coastguard Worker      raise GNError("Trailing input after parsing:\n  " + self.input[self.cur:])
341*8975f5c5SAndroid Build Coastguard Worker    return result
342*8975f5c5SAndroid Build Coastguard Worker
343*8975f5c5SAndroid Build Coastguard Worker  def ParseArgs(self):
344*8975f5c5SAndroid Build Coastguard Worker    """Converts a whitespace-separated list of ident=literals to a dict.
345*8975f5c5SAndroid Build Coastguard Worker
346*8975f5c5SAndroid Build Coastguard Worker    See additional usage notes on FromGNArgs(), above.
347*8975f5c5SAndroid Build Coastguard Worker
348*8975f5c5SAndroid Build Coastguard Worker    Raises:
349*8975f5c5SAndroid Build Coastguard Worker      GNError: Parse fails.
350*8975f5c5SAndroid Build Coastguard Worker    """
351*8975f5c5SAndroid Build Coastguard Worker    d = {}
352*8975f5c5SAndroid Build Coastguard Worker
353*8975f5c5SAndroid Build Coastguard Worker    self.ReplaceImports()
354*8975f5c5SAndroid Build Coastguard Worker    self.ConsumeCommentAndWhitespace()
355*8975f5c5SAndroid Build Coastguard Worker
356*8975f5c5SAndroid Build Coastguard Worker    while not self.IsDone():
357*8975f5c5SAndroid Build Coastguard Worker      ident = self._ParseIdent()
358*8975f5c5SAndroid Build Coastguard Worker      self.ConsumeCommentAndWhitespace()
359*8975f5c5SAndroid Build Coastguard Worker      if self.input[self.cur] != '=':
360*8975f5c5SAndroid Build Coastguard Worker        raise GNError("Unexpected token: " + self.input[self.cur:])
361*8975f5c5SAndroid Build Coastguard Worker      self.cur += 1
362*8975f5c5SAndroid Build Coastguard Worker      self.ConsumeCommentAndWhitespace()
363*8975f5c5SAndroid Build Coastguard Worker      val = self._ParseAllowTrailing()
364*8975f5c5SAndroid Build Coastguard Worker      self.ConsumeCommentAndWhitespace()
365*8975f5c5SAndroid Build Coastguard Worker      d[ident] = val
366*8975f5c5SAndroid Build Coastguard Worker
367*8975f5c5SAndroid Build Coastguard Worker    return d
368*8975f5c5SAndroid Build Coastguard Worker
369*8975f5c5SAndroid Build Coastguard Worker  def _ParseAllowTrailing(self):
370*8975f5c5SAndroid Build Coastguard Worker    """Internal version of Parse() that doesn't check for trailing stuff."""
371*8975f5c5SAndroid Build Coastguard Worker    self.ConsumeCommentAndWhitespace()
372*8975f5c5SAndroid Build Coastguard Worker    if self.IsDone():
373*8975f5c5SAndroid Build Coastguard Worker      raise GNError("Expected input to parse.")
374*8975f5c5SAndroid Build Coastguard Worker
375*8975f5c5SAndroid Build Coastguard Worker    next_char = self.input[self.cur]
376*8975f5c5SAndroid Build Coastguard Worker    if next_char == '[':
377*8975f5c5SAndroid Build Coastguard Worker      return self.ParseList()
378*8975f5c5SAndroid Build Coastguard Worker    elif next_char == '{':
379*8975f5c5SAndroid Build Coastguard Worker      return self.ParseScope()
380*8975f5c5SAndroid Build Coastguard Worker    elif _IsDigitOrMinus(next_char):
381*8975f5c5SAndroid Build Coastguard Worker      return self.ParseNumber()
382*8975f5c5SAndroid Build Coastguard Worker    elif next_char == '"':
383*8975f5c5SAndroid Build Coastguard Worker      return self.ParseString()
384*8975f5c5SAndroid Build Coastguard Worker    elif self._ConstantFollows('true'):
385*8975f5c5SAndroid Build Coastguard Worker      return True
386*8975f5c5SAndroid Build Coastguard Worker    elif self._ConstantFollows('false'):
387*8975f5c5SAndroid Build Coastguard Worker      return False
388*8975f5c5SAndroid Build Coastguard Worker    else:
389*8975f5c5SAndroid Build Coastguard Worker      raise GNError("Unexpected token: " + self.input[self.cur:])
390*8975f5c5SAndroid Build Coastguard Worker
391*8975f5c5SAndroid Build Coastguard Worker  def _ParseIdent(self):
392*8975f5c5SAndroid Build Coastguard Worker    ident = ''
393*8975f5c5SAndroid Build Coastguard Worker
394*8975f5c5SAndroid Build Coastguard Worker    next_char = self.input[self.cur]
395*8975f5c5SAndroid Build Coastguard Worker    if not next_char.isalpha() and not next_char=='_':
396*8975f5c5SAndroid Build Coastguard Worker      raise GNError("Expected an identifier: " + self.input[self.cur:])
397*8975f5c5SAndroid Build Coastguard Worker
398*8975f5c5SAndroid Build Coastguard Worker    ident += next_char
399*8975f5c5SAndroid Build Coastguard Worker    self.cur += 1
400*8975f5c5SAndroid Build Coastguard Worker
401*8975f5c5SAndroid Build Coastguard Worker    next_char = self.input[self.cur]
402*8975f5c5SAndroid Build Coastguard Worker    while next_char.isalpha() or next_char.isdigit() or next_char=='_':
403*8975f5c5SAndroid Build Coastguard Worker      ident += next_char
404*8975f5c5SAndroid Build Coastguard Worker      self.cur += 1
405*8975f5c5SAndroid Build Coastguard Worker      next_char = self.input[self.cur]
406*8975f5c5SAndroid Build Coastguard Worker
407*8975f5c5SAndroid Build Coastguard Worker    return ident
408*8975f5c5SAndroid Build Coastguard Worker
409*8975f5c5SAndroid Build Coastguard Worker  def ParseNumber(self):
410*8975f5c5SAndroid Build Coastguard Worker    self.ConsumeCommentAndWhitespace()
411*8975f5c5SAndroid Build Coastguard Worker    if self.IsDone():
412*8975f5c5SAndroid Build Coastguard Worker      raise GNError('Expected number but got nothing.')
413*8975f5c5SAndroid Build Coastguard Worker
414*8975f5c5SAndroid Build Coastguard Worker    begin = self.cur
415*8975f5c5SAndroid Build Coastguard Worker
416*8975f5c5SAndroid Build Coastguard Worker    # The first character can include a negative sign.
417*8975f5c5SAndroid Build Coastguard Worker    if not self.IsDone() and _IsDigitOrMinus(self.input[self.cur]):
418*8975f5c5SAndroid Build Coastguard Worker      self.cur += 1
419*8975f5c5SAndroid Build Coastguard Worker    while not self.IsDone() and self.input[self.cur].isdigit():
420*8975f5c5SAndroid Build Coastguard Worker      self.cur += 1
421*8975f5c5SAndroid Build Coastguard Worker
422*8975f5c5SAndroid Build Coastguard Worker    number_string = self.input[begin:self.cur]
423*8975f5c5SAndroid Build Coastguard Worker    if not len(number_string) or number_string == '-':
424*8975f5c5SAndroid Build Coastguard Worker      raise GNError('Not a valid number.')
425*8975f5c5SAndroid Build Coastguard Worker    return int(number_string)
426*8975f5c5SAndroid Build Coastguard Worker
427*8975f5c5SAndroid Build Coastguard Worker  def ParseString(self):
428*8975f5c5SAndroid Build Coastguard Worker    self.ConsumeCommentAndWhitespace()
429*8975f5c5SAndroid Build Coastguard Worker    if self.IsDone():
430*8975f5c5SAndroid Build Coastguard Worker      raise GNError('Expected string but got nothing.')
431*8975f5c5SAndroid Build Coastguard Worker
432*8975f5c5SAndroid Build Coastguard Worker    if self.input[self.cur] != '"':
433*8975f5c5SAndroid Build Coastguard Worker      raise GNError('Expected string beginning in a " but got:\n  ' +
434*8975f5c5SAndroid Build Coastguard Worker                    self.input[self.cur:])
435*8975f5c5SAndroid Build Coastguard Worker    self.cur += 1  # Skip over quote.
436*8975f5c5SAndroid Build Coastguard Worker
437*8975f5c5SAndroid Build Coastguard Worker    begin = self.cur
438*8975f5c5SAndroid Build Coastguard Worker    while not self.IsDone() and self.input[self.cur] != '"':
439*8975f5c5SAndroid Build Coastguard Worker      if self.input[self.cur] == '\\':
440*8975f5c5SAndroid Build Coastguard Worker        self.cur += 1  # Skip over the backslash.
441*8975f5c5SAndroid Build Coastguard Worker        if self.IsDone():
442*8975f5c5SAndroid Build Coastguard Worker          raise GNError('String ends in a backslash in:\n  ' + self.input)
443*8975f5c5SAndroid Build Coastguard Worker      self.cur += 1
444*8975f5c5SAndroid Build Coastguard Worker
445*8975f5c5SAndroid Build Coastguard Worker    if self.IsDone():
446*8975f5c5SAndroid Build Coastguard Worker      raise GNError('Unterminated string:\n  ' + self.input[begin:])
447*8975f5c5SAndroid Build Coastguard Worker
448*8975f5c5SAndroid Build Coastguard Worker    end = self.cur
449*8975f5c5SAndroid Build Coastguard Worker    self.cur += 1  # Consume trailing ".
450*8975f5c5SAndroid Build Coastguard Worker
451*8975f5c5SAndroid Build Coastguard Worker    return UnescapeGNString(self.input[begin:end])
452*8975f5c5SAndroid Build Coastguard Worker
453*8975f5c5SAndroid Build Coastguard Worker  def ParseList(self):
454*8975f5c5SAndroid Build Coastguard Worker    self.ConsumeCommentAndWhitespace()
455*8975f5c5SAndroid Build Coastguard Worker    if self.IsDone():
456*8975f5c5SAndroid Build Coastguard Worker      raise GNError('Expected list but got nothing.')
457*8975f5c5SAndroid Build Coastguard Worker
458*8975f5c5SAndroid Build Coastguard Worker    # Skip over opening '['.
459*8975f5c5SAndroid Build Coastguard Worker    if self.input[self.cur] != '[':
460*8975f5c5SAndroid Build Coastguard Worker      raise GNError('Expected [ for list but got:\n  ' + self.input[self.cur:])
461*8975f5c5SAndroid Build Coastguard Worker    self.cur += 1
462*8975f5c5SAndroid Build Coastguard Worker    self.ConsumeCommentAndWhitespace()
463*8975f5c5SAndroid Build Coastguard Worker    if self.IsDone():
464*8975f5c5SAndroid Build Coastguard Worker      raise GNError('Unterminated list:\n  ' + self.input)
465*8975f5c5SAndroid Build Coastguard Worker
466*8975f5c5SAndroid Build Coastguard Worker    list_result = []
467*8975f5c5SAndroid Build Coastguard Worker    previous_had_trailing_comma = True
468*8975f5c5SAndroid Build Coastguard Worker    while not self.IsDone():
469*8975f5c5SAndroid Build Coastguard Worker      if self.input[self.cur] == ']':
470*8975f5c5SAndroid Build Coastguard Worker        self.cur += 1  # Skip over ']'.
471*8975f5c5SAndroid Build Coastguard Worker        return list_result
472*8975f5c5SAndroid Build Coastguard Worker
473*8975f5c5SAndroid Build Coastguard Worker      if not previous_had_trailing_comma:
474*8975f5c5SAndroid Build Coastguard Worker        raise GNError('List items not separated by comma.')
475*8975f5c5SAndroid Build Coastguard Worker
476*8975f5c5SAndroid Build Coastguard Worker      list_result += [ self._ParseAllowTrailing() ]
477*8975f5c5SAndroid Build Coastguard Worker      self.ConsumeCommentAndWhitespace()
478*8975f5c5SAndroid Build Coastguard Worker      if self.IsDone():
479*8975f5c5SAndroid Build Coastguard Worker        break
480*8975f5c5SAndroid Build Coastguard Worker
481*8975f5c5SAndroid Build Coastguard Worker      # Consume comma if there is one.
482*8975f5c5SAndroid Build Coastguard Worker      previous_had_trailing_comma = self.input[self.cur] == ','
483*8975f5c5SAndroid Build Coastguard Worker      if previous_had_trailing_comma:
484*8975f5c5SAndroid Build Coastguard Worker        # Consume comma.
485*8975f5c5SAndroid Build Coastguard Worker        self.cur += 1
486*8975f5c5SAndroid Build Coastguard Worker        self.ConsumeCommentAndWhitespace()
487*8975f5c5SAndroid Build Coastguard Worker
488*8975f5c5SAndroid Build Coastguard Worker    raise GNError('Unterminated list:\n  ' + self.input)
489*8975f5c5SAndroid Build Coastguard Worker
490*8975f5c5SAndroid Build Coastguard Worker  def ParseScope(self):
491*8975f5c5SAndroid Build Coastguard Worker    self.ConsumeCommentAndWhitespace()
492*8975f5c5SAndroid Build Coastguard Worker    if self.IsDone():
493*8975f5c5SAndroid Build Coastguard Worker      raise GNError('Expected scope but got nothing.')
494*8975f5c5SAndroid Build Coastguard Worker
495*8975f5c5SAndroid Build Coastguard Worker    # Skip over opening '{'.
496*8975f5c5SAndroid Build Coastguard Worker    if self.input[self.cur] != '{':
497*8975f5c5SAndroid Build Coastguard Worker      raise GNError('Expected { for scope but got:\n ' + self.input[self.cur:])
498*8975f5c5SAndroid Build Coastguard Worker    self.cur += 1
499*8975f5c5SAndroid Build Coastguard Worker    self.ConsumeCommentAndWhitespace()
500*8975f5c5SAndroid Build Coastguard Worker    if self.IsDone():
501*8975f5c5SAndroid Build Coastguard Worker      raise GNError('Unterminated scope:\n ' + self.input)
502*8975f5c5SAndroid Build Coastguard Worker
503*8975f5c5SAndroid Build Coastguard Worker    scope_result = {}
504*8975f5c5SAndroid Build Coastguard Worker    while not self.IsDone():
505*8975f5c5SAndroid Build Coastguard Worker      if self.input[self.cur] == '}':
506*8975f5c5SAndroid Build Coastguard Worker        self.cur += 1
507*8975f5c5SAndroid Build Coastguard Worker        return scope_result
508*8975f5c5SAndroid Build Coastguard Worker
509*8975f5c5SAndroid Build Coastguard Worker      ident = self._ParseIdent()
510*8975f5c5SAndroid Build Coastguard Worker      self.ConsumeCommentAndWhitespace()
511*8975f5c5SAndroid Build Coastguard Worker      if self.input[self.cur] != '=':
512*8975f5c5SAndroid Build Coastguard Worker        raise GNError("Unexpected token: " + self.input[self.cur:])
513*8975f5c5SAndroid Build Coastguard Worker      self.cur += 1
514*8975f5c5SAndroid Build Coastguard Worker      self.ConsumeCommentAndWhitespace()
515*8975f5c5SAndroid Build Coastguard Worker      val = self._ParseAllowTrailing()
516*8975f5c5SAndroid Build Coastguard Worker      self.ConsumeCommentAndWhitespace()
517*8975f5c5SAndroid Build Coastguard Worker      scope_result[ident] = val
518*8975f5c5SAndroid Build Coastguard Worker
519*8975f5c5SAndroid Build Coastguard Worker    raise GNError('Unterminated scope:\n ' + self.input)
520*8975f5c5SAndroid Build Coastguard Worker
521*8975f5c5SAndroid Build Coastguard Worker  def _ConstantFollows(self, constant):
522*8975f5c5SAndroid Build Coastguard Worker    """Checks and maybe consumes a string constant at current input location.
523*8975f5c5SAndroid Build Coastguard Worker
524*8975f5c5SAndroid Build Coastguard Worker    Param:
525*8975f5c5SAndroid Build Coastguard Worker      constant: The string constant to check.
526*8975f5c5SAndroid Build Coastguard Worker
527*8975f5c5SAndroid Build Coastguard Worker    Returns:
528*8975f5c5SAndroid Build Coastguard Worker      True if |constant| follows immediately at the current location in the
529*8975f5c5SAndroid Build Coastguard Worker      input. In this case, the string is consumed as a side effect. Otherwise,
530*8975f5c5SAndroid Build Coastguard Worker      returns False and the current position is unchanged.
531*8975f5c5SAndroid Build Coastguard Worker    """
532*8975f5c5SAndroid Build Coastguard Worker    end = self.cur + len(constant)
533*8975f5c5SAndroid Build Coastguard Worker    if end > len(self.input):
534*8975f5c5SAndroid Build Coastguard Worker      return False  # Not enough room.
535*8975f5c5SAndroid Build Coastguard Worker    if self.input[self.cur:end] == constant:
536*8975f5c5SAndroid Build Coastguard Worker      self.cur = end
537*8975f5c5SAndroid Build Coastguard Worker      return True
538*8975f5c5SAndroid Build Coastguard Worker    return False
539*8975f5c5SAndroid Build Coastguard Worker
540*8975f5c5SAndroid Build Coastguard Worker
541*8975f5c5SAndroid Build Coastguard Workerdef ReadBuildVars(output_directory):
542*8975f5c5SAndroid Build Coastguard Worker  """Parses $output_directory/build_vars.json into a dict."""
543*8975f5c5SAndroid Build Coastguard Worker  with open(os.path.join(output_directory, BUILD_VARS_FILENAME)) as f:
544*8975f5c5SAndroid Build Coastguard Worker    return json.load(f)
545*8975f5c5SAndroid Build Coastguard Worker
546*8975f5c5SAndroid Build Coastguard Worker
547*8975f5c5SAndroid Build Coastguard Workerdef CreateBuildCommand(output_directory):
548*8975f5c5SAndroid Build Coastguard Worker  """Returns [cmd, -C, output_directory].
549*8975f5c5SAndroid Build Coastguard Worker
550*8975f5c5SAndroid Build Coastguard Worker  Where |cmd| is one of: siso ninja, ninja, or autoninja.
551*8975f5c5SAndroid Build Coastguard Worker  """
552*8975f5c5SAndroid Build Coastguard Worker  suffix = '.bat' if sys.platform.startswith('win32') else ''
553*8975f5c5SAndroid Build Coastguard Worker  # Prefer the version on PATH, but fallback to known version if PATH doesn't
554*8975f5c5SAndroid Build Coastguard Worker  # have one (e.g. on bots).
555*8975f5c5SAndroid Build Coastguard Worker  if not shutil.which(f'autoninja{suffix}'):
556*8975f5c5SAndroid Build Coastguard Worker    third_party_prefix = os.path.join(_CHROMIUM_ROOT, 'third_party')
557*8975f5c5SAndroid Build Coastguard Worker    ninja_prefix = os.path.join(third_party_prefix, 'ninja', '')
558*8975f5c5SAndroid Build Coastguard Worker    siso_prefix = os.path.join(third_party_prefix, 'siso', 'cipd', '')
559*8975f5c5SAndroid Build Coastguard Worker    # Also - bots configure reclient manually, and so do not use the "auto"
560*8975f5c5SAndroid Build Coastguard Worker    # wrappers.
561*8975f5c5SAndroid Build Coastguard Worker    ninja_cmd = [f'{ninja_prefix}ninja{suffix}']
562*8975f5c5SAndroid Build Coastguard Worker    siso_cmd = [f'{siso_prefix}siso{suffix}', 'ninja']
563*8975f5c5SAndroid Build Coastguard Worker  else:
564*8975f5c5SAndroid Build Coastguard Worker    ninja_cmd = [f'autoninja{suffix}']
565*8975f5c5SAndroid Build Coastguard Worker    siso_cmd = list(ninja_cmd)
566*8975f5c5SAndroid Build Coastguard Worker
567*8975f5c5SAndroid Build Coastguard Worker  if output_directory and os.path.abspath(output_directory) != os.path.abspath(
568*8975f5c5SAndroid Build Coastguard Worker      os.curdir):
569*8975f5c5SAndroid Build Coastguard Worker    ninja_cmd += ['-C', output_directory]
570*8975f5c5SAndroid Build Coastguard Worker    siso_cmd += ['-C', output_directory]
571*8975f5c5SAndroid Build Coastguard Worker  siso_deps = os.path.exists(os.path.join(output_directory, '.siso_deps'))
572*8975f5c5SAndroid Build Coastguard Worker  ninja_deps = os.path.exists(os.path.join(output_directory, '.ninja_deps'))
573*8975f5c5SAndroid Build Coastguard Worker  if siso_deps and ninja_deps:
574*8975f5c5SAndroid Build Coastguard Worker    raise Exception('Found both .siso_deps and .ninja_deps in '
575*8975f5c5SAndroid Build Coastguard Worker                    f'{output_directory}. Not sure which build tool to use. '
576*8975f5c5SAndroid Build Coastguard Worker                    'Please delete one, or better, run "gn clean".')
577*8975f5c5SAndroid Build Coastguard Worker  if siso_deps:
578*8975f5c5SAndroid Build Coastguard Worker    return siso_cmd
579*8975f5c5SAndroid Build Coastguard Worker  return ninja_cmd
580