• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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