1e5c31af7Sopenharmony_ci#!/usr/bin/python3 2e5c31af7Sopenharmony_ci# 3e5c31af7Sopenharmony_ci# Copyright 2016-2024 The Khronos Group Inc. 4e5c31af7Sopenharmony_ci# 5e5c31af7Sopenharmony_ci# SPDX-License-Identifier: Apache-2.0 6e5c31af7Sopenharmony_ci 7e5c31af7Sopenharmony_ci# Utility functions for automatic ref page generation and other script stuff 8e5c31af7Sopenharmony_ci 9e5c31af7Sopenharmony_ciimport io 10e5c31af7Sopenharmony_ciimport re 11e5c31af7Sopenharmony_ciimport sys 12e5c31af7Sopenharmony_ciimport subprocess 13e5c31af7Sopenharmony_ci 14e5c31af7Sopenharmony_ci# global errFile, warnFile, diagFile 15e5c31af7Sopenharmony_ci 16e5c31af7Sopenharmony_cierrFile = sys.stderr 17e5c31af7Sopenharmony_ciwarnFile = sys.stdout 18e5c31af7Sopenharmony_cidiagFile = None 19e5c31af7Sopenharmony_cilogSourcefile = None 20e5c31af7Sopenharmony_cilogProcname = None 21e5c31af7Sopenharmony_cilogLine = None 22e5c31af7Sopenharmony_ci 23e5c31af7Sopenharmony_cidef unescapeQuotes(s): 24e5c31af7Sopenharmony_ci """Remove \' escape sequences in a string (refpage description)""" 25e5c31af7Sopenharmony_ci return s.replace('\\\'', '\'') 26e5c31af7Sopenharmony_ci 27e5c31af7Sopenharmony_cidef write(*args, **kwargs ): 28e5c31af7Sopenharmony_ci file = kwargs.pop('file',sys.stdout) 29e5c31af7Sopenharmony_ci end = kwargs.pop('end','\n') 30e5c31af7Sopenharmony_ci file.write(' '.join(str(arg) for arg in args)) 31e5c31af7Sopenharmony_ci file.write(end) 32e5c31af7Sopenharmony_ci 33e5c31af7Sopenharmony_cidef setLogSourcefile(filename): 34e5c31af7Sopenharmony_ci """Metadata which may be printed (if not None) for diagnostic messages""" 35e5c31af7Sopenharmony_ci global logSourcefile 36e5c31af7Sopenharmony_ci logSourcefile = filename 37e5c31af7Sopenharmony_ci 38e5c31af7Sopenharmony_cidef setLogProcname(procname): 39e5c31af7Sopenharmony_ci global logProcname 40e5c31af7Sopenharmony_ci logProcname = procname 41e5c31af7Sopenharmony_ci 42e5c31af7Sopenharmony_cidef setLogLine(line): 43e5c31af7Sopenharmony_ci global logLine 44e5c31af7Sopenharmony_ci logLine = line 45e5c31af7Sopenharmony_ci 46e5c31af7Sopenharmony_cidef logHeader(severity): 47e5c31af7Sopenharmony_ci """Generate prefix for a diagnostic line using metadata and severity""" 48e5c31af7Sopenharmony_ci global logSourcefile, logProcname, logLine 49e5c31af7Sopenharmony_ci 50e5c31af7Sopenharmony_ci msg = severity + ': ' 51e5c31af7Sopenharmony_ci if logProcname: 52e5c31af7Sopenharmony_ci msg = msg + ' in ' + logProcname 53e5c31af7Sopenharmony_ci if logSourcefile: 54e5c31af7Sopenharmony_ci msg = msg + ' for ' + logSourcefile 55e5c31af7Sopenharmony_ci if logLine: 56e5c31af7Sopenharmony_ci msg = msg + ' line ' + str(logLine) 57e5c31af7Sopenharmony_ci return msg + ' ' 58e5c31af7Sopenharmony_ci 59e5c31af7Sopenharmony_cidef setLogFile(setDiag, setWarn, filename): 60e5c31af7Sopenharmony_ci """Set the file handle to log either or both warnings and diagnostics to. 61e5c31af7Sopenharmony_ci 62e5c31af7Sopenharmony_ci - setDiag and setWarn are True if the corresponding handle is to be set. 63e5c31af7Sopenharmony_ci - filename is None for no logging, '-' for stdout, or a pathname.""" 64e5c31af7Sopenharmony_ci global diagFile, warnFile 65e5c31af7Sopenharmony_ci 66e5c31af7Sopenharmony_ci if filename is None: 67e5c31af7Sopenharmony_ci return 68e5c31af7Sopenharmony_ci 69e5c31af7Sopenharmony_ci if filename == '-': 70e5c31af7Sopenharmony_ci fp = sys.stdout 71e5c31af7Sopenharmony_ci else: 72e5c31af7Sopenharmony_ci fp = open(filename, 'w', encoding='utf-8') 73e5c31af7Sopenharmony_ci 74e5c31af7Sopenharmony_ci if setDiag: 75e5c31af7Sopenharmony_ci diagFile = fp 76e5c31af7Sopenharmony_ci if setWarn: 77e5c31af7Sopenharmony_ci warnFile = fp 78e5c31af7Sopenharmony_ci 79e5c31af7Sopenharmony_cidef logDiag(*args, **kwargs): 80e5c31af7Sopenharmony_ci file = kwargs.pop('file', diagFile) 81e5c31af7Sopenharmony_ci end = kwargs.pop('end','\n') 82e5c31af7Sopenharmony_ci if file is not None: 83e5c31af7Sopenharmony_ci file.write(logHeader('DIAG') + ' '.join(str(arg) for arg in args)) 84e5c31af7Sopenharmony_ci file.write(end) 85e5c31af7Sopenharmony_ci 86e5c31af7Sopenharmony_cidef logWarn(*args, **kwargs): 87e5c31af7Sopenharmony_ci file = kwargs.pop('file', warnFile) 88e5c31af7Sopenharmony_ci end = kwargs.pop('end','\n') 89e5c31af7Sopenharmony_ci if file is not None: 90e5c31af7Sopenharmony_ci file.write(logHeader('WARN') + ' '.join(str(arg) for arg in args)) 91e5c31af7Sopenharmony_ci file.write(end) 92e5c31af7Sopenharmony_ci 93e5c31af7Sopenharmony_cidef logErr(*args, **kwargs): 94e5c31af7Sopenharmony_ci file = kwargs.pop('file', errFile) 95e5c31af7Sopenharmony_ci end = kwargs.pop('end','\n') 96e5c31af7Sopenharmony_ci 97e5c31af7Sopenharmony_ci strfile = io.StringIO() 98e5c31af7Sopenharmony_ci strfile.write(logHeader('ERROR') + ' '.join(str(arg) for arg in args)) 99e5c31af7Sopenharmony_ci strfile.write(end) 100e5c31af7Sopenharmony_ci 101e5c31af7Sopenharmony_ci if file is not None: 102e5c31af7Sopenharmony_ci file.write(strfile.getvalue()) 103e5c31af7Sopenharmony_ci raise UserWarning(strfile.getvalue()) 104e5c31af7Sopenharmony_ci 105e5c31af7Sopenharmony_cidef isempty(s): 106e5c31af7Sopenharmony_ci """Return True if s is nothing but white space, False otherwise""" 107e5c31af7Sopenharmony_ci return len(''.join(s.split())) == 0 108e5c31af7Sopenharmony_ci 109e5c31af7Sopenharmony_ciclass pageInfo: 110e5c31af7Sopenharmony_ci """Information about a ref page relative to the file it is extracted from.""" 111e5c31af7Sopenharmony_ci def __init__(self): 112e5c31af7Sopenharmony_ci self.extractPage = True 113e5c31af7Sopenharmony_ci """True if page should be extracted""" 114e5c31af7Sopenharmony_ci 115e5c31af7Sopenharmony_ci self.Warning = None 116e5c31af7Sopenharmony_ci """string warning if page is suboptimal or cannot be generated""" 117e5c31af7Sopenharmony_ci 118e5c31af7Sopenharmony_ci self.embed = False 119e5c31af7Sopenharmony_ci """False or the name of the ref page this include is embedded within""" 120e5c31af7Sopenharmony_ci 121e5c31af7Sopenharmony_ci self.type = None 122e5c31af7Sopenharmony_ci """refpage type attribute - 'structs', 'protos', 'freeform', etc.""" 123e5c31af7Sopenharmony_ci 124e5c31af7Sopenharmony_ci self.name = None 125e5c31af7Sopenharmony_ci """struct/proto/enumerant/etc. name""" 126e5c31af7Sopenharmony_ci 127e5c31af7Sopenharmony_ci self.desc = None 128e5c31af7Sopenharmony_ci """short description of ref page""" 129e5c31af7Sopenharmony_ci 130e5c31af7Sopenharmony_ci self.begin = None 131e5c31af7Sopenharmony_ci """index of first line of the page (heuristic or // refBegin)""" 132e5c31af7Sopenharmony_ci 133e5c31af7Sopenharmony_ci self.include = None 134e5c31af7Sopenharmony_ci """index of include:: line defining the page""" 135e5c31af7Sopenharmony_ci 136e5c31af7Sopenharmony_ci self.param = None 137e5c31af7Sopenharmony_ci """index of first line of parameter/member definitions""" 138e5c31af7Sopenharmony_ci 139e5c31af7Sopenharmony_ci self.body = None 140e5c31af7Sopenharmony_ci """index of first line of body text""" 141e5c31af7Sopenharmony_ci 142e5c31af7Sopenharmony_ci self.validity = None 143e5c31af7Sopenharmony_ci """index of validity include""" 144e5c31af7Sopenharmony_ci 145e5c31af7Sopenharmony_ci self.end = None 146e5c31af7Sopenharmony_ci """index of last line of the page (heuristic validity include, or // refEnd)""" 147e5c31af7Sopenharmony_ci 148e5c31af7Sopenharmony_ci self.alias = '' 149e5c31af7Sopenharmony_ci """aliases of this name, if supplied, or ''""" 150e5c31af7Sopenharmony_ci 151e5c31af7Sopenharmony_ci self.refs = '' 152e5c31af7Sopenharmony_ci """cross-references on // refEnd line, if supplied""" 153e5c31af7Sopenharmony_ci 154e5c31af7Sopenharmony_ci self.spec = None 155e5c31af7Sopenharmony_ci """'spec' attribute in refpage open block, if supplied, or None for the default ('api') type""" 156e5c31af7Sopenharmony_ci 157e5c31af7Sopenharmony_ci self.anchor = None 158e5c31af7Sopenharmony_ci """'anchor' attribute in refpage open block, if supplied, or inferred to be the same as the 'name'""" 159e5c31af7Sopenharmony_ci 160e5c31af7Sopenharmony_cidef printPageInfoField(desc, line, file): 161e5c31af7Sopenharmony_ci """Print a single field of a pageInfo struct, possibly None. 162e5c31af7Sopenharmony_ci 163e5c31af7Sopenharmony_ci - desc - string description of field 164e5c31af7Sopenharmony_ci - line - field value or None 165e5c31af7Sopenharmony_ci - file - indexed by line""" 166e5c31af7Sopenharmony_ci if line is not None: 167e5c31af7Sopenharmony_ci logDiag(desc + ':', line + 1, '\t-> ', file[line], end='') 168e5c31af7Sopenharmony_ci else: 169e5c31af7Sopenharmony_ci logDiag(desc + ':', line) 170e5c31af7Sopenharmony_ci 171e5c31af7Sopenharmony_cidef printPageInfo(pi, file): 172e5c31af7Sopenharmony_ci """Print out fields of a pageInfo struct 173e5c31af7Sopenharmony_ci 174e5c31af7Sopenharmony_ci - pi - pageInfo 175e5c31af7Sopenharmony_ci - file - indexed by pageInfo""" 176e5c31af7Sopenharmony_ci logDiag('TYPE: ', pi.type) 177e5c31af7Sopenharmony_ci logDiag('NAME: ', pi.name) 178e5c31af7Sopenharmony_ci logDiag('WARNING:', pi.Warning) 179e5c31af7Sopenharmony_ci logDiag('EXTRACT:', pi.extractPage) 180e5c31af7Sopenharmony_ci logDiag('EMBED: ', pi.embed) 181e5c31af7Sopenharmony_ci logDiag('DESC: ', pi.desc) 182e5c31af7Sopenharmony_ci printPageInfoField('BEGIN ', pi.begin, file) 183e5c31af7Sopenharmony_ci printPageInfoField('INCLUDE ', pi.include, file) 184e5c31af7Sopenharmony_ci printPageInfoField('PARAM ', pi.param, file) 185e5c31af7Sopenharmony_ci printPageInfoField('BODY ', pi.body, file) 186e5c31af7Sopenharmony_ci printPageInfoField('VALIDITY', pi.validity, file) 187e5c31af7Sopenharmony_ci printPageInfoField('END ', pi.end, file) 188e5c31af7Sopenharmony_ci logDiag('REFS: "' + pi.refs + '"') 189e5c31af7Sopenharmony_ci 190e5c31af7Sopenharmony_cidef prevPara(file, line): 191e5c31af7Sopenharmony_ci """Go back one paragraph from the specified line and return the line number 192e5c31af7Sopenharmony_ci of the first line of that paragraph. 193e5c31af7Sopenharmony_ci 194e5c31af7Sopenharmony_ci Paragraphs are delimited by blank lines. It is assumed that the 195e5c31af7Sopenharmony_ci current line is the first line of a paragraph. 196e5c31af7Sopenharmony_ci 197e5c31af7Sopenharmony_ci - file is an array of strings 198e5c31af7Sopenharmony_ci - line is the starting point (zero-based)""" 199e5c31af7Sopenharmony_ci # Skip over current paragraph 200e5c31af7Sopenharmony_ci while (line >= 0 and not isempty(file[line])): 201e5c31af7Sopenharmony_ci line = line - 1 202e5c31af7Sopenharmony_ci # Skip over white space 203e5c31af7Sopenharmony_ci while (line >= 0 and isempty(file[line])): 204e5c31af7Sopenharmony_ci line = line - 1 205e5c31af7Sopenharmony_ci # Skip to first line of previous paragraph 206e5c31af7Sopenharmony_ci while (line >= 1 and not isempty(file[line-1])): 207e5c31af7Sopenharmony_ci line = line - 1 208e5c31af7Sopenharmony_ci return line 209e5c31af7Sopenharmony_ci 210e5c31af7Sopenharmony_cidef nextPara(file, line): 211e5c31af7Sopenharmony_ci """Go forward one paragraph from the specified line and return the line 212e5c31af7Sopenharmony_ci number of the first line of that paragraph. 213e5c31af7Sopenharmony_ci 214e5c31af7Sopenharmony_ci Paragraphs are delimited by blank lines. It is assumed that the 215e5c31af7Sopenharmony_ci current line is standalone (which is bogus). 216e5c31af7Sopenharmony_ci 217e5c31af7Sopenharmony_ci - file is an array of strings 218e5c31af7Sopenharmony_ci - line is the starting point (zero-based)""" 219e5c31af7Sopenharmony_ci maxLine = len(file) - 1 220e5c31af7Sopenharmony_ci # Skip over current paragraph 221e5c31af7Sopenharmony_ci while (line != maxLine and not isempty(file[line])): 222e5c31af7Sopenharmony_ci line = line + 1 223e5c31af7Sopenharmony_ci # Skip over white space 224e5c31af7Sopenharmony_ci while (line != maxLine and isempty(file[line])): 225e5c31af7Sopenharmony_ci line = line + 1 226e5c31af7Sopenharmony_ci return line 227e5c31af7Sopenharmony_ci 228e5c31af7Sopenharmony_cidef lookupPage(pageMap, name): 229e5c31af7Sopenharmony_ci """Return (creating if needed) the pageInfo entry in pageMap for name""" 230e5c31af7Sopenharmony_ci if name not in pageMap: 231e5c31af7Sopenharmony_ci pi = pageInfo() 232e5c31af7Sopenharmony_ci pi.name = name 233e5c31af7Sopenharmony_ci pageMap[name] = pi 234e5c31af7Sopenharmony_ci else: 235e5c31af7Sopenharmony_ci pi = pageMap[name] 236e5c31af7Sopenharmony_ci return pi 237e5c31af7Sopenharmony_ci 238e5c31af7Sopenharmony_cidef loadFile(filename): 239e5c31af7Sopenharmony_ci """Load a file into a list of strings. Return the (list, newline_string) or (None, None) on failure""" 240e5c31af7Sopenharmony_ci newline_string = "\n" 241e5c31af7Sopenharmony_ci try: 242e5c31af7Sopenharmony_ci with open(filename, 'rb') as fp: 243e5c31af7Sopenharmony_ci contents = fp.read() 244e5c31af7Sopenharmony_ci if contents.count(b"\r\n") > 1: 245e5c31af7Sopenharmony_ci newline_string = "\r\n" 246e5c31af7Sopenharmony_ci 247e5c31af7Sopenharmony_ci with open(filename, 'r', encoding='utf-8') as fp: 248e5c31af7Sopenharmony_ci lines = fp.readlines() 249e5c31af7Sopenharmony_ci except: 250e5c31af7Sopenharmony_ci logWarn('Cannot open file', filename, ':', sys.exc_info()[0]) 251e5c31af7Sopenharmony_ci return None, None 252e5c31af7Sopenharmony_ci 253e5c31af7Sopenharmony_ci return lines, newline_string 254e5c31af7Sopenharmony_ci 255e5c31af7Sopenharmony_cidef clampToBlock(line, minline, maxline): 256e5c31af7Sopenharmony_ci """Clamp a line number to be in the range [minline,maxline]. 257e5c31af7Sopenharmony_ci 258e5c31af7Sopenharmony_ci If the line number is None, just return it. 259e5c31af7Sopenharmony_ci If minline is None, do not clamp to that value.""" 260e5c31af7Sopenharmony_ci if line is None: 261e5c31af7Sopenharmony_ci return line 262e5c31af7Sopenharmony_ci if minline and line < minline: 263e5c31af7Sopenharmony_ci return minline 264e5c31af7Sopenharmony_ci if line > maxline: 265e5c31af7Sopenharmony_ci return maxline 266e5c31af7Sopenharmony_ci 267e5c31af7Sopenharmony_ci return line 268e5c31af7Sopenharmony_ci 269e5c31af7Sopenharmony_cidef fixupRefs(pageMap, specFile, file): 270e5c31af7Sopenharmony_ci """Fill in missing fields in pageInfo structures, to the extent they can be 271e5c31af7Sopenharmony_ci inferred. 272e5c31af7Sopenharmony_ci 273e5c31af7Sopenharmony_ci - pageMap - dictionary of pageInfo structures 274e5c31af7Sopenharmony_ci - specFile - filename 275e5c31af7Sopenharmony_ci - file - list of strings making up the file, indexed by pageInfo""" 276e5c31af7Sopenharmony_ci # All potential ref pages are now in pageMap. Process them to 277e5c31af7Sopenharmony_ci # identify actual page start/end/description boundaries, if 278e5c31af7Sopenharmony_ci # not already determined from the text. 279e5c31af7Sopenharmony_ci for name in sorted(pageMap.keys()): 280e5c31af7Sopenharmony_ci pi = pageMap[name] 281e5c31af7Sopenharmony_ci 282e5c31af7Sopenharmony_ci # # If nothing is found but an include line with no begin, validity, 283e5c31af7Sopenharmony_ci # # or end, this is not intended as a ref page (yet). Set the begin 284e5c31af7Sopenharmony_ci # # line to the include line, so autogeneration can at least 285e5c31af7Sopenharmony_ci # # pull the include out, but mark it not to be extracted. 286e5c31af7Sopenharmony_ci # # Examples include the host sync table includes in 287e5c31af7Sopenharmony_ci # # chapters/fundamentals.adoc and the table of Vk*Flag types in 288e5c31af7Sopenharmony_ci # # appendices/boilerplate.adoc. 289e5c31af7Sopenharmony_ci # if pi.begin is None and pi.validity is None and pi.end is None: 290e5c31af7Sopenharmony_ci # pi.begin = pi.include 291e5c31af7Sopenharmony_ci # pi.extractPage = False 292e5c31af7Sopenharmony_ci # pi.Warning = 'No begin, validity, or end lines identified' 293e5c31af7Sopenharmony_ci # continue 294e5c31af7Sopenharmony_ci 295e5c31af7Sopenharmony_ci # Using open block delimiters, ref pages must *always* have a 296e5c31af7Sopenharmony_ci # defined begin and end. If either is undefined, that is fatal. 297e5c31af7Sopenharmony_ci if pi.begin is None: 298e5c31af7Sopenharmony_ci pi.extractPage = False 299e5c31af7Sopenharmony_ci pi.Warning = 'Can\'t identify begin of ref page open block' 300e5c31af7Sopenharmony_ci continue 301e5c31af7Sopenharmony_ci 302e5c31af7Sopenharmony_ci if pi.end is None: 303e5c31af7Sopenharmony_ci pi.extractPage = False 304e5c31af7Sopenharmony_ci pi.Warning = 'Can\'t identify end of ref page open block' 305e5c31af7Sopenharmony_ci continue 306e5c31af7Sopenharmony_ci 307e5c31af7Sopenharmony_ci # If there is no description of the page, infer one from the type 308e5c31af7Sopenharmony_ci if pi.desc is None: 309e5c31af7Sopenharmony_ci if pi.type is not None: 310e5c31af7Sopenharmony_ci # pi.desc = pi.type[0:len(pi.type)-1] + ' (no short description available)' 311e5c31af7Sopenharmony_ci pi.Warning = 'No short description available; could infer from the type and name' 312e5c31af7Sopenharmony_ci else: 313e5c31af7Sopenharmony_ci pi.extractPage = False 314e5c31af7Sopenharmony_ci pi.Warning = 'No short description available, cannot infer from the type' 315e5c31af7Sopenharmony_ci continue 316e5c31af7Sopenharmony_ci 317e5c31af7Sopenharmony_ci # Try to determine where the parameter and body sections of the page 318e5c31af7Sopenharmony_ci # begin. funcpointer, proto, and struct pages infer the location of 319e5c31af7Sopenharmony_ci # the parameter and body sections. Other pages infer the location of 320e5c31af7Sopenharmony_ci # the body, but have no parameter sections. 321e5c31af7Sopenharmony_ci # 322e5c31af7Sopenharmony_ci # Probably some other types infer this as well - refer to list of 323e5c31af7Sopenharmony_ci # all page types in genRef.py:emitPage() 324e5c31af7Sopenharmony_ci if pi.include is not None: 325e5c31af7Sopenharmony_ci if pi.type in ['funcpointers', 'protos', 'structs']: 326e5c31af7Sopenharmony_ci pi.param = nextPara(file, pi.include) 327e5c31af7Sopenharmony_ci if pi.body is None: 328e5c31af7Sopenharmony_ci pi.body = nextPara(file, pi.param) 329e5c31af7Sopenharmony_ci else: 330e5c31af7Sopenharmony_ci if pi.body is None: 331e5c31af7Sopenharmony_ci pi.body = nextPara(file, pi.include) 332e5c31af7Sopenharmony_ci else: 333e5c31af7Sopenharmony_ci pi.Warning = 'Page does not have an API definition include::' 334e5c31af7Sopenharmony_ci 335e5c31af7Sopenharmony_ci # It is possible for the inferred param and body lines to run past 336e5c31af7Sopenharmony_ci # the end of block, if, for example, there is no parameter section. 337e5c31af7Sopenharmony_ci pi.param = clampToBlock(pi.param, pi.include, pi.end) 338e5c31af7Sopenharmony_ci pi.body = clampToBlock(pi.body, pi.param, pi.end) 339e5c31af7Sopenharmony_ci 340e5c31af7Sopenharmony_ci # We can get to this point with .include, .param, and .validity 341e5c31af7Sopenharmony_ci # all being None, indicating those sections were not found. 342e5c31af7Sopenharmony_ci 343e5c31af7Sopenharmony_ci logDiag('fixupRefs: after processing,', pi.name, 'looks like:') 344e5c31af7Sopenharmony_ci printPageInfo(pi, file) 345e5c31af7Sopenharmony_ci 346e5c31af7Sopenharmony_ci # Now that all the valid pages have been found, try to make some 347e5c31af7Sopenharmony_ci # inferences about invalid pages. 348e5c31af7Sopenharmony_ci # 349e5c31af7Sopenharmony_ci # If a reference without a .end is entirely inside a valid reference, 350e5c31af7Sopenharmony_ci # then it is intentionally embedded - may want to create an indirect 351e5c31af7Sopenharmony_ci # page that links into the embedding page. This is done by a very 352e5c31af7Sopenharmony_ci # inefficient double loop, but the loop depth is small. 353e5c31af7Sopenharmony_ci for name in sorted(pageMap.keys()): 354e5c31af7Sopenharmony_ci pi = pageMap[name] 355e5c31af7Sopenharmony_ci 356e5c31af7Sopenharmony_ci if pi.end is None: 357e5c31af7Sopenharmony_ci for embedName in sorted(pageMap.keys()): 358e5c31af7Sopenharmony_ci logDiag('fixupRefs: comparing', pi.name, 'to', embedName) 359e5c31af7Sopenharmony_ci embed = pageMap[embedName] 360e5c31af7Sopenharmony_ci # Do not check embeddings which are themselves invalid 361e5c31af7Sopenharmony_ci if not embed.extractPage: 362e5c31af7Sopenharmony_ci logDiag('Skipping check for embedding in:', embed.name) 363e5c31af7Sopenharmony_ci continue 364e5c31af7Sopenharmony_ci if embed.begin is None or embed.end is None: 365e5c31af7Sopenharmony_ci logDiag('fixupRefs:', name + ':', 366e5c31af7Sopenharmony_ci 'can\'t compare to unanchored ref:', embed.name, 367e5c31af7Sopenharmony_ci 'in', specFile, 'at line', pi.include ) 368e5c31af7Sopenharmony_ci printPageInfo(pi, file) 369e5c31af7Sopenharmony_ci printPageInfo(embed, file) 370e5c31af7Sopenharmony_ci # If an embed is found, change the error to a warning 371e5c31af7Sopenharmony_ci elif (pi.include is not None and pi.include >= embed.begin and 372e5c31af7Sopenharmony_ci pi.include <= embed.end): 373e5c31af7Sopenharmony_ci logDiag('fixupRefs: Found embed for:', name, 374e5c31af7Sopenharmony_ci 'inside:', embedName, 375e5c31af7Sopenharmony_ci 'in', specFile, 'at line', pi.include ) 376e5c31af7Sopenharmony_ci pi.embed = embed.name 377e5c31af7Sopenharmony_ci pi.Warning = 'Embedded in definition for ' + embed.name 378e5c31af7Sopenharmony_ci break 379e5c31af7Sopenharmony_ci else: 380e5c31af7Sopenharmony_ci logDiag('fixupRefs: No embed match for:', name, 381e5c31af7Sopenharmony_ci 'inside:', embedName, 'in', specFile, 382e5c31af7Sopenharmony_ci 'at line', pi.include) 383e5c31af7Sopenharmony_ci 384e5c31af7Sopenharmony_ci 385e5c31af7Sopenharmony_cidef compatiblePageTypes(refpage_type, pagemap_type): 386e5c31af7Sopenharmony_ci """Returns whether two refpage 'types' (categories) are compatible - 387e5c31af7Sopenharmony_ci this is only true for 'consts' and 'enums' types.""" 388e5c31af7Sopenharmony_ci 389e5c31af7Sopenharmony_ci constsEnums = [ 'consts', 'enums' ] 390e5c31af7Sopenharmony_ci 391e5c31af7Sopenharmony_ci if refpage_type == pagemap_type: 392e5c31af7Sopenharmony_ci return True 393e5c31af7Sopenharmony_ci if refpage_type in constsEnums and pagemap_type in constsEnums: 394e5c31af7Sopenharmony_ci return True 395e5c31af7Sopenharmony_ci return False 396e5c31af7Sopenharmony_ci 397e5c31af7Sopenharmony_ci# Patterns used to recognize interesting lines in an asciidoc source file. 398e5c31af7Sopenharmony_ci# These patterns are only compiled once. 399e5c31af7Sopenharmony_ciendifPat = re.compile(r'^endif::(?P<condition>[\w_+,]+)\[\]') 400e5c31af7Sopenharmony_cibeginPat = re.compile(r'^\[open,(?P<attribs>refpage=.*)\]') 401e5c31af7Sopenharmony_ci# attribute key/value pairs of an open block 402e5c31af7Sopenharmony_ciattribStr = r"([a-z]+)='([^'\\]*(?:\\.[^'\\]*)*)'" 403e5c31af7Sopenharmony_ciattribPat = re.compile(attribStr) 404e5c31af7Sopenharmony_cibodyPat = re.compile(r'^// *refBody') 405e5c31af7Sopenharmony_cierrorPat = re.compile(r'^// *refError') 406e5c31af7Sopenharmony_ci 407e5c31af7Sopenharmony_ci# This regex transplanted from check_spec_links 408e5c31af7Sopenharmony_ci# It looks for various generated file conventions, and for the api/validity 409e5c31af7Sopenharmony_ci# include (generated_type), protos/struct/etc path (category), and API name 410e5c31af7Sopenharmony_ci# (entity_name). 411e5c31af7Sopenharmony_ci# It could be put into the API conventions object, instead of being 412e5c31af7Sopenharmony_ci# generalized for all the different specs. 413e5c31af7Sopenharmony_ciINCLUDE = re.compile( 414e5c31af7Sopenharmony_ci r'include::(?P<directory_traverse>((../){1,4}|\{generated\}/)(generated/)?)(?P<generated_type>[\w]+)/(?P<category>\w+)/(?P<entity_name>[^./]+)\.(adoc|txt)[\[][\]]') 415e5c31af7Sopenharmony_ci 416e5c31af7Sopenharmony_cidef findRefs(file, filename): 417e5c31af7Sopenharmony_ci """Identify reference pages in a list of strings, returning a dictionary of 418e5c31af7Sopenharmony_ci pageInfo entries for each one found, or None on failure.""" 419e5c31af7Sopenharmony_ci setLogSourcefile(filename) 420e5c31af7Sopenharmony_ci setLogProcname('findRefs') 421e5c31af7Sopenharmony_ci 422e5c31af7Sopenharmony_ci # To reliably detect the open blocks around reference pages, we must 423e5c31af7Sopenharmony_ci # first detect the '[open,refpage=...]' markup delimiting the block; 424e5c31af7Sopenharmony_ci # skip past the '--' block delimiter on the next line; and identify the 425e5c31af7Sopenharmony_ci # '--' block delimiter closing the page. 426e5c31af7Sopenharmony_ci # This cannot be done solely with pattern matching, and requires state to 427e5c31af7Sopenharmony_ci # track 'inside/outside block'. 428e5c31af7Sopenharmony_ci # When looking for open blocks, possible states are: 429e5c31af7Sopenharmony_ci # 'outside' - outside a block 430e5c31af7Sopenharmony_ci # 'start' - have found the '[open...]' line 431e5c31af7Sopenharmony_ci # 'inside' - have found the following '--' line 432e5c31af7Sopenharmony_ci openBlockState = 'outside' 433e5c31af7Sopenharmony_ci 434e5c31af7Sopenharmony_ci # Dictionary of interesting line numbers and strings related to an API 435e5c31af7Sopenharmony_ci # name 436e5c31af7Sopenharmony_ci pageMap = {} 437e5c31af7Sopenharmony_ci 438e5c31af7Sopenharmony_ci numLines = len(file) 439e5c31af7Sopenharmony_ci line = 0 440e5c31af7Sopenharmony_ci 441e5c31af7Sopenharmony_ci # Track the pageInfo object corresponding to the current open block 442e5c31af7Sopenharmony_ci pi = None 443e5c31af7Sopenharmony_ci 444e5c31af7Sopenharmony_ci while (line < numLines): 445e5c31af7Sopenharmony_ci setLogLine(line) 446e5c31af7Sopenharmony_ci 447e5c31af7Sopenharmony_ci # Only one of the patterns can possibly match. Add it to 448e5c31af7Sopenharmony_ci # the dictionary for that name. 449e5c31af7Sopenharmony_ci 450e5c31af7Sopenharmony_ci # [open,refpage=...] starting a refpage block 451e5c31af7Sopenharmony_ci matches = beginPat.search(file[line]) 452e5c31af7Sopenharmony_ci if matches is not None: 453e5c31af7Sopenharmony_ci logDiag('Matched open block pattern') 454e5c31af7Sopenharmony_ci attribs = matches.group('attribs') 455e5c31af7Sopenharmony_ci 456e5c31af7Sopenharmony_ci # If the previous open block was not closed, raise an error 457e5c31af7Sopenharmony_ci if openBlockState != 'outside': 458e5c31af7Sopenharmony_ci logErr('Nested open block starting at line', line, 'of', 459e5c31af7Sopenharmony_ci filename) 460e5c31af7Sopenharmony_ci 461e5c31af7Sopenharmony_ci openBlockState = 'start' 462e5c31af7Sopenharmony_ci 463e5c31af7Sopenharmony_ci # Parse the block attributes 464e5c31af7Sopenharmony_ci matches = attribPat.findall(attribs) 465e5c31af7Sopenharmony_ci 466e5c31af7Sopenharmony_ci # Extract each attribute 467e5c31af7Sopenharmony_ci name = None 468e5c31af7Sopenharmony_ci desc = None 469e5c31af7Sopenharmony_ci refpage_type = None 470e5c31af7Sopenharmony_ci spec_type = None 471e5c31af7Sopenharmony_ci anchor = None 472e5c31af7Sopenharmony_ci alias = None 473e5c31af7Sopenharmony_ci xrefs = None 474e5c31af7Sopenharmony_ci 475e5c31af7Sopenharmony_ci for (key,value) in matches: 476e5c31af7Sopenharmony_ci logDiag('got attribute', key, '=', value) 477e5c31af7Sopenharmony_ci if key == 'refpage': 478e5c31af7Sopenharmony_ci name = value 479e5c31af7Sopenharmony_ci elif key == 'desc': 480e5c31af7Sopenharmony_ci desc = unescapeQuotes(value) 481e5c31af7Sopenharmony_ci elif key == 'type': 482e5c31af7Sopenharmony_ci refpage_type = value 483e5c31af7Sopenharmony_ci elif key == 'spec': 484e5c31af7Sopenharmony_ci spec_type = value 485e5c31af7Sopenharmony_ci elif key == 'anchor': 486e5c31af7Sopenharmony_ci anchor = value 487e5c31af7Sopenharmony_ci elif key == 'alias': 488e5c31af7Sopenharmony_ci alias = value 489e5c31af7Sopenharmony_ci elif key == 'xrefs': 490e5c31af7Sopenharmony_ci xrefs = value 491e5c31af7Sopenharmony_ci else: 492e5c31af7Sopenharmony_ci logWarn('unknown open block attribute:', key) 493e5c31af7Sopenharmony_ci 494e5c31af7Sopenharmony_ci if name is None or desc is None or refpage_type is None: 495e5c31af7Sopenharmony_ci logWarn('missing one or more required open block attributes:' 496e5c31af7Sopenharmony_ci 'refpage, desc, or type') 497e5c31af7Sopenharmony_ci # Leave pi is None so open block delimiters are ignored 498e5c31af7Sopenharmony_ci else: 499e5c31af7Sopenharmony_ci pi = lookupPage(pageMap, name) 500e5c31af7Sopenharmony_ci pi.desc = desc 501e5c31af7Sopenharmony_ci # Must match later type definitions in interface/validity includes 502e5c31af7Sopenharmony_ci pi.type = refpage_type 503e5c31af7Sopenharmony_ci pi.spec = spec_type 504e5c31af7Sopenharmony_ci pi.anchor = anchor 505e5c31af7Sopenharmony_ci if alias: 506e5c31af7Sopenharmony_ci pi.alias = alias 507e5c31af7Sopenharmony_ci if xrefs: 508e5c31af7Sopenharmony_ci pi.refs = xrefs 509e5c31af7Sopenharmony_ci logDiag('open block for', name, 'added DESC =', desc, 510e5c31af7Sopenharmony_ci 'TYPE =', refpage_type, 'ALIAS =', alias, 511e5c31af7Sopenharmony_ci 'XREFS =', xrefs, 'SPEC =', spec_type, 512e5c31af7Sopenharmony_ci 'ANCHOR =', anchor) 513e5c31af7Sopenharmony_ci 514e5c31af7Sopenharmony_ci line = line + 1 515e5c31af7Sopenharmony_ci continue 516e5c31af7Sopenharmony_ci 517e5c31af7Sopenharmony_ci # '--' starting or ending and open block 518e5c31af7Sopenharmony_ci if file[line].rstrip() == '--': 519e5c31af7Sopenharmony_ci if openBlockState == 'outside': 520e5c31af7Sopenharmony_ci # Only refpage open blocks should use -- delimiters 521e5c31af7Sopenharmony_ci logWarn('Unexpected double-dash block delimiters') 522e5c31af7Sopenharmony_ci elif openBlockState == 'start': 523e5c31af7Sopenharmony_ci # -- delimiter following [open,refpage=...] 524e5c31af7Sopenharmony_ci openBlockState = 'inside' 525e5c31af7Sopenharmony_ci 526e5c31af7Sopenharmony_ci if pi is None: 527e5c31af7Sopenharmony_ci logWarn('no pageInfo available for opening -- delimiter') 528e5c31af7Sopenharmony_ci else: 529e5c31af7Sopenharmony_ci pi.begin = line + 1 530e5c31af7Sopenharmony_ci logDiag('opening -- delimiter: added BEGIN =', pi.begin) 531e5c31af7Sopenharmony_ci elif openBlockState == 'inside': 532e5c31af7Sopenharmony_ci # -- delimiter ending an open block 533e5c31af7Sopenharmony_ci if pi is None: 534e5c31af7Sopenharmony_ci logWarn('no pageInfo available for closing -- delimiter') 535e5c31af7Sopenharmony_ci else: 536e5c31af7Sopenharmony_ci pi.end = line - 1 537e5c31af7Sopenharmony_ci logDiag('closing -- delimiter: added END =', pi.end) 538e5c31af7Sopenharmony_ci 539e5c31af7Sopenharmony_ci openBlockState = 'outside' 540e5c31af7Sopenharmony_ci pi = None 541e5c31af7Sopenharmony_ci else: 542e5c31af7Sopenharmony_ci logWarn('unknown openBlockState:', openBlockState) 543e5c31af7Sopenharmony_ci 544e5c31af7Sopenharmony_ci line = line + 1 545e5c31af7Sopenharmony_ci continue 546e5c31af7Sopenharmony_ci 547e5c31af7Sopenharmony_ci matches = INCLUDE.search(file[line]) 548e5c31af7Sopenharmony_ci if matches is not None: 549e5c31af7Sopenharmony_ci # Something got included, not sure what yet. 550e5c31af7Sopenharmony_ci gen_type = matches.group('generated_type') 551e5c31af7Sopenharmony_ci refpage_type = matches.group('category') 552e5c31af7Sopenharmony_ci name = matches.group('entity_name') 553e5c31af7Sopenharmony_ci 554e5c31af7Sopenharmony_ci # This will never match in OpenCL 555e5c31af7Sopenharmony_ci if gen_type == 'validity': 556e5c31af7Sopenharmony_ci logDiag('Matched validity pattern') 557e5c31af7Sopenharmony_ci if pi is not None: 558e5c31af7Sopenharmony_ci if pi.type and not compatiblePageTypes(refpage_type, pi.type): 559e5c31af7Sopenharmony_ci logWarn('ERROR: pageMap[' + name + '] type:', 560e5c31af7Sopenharmony_ci pi.type, 'does not match type:', refpage_type) 561e5c31af7Sopenharmony_ci pi.type = refpage_type 562e5c31af7Sopenharmony_ci pi.validity = line 563e5c31af7Sopenharmony_ci logDiag('added TYPE =', pi.type, 'VALIDITY =', pi.validity) 564e5c31af7Sopenharmony_ci else: 565e5c31af7Sopenharmony_ci logWarn('validity include:: line NOT inside block') 566e5c31af7Sopenharmony_ci 567e5c31af7Sopenharmony_ci line = line + 1 568e5c31af7Sopenharmony_ci continue 569e5c31af7Sopenharmony_ci 570e5c31af7Sopenharmony_ci if gen_type == 'api': 571e5c31af7Sopenharmony_ci logDiag('Matched include pattern') 572e5c31af7Sopenharmony_ci if pi is not None: 573e5c31af7Sopenharmony_ci if pi.include is not None: 574e5c31af7Sopenharmony_ci logDiag('found multiple includes for this block') 575e5c31af7Sopenharmony_ci if pi.type and not compatiblePageTypes(refpage_type, pi.type): 576e5c31af7Sopenharmony_ci logWarn('ERROR: pageMap[' + name + '] type:', 577e5c31af7Sopenharmony_ci pi.type, 'does not match type:', refpage_type) 578e5c31af7Sopenharmony_ci pi.type = refpage_type 579e5c31af7Sopenharmony_ci pi.include = line 580e5c31af7Sopenharmony_ci logDiag('added TYPE =', pi.type, 'INCLUDE =', pi.include) 581e5c31af7Sopenharmony_ci else: 582e5c31af7Sopenharmony_ci logWarn('interface include:: line NOT inside block') 583e5c31af7Sopenharmony_ci 584e5c31af7Sopenharmony_ci line = line + 1 585e5c31af7Sopenharmony_ci continue 586e5c31af7Sopenharmony_ci 587e5c31af7Sopenharmony_ci logDiag('ignoring unrecognized include line ', matches.group()) 588e5c31af7Sopenharmony_ci 589e5c31af7Sopenharmony_ci # Vulkan 1.1 markup allows the last API include construct to be 590e5c31af7Sopenharmony_ci # followed by an asciidoctor endif:: construct (and also preceded, 591e5c31af7Sopenharmony_ci # at some distance). 592e5c31af7Sopenharmony_ci # This looks for endif:: immediately following an include:: line 593e5c31af7Sopenharmony_ci # and, if found, moves the include boundary to this line. 594e5c31af7Sopenharmony_ci matches = endifPat.search(file[line]) 595e5c31af7Sopenharmony_ci if matches is not None and pi is not None: 596e5c31af7Sopenharmony_ci if pi.include == line - 1: 597e5c31af7Sopenharmony_ci logDiag('Matched endif pattern following include; moving include') 598e5c31af7Sopenharmony_ci pi.include = line 599e5c31af7Sopenharmony_ci else: 600e5c31af7Sopenharmony_ci logDiag('Matched endif pattern (not following include)') 601e5c31af7Sopenharmony_ci 602e5c31af7Sopenharmony_ci line = line + 1 603e5c31af7Sopenharmony_ci continue 604e5c31af7Sopenharmony_ci 605e5c31af7Sopenharmony_ci matches = bodyPat.search(file[line]) 606e5c31af7Sopenharmony_ci if matches is not None: 607e5c31af7Sopenharmony_ci logDiag('Matched // refBody pattern') 608e5c31af7Sopenharmony_ci if pi is not None: 609e5c31af7Sopenharmony_ci pi.body = line 610e5c31af7Sopenharmony_ci logDiag('added BODY =', pi.body) 611e5c31af7Sopenharmony_ci else: 612e5c31af7Sopenharmony_ci logWarn('// refBody line NOT inside block') 613e5c31af7Sopenharmony_ci 614e5c31af7Sopenharmony_ci line = line + 1 615e5c31af7Sopenharmony_ci continue 616e5c31af7Sopenharmony_ci 617e5c31af7Sopenharmony_ci # OpenCL spec uses // refError to tag "validity" (Errors) language, 618e5c31af7Sopenharmony_ci # instead of /validity/ includes. 619e5c31af7Sopenharmony_ci matches = errorPat.search(file[line]) 620e5c31af7Sopenharmony_ci if matches is not None: 621e5c31af7Sopenharmony_ci logDiag('Matched // refError pattern') 622e5c31af7Sopenharmony_ci if pi is not None: 623e5c31af7Sopenharmony_ci pi.validity = line 624e5c31af7Sopenharmony_ci logDiag('added VALIDITY (refError) =', pi.validity) 625e5c31af7Sopenharmony_ci else: 626e5c31af7Sopenharmony_ci logWarn('// refError line NOT inside block') 627e5c31af7Sopenharmony_ci 628e5c31af7Sopenharmony_ci line = line + 1 629e5c31af7Sopenharmony_ci continue 630e5c31af7Sopenharmony_ci 631e5c31af7Sopenharmony_ci line = line + 1 632e5c31af7Sopenharmony_ci continue 633e5c31af7Sopenharmony_ci 634e5c31af7Sopenharmony_ci if pi is not None: 635e5c31af7Sopenharmony_ci logErr('Unclosed open block at EOF!') 636e5c31af7Sopenharmony_ci 637e5c31af7Sopenharmony_ci setLogSourcefile(None) 638e5c31af7Sopenharmony_ci setLogProcname(None) 639e5c31af7Sopenharmony_ci setLogLine(None) 640e5c31af7Sopenharmony_ci 641e5c31af7Sopenharmony_ci return pageMap 642e5c31af7Sopenharmony_ci 643e5c31af7Sopenharmony_ci 644e5c31af7Sopenharmony_cidef getBranch(): 645e5c31af7Sopenharmony_ci """Determine current git branch 646e5c31af7Sopenharmony_ci 647e5c31af7Sopenharmony_ci Returns (branch name, ''), or (None, stderr output) if the branch name 648e5c31af7Sopenharmony_ci cannot be determined""" 649e5c31af7Sopenharmony_ci 650e5c31af7Sopenharmony_ci command = [ 'git', 'symbolic-ref', '--short', 'HEAD' ] 651e5c31af7Sopenharmony_ci results = subprocess.run(command, 652e5c31af7Sopenharmony_ci stdout=subprocess.PIPE, 653e5c31af7Sopenharmony_ci stderr=subprocess.PIPE) 654e5c31af7Sopenharmony_ci 655e5c31af7Sopenharmony_ci # git command failed 656e5c31af7Sopenharmony_ci if len(results.stderr) > 0: 657e5c31af7Sopenharmony_ci return (None, results.stderr) 658e5c31af7Sopenharmony_ci 659e5c31af7Sopenharmony_ci # Remove newline from output and convert to a string 660e5c31af7Sopenharmony_ci branch = results.stdout.rstrip().decode() 661e5c31af7Sopenharmony_ci if len(branch) > 0: 662e5c31af7Sopenharmony_ci # Strip trailing newline 663e5c31af7Sopenharmony_ci branch = results.stdout.decode()[0:-1] 664e5c31af7Sopenharmony_ci 665e5c31af7Sopenharmony_ci return (branch, '') 666