Home | History | Annotate | Download | only in tools
      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