Home | History | Annotate | Download | only in layers
      1 #!/usr/bin/python3 -i
      2 
      3 import sys
      4 import xml.etree.ElementTree as etree
      5 import urllib2
      6 
      7 #############################
      8 # spec.py script
      9 #
     10 # Overview - this script is intended to generate validation error codes and message strings from the xhtml version of
     11 #  the specification. In addition to generating the header file, it provides a number of corrollary services to aid in
     12 #  generating/updating the header.
     13 #
     14 # Ideal flow - Not there currently, but the ideal flow for this script would be that you run the script, it pulls the
     15 #  latest spec, compares it to the current set of generated error codes, and makes any updates as needed
     16 #
     17 # Current flow - the current flow acheives all of the ideal flow goals, but with more steps than are desired
     18 #  1. Get the spec - right now spec has to be manually generated or pulled from the web
     19 #  2. Generate header from spec - This is done in a single command line
     20 #  3. Generate database file from spec - Can be done along with step #2 above, the database file contains a list of
     21 #      all error enums and message strings, along with some other info on if those errors are implemented/tested
     22 #  4. Update header using a given database file as the root and a new spec file as goal - This makes sure that existing
     23 #      errors keep the same enum identifier while also making sure that new errors get a unique_id that continues on
     24 #      from the end of the previous highest unique_id.
     25 #
     26 # TODO:
     27 #  1. Improve string matching to add more automation for figuring out which messages are changed vs. completely new
     28 #
     29 #############################
     30 
     31 
     32 spec_filename = "vkspec.html" # can override w/ '-spec <filename>' option
     33 out_filename = "vk_validation_error_messages.h" # can override w/ '-out <filename>' option
     34 db_filename = "vk_validation_error_database.txt" # can override w/ '-gendb <filename>' option
     35 gen_db = False # set to True when '-gendb <filename>' option provided
     36 spec_compare = False # set to True with '-compare <db_filename>' option
     37 # This is the root spec link that is used in error messages to point users to spec sections
     38 #old_spec_url = "https://www.khronos.org/registry/vulkan/specs/1.0/xhtml/vkspec.html"
     39 spec_url = "https://www.khronos.org/registry/vulkan/specs/1.0-extensions/xhtml/vkspec.html"
     40 # After the custom validation error message, this is the prefix for the standard message that includes the
     41 #  spec valid usage language as well as the link to nearest section of spec to that language
     42 error_msg_prefix = "For more information refer to Vulkan Spec Section "
     43 ns = {'ns': 'http://www.w3.org/1999/xhtml'}
     44 validation_error_enum_name = "VALIDATION_ERROR_"
     45 # Dict of new enum values that should be forced to remap to old handles, explicitly set by -remap option
     46 remap_dict = {}
     47 
     48 def printHelp():
     49     print "Usage: python spec.py [-spec <specfile.html>] [-out <headerfile.h>] [-gendb <databasefile.txt>] [-compare <databasefile.txt>] [-update] [-remap <new_id-old_id,count>] [-help]"
     50     print "\n Default script behavior is to parse the specfile and generate a header of unique error enums and corresponding error messages based on the specfile.\n"
     51     print "  Default specfile is from online at %s" % (spec_url)
     52     print "  Default headerfile is %s" % (out_filename)
     53     print "  Default databasefile is %s" % (db_filename)
     54     print "\nIf '-gendb' option is specified then a database file is generated to default file or <databasefile.txt> if supplied. The database file stores"
     55     print "  the list of enums and their error messages."
     56     print "\nIf '-compare' option is specified then the given database file will be read in as the baseline for generating the new specfile"
     57     print "\nIf '-update' option is specified this triggers the master flow to automate updating header and database files using default db file as baseline"
     58     print "  and online spec file as the latest. The default header and database files will be updated in-place for review and commit to the git repo."
     59     print "\nIf '-remap' option is specified it supplies forced remapping from new enum ids to old enum ids. This should only be specified along with -update"
     60     print "  option. Starting at newid and remapping to oldid, count ids will be remapped. Default count is '1' and use ':' to specify multiple remappings."
     61 
     62 class Specification:
     63     def __init__(self):
     64         self.tree   = None
     65         self.val_error_dict = {} # string for enum is key that references 'error_msg' and 'api'
     66         self.error_db_dict = {} # dict of previous error values read in from database file
     67         self.delimiter = '~^~' # delimiter for db file
     68         self.copyright = """/* THIS FILE IS GENERATED.  DO NOT EDIT. */
     69 
     70 /*
     71  * Vulkan
     72  *
     73  * Copyright (c) 2016 Google Inc.
     74  * Copyright (c) 2016 LunarG, Inc.
     75  *
     76  * Licensed under the Apache License, Version 2.0 (the "License");
     77  * you may not use this file except in compliance with the License.
     78  * You may obtain a copy of the License at
     79  *
     80  *     http://www.apache.org/licenses/LICENSE-2.0
     81  *
     82  * Unless required by applicable law or agreed to in writing, software
     83  * distributed under the License is distributed on an "AS IS" BASIS,
     84  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     85  * See the License for the specific language governing permissions and
     86  * limitations under the License.
     87  *
     88  * Author: Tobin Ehlis <tobine (at] google.com>
     89  */"""
     90     def _checkInternetSpec(self):
     91         """Verify that we can access the spec online"""
     92         try:
     93             online = urllib2.urlopen(spec_url,timeout=1)
     94             return True
     95         except urllib2.URLError as err:
     96             return False
     97         return False
     98     def loadFile(self, online=True, spec_file=spec_filename):
     99         """Load an API registry XML file into a Registry object and parse it"""
    100         # Check if spec URL is available
    101         if (online and self._checkInternetSpec()):
    102             print "Using spec from online at %s" % (spec_url)
    103             self.tree = etree.parse(urllib2.urlopen(spec_url))
    104         else:
    105             print "Using local spec %s" % (spec_file)
    106             self.tree = etree.parse(spec_file)
    107         #self.tree.write("tree_output.xhtml")
    108         #self.tree = etree.parse("tree_output.xhtml")
    109         self.parseTree()
    110     def updateDict(self, updated_dict):
    111         """Assign internal dict to use updated_dict"""
    112         self.val_error_dict = updated_dict
    113     def parseTree(self):
    114         """Parse the registry Element, once created"""
    115         print "Parsing spec file..."
    116         unique_enum_id = 0
    117         self.root = self.tree.getroot()
    118         #print "ROOT: %s" % self.root
    119         prev_heading = '' # Last seen section heading or sub-heading
    120         prev_link = '' # Last seen link id within the spec
    121         api_function = '' # API call that a check appears under
    122         error_strings = set() # Flag any exact duplicate error strings and skip them
    123         for tag in self.root.iter(): # iterate down tree
    124             # Grab most recent section heading and link
    125             if tag.tag in ['{http://www.w3.org/1999/xhtml}h2', '{http://www.w3.org/1999/xhtml}h3']:
    126                 if tag.get('class') != 'title':
    127                     continue
    128                 #print "Found heading %s" % (tag.tag)
    129                 prev_heading = "".join(tag.itertext())
    130                 # Insert a space between heading number & title
    131                 sh_list = prev_heading.rsplit('.', 1)
    132                 prev_heading = '. '.join(sh_list)
    133                 prev_link = tag[0].get('id')
    134                 #print "Set prev_heading %s to have link of %s" % (prev_heading.encode("ascii", "ignore"), prev_link.encode("ascii", "ignore"))
    135             elif tag.tag == '{http://www.w3.org/1999/xhtml}a': # grab any intermediate links
    136                 if tag.get('id') != None:
    137                     prev_link = tag.get('id')
    138                     #print "Updated prev link to %s" % (prev_link)
    139             elif tag.tag == '{http://www.w3.org/1999/xhtml}pre' and tag.get('class') == 'programlisting':
    140                 # Check and see if this is API function
    141                 code_text = "".join(tag.itertext()).replace('\n', '')
    142                 code_text_list = code_text.split()
    143                 if len(code_text_list) > 1 and code_text_list[1].startswith('vk'):
    144                     api_function = code_text_list[1].strip('(')
    145                     print "Found API function: %s" % (api_function)
    146             elif tag.tag == '{http://www.w3.org/1999/xhtml}div' and tag.get('class') == 'sidebar':
    147                 # parse down sidebar to check for valid usage cases
    148                 valid_usage = False
    149                 for elem in tag.iter():
    150                     if elem.tag == '{http://www.w3.org/1999/xhtml}strong' and None != elem.text and 'Valid Usage' in elem.text:
    151                         valid_usage = True
    152                     elif valid_usage and elem.tag == '{http://www.w3.org/1999/xhtml}li': # grab actual valid usage requirements
    153                         error_msg_str = "%s '%s' which states '%s' (%s#%s)" % (error_msg_prefix, prev_heading, "".join(elem.itertext()).replace('\n', ''), spec_url, prev_link)
    154                         # Some txt has multiple spaces so split on whitespace and join w/ single space
    155                         error_msg_str = " ".join(error_msg_str.split())
    156                         if error_msg_str in error_strings:
    157                             print "WARNING: SKIPPING adding repeat entry for string. Please review spec and file issue as appropriate. Repeat string is: %s" % (error_msg_str)
    158                         else:
    159                             error_strings.add(error_msg_str)
    160                             enum_str = "%s%05d" % (validation_error_enum_name, unique_enum_id)
    161                             # TODO : '\' chars in spec error messages are most likely bad spec txt that needs to be updated
    162                             self.val_error_dict[enum_str] = {}
    163                             self.val_error_dict[enum_str]['error_msg'] = error_msg_str.encode("ascii", "ignore").replace("\\", "/")
    164                             self.val_error_dict[enum_str]['api'] = api_function
    165                             unique_enum_id = unique_enum_id + 1
    166         #print "Validation Error Dict has a total of %d unique errors and contents are:\n%s" % (unique_enum_id, self.val_error_dict)
    167     def genHeader(self, header_file):
    168         """Generate a header file based on the contents of a parsed spec"""
    169         print "Generating header %s..." % (header_file)
    170         file_contents = []
    171         file_contents.append(self.copyright)
    172         file_contents.append('\n#pragma once')
    173         file_contents.append('#include <unordered_map>')
    174         file_contents.append('\n// enum values for unique validation error codes')
    175         file_contents.append('//  Corresponding validation error message for each enum is given in the mapping table below')
    176         file_contents.append('//  When a given error occurs, these enum values should be passed to the as the messageCode')
    177         file_contents.append('//  parameter to the PFN_vkDebugReportCallbackEXT function')
    178         enum_decl = ['enum UNIQUE_VALIDATION_ERROR_CODE {']
    179         error_string_map = ['static std::unordered_map<int, char const *const> validation_error_map{']
    180         for enum in sorted(self.val_error_dict):
    181             #print "Header enum is %s" % (enum)
    182             enum_decl.append('    %s = %d,' % (enum, int(enum.split('_')[-1])))
    183             error_string_map.append('    {%s, "%s"},' % (enum, self.val_error_dict[enum]['error_msg']))
    184         enum_decl.append('    %sMAX_ENUM = %d,' % (validation_error_enum_name, int(enum.split('_')[-1]) + 1))
    185         enum_decl.append('};')
    186         error_string_map.append('};\n')
    187         file_contents.extend(enum_decl)
    188         file_contents.append('\n// Mapping from unique validation error enum to the corresponding error message')
    189         file_contents.append('// The error message should be appended to the end of a custom error message that is passed')
    190         file_contents.append('// as the pMessage parameter to the PFN_vkDebugReportCallbackEXT function')
    191         file_contents.extend(error_string_map)
    192         #print "File contents: %s" % (file_contents)
    193         with open(header_file, "w") as outfile:
    194             outfile.write("\n".join(file_contents))
    195     def analyze(self):
    196         """Print out some stats on the valid usage dict"""
    197         # Create dict for # of occurences of identical strings
    198         str_count_dict = {}
    199         unique_id_count = 0
    200         for enum in self.val_error_dict:
    201             err_str = self.val_error_dict[enum]['error_msg']
    202             if err_str in str_count_dict:
    203                 print "Found repeat error string"
    204                 str_count_dict[err_str] = str_count_dict[err_str] + 1
    205             else:
    206                 str_count_dict[err_str] = 1
    207             unique_id_count = unique_id_count + 1
    208         print "Processed %d unique_ids" % (unique_id_count)
    209         repeat_string = 0
    210         for es in str_count_dict:
    211             if str_count_dict[es] > 1:
    212                 repeat_string = repeat_string + 1
    213                 print "String '%s' repeated %d times" % (es, repeat_string)
    214         print "Found %d repeat strings" % (repeat_string)
    215     def genDB(self, db_file):
    216         """Generate a database of check_enum, check_coded?, testname, error_string"""
    217         db_lines = []
    218         # Write header for database file
    219         db_lines.append("# This is a database file with validation error check information")
    220         db_lines.append("# Comments are denoted with '#' char")
    221         db_lines.append("# The format of the lines is:")
    222         db_lines.append("# <error_enum>%s<check_implemented>%s<testname>%s<api>%s<errormsg>%s<note>" % (self.delimiter, self.delimiter, self.delimiter, self.delimiter, self.delimiter))
    223         db_lines.append("# error_enum: Unique error enum for this check of format %s<uniqueid>" % validation_error_enum_name)
    224         db_lines.append("# check_implemented: 'Y' if check has been implemented in layers, 'U' for unknown, or 'N' for not implemented")
    225         db_lines.append("# testname: Name of validation test for this check, 'Unknown' for unknown, or 'None' if not implmented")
    226         db_lines.append("# api: Vulkan API function that this check is related to")
    227         db_lines.append("# errormsg: The unique error message for this check that includes spec language and link")
    228         db_lines.append("# note: Free txt field with any custom notes related to the check in question")
    229         for enum in sorted(self.val_error_dict):
    230             # Default to unknown if check or test are implemented, then update below if appropriate
    231             implemented = 'U'
    232             testname = 'Unknown'
    233             note = ''
    234             # If we have an existing db entry for this enum, use its implemented/testname values
    235             if enum in self.error_db_dict:
    236                 implemented = self.error_db_dict[enum]['check_implemented']
    237                 testname = self.error_db_dict[enum]['testname']
    238                 note = self.error_db_dict[enum]['note']
    239             #print "delimiter: %s, id: %s, str: %s" % (self.delimiter, enum, self.val_error_dict[enum])
    240             # No existing entry so default to N for implemented and None for testname
    241             db_lines.append("%s%s%s%s%s%s%s%s%s%s%s" % (enum, self.delimiter, implemented, self.delimiter, testname, self.delimiter, self.val_error_dict[enum]['api'], self.delimiter, self.val_error_dict[enum]['error_msg'], self.delimiter, note))
    242         print "Generating database file %s" % (db_file)
    243         with open(db_file, "w") as outfile:
    244             outfile.write("\n".join(db_lines))
    245             outfile.write("\n")
    246     def readDB(self, db_file):
    247         """Read a db file into a dict, format of each line is <enum><implemented Y|N?><testname><errormsg>"""
    248         db_dict = {} # This is a simple db of just enum->errormsg, the same as is created from spec
    249         max_id = 0
    250         with open(db_file, "r") as infile:
    251             for line in infile:
    252                 if line.startswith('#'):
    253                     continue
    254                 line = line.strip()
    255                 db_line = line.split(self.delimiter)
    256                 if len(db_line) != 6:
    257                     print "ERROR: Bad database line doesn't have 6 elements: %s" % (line)
    258                 error_enum = db_line[0]
    259                 implemented = db_line[1]
    260                 testname = db_line[2]
    261                 api = db_line[3]
    262                 error_str = db_line[4]
    263                 note = db_line[5]
    264                 db_dict[error_enum] = error_str
    265                 # Also read complete database contents into our class var for later use
    266                 self.error_db_dict[error_enum] = {}
    267                 self.error_db_dict[error_enum]['check_implemented'] = implemented
    268                 self.error_db_dict[error_enum]['testname'] = testname
    269                 self.error_db_dict[error_enum]['api'] = api
    270                 self.error_db_dict[error_enum]['error_string'] = error_str
    271                 self.error_db_dict[error_enum]['note'] = note
    272                 unique_id = int(db_line[0].split('_')[-1])
    273                 if unique_id > max_id:
    274                     max_id = unique_id
    275         return (db_dict, max_id)
    276     # Compare unique ids from original database to data generated from updated spec
    277     # 1. If a new id and error code exactly match original, great
    278     # 2. If new id is not in original, but exact error code is, need to use original error code
    279     # 3. If new id and new error are not in original, make sure new id picks up from end of original list
    280     # 4. If new id in original, but error strings don't match then:
    281     #   4a. If error string has exact match in original, update new to use original
    282     #   4b. If error string not in original, may be updated error message, manually address
    283     def compareDB(self, orig_error_msg_dict, max_id):
    284         """Compare orig database dict to new dict, report out findings, and return potential new dict for parsed spec"""
    285         # First create reverse dicts of err_strings to IDs
    286         next_id = max_id + 1
    287         orig_err_to_id_dict = {}
    288         # Create an updated dict in-place that will be assigned to self.val_error_dict when done
    289         updated_val_error_dict = {}
    290         for enum in orig_error_msg_dict:
    291             orig_err_to_id_dict[orig_error_msg_dict[enum]] = enum
    292         new_err_to_id_dict = {}
    293         for enum in self.val_error_dict:
    294             new_err_to_id_dict[self.val_error_dict[enum]['error_msg']] = enum
    295         ids_parsed = 0
    296         # Values to be used for the update dict
    297         update_enum = ''
    298         update_msg = ''
    299         update_api = ''
    300         # Now parse through new dict and figure out what to do with non-matching things
    301         for enum in sorted(self.val_error_dict):
    302             ids_parsed = ids_parsed + 1
    303             enum_list = enum.split('_') # grab sections of enum for use below
    304             # Default update values to be the same
    305             update_enum = enum
    306             update_msg = self.val_error_dict[enum]['error_msg']
    307             update_api = self.val_error_dict[enum]['api']
    308             # Any user-forced remap takes precendence
    309             if enum_list[-1] in remap_dict:
    310                 enum_list[-1] = remap_dict[enum_list[-1]]
    311                 new_enum = "_".join(enum_list)
    312                 print "NOTE: Using user-supplied remap to force %s to be %s" % (enum, new_enum)
    313                 update_enum = new_enum
    314             elif enum in orig_error_msg_dict:
    315                 if self.val_error_dict[enum]['error_msg'] == orig_error_msg_dict[enum]:
    316                     print "Exact match for enum %s" % (enum)
    317                     # Nothing to see here
    318                     if enum in updated_val_error_dict:
    319                         print "ERROR: About to overwrite entry for %s" % (enum)
    320                 elif self.val_error_dict[enum]['error_msg'] in orig_err_to_id_dict:
    321                     # Same value w/ different error id, need to anchor to original id
    322                     print "Need to switch new id %s to original id %s" % (enum, orig_err_to_id_dict[self.val_error_dict[enum]['error_msg']])
    323                     # Update id at end of new enum to be same id from original enum
    324                     enum_list[-1] = orig_err_to_id_dict[self.val_error_dict[enum]['error_msg']].split('_')[-1]
    325                     new_enum = "_".join(enum_list)
    326                     if new_enum in updated_val_error_dict:
    327                         print "ERROR: About to overwrite entry for %s" % (new_enum)
    328                     update_enum = new_enum
    329                 else:
    330                     # No error match:
    331                     #  First check if only link has changed, in which case keep ID but update message
    332                     orig_msg_list = orig_error_msg_dict[enum].split('(', 1)
    333                     new_msg_list = self.val_error_dict[enum]['error_msg'].split('(', 1)
    334                     if orig_msg_list[0] == new_msg_list[0]: # Msg is same bug link has changed, keep enum & update msg
    335                         print "NOTE: Found that only spec link changed for %s so keeping same id w/ new link" % (enum)
    336                     #  This seems to be a new error so need to pick it up from end of original unique ids & flag for review
    337                     else:
    338                         enum_list[-1] = "%05d" % (next_id)
    339                         new_enum = "_".join(enum_list)
    340                         next_id = next_id + 1
    341                         print "MANUALLY VERIFY: Updated new enum %s to be unique %s. Make sure new error msg is actually unique and not just changed" % (enum, new_enum)
    342                         print "   New error string: %s" % (self.val_error_dict[enum]['error_msg'])
    343                         if new_enum in updated_val_error_dict:
    344                             print "ERROR: About to overwrite entry for %s" % (new_enum)
    345                         update_enum = new_enum
    346             else: # new enum is not in orig db
    347                 if self.val_error_dict[enum]['error_msg'] in orig_err_to_id_dict:
    348                     print "New enum %s not in orig dict, but exact error message matches original unique id %s" % (enum, orig_err_to_id_dict[self.val_error_dict[enum]['error_msg']])
    349                     # Update new unique_id to use original
    350                     enum_list[-1] = orig_err_to_id_dict[self.val_error_dict[enum]['error_msg']].split('_')[-1]
    351                     new_enum = "_".join(enum_list)
    352                     if new_enum in updated_val_error_dict:
    353                         print "ERROR: About to overwrite entry for %s" % (new_enum)
    354                     update_enum = new_enum
    355                 else:
    356                     enum_list[-1] = "%05d" % (next_id)
    357                     new_enum = "_".join(enum_list)
    358                     next_id = next_id + 1
    359                     print "Completely new id and error code, update new id from %s to unique %s" % (enum, new_enum)
    360                     if new_enum in updated_val_error_dict:
    361                         print "ERROR: About to overwrite entry for %s" % (new_enum)
    362                     update_enum = new_enum
    363             updated_val_error_dict[update_enum] = {}
    364             updated_val_error_dict[update_enum]['error_msg'] = update_msg
    365             updated_val_error_dict[update_enum]['api'] = update_api
    366         # Assign parsed dict to be the udpated dict based on db compare
    367         print "In compareDB parsed %d entries" % (ids_parsed)
    368         return updated_val_error_dict
    369     def validateUpdateDict(self, update_dict):
    370         """Compare original dict vs. update dict and make sure that all of the checks are still there"""
    371         # Currently just make sure that the same # of checks as the original checks are there
    372         #orig_ids = {}
    373         orig_id_count = len(self.val_error_dict)
    374         #update_ids = {}
    375         update_id_count = len(update_dict)
    376         if orig_id_count != update_id_count:
    377             print "Original dict had %d unique_ids, but updated dict has %d!" % (orig_id_count, update_id_count)
    378             return False
    379         print "Original dict and updated dict both have %d unique_ids. Great!" % (orig_id_count)
    380         return True
    381         # TODO : include some more analysis
    382 
    383 # User passes in arg of form <new_id1>-<old_id1>[,count1]:<new_id2>-<old_id2>[,count2]:...
    384 #  new_id# = the new enum id that was assigned to an error
    385 #  old_id# = the previous enum id that was assigned to the same error
    386 #  [,count#] = The number of ids to remap starting at new_id#=old_id# and ending at new_id[#+count#-1]=old_id[#+count#-1]
    387 #     If not supplied, then ,1 is assumed, which will only update a single id
    388 def updateRemapDict(remap_string):
    389     """Set up global remap_dict based on user input"""
    390     remap_list = remap_string.split(":")
    391     for rmap in remap_list:
    392         count = 1 # Default count if none supplied
    393         id_count_list = rmap.split(',')
    394         if len(id_count_list) > 1:
    395             count = int(id_count_list[1])
    396         new_old_id_list = id_count_list[0].split('-')
    397         for offset in range(count):
    398             remap_dict["%05d" % (int(new_old_id_list[0]) + offset)] = "%05d" % (int(new_old_id_list[1]) + offset)
    399     for new_id in sorted(remap_dict):
    400         print "Set to remap new id %s to old id %s" % (new_id, remap_dict[new_id])
    401 
    402 if __name__ == "__main__":
    403     i = 1
    404     use_online = True # Attempt to grab spec from online by default
    405     update_option = False
    406     while (i < len(sys.argv)):
    407         arg = sys.argv[i]
    408         i = i + 1
    409         if (arg == '-spec'):
    410             spec_filename = sys.argv[i]
    411             # If user specifies local specfile, skip online
    412             use_online = False
    413             i = i + 1
    414         elif (arg == '-out'):
    415             out_filename = sys.argv[i]
    416             i = i + 1
    417         elif (arg == '-gendb'):
    418             gen_db = True
    419             # Set filename if supplied, else use default
    420             if i < len(sys.argv) and not sys.argv[i].startswith('-'):
    421                 db_filename = sys.argv[i]
    422                 i = i + 1
    423         elif (arg == '-compare'):
    424             db_filename = sys.argv[i]
    425             spec_compare = True
    426             i = i + 1
    427         elif (arg == '-update'):
    428             update_option = True
    429             spec_compare = True
    430             gen_db = True
    431         elif (arg == '-remap'):
    432             updateRemapDict(sys.argv[i])
    433             i = i + 1
    434         elif (arg in ['-help', '-h']):
    435             printHelp()
    436             sys.exit()
    437     if len(remap_dict) > 1 and not update_option:
    438         print "ERROR: '-remap' option can only be used along with '-update' option. Exiting."
    439         sys.exit()
    440     spec = Specification()
    441     spec.loadFile(use_online, spec_filename)
    442     #spec.parseTree()
    443     #spec.genHeader(out_filename)
    444     spec.analyze()
    445     if (spec_compare):
    446         # Read in old spec info from db file
    447         (orig_err_msg_dict, max_id) = spec.readDB(db_filename)
    448         # New spec data should already be read into self.val_error_dict
    449         updated_dict = spec.compareDB(orig_err_msg_dict, max_id)
    450         update_valid = spec.validateUpdateDict(updated_dict)
    451         if update_valid:
    452             spec.updateDict(updated_dict)
    453         else:
    454             sys.exit()
    455     if (gen_db):
    456         spec.genDB(db_filename)
    457     print "Writing out file (-out) to '%s'" % (out_filename)
    458     spec.genHeader(out_filename)
    459 
    460 ##### Example dataset
    461 # <div class="sidebar">
    462 #   <div class="titlepage">
    463 #     <div>
    464 #       <div>
    465 #         <p class="title">
    466 #           <strong>Valid Usage</strong> # When we get to this guy, we know we're under interesting sidebar
    467 #         </p>
    468 #       </div>
    469 #     </div>
    470 #   </div>
    471 # <div class="itemizedlist">
    472 #   <ul class="itemizedlist" style="list-style-type: disc; ">
    473 #     <li class="listitem">
    474 #       <em class="parameter">
    475 #         <code>device</code>
    476 #       </em>
    477 #       <span class="normative">must</span> be a valid
    478 #       <code class="code">VkDevice</code> handle
    479 #     </li>
    480 #     <li class="listitem">
    481 #       <em class="parameter">
    482 #         <code>commandPool</code>
    483 #       </em>
    484 #       <span class="normative">must</span> be a valid
    485 #       <code class="code">VkCommandPool</code> handle
    486 #     </li>
    487 #     <li class="listitem">
    488 #       <em class="parameter">
    489 #         <code>flags</code>
    490 #       </em>
    491 #       <span class="normative">must</span> be a valid combination of
    492 #       <code class="code">
    493 #         <a class="link" href="#VkCommandPoolResetFlagBits">VkCommandPoolResetFlagBits</a>
    494 #       </code> values
    495 #     </li>
    496 #     <li class="listitem">
    497 #       <em class="parameter">
    498 #         <code>commandPool</code>
    499 #       </em>
    500 #       <span class="normative">must</span> have been created, allocated, or retrieved from
    501 #       <em class="parameter">
    502 #         <code>device</code>
    503 #       </em>
    504 #     </li>
    505 #     <li class="listitem">All
    506 #       <code class="code">VkCommandBuffer</code>
    507 #       objects allocated from
    508 #       <em class="parameter">
    509 #         <code>commandPool</code>
    510 #       </em>
    511 #       <span class="normative">must</span> not currently be pending execution
    512 #     </li>
    513 #   </ul>
    514 # </div>
    515 # </div>
    516 ##### Second example dataset
    517 # <div class="sidebar">
    518 #   <div class="titlepage">
    519 #     <div>
    520 #       <div>
    521 #         <p class="title">
    522 #           <strong>Valid Usage</strong>
    523 #         </p>
    524 #       </div>
    525 #     </div>
    526 #   </div>
    527 #   <div class="itemizedlist">
    528 #     <ul class="itemizedlist" style="list-style-type: disc; ">
    529 #       <li class="listitem">The <em class="parameter"><code>queueFamilyIndex</code></em> member of any given element of <em class="parameter"><code>pQueueCreateInfos</code></em> <span class="normative">must</span> be unique within <em class="parameter"><code>pQueueCreateInfos</code></em>
    530 #       </li>
    531 #     </ul>
    532 #   </div>
    533 # </div>
    534 # <div class="sidebar">
    535 #   <div class="titlepage">
    536 #     <div>
    537 #       <div>
    538 #         <p class="title">
    539 #           <strong>Valid Usage (Implicit)</strong>
    540 #         </p>
    541 #       </div>
    542 #     </div>
    543 #   </div>
    544 #   <div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">
    545 #<em class="parameter"><code>sType</code></em> <span class="normative">must</span> be <code class="code">VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO</code>
    546 #</li><li class="listitem">
    547 #<em class="parameter"><code>pNext</code></em> <span class="normative">must</span> be <code class="literal">NULL</code>
    548 #</li><li class="listitem">
    549 #<em class="parameter"><code>flags</code></em> <span class="normative">must</span> be <code class="literal">0</code>
    550 #</li><li class="listitem">
    551 #<em class="parameter"><code>pQueueCreateInfos</code></em> <span class="normative">must</span> be a pointer to an array of <em class="parameter"><code>queueCreateInfoCount</code></em> valid <code class="code">VkDeviceQueueCreateInfo</code> structures
    552 #</li>