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