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