X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=build-aux%2Fextract-ofp-errors;h=ee8dd0f4920797aac2b8bbaedc6f2467f0fce6ba;hb=003ce655b7116d18c86a74c50391e54990346931;hp=5c3cd26fd660c1cb6bd1db0abe8607ffa9cb983f;hpb=90bf1e0732ac9b11dd51ca856b635cac1f0269c1;p=sliver-openvswitch.git diff --git a/build-aux/extract-ofp-errors b/build-aux/extract-ofp-errors index 5c3cd26fd..ee8dd0f49 100755 --- a/build-aux/extract-ofp-errors +++ b/build-aux/extract-ofp-errors @@ -6,6 +6,14 @@ import re macros = {} +# Map from OpenFlow version number to version ID used in ofp_header. +version_map = {"1.0": 0x01, + "1.1": 0x02, + "1.2": 0x03, + "1.3": 0x04, + "1.4": 0x05} +version_reverse_map = dict((v, k) for (k, v) in version_map.iteritems()) + token = None line = "" idRe = "[a-zA-Z_][a-zA-Z_0-9]*" @@ -13,12 +21,24 @@ tokenRe = "#?" + idRe + "|[0-9]+|." inComment = False inDirective = False -def getLine(): +def open_file(fn): + global fileName + global inputFile + global lineNumber + fileName = fn + inputFile = open(fileName) + lineNumber = 0 + +def tryGetLine(): + global inputFile global line global lineNumber line = inputFile.readline() lineNumber += 1 - if line == "": + return line != "" + +def getLine(): + if not tryGetLine(): fatal("unexpected end of input") def getToken(): @@ -66,8 +86,14 @@ def getToken(): token = None return False -def fatal(msg): +n_errors = 0 +def error(msg): + global n_errors sys.stderr.write("%s:%d: %s\n" % (fileName, lineNumber, msg)) + n_errors += 1 + +def fatal(msg): + error(msg) sys.exit(1) def skipDirective(): @@ -107,122 +133,214 @@ def parseTaggedName(): return name def print_enum(tag, constants, storage_class): - print """ + print (""" %(storage_class)sconst char * %(tag)s_to_string(uint16_t value) { switch (value) {\ """ % {"tag": tag, "bufferlen": len(tag) + 32, - "storage_class": storage_class} + "storage_class": storage_class}) for constant in constants: - print " case %s: return \"%s\";" % (constant, constant) - print """\ + print (" case %s: return \"%s\";" % (constant, constant)) + print ("""\ } return NULL; }\ -""" % {"tag": tag} +""" % {"tag": tag}) def usage(): argv0 = os.path.basename(sys.argv[0]) - print '''\ + print ('''\ %(argv0)s, for extracting OpenFlow error codes from header files -usage: %(argv0)s FILE [FILE...] +usage: %(argv0)s ERROR_HEADER VENDOR_HEADER -This program reads the header files specified on the command line and -outputs a C source file for translating OpenFlow error codes into -strings, for use as lib/ofp-errors.c in the Open vSwitch source tree. +This program reads VENDOR_HEADER to obtain OpenFlow vendor (aka +experimenter IDs), then ERROR_HEADER to obtain OpenFlow error number. +It outputs a C source file for translating OpenFlow error codes into +strings. -This program is specialized for reading lib/ofp-errors.h. It will not -work on arbitrary header files without extensions.\ -''' % {"argv0": argv0} +ERROR_HEADER should point to lib/ofp-errors.h. +VENDOR_HEADER should point to include/openflow/openflow-common.h. +The output is suitable for use as lib/ofp-errors.inc.\ +''' % {"argv0": argv0}) sys.exit(0) -def extract_ofp_errors(filenames): +def extract_vendor_ids(fn): + global vendor_map + vendor_map = {} + vendor_loc = {} + + open_file(fn) + while tryGetLine(): + m = re.match(r'#define\s+([A-Z0-9_]+)_VENDOR_ID\s+(0x[0-9a-fA-F]+|[0-9]+)', line) + if not m: + continue + + name = m.group(1) + id_ = int(m.group(2), 0) + + if name in vendor_map: + error("%s: duplicate definition of vendor" % name) + sys.stderr.write("%s: Here is the location of the previous " + "definition.\n" % vendor_loc[name]) + sys.exit(1) + + vendor_map[name] = id_ + vendor_loc[name] = "%s:%d" % (fileName, lineNumber) + + if not vendor_map: + fatal("%s: no vendor definitions found" % fn) + + inputFile.close() + + vendor_reverse_map = {} + for name, id_ in vendor_map.items(): + if id_ in vendor_reverse_map: + fatal("%s: duplicate vendor id for vendors %s and %s" + % (id_, vendor_reverse_map[id_], name)) + vendor_reverse_map[id_] = name + +def extract_ofp_errors(fn): error_types = {} comments = [] names = [] domain = {} reverse = {} - for domain_name in ("OF1.0", "OF1.1", "NX1.0", "NX1.1"): + for domain_name in version_map.values(): domain[domain_name] = {} reverse[domain_name] = {} - global fileName - for fileName in filenames: - global inputFile - global lineNumber - inputFile = open(fileName) - lineNumber = 0 + n_errors = 0 + expected_errors = {} - while True: - getLine() - if re.match('enum ofperr', line): - break + open_file(fn) + + while True: + getLine() + if re.match('enum ofperr', line): + break + + while True: + getLine() + if line.startswith('/*') or not line or line.isspace(): + continue + elif re.match('}', line): + break - while True: + if not line.lstrip().startswith('/*'): + fatal("unexpected syntax between errors") + + comment = line.lstrip()[2:].strip() + while not comment.endswith('*/'): getLine() if line.startswith('/*') or not line or line.isspace(): - continue - elif re.match('}', line): - break + fatal("unexpected syntax within error") + comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n') + comment = comment[:-2].rstrip() - m = re.match('\s+/\* ((?:.(?!\. ))+.)\. (.*)$', line) - if not m: - fatal("unexpected syntax between errors") + m = re.match('Expected: (.*)\.$', comment) + if m: + expected_errors[m.group(1)] = (fileName, lineNumber) + continue - dsts, comment = m.groups() + m = re.match('((?:.(?!\. ))+.)\. (.*)$', comment) + if not m: + fatal("unexpected syntax between errors") - comment.rstrip() - while not comment.endswith('*/'): - getLine() - if line.startswith('/*') or not line or line.isspace(): - fatal("unexpected syntax within error") - comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n') - comment = comment[:-2].rstrip() + dsts, comment = m.groups() - getLine() - m = re.match('\s+(?:OFPERR_((?:OFP|NX)[A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,', - line) - if not m: - fatal("syntax error expecting enum value") + getLine() + m = re.match('\s+(?:OFPERR_([A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,', + line) + if not m: + fatal("syntax error expecting enum value") - enum = m.group(1) + enum = m.group(1) + if enum in names: + fatal("%s specified twice" % enum) - comments.append(comment) - names.append(enum) + comments.append(re.sub('\[[^]]*\]', '', comment)) + names.append(enum) - for dst in dsts.split(', '): - m = re.match(r'([A-Z0-9.]+)\((\d+)(?:,(\d+))?\)$', dst) - if not m: - fatal("%s: syntax error in destination" % dst) - targets = m.group(1) - type_ = int(m.group(2)) - if m.group(3): - code = int(m.group(3)) + for dst in dsts.split(', '): + m = re.match(r'([A-Z]+)([0-9.]+)(\+|-[0-9.]+)?\((\d+)(?:,(\d+))?\)$', dst) + if not m: + fatal("%r: syntax error in destination" % dst) + vendor_name = m.group(1) + version1_name = m.group(2) + version2_name = m.group(3) + type_ = int(m.group(4)) + if m.group(5): + code = int(m.group(5)) + else: + code = None + + if vendor_name not in vendor_map: + fatal("%s: unknown vendor" % vendor_name) + vendor = vendor_map[vendor_name] + + if version1_name not in version_map: + fatal("%s: unknown OpenFlow version" % version1_name) + v1 = version_map[version1_name] + + if version2_name is None: + v2 = v1 + elif version2_name == "+": + v2 = max(version_map.values()) + elif version2_name[1:] not in version_map: + fatal("%s: unknown OpenFlow version" % version2_name[1:]) + else: + v2 = version_map[version2_name[1:]] + + if v2 < v1: + fatal("%s%s: %s precedes %s" + % (version1_name, version2_name, + version2_name, version1_name)) + + if vendor == vendor_map['NX']: + if v1 >= version_map['1.2'] or v2 >= version_map['1.2']: + if code is not None: + fatal("%s: NX1.2+ domains do not have codes" % dst) + code = 0 + elif vendor != vendor_map['OF']: + if code is not None: + fatal("%s: %s domains do not have codes" % vendor_name) + + for version in range(v1, v2 + 1): + domain[version].setdefault(vendor, {}) + domain[version][vendor].setdefault(type_, {}) + if code in domain[version][vendor][type_]: + msg = "%#x,%d,%d in OF%s means both %s and %s" % ( + vendor, type_, code, version_reverse_map[version], + domain[version][vendor][type_][code][0], enum) + if msg in expected_errors: + del expected_errors[msg] + else: + error("%s: %s." % (dst, msg)) + sys.stderr.write("%s:%d: %s: Here is the location " + "of the previous definition.\n" + % (domain[version][vendor][type_][code][1], + domain[version][vendor][type_][code][2], + dst)) else: - code = None - - target_map = {"OF": ("OF1.0", "OF1.1"), - "OF1.0": ("OF1.0",), - "OF1.1": ("OF1.1",), - "NX": ("OF1.0", "OF1.1"), - "NX1.0": ("OF1.0",), - "NX1.1": ("OF1.1",)} - if targets not in target_map: - fatal("%s: unknown error domain" % target) - for target in target_map[targets]: - if type_ not in domain[target]: - domain[target][type_] = {} - if code in domain[target][type_]: - fatal("%s: duplicate assignment in domain" % dst) - domain[target][type_][code] = enum - reverse[target][enum] = (type_, code) - - inputFile.close() - - print """\ + domain[version][vendor][type_][code] = (enum, fileName, + lineNumber) + + assert enum not in reverse[version] + reverse[version][enum] = (vendor, type_, code) + + inputFile.close() + + for fn, ln in expected_errors.values(): + sys.stderr.write("%s:%d: expected duplicate not used.\n" % (fn, ln)) + n_errors += 1 + + if n_errors: + sys.exit(1) + + print ("""\ /* Generated automatically; do not modify! -*- buffer-read-only: t -*- */ #define OFPERR_N_ERRORS %d @@ -230,9 +348,8 @@ def extract_ofp_errors(filenames): struct ofperr_domain { const char *name; uint8_t version; - enum ofperr (*decode)(uint16_t type, uint16_t code); - enum ofperr (*decode_type)(uint16_t type); - struct pair errors[OFPERR_N_ERRORS]; + enum ofperr (*decode)(uint32_t vendor, uint16_t type, uint16_t code); + struct triplet errors[OFPERR_N_ERRORS]; }; static const char *error_names[OFPERR_N_ERRORS] = { @@ -245,74 +362,67 @@ static const char *error_comments[OFPERR_N_ERRORS] = { """ % (len(names), '\n'.join(' "%s",' % name for name in names), '\n'.join(' "%s",' % re.sub(r'(["\\])', r'\\\1', comment) - for comment in comments)) + for comment in comments))) def output_domain(map, name, description, version): - print """ + print (""" static enum ofperr -%s_decode(uint16_t type, uint16_t code) +%s_decode(uint32_t vendor, uint16_t type, uint16_t code) { - switch ((type << 16) | code) {""" % name + switch (((uint64_t) vendor << 32) | (type << 16) | code) {""" % name) + found = set() for enum in names: if enum not in map: continue - type_, code = map[enum] + vendor, type_, code = map[enum] if code is None: continue - print " case (%d << 16) | %d:" % (type_, code) - print " return OFPERR_%s;" % enum - print """\ - } - - return 0; -} - -static enum ofperr -%s_decode_type(uint16_t type) -{ - switch (type) {""" % name - for enum in names: - if enum not in map: - continue - type_, code = map[enum] - if code is not None: + value = (vendor << 32) | (type_ << 16) | code + if value in found: continue - print " case %d:" % type_ - print " return OFPERR_%s;" % enum - print """\ + found.add(value) + if vendor: + vendor_s = "(%#xULL << 32) | " % vendor + else: + vendor_s = "" + print (" case %s(%d << 16) | %d:" % (vendor_s, type_, code)) + print (" return OFPERR_%s;" % enum) + print ("""\ } return 0; -}""" +}""") - print """ -const struct ofperr_domain %s = { + print (""" +static const struct ofperr_domain %s = { "%s", %d, %s_decode, - %s_decode_type, - {""" % (name, description, version, name, name) + {""" % (name, description, version, name)) for enum in names: if enum in map: - type_, code = map[enum] + vendor, type_, code = map[enum] if code == None: code = -1 + print " { %#8x, %2d, %3d }, /* %s */" % (vendor, type_, code, enum) else: - type_ = code = -1 - print " { %2d, %3d }, /* %s */" % (type_, code, enum) - print """\ + print (" { -1, -1, -1 }, /* %s */" % enum) + print ("""\ }, -};""" +};""") - output_domain(reverse["OF1.0"], "ofperr_of10", "OpenFlow 1.0", 0x01) - output_domain(reverse["OF1.1"], "ofperr_of11", "OpenFlow 1.1", 0x02) + for version_name, id_ in version_map.items(): + var = 'ofperr_of' + re.sub('[^A-Za-z0-9_]', '', version_name) + description = "OpenFlow %s" % version_name + output_domain(reverse[id_], var, description, id_) if __name__ == '__main__': if '--help' in sys.argv: usage() - elif len(sys.argv) < 2: - sys.stderr.write("at least one non-option argument required; " + elif len(sys.argv) != 3: + sys.stderr.write("exactly two non-options arguments required; " "use --help for help\n") sys.exit(1) else: - extract_ofp_errors(sys.argv[1:]) + extract_vendor_ids(sys.argv[2]) + extract_ofp_errors(sys.argv[1])