1e5c31af7Sopenharmony_ci#!/usr/bin/python3 2e5c31af7Sopenharmony_ci# 3e5c31af7Sopenharmony_ci# Copyright 2015-2024 The Khronos Group Inc. 4e5c31af7Sopenharmony_ci# 5e5c31af7Sopenharmony_ci# SPDX-License-Identifier: Apache-2.0 6e5c31af7Sopenharmony_ci 7e5c31af7Sopenharmony_ci# checkLinks.py - validate link/reference API constructs in files 8e5c31af7Sopenharmony_ci# 9e5c31af7Sopenharmony_ci# Usage: checkLinks.py [options] files > logfile 10e5c31af7Sopenharmony_ci# 11e5c31af7Sopenharmony_ci# Options: 12e5c31af7Sopenharmony_ci# -follow attempt to follow include:: directives. This script is not an 13e5c31af7Sopenharmony_ci# Asciidoctor processor, so only literal relative paths can be followed. 14e5c31af7Sopenharmony_ci# -info print some internal diagnostics. 15e5c31af7Sopenharmony_ci# -paramcheck attempt to validate param: names against the surrounding 16e5c31af7Sopenharmony_ci# context (the current structure/function being validated, for example). 17e5c31af7Sopenharmony_ci# This generates many false positives, so is not enabled by default. 18e5c31af7Sopenharmony_ci# -fatal unvalidatable links cause immediate error exit from the script. 19e5c31af7Sopenharmony_ci# Otherwise, errors are accumulated and summarized at the end. 20e5c31af7Sopenharmony_ci# 21e5c31af7Sopenharmony_ci# Depends on vkapi.py, which is a Python representation of relevant parts 22e5c31af7Sopenharmony_ci# of the Vulkan API. Only works when vkapi.py is generated for the full 23e5c31af7Sopenharmony_ci# API, e.g. 'makeAllExts checklinks'; otherwise many false-flagged errors 24e5c31af7Sopenharmony_ci# will occur. 25e5c31af7Sopenharmony_ci 26e5c31af7Sopenharmony_ciimport copy, os, pdb, re, string, sys 27e5c31af7Sopenharmony_cifrom vkapi import * 28e5c31af7Sopenharmony_ci 29e5c31af7Sopenharmony_ciglobal curFile, curLine, sectionDepth 30e5c31af7Sopenharmony_ciglobal errCount, warnCount, emittedPrefix, printInfo 31e5c31af7Sopenharmony_ci 32e5c31af7Sopenharmony_cicurFile = '???' 33e5c31af7Sopenharmony_cicurLine = -1 34e5c31af7Sopenharmony_cisectionDepth = 0 35e5c31af7Sopenharmony_ciemittedPrefix = {} 36e5c31af7Sopenharmony_ciprintInfo = False 37e5c31af7Sopenharmony_ci 38e5c31af7Sopenharmony_ci# Called before printing a warning or error. Only prints once prior 39e5c31af7Sopenharmony_ci# to output for a given file. 40e5c31af7Sopenharmony_cidef emitPrefix(): 41e5c31af7Sopenharmony_ci global curFile, curLine, emittedPrefix 42e5c31af7Sopenharmony_ci if (curFile not in emittedPrefix.keys()): 43e5c31af7Sopenharmony_ci emittedPrefix[curFile] = None 44e5c31af7Sopenharmony_ci print('Checking file:', curFile) 45e5c31af7Sopenharmony_ci print('-------------------------------') 46e5c31af7Sopenharmony_ci 47e5c31af7Sopenharmony_cidef info(*args, **kwargs): 48e5c31af7Sopenharmony_ci global curFile, curLine, printInfo 49e5c31af7Sopenharmony_ci if (printInfo): 50e5c31af7Sopenharmony_ci 51e5c31af7Sopenharmony_ci emitPrefix() 52e5c31af7Sopenharmony_ci print('INFO: %s line %d:' % (curFile, curLine), 53e5c31af7Sopenharmony_ci ' '.join([str(arg) for arg in args])) 54e5c31af7Sopenharmony_ci 55e5c31af7Sopenharmony_ci# Print a validation warning found in a file 56e5c31af7Sopenharmony_cidef warning(*args, **kwargs): 57e5c31af7Sopenharmony_ci global curFile, curLine, warnCount 58e5c31af7Sopenharmony_ci 59e5c31af7Sopenharmony_ci warnCount = warnCount + 1 60e5c31af7Sopenharmony_ci emitPrefix() 61e5c31af7Sopenharmony_ci print('WARNING: %s line %d:' % (curFile, curLine), 62e5c31af7Sopenharmony_ci ' '.join([str(arg) for arg in args])) 63e5c31af7Sopenharmony_ci 64e5c31af7Sopenharmony_ci# Print a validation error found in a file 65e5c31af7Sopenharmony_cidef error(*args, **kwargs): 66e5c31af7Sopenharmony_ci global curFile, curLine, errCount 67e5c31af7Sopenharmony_ci 68e5c31af7Sopenharmony_ci errCount = errCount + 1 69e5c31af7Sopenharmony_ci emitPrefix() 70e5c31af7Sopenharmony_ci print('ERROR: %s line %d:' % (curFile, curLine), 71e5c31af7Sopenharmony_ci ' '.join([str(arg) for arg in args])) 72e5c31af7Sopenharmony_ci 73e5c31af7Sopenharmony_ci# See if a tag value exists in the specified dictionary and 74e5c31af7Sopenharmony_ci# suggest it as an alternative if so. 75e5c31af7Sopenharmony_cidef checkTag(tag, value, dict, dictName, tagName): 76e5c31af7Sopenharmony_ci if (value in dict.keys()): 77e5c31af7Sopenharmony_ci warning(value, 'exists in the API but not as a', 78e5c31af7Sopenharmony_ci tag + ': .', 'Try using the', tagName + ': tag.') 79e5c31af7Sopenharmony_ci 80e5c31af7Sopenharmony_ci# Report an error due to an asciidoc tag which does not match 81e5c31af7Sopenharmony_ci# a corresponding API entity. 82e5c31af7Sopenharmony_cidef foundError(errType, tag, value, fatal): 83e5c31af7Sopenharmony_ci global curFile, curLine 84e5c31af7Sopenharmony_ci error('no such', errType, tag + ':' + value) 85e5c31af7Sopenharmony_ci # Try some heuristics to detect likely problems such as missing vk 86e5c31af7Sopenharmony_ci # prefixes or the wrong tag. 87e5c31af7Sopenharmony_ci 88e5c31af7Sopenharmony_ci # Look in all the dictionaries in vkapi.py to see if the tag 89e5c31af7Sopenharmony_ci # is just wrong but the API entity actually exists. 90e5c31af7Sopenharmony_ci checkTag(tag, value, flags, 'flags', 'tlink/tname') 91e5c31af7Sopenharmony_ci checkTag(tag, value, enums, 'enums', 'elink') 92e5c31af7Sopenharmony_ci checkTag(tag, value, structs, 'structs', 'slink/sname') 93e5c31af7Sopenharmony_ci checkTag(tag, value, handles, 'handles', 'slink/sname') 94e5c31af7Sopenharmony_ci checkTag(tag, value, defines, 'defines', 'slink/sname') 95e5c31af7Sopenharmony_ci checkTag(tag, value, consts, 'consts', 'ename') 96e5c31af7Sopenharmony_ci checkTag(tag, value, protos, 'protos', 'flink/fname') 97e5c31af7Sopenharmony_ci checkTag(tag, value, funcpointers, 'funcpointers', 'tlink/tname') 98e5c31af7Sopenharmony_ci 99e5c31af7Sopenharmony_ci # Look for missing vk prefixes (quirky since it is case-dependent) 100e5c31af7Sopenharmony_ci # NOT DONE YET 101e5c31af7Sopenharmony_ci 102e5c31af7Sopenharmony_ci if fatal: 103e5c31af7Sopenharmony_ci print('ERROR: %s line %d:' % (curFile, curLine), 104e5c31af7Sopenharmony_ci ' '.join(['no such', errType, tag + ':' + value]), file=sys.stderr) 105e5c31af7Sopenharmony_ci sys.exit(1) 106e5c31af7Sopenharmony_ci 107e5c31af7Sopenharmony_ci# Look for param in the list of all parameters of the specified functions 108e5c31af7Sopenharmony_ci# Returns True if found, False otherwise 109e5c31af7Sopenharmony_cidef findParam(param, funclist): 110e5c31af7Sopenharmony_ci for f in funclist: 111e5c31af7Sopenharmony_ci if (param in protos[f]): 112e5c31af7Sopenharmony_ci info('parameter:', param, 'found in function:', f) 113e5c31af7Sopenharmony_ci return True 114e5c31af7Sopenharmony_ci return False 115e5c31af7Sopenharmony_ci 116e5c31af7Sopenharmony_ci# Initialize tracking state for checking links/includes 117e5c31af7Sopenharmony_cidef initChecks(): 118e5c31af7Sopenharmony_ci global curFile, curLine, curFuncs, curStruct, accumFunc, sectionDepth 119e5c31af7Sopenharmony_ci global errCount, warnCount 120e5c31af7Sopenharmony_ci global incPat, linkPat, pathPat, sectionPat 121e5c31af7Sopenharmony_ci 122e5c31af7Sopenharmony_ci # Matches asciidoc single-line section tags 123e5c31af7Sopenharmony_ci sectionPat = re.compile('^(=+) ') 124e5c31af7Sopenharmony_ci 125e5c31af7Sopenharmony_ci # Matches any asciidoc include:: directive 126e5c31af7Sopenharmony_ci pathPat = re.compile('^include::([\w./_]+)\[\]') 127e5c31af7Sopenharmony_ci 128e5c31af7Sopenharmony_ci # Matches asciidoc include:: directives used in spec/ref pages (and also 129e5c31af7Sopenharmony_ci # others such as validity). This is specific to the layout of the api/ 130e5c31af7Sopenharmony_ci # includes and allows any path preceding 'api/' followed by the category 131e5c31af7Sopenharmony_ci # (protos, structs, enums, etc.) followed by the name of the proto, 132e5c31af7Sopenharmony_ci # struct, etc. file. 133e5c31af7Sopenharmony_ci incPat = re.compile('^.*api/(\w+)/(\w+)\.adoc') 134e5c31af7Sopenharmony_ci 135e5c31af7Sopenharmony_ci # Lists of current /protos/ (functions) and /structs/ includes. There 136e5c31af7Sopenharmony_ci # can be several protos contiguously for different forms of a command 137e5c31af7Sopenharmony_ci curFuncs = [] 138e5c31af7Sopenharmony_ci curStruct = None 139e5c31af7Sopenharmony_ci 140e5c31af7Sopenharmony_ci # Tag if we should accumulate funcs or start a new list. Any intervening 141e5c31af7Sopenharmony_ci # pname: tags or struct includes will restart the list. 142e5c31af7Sopenharmony_ci accumFunc = False 143e5c31af7Sopenharmony_ci 144e5c31af7Sopenharmony_ci # Matches all link names in the current spec/man pages. Assumes these 145e5c31af7Sopenharmony_ci # macro names are not trailing subsets of other macros. Used to 146e5c31af7Sopenharmony_ci # precede the regexp with [^A-Za-z], but this did not catch macros 147e5c31af7Sopenharmony_ci # at start of line. 148e5c31af7Sopenharmony_ci linkPat = re.compile('([efpst](name|link)):(\w*)') 149e5c31af7Sopenharmony_ci 150e5c31af7Sopenharmony_ci # Total error/warning counters 151e5c31af7Sopenharmony_ci errCount = 0 152e5c31af7Sopenharmony_ci warnCount = 0 153e5c31af7Sopenharmony_ci 154e5c31af7Sopenharmony_ci# Validate asciidoc internal links in specified file. 155e5c31af7Sopenharmony_ci# infile - filename to validate 156e5c31af7Sopenharmony_ci# follow - if True, recursively follow include:: directives 157e5c31af7Sopenharmony_ci# paramCheck - if True, try to verify pname: refers to valid 158e5c31af7Sopenharmony_ci# parameter/member names. This generates many false flags currently 159e5c31af7Sopenharmony_ci# included - if True, function was called recursively 160e5c31af7Sopenharmony_ci# fatalExit - if True, validation errors cause an error exit immediately 161e5c31af7Sopenharmony_ci# Links checked are: 162e5c31af7Sopenharmony_ci# fname:vkBlah - Vulkan command name (generates internal link) 163e5c31af7Sopenharmony_ci# flink:vkBlah - Vulkan command name 164e5c31af7Sopenharmony_ci# sname:VkBlah - Vulkan struct name (generates internal link) 165e5c31af7Sopenharmony_ci# slink:VkBlah - Vulkan struct name 166e5c31af7Sopenharmony_ci# elink:VkEnumName - Vulkan enumeration ('enum') type name (generates internal link) 167e5c31af7Sopenharmony_ci# ename:VK_BLAH - Vulkan enumerant token name 168e5c31af7Sopenharmony_ci# pname:name - parameter name to a command or a struct member 169e5c31af7Sopenharmony_ci# tlink:name - Other Vulkan type name (generates internal link) 170e5c31af7Sopenharmony_ci# tname:name - Other Vulkan type name 171e5c31af7Sopenharmony_cidef checkLinks(infile, follow = False, paramCheck = True, included = False, fatalExit = False): 172e5c31af7Sopenharmony_ci global curFile, curLine, curFuncs, curStruct, accumFunc, sectionDepth 173e5c31af7Sopenharmony_ci global errCount, warnCount 174e5c31af7Sopenharmony_ci global incPat, linkPat, pathPat, sectionPat 175e5c31af7Sopenharmony_ci 176e5c31af7Sopenharmony_ci # Global state which gets saved and restored by this function 177e5c31af7Sopenharmony_ci oldCurFile = curFile 178e5c31af7Sopenharmony_ci oldCurLine = curLine 179e5c31af7Sopenharmony_ci curFile = infile 180e5c31af7Sopenharmony_ci curLine = 0 181e5c31af7Sopenharmony_ci 182e5c31af7Sopenharmony_ci # N.b. dirname() returns an empty string for a path with no directories, 183e5c31af7Sopenharmony_ci # unlike the shell dirname(1). 184e5c31af7Sopenharmony_ci if (not os.path.exists(curFile)): 185e5c31af7Sopenharmony_ci error('No such file', curFile, '- skipping check') 186e5c31af7Sopenharmony_ci # Restore global state before exiting the function 187e5c31af7Sopenharmony_ci curFile = oldCurFile 188e5c31af7Sopenharmony_ci curLine = oldCurLine 189e5c31af7Sopenharmony_ci return 190e5c31af7Sopenharmony_ci 191e5c31af7Sopenharmony_ci inPath = os.path.dirname(curFile) 192e5c31af7Sopenharmony_ci fp = open(curFile, 'r', encoding='utf-8') 193e5c31af7Sopenharmony_ci 194e5c31af7Sopenharmony_ci for line in fp: 195e5c31af7Sopenharmony_ci curLine = curLine + 1 196e5c31af7Sopenharmony_ci 197e5c31af7Sopenharmony_ci # Track changes up and down section headers, and forget 198e5c31af7Sopenharmony_ci # the current functions/structure when popping up a level 199e5c31af7Sopenharmony_ci match = sectionPat.search(line) 200e5c31af7Sopenharmony_ci if (match): 201e5c31af7Sopenharmony_ci info('Match sectionPat for line:', line) 202e5c31af7Sopenharmony_ci depth = len(match.group(1)) 203e5c31af7Sopenharmony_ci if (depth < sectionDepth): 204e5c31af7Sopenharmony_ci info('Resetting current function/structure for section:', line) 205e5c31af7Sopenharmony_ci curFuncs = [] 206e5c31af7Sopenharmony_ci curStruct = None 207e5c31af7Sopenharmony_ci sectionDepth = depth 208e5c31af7Sopenharmony_ci 209e5c31af7Sopenharmony_ci match = pathPat.search(line) 210e5c31af7Sopenharmony_ci if (match): 211e5c31af7Sopenharmony_ci incpath = match.group(1) 212e5c31af7Sopenharmony_ci info('Match pathPat for line:', line) 213e5c31af7Sopenharmony_ci info(' incpath =', incpath) 214e5c31af7Sopenharmony_ci # An include:: directive. First check if it looks like a 215e5c31af7Sopenharmony_ci # function or struct include file, and modify the corresponding 216e5c31af7Sopenharmony_ci # current function or struct state accordingly. 217e5c31af7Sopenharmony_ci match = incPat.search(incpath) 218e5c31af7Sopenharmony_ci if (match): 219e5c31af7Sopenharmony_ci info('Match incPat for line:', line) 220e5c31af7Sopenharmony_ci # For prototypes, if it is preceded by 221e5c31af7Sopenharmony_ci # another include:: directive with no intervening link: tags, 222e5c31af7Sopenharmony_ci # add to the current function list. Otherwise start a new list. 223e5c31af7Sopenharmony_ci # There is only one current structure. 224e5c31af7Sopenharmony_ci category = match.group(1) 225e5c31af7Sopenharmony_ci tag = match.group(2) 226e5c31af7Sopenharmony_ci # @ Validate tag! 227e5c31af7Sopenharmony_ci # @ Arguably, any intervening text should shift to accumFuncs = False, 228e5c31af7Sopenharmony_ci # e.g. only back-to-back includes separated by blank lines would be 229e5c31af7Sopenharmony_ci # accumulated. 230e5c31af7Sopenharmony_ci if (category == 'protos'): 231e5c31af7Sopenharmony_ci if (tag in protos.keys()): 232e5c31af7Sopenharmony_ci if (accumFunc): 233e5c31af7Sopenharmony_ci curFuncs.append(tag) 234e5c31af7Sopenharmony_ci else: 235e5c31af7Sopenharmony_ci curFuncs = [ tag ] 236e5c31af7Sopenharmony_ci # Restart accumulating functions 237e5c31af7Sopenharmony_ci accumFunc = True 238e5c31af7Sopenharmony_ci info('curFuncs =', curFuncs, 'accumFunc =', accumFunc) 239e5c31af7Sopenharmony_ci else: 240e5c31af7Sopenharmony_ci error('include of nonexistent function', tag) 241e5c31af7Sopenharmony_ci elif (category == 'structs'): 242e5c31af7Sopenharmony_ci if (tag in structs.keys()): 243e5c31af7Sopenharmony_ci curStruct = tag 244e5c31af7Sopenharmony_ci # Any /structs/ include means to stop accumulating /protos/ 245e5c31af7Sopenharmony_ci accumFunc = False 246e5c31af7Sopenharmony_ci info('curStruct =', curStruct) 247e5c31af7Sopenharmony_ci else: 248e5c31af7Sopenharmony_ci error('include of nonexistent struct', tag) 249e5c31af7Sopenharmony_ci if (follow): 250e5c31af7Sopenharmony_ci # Actually process the included file now, recursively 251e5c31af7Sopenharmony_ci newpath = os.path.normpath(os.path.join(inPath, incpath)) 252e5c31af7Sopenharmony_ci info(curFile, ': including file:', newpath) 253e5c31af7Sopenharmony_ci checkLinks(newpath, follow, paramCheck, included = True, fatalExit = fatalExit) 254e5c31af7Sopenharmony_ci 255e5c31af7Sopenharmony_ci matches = linkPat.findall(line) 256e5c31af7Sopenharmony_ci for match in matches: 257e5c31af7Sopenharmony_ci # Start actual validation work. Depending on what the 258e5c31af7Sopenharmony_ci # asciidoc tag name is, look up the value in the corresponding 259e5c31af7Sopenharmony_ci # dictionary. 260e5c31af7Sopenharmony_ci tag = match[0] 261e5c31af7Sopenharmony_ci value = match[2] 262e5c31af7Sopenharmony_ci if (tag == 'fname' or tag == 'flink'): 263e5c31af7Sopenharmony_ci if (value not in protos.keys()): 264e5c31af7Sopenharmony_ci foundError('function', tag, value, False) 265e5c31af7Sopenharmony_ci elif (tag == 'sname' or tag == 'slink'): 266e5c31af7Sopenharmony_ci if (value not in structs.keys() and 267e5c31af7Sopenharmony_ci value not in handles.keys()): 268e5c31af7Sopenharmony_ci foundError('aggregate/scalar/handle/define type', tag, value, False) 269e5c31af7Sopenharmony_ci elif (tag == 'ename'): 270e5c31af7Sopenharmony_ci if (value not in consts.keys() and value not in defines.keys()): 271e5c31af7Sopenharmony_ci foundError('enumerant/constant', tag, value, False) 272e5c31af7Sopenharmony_ci elif (tag == 'elink'): 273e5c31af7Sopenharmony_ci if (value not in enums.keys() and value not in flags.keys()): 274e5c31af7Sopenharmony_ci foundError('enum/bitflag type', tag, value, fatalExit) 275e5c31af7Sopenharmony_ci # tname and tlink are the same except if the errors are treated as fatal 276e5c31af7Sopenharmony_ci # They can be recombined once both are error-clean 277e5c31af7Sopenharmony_ci elif (tag == 'tname'): 278e5c31af7Sopenharmony_ci if (value not in funcpointers.keys() and value not in flags.keys()): 279e5c31af7Sopenharmony_ci foundError('function pointer/other type', tag, value, fatalExit) 280e5c31af7Sopenharmony_ci elif (tag == 'tlink'): 281e5c31af7Sopenharmony_ci if (value not in funcpointers.keys() and value not in flags.keys()): 282e5c31af7Sopenharmony_ci foundError('function pointer/other type', tag, value, False) 283e5c31af7Sopenharmony_ci elif (tag == 'pname'): 284e5c31af7Sopenharmony_ci # Any pname: tag means to stop accumulating /protos/ 285e5c31af7Sopenharmony_ci accumFunc = False 286e5c31af7Sopenharmony_ci # See if this parameter is in the current proto(s) and struct 287e5c31af7Sopenharmony_ci foundParam = False 288e5c31af7Sopenharmony_ci if (curStruct and value in structs[curStruct]): 289e5c31af7Sopenharmony_ci info('parameter', value, 'found in struct', curStruct) 290e5c31af7Sopenharmony_ci elif (curFuncs and findParam(value, curFuncs)): 291e5c31af7Sopenharmony_ci True 292e5c31af7Sopenharmony_ci else: 293e5c31af7Sopenharmony_ci if paramCheck: 294e5c31af7Sopenharmony_ci warning('parameter', value, 'not found. curStruct =', 295e5c31af7Sopenharmony_ci curStruct, 'curFuncs =', curFuncs) 296e5c31af7Sopenharmony_ci else: 297e5c31af7Sopenharmony_ci # This is a logic error 298e5c31af7Sopenharmony_ci error('unknown tag', tag + ':' + value) 299e5c31af7Sopenharmony_ci fp.close() 300e5c31af7Sopenharmony_ci 301e5c31af7Sopenharmony_ci if (errCount > 0 or warnCount > 0): 302e5c31af7Sopenharmony_ci if (not included): 303e5c31af7Sopenharmony_ci print('Errors found:', errCount, 'Warnings found:', warnCount) 304e5c31af7Sopenharmony_ci print('') 305e5c31af7Sopenharmony_ci 306e5c31af7Sopenharmony_ci if (included): 307e5c31af7Sopenharmony_ci info('----- returning from:', infile, 'to parent file', '-----') 308e5c31af7Sopenharmony_ci 309e5c31af7Sopenharmony_ci # Do not generate any output for files without errors 310e5c31af7Sopenharmony_ci # else: 311e5c31af7Sopenharmony_ci # print(curFile + ': No errors found') 312e5c31af7Sopenharmony_ci 313e5c31af7Sopenharmony_ci # Restore global state before exiting the function 314e5c31af7Sopenharmony_ci curFile = oldCurFile 315e5c31af7Sopenharmony_ci curLine = oldCurLine 316e5c31af7Sopenharmony_ci 317e5c31af7Sopenharmony_ciif __name__ == '__main__': 318e5c31af7Sopenharmony_ci follow = False 319e5c31af7Sopenharmony_ci paramCheck = False 320e5c31af7Sopenharmony_ci included = False 321e5c31af7Sopenharmony_ci fatalExit = False 322e5c31af7Sopenharmony_ci 323e5c31af7Sopenharmony_ci totalErrCount = 0 324e5c31af7Sopenharmony_ci totalWarnCount = 0 325e5c31af7Sopenharmony_ci 326e5c31af7Sopenharmony_ci if (len(sys.argv) > 1): 327e5c31af7Sopenharmony_ci for file in sys.argv[1:]: 328e5c31af7Sopenharmony_ci if (file == '-follow'): 329e5c31af7Sopenharmony_ci follow = True 330e5c31af7Sopenharmony_ci elif (file == '-info'): 331e5c31af7Sopenharmony_ci printInfo = True 332e5c31af7Sopenharmony_ci elif file == '-paramcheck': 333e5c31af7Sopenharmony_ci paramCheck = True 334e5c31af7Sopenharmony_ci elif (file == '-fatal'): 335e5c31af7Sopenharmony_ci fatalExit = True 336e5c31af7Sopenharmony_ci else: 337e5c31af7Sopenharmony_ci initChecks() 338e5c31af7Sopenharmony_ci checkLinks(file, 339e5c31af7Sopenharmony_ci follow, 340e5c31af7Sopenharmony_ci paramCheck = paramCheck, 341e5c31af7Sopenharmony_ci included = included, 342e5c31af7Sopenharmony_ci fatalExit = fatalExit) 343e5c31af7Sopenharmony_ci totalErrCount = totalErrCount + errCount 344e5c31af7Sopenharmony_ci totalWarnCount = totalWarnCount + warnCount 345e5c31af7Sopenharmony_ci else: 346e5c31af7Sopenharmony_ci print('Need arguments: [-follow] [-info] [-paramcheck] [-fatal] infile [infile...]', file=sys.stderr) 347e5c31af7Sopenharmony_ci 348e5c31af7Sopenharmony_ci if (totalErrCount > 0 or totalWarnCount > 0): 349e5c31af7Sopenharmony_ci if (not included): 350e5c31af7Sopenharmony_ci print('TOTAL Errors found:', totalErrCount, 'Warnings found:', 351e5c31af7Sopenharmony_ci totalWarnCount) 352e5c31af7Sopenharmony_ci if totalErrCount > 0: 353e5c31af7Sopenharmony_ci sys.exit(1) 354