ninja_rsp.py 2.4 KB

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