blob: bcece9537ddb910b5fec5f38d296e01d101ee329 [file] [log] [blame]
Tobin Ehlis5ade0692016-10-05 17:18:15 -06001#!/usr/bin/python3 -i
2
3import sys
4import xml.etree.ElementTree as etree
5import urllib2
Tobin Ehlis5ade0692016-10-05 17:18:15 -06006
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
Tobin Ehlis5ade0692016-10-05 17:18:15 -060028#
29#############################
30
31
32spec_filename = "vkspec.html" # can override w/ '-spec <filename>' option
33out_filename = "vk_validation_error_messages.h" # can override w/ '-out <filename>' option
34db_filename = "vk_validation_error_database.txt" # can override w/ '-gendb <filename>' option
35gen_db = False # set to True when '-gendb <filename>' option provided
36spec_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
Tobin Ehlisbd0a9c62016-10-14 18:06:16 -060038#old_spec_url = "https://www.khronos.org/registry/vulkan/specs/1.0/xhtml/vkspec.html"
39spec_url = "https://www.khronos.org/registry/vulkan/specs/1.0-extensions/xhtml/vkspec.html"
Tobin Ehlis5ade0692016-10-05 17:18:15 -060040# 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
42error_msg_prefix = "For more information refer to Vulkan Spec Section "
43ns = {'ns': 'http://www.w3.org/1999/xhtml'}
Mark Lobodzinski629d47b2016-10-18 13:34:58 -060044validation_error_enum_name = "VALIDATION_ERROR_"
Tobin Ehlis5ade0692016-10-05 17:18:15 -060045# Dict of new enum values that should be forced to remap to old handles, explicitly set by -remap option
46remap_dict = {}
47
48def 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
62class Specification:
63 def __init__(self):
64 self.tree = None
Tobin Ehlise7560e72016-10-19 15:59:38 -060065 self.val_error_dict = {} # string for enum is key that references 'error_msg' and 'api'
Tobin Ehlis5ade0692016-10-05 17:18:15 -060066 self.error_db_dict = {} # dict of previous error values read in from database file
67 self.delimiter = '~^~' # delimiter for db file
Tobin Ehlis2a176b12017-01-11 16:18:20 -070068 self.implicit_count = 0
Tobin Ehlis5ade0692016-10-05 17:18:15 -060069 self.copyright = """/* THIS FILE IS GENERATED. DO NOT EDIT. */
70
71/*
72 * Vulkan
73 *
74 * Copyright (c) 2016 Google Inc.
Mark Lobodzinski629d47b2016-10-18 13:34:58 -060075 * Copyright (c) 2016 LunarG, Inc.
Tobin Ehlis5ade0692016-10-05 17:18:15 -060076 *
77 * Licensed under the Apache License, Version 2.0 (the "License");
78 * you may not use this file except in compliance with the License.
79 * You may obtain a copy of the License at
80 *
81 * http://www.apache.org/licenses/LICENSE-2.0
82 *
83 * Unless required by applicable law or agreed to in writing, software
84 * distributed under the License is distributed on an "AS IS" BASIS,
85 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
86 * See the License for the specific language governing permissions and
87 * limitations under the License.
88 *
89 * Author: Tobin Ehlis <tobine@google.com>
90 */"""
91 def _checkInternetSpec(self):
92 """Verify that we can access the spec online"""
93 try:
94 online = urllib2.urlopen(spec_url,timeout=1)
95 return True
96 except urllib2.URLError as err:
97 return False
98 return False
99 def loadFile(self, online=True, spec_file=spec_filename):
100 """Load an API registry XML file into a Registry object and parse it"""
101 # Check if spec URL is available
102 if (online and self._checkInternetSpec()):
103 print "Using spec from online at %s" % (spec_url)
104 self.tree = etree.parse(urllib2.urlopen(spec_url))
105 else:
106 print "Using local spec %s" % (spec_file)
107 self.tree = etree.parse(spec_file)
108 #self.tree.write("tree_output.xhtml")
109 #self.tree = etree.parse("tree_output.xhtml")
110 self.parseTree()
111 def updateDict(self, updated_dict):
112 """Assign internal dict to use updated_dict"""
113 self.val_error_dict = updated_dict
114 def parseTree(self):
115 """Parse the registry Element, once created"""
116 print "Parsing spec file..."
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600117 unique_enum_id = 0
118 self.root = self.tree.getroot()
119 #print "ROOT: %s" % self.root
120 prev_heading = '' # Last seen section heading or sub-heading
121 prev_link = '' # Last seen link id within the spec
Tobin Ehlise7560e72016-10-19 15:59:38 -0600122 api_function = '' # API call that a check appears under
Tobin Ehlis85008cd2016-10-19 15:32:35 -0600123 error_strings = set() # Flag any exact duplicate error strings and skip them
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700124 implicit_count = 0
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600125 for tag in self.root.iter(): # iterate down tree
126 # Grab most recent section heading and link
127 if tag.tag in ['{http://www.w3.org/1999/xhtml}h2', '{http://www.w3.org/1999/xhtml}h3']:
128 if tag.get('class') != 'title':
129 continue
130 #print "Found heading %s" % (tag.tag)
131 prev_heading = "".join(tag.itertext())
132 # Insert a space between heading number & title
133 sh_list = prev_heading.rsplit('.', 1)
134 prev_heading = '. '.join(sh_list)
135 prev_link = tag[0].get('id')
136 #print "Set prev_heading %s to have link of %s" % (prev_heading.encode("ascii", "ignore"), prev_link.encode("ascii", "ignore"))
137 elif tag.tag == '{http://www.w3.org/1999/xhtml}a': # grab any intermediate links
138 if tag.get('id') != None:
139 prev_link = tag.get('id')
Tobin Ehlis16b159c2016-10-25 06:33:27 -0600140 #print "Updated prev link to %s" % (prev_link)
Tobin Ehlise7560e72016-10-19 15:59:38 -0600141 elif tag.tag == '{http://www.w3.org/1999/xhtml}pre' and tag.get('class') == 'programlisting':
142 # Check and see if this is API function
143 code_text = "".join(tag.itertext()).replace('\n', '')
144 code_text_list = code_text.split()
145 if len(code_text_list) > 1 and code_text_list[1].startswith('vk'):
146 api_function = code_text_list[1].strip('(')
Tobin Ehlisf4245cb2016-10-31 07:55:19 -0600147 #print "Found API function: %s" % (api_function)
Tobin Ehlis69ebddf2016-10-18 15:55:07 -0600148 elif tag.tag == '{http://www.w3.org/1999/xhtml}div' and tag.get('class') == 'sidebar':
149 # parse down sidebar to check for valid usage cases
150 valid_usage = False
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700151 implicit = False
Tobin Ehlis69ebddf2016-10-18 15:55:07 -0600152 for elem in tag.iter():
153 if elem.tag == '{http://www.w3.org/1999/xhtml}strong' and None != elem.text and 'Valid Usage' in elem.text:
154 valid_usage = True
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700155 if '(Implicit)' in elem.text:
156 implicit = True
157 else:
158 implicit = False
Tobin Ehlis69ebddf2016-10-18 15:55:07 -0600159 elif valid_usage and elem.tag == '{http://www.w3.org/1999/xhtml}li': # grab actual valid usage requirements
160 error_msg_str = "%s '%s' which states '%s' (%s#%s)" % (error_msg_prefix, prev_heading, "".join(elem.itertext()).replace('\n', ''), spec_url, prev_link)
161 # Some txt has multiple spaces so split on whitespace and join w/ single space
162 error_msg_str = " ".join(error_msg_str.split())
Tobin Ehlis85008cd2016-10-19 15:32:35 -0600163 if error_msg_str in error_strings:
164 print "WARNING: SKIPPING adding repeat entry for string. Please review spec and file issue as appropriate. Repeat string is: %s" % (error_msg_str)
165 else:
166 error_strings.add(error_msg_str)
167 enum_str = "%s%05d" % (validation_error_enum_name, unique_enum_id)
168 # TODO : '\' chars in spec error messages are most likely bad spec txt that needs to be updated
Tobin Ehlise7560e72016-10-19 15:59:38 -0600169 self.val_error_dict[enum_str] = {}
170 self.val_error_dict[enum_str]['error_msg'] = error_msg_str.encode("ascii", "ignore").replace("\\", "/")
171 self.val_error_dict[enum_str]['api'] = api_function
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700172 self.val_error_dict[enum_str]['implicit'] = False
173 if implicit:
174 self.val_error_dict[enum_str]['implicit'] = True
175 self.implicit_count = self.implicit_count + 1
Tobin Ehlis85008cd2016-10-19 15:32:35 -0600176 unique_enum_id = unique_enum_id + 1
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600177 #print "Validation Error Dict has a total of %d unique errors and contents are:\n%s" % (unique_enum_id, self.val_error_dict)
178 def genHeader(self, header_file):
179 """Generate a header file based on the contents of a parsed spec"""
180 print "Generating header %s..." % (header_file)
181 file_contents = []
182 file_contents.append(self.copyright)
183 file_contents.append('\n#pragma once')
Tobin Ehlisbf98b692016-10-06 12:58:06 -0600184 file_contents.append('#include <unordered_map>')
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600185 file_contents.append('\n// enum values for unique validation error codes')
186 file_contents.append('// Corresponding validation error message for each enum is given in the mapping table below')
187 file_contents.append('// When a given error occurs, these enum values should be passed to the as the messageCode')
188 file_contents.append('// parameter to the PFN_vkDebugReportCallbackEXT function')
Tobin Ehlis387fd632016-12-08 13:32:05 -0700189 enum_decl = ['enum UNIQUE_VALIDATION_ERROR_CODE {\n VALIDATION_ERROR_UNDEFINED = -1,']
Tobin Ehlisbf98b692016-10-06 12:58:06 -0600190 error_string_map = ['static std::unordered_map<int, char const *const> validation_error_map{']
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600191 for enum in sorted(self.val_error_dict):
192 #print "Header enum is %s" % (enum)
193 enum_decl.append(' %s = %d,' % (enum, int(enum.split('_')[-1])))
Tobin Ehlise7560e72016-10-19 15:59:38 -0600194 error_string_map.append(' {%s, "%s"},' % (enum, self.val_error_dict[enum]['error_msg']))
Mark Lobodzinski629d47b2016-10-18 13:34:58 -0600195 enum_decl.append(' %sMAX_ENUM = %d,' % (validation_error_enum_name, int(enum.split('_')[-1]) + 1))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600196 enum_decl.append('};')
Tobin Ehlise7560e72016-10-19 15:59:38 -0600197 error_string_map.append('};\n')
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600198 file_contents.extend(enum_decl)
199 file_contents.append('\n// Mapping from unique validation error enum to the corresponding error message')
200 file_contents.append('// The error message should be appended to the end of a custom error message that is passed')
201 file_contents.append('// as the pMessage parameter to the PFN_vkDebugReportCallbackEXT function')
202 file_contents.extend(error_string_map)
203 #print "File contents: %s" % (file_contents)
204 with open(header_file, "w") as outfile:
205 outfile.write("\n".join(file_contents))
206 def analyze(self):
207 """Print out some stats on the valid usage dict"""
208 # Create dict for # of occurences of identical strings
209 str_count_dict = {}
210 unique_id_count = 0
211 for enum in self.val_error_dict:
Tobin Ehlise7560e72016-10-19 15:59:38 -0600212 err_str = self.val_error_dict[enum]['error_msg']
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600213 if err_str in str_count_dict:
Tobin Ehlis69ebddf2016-10-18 15:55:07 -0600214 print "Found repeat error string"
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600215 str_count_dict[err_str] = str_count_dict[err_str] + 1
216 else:
217 str_count_dict[err_str] = 1
218 unique_id_count = unique_id_count + 1
219 print "Processed %d unique_ids" % (unique_id_count)
220 repeat_string = 0
221 for es in str_count_dict:
222 if str_count_dict[es] > 1:
223 repeat_string = repeat_string + 1
Tobin Ehlis69ebddf2016-10-18 15:55:07 -0600224 print "String '%s' repeated %d times" % (es, repeat_string)
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600225 print "Found %d repeat strings" % (repeat_string)
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700226 print "Found %d implicit checks" % (self.implicit_count)
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600227 def genDB(self, db_file):
228 """Generate a database of check_enum, check_coded?, testname, error_string"""
229 db_lines = []
230 # Write header for database file
231 db_lines.append("# This is a database file with validation error check information")
232 db_lines.append("# Comments are denoted with '#' char")
233 db_lines.append("# The format of the lines is:")
Tobin Ehlise7560e72016-10-19 15:59:38 -0600234 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))
Mark Lobodzinski629d47b2016-10-18 13:34:58 -0600235 db_lines.append("# error_enum: Unique error enum for this check of format %s<uniqueid>" % validation_error_enum_name)
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600236 db_lines.append("# check_implemented: 'Y' if check has been implemented in layers, 'U' for unknown, or 'N' for not implemented")
237 db_lines.append("# testname: Name of validation test for this check, 'Unknown' for unknown, or 'None' if not implmented")
Tobin Ehlise7560e72016-10-19 15:59:38 -0600238 db_lines.append("# api: Vulkan API function that this check is related to")
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600239 db_lines.append("# errormsg: The unique error message for this check that includes spec language and link")
Tobin Ehlise7560e72016-10-19 15:59:38 -0600240 db_lines.append("# note: Free txt field with any custom notes related to the check in question")
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600241 for enum in sorted(self.val_error_dict):
242 # Default to unknown if check or test are implemented, then update below if appropriate
243 implemented = 'U'
244 testname = 'Unknown'
Tobin Ehlis70980c02016-10-25 14:00:20 -0600245 note = ''
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700246 implicit = self.val_error_dict[enum]['implicit']
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600247 # If we have an existing db entry for this enum, use its implemented/testname values
248 if enum in self.error_db_dict:
249 implemented = self.error_db_dict[enum]['check_implemented']
250 testname = self.error_db_dict[enum]['testname']
Tobin Ehlis70980c02016-10-25 14:00:20 -0600251 note = self.error_db_dict[enum]['note']
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700252 if implicit and 'implicit' not in note: # add implicit note
253 if '' != note:
254 note = "implicit, %s" % (note)
255 else:
256 note = "implicit"
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600257 #print "delimiter: %s, id: %s, str: %s" % (self.delimiter, enum, self.val_error_dict[enum])
258 # No existing entry so default to N for implemented and None for testname
Tobin Ehlis70980c02016-10-25 14:00:20 -0600259 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))
Tobin Ehlisaf75f7c2016-10-31 11:10:38 -0600260 db_lines.append("\n") # newline at end of file
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600261 print "Generating database file %s" % (db_file)
262 with open(db_file, "w") as outfile:
263 outfile.write("\n".join(db_lines))
264 def readDB(self, db_file):
265 """Read a db file into a dict, format of each line is <enum><implemented Y|N?><testname><errormsg>"""
266 db_dict = {} # This is a simple db of just enum->errormsg, the same as is created from spec
267 max_id = 0
268 with open(db_file, "r") as infile:
269 for line in infile:
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600270 line = line.strip()
Tobin Ehlisf4245cb2016-10-31 07:55:19 -0600271 if line.startswith('#') or '' == line:
272 continue
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600273 db_line = line.split(self.delimiter)
Tobin Ehlis70980c02016-10-25 14:00:20 -0600274 if len(db_line) != 6:
275 print "ERROR: Bad database line doesn't have 6 elements: %s" % (line)
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600276 error_enum = db_line[0]
277 implemented = db_line[1]
278 testname = db_line[2]
Tobin Ehlis70980c02016-10-25 14:00:20 -0600279 api = db_line[3]
280 error_str = db_line[4]
281 note = db_line[5]
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600282 db_dict[error_enum] = error_str
283 # Also read complete database contents into our class var for later use
284 self.error_db_dict[error_enum] = {}
285 self.error_db_dict[error_enum]['check_implemented'] = implemented
286 self.error_db_dict[error_enum]['testname'] = testname
Tobin Ehlis70980c02016-10-25 14:00:20 -0600287 self.error_db_dict[error_enum]['api'] = api
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600288 self.error_db_dict[error_enum]['error_string'] = error_str
Tobin Ehlis70980c02016-10-25 14:00:20 -0600289 self.error_db_dict[error_enum]['note'] = note
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600290 unique_id = int(db_line[0].split('_')[-1])
291 if unique_id > max_id:
292 max_id = unique_id
293 return (db_dict, max_id)
294 # Compare unique ids from original database to data generated from updated spec
295 # 1. If a new id and error code exactly match original, great
296 # 2. If new id is not in original, but exact error code is, need to use original error code
297 # 3. If new id and new error are not in original, make sure new id picks up from end of original list
298 # 4. If new id in original, but error strings don't match then:
299 # 4a. If error string has exact match in original, update new to use original
300 # 4b. If error string not in original, may be updated error message, manually address
Tobin Ehlise7560e72016-10-19 15:59:38 -0600301 def compareDB(self, orig_error_msg_dict, max_id):
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600302 """Compare orig database dict to new dict, report out findings, and return potential new dict for parsed spec"""
303 # First create reverse dicts of err_strings to IDs
304 next_id = max_id + 1
305 orig_err_to_id_dict = {}
306 # Create an updated dict in-place that will be assigned to self.val_error_dict when done
307 updated_val_error_dict = {}
Tobin Ehlise7560e72016-10-19 15:59:38 -0600308 for enum in orig_error_msg_dict:
309 orig_err_to_id_dict[orig_error_msg_dict[enum]] = enum
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600310 new_err_to_id_dict = {}
311 for enum in self.val_error_dict:
Tobin Ehlise7560e72016-10-19 15:59:38 -0600312 new_err_to_id_dict[self.val_error_dict[enum]['error_msg']] = enum
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600313 ids_parsed = 0
Tobin Ehlise7560e72016-10-19 15:59:38 -0600314 # Values to be used for the update dict
315 update_enum = ''
316 update_msg = ''
317 update_api = ''
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600318 # Now parse through new dict and figure out what to do with non-matching things
319 for enum in sorted(self.val_error_dict):
320 ids_parsed = ids_parsed + 1
321 enum_list = enum.split('_') # grab sections of enum for use below
Tobin Ehlise7560e72016-10-19 15:59:38 -0600322 # Default update values to be the same
323 update_enum = enum
324 update_msg = self.val_error_dict[enum]['error_msg']
325 update_api = self.val_error_dict[enum]['api']
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700326 implicit = self.val_error_dict[enum]['implicit']
Tobin Ehlisbd0a9c62016-10-14 18:06:16 -0600327 # Any user-forced remap takes precendence
328 if enum_list[-1] in remap_dict:
329 enum_list[-1] = remap_dict[enum_list[-1]]
330 new_enum = "_".join(enum_list)
331 print "NOTE: Using user-supplied remap to force %s to be %s" % (enum, new_enum)
Tobin Ehlise7560e72016-10-19 15:59:38 -0600332 update_enum = new_enum
333 elif enum in orig_error_msg_dict:
334 if self.val_error_dict[enum]['error_msg'] == orig_error_msg_dict[enum]:
Tobin Ehlisbd0a9c62016-10-14 18:06:16 -0600335 print "Exact match for enum %s" % (enum)
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600336 # Nothing to see here
337 if enum in updated_val_error_dict:
338 print "ERROR: About to overwrite entry for %s" % (enum)
Tobin Ehlise7560e72016-10-19 15:59:38 -0600339 elif self.val_error_dict[enum]['error_msg'] in orig_err_to_id_dict:
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600340 # Same value w/ different error id, need to anchor to original id
Tobin Ehlise7560e72016-10-19 15:59:38 -0600341 print "Need to switch new id %s to original id %s" % (enum, orig_err_to_id_dict[self.val_error_dict[enum]['error_msg']])
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600342 # Update id at end of new enum to be same id from original enum
Tobin Ehlise7560e72016-10-19 15:59:38 -0600343 enum_list[-1] = orig_err_to_id_dict[self.val_error_dict[enum]['error_msg']].split('_')[-1]
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600344 new_enum = "_".join(enum_list)
345 if new_enum in updated_val_error_dict:
346 print "ERROR: About to overwrite entry for %s" % (new_enum)
Tobin Ehlise7560e72016-10-19 15:59:38 -0600347 update_enum = new_enum
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600348 else:
349 # No error match:
350 # First check if only link has changed, in which case keep ID but update message
Tobin Ehlise7560e72016-10-19 15:59:38 -0600351 orig_msg_list = orig_error_msg_dict[enum].split('(', 1)
352 new_msg_list = self.val_error_dict[enum]['error_msg'].split('(', 1)
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600353 if orig_msg_list[0] == new_msg_list[0]: # Msg is same bug link has changed, keep enum & update msg
354 print "NOTE: Found that only spec link changed for %s so keeping same id w/ new link" % (enum)
Tobin Ehlisbd0a9c62016-10-14 18:06:16 -0600355 # This seems to be a new error so need to pick it up from end of original unique ids & flag for review
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600356 else:
357 enum_list[-1] = "%05d" % (next_id)
358 new_enum = "_".join(enum_list)
359 next_id = next_id + 1
360 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)
Tobin Ehlise7560e72016-10-19 15:59:38 -0600361 print " New error string: %s" % (self.val_error_dict[enum]['error_msg'])
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600362 if new_enum in updated_val_error_dict:
363 print "ERROR: About to overwrite entry for %s" % (new_enum)
Tobin Ehlise7560e72016-10-19 15:59:38 -0600364 update_enum = new_enum
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600365 else: # new enum is not in orig db
Tobin Ehlise7560e72016-10-19 15:59:38 -0600366 if self.val_error_dict[enum]['error_msg'] in orig_err_to_id_dict:
367 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']])
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600368 # Update new unique_id to use original
Tobin Ehlise7560e72016-10-19 15:59:38 -0600369 enum_list[-1] = orig_err_to_id_dict[self.val_error_dict[enum]['error_msg']].split('_')[-1]
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600370 new_enum = "_".join(enum_list)
371 if new_enum in updated_val_error_dict:
372 print "ERROR: About to overwrite entry for %s" % (new_enum)
Tobin Ehlise7560e72016-10-19 15:59:38 -0600373 update_enum = new_enum
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600374 else:
375 enum_list[-1] = "%05d" % (next_id)
376 new_enum = "_".join(enum_list)
377 next_id = next_id + 1
378 print "Completely new id and error code, update new id from %s to unique %s" % (enum, new_enum)
379 if new_enum in updated_val_error_dict:
380 print "ERROR: About to overwrite entry for %s" % (new_enum)
Tobin Ehlise7560e72016-10-19 15:59:38 -0600381 update_enum = new_enum
382 updated_val_error_dict[update_enum] = {}
383 updated_val_error_dict[update_enum]['error_msg'] = update_msg
384 updated_val_error_dict[update_enum]['api'] = update_api
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700385 updated_val_error_dict[update_enum]['implicit'] = implicit
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600386 # Assign parsed dict to be the udpated dict based on db compare
387 print "In compareDB parsed %d entries" % (ids_parsed)
388 return updated_val_error_dict
389 def validateUpdateDict(self, update_dict):
390 """Compare original dict vs. update dict and make sure that all of the checks are still there"""
391 # Currently just make sure that the same # of checks as the original checks are there
392 #orig_ids = {}
393 orig_id_count = len(self.val_error_dict)
394 #update_ids = {}
395 update_id_count = len(update_dict)
396 if orig_id_count != update_id_count:
397 print "Original dict had %d unique_ids, but updated dict has %d!" % (orig_id_count, update_id_count)
398 return False
399 print "Original dict and updated dict both have %d unique_ids. Great!" % (orig_id_count)
400 return True
401 # TODO : include some more analysis
402
403# User passes in arg of form <new_id1>-<old_id1>[,count1]:<new_id2>-<old_id2>[,count2]:...
404# new_id# = the new enum id that was assigned to an error
405# old_id# = the previous enum id that was assigned to the same error
406# [,count#] = The number of ids to remap starting at new_id#=old_id# and ending at new_id[#+count#-1]=old_id[#+count#-1]
407# If not supplied, then ,1 is assumed, which will only update a single id
408def updateRemapDict(remap_string):
409 """Set up global remap_dict based on user input"""
410 remap_list = remap_string.split(":")
411 for rmap in remap_list:
412 count = 1 # Default count if none supplied
413 id_count_list = rmap.split(',')
414 if len(id_count_list) > 1:
415 count = int(id_count_list[1])
416 new_old_id_list = id_count_list[0].split('-')
417 for offset in range(count):
418 remap_dict["%05d" % (int(new_old_id_list[0]) + offset)] = "%05d" % (int(new_old_id_list[1]) + offset)
419 for new_id in sorted(remap_dict):
420 print "Set to remap new id %s to old id %s" % (new_id, remap_dict[new_id])
421
422if __name__ == "__main__":
423 i = 1
424 use_online = True # Attempt to grab spec from online by default
425 update_option = False
426 while (i < len(sys.argv)):
427 arg = sys.argv[i]
428 i = i + 1
429 if (arg == '-spec'):
430 spec_filename = sys.argv[i]
431 # If user specifies local specfile, skip online
432 use_online = False
433 i = i + 1
434 elif (arg == '-out'):
435 out_filename = sys.argv[i]
436 i = i + 1
437 elif (arg == '-gendb'):
438 gen_db = True
439 # Set filename if supplied, else use default
440 if i < len(sys.argv) and not sys.argv[i].startswith('-'):
441 db_filename = sys.argv[i]
442 i = i + 1
443 elif (arg == '-compare'):
444 db_filename = sys.argv[i]
445 spec_compare = True
446 i = i + 1
447 elif (arg == '-update'):
448 update_option = True
449 spec_compare = True
450 gen_db = True
451 elif (arg == '-remap'):
452 updateRemapDict(sys.argv[i])
453 i = i + 1
454 elif (arg in ['-help', '-h']):
455 printHelp()
456 sys.exit()
457 if len(remap_dict) > 1 and not update_option:
458 print "ERROR: '-remap' option can only be used along with '-update' option. Exiting."
459 sys.exit()
460 spec = Specification()
461 spec.loadFile(use_online, spec_filename)
462 #spec.parseTree()
463 #spec.genHeader(out_filename)
464 spec.analyze()
465 if (spec_compare):
466 # Read in old spec info from db file
Tobin Ehlise7560e72016-10-19 15:59:38 -0600467 (orig_err_msg_dict, max_id) = spec.readDB(db_filename)
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600468 # New spec data should already be read into self.val_error_dict
Tobin Ehlise7560e72016-10-19 15:59:38 -0600469 updated_dict = spec.compareDB(orig_err_msg_dict, max_id)
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600470 update_valid = spec.validateUpdateDict(updated_dict)
471 if update_valid:
472 spec.updateDict(updated_dict)
473 else:
474 sys.exit()
475 if (gen_db):
476 spec.genDB(db_filename)
477 print "Writing out file (-out) to '%s'" % (out_filename)
478 spec.genHeader(out_filename)
479
480##### Example dataset
481# <div class="sidebar">
482# <div class="titlepage">
483# <div>
484# <div>
485# <p class="title">
486# <strong>Valid Usage</strong> # When we get to this guy, we know we're under interesting sidebar
487# </p>
488# </div>
489# </div>
490# </div>
491# <div class="itemizedlist">
492# <ul class="itemizedlist" style="list-style-type: disc; ">
493# <li class="listitem">
494# <em class="parameter">
495# <code>device</code>
496# </em>
497# <span class="normative">must</span> be a valid
498# <code class="code">VkDevice</code> handle
499# </li>
500# <li class="listitem">
501# <em class="parameter">
502# <code>commandPool</code>
503# </em>
504# <span class="normative">must</span> be a valid
505# <code class="code">VkCommandPool</code> handle
506# </li>
507# <li class="listitem">
508# <em class="parameter">
509# <code>flags</code>
510# </em>
511# <span class="normative">must</span> be a valid combination of
512# <code class="code">
513# <a class="link" href="#VkCommandPoolResetFlagBits">VkCommandPoolResetFlagBits</a>
514# </code> values
515# </li>
516# <li class="listitem">
517# <em class="parameter">
518# <code>commandPool</code>
519# </em>
520# <span class="normative">must</span> have been created, allocated, or retrieved from
521# <em class="parameter">
522# <code>device</code>
523# </em>
524# </li>
525# <li class="listitem">All
526# <code class="code">VkCommandBuffer</code>
527# objects allocated from
528# <em class="parameter">
529# <code>commandPool</code>
530# </em>
531# <span class="normative">must</span> not currently be pending execution
532# </li>
533# </ul>
534# </div>
535# </div>
536##### Second example dataset
537# <div class="sidebar">
538# <div class="titlepage">
539# <div>
540# <div>
541# <p class="title">
542# <strong>Valid Usage</strong>
543# </p>
544# </div>
545# </div>
546# </div>
547# <div class="itemizedlist">
548# <ul class="itemizedlist" style="list-style-type: disc; ">
549# <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>
550# </li>
551# </ul>
552# </div>
553# </div>
554# <div class="sidebar">
555# <div class="titlepage">
556# <div>
557# <div>
558# <p class="title">
559# <strong>Valid Usage (Implicit)</strong>
560# </p>
561# </div>
562# </div>
563# </div>
564# <div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">
565#<em class="parameter"><code>sType</code></em> <span class="normative">must</span> be <code class="code">VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO</code>
566#</li><li class="listitem">
567#<em class="parameter"><code>pNext</code></em> <span class="normative">must</span> be <code class="literal">NULL</code>
568#</li><li class="listitem">
569#<em class="parameter"><code>flags</code></em> <span class="normative">must</span> be <code class="literal">0</code>
570#</li><li class="listitem">
571#<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
572#</li>