xref: /aosp_15_r20/build/soong/scripts/ninja_rsp.py (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker# Copyright (C) 2020 The Android Open Source Project
2*333d2b36SAndroid Build Coastguard Worker#
3*333d2b36SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*333d2b36SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*333d2b36SAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*333d2b36SAndroid Build Coastguard Worker#
7*333d2b36SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
8*333d2b36SAndroid Build Coastguard Worker#
9*333d2b36SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*333d2b36SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*333d2b36SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*333d2b36SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*333d2b36SAndroid Build Coastguard Worker# limitations under the License.
14*333d2b36SAndroid Build Coastguard Worker#
15*333d2b36SAndroid Build Coastguard Worker
16*333d2b36SAndroid Build Coastguard Worker"""This file reads entries from a Ninja rsp file."""
17*333d2b36SAndroid Build Coastguard Worker
18*333d2b36SAndroid Build Coastguard Workerclass NinjaRspFileReader:
19*333d2b36SAndroid Build Coastguard Worker  """
20*333d2b36SAndroid Build Coastguard Worker  Reads entries from a Ninja rsp file.  Ninja escapes any entries in the file that contain a
21*333d2b36SAndroid Build Coastguard Worker  non-standard character by surrounding the whole entry with single quotes, and then replacing
22*333d2b36SAndroid Build Coastguard Worker  any single quotes in the entry with the escape sequence '\''.
23*333d2b36SAndroid Build Coastguard Worker  """
24*333d2b36SAndroid Build Coastguard Worker
25*333d2b36SAndroid Build Coastguard Worker  def __init__(self, filename):
26*333d2b36SAndroid Build Coastguard Worker    self.f = open(filename, 'r')
27*333d2b36SAndroid Build Coastguard Worker    self.r = self.character_reader(self.f)
28*333d2b36SAndroid Build Coastguard Worker
29*333d2b36SAndroid Build Coastguard Worker  def __iter__(self):
30*333d2b36SAndroid Build Coastguard Worker    return self
31*333d2b36SAndroid Build Coastguard Worker
32*333d2b36SAndroid Build Coastguard Worker  def character_reader(self, f):
33*333d2b36SAndroid Build Coastguard Worker    """Turns a file into a generator that returns one character at a time."""
34*333d2b36SAndroid Build Coastguard Worker    while True:
35*333d2b36SAndroid Build Coastguard Worker      c = f.read(1)
36*333d2b36SAndroid Build Coastguard Worker      if c:
37*333d2b36SAndroid Build Coastguard Worker        yield c
38*333d2b36SAndroid Build Coastguard Worker      else:
39*333d2b36SAndroid Build Coastguard Worker        return
40*333d2b36SAndroid Build Coastguard Worker
41*333d2b36SAndroid Build Coastguard Worker  def __next__(self):
42*333d2b36SAndroid Build Coastguard Worker    entry = self.read_entry()
43*333d2b36SAndroid Build Coastguard Worker    if entry:
44*333d2b36SAndroid Build Coastguard Worker      return entry
45*333d2b36SAndroid Build Coastguard Worker    else:
46*333d2b36SAndroid Build Coastguard Worker      raise StopIteration
47*333d2b36SAndroid Build Coastguard Worker
48*333d2b36SAndroid Build Coastguard Worker  def read_entry(self):
49*333d2b36SAndroid Build Coastguard Worker    c = next(self.r, "")
50*333d2b36SAndroid Build Coastguard Worker    if not c:
51*333d2b36SAndroid Build Coastguard Worker      return ""
52*333d2b36SAndroid Build Coastguard Worker    elif c == "'":
53*333d2b36SAndroid Build Coastguard Worker      return self.read_quoted_entry()
54*333d2b36SAndroid Build Coastguard Worker    else:
55*333d2b36SAndroid Build Coastguard Worker      entry = c
56*333d2b36SAndroid Build Coastguard Worker      for c in self.r:
57*333d2b36SAndroid Build Coastguard Worker        if c == " " or c == "\n":
58*333d2b36SAndroid Build Coastguard Worker          break
59*333d2b36SAndroid Build Coastguard Worker        entry += c
60*333d2b36SAndroid Build Coastguard Worker      return entry
61*333d2b36SAndroid Build Coastguard Worker
62*333d2b36SAndroid Build Coastguard Worker  def read_quoted_entry(self):
63*333d2b36SAndroid Build Coastguard Worker    entry = ""
64*333d2b36SAndroid Build Coastguard Worker    for c in self.r:
65*333d2b36SAndroid Build Coastguard Worker      if c == "'":
66*333d2b36SAndroid Build Coastguard Worker        # Either the end of the quoted entry, or the beginning of an escape sequence, read the next
67*333d2b36SAndroid Build Coastguard Worker        # character to find out.
68*333d2b36SAndroid Build Coastguard Worker        c = next(self.r)
69*333d2b36SAndroid Build Coastguard Worker        if not c or c == " " or c == "\n":
70*333d2b36SAndroid Build Coastguard Worker          # End of the item
71*333d2b36SAndroid Build Coastguard Worker          return entry
72*333d2b36SAndroid Build Coastguard Worker        elif c == "\\":
73*333d2b36SAndroid Build Coastguard Worker          # Escape sequence, expect a '
74*333d2b36SAndroid Build Coastguard Worker          c = next(self.r)
75*333d2b36SAndroid Build Coastguard Worker          if c != "'":
76*333d2b36SAndroid Build Coastguard Worker            # Malformed escape sequence
77*333d2b36SAndroid Build Coastguard Worker            raise "malformed escape sequence %s'\\%s" % (entry, c)
78*333d2b36SAndroid Build Coastguard Worker          entry += "'"
79*333d2b36SAndroid Build Coastguard Worker        else:
80*333d2b36SAndroid Build Coastguard Worker          raise "malformed escape sequence %s'%s" % (entry, c)
81*333d2b36SAndroid Build Coastguard Worker      else:
82*333d2b36SAndroid Build Coastguard Worker        entry += c
83*333d2b36SAndroid Build Coastguard Worker    raise "unterminated quoted entry %s" % entry
84