blob: 737aa7b21a0f24e78d3bc02a78a91cbba81546e4 [file] [log] [blame]
Tobin Ehlis3198ba32017-04-19 17:30:52 -06001#!/usr/bin/python -i
Tobin Ehlis5ade0692016-10-05 17:18:15 -06002
3import sys
Tobin Ehlis98d109a2017-05-11 14:42:38 -06004#import xml.etree.ElementTree as etree
Tobin Ehlis5ade0692016-10-05 17:18:15 -06005import urllib2
Tobin Ehlis3198ba32017-04-19 17:30:52 -06006from bs4 import BeautifulSoup
Tobin Ehlis98d109a2017-05-11 14:42:38 -06007import json
8import vuid_mapping
Tobin Ehlis5ade0692016-10-05 17:18:15 -06009
10#############################
11# spec.py script
12#
13# Overview - this script is intended to generate validation error codes and message strings from the xhtml version of
14# the specification. In addition to generating the header file, it provides a number of corrollary services to aid in
15# generating/updating the header.
16#
17# Ideal flow - Not there currently, but the ideal flow for this script would be that you run the script, it pulls the
18# latest spec, compares it to the current set of generated error codes, and makes any updates as needed
19#
20# Current flow - the current flow acheives all of the ideal flow goals, but with more steps than are desired
21# 1. Get the spec - right now spec has to be manually generated or pulled from the web
22# 2. Generate header from spec - This is done in a single command line
23# 3. Generate database file from spec - Can be done along with step #2 above, the database file contains a list of
24# all error enums and message strings, along with some other info on if those errors are implemented/tested
25# 4. Update header using a given database file as the root and a new spec file as goal - This makes sure that existing
26# errors keep the same enum identifier while also making sure that new errors get a unique_id that continues on
27# from the end of the previous highest unique_id.
28#
29# TODO:
30# 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 -060031#
32#############################
33
34
35spec_filename = "vkspec.html" # can override w/ '-spec <filename>' option
36out_filename = "vk_validation_error_messages.h" # can override w/ '-out <filename>' option
37db_filename = "vk_validation_error_database.txt" # can override w/ '-gendb <filename>' option
Tobin Ehlis2c932132017-05-19 16:32:15 -060038json_filename = None # con pass in w/ '-json <filename> option
Tobin Ehlis5ade0692016-10-05 17:18:15 -060039gen_db = False # set to True when '-gendb <filename>' option provided
40spec_compare = False # set to True with '-compare <db_filename>' option
Tobin Ehlis2c932132017-05-19 16:32:15 -060041json_compare = False # compare existing DB to json file input
Tobin Ehlis5ade0692016-10-05 17:18:15 -060042# 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 -060043#old_spec_url = "https://www.khronos.org/registry/vulkan/specs/1.0/xhtml/vkspec.html"
Tobin Ehlisa55b1d42017-04-04 12:23:48 -060044spec_url = "https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html"
Tobin Ehlis5ade0692016-10-05 17:18:15 -060045# After the custom validation error message, this is the prefix for the standard message that includes the
46# spec valid usage language as well as the link to nearest section of spec to that language
47error_msg_prefix = "For more information refer to Vulkan Spec Section "
48ns = {'ns': 'http://www.w3.org/1999/xhtml'}
Mark Lobodzinski629d47b2016-10-18 13:34:58 -060049validation_error_enum_name = "VALIDATION_ERROR_"
Tobin Ehlis5ade0692016-10-05 17:18:15 -060050# Dict of new enum values that should be forced to remap to old handles, explicitly set by -remap option
51remap_dict = {}
Tobin Ehlis2c932132017-05-19 16:32:15 -060052# Custom map to override known-bad values
53struct_to_api_map = {
54'VkDeviceQueueCreateInfo' : 'vkCreateDevice',
55}
Tobin Ehlis5ade0692016-10-05 17:18:15 -060056
57def printHelp():
Tobin Ehlis98d109a2017-05-11 14:42:38 -060058 print ("Usage: python spec.py [-spec <specfile.html>] [-out <headerfile.h>] [-gendb <databasefile.txt>] [-compare <databasefile.txt>] [-update] [-remap <new_id-old_id,count>] [-json <json_file>] [-help]")
Tobin Ehlis3198ba32017-04-19 17:30:52 -060059 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")
60 print (" Default specfile is from online at %s" % (spec_url))
61 print (" Default headerfile is %s" % (out_filename))
62 print (" Default databasefile is %s" % (db_filename))
63 print ("\nIf '-gendb' option is specified then a database file is generated to default file or <databasefile.txt> if supplied. The database file stores")
64 print (" the list of enums and their error messages.")
65 print ("\nIf '-compare' option is specified then the given database file will be read in as the baseline for generating the new specfile")
66 print ("\nIf '-update' option is specified this triggers the master flow to automate updating header and database files using default db file as baseline")
67 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.")
68 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")
69 print (" option. Starting at newid and remapping to oldid, count ids will be remapped. Default count is '1' and use ':' to specify multiple remappings.")
Tobin Ehlis98d109a2017-05-11 14:42:38 -060070 print ("\nIf '-json' option is used to point to json file, parse the json file and generate VUIDs based on that.")
Tobin Ehlis5ade0692016-10-05 17:18:15 -060071
72class Specification:
73 def __init__(self):
74 self.tree = None
Tobin Ehlise7560e72016-10-19 15:59:38 -060075 self.val_error_dict = {} # string for enum is key that references 'error_msg' and 'api'
Tobin Ehlis5ade0692016-10-05 17:18:15 -060076 self.error_db_dict = {} # dict of previous error values read in from database file
77 self.delimiter = '~^~' # delimiter for db file
Tobin Ehlis2a176b12017-01-11 16:18:20 -070078 self.implicit_count = 0
Tobin Ehlisa55b1d42017-04-04 12:23:48 -060079 # Global dicts used for tracking spec updates from old to new VUs
80 self.orig_full_msg_dict = {} # Original full error msg to ID mapping
81 self.orig_no_link_msg_dict = {} # Pair of API,Original msg w/o spec link to ID list mapping
82 self.orig_core_msg_dict = {} # Pair of API,Original core msg (no link or section) to ID list mapping
83 self.last_mapped_id = -10 # start as negative so we don't hit an accidental sequence
84 self.orig_test_imp_enums = set() # Track old enums w/ tests and/or implementation to flag any that aren't carried fwd
Tobin Ehlis2c932132017-05-19 16:32:15 -060085 # Dict of data from json DB
86 # Key is API,<short_msg> which leads to dict w/ following values
87 # 'ext' -> <core|<ext_name>>
88 # 'string_vuid' -> <string_vuid>
89 # 'number_vuid' -> <numerical_vuid>
90 self.json_db = {}
91 self.json_missing = 0
92 self.struct_to_func_map = {} # Map structs to the API func that they fall under in the spec
93 self.duplicate_json_key_count = 0
Tobin Ehlis5ade0692016-10-05 17:18:15 -060094 self.copyright = """/* THIS FILE IS GENERATED. DO NOT EDIT. */
95
96/*
97 * Vulkan
98 *
99 * Copyright (c) 2016 Google Inc.
Mark Lobodzinski629d47b2016-10-18 13:34:58 -0600100 * Copyright (c) 2016 LunarG, Inc.
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600101 *
102 * Licensed under the Apache License, Version 2.0 (the "License");
103 * you may not use this file except in compliance with the License.
104 * You may obtain a copy of the License at
105 *
106 * http://www.apache.org/licenses/LICENSE-2.0
107 *
108 * Unless required by applicable law or agreed to in writing, software
109 * distributed under the License is distributed on an "AS IS" BASIS,
110 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
111 * See the License for the specific language governing permissions and
112 * limitations under the License.
113 *
114 * Author: Tobin Ehlis <tobine@google.com>
115 */"""
116 def _checkInternetSpec(self):
117 """Verify that we can access the spec online"""
118 try:
119 online = urllib2.urlopen(spec_url,timeout=1)
120 return True
121 except urllib2.URLError as err:
122 return False
123 return False
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600124 def soupLoadFile(self, online=True, spec_file=spec_filename):
125 """Load a spec file into BeutifulSoup"""
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600126 if (online and self._checkInternetSpec()):
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600127 print ("Making soup from spec online at %s, this will take a minute" % (spec_url))
128 self.soup = BeautifulSoup(urllib2.urlopen(spec_url), 'html.parser')
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600129 else:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600130 print ("Making soup from local spec %s, this will take a minute" % (spec_file))
Tobin Ehlisec45e422017-05-19 08:24:04 -0600131 with open(spec_file, "r") as sf:
132 self.soup = BeautifulSoup(sf, 'html.parser')
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600133 self.parseSoup()
134 #print(self.soup.prettify())
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600135 def updateDict(self, updated_dict):
136 """Assign internal dict to use updated_dict"""
137 self.val_error_dict = updated_dict
Tobin Ehlis98d109a2017-05-11 14:42:38 -0600138
139 def readJSON(self, json_file):
140 """Read in JSON file"""
141 with open(json_file) as jsf:
142 self.json_data = json.load(jsf)
143 def parseJSON(self):
144 """Parse JSON VUIDs into data struct"""
145 # Format of JSON file is:
146 # "API": { "core|EXT": [ {"vuid": "<id>", "text": "<VU txt>"}]},
147 # "VK_KHX_external_memory" & "VK_KHX_device_group" - extension case (vs. "core")
Tobin Ehlis98d109a2017-05-11 14:42:38 -0600148 for api in sorted(self.json_data):
149 for ext in sorted(self.json_data[api]):
150 for vu_txt_dict in self.json_data[api][ext]:
151 vuid = vu_txt_dict['vuid']
152 vutxt = vu_txt_dict['text']
153 #print ("%s:%s:%s:%s" % (api, ext, vuid, vutxt))
Tobin Ehlis2c932132017-05-19 16:32:15 -0600154 print ("VUTXT orig:%s" % (vutxt))
155 just_txt = BeautifulSoup(vutxt, 'html.parser')
156 #print ("VUTXT only:%s" % (just_txt.get_text()))
157 num_vuid = vuid_mapping.convertVUID(vuid)
158 function = api
159 if function in self.struct_to_func_map:
160 function = self.struct_to_func_map[function]
161 key = "%s,'%s'" % (function, just_txt.get_text())
162 if key in self.json_db:
163 print ("Key '%s' is already in json_db!" % (key))
164 self.duplicate_json_key_count = self.duplicate_json_key_count + 1
165 #sys.exit()
166 self.json_db[key] = {}
167 self.json_db[key]['ext'] = ext
168 self.json_db[key]['string_vuid'] = vuid
169 self.json_db[key]['number_vuid'] = num_vuid
170 self.json_db[key]['struct_func'] = api
171 if 'must specify aspects present in the calling command' in key:
172 print "Found KEY:%s" % (key)
173 #sys.exit()
174
175 def compareJSON(self):
176 """Compare parsed json file with existing data read in from DB file"""
177 for enum in self.error_db_dict:
178 full_error_string = self.error_db_dict[enum]['error_string']
179 api_struct = full_error_string.rsplit('#', 1)[-1].strip(')')
180 if 'VUID' in api_struct:
181 api_struct = api_struct.split('-')[1]
182
183 no_link_msg = full_error_string.split(' (https', 1)[0]
184 core_msg = no_link_msg.split(' which states ', 1)[-1]
185 #keys = []
186 #keys.append("%s,%s" % (api_struct, core_msg))
187 api = self.error_db_dict[enum]['api']
188 key = "%s,%s" % (api, core_msg)
189# if 'vkCreate' in api:
190# if 's' == api[-1] and not api.endswith('Pass'): # strip a single trailing s
191# api = api[0:-1]
192# create_struct = api.replace('vkCreate', 'Vk')
193# if create_struct.endswith('KHR'):
194# create_struct = create_struct.replace('KHR', 'CreateInfoKHR')
195# elif create_struct.endswith('NVX'):
196# create_struct = create_struct.replace('NVX', 'CreateInfoNVX')
197# else:
198# create_struct = "%sCreateInfo" % (create_struct)
199# keys.append("%s,%s" % (create_struct, core_msg))
200# elif 'vkGet' in api:
201# get_struct = api.replace('vkGet', 'Vk')
202# get_struct = get_struct.replace('Properties', 'Info')
203# keys.append("%s,%s" % (get_struct, core_msg))
204# elif 'vkAllocate' in api:
205# if 's' == api[-1]: # strip a trailing 0
206# api = api[0:-1]
207# alloc_struct = api.replace('vkAllocate', 'Vk')
208# alloc_struct = "%sAllocateInfo" % (alloc_struct)
209# keys.append("%s,%s" % (alloc_struct, core_msg))
210 #if True not in [key in self.json_db for key in keys]:
211 if key not in self.json_db:
212 print ("Full string for %s is:%s" % (enum, full_error_string))
213 print ("WARN: Couldn't find key in json db:%s" % (key))
214 self.json_missing = self.json_missing + 1
Tobin Ehlis98d109a2017-05-11 14:42:38 -0600215
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600216 def parseSoup(self):
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600217 """Parse the registry Element, once created"""
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600218 print ("Parsing spec file...")
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600219 unique_enum_id = 0
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600220 #self.root = self.tree.getroot()
221 #print ("ROOT: %s") % self.root
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600222 prev_heading = '' # Last seen section heading or sub-heading
223 prev_link = '' # Last seen link id within the spec
Tobin Ehlise7560e72016-10-19 15:59:38 -0600224 api_function = '' # API call that a check appears under
Tobin Ehlis85008cd2016-10-19 15:32:35 -0600225 error_strings = set() # Flag any exact duplicate error strings and skip them
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600226 for tag in self.soup.find_all(True):#self.root.iter(): # iterate down tree
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600227 # Grab most recent section heading and link
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600228 #print ("tag.name is %s and class is %s" % (tag.name, tag.get('class')))
229 if tag.name in ['h2', 'h3', 'h4']:
Tobin Ehlisce1e56f2017-01-25 12:42:55 -0800230 #if tag.get('class') != 'title':
231 # continue
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600232 #print ("Found heading %s w/ string %s" % (tag.name, tag.string))
233 if None == tag.string:
234 prev_heading = ""
235 else:
236 prev_heading = "".join(tag.string)
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600237 # Insert a space between heading number & title
238 sh_list = prev_heading.rsplit('.', 1)
239 prev_heading = '. '.join(sh_list)
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600240 prev_link = tag['id']
241 #print ("Set prev_heading %s to have link of %s" % (prev_heading.encode("ascii", "ignore"), prev_link.encode("ascii", "ignore")))
242 elif tag.name == 'a': # grab any intermediate links
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600243 if tag.get('id') != None:
244 prev_link = tag.get('id')
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600245 #print ("Updated prev link to %s" % (prev_link))
246 elif tag.name == 'div' and tag.get('class') is not None and tag['class'][0] == 'listingblock':
Tobin Ehlise7560e72016-10-19 15:59:38 -0600247 # Check and see if this is API function
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600248 code_text = "".join(tag.strings).replace('\n', '')
Tobin Ehlise7560e72016-10-19 15:59:38 -0600249 code_text_list = code_text.split()
250 if len(code_text_list) > 1 and code_text_list[1].startswith('vk'):
251 api_function = code_text_list[1].strip('(')
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600252 #print ("Found API function: %s" % (api_function))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600253 prev_link = api_function
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600254 #print ("Updated prev link to %s" % (prev_link))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600255 elif tag.get('id') != None:
256 prev_link = tag.get('id')
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600257 #print ("Updated prev link to %s" % (prev_link))
Tobin Ehlis2c932132017-05-19 16:32:15 -0600258 if tag.get('id').startswith('Vk') and api_function != '':
259 #if len(code_text_list) > 1 and code_text_list[1].startswith('Vk'):
260 self.struct_to_func_map[tag.get('id')] = api_function
261 print ("Added mapping from struct:%s to API:%s" % (tag.get('id'), api_function))
262
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600263 #elif tag.name == '{http://www.w3.org/1999/xhtml}div' and tag.get('class') == 'sidebar':
264 elif tag.name == 'div' and tag.get('class') is not None and tag['class'][0] == 'content':
265 #print("Parsing down a div content tag")
Tobin Ehlis69ebddf2016-10-18 15:55:07 -0600266 # parse down sidebar to check for valid usage cases
267 valid_usage = False
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700268 implicit = False
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600269 for elem in tag.find_all(True):
270 #print(" elem is %s w/ string %s" % (elem.name, elem.string))
271 if elem.name == 'div' and None != elem.string and 'Valid Usage' in elem.string:
Tobin Ehlis69ebddf2016-10-18 15:55:07 -0600272 valid_usage = True
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600273 if '(Implicit)' in elem.string:
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700274 implicit = True
275 else:
276 implicit = False
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600277 elif valid_usage and elem.name == 'li': # grab actual valid usage requirements
Tobin Ehlis2c932132017-05-19 16:32:15 -0600278 # Grab link
279 prev_link = elem.a.get('id')
280 if 'VUID' not in prev_link:
281 print ("Found VU link that doesn't have 'VUID':%s" % (prev_link))
282 sys.exit()
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600283 #print("I think this is a VU w/ elem.strings is %s" % (elem.strings))
284 error_msg_str = "%s '%s' which states '%s' (%s#%s)" % (error_msg_prefix, prev_heading, "".join(elem.strings).replace('\n', ' ').strip(), spec_url, prev_link)
Tobin Ehlis69ebddf2016-10-18 15:55:07 -0600285 # Some txt has multiple spaces so split on whitespace and join w/ single space
286 error_msg_str = " ".join(error_msg_str.split())
Tobin Ehlis85008cd2016-10-19 15:32:35 -0600287 if error_msg_str in error_strings:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600288 print ("WARNING: SKIPPING adding repeat entry for string. Please review spec and file issue as appropriate. Repeat string is: %s" % (error_msg_str))
Tobin Ehlis85008cd2016-10-19 15:32:35 -0600289 else:
290 error_strings.add(error_msg_str)
291 enum_str = "%s%05d" % (validation_error_enum_name, unique_enum_id)
292 # TODO : '\' chars in spec error messages are most likely bad spec txt that needs to be updated
Tobin Ehlise7560e72016-10-19 15:59:38 -0600293 self.val_error_dict[enum_str] = {}
294 self.val_error_dict[enum_str]['error_msg'] = error_msg_str.encode("ascii", "ignore").replace("\\", "/")
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600295 self.val_error_dict[enum_str]['api'] = api_function.encode("ascii", "ignore")
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700296 self.val_error_dict[enum_str]['implicit'] = False
Tobin Ehlis2c932132017-05-19 16:32:15 -0600297 self.val_error_dict[enum_str]['vuid_string'] = prev_link
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700298 if implicit:
299 self.val_error_dict[enum_str]['implicit'] = True
300 self.implicit_count = self.implicit_count + 1
Tobin Ehlis85008cd2016-10-19 15:32:35 -0600301 unique_enum_id = unique_enum_id + 1
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600302 #print ("Validation Error Dict has a total of %d unique errors and contents are:\n%s" % (unique_enum_id, self.val_error_dict))
Tobin Ehlis2c932132017-05-19 16:32:15 -0600303 # TEMP : override struct to api mapping with manual data
304 for struct in struct_to_api_map:
305 self.struct_to_func_map[struct] = struct_to_api_map[struct]
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600306 print ("Validation Error Dict has a total of %d unique errors" % (unique_enum_id))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600307 def genHeader(self, header_file):
308 """Generate a header file based on the contents of a parsed spec"""
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600309 print ("Generating header %s..." % (header_file))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600310 file_contents = []
311 file_contents.append(self.copyright)
312 file_contents.append('\n#pragma once')
Mark Lobodzinski267a7cf2017-01-25 09:33:25 -0700313 file_contents.append('\n// Disable auto-formatting for generated file')
314 file_contents.append('// clang-format off')
315 file_contents.append('\n#include <unordered_map>')
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600316 file_contents.append('\n// enum values for unique validation error codes')
317 file_contents.append('// Corresponding validation error message for each enum is given in the mapping table below')
318 file_contents.append('// When a given error occurs, these enum values should be passed to the as the messageCode')
319 file_contents.append('// parameter to the PFN_vkDebugReportCallbackEXT function')
Tobin Ehlis387fd632016-12-08 13:32:05 -0700320 enum_decl = ['enum UNIQUE_VALIDATION_ERROR_CODE {\n VALIDATION_ERROR_UNDEFINED = -1,']
Tobin Ehlisbf98b692016-10-06 12:58:06 -0600321 error_string_map = ['static std::unordered_map<int, char const *const> validation_error_map{']
Tobin Ehlisce1e56f2017-01-25 12:42:55 -0800322 enum_value = 0
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600323 for enum in sorted(self.val_error_dict):
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600324 #print ("Header enum is %s" % (enum))
Tobin Ehlisce1e56f2017-01-25 12:42:55 -0800325 enum_value = int(enum.split('_')[-1])
326 enum_decl.append(' %s = %d,' % (enum, enum_value))
Tobin Ehlise7560e72016-10-19 15:59:38 -0600327 error_string_map.append(' {%s, "%s"},' % (enum, self.val_error_dict[enum]['error_msg']))
Tobin Ehlisce1e56f2017-01-25 12:42:55 -0800328 enum_decl.append(' %sMAX_ENUM = %d,' % (validation_error_enum_name, enum_value + 1))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600329 enum_decl.append('};')
Tobin Ehlise7560e72016-10-19 15:59:38 -0600330 error_string_map.append('};\n')
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600331 file_contents.extend(enum_decl)
332 file_contents.append('\n// Mapping from unique validation error enum to the corresponding error message')
333 file_contents.append('// The error message should be appended to the end of a custom error message that is passed')
334 file_contents.append('// as the pMessage parameter to the PFN_vkDebugReportCallbackEXT function')
335 file_contents.extend(error_string_map)
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600336 #print ("File contents: %s" % (file_contents))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600337 with open(header_file, "w") as outfile:
338 outfile.write("\n".join(file_contents))
339 def analyze(self):
340 """Print out some stats on the valid usage dict"""
341 # Create dict for # of occurences of identical strings
342 str_count_dict = {}
343 unique_id_count = 0
344 for enum in self.val_error_dict:
Tobin Ehlise7560e72016-10-19 15:59:38 -0600345 err_str = self.val_error_dict[enum]['error_msg']
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600346 if err_str in str_count_dict:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600347 print ("Found repeat error string")
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600348 str_count_dict[err_str] = str_count_dict[err_str] + 1
349 else:
350 str_count_dict[err_str] = 1
351 unique_id_count = unique_id_count + 1
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600352 print ("Processed %d unique_ids" % (unique_id_count))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600353 repeat_string = 0
354 for es in str_count_dict:
355 if str_count_dict[es] > 1:
356 repeat_string = repeat_string + 1
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600357 print ("String '%s' repeated %d times" % (es, repeat_string))
358 print ("Found %d repeat strings" % (repeat_string))
359 print ("Found %d implicit checks" % (self.implicit_count))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600360 def genDB(self, db_file):
Tobin Ehlis2c932132017-05-19 16:32:15 -0600361 """Generate a database of check_enum, check_coded?, testname, API, VUID_string, error_string"""
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600362 db_lines = []
363 # Write header for database file
364 db_lines.append("# This is a database file with validation error check information")
365 db_lines.append("# Comments are denoted with '#' char")
366 db_lines.append("# The format of the lines is:")
Tobin Ehlis2c932132017-05-19 16:32:15 -0600367 db_lines.append("# <error_enum>%s<check_implemented>%s<testname>%s<api>%s<vuid_string>%s<errormsg>%s<note>" % (self.delimiter, self.delimiter, self.delimiter, self.delimiter, self.delimiter, self.delimiter))
Mark Lobodzinski629d47b2016-10-18 13:34:58 -0600368 db_lines.append("# error_enum: Unique error enum for this check of format %s<uniqueid>" % validation_error_enum_name)
Mike Weiblenfe186122017-02-03 12:44:53 -0700369 db_lines.append("# check_implemented: 'Y' if check has been implemented in layers, or 'N' for not implemented")
Dave Houlton14f7e662017-05-17 13:25:53 -0600370 db_lines.append("# testname: Name of validation test for this check, 'Unknown' for unknown, 'None' if not implemented, or 'NotTestable' if cannot be implemented")
Tobin Ehlise7560e72016-10-19 15:59:38 -0600371 db_lines.append("# api: Vulkan API function that this check is related to")
Tobin Ehlis2c932132017-05-19 16:32:15 -0600372 db_lines.append("# vuid_string: Unique string to identify this check")
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600373 db_lines.append("# errormsg: The unique error message for this check that includes spec language and link")
Tobin Ehlise7560e72016-10-19 15:59:38 -0600374 db_lines.append("# note: Free txt field with any custom notes related to the check in question")
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600375 for enum in sorted(self.val_error_dict):
Mike Weiblenfe186122017-02-03 12:44:53 -0700376 # Default check/test implementation status to N/Unknown, then update below if appropriate
377 implemented = 'N'
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600378 testname = 'Unknown'
Tobin Ehlis70980c02016-10-25 14:00:20 -0600379 note = ''
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700380 implicit = self.val_error_dict[enum]['implicit']
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600381 # If we have an existing db entry for this enum, use its implemented/testname values
382 if enum in self.error_db_dict:
383 implemented = self.error_db_dict[enum]['check_implemented']
384 testname = self.error_db_dict[enum]['testname']
Tobin Ehlis70980c02016-10-25 14:00:20 -0600385 note = self.error_db_dict[enum]['note']
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700386 if implicit and 'implicit' not in note: # add implicit note
387 if '' != note:
388 note = "implicit, %s" % (note)
389 else:
390 note = "implicit"
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600391 #print ("delimiter: %s, id: %s, str: %s" % (self.delimiter, enum, self.val_error_dict[enum])
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600392 # No existing entry so default to N for implemented and None for testname
Tobin Ehlis2c932132017-05-19 16:32:15 -0600393 db_lines.append("%s%s%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]['vuid_string'], self.delimiter, self.val_error_dict[enum]['error_msg'], self.delimiter, note))
Tobin Ehlisaf75f7c2016-10-31 11:10:38 -0600394 db_lines.append("\n") # newline at end of file
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600395 print ("Generating database file %s" % (db_file))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600396 with open(db_file, "w") as outfile:
397 outfile.write("\n".join(db_lines))
398 def readDB(self, db_file):
Tobin Ehlis2c932132017-05-19 16:32:15 -0600399 """Read a db file into a dict, refer to genDB function above for format of each line"""
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600400 db_dict = {} # This is a simple db of just enum->errormsg, the same as is created from spec
401 max_id = 0
402 with open(db_file, "r") as infile:
403 for line in infile:
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600404 line = line.strip()
Tobin Ehlisf4245cb2016-10-31 07:55:19 -0600405 if line.startswith('#') or '' == line:
406 continue
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600407 db_line = line.split(self.delimiter)
Tobin Ehlis70980c02016-10-25 14:00:20 -0600408 if len(db_line) != 6:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600409 print ("ERROR: Bad database line doesn't have 6 elements: %s" % (line))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600410 error_enum = db_line[0]
411 implemented = db_line[1]
412 testname = db_line[2]
Tobin Ehlis70980c02016-10-25 14:00:20 -0600413 api = db_line[3]
414 error_str = db_line[4]
415 note = db_line[5]
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600416 db_dict[error_enum] = error_str
417 # Also read complete database contents into our class var for later use
418 self.error_db_dict[error_enum] = {}
419 self.error_db_dict[error_enum]['check_implemented'] = implemented
420 self.error_db_dict[error_enum]['testname'] = testname
Tobin Ehlis70980c02016-10-25 14:00:20 -0600421 self.error_db_dict[error_enum]['api'] = api
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600422 self.error_db_dict[error_enum]['error_string'] = error_str
Tobin Ehlis70980c02016-10-25 14:00:20 -0600423 self.error_db_dict[error_enum]['note'] = note
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600424 unique_id = int(db_line[0].split('_')[-1])
425 if unique_id > max_id:
426 max_id = unique_id
427 return (db_dict, max_id)
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600428 # This is a helper function to do bookkeeping on data structs when comparing original
429 # error ids to current error ids
430 # It tracks all updated enums in mapped_enums and removes those enums from any lists
431 # in the no_link and core dicts
432 def _updateMappedEnum(self, mapped_enums, enum):
433 mapped_enums.add(enum)
434 # When looking for ID to map, we favor sequences so track last ID mapped
435 self.last_mapped_id = int(enum.split('_')[-1])
436 for msg in self.orig_no_link_msg_dict:
437 if enum in self.orig_no_link_msg_dict[msg]:
438 self.orig_no_link_msg_dict[msg].remove(enum)
439 for msg in self.orig_core_msg_dict:
440 if enum in self.orig_core_msg_dict[msg]:
441 self.orig_core_msg_dict[msg].remove(enum)
442 return mapped_enums
443 # Check all ids in given id list to see if one is in sequence from last mapped id
444 def findSeqID(self, id_list):
445 next_seq_id = self.last_mapped_id + 1
446 for map_id in id_list:
447 id_num = int(map_id.split('_')[-1])
448 if id_num == next_seq_id:
449 return True
450 return False
451 # Use the next ID in sequence. This should only be called if findSeqID() just returned True
452 def useSeqID(self, id_list, mapped_enums):
453 next_seq_id = self.last_mapped_id + 1
454 mapped_id = ''
455 for map_id in id_list:
456 id_num = int(map_id.split('_')[-1])
457 if id_num == next_seq_id:
458 mapped_id = map_id
459 self._updateMappedEnum(mapped_enums, mapped_id)
460 return (mapped_enums, mapped_id)
461 return (mapped_enums, mapped_id)
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600462 # Compare unique ids from original database to data generated from updated spec
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600463 # First, make 3 separate mappings of original error messages:
464 # 1. Map the full error message to its id. There should only be 1 ID per full message (orig_full_msg_dict)
465 # 2. Map the intial portion of the message w/o link to list of IDs. There May be a little aliasing here (orig_no_link_msg_dict)
466 # 3. Map the core spec message w/o link or section info to list of IDs. There will be lots of aliasing here (orig_core_msg_dict)
467 # Also store a set of all IDs that have been mapped to that will serve 2 purposes:
468 # 1. Pull IDs out of the above dicts as they're remapped since we know they won't be used
469 # 2. Make sure that we don't re-use an ID
470 # The general algorithm for remapping from new IDs to old IDs is:
471 # 1. If there is a user-specified remapping, use that above all else
472 # 2. Elif the new error message hits in orig_full_msg_dict then use that ID
473 # 3. Elif the new error message hits orig_no_link_msg_dict then
474 # a. If only a single ID, use it
475 # b. Elif multiple IDs & one matches last used ID in sequence, use it
476 # c. Else assign a new ID and flag for manual remapping
477 # 4. Elif the new error message hits orig_core_msg_dict then
478 # a. If only a single ID, use it
479 # b. Elif multiple IDs & one matches last used ID in sequence, use it
480 # c. Else assign a new ID and flag for manual remapping
481 # 5. Else - No matches use a new ID
Tobin Ehlise7560e72016-10-19 15:59:38 -0600482 def compareDB(self, orig_error_msg_dict, max_id):
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600483 """Compare orig database dict to new dict, report out findings, and return potential new dict for parsed spec"""
484 # First create reverse dicts of err_strings to IDs
485 next_id = max_id + 1
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600486 ids_parsed = 0
487 mapped_enums = set() # store all enums that have been mapped to avoid re-use
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600488 # Create an updated dict in-place that will be assigned to self.val_error_dict when done
489 updated_val_error_dict = {}
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600490 # Create a few separate mappings of error msg formats to associated ID(s)
Tobin Ehlise7560e72016-10-19 15:59:38 -0600491 for enum in orig_error_msg_dict:
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600492 api = self.error_db_dict[enum]['api']
493 original_full_msg = orig_error_msg_dict[enum]
494 orig_no_link_msg = "%s,%s" % (api, original_full_msg.split('(https', 1)[0])
495 orig_core_msg = "%s,%s" % (api, orig_no_link_msg.split(' which states ', 1)[-1])
496 orig_core_msg_period = "%s.' " % (orig_core_msg[:-2])
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600497 print ("Orig core msg:%s\nOrig cw/o per:%s" % (orig_core_msg, orig_core_msg_period))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600498
499 # First store mapping of full error msg to ID, shouldn't have duplicates
500 if original_full_msg in self.orig_full_msg_dict:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600501 print ("ERROR: Found duplicate full msg in original full error messages: %s" % (original_full_msg))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600502 self.orig_full_msg_dict[original_full_msg] = enum
503 # Now map API,no_link_msg to list of IDs
504 if orig_no_link_msg in self.orig_no_link_msg_dict:
505 self.orig_no_link_msg_dict[orig_no_link_msg].append(enum)
506 else:
507 self.orig_no_link_msg_dict[orig_no_link_msg] = [enum]
508 # Finally map API,core_msg to list of IDs
509 if orig_core_msg in self.orig_core_msg_dict:
510 self.orig_core_msg_dict[orig_core_msg].append(enum)
511 else:
512 self.orig_core_msg_dict[orig_core_msg] = [enum]
513 if orig_core_msg_period in self.orig_core_msg_dict:
514 self.orig_core_msg_dict[orig_core_msg_period].append(enum)
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600515 print ("Added msg '%s' w/ enum %s to orig_core_msg_dict" % (orig_core_msg_period, enum))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600516 else:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600517 print ("Added msg '%s' w/ enum %s to orig_core_msg_dict" % (orig_core_msg_period, enum))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600518 self.orig_core_msg_dict[orig_core_msg_period] = [enum]
519 # Also capture all enums that have a test and/or implementation
Dave Houlton14f7e662017-05-17 13:25:53 -0600520 if self.error_db_dict[enum]['check_implemented'] == 'Y' or self.error_db_dict[enum]['testname'] not in ['None','Unknown','NotTestable']:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600521 print ("Recording %s with implemented value %s and testname %s" % (enum, self.error_db_dict[enum]['check_implemented'], self.error_db_dict[enum]['testname']))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600522 self.orig_test_imp_enums.add(enum)
Tobin Ehlise7560e72016-10-19 15:59:38 -0600523 # Values to be used for the update dict
524 update_enum = ''
525 update_msg = ''
526 update_api = ''
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600527 # Now parse through new dict and figure out what to do with non-matching things
528 for enum in sorted(self.val_error_dict):
529 ids_parsed = ids_parsed + 1
530 enum_list = enum.split('_') # grab sections of enum for use below
Tobin Ehlise7560e72016-10-19 15:59:38 -0600531 # Default update values to be the same
532 update_enum = enum
533 update_msg = self.val_error_dict[enum]['error_msg']
534 update_api = self.val_error_dict[enum]['api']
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700535 implicit = self.val_error_dict[enum]['implicit']
Tobin Ehlis2c932132017-05-19 16:32:15 -0600536 vuid_string = self.val_error_dict[enum]['vuid_string']
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600537 new_full_msg = update_msg
538 new_no_link_msg = "%s,%s" % (update_api, new_full_msg.split('(https', 1)[0])
539 new_core_msg = "%s,%s" % (update_api, new_no_link_msg.split(' which states ', 1)[-1])
Tobin Ehlisbd0a9c62016-10-14 18:06:16 -0600540 # Any user-forced remap takes precendence
541 if enum_list[-1] in remap_dict:
542 enum_list[-1] = remap_dict[enum_list[-1]]
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600543 self.last_mapped_id = int(enum_list[-1])
Tobin Ehlisbd0a9c62016-10-14 18:06:16 -0600544 new_enum = "_".join(enum_list)
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600545 print ("NOTE: Using user-supplied remap to force %s to be %s" % (enum, new_enum))
546 mapped_enums = self._updateMappedEnum(mapped_enums, new_enum)
Tobin Ehlise7560e72016-10-19 15:59:38 -0600547 update_enum = new_enum
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600548 elif new_full_msg in self.orig_full_msg_dict:
549 orig_enum = self.orig_full_msg_dict[new_full_msg]
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600550 print ("Found exact match for full error msg so switching new ID %s to original ID %s" % (enum, orig_enum))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600551 mapped_enums = self._updateMappedEnum(mapped_enums, orig_enum)
552 update_enum = orig_enum
553 elif new_no_link_msg in self.orig_no_link_msg_dict:
554 # Try to get single ID to map to from no_link matches
555 if len(self.orig_no_link_msg_dict[new_no_link_msg]) == 1: # Only 1 id, use it!
556 orig_enum = self.orig_no_link_msg_dict[new_no_link_msg][0]
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600557 print ("Found no-link err msg match w/ only 1 ID match so switching new ID %s to original ID %s" % (enum, orig_enum))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600558 mapped_enums = self._updateMappedEnum(mapped_enums, orig_enum)
559 update_enum = orig_enum
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600560 else:
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600561 if self.findSeqID(self.orig_no_link_msg_dict[new_no_link_msg]): # If we have an id in sequence, use it!
562 (mapped_enums, update_enum) = self.useSeqID(self.orig_no_link_msg_dict[new_no_link_msg], mapped_enums)
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600563 print ("Found no-link err msg match w/ seq ID match so switching new ID %s to original ID %s" % (enum, update_enum))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600564 else:
565 enum_list[-1] = "%05d" % (next_id)
566 new_enum = "_".join(enum_list)
567 next_id = next_id + 1
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600568 print ("Found no-link msg match but have multiple matched IDs w/o a sequence ID, updating ID %s to unique ID %s for msg %s" % (enum, new_enum, new_no_link_msg))
Tobin Ehlise7560e72016-10-19 15:59:38 -0600569 update_enum = new_enum
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600570 elif new_core_msg in self.orig_core_msg_dict:
571 # Do similar stuff here
572 if len(self.orig_core_msg_dict[new_core_msg]) == 1:
573 orig_enum = self.orig_core_msg_dict[new_core_msg][0]
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600574 print ("Found core err msg match w/ only 1 ID match so switching new ID %s to original ID %s" % (enum, orig_enum))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600575 mapped_enums = self._updateMappedEnum(mapped_enums, orig_enum)
576 update_enum = orig_enum
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600577 else:
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600578 if self.findSeqID(self.orig_core_msg_dict[new_core_msg]):
579 (mapped_enums, update_enum) = self.useSeqID(self.orig_core_msg_dict[new_core_msg], mapped_enums)
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600580 print ("Found core err msg match w/ seq ID match so switching new ID %s to original ID %s" % (enum, update_enum))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600581 else:
582 enum_list[-1] = "%05d" % (next_id)
583 new_enum = "_".join(enum_list)
584 next_id = next_id + 1
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600585 print ("Found core msg match but have multiple matched IDs w/o a sequence ID, updating ID %s to unique ID %s for msg %s" % (enum, new_enum, new_no_link_msg))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600586 update_enum = new_enum
587 # This seems to be a new error so need to pick it up from end of original unique ids & flag for review
588 else:
589 enum_list[-1] = "%05d" % (next_id)
590 new_enum = "_".join(enum_list)
591 next_id = next_id + 1
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600592 print ("Completely new id and error code, update new id from %s to unique %s for core message:%s" % (enum, new_enum, new_core_msg))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600593 update_enum = new_enum
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600594 if update_enum in updated_val_error_dict:
595 print ("ERROR: About to OVERWRITE entry for %s" % update_enum)
Tobin Ehlise7560e72016-10-19 15:59:38 -0600596 updated_val_error_dict[update_enum] = {}
597 updated_val_error_dict[update_enum]['error_msg'] = update_msg
598 updated_val_error_dict[update_enum]['api'] = update_api
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700599 updated_val_error_dict[update_enum]['implicit'] = implicit
Tobin Ehlis2c932132017-05-19 16:32:15 -0600600 updated_val_error_dict[update_enum]['vuid_string'] = vuid_string
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600601 # Assign parsed dict to be the updated dict based on db compare
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600602 print ("In compareDB parsed %d entries" % (ids_parsed))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600603 return updated_val_error_dict
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600604
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600605 def validateUpdateDict(self, update_dict):
606 """Compare original dict vs. update dict and make sure that all of the checks are still there"""
607 # Currently just make sure that the same # of checks as the original checks are there
608 #orig_ids = {}
609 orig_id_count = len(self.val_error_dict)
610 #update_ids = {}
611 update_id_count = len(update_dict)
612 if orig_id_count != update_id_count:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600613 print ("Original dict had %d unique_ids, but updated dict has %d!" % (orig_id_count, update_id_count))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600614 return False
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600615 print ("Original dict and updated dict both have %d unique_ids. Great!" % (orig_id_count))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600616 # Now flag any original dict enums that had tests and/or checks that are missing from updated
617 for enum in update_dict:
618 if enum in self.orig_test_imp_enums:
619 self.orig_test_imp_enums.remove(enum)
620 if len(self.orig_test_imp_enums) > 0:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600621 print ("TODO: Have some enums with tests and/or checks implemented that are missing in update:")
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600622 for enum in sorted(self.orig_test_imp_enums):
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600623 print ("\t%s") % enum
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600624 return True
625 # TODO : include some more analysis
626
627# User passes in arg of form <new_id1>-<old_id1>[,count1]:<new_id2>-<old_id2>[,count2]:...
628# new_id# = the new enum id that was assigned to an error
629# old_id# = the previous enum id that was assigned to the same error
630# [,count#] = The number of ids to remap starting at new_id#=old_id# and ending at new_id[#+count#-1]=old_id[#+count#-1]
631# If not supplied, then ,1 is assumed, which will only update a single id
632def updateRemapDict(remap_string):
633 """Set up global remap_dict based on user input"""
634 remap_list = remap_string.split(":")
635 for rmap in remap_list:
636 count = 1 # Default count if none supplied
637 id_count_list = rmap.split(',')
638 if len(id_count_list) > 1:
639 count = int(id_count_list[1])
640 new_old_id_list = id_count_list[0].split('-')
641 for offset in range(count):
642 remap_dict["%05d" % (int(new_old_id_list[0]) + offset)] = "%05d" % (int(new_old_id_list[1]) + offset)
643 for new_id in sorted(remap_dict):
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600644 print ("Set to remap new id %s to old id %s" % (new_id, remap_dict[new_id]))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600645
646if __name__ == "__main__":
647 i = 1
648 use_online = True # Attempt to grab spec from online by default
649 update_option = False
650 while (i < len(sys.argv)):
651 arg = sys.argv[i]
652 i = i + 1
653 if (arg == '-spec'):
654 spec_filename = sys.argv[i]
655 # If user specifies local specfile, skip online
656 use_online = False
657 i = i + 1
Tobin Ehlis98d109a2017-05-11 14:42:38 -0600658 elif (arg == '-json'):
659 json_filename = sys.argv[i]
660 i = i + 1
Tobin Ehlis2c932132017-05-19 16:32:15 -0600661 elif (arg == '-json-compare'):
662 json_compare = True
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600663 elif (arg == '-out'):
664 out_filename = sys.argv[i]
665 i = i + 1
666 elif (arg == '-gendb'):
667 gen_db = True
668 # Set filename if supplied, else use default
669 if i < len(sys.argv) and not sys.argv[i].startswith('-'):
670 db_filename = sys.argv[i]
671 i = i + 1
672 elif (arg == '-compare'):
673 db_filename = sys.argv[i]
674 spec_compare = True
675 i = i + 1
676 elif (arg == '-update'):
677 update_option = True
678 spec_compare = True
679 gen_db = True
680 elif (arg == '-remap'):
681 updateRemapDict(sys.argv[i])
682 i = i + 1
683 elif (arg in ['-help', '-h']):
684 printHelp()
685 sys.exit()
686 if len(remap_dict) > 1 and not update_option:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600687 print ("ERROR: '-remap' option can only be used along with '-update' option. Exiting.")
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600688 sys.exit()
689 spec = Specification()
Tobin Ehlis2c932132017-05-19 16:32:15 -0600690 spec.soupLoadFile(use_online, spec_filename)
691 spec.analyze()
Tobin Ehlis98d109a2017-05-11 14:42:38 -0600692 if (None != json_filename):
693 print ("Reading json file:%s" % (json_filename))
694 spec.readJSON(json_filename)
695 spec.parseJSON()
Tobin Ehlis2c932132017-05-19 16:32:15 -0600696 #sys.exit()
697 if (json_compare):
698 # Read in current spec info from db file
699 (orig_err_msg_dict, max_id) = spec.readDB(db_filename)
700 spec.compareJSON()
701 print ("Found %d missing db entries in json db" % (spec.json_missing))
702 print ("Found %d duplicate json entries" % (spec.duplicate_json_key_count))
Tobin Ehlis98d109a2017-05-11 14:42:38 -0600703 sys.exit()
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600704 if (spec_compare):
705 # Read in old spec info from db file
Tobin Ehlise7560e72016-10-19 15:59:38 -0600706 (orig_err_msg_dict, max_id) = spec.readDB(db_filename)
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600707 # New spec data should already be read into self.val_error_dict
Tobin Ehlise7560e72016-10-19 15:59:38 -0600708 updated_dict = spec.compareDB(orig_err_msg_dict, max_id)
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600709 update_valid = spec.validateUpdateDict(updated_dict)
710 if update_valid:
711 spec.updateDict(updated_dict)
712 else:
713 sys.exit()
714 if (gen_db):
715 spec.genDB(db_filename)
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600716 print ("Writing out file (-out) to '%s'" % (out_filename))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600717 spec.genHeader(out_filename)
718
719##### Example dataset
720# <div class="sidebar">
721# <div class="titlepage">
722# <div>
723# <div>
724# <p class="title">
725# <strong>Valid Usage</strong> # When we get to this guy, we know we're under interesting sidebar
726# </p>
727# </div>
728# </div>
729# </div>
730# <div class="itemizedlist">
731# <ul class="itemizedlist" style="list-style-type: disc; ">
732# <li class="listitem">
733# <em class="parameter">
734# <code>device</code>
735# </em>
736# <span class="normative">must</span> be a valid
737# <code class="code">VkDevice</code> handle
738# </li>
739# <li class="listitem">
740# <em class="parameter">
741# <code>commandPool</code>
742# </em>
743# <span class="normative">must</span> be a valid
744# <code class="code">VkCommandPool</code> handle
745# </li>
746# <li class="listitem">
747# <em class="parameter">
748# <code>flags</code>
749# </em>
750# <span class="normative">must</span> be a valid combination of
751# <code class="code">
752# <a class="link" href="#VkCommandPoolResetFlagBits">VkCommandPoolResetFlagBits</a>
753# </code> values
754# </li>
755# <li class="listitem">
756# <em class="parameter">
757# <code>commandPool</code>
758# </em>
759# <span class="normative">must</span> have been created, allocated, or retrieved from
760# <em class="parameter">
761# <code>device</code>
762# </em>
763# </li>
764# <li class="listitem">All
765# <code class="code">VkCommandBuffer</code>
766# objects allocated from
767# <em class="parameter">
768# <code>commandPool</code>
769# </em>
770# <span class="normative">must</span> not currently be pending execution
771# </li>
772# </ul>
773# </div>
774# </div>
775##### Second example dataset
776# <div class="sidebar">
777# <div class="titlepage">
778# <div>
779# <div>
780# <p class="title">
781# <strong>Valid Usage</strong>
782# </p>
783# </div>
784# </div>
785# </div>
786# <div class="itemizedlist">
787# <ul class="itemizedlist" style="list-style-type: disc; ">
788# <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>
789# </li>
790# </ul>
791# </div>
792# </div>
793# <div class="sidebar">
794# <div class="titlepage">
795# <div>
796# <div>
797# <p class="title">
798# <strong>Valid Usage (Implicit)</strong>
799# </p>
800# </div>
801# </div>
802# </div>
803# <div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">
804#<em class="parameter"><code>sType</code></em> <span class="normative">must</span> be <code class="code">VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO</code>
805#</li><li class="listitem">
806#<em class="parameter"><code>pNext</code></em> <span class="normative">must</span> be <code class="literal">NULL</code>
807#</li><li class="listitem">
808#<em class="parameter"><code>flags</code></em> <span class="normative">must</span> be <code class="literal">0</code>
809#</li><li class="listitem">
810#<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
Mark Lobodzinski267a7cf2017-01-25 09:33:25 -0700811#</li>