#!/usr/bin/python3
|
""" Synchronizes enums and their comments from the NeuralNetworks.h to types.hal
|
|
Workflow:
|
- Don't try to make other changes to types.hal in the same branch, as this
|
will check out and overwrite files
|
- Edit NeuralNetworks.h
|
- run sync_enums_to_hal.py
|
- can be run from anywhere, but ANDROID_BUILD_TOP must be set
|
- this resets 1.[0-2]/types.hal to last commit (so you can run
|
the script multiple times with changes to it in-between), and
|
- overwrites types.hal in-place
|
- Check the output (git diff)
|
- Recreate hashes
|
- commit and upload for review
|
|
Note:
|
This is somewhat brittle in terms of ordering and formatting of the
|
relevant files. It's the author's opinion that it's not worth spending a lot of
|
time upfront coming up with better mechanisms, but to improve it when needed.
|
For example, currently Operations have differences between 1.0 and 1.1,
|
but Operands do not, so the script is explicit rather than generic.
|
|
There are asserts in the code to make sure the expectations on the ordering and
|
formatting of the headers are met, so this should fail rather than produce
|
completely unexpected output.
|
|
The alternative would be to add explicit section markers to the files.
|
|
"""
|
|
import os
|
import re
|
import subprocess
|
|
class HeaderReader(object):
|
""" Simple base class facilitates reading a file into sections and writing it
|
back out
|
"""
|
def __init__(self):
|
self.sections = []
|
self.current = -1
|
self.next_section()
|
|
def put_back(self, no_of_lines=1):
|
assert not self.sections[self.current]
|
for i in range(no_of_lines):
|
line = self.sections[self.current - 1].pop()
|
self.sections[self.current].insert(0, line)
|
|
def pop_back(self, no_of_lines=1):
|
for i in range(no_of_lines):
|
self.sections[self.current].pop()
|
|
def next_section(self):
|
self.current = self.current + 1
|
self.sections.append([])
|
|
def get_contents(self):
|
return "".join([ "".join(s) for s in self.sections])
|
|
def get_section(self, which):
|
return "".join(self.sections[which])
|
|
def handle_line(self, line):
|
assert False
|
|
def read(self, filename):
|
assert self.current == 0
|
self.filename = filename
|
with open(filename) as f:
|
lines = f.readlines()
|
for line in lines:
|
self.sections[self.current].append(line)
|
if self.current == self.REST:
|
continue
|
self.handle_line(line)
|
assert self.current == self.REST
|
|
def write(self):
|
with open(self.filename, "w") as f:
|
f.write(self.get_contents())
|
|
class Types10Reader(HeaderReader):
|
""" Reader for 1.0 types.hal
|
|
The structure of the file is:
|
- preamble
|
- enum OperandType ... {
|
< this becomes the OPERAND section >
|
OEM operands
|
};
|
- comments
|
- enum OperationType ... {
|
< this becomes the OPERATION section >
|
OEM operarions
|
};
|
- rest
|
"""
|
BEFORE_OPERAND = 0
|
OPERAND = 1
|
BEFORE_OPERATION = 2
|
OPERATION = 3
|
REST = 4
|
|
def __init__(self):
|
super(Types10Reader, self).__init__()
|
self.read("hardware/interfaces/neuralnetworks/1.0/types.hal")
|
|
def handle_line(self, line):
|
if "enum OperandType" in line:
|
assert self.current == self.BEFORE_OPERAND
|
self.next_section()
|
elif "enum OperationType" in line:
|
assert self.current == self.BEFORE_OPERATION
|
self.next_section()
|
elif "OEM" in line and self.current == self.OPERAND:
|
self.next_section()
|
self.put_back(2)
|
elif "OEM specific" in line and self.current == self.OPERATION:
|
self.next_section()
|
self.put_back(2)
|
|
class Types11Reader(HeaderReader):
|
""" Reader for 1.1 types.hal
|
|
The structure of the file is:
|
- preamble
|
- enum OperationType ... {
|
< this becomes the OPERATION section >
|
};
|
- rest
|
"""
|
|
BEFORE_OPERATION = 0
|
OPERATION = 1
|
REST = 2
|
|
FILENAME = "hardware/interfaces/neuralnetworks/1.1/types.hal"
|
|
def __init__(self):
|
super(Types11Reader, self).__init__()
|
self.read(self.FILENAME)
|
|
def handle_line(self, line):
|
if "enum OperationType" in line:
|
assert self.current == self.BEFORE_OPERATION
|
self.next_section()
|
# there is more content after the enums we are interested in so
|
# it cannot be the last line, can match with \n
|
elif line == "};\n":
|
self.next_section()
|
self.put_back()
|
|
class Types12Reader(Types11Reader):
|
""" Reader for 1.2 types.hal
|
|
Assumes the structure of the file is the same as in v1.1.
|
"""
|
|
FILENAME = "hardware/interfaces/neuralnetworks/1.2/types.hal"
|
|
class NeuralNetworksReader(HeaderReader):
|
""" Reader for NeuralNetworks.h
|
|
The structure of the file is:
|
- preamble
|
- typedef enum {
|
< this becomes the OPERAND section >
|
} OperandCode;
|
- comments
|
- typedef enum {
|
// Operations below are available since API level 27.
|
< this becomes the OPERATION_V1_0 section >
|
// Operations below are available since API level 28.
|
< this becomes the OPERATION_V1_1 section >
|
// Operations below are available since API level 29.
|
< this becomes the OPERATION_V1_2 section >
|
} OperationCode;
|
- rest
|
"""
|
|
BEFORE_OPERAND = 0
|
OPERAND = 1
|
BEFORE_OPERATION = 2
|
OPERATION_V1_0 = 3
|
OPERATION_V1_1 = 4
|
OPERATION_V1_2 = 5
|
REST = 6
|
|
def __init__(self):
|
super(NeuralNetworksReader, self).__init__()
|
self.read("frameworks/ml/nn/runtime/include/NeuralNetworks.h")
|
|
def handle_line(self, line):
|
if line == "typedef enum {\n":
|
self.next_section()
|
elif line == "} OperandCode;\n":
|
assert self.current == self.OPERAND
|
self.next_section()
|
self.put_back()
|
elif "// Operations below are available since API level 27." in line:
|
assert self.current == self.OPERATION_V1_0
|
self.pop_back()
|
elif "// Operations below are available since API level 28." in line:
|
assert self.current == self.OPERATION_V1_0
|
self.pop_back()
|
self.next_section()
|
elif "// Operations below are available since API level 29." in line:
|
assert self.current == self.OPERATION_V1_1
|
self.pop_back()
|
self.next_section()
|
elif line == "} OperationCode;\n":
|
assert self.current == self.OPERATION_V1_2
|
self.next_section()
|
self.put_back()
|
|
|
if __name__ == "__main__":
|
# Reset
|
assert os.environ["ANDROID_BUILD_TOP"]
|
os.chdir(os.environ["ANDROID_BUILD_TOP"])
|
subprocess.run(
|
"cd hardware/interfaces/neuralnetworks && git checkout */types.hal",
|
shell=True)
|
|
# Read existing contents
|
types10 = Types10Reader()
|
types11 = Types11Reader()
|
types12 = Types12Reader()
|
nn = NeuralNetworksReader()
|
|
# Rewrite from header syntax to HAL and replace types.hal contents
|
operand = []
|
for line in nn.sections[nn.OPERAND]:
|
line = line.replace("ANEURALNETWORKS_", "")
|
operand.append(line)
|
types10.sections[types10.OPERAND] = operand
|
def rewrite_operation(from_nn):
|
hal = []
|
for line in from_nn:
|
if "TODO" in line:
|
continue
|
|
# Match multiline comment style
|
if re.match("^ */\*\* \w.*[^/]$", line):
|
hal.append(" /**\n")
|
line = line.replace("/** ", " * ")
|
# Match naming changes in HAL vs framework
|
line = line.replace("@link OperandCode", "@link OperandType")
|
line = line.replace("@link ANEURALNETWORKS_", "@link OperandType::")
|
line = line.replace("ANEURALNETWORKS_", "")
|
line = line.replace("FuseCode", "FusedActivationFunc")
|
# PaddingCode is not part of HAL, rewrite
|
line = line.replace("{@link PaddingCode} values",
|
"following values: {0 (NONE), 1 (SAME), 2 (VALID)}")
|
hal.append(line)
|
return hal
|
types10.sections[types10.OPERATION] = rewrite_operation(nn.sections[nn.OPERATION_V1_0])
|
types11.sections[types11.OPERATION] = rewrite_operation(nn.sections[nn.OPERATION_V1_1])
|
types12.sections[types12.OPERATION] = rewrite_operation(nn.sections[nn.OPERATION_V1_2])
|
|
# Write synced contents
|
types10.write()
|
types11.write()
|
types12.write()
|
|
print("")
|
print("The files")
|
print(" " + types10.filename + " and")
|
print(" " + types11.filename + " and")
|
print(" " + types12.filename)
|
print("have been rewritten")
|
print("")
|
print("Check that the change matches your expectations and regenerate the hashes")
|
print("")
|