1 #!/usr/bin/python3 2 """ Synchronizes enums and their comments from the NeuralNetworks.h to types.hal 3 4 Workflow: 5 - Don't try to make other changes to types.hal in the same branch, as this 6 will check out and overwrite files 7 - Edit NeuralNetworks.h 8 - run sync_enums_to_hal.py 9 - can be run from anywhere, but ANDROID_BUILD_TOP must be set 10 - this resets 1.[0-2]/types.hal to last commit (so you can run 11 the script multiple times with changes to it in-between), and 12 - overwrites types.hal in-place 13 - Check the output (git diff) 14 - Recreate hashes 15 - commit and upload for review 16 17 Note: 18 This is somewhat brittle in terms of ordering and formatting of the 19 relevant files. It's the author's opinion that it's not worth spending a lot of 20 time upfront coming up with better mechanisms, but to improve it when needed. 21 For example, currently Operations have differences between 1.0 and 1.1, 22 but Operands do not, so the script is explicit rather than generic. 23 24 There are asserts in the code to make sure the expectations on the ordering and 25 formatting of the headers are met, so this should fail rather than produce 26 completely unexpected output. 27 28 The alternative would be to add explicit section markers to the files. 29 30 """ 31 32 import os 33 import re 34 import subprocess 35 36 class HeaderReader(object): 37 """ Simple base class facilitates reading a file into sections and writing it 38 back out 39 """ 40 def __init__(self): 41 self.sections = [] 42 self.current = -1 43 self.next_section() 44 45 def put_back(self, no_of_lines=1): 46 assert not self.sections[self.current] 47 for i in range(no_of_lines): 48 line = self.sections[self.current - 1].pop() 49 self.sections[self.current].insert(0, line) 50 51 def pop_back(self, no_of_lines=1): 52 for i in range(no_of_lines): 53 self.sections[self.current].pop() 54 55 def next_section(self): 56 self.current = self.current + 1 57 self.sections.append([]) 58 59 def get_contents(self): 60 return "".join([ "".join(s) for s in self.sections]) 61 62 def get_section(self, which): 63 return "".join(self.sections[which]) 64 65 def handle_line(self, line): 66 assert False 67 68 def read(self, filename): 69 assert self.current == 0 70 self.filename = filename 71 with open(filename) as f: 72 lines = f.readlines() 73 for line in lines: 74 self.sections[self.current].append(line) 75 if self.current == self.REST: 76 continue 77 self.handle_line(line) 78 assert self.current == self.REST 79 80 def write(self): 81 with open(self.filename, "w") as f: 82 f.write(self.get_contents()) 83 84 class Types10Reader(HeaderReader): 85 """ Reader for 1.0 types.hal 86 87 The structure of the file is: 88 - preamble 89 - enum OperandType ... { 90 < this becomes the OPERAND section > 91 OEM operands 92 }; 93 - comments 94 - enum OperationType ... { 95 < this becomes the OPERATION section > 96 OEM operarions 97 }; 98 - rest 99 """ 100 BEFORE_OPERAND = 0 101 OPERAND = 1 102 BEFORE_OPERATION = 2 103 OPERATION = 3 104 REST = 4 105 106 def __init__(self): 107 super(Types10Reader, self).__init__() 108 self.read("hardware/interfaces/neuralnetworks/1.0/types.hal") 109 110 def handle_line(self, line): 111 if "enum OperandType" in line: 112 assert self.current == self.BEFORE_OPERAND 113 self.next_section() 114 elif "enum OperationType" in line: 115 assert self.current == self.BEFORE_OPERATION 116 self.next_section() 117 elif "OEM" in line and self.current == self.OPERAND: 118 self.next_section() 119 self.put_back(2) 120 elif "OEM specific" in line and self.current == self.OPERATION: 121 self.next_section() 122 self.put_back(2) 123 124 class Types11Reader(HeaderReader): 125 """ Reader for 1.1 types.hal 126 127 The structure of the file is: 128 - preamble 129 - enum OperationType ... { 130 < this becomes the OPERATION section > 131 }; 132 - rest 133 """ 134 135 BEFORE_OPERATION = 0 136 OPERATION = 1 137 REST = 2 138 139 FILENAME = "hardware/interfaces/neuralnetworks/1.1/types.hal" 140 141 def __init__(self): 142 super(Types11Reader, self).__init__() 143 self.read(self.FILENAME) 144 145 def handle_line(self, line): 146 if "enum OperationType" in line: 147 assert self.current == self.BEFORE_OPERATION 148 self.next_section() 149 # there is more content after the enums we are interested in so 150 # it cannot be the last line, can match with \n 151 elif line == "};\n": 152 self.next_section() 153 self.put_back() 154 155 class Types12Reader(Types11Reader): 156 """ Reader for 1.2 types.hal 157 158 Assumes the structure of the file is the same as in v1.1. 159 """ 160 161 FILENAME = "hardware/interfaces/neuralnetworks/1.2/types.hal" 162 163 class NeuralNetworksReader(HeaderReader): 164 """ Reader for NeuralNetworks.h 165 166 The structure of the file is: 167 - preamble 168 - typedef enum { 169 < this becomes the OPERAND section > 170 } OperandCode; 171 - comments 172 - typedef enum { 173 // Operations below are available since API level 27. 174 < this becomes the OPERATION_V1_0 section > 175 // Operations below are available since API level 28. 176 < this becomes the OPERATION_V1_1 section > 177 // Operations below are available since API level 29. 178 < this becomes the OPERATION_V1_2 section > 179 } OperationCode; 180 - rest 181 """ 182 183 BEFORE_OPERAND = 0 184 OPERAND = 1 185 BEFORE_OPERATION = 2 186 OPERATION_V1_0 = 3 187 OPERATION_V1_1 = 4 188 OPERATION_V1_2 = 5 189 REST = 6 190 191 def __init__(self): 192 super(NeuralNetworksReader, self).__init__() 193 self.read("frameworks/ml/nn/runtime/include/NeuralNetworks.h") 194 195 def handle_line(self, line): 196 if line == "typedef enum {\n": 197 self.next_section() 198 elif line == "} OperandCode;\n": 199 assert self.current == self.OPERAND 200 self.next_section() 201 self.put_back() 202 elif "// Operations below are available since API level 27." in line: 203 assert self.current == self.OPERATION_V1_0 204 self.pop_back() 205 elif "// Operations below are available since API level 28." in line: 206 assert self.current == self.OPERATION_V1_0 207 self.pop_back() 208 self.next_section() 209 elif "// Operations below are available since API level 29." in line: 210 assert self.current == self.OPERATION_V1_1 211 self.pop_back() 212 self.next_section() 213 elif line == "} OperationCode;\n": 214 assert self.current == self.OPERATION_V1_2 215 self.next_section() 216 self.put_back() 217 218 219 if __name__ == "__main__": 220 # Reset 221 assert os.environ["ANDROID_BUILD_TOP"] 222 os.chdir(os.environ["ANDROID_BUILD_TOP"]) 223 subprocess.run( 224 "cd hardware/interfaces/neuralnetworks && git checkout */types.hal", 225 shell=True) 226 227 # Read existing contents 228 types10 = Types10Reader() 229 types11 = Types11Reader() 230 types12 = Types12Reader() 231 nn = NeuralNetworksReader() 232 233 # Rewrite from header syntax to HAL and replace types.hal contents 234 operand = [] 235 for line in nn.sections[nn.OPERAND]: 236 line = line.replace("ANEURALNETWORKS_", "") 237 operand.append(line) 238 types10.sections[types10.OPERAND] = operand 239 def rewrite_operation(from_nn): 240 hal = [] 241 for line in from_nn: 242 if "TODO" in line: 243 continue 244 245 # Match multiline comment style 246 if re.match("^ */\*\* \w.*[^/]$", line): 247 hal.append(" /**\n") 248 line = line.replace("/** ", " * ") 249 # Match naming changes in HAL vs framework 250 line = line.replace("@link OperandCode", "@link OperandType") 251 line = line.replace("@link ANEURALNETWORKS_", "@link OperandType::") 252 line = line.replace("ANEURALNETWORKS_", "") 253 line = line.replace("FuseCode", "FusedActivationFunc") 254 # PaddingCode is not part of HAL, rewrite 255 line = line.replace("{@link PaddingCode} values", 256 "following values: {0 (NONE), 1 (SAME), 2 (VALID)}") 257 hal.append(line) 258 return hal 259 types10.sections[types10.OPERATION] = rewrite_operation(nn.sections[nn.OPERATION_V1_0]) 260 types11.sections[types11.OPERATION] = rewrite_operation(nn.sections[nn.OPERATION_V1_1]) 261 types12.sections[types12.OPERATION] = rewrite_operation(nn.sections[nn.OPERATION_V1_2]) 262 263 # Write synced contents 264 types10.write() 265 types11.write() 266 types12.write() 267 268 print("") 269 print("The files") 270 print(" " + types10.filename + " and") 271 print(" " + types11.filename + " and") 272 print(" " + types12.filename) 273 print("have been rewritten") 274 print("") 275 print("Check that the change matches your expectations and regenerate the hashes") 276 print("") 277