scripts:Add core/ext column to error database
Update the validation error database to have a column that indicates if
any extensions are required for a given VU to be valid. If an extension
is required then the spec link will point into the spec with all
extensions, otherwise the core spec will be linked.
diff --git a/layers/spec.py b/layers/spec.py
index 737aa7b..e259509 100644
--- a/layers/spec.py
+++ b/layers/spec.py
@@ -39,9 +39,13 @@
gen_db = False # set to True when '-gendb <filename>' option provided
spec_compare = False # set to True with '-compare <db_filename>' option
json_compare = False # compare existing DB to json file input
+json_url = "https://www.khronos.org/registry/vulkan/specs/1.0-extensions/validation/validusage.json"
+read_json = False
# This is the root spec link that is used in error messages to point users to spec sections
#old_spec_url = "https://www.khronos.org/registry/vulkan/specs/1.0/xhtml/vkspec.html"
spec_url = "https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html"
+core_url = "https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html"
+ext_url = "https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html"
# After the custom validation error message, this is the prefix for the standard message that includes the
# spec valid usage language as well as the link to nearest section of spec to that language
error_msg_prefix = "For more information refer to Vulkan Spec Section "
@@ -67,7 +71,8 @@
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.")
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")
print (" option. Starting at newid and remapping to oldid, count ids will be remapped. Default count is '1' and use ':' to specify multiple remappings.")
- print ("\nIf '-json' option is used to point to json file, parse the json file and generate VUIDs based on that.")
+ print ("\nIf '-json' option is used trigger the script to load in data from a json file.")
+ print ("\nIf '-json-file' option is it will point to a local json file, else '%s' is used from the web." % (json_url))
class Specification:
def __init__(self):
@@ -136,10 +141,14 @@
"""Assign internal dict to use updated_dict"""
self.val_error_dict = updated_dict
- def readJSON(self, json_file):
+ def readJSON(self):
"""Read in JSON file"""
- with open(json_file) as jsf:
- self.json_data = json.load(jsf)
+ if json_filename is not None:
+ with open(json_filename) as jsf:
+ self.json_data = json.load(jsf)
+ else:
+ self.json_data = json.load(urllib2.urlopen(json_url))
+
def parseJSON(self):
"""Parse JSON VUIDs into data struct"""
# Format of JSON file is:
@@ -151,7 +160,7 @@
vuid = vu_txt_dict['vuid']
vutxt = vu_txt_dict['text']
#print ("%s:%s:%s:%s" % (api, ext, vuid, vutxt))
- print ("VUTXT orig:%s" % (vutxt))
+ #print ("VUTXT orig:%s" % (vutxt))
just_txt = BeautifulSoup(vutxt, 'html.parser')
#print ("VUTXT only:%s" % (just_txt.get_text()))
num_vuid = vuid_mapping.convertVUID(vuid)
@@ -163,55 +172,82 @@
print ("Key '%s' is already in json_db!" % (key))
self.duplicate_json_key_count = self.duplicate_json_key_count + 1
#sys.exit()
- self.json_db[key] = {}
- self.json_db[key]['ext'] = ext
- self.json_db[key]['string_vuid'] = vuid
- self.json_db[key]['number_vuid'] = num_vuid
- self.json_db[key]['struct_func'] = api
- if 'must specify aspects present in the calling command' in key:
- print "Found KEY:%s" % (key)
+ self.json_db[vuid] = {}
+ self.json_db[vuid]['ext'] = ext
+ #self.json_db[key]['string_vuid'] = vuid
+ self.json_db[vuid]['number_vuid'] = num_vuid
+ self.json_db[vuid]['struct_func'] = api
+ just_txt = just_txt.get_text().strip()
+ unicode_map = {
+ u"\u2019" : "'",
+ u"\u2192" : "->",
+ }
+ for um in unicode_map:
+ just_txt = just_txt.replace(um, unicode_map[um])
+ self.json_db[vuid]['vu_txt'] = just_txt
+ print ("Spec vu txt:%s" % (self.json_db[vuid]['vu_txt']))
+# if 'must specify aspects present in the calling command' in key:
+# print "Found KEY:%s" % (key)
#sys.exit()
+ #sys.exit()
def compareJSON(self):
"""Compare parsed json file with existing data read in from DB file"""
+ json_db_set = set()
+ for vuid in self.json_db: # pull entries out and see which fields we're missing from error_db
+ json_db_set.add(vuid)
for enum in self.error_db_dict:
- full_error_string = self.error_db_dict[enum]['error_string']
- api_struct = full_error_string.rsplit('#', 1)[-1].strip(')')
- if 'VUID' in api_struct:
- api_struct = api_struct.split('-')[1]
-
- no_link_msg = full_error_string.split(' (https', 1)[0]
- core_msg = no_link_msg.split(' which states ', 1)[-1]
- #keys = []
- #keys.append("%s,%s" % (api_struct, core_msg))
- api = self.error_db_dict[enum]['api']
- key = "%s,%s" % (api, core_msg)
-# if 'vkCreate' in api:
-# if 's' == api[-1] and not api.endswith('Pass'): # strip a single trailing s
-# api = api[0:-1]
-# create_struct = api.replace('vkCreate', 'Vk')
-# if create_struct.endswith('KHR'):
-# create_struct = create_struct.replace('KHR', 'CreateInfoKHR')
-# elif create_struct.endswith('NVX'):
-# create_struct = create_struct.replace('NVX', 'CreateInfoNVX')
-# else:
-# create_struct = "%sCreateInfo" % (create_struct)
-# keys.append("%s,%s" % (create_struct, core_msg))
-# elif 'vkGet' in api:
-# get_struct = api.replace('vkGet', 'Vk')
-# get_struct = get_struct.replace('Properties', 'Info')
-# keys.append("%s,%s" % (get_struct, core_msg))
-# elif 'vkAllocate' in api:
-# if 's' == api[-1]: # strip a trailing 0
-# api = api[0:-1]
-# alloc_struct = api.replace('vkAllocate', 'Vk')
-# alloc_struct = "%sAllocateInfo" % (alloc_struct)
-# keys.append("%s,%s" % (alloc_struct, core_msg))
- #if True not in [key in self.json_db for key in keys]:
- if key not in self.json_db:
- print ("Full string for %s is:%s" % (enum, full_error_string))
- print ("WARN: Couldn't find key in json db:%s" % (key))
+ vuid_string = self.error_db_dict[enum]['vuid_string']
+ if vuid_string not in self.json_db:
+ #print ("Full string for %s is:%s" % (enum, full_error_string))
+ print ("WARN: Couldn't find vuid_string in json db:%s" % (vuid_string))
self.json_missing = self.json_missing + 1
+ self.error_db_dict[enum]['ext'] = 'core'
+ #sys.exit()
+ else:
+ json_db_set.remove(vuid_string)
+ # TMP: Add ext details to error db
+ self.error_db_dict[enum]['ext'] = self.json_db[vuid_string]['ext']
+ if 'core' == self.json_db[vuid_string]['ext'] or '!' in self.json_db[vuid_string]['ext']:
+ spec_link = "%s#%s" % (core_url, vuid_string)
+ else:
+ spec_link = "%s#%s" % (ext_url, vuid_string)
+ self.error_db_dict[enum]['error_msg'] = "The spec valid usage text states '%s' (%s)" % (self.json_db[vuid_string]['vu_txt'], spec_link)
+ print ("Updated error_db error_msg:%s" % (self.error_db_dict[enum]['error_msg']))
+ #sys.exit()
+ print ("These json DB entries are not in error DB:")
+ for extra_vuid in json_db_set:
+ print ("\t%s" % (extra_vuid))
+ # Add these missing entries into the error_db
+ # Create link into core or ext spec as needed
+ if 'core' == self.json_db[extra_vuid]['ext'] or '!' in self.json_db[extra_vuid]['ext']:
+ spec_link = "%s#%s" % (core_url, extra_vuid)
+ else:
+ spec_link = "%s#%s" % (ext_url, extra_vuid)
+ error_enum = "VALIDATION_ERROR_%d" % (self.json_db[extra_vuid]['number_vuid'])
+ self.error_db_dict[error_enum] = {}
+ self.error_db_dict[error_enum]['check_implemented'] = 'N'
+ self.error_db_dict[error_enum]['testname'] = 'None'
+ self.error_db_dict[error_enum]['api'] = self.json_db[extra_vuid]['struct_func']
+ self.error_db_dict[error_enum]['vuid_string'] = extra_vuid
+ self.error_db_dict[error_enum]['error_msg'] = "The spec valid usage text states '%s' (%s)" % (self.json_db[extra_vuid]['vu_txt'], spec_link)
+ self.error_db_dict[error_enum]['note'] = ''
+ self.error_db_dict[error_enum]['ext'] = self.json_db[extra_vuid]['ext']
+ # Enable this code if you want to reset val_error_dict & assign to db dict
+# self.val_error_dict = {}
+# for enum in self.error_db_dict:
+# self.val_error_dict[enum] = {}
+# self.val_error_dict[enum]['check_implemented'] = self.error_db_dict[enum]['check_implemented']
+# self.val_error_dict[enum]['testname'] = self.error_db_dict[enum]['testname']
+# self.val_error_dict[enum]['api'] = self.error_db_dict[enum]['api']
+# self.val_error_dict[enum]['vuid_string'] = self.error_db_dict[enum]['vuid_string']
+# self.val_error_dict[enum]['ext'] = self.error_db_dict[enum]['ext']
+# self.val_error_dict[enum]['error_msg'] = self.error_db_dict[enum]['error_msg']
+# self.val_error_dict[enum]['note'] = self.error_db_dict[enum]['note']
+# implicit = False
+# if extra_vuid.split("_")[-1].isdigit():
+# implicit = True
+# self.val_error_dict[enum]['implicit'] = implicit
def parseSoup(self):
"""Parse the registry Element, once created"""
@@ -358,18 +394,19 @@
print ("Found %d repeat strings" % (repeat_string))
print ("Found %d implicit checks" % (self.implicit_count))
def genDB(self, db_file):
- """Generate a database of check_enum, check_coded?, testname, API, VUID_string, error_string"""
+ """Generate a database of check_enum, check_coded?, testname, API, VUID_string, core|ext, error_string, notes"""
db_lines = []
# Write header for database file
db_lines.append("# This is a database file with validation error check information")
db_lines.append("# Comments are denoted with '#' char")
db_lines.append("# The format of the lines is:")
- 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))
+ db_lines.append("# <error_enum>%s<check_implemented>%s<testname>%s<api>%s<vuid_string>%s<core|ext>%s<errormsg>%s<note>" % (self.delimiter, self.delimiter, self.delimiter, self.delimiter, self.delimiter, self.delimiter, self.delimiter))
db_lines.append("# error_enum: Unique error enum for this check of format %s<uniqueid>" % validation_error_enum_name)
db_lines.append("# check_implemented: 'Y' if check has been implemented in layers, or 'N' for not implemented")
db_lines.append("# testname: Name of validation test for this check, 'Unknown' for unknown, 'None' if not implemented, or 'NotTestable' if cannot be implemented")
db_lines.append("# api: Vulkan API function that this check is related to")
db_lines.append("# vuid_string: Unique string to identify this check")
+ db_lines.append("# core|ext: Either 'core' for core spec or some extension string that indicates the extension required for this VU to be relevant")
db_lines.append("# errormsg: The unique error message for this check that includes spec language and link")
db_lines.append("# note: Free txt field with any custom notes related to the check in question")
for enum in sorted(self.val_error_dict):
@@ -377,12 +414,15 @@
implemented = 'N'
testname = 'Unknown'
note = ''
+ core_ext = 'core'
implicit = self.val_error_dict[enum]['implicit']
# If we have an existing db entry for this enum, use its implemented/testname values
if enum in self.error_db_dict:
implemented = self.error_db_dict[enum]['check_implemented']
testname = self.error_db_dict[enum]['testname']
note = self.error_db_dict[enum]['note']
+ core_ext = self.error_db_dict[enum]['ext']
+ self.val_error_dict[enum]['vuid_string'] = self.error_db_dict[enum]['vuid_string']
if implicit and 'implicit' not in note: # add implicit note
if '' != note:
note = "implicit, %s" % (note)
@@ -390,7 +430,7 @@
note = "implicit"
#print ("delimiter: %s, id: %s, str: %s" % (self.delimiter, enum, self.val_error_dict[enum])
# No existing entry so default to N for implemented and None for testname
- 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))
+ db_lines.append("%s%s%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, core_ext, self.delimiter, self.val_error_dict[enum]['error_msg'], self.delimiter, note))
db_lines.append("\n") # newline at end of file
print ("Generating database file %s" % (db_file))
with open(db_file, "w") as outfile:
@@ -405,21 +445,25 @@
if line.startswith('#') or '' == line:
continue
db_line = line.split(self.delimiter)
- if len(db_line) != 6:
- print ("ERROR: Bad database line doesn't have 6 elements: %s" % (line))
+ if len(db_line) != 8:
+ print ("ERROR: Bad database line doesn't have 8 elements: %s" % (line))
error_enum = db_line[0]
implemented = db_line[1]
testname = db_line[2]
api = db_line[3]
- error_str = db_line[4]
- note = db_line[5]
+ vuid_str = db_line[4]
+ core_ext = db_line[5]
+ error_str = db_line[6]
+ note = db_line[7]
db_dict[error_enum] = error_str
# Also read complete database contents into our class var for later use
self.error_db_dict[error_enum] = {}
self.error_db_dict[error_enum]['check_implemented'] = implemented
self.error_db_dict[error_enum]['testname'] = testname
self.error_db_dict[error_enum]['api'] = api
- self.error_db_dict[error_enum]['error_string'] = error_str
+ self.error_db_dict[error_enum]['vuid_string'] = vuid_str
+ self.error_db_dict[error_enum]['core_ext'] = core_ext
+ self.error_db_dict[error_enum]['error_msg'] = error_str
self.error_db_dict[error_enum]['note'] = note
unique_id = int(db_line[0].split('_')[-1])
if unique_id > max_id:
@@ -655,9 +699,11 @@
# If user specifies local specfile, skip online
use_online = False
i = i + 1
- elif (arg == '-json'):
+ elif (arg == '-json-file'):
json_filename = sys.argv[i]
i = i + 1
+ elif (arg == '-json'):
+ read_json = True
elif (arg == '-json-compare'):
json_compare = True
elif (arg == '-out'):
@@ -689,9 +735,8 @@
spec = Specification()
spec.soupLoadFile(use_online, spec_filename)
spec.analyze()
- if (None != json_filename):
- print ("Reading json file:%s" % (json_filename))
- spec.readJSON(json_filename)
+ if read_json:
+ spec.readJSON()
spec.parseJSON()
#sys.exit()
if (json_compare):
@@ -700,6 +745,7 @@
spec.compareJSON()
print ("Found %d missing db entries in json db" % (spec.json_missing))
print ("Found %d duplicate json entries" % (spec.duplicate_json_key_count))
+ spec.genDB("json_vk_validation_error_database.txt")
sys.exit()
if (spec_compare):
# Read in old spec info from db file