sync_enums_to_hal.py 8.2 KB

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