blob: 13deda788f49fd82c23dd219edd56b537c760e5a [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
38gen_db = False # set to True when '-gendb <filename>' option provided
39spec_compare = False # set to True with '-compare <db_filename>' option
40# 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 -060041#old_spec_url = "https://www.khronos.org/registry/vulkan/specs/1.0/xhtml/vkspec.html"
Tobin Ehlisa55b1d42017-04-04 12:23:48 -060042spec_url = "https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html"
Tobin Ehlis5ade0692016-10-05 17:18:15 -060043# After the custom validation error message, this is the prefix for the standard message that includes the
44# spec valid usage language as well as the link to nearest section of spec to that language
45error_msg_prefix = "For more information refer to Vulkan Spec Section "
46ns = {'ns': 'http://www.w3.org/1999/xhtml'}
Mark Lobodzinski629d47b2016-10-18 13:34:58 -060047validation_error_enum_name = "VALIDATION_ERROR_"
Tobin Ehlis5ade0692016-10-05 17:18:15 -060048# Dict of new enum values that should be forced to remap to old handles, explicitly set by -remap option
49remap_dict = {}
50
51def printHelp():
Tobin Ehlis98d109a2017-05-11 14:42:38 -060052 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 -060053 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")
54 print (" Default specfile is from online at %s" % (spec_url))
55 print (" Default headerfile is %s" % (out_filename))
56 print (" Default databasefile is %s" % (db_filename))
57 print ("\nIf '-gendb' option is specified then a database file is generated to default file or <databasefile.txt> if supplied. The database file stores")
58 print (" the list of enums and their error messages.")
59 print ("\nIf '-compare' option is specified then the given database file will be read in as the baseline for generating the new specfile")
60 print ("\nIf '-update' option is specified this triggers the master flow to automate updating header and database files using default db file as baseline")
61 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.")
62 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")
63 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 -060064 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 -060065
66class Specification:
67 def __init__(self):
68 self.tree = None
Tobin Ehlise7560e72016-10-19 15:59:38 -060069 self.val_error_dict = {} # string for enum is key that references 'error_msg' and 'api'
Tobin Ehlis5ade0692016-10-05 17:18:15 -060070 self.error_db_dict = {} # dict of previous error values read in from database file
71 self.delimiter = '~^~' # delimiter for db file
Tobin Ehlis2a176b12017-01-11 16:18:20 -070072 self.implicit_count = 0
Tobin Ehlisa55b1d42017-04-04 12:23:48 -060073 # Global dicts used for tracking spec updates from old to new VUs
74 self.orig_full_msg_dict = {} # Original full error msg to ID mapping
75 self.orig_no_link_msg_dict = {} # Pair of API,Original msg w/o spec link to ID list mapping
76 self.orig_core_msg_dict = {} # Pair of API,Original core msg (no link or section) to ID list mapping
77 self.last_mapped_id = -10 # start as negative so we don't hit an accidental sequence
78 self.orig_test_imp_enums = set() # Track old enums w/ tests and/or implementation to flag any that aren't carried fwd
Tobin Ehlis5ade0692016-10-05 17:18:15 -060079 self.copyright = """/* THIS FILE IS GENERATED. DO NOT EDIT. */
80
81/*
82 * Vulkan
83 *
84 * Copyright (c) 2016 Google Inc.
Mark Lobodzinski629d47b2016-10-18 13:34:58 -060085 * Copyright (c) 2016 LunarG, Inc.
Tobin Ehlis5ade0692016-10-05 17:18:15 -060086 *
87 * Licensed under the Apache License, Version 2.0 (the "License");
88 * you may not use this file except in compliance with the License.
89 * You may obtain a copy of the License at
90 *
91 * http://www.apache.org/licenses/LICENSE-2.0
92 *
93 * Unless required by applicable law or agreed to in writing, software
94 * distributed under the License is distributed on an "AS IS" BASIS,
95 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
96 * See the License for the specific language governing permissions and
97 * limitations under the License.
98 *
99 * Author: Tobin Ehlis <tobine@google.com>
100 */"""
101 def _checkInternetSpec(self):
102 """Verify that we can access the spec online"""
103 try:
104 online = urllib2.urlopen(spec_url,timeout=1)
105 return True
106 except urllib2.URLError as err:
107 return False
108 return False
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600109 def soupLoadFile(self, online=True, spec_file=spec_filename):
110 """Load a spec file into BeutifulSoup"""
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600111 if (online and self._checkInternetSpec()):
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600112 print ("Making soup from spec online at %s, this will take a minute" % (spec_url))
113 self.soup = BeautifulSoup(urllib2.urlopen(spec_url), 'html.parser')
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600114 else:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600115 print ("Making soup from local spec %s, this will take a minute" % (spec_file))
Tobin Ehlisec45e422017-05-19 08:24:04 -0600116 with open(spec_file, "r") as sf:
117 self.soup = BeautifulSoup(sf, 'html.parser')
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600118 self.parseSoup()
119 #print(self.soup.prettify())
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600120 def updateDict(self, updated_dict):
121 """Assign internal dict to use updated_dict"""
122 self.val_error_dict = updated_dict
Tobin Ehlis98d109a2017-05-11 14:42:38 -0600123
124 def readJSON(self, json_file):
125 """Read in JSON file"""
126 with open(json_file) as jsf:
127 self.json_data = json.load(jsf)
128 def parseJSON(self):
129 """Parse JSON VUIDs into data struct"""
130 # Format of JSON file is:
131 # "API": { "core|EXT": [ {"vuid": "<id>", "text": "<VU txt>"}]},
132 # "VK_KHX_external_memory" & "VK_KHX_device_group" - extension case (vs. "core")
133
134 for api in sorted(self.json_data):
135 for ext in sorted(self.json_data[api]):
136 for vu_txt_dict in self.json_data[api][ext]:
137 vuid = vu_txt_dict['vuid']
138 vutxt = vu_txt_dict['text']
139 #print ("%s:%s:%s:%s" % (api, ext, vuid, vutxt))
140 vuid_mapping.convertVUID(vuid)
141
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600142 def parseSoup(self):
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600143 """Parse the registry Element, once created"""
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600144 print ("Parsing spec file...")
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600145 unique_enum_id = 0
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600146 #self.root = self.tree.getroot()
147 #print ("ROOT: %s") % self.root
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600148 prev_heading = '' # Last seen section heading or sub-heading
149 prev_link = '' # Last seen link id within the spec
Tobin Ehlise7560e72016-10-19 15:59:38 -0600150 api_function = '' # API call that a check appears under
Tobin Ehlis85008cd2016-10-19 15:32:35 -0600151 error_strings = set() # Flag any exact duplicate error strings and skip them
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600152 for tag in self.soup.find_all(True):#self.root.iter(): # iterate down tree
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600153 # Grab most recent section heading and link
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600154 #print ("tag.name is %s and class is %s" % (tag.name, tag.get('class')))
155 if tag.name in ['h2', 'h3', 'h4']:
Tobin Ehlisce1e56f2017-01-25 12:42:55 -0800156 #if tag.get('class') != 'title':
157 # continue
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600158 #print ("Found heading %s w/ string %s" % (tag.name, tag.string))
159 if None == tag.string:
160 prev_heading = ""
161 else:
162 prev_heading = "".join(tag.string)
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600163 # Insert a space between heading number & title
164 sh_list = prev_heading.rsplit('.', 1)
165 prev_heading = '. '.join(sh_list)
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600166 prev_link = tag['id']
167 #print ("Set prev_heading %s to have link of %s" % (prev_heading.encode("ascii", "ignore"), prev_link.encode("ascii", "ignore")))
168 elif tag.name == 'a': # grab any intermediate links
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600169 if tag.get('id') != None:
170 prev_link = tag.get('id')
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600171 #print ("Updated prev link to %s" % (prev_link))
172 elif tag.name == 'div' and tag.get('class') is not None and tag['class'][0] == 'listingblock':
Tobin Ehlise7560e72016-10-19 15:59:38 -0600173 # Check and see if this is API function
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600174 code_text = "".join(tag.strings).replace('\n', '')
Tobin Ehlise7560e72016-10-19 15:59:38 -0600175 code_text_list = code_text.split()
176 if len(code_text_list) > 1 and code_text_list[1].startswith('vk'):
177 api_function = code_text_list[1].strip('(')
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600178 #print ("Found API function: %s" % (api_function))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600179 prev_link = api_function
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600180 #print ("Updated prev link to %s" % (prev_link))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600181 elif tag.get('id') != None:
182 prev_link = tag.get('id')
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600183 #print ("Updated prev link to %s" % (prev_link))
184 #elif tag.name == '{http://www.w3.org/1999/xhtml}div' and tag.get('class') == 'sidebar':
185 elif tag.name == 'div' and tag.get('class') is not None and tag['class'][0] == 'content':
186 #print("Parsing down a div content tag")
Tobin Ehlis69ebddf2016-10-18 15:55:07 -0600187 # parse down sidebar to check for valid usage cases
188 valid_usage = False
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700189 implicit = False
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600190 for elem in tag.find_all(True):
191 #print(" elem is %s w/ string %s" % (elem.name, elem.string))
192 if elem.name == 'div' and None != elem.string and 'Valid Usage' in elem.string:
Tobin Ehlis69ebddf2016-10-18 15:55:07 -0600193 valid_usage = True
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600194 if '(Implicit)' in elem.string:
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700195 implicit = True
196 else:
197 implicit = False
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600198 elif valid_usage and elem.name == 'li': # grab actual valid usage requirements
199 #print("I think this is a VU w/ elem.strings is %s" % (elem.strings))
200 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 -0600201 # Some txt has multiple spaces so split on whitespace and join w/ single space
202 error_msg_str = " ".join(error_msg_str.split())
Tobin Ehlis85008cd2016-10-19 15:32:35 -0600203 if error_msg_str in error_strings:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600204 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 -0600205 else:
206 error_strings.add(error_msg_str)
207 enum_str = "%s%05d" % (validation_error_enum_name, unique_enum_id)
208 # TODO : '\' chars in spec error messages are most likely bad spec txt that needs to be updated
Tobin Ehlise7560e72016-10-19 15:59:38 -0600209 self.val_error_dict[enum_str] = {}
210 self.val_error_dict[enum_str]['error_msg'] = error_msg_str.encode("ascii", "ignore").replace("\\", "/")
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600211 self.val_error_dict[enum_str]['api'] = api_function.encode("ascii", "ignore")
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700212 self.val_error_dict[enum_str]['implicit'] = False
213 if implicit:
214 self.val_error_dict[enum_str]['implicit'] = True
215 self.implicit_count = self.implicit_count + 1
Tobin Ehlis85008cd2016-10-19 15:32:35 -0600216 unique_enum_id = unique_enum_id + 1
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600217 #print ("Validation Error Dict has a total of %d unique errors and contents are:\n%s" % (unique_enum_id, self.val_error_dict))
218 print ("Validation Error Dict has a total of %d unique errors" % (unique_enum_id))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600219 def genHeader(self, header_file):
220 """Generate a header file based on the contents of a parsed spec"""
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600221 print ("Generating header %s..." % (header_file))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600222 file_contents = []
223 file_contents.append(self.copyright)
224 file_contents.append('\n#pragma once')
Mark Lobodzinski267a7cf2017-01-25 09:33:25 -0700225 file_contents.append('\n// Disable auto-formatting for generated file')
226 file_contents.append('// clang-format off')
227 file_contents.append('\n#include <unordered_map>')
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600228 file_contents.append('\n// enum values for unique validation error codes')
229 file_contents.append('// Corresponding validation error message for each enum is given in the mapping table below')
230 file_contents.append('// When a given error occurs, these enum values should be passed to the as the messageCode')
231 file_contents.append('// parameter to the PFN_vkDebugReportCallbackEXT function')
Tobin Ehlis387fd632016-12-08 13:32:05 -0700232 enum_decl = ['enum UNIQUE_VALIDATION_ERROR_CODE {\n VALIDATION_ERROR_UNDEFINED = -1,']
Tobin Ehlisbf98b692016-10-06 12:58:06 -0600233 error_string_map = ['static std::unordered_map<int, char const *const> validation_error_map{']
Tobin Ehlisce1e56f2017-01-25 12:42:55 -0800234 enum_value = 0
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600235 for enum in sorted(self.val_error_dict):
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600236 #print ("Header enum is %s" % (enum))
Tobin Ehlisce1e56f2017-01-25 12:42:55 -0800237 enum_value = int(enum.split('_')[-1])
238 enum_decl.append(' %s = %d,' % (enum, enum_value))
Tobin Ehlise7560e72016-10-19 15:59:38 -0600239 error_string_map.append(' {%s, "%s"},' % (enum, self.val_error_dict[enum]['error_msg']))
Tobin Ehlisce1e56f2017-01-25 12:42:55 -0800240 enum_decl.append(' %sMAX_ENUM = %d,' % (validation_error_enum_name, enum_value + 1))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600241 enum_decl.append('};')
Tobin Ehlise7560e72016-10-19 15:59:38 -0600242 error_string_map.append('};\n')
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600243 file_contents.extend(enum_decl)
244 file_contents.append('\n// Mapping from unique validation error enum to the corresponding error message')
245 file_contents.append('// The error message should be appended to the end of a custom error message that is passed')
246 file_contents.append('// as the pMessage parameter to the PFN_vkDebugReportCallbackEXT function')
247 file_contents.extend(error_string_map)
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600248 #print ("File contents: %s" % (file_contents))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600249 with open(header_file, "w") as outfile:
250 outfile.write("\n".join(file_contents))
251 def analyze(self):
252 """Print out some stats on the valid usage dict"""
253 # Create dict for # of occurences of identical strings
254 str_count_dict = {}
255 unique_id_count = 0
256 for enum in self.val_error_dict:
Tobin Ehlise7560e72016-10-19 15:59:38 -0600257 err_str = self.val_error_dict[enum]['error_msg']
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600258 if err_str in str_count_dict:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600259 print ("Found repeat error string")
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600260 str_count_dict[err_str] = str_count_dict[err_str] + 1
261 else:
262 str_count_dict[err_str] = 1
263 unique_id_count = unique_id_count + 1
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600264 print ("Processed %d unique_ids" % (unique_id_count))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600265 repeat_string = 0
266 for es in str_count_dict:
267 if str_count_dict[es] > 1:
268 repeat_string = repeat_string + 1
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600269 print ("String '%s' repeated %d times" % (es, repeat_string))
270 print ("Found %d repeat strings" % (repeat_string))
271 print ("Found %d implicit checks" % (self.implicit_count))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600272 def genDB(self, db_file):
273 """Generate a database of check_enum, check_coded?, testname, error_string"""
274 db_lines = []
275 # Write header for database file
276 db_lines.append("# This is a database file with validation error check information")
277 db_lines.append("# Comments are denoted with '#' char")
278 db_lines.append("# The format of the lines is:")
Tobin Ehlise7560e72016-10-19 15:59:38 -0600279 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 -0600280 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 -0700281 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 -0600282 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 -0600283 db_lines.append("# api: Vulkan API function that this check is related to")
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600284 db_lines.append("# errormsg: The unique error message for this check that includes spec language and link")
Tobin Ehlise7560e72016-10-19 15:59:38 -0600285 db_lines.append("# note: Free txt field with any custom notes related to the check in question")
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600286 for enum in sorted(self.val_error_dict):
Mike Weiblenfe186122017-02-03 12:44:53 -0700287 # Default check/test implementation status to N/Unknown, then update below if appropriate
288 implemented = 'N'
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600289 testname = 'Unknown'
Tobin Ehlis70980c02016-10-25 14:00:20 -0600290 note = ''
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700291 implicit = self.val_error_dict[enum]['implicit']
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600292 # If we have an existing db entry for this enum, use its implemented/testname values
293 if enum in self.error_db_dict:
294 implemented = self.error_db_dict[enum]['check_implemented']
295 testname = self.error_db_dict[enum]['testname']
Tobin Ehlis70980c02016-10-25 14:00:20 -0600296 note = self.error_db_dict[enum]['note']
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700297 if implicit and 'implicit' not in note: # add implicit note
298 if '' != note:
299 note = "implicit, %s" % (note)
300 else:
301 note = "implicit"
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600302 #print ("delimiter: %s, id: %s, str: %s" % (self.delimiter, enum, self.val_error_dict[enum])
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600303 # No existing entry so default to N for implemented and None for testname
Tobin Ehlis70980c02016-10-25 14:00:20 -0600304 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 -0600305 db_lines.append("\n") # newline at end of file
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600306 print ("Generating database file %s" % (db_file))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600307 with open(db_file, "w") as outfile:
308 outfile.write("\n".join(db_lines))
309 def readDB(self, db_file):
310 """Read a db file into a dict, format of each line is <enum><implemented Y|N?><testname><errormsg>"""
311 db_dict = {} # This is a simple db of just enum->errormsg, the same as is created from spec
312 max_id = 0
313 with open(db_file, "r") as infile:
314 for line in infile:
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600315 line = line.strip()
Tobin Ehlisf4245cb2016-10-31 07:55:19 -0600316 if line.startswith('#') or '' == line:
317 continue
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600318 db_line = line.split(self.delimiter)
Tobin Ehlis70980c02016-10-25 14:00:20 -0600319 if len(db_line) != 6:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600320 print ("ERROR: Bad database line doesn't have 6 elements: %s" % (line))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600321 error_enum = db_line[0]
322 implemented = db_line[1]
323 testname = db_line[2]
Tobin Ehlis70980c02016-10-25 14:00:20 -0600324 api = db_line[3]
325 error_str = db_line[4]
326 note = db_line[5]
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600327 db_dict[error_enum] = error_str
328 # Also read complete database contents into our class var for later use
329 self.error_db_dict[error_enum] = {}
330 self.error_db_dict[error_enum]['check_implemented'] = implemented
331 self.error_db_dict[error_enum]['testname'] = testname
Tobin Ehlis70980c02016-10-25 14:00:20 -0600332 self.error_db_dict[error_enum]['api'] = api
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600333 self.error_db_dict[error_enum]['error_string'] = error_str
Tobin Ehlis70980c02016-10-25 14:00:20 -0600334 self.error_db_dict[error_enum]['note'] = note
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600335 unique_id = int(db_line[0].split('_')[-1])
336 if unique_id > max_id:
337 max_id = unique_id
338 return (db_dict, max_id)
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600339 # This is a helper function to do bookkeeping on data structs when comparing original
340 # error ids to current error ids
341 # It tracks all updated enums in mapped_enums and removes those enums from any lists
342 # in the no_link and core dicts
343 def _updateMappedEnum(self, mapped_enums, enum):
344 mapped_enums.add(enum)
345 # When looking for ID to map, we favor sequences so track last ID mapped
346 self.last_mapped_id = int(enum.split('_')[-1])
347 for msg in self.orig_no_link_msg_dict:
348 if enum in self.orig_no_link_msg_dict[msg]:
349 self.orig_no_link_msg_dict[msg].remove(enum)
350 for msg in self.orig_core_msg_dict:
351 if enum in self.orig_core_msg_dict[msg]:
352 self.orig_core_msg_dict[msg].remove(enum)
353 return mapped_enums
354 # Check all ids in given id list to see if one is in sequence from last mapped id
355 def findSeqID(self, id_list):
356 next_seq_id = self.last_mapped_id + 1
357 for map_id in id_list:
358 id_num = int(map_id.split('_')[-1])
359 if id_num == next_seq_id:
360 return True
361 return False
362 # Use the next ID in sequence. This should only be called if findSeqID() just returned True
363 def useSeqID(self, id_list, mapped_enums):
364 next_seq_id = self.last_mapped_id + 1
365 mapped_id = ''
366 for map_id in id_list:
367 id_num = int(map_id.split('_')[-1])
368 if id_num == next_seq_id:
369 mapped_id = map_id
370 self._updateMappedEnum(mapped_enums, mapped_id)
371 return (mapped_enums, mapped_id)
372 return (mapped_enums, mapped_id)
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600373 # Compare unique ids from original database to data generated from updated spec
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600374 # First, make 3 separate mappings of original error messages:
375 # 1. Map the full error message to its id. There should only be 1 ID per full message (orig_full_msg_dict)
376 # 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)
377 # 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)
378 # Also store a set of all IDs that have been mapped to that will serve 2 purposes:
379 # 1. Pull IDs out of the above dicts as they're remapped since we know they won't be used
380 # 2. Make sure that we don't re-use an ID
381 # The general algorithm for remapping from new IDs to old IDs is:
382 # 1. If there is a user-specified remapping, use that above all else
383 # 2. Elif the new error message hits in orig_full_msg_dict then use that ID
384 # 3. Elif the new error message hits orig_no_link_msg_dict then
385 # a. If only a single ID, use it
386 # b. Elif multiple IDs & one matches last used ID in sequence, use it
387 # c. Else assign a new ID and flag for manual remapping
388 # 4. Elif the new error message hits orig_core_msg_dict then
389 # a. If only a single ID, use it
390 # b. Elif multiple IDs & one matches last used ID in sequence, use it
391 # c. Else assign a new ID and flag for manual remapping
392 # 5. Else - No matches use a new ID
Tobin Ehlise7560e72016-10-19 15:59:38 -0600393 def compareDB(self, orig_error_msg_dict, max_id):
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600394 """Compare orig database dict to new dict, report out findings, and return potential new dict for parsed spec"""
395 # First create reverse dicts of err_strings to IDs
396 next_id = max_id + 1
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600397 ids_parsed = 0
398 mapped_enums = set() # store all enums that have been mapped to avoid re-use
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600399 # Create an updated dict in-place that will be assigned to self.val_error_dict when done
400 updated_val_error_dict = {}
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600401 # Create a few separate mappings of error msg formats to associated ID(s)
Tobin Ehlise7560e72016-10-19 15:59:38 -0600402 for enum in orig_error_msg_dict:
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600403 api = self.error_db_dict[enum]['api']
404 original_full_msg = orig_error_msg_dict[enum]
405 orig_no_link_msg = "%s,%s" % (api, original_full_msg.split('(https', 1)[0])
406 orig_core_msg = "%s,%s" % (api, orig_no_link_msg.split(' which states ', 1)[-1])
407 orig_core_msg_period = "%s.' " % (orig_core_msg[:-2])
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600408 print ("Orig core msg:%s\nOrig cw/o per:%s" % (orig_core_msg, orig_core_msg_period))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600409
410 # First store mapping of full error msg to ID, shouldn't have duplicates
411 if original_full_msg in self.orig_full_msg_dict:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600412 print ("ERROR: Found duplicate full msg in original full error messages: %s" % (original_full_msg))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600413 self.orig_full_msg_dict[original_full_msg] = enum
414 # Now map API,no_link_msg to list of IDs
415 if orig_no_link_msg in self.orig_no_link_msg_dict:
416 self.orig_no_link_msg_dict[orig_no_link_msg].append(enum)
417 else:
418 self.orig_no_link_msg_dict[orig_no_link_msg] = [enum]
419 # Finally map API,core_msg to list of IDs
420 if orig_core_msg in self.orig_core_msg_dict:
421 self.orig_core_msg_dict[orig_core_msg].append(enum)
422 else:
423 self.orig_core_msg_dict[orig_core_msg] = [enum]
424 if orig_core_msg_period in self.orig_core_msg_dict:
425 self.orig_core_msg_dict[orig_core_msg_period].append(enum)
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600426 print ("Added msg '%s' w/ enum %s to orig_core_msg_dict" % (orig_core_msg_period, enum))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600427 else:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600428 print ("Added msg '%s' w/ enum %s to orig_core_msg_dict" % (orig_core_msg_period, enum))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600429 self.orig_core_msg_dict[orig_core_msg_period] = [enum]
430 # Also capture all enums that have a test and/or implementation
Dave Houlton14f7e662017-05-17 13:25:53 -0600431 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 -0600432 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 -0600433 self.orig_test_imp_enums.add(enum)
Tobin Ehlise7560e72016-10-19 15:59:38 -0600434 # Values to be used for the update dict
435 update_enum = ''
436 update_msg = ''
437 update_api = ''
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600438 # Now parse through new dict and figure out what to do with non-matching things
439 for enum in sorted(self.val_error_dict):
440 ids_parsed = ids_parsed + 1
441 enum_list = enum.split('_') # grab sections of enum for use below
Tobin Ehlise7560e72016-10-19 15:59:38 -0600442 # Default update values to be the same
443 update_enum = enum
444 update_msg = self.val_error_dict[enum]['error_msg']
445 update_api = self.val_error_dict[enum]['api']
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700446 implicit = self.val_error_dict[enum]['implicit']
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600447 new_full_msg = update_msg
448 new_no_link_msg = "%s,%s" % (update_api, new_full_msg.split('(https', 1)[0])
449 new_core_msg = "%s,%s" % (update_api, new_no_link_msg.split(' which states ', 1)[-1])
Tobin Ehlisbd0a9c62016-10-14 18:06:16 -0600450 # Any user-forced remap takes precendence
451 if enum_list[-1] in remap_dict:
452 enum_list[-1] = remap_dict[enum_list[-1]]
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600453 self.last_mapped_id = int(enum_list[-1])
Tobin Ehlisbd0a9c62016-10-14 18:06:16 -0600454 new_enum = "_".join(enum_list)
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600455 print ("NOTE: Using user-supplied remap to force %s to be %s" % (enum, new_enum))
456 mapped_enums = self._updateMappedEnum(mapped_enums, new_enum)
Tobin Ehlise7560e72016-10-19 15:59:38 -0600457 update_enum = new_enum
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600458 elif new_full_msg in self.orig_full_msg_dict:
459 orig_enum = self.orig_full_msg_dict[new_full_msg]
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600460 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 -0600461 mapped_enums = self._updateMappedEnum(mapped_enums, orig_enum)
462 update_enum = orig_enum
463 elif new_no_link_msg in self.orig_no_link_msg_dict:
464 # Try to get single ID to map to from no_link matches
465 if len(self.orig_no_link_msg_dict[new_no_link_msg]) == 1: # Only 1 id, use it!
466 orig_enum = self.orig_no_link_msg_dict[new_no_link_msg][0]
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600467 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 -0600468 mapped_enums = self._updateMappedEnum(mapped_enums, orig_enum)
469 update_enum = orig_enum
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600470 else:
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600471 if self.findSeqID(self.orig_no_link_msg_dict[new_no_link_msg]): # If we have an id in sequence, use it!
472 (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 -0600473 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 -0600474 else:
475 enum_list[-1] = "%05d" % (next_id)
476 new_enum = "_".join(enum_list)
477 next_id = next_id + 1
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600478 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 -0600479 update_enum = new_enum
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600480 elif new_core_msg in self.orig_core_msg_dict:
481 # Do similar stuff here
482 if len(self.orig_core_msg_dict[new_core_msg]) == 1:
483 orig_enum = self.orig_core_msg_dict[new_core_msg][0]
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600484 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 -0600485 mapped_enums = self._updateMappedEnum(mapped_enums, orig_enum)
486 update_enum = orig_enum
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600487 else:
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600488 if self.findSeqID(self.orig_core_msg_dict[new_core_msg]):
489 (mapped_enums, update_enum) = self.useSeqID(self.orig_core_msg_dict[new_core_msg], mapped_enums)
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600490 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 -0600491 else:
492 enum_list[-1] = "%05d" % (next_id)
493 new_enum = "_".join(enum_list)
494 next_id = next_id + 1
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600495 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 -0600496 update_enum = new_enum
497 # This seems to be a new error so need to pick it up from end of original unique ids & flag for review
498 else:
499 enum_list[-1] = "%05d" % (next_id)
500 new_enum = "_".join(enum_list)
501 next_id = next_id + 1
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600502 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 -0600503 update_enum = new_enum
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600504 if update_enum in updated_val_error_dict:
505 print ("ERROR: About to OVERWRITE entry for %s" % update_enum)
Tobin Ehlise7560e72016-10-19 15:59:38 -0600506 updated_val_error_dict[update_enum] = {}
507 updated_val_error_dict[update_enum]['error_msg'] = update_msg
508 updated_val_error_dict[update_enum]['api'] = update_api
Tobin Ehlis2a176b12017-01-11 16:18:20 -0700509 updated_val_error_dict[update_enum]['implicit'] = implicit
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600510 # Assign parsed dict to be the updated dict based on db compare
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600511 print ("In compareDB parsed %d entries" % (ids_parsed))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600512 return updated_val_error_dict
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600513
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600514 def validateUpdateDict(self, update_dict):
515 """Compare original dict vs. update dict and make sure that all of the checks are still there"""
516 # Currently just make sure that the same # of checks as the original checks are there
517 #orig_ids = {}
518 orig_id_count = len(self.val_error_dict)
519 #update_ids = {}
520 update_id_count = len(update_dict)
521 if orig_id_count != update_id_count:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600522 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 -0600523 return False
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600524 print ("Original dict and updated dict both have %d unique_ids. Great!" % (orig_id_count))
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600525 # Now flag any original dict enums that had tests and/or checks that are missing from updated
526 for enum in update_dict:
527 if enum in self.orig_test_imp_enums:
528 self.orig_test_imp_enums.remove(enum)
529 if len(self.orig_test_imp_enums) > 0:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600530 print ("TODO: Have some enums with tests and/or checks implemented that are missing in update:")
Tobin Ehlisa55b1d42017-04-04 12:23:48 -0600531 for enum in sorted(self.orig_test_imp_enums):
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600532 print ("\t%s") % enum
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600533 return True
534 # TODO : include some more analysis
535
536# User passes in arg of form <new_id1>-<old_id1>[,count1]:<new_id2>-<old_id2>[,count2]:...
537# new_id# = the new enum id that was assigned to an error
538# old_id# = the previous enum id that was assigned to the same error
539# [,count#] = The number of ids to remap starting at new_id#=old_id# and ending at new_id[#+count#-1]=old_id[#+count#-1]
540# If not supplied, then ,1 is assumed, which will only update a single id
541def updateRemapDict(remap_string):
542 """Set up global remap_dict based on user input"""
543 remap_list = remap_string.split(":")
544 for rmap in remap_list:
545 count = 1 # Default count if none supplied
546 id_count_list = rmap.split(',')
547 if len(id_count_list) > 1:
548 count = int(id_count_list[1])
549 new_old_id_list = id_count_list[0].split('-')
550 for offset in range(count):
551 remap_dict["%05d" % (int(new_old_id_list[0]) + offset)] = "%05d" % (int(new_old_id_list[1]) + offset)
552 for new_id in sorted(remap_dict):
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600553 print ("Set to remap new id %s to old id %s" % (new_id, remap_dict[new_id]))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600554
555if __name__ == "__main__":
556 i = 1
557 use_online = True # Attempt to grab spec from online by default
558 update_option = False
559 while (i < len(sys.argv)):
560 arg = sys.argv[i]
561 i = i + 1
562 if (arg == '-spec'):
563 spec_filename = sys.argv[i]
564 # If user specifies local specfile, skip online
565 use_online = False
566 i = i + 1
Tobin Ehlis98d109a2017-05-11 14:42:38 -0600567 elif (arg == '-json'):
568 json_filename = sys.argv[i]
569 i = i + 1
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600570 elif (arg == '-out'):
571 out_filename = sys.argv[i]
572 i = i + 1
573 elif (arg == '-gendb'):
574 gen_db = True
575 # Set filename if supplied, else use default
576 if i < len(sys.argv) and not sys.argv[i].startswith('-'):
577 db_filename = sys.argv[i]
578 i = i + 1
579 elif (arg == '-compare'):
580 db_filename = sys.argv[i]
581 spec_compare = True
582 i = i + 1
583 elif (arg == '-update'):
584 update_option = True
585 spec_compare = True
586 gen_db = True
587 elif (arg == '-remap'):
588 updateRemapDict(sys.argv[i])
589 i = i + 1
590 elif (arg in ['-help', '-h']):
591 printHelp()
592 sys.exit()
593 if len(remap_dict) > 1 and not update_option:
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600594 print ("ERROR: '-remap' option can only be used along with '-update' option. Exiting.")
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600595 sys.exit()
596 spec = Specification()
Tobin Ehlis98d109a2017-05-11 14:42:38 -0600597 if (None != json_filename):
598 print ("Reading json file:%s" % (json_filename))
599 spec.readJSON(json_filename)
600 spec.parseJSON()
601 sys.exit()
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600602 spec.soupLoadFile(use_online, spec_filename)
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600603 spec.analyze()
604 if (spec_compare):
605 # Read in old spec info from db file
Tobin Ehlise7560e72016-10-19 15:59:38 -0600606 (orig_err_msg_dict, max_id) = spec.readDB(db_filename)
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600607 # New spec data should already be read into self.val_error_dict
Tobin Ehlise7560e72016-10-19 15:59:38 -0600608 updated_dict = spec.compareDB(orig_err_msg_dict, max_id)
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600609 update_valid = spec.validateUpdateDict(updated_dict)
610 if update_valid:
611 spec.updateDict(updated_dict)
612 else:
613 sys.exit()
614 if (gen_db):
615 spec.genDB(db_filename)
Tobin Ehlis3198ba32017-04-19 17:30:52 -0600616 print ("Writing out file (-out) to '%s'" % (out_filename))
Tobin Ehlis5ade0692016-10-05 17:18:15 -0600617 spec.genHeader(out_filename)
618
619##### Example dataset
620# <div class="sidebar">
621# <div class="titlepage">
622# <div>
623# <div>
624# <p class="title">
625# <strong>Valid Usage</strong> # When we get to this guy, we know we're under interesting sidebar
626# </p>
627# </div>
628# </div>
629# </div>
630# <div class="itemizedlist">
631# <ul class="itemizedlist" style="list-style-type: disc; ">
632# <li class="listitem">
633# <em class="parameter">
634# <code>device</code>
635# </em>
636# <span class="normative">must</span> be a valid
637# <code class="code">VkDevice</code> handle
638# </li>
639# <li class="listitem">
640# <em class="parameter">
641# <code>commandPool</code>
642# </em>
643# <span class="normative">must</span> be a valid
644# <code class="code">VkCommandPool</code> handle
645# </li>
646# <li class="listitem">
647# <em class="parameter">
648# <code>flags</code>
649# </em>
650# <span class="normative">must</span> be a valid combination of
651# <code class="code">
652# <a class="link" href="#VkCommandPoolResetFlagBits">VkCommandPoolResetFlagBits</a>
653# </code> values
654# </li>
655# <li class="listitem">
656# <em class="parameter">
657# <code>commandPool</code>
658# </em>
659# <span class="normative">must</span> have been created, allocated, or retrieved from
660# <em class="parameter">
661# <code>device</code>
662# </em>
663# </li>
664# <li class="listitem">All
665# <code class="code">VkCommandBuffer</code>
666# objects allocated from
667# <em class="parameter">
668# <code>commandPool</code>
669# </em>
670# <span class="normative">must</span> not currently be pending execution
671# </li>
672# </ul>
673# </div>
674# </div>
675##### Second example dataset
676# <div class="sidebar">
677# <div class="titlepage">
678# <div>
679# <div>
680# <p class="title">
681# <strong>Valid Usage</strong>
682# </p>
683# </div>
684# </div>
685# </div>
686# <div class="itemizedlist">
687# <ul class="itemizedlist" style="list-style-type: disc; ">
688# <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>
689# </li>
690# </ul>
691# </div>
692# </div>
693# <div class="sidebar">
694# <div class="titlepage">
695# <div>
696# <div>
697# <p class="title">
698# <strong>Valid Usage (Implicit)</strong>
699# </p>
700# </div>
701# </div>
702# </div>
703# <div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">
704#<em class="parameter"><code>sType</code></em> <span class="normative">must</span> be <code class="code">VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO</code>
705#</li><li class="listitem">
706#<em class="parameter"><code>pNext</code></em> <span class="normative">must</span> be <code class="literal">NULL</code>
707#</li><li class="listitem">
708#<em class="parameter"><code>flags</code></em> <span class="normative">must</span> be <code class="literal">0</code>
709#</li><li class="listitem">
710#<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 -0700711#</li>