1# Copyright (C) 2023 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. 14import dataclasses 15import re 16from pathlib import Path 17 18import util 19 20 21@dataclasses.dataclass(frozen=True) 22class Defaults: 23 allowed: bool 24 recurse: bool 25 26 27@dataclasses.dataclass(frozen=True) 28class GoAllowlistManipulator: 29 """ 30 This is a bare-bones regex-based utility for manipulating `allowlists.go` 31 It expects that file to be propertly formatted. 32 """ 33 34 lines: list[str] 35 """the source code lines of `allowlists.go`""" 36 _lists: dict[str, "GoList"] = dataclasses.field(default_factory=lambda: {}) 37 """ 38 All GoList instances retrieved via `locate()` indexed by their list names. 39 This dict is kept around such that any list when modified can adjust the 40 line numbers of all other lists appropriately 41 """ 42 dir_defaults: dict[Path, Defaults] = dataclasses.field(default_factory=lambda: {}) 43 """ 44 the mappings from directories to whether they are bp2build allowed or not 45 """ 46 47 def __post_init__(self): 48 # reads the Bp2BuildConfig to materialize `dir_defaults` 49 start = re.compile(r"\w+\s*=\s*Bp2BuildConfig\{") 50 entry = re.compile( 51 r'"(?P<path>[^"]+)"\s*:\s*Bp2BuildDefault(?P<allowed>True|False)(?P<recurse>Recursively)?' 52 ) 53 begun = False 54 left_pad: str = "" 55 for line in self.lines: 56 line = line.strip() 57 if not begun: 58 begun = bool(start.match(line)) 59 elif line == "}": 60 break 61 else: 62 real_item = line.strip() 63 m = entry.search(real_item) 64 if m: 65 key = Path(m.group("path")) 66 value = Defaults( 67 m.group("allowed") == "True", bool(m.group("recurse")) 68 ) 69 self.dir_defaults[key] = value 70 if left_pad == "": 71 left_pad = line[: line.index(real_item)] 72 73 else: 74 raise RuntimeError("Bp2BuildConfig missing") 75 76 def locate(self, listname: str) -> "GoList": 77 if listname in self._lists: 78 return self._lists[listname] 79 start = re.compile(r"^\s*{l}\s=\s*\[]string\{{\s*$".format(l=listname)) 80 begin: int = -1 81 left_pad: str = "" 82 for i, line in enumerate(self.lines): 83 if begin == -1: 84 if start.match(line): 85 begin = i + 1 86 else: 87 if line.strip() == "}": 88 go_list = GoList(self, begin, end=i, left_pad=left_pad) 89 self._lists[listname] = go_list 90 return go_list 91 elif left_pad == "": 92 real_item = line.lstrip() 93 left_pad = line[: line.index(real_item)] 94 raise RuntimeError(f"{listname} not found") 95 96 def is_dir_allowed(self, d: Path) -> bool: 97 if d.is_absolute(): 98 d = d.relative_to(util.get_top_dir()) 99 if d in self.dir_defaults: 100 return self.dir_defaults[d].allowed 101 while d.parent != d: 102 if d.parent in self.dir_defaults: 103 v = self.dir_defaults[d.parent] 104 return v.allowed and v.recurse 105 d = d.parent 106 return False 107 108 @property 109 def lists(self): 110 return self._lists 111 112 113@dataclasses.dataclass 114class GoList: 115 parent: GoAllowlistManipulator 116 begin: int 117 end: int 118 left_pad: str = "" 119 120 def __contains__(self, item: str) -> bool: 121 quoted = f'"{item}"' 122 for i in range(self.begin, self.end): 123 if quoted in self.parent.lines[i]: 124 return True 125 return False 126 127 def prepend(self, items: list[str]): 128 clones = [f'{self.left_pad}"{i}",\n' for i in items] 129 self.parent.lines[self.begin : self.begin] = clones 130 growth = len(items) 131 self.end += growth 132 for go_list in self.parent.lists.values(): 133 # adjust line numbers for all subsequent lists 134 # in the source code 135 if go_list.begin > self.begin: 136 go_list.begin += growth 137 go_list.end += growth 138