diff options
Diffstat (limited to 'codegen/vulkan/scripts/reflow.py')
-rwxr-xr-x | codegen/vulkan/scripts/reflow.py | 912 |
1 files changed, 0 insertions, 912 deletions
diff --git a/codegen/vulkan/scripts/reflow.py b/codegen/vulkan/scripts/reflow.py deleted file mode 100755 index 1897b05c..00000000 --- a/codegen/vulkan/scripts/reflow.py +++ /dev/null @@ -1,912 +0,0 @@ -#!/usr/bin/python3 -# -# Copyright 2016-2021 The Khronos Group Inc. -# -# SPDX-License-Identifier: Apache-2.0 - -"""Used for automatic reflow of spec sources to satisfy the agreed layout to -minimize git churn. Most of the logic has to do with detecting asciidoc -markup or block types that *shouldn't* be reflowed (tables, code) and -ignoring them. It's very likely there are many asciidoc constructs not yet -accounted for in the script, our usage of asciidoc markup is intentionally -somewhat limited. - -Also used to insert identifying tags on explicit Valid Usage statements. - -Usage: `reflow.py [-noflow] [-tagvu] [-nextvu #] [-overwrite] [-out dir] [-suffix str] files` - -- `-noflow` acts as a passthrough, instead of reflowing text. Other - processing may occur. -- `-tagvu` generates explicit VUID tag for Valid Usage statements which - don't already have them. -- `-nextvu #` starts VUID tag generation at the specified # instead of - the value wired into the `reflow.py` script. -- `-overwrite` updates in place (can be risky, make sure there are backups) -- `-check FAIL|WARN` runs some simple sanity checks on markup. If the checks - fail and the WARN option is given, the script will simply print a warning - message. If the checks fail and the FAIL option is given, the script will - exit with an error code. FAIL is for use with continuous integration - scripts enforcing the checks. -- `-out` specifies directory to create output file in, default 'out' -- `-suffix` specifies suffix to add to output files, default '' -- `files` are asciidoc source files from the spec to reflow. -""" -# For error and file-loading interfaces only -import argparse -import os -import re -import sys -from reflib import loadFile, logDiag, logWarn, logErr, setLogFile, getBranch - -# Vulkan-specific - will consolidate into scripts/ like OpenXR soon -sys.path.insert(0, 'xml') - -from vkconventions import VulkanConventions as APIConventions -conventions = APIConventions() - -# Markup that always ends a paragraph -# empty line or whitespace -# [block options] -# [[anchor]] -# // comment -# <<<< page break -# :attribute-setting -# macro-directive::terms -# + standalone list item continuation -# label:: labelled list - label must be standalone -endPara = re.compile(r'^( *|\[.*\]|//.*|<<<<|:.*|[a-z]+::.*|\+|.*::)$') - -# Special case of markup ending a paragraph, used to track the current -# command/structure. This allows for either OpenXR or Vulkan API path -# conventions. Nominally it should use the file suffix defined by the API -# conventions (conventions.file_suffix), except that XR uses '.txt' for -# generated API include files, not '.adoc' like its other includes. -includePat = re.compile( - r'include::(?P<directory_traverse>((../){1,4}|\{INCS-VAR\}/|\{generated\}/)(generated/)?)(?P<generated_type>[\w]+)/(?P<category>\w+)/(?P<entity_name>[^./]+).txt[\[][\]]') - -# Find the first pname: or code: pattern in a Valid Usage statement -pnamePat = re.compile(r'pname:(?P<param>\{?\w+\}?)') -codePat = re.compile(r'code:(?P<param>\w+)') - -# Markup that's OK in a contiguous paragraph but otherwise passed through -# .anything (except .., which indicates a literal block) -# === Section Titles -endParaContinue = re.compile(r'^(\.[^.].*|=+ .*)$') - -# Markup for block delimiters whose contents *should* be reformatted -# -- (exactly two) (open block) -# **** (4 or more) (sidebar block - why do we have these?!) -# ==== (4 or more) (example block) -# ____ (4 or more) (quote block) -blockReflow = re.compile(r'^(--|[*=_]{4,})$') - -# Fake block delimiters for "common" VU statements -blockCommonReflow = '// Common Valid Usage\n' - -# Markup for block delimiters whose contents should *not* be reformatted -# |=== (3 or more) (table) -# ++++ (4 or more) (passthrough block) -# .... (4 or more) (literal block) -# //// (4 or more) (comment block) -# ---- (4 or more) (listing block) -# ``` (3 or more) (listing block) -# **** (4 or more) (sidebar block) -blockPassthrough = re.compile(r'^(\|={3,}|[`]{3}|[\-+./~]{4,})$') - -# Markup for introducing lists (hanging paragraphs) -# * bullet -# ** bullet -# -- bullet -# . bullet -# :: bullet (no longer supported by asciidoctor 2) -# {empty}:: bullet -# 1. list item -beginBullet = re.compile(r'^ *([*\-.]+|\{empty\}::|::|[0-9]+[.]) ') - -# Start of an asciidoctor conditional -# ifdef:: -# ifndef:: -conditionalStart = re.compile(r'^(ifdef|ifndef)::') - -# Text that (may) not end sentences - -# A single letter followed by a period, typically a middle initial. -endInitial = re.compile(r'^[A-Z]\.$') -# An abbreviation, which doesn't (usually) end a line. -endAbbrev = re.compile(r'(e\.g|i\.e|c\.f|vs)\.$', re.IGNORECASE) - -class ReflowState: - """State machine for reflowing. - - Represents the state of the reflow operation""" - def __init__(self, - filename, - margin = 76, - file = sys.stdout, - breakPeriod = True, - reflow = True, - nextvu = None, - maxvu = None): - - self.blockStack = [ None ] - """The last element is a line with the asciidoc block delimiter that's currently in effect, - such as '--', '----', '****', '======', or '+++++++++'. - This affects whether or not the block contents should be formatted.""" - - self.reflowStack = [ True ] - """The last element is True or False if the current blockStack contents - should be reflowed.""" - self.vuStack = [ False ] - """the last element is True or False if the current blockStack contents - are an explicit Valid Usage block.""" - - self.margin = margin - """margin to reflow text to.""" - - self.para = [] - """list of lines in the paragraph being accumulated. - When this is non-empty, there is a current paragraph.""" - - self.lastTitle = False - """true if the previous line was a document title line - (e.g. :leveloffset: 0 - no attempt to track changes to this is made).""" - - self.leadIndent = 0 - """indent level (in spaces) of the first line of a paragraph.""" - - self.hangIndent = 0 - """indent level of the remaining lines of a paragraph.""" - - self.file = file - """file handle to write to.""" - - self.filename = filename - """base name of file being read from.""" - - self.lineNumber = 0 - """line number being read from the input file.""" - - self.breakPeriod = breakPeriod - """True if justification should break to a new line after the end of a sentence.""" - - self.breakInitial = True - """True if justification should break to a new line after - something that appears to be an initial in someone's name. **TBD**""" - - self.reflow = reflow - """True if text should be reflowed, False to pass through unchanged.""" - - self.vuPrefix = 'VUID' - """Prefix of generated Valid Usage tags""" - - self.vuFormat = '{0}-{1}-{2}-{3:0>5d}' - """Format string for generating Valid Usage tags. - First argument is vuPrefix, second is command/struct name, third is parameter name, fourth is the tag number.""" - - self.nextvu = nextvu - """Integer to start tagging un-numbered Valid Usage statements with, - or None if no tagging should be done.""" - - self.maxvu = maxvu - """Maximum tag to use for Valid Usage statements, or None if no - tagging should be done.""" - - self.defaultApiName = '{refpage}' - self.apiName = self.defaultApiName - """String name of a Vulkan structure or command for VUID tag - generation, or {refpage} if one hasn't been included in this file - yet.""" - - def incrLineNumber(self): - self.lineNumber = self.lineNumber + 1 - - def printLines(self, lines): - """Print an array of lines with newlines already present""" - if len(lines) > 0: - logDiag(':: printLines:', len(lines), 'lines: ', lines[0], end='') - - if self.file is not None: - for line in lines: - print(line, file=self.file, end='') - - def endSentence(self, word): - """Return True if word ends with a sentence-period, False otherwise. - - Allows for contraction cases which won't end a line: - - - A single letter (if breakInitial is True) - - Abbreviations: 'c.f.', 'e.g.', 'i.e.' (or mixed-case versions)""" - if (word[-1:] != '.' or - endAbbrev.search(word) or - (self.breakInitial and endInitial.match(word))): - return False - - return True - - def vuidAnchor(self, word): - """Return True if word is a Valid Usage ID Tag anchor.""" - return (word[0:7] == '[[VUID-') - - def isOpenBlockDelimiter(self, line): - """Returns True if line is an open block delimiter.""" - return line[0:2] == '--' - - def reflowPara(self): - """Reflow the current paragraph, respecting the paragraph lead and - hanging indentation levels. - - The algorithm also respects trailing '+' signs that indicate embedded newlines, - and will not reflow a very long word immediately after a bullet point. - - Just return the paragraph unchanged if the -noflow argument was - given.""" - if not self.reflow: - return self.para - - logDiag('reflowPara lead indent = ', self.leadIndent, - 'hangIndent =', self.hangIndent, - 'para:', self.para[0], end='') - - # Total words processed (we care about the *first* word vs. others) - wordCount = 0 - - # Tracks the *previous* word processed. It must not be empty. - prevWord = ' ' - - # Track the previous line and paragraph being indented, if any - outLine = None - outPara = [] - - for line in self.para: - line = line.rstrip() - words = line.split() - - # logDiag('reflowPara: input line =', line) - numWords = len(words) - 1 - - for i in range(0, numWords + 1): - word = words[i] - wordLen = len(word) - wordCount += 1 - - endEscape = False - if i == numWords and word == '+': - # Trailing ' +' must stay on the same line - endEscape = word - # logDiag('reflowPara last word of line =', word, 'prevWord =', prevWord, 'endEscape =', endEscape) - else: - pass - # logDiag('reflowPara wordCount =', wordCount, 'word =', word, 'prevWord =', prevWord) - - if wordCount == 1: - # The first word of the paragraph is treated specially. - # The loop logic becomes trickier if all this code is - # done prior to looping over lines and words, so all the - # setup logic is done here. - - outPara = [] - outLine = ''.ljust(self.leadIndent) + word - outLineLen = self.leadIndent + wordLen - - # If the paragraph begins with a bullet point, generate - # a hanging indent level if there isn't one already. - if beginBullet.match(self.para[0]): - bulletPoint = True - if len(self.para) > 1: - logDiag('reflowPara first line matches bullet point', - 'but indent already hanging @ input line', - self.lineNumber) - else: - logDiag('reflowPara first line matches bullet point -' - 'single line, assuming hangIndent @ input line', - self.lineNumber) - self.hangIndent = outLineLen + 1 - else: - bulletPoint = False - else: - # Possible actions to take with this word - # - # addWord - add word to current line - # closeLine - append line and start a new (null) one - # startLine - add word to a new line - - # Default behavior if all the tests below fail is to add - # this word to the current line, and keep accumulating - # that line. - (addWord, closeLine, startLine) = (True, False, False) - - # How long would this line be if the word were added? - newLen = outLineLen + 1 + wordLen - - # Are we on the first word following a bullet point? - firstBullet = (wordCount == 2 and bulletPoint) - - if endEscape: - # If the new word ends the input line with ' +', - # add it to the current line. - - (addWord, closeLine, startLine) = (True, True, False) - elif self.vuidAnchor(word): - # If the new word is a Valid Usage anchor, break the - # line afterwards. Note that this should only happen - # immediately after a bullet point, but we don't - # currently check for this. - (addWord, closeLine, startLine) = (True, True, False) - elif newLen > self.margin: - if firstBullet: - # If the word follows a bullet point, add it to - # the current line no matter its length. - - (addWord, closeLine, startLine) = (True, True, False) - elif beginBullet.match(word + ' '): - # If the word *is* a bullet point, add it to - # the current line no matter its length. - # This avoids an innocent inline '-' or '*' - # turning into a bogus bullet point. - - (addWord, closeLine, startLine) = (True, True, False) - else: - # The word overflows, so add it to a new line. - - (addWord, closeLine, startLine) = (False, True, True) - elif (self.breakPeriod and - (wordCount > 2 or not firstBullet) and - self.endSentence(prevWord)): - # If the previous word ends a sentence and - # breakPeriod is set, start a new line. - # The complicated logic allows for leading bullet - # points which are periods (implicitly numbered lists). - # @@@ But not yet for explicitly numbered lists. - - (addWord, closeLine, startLine) = (False, True, True) - - # Add a word to the current line - if addWord: - if outLine: - outLine += ' ' + word - outLineLen = newLen - else: - # Fall through to startLine case if there's no - # current line yet. - startLine = True - - # Add current line to the output paragraph. Force - # starting a new line, although we don't yet know if it - # will ever have contents. - if closeLine: - if outLine: - outPara.append(outLine + '\n') - outLine = None - - # Start a new line and add a word to it - if startLine: - outLine = ''.ljust(self.hangIndent) + word - outLineLen = self.hangIndent + wordLen - - # Track the previous word, for use in breaking at end of - # a sentence - prevWord = word - - # Add this line to the output paragraph. - if outLine: - outPara.append(outLine + '\n') - - return outPara - - def emitPara(self): - """Emit a paragraph, possibly reflowing it depending on the block context. - - Resets the paragraph accumulator.""" - if self.para != []: - if self.vuStack[-1] and self.nextvu is not None: - # If: - # - this paragraph is in a Valid Usage block, - # - VUID tags are being assigned, - # Try to assign VUIDs - - if nestedVuPat.search(self.para[0]): - # Check for nested bullet points. These should not be - # assigned VUIDs, nor present at all, because they break - # the VU extractor. - logWarn(self.filename + ': Invalid nested bullet point in VU block:', self.para[0]) - elif self.vuPrefix not in self.para[0]: - # If: - # - a tag is not already present, and - # - the paragraph is a properly marked-up list item - # Then add a VUID tag starting with the next free ID. - - # Split the first line after the bullet point - matches = vuPat.search(self.para[0]) - if matches is not None: - logDiag('findRefs: Matched vuPat on line:', self.para[0], end='') - head = matches.group('head') - tail = matches.group('tail') - - # Use the first pname: or code: tag in the paragraph as - # the parameter name in the VUID tag. This won't always - # be correct, but should be highly reliable. - for vuLine in self.para: - matches = pnamePat.search(vuLine) - if matches is not None: - break - matches = codePat.search(vuLine) - if matches is not None: - break - - if matches is not None: - paramName = matches.group('param') - else: - paramName = 'None' - logWarn(self.filename, - 'No param name found for VUID tag on line:', - self.para[0]) - - newline = (head + ' [[' + - self.vuFormat.format(self.vuPrefix, - self.apiName, - paramName, - self.nextvu) + ']] ' + tail) - - logDiag('Assigning', self.vuPrefix, self.apiName, self.nextvu, - ' on line:', self.para[0], '->', newline, 'END') - - # Don't actually assign the VUID unless it's in the reserved range - if self.nextvu <= self.maxvu: - if self.nextvu == self.maxvu: - logWarn('Skipping VUID assignment, no more VUIDs available') - self.para[0] = newline - self.nextvu = self.nextvu + 1 - # else: - # There are only a few cases of this, and they're all - # legitimate. Leave detecting this case to another tool - # or hand inspection. - # logWarn(self.filename + ': Unexpected non-bullet item in VU block (harmless if following an ifdef):', - # self.para[0]) - - if self.reflowStack[-1]: - self.printLines(self.reflowPara()) - else: - self.printLines(self.para) - - # Reset the paragraph, including its indentation level - self.para = [] - self.leadIndent = 0 - self.hangIndent = 0 - - def endPara(self, line): - """'line' ends a paragraph and should itself be emitted. - line may be None to indicate EOF or other exception.""" - logDiag('endPara line', self.lineNumber, ': emitting paragraph') - - # Emit current paragraph, this line, and reset tracker - self.emitPara() - - if line: - self.printLines( [ line ] ) - - def endParaContinue(self, line): - """'line' ends a paragraph (unless there's already a paragraph being - accumulated, e.g. len(para) > 0 - currently not implemented)""" - self.endPara(line) - - def endBlock(self, line, reflow = False, vuBlock = False): - """'line' begins or ends a block. - - If beginning a block, tag whether or not to reflow the contents. - - vuBlock is True if the previous line indicates this is a Valid Usage block.""" - self.endPara(line) - - if self.blockStack[-1] == line: - logDiag('endBlock line', self.lineNumber, - ': popping block end depth:', len(self.blockStack), - ':', line, end='') - - # Reset apiName at the end of an open block. - # Open blocks cannot be nested (at present), so this is safe. - if self.isOpenBlockDelimiter(line): - logDiag('reset apiName to empty at line', self.lineNumber) - self.apiName = self.defaultApiName - else: - logDiag('NOT resetting apiName to default at line', self.lineNumber) - - self.blockStack.pop() - self.reflowStack.pop() - self.vuStack.pop() - else: - # Start a block - self.blockStack.append(line) - self.reflowStack.append(reflow) - self.vuStack.append(vuBlock) - - logDiag('endBlock reflow =', reflow, ' line', self.lineNumber, - ': pushing block start depth', len(self.blockStack), - ':', line, end='') - - def endParaBlockReflow(self, line, vuBlock): - """'line' begins or ends a block. The paragraphs in the block *should* be - reformatted (e.g. a NOTE).""" - self.endBlock(line, reflow = True, vuBlock = vuBlock) - - def endParaBlockPassthrough(self, line): - """'line' begins or ends a block. The paragraphs in the block should - *not* be reformatted (e.g. a code listing).""" - self.endBlock(line, reflow = False) - - def addLine(self, line): - """'line' starts or continues a paragraph. - - Paragraphs may have "hanging indent", e.g. - - ``` - * Bullet point... - ... continued - ``` - - In this case, when the higher indentation level ends, so does the - paragraph.""" - logDiag('addLine line', self.lineNumber, ':', line, end='') - - # See https://stackoverflow.com/questions/13648813/what-is-the-pythonic-way-to-count-the-leading-spaces-in-a-string - indent = len(line) - len(line.lstrip()) - - # A hanging paragraph ends due to a less-indented line. - if self.para != [] and indent < self.hangIndent: - logDiag('addLine: line reduces indentation, emit paragraph') - self.emitPara() - - # A bullet point (or something that looks like one) always ends the - # current paragraph. - if beginBullet.match(line): - logDiag('addLine: line matches beginBullet, emit paragraph') - self.emitPara() - - if self.para == []: - # Begin a new paragraph - self.para = [ line ] - self.leadIndent = indent - self.hangIndent = indent - else: - # Add a line to a paragraph. Increase the hanging indentation - # level - once. - if self.hangIndent == self.leadIndent: - self.hangIndent = indent - self.para.append(line) - -def apiMatch(oldname, newname): - """Returns whether oldname and newname match, up to an API suffix. - This should use the API map instead of this heuristic, since aliases - like VkPhysicalDeviceVariablePointerFeatures -> - VkPhysicalDeviceVariablePointersFeatures are not recognized.""" - upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - return oldname.rstrip(upper) == newname.rstrip(upper) - -def reflowFile(filename, args): - logDiag('reflow: filename', filename) - - lines = loadFile(filename) - if lines is None: - return - - # Output file handle and reflow object for this file. There are no race - # conditions on overwriting the input, but it's not recommended unless - # you have backing store such as git. - - if args.overwrite: - outFilename = filename - else: - outFilename = args.outDir + '/' + os.path.basename(filename) + args.suffix - - if args.nowrite: - fp = None - else: - try: - fp = open(outFilename, 'w', encoding='utf8') - except: - logWarn('Cannot open output file', outFilename, ':', sys.exc_info()[0]) - return - - state = ReflowState(filename, - margin = args.margin, - file = fp, - reflow = not args.noflow, - nextvu = args.nextvu, - maxvu = args.maxvu) - - for line in lines: - state.incrLineNumber() - - # Is this a title line (leading '= ' followed by text)? - thisTitle = False - - matches = vuidPat.search(line) - if matches is not None: - # If we found a VUID pattern, add the (filename,line) it was - # found at to a list for that VUID, to find duplicates. - vuid = matches.group('vuid') - if vuid not in args.vuidDict: - args.vuidDict[vuid] = [] - args.vuidDict[vuid].append([filename, line]) - - # The logic here is broken. If we're in a non-reflowable block and - # this line *doesn't* end the block, it should always be - # accumulated. - - # Test for a blockCommonReflow delimiter comment first, to avoid - # treating it solely as a end-Paragraph marker comment. - if line == blockCommonReflow: - # Starting or ending a pseudo-block for "common" VU statements. - state.endParaBlockReflow(line, vuBlock = True) - - elif blockReflow.match(line): - # Starting or ending a block whose contents may be reflowed. - # Blocks cannot be nested. - - # Is this is an explicit Valid Usage block? - vuBlock = (state.lineNumber > 1 and - lines[state.lineNumber-2] == '.Valid Usage\n') - - state.endParaBlockReflow(line, vuBlock) - - elif endPara.match(line): - # Ending a paragraph. Emit the current paragraph, if any, and - # prepare to begin a new paragraph. - - state.endPara(line) - - # If this is an include:: line starting the definition of a - # structure or command, track that for use in VUID generation. - - matches = includePat.search(line) - if matches is not None: - generated_type = matches.group('generated_type') - include_type = matches.group('category') - if generated_type == 'api' and include_type in ('protos', 'structs', 'funcpointers'): - apiName = matches.group('entity_name') - if state.apiName != state.defaultApiName: - # This happens when there are multiple API include - # lines in a single block. The style guideline is to - # always place the API which others are promoted to - # first. In virtually all cases, the promoted API - # will differ solely in the vendor suffix (or - # absence of it), which is benign. - if not apiMatch(state.apiName, apiName): - logDiag(f'Promoted API name mismatch at line {state.lineNumber}: {apiName} does not match state.apiName (this is OK if it is just a spelling alias)') - else: - state.apiName = apiName - - elif endParaContinue.match(line): - # For now, always just end the paragraph. - # Could check see if len(para) > 0 to accumulate. - - state.endParaContinue(line) - - # If it's a title line, track that - if line[0:2] == '= ': - thisTitle = True - - elif blockPassthrough.match(line): - # Starting or ending a block whose contents must not be reflowed. - # These are tables, etc. Blocks cannot be nested. - - state.endParaBlockPassthrough(line) - elif state.lastTitle: - # The previous line was a document title line. This line - # is the author / credits line and must not be reflowed. - - state.endPara(line) - else: - # Just accumulate a line to the current paragraph. Watch out for - # hanging indents / bullet-points and track that indent level. - - state.addLine(line) - - # This test looks for disallowed conditionals inside Valid Usage - # blocks, by checking if (a) this line does not start a new VU - # (bullet point) and (b) the previous line starts an asciidoctor - # conditional (ifdef:: or ifndef::). - - if (args.check - and state.vuStack[-1] - and not beginBullet.match(line) - and conditionalStart.match(lines[state.lineNumber-2])): - - logWarn('Detected embedded Valid Usage conditional: {}:{}'.format( - filename, state.lineNumber - 1)) - # Keep track of warning check count - args.warnCount = args.warnCount + 1 - - state.lastTitle = thisTitle - - # Cleanup at end of file - state.endPara(None) - - # Check for sensible block nesting - if len(state.blockStack) > 1: - logWarn('file', filename, - 'mismatched asciidoc block delimiters at EOF:', - state.blockStack[-1]) - - if fp is not None: - fp.close() - - # Update the 'nextvu' value - if args.nextvu != state.nextvu: - logWarn('Updated nextvu to', state.nextvu, 'after file', filename) - args.nextvu = state.nextvu - -def reflowAllAdocFiles(folder_to_reflow, args): - for root, subdirs, files in os.walk(folder_to_reflow): - for file in files: - if file.endswith(conventions.file_suffix): - file_path = os.path.join(root, file) - reflowFile(file_path, args) - for subdir in subdirs: - sub_folder = os.path.join(root, subdir) - print('Sub-folder = %s' % sub_folder) - if subdir.lower() not in conventions.spec_no_reflow_dirs: - print(' Parsing = %s' % sub_folder) - reflowAllAdocFiles(sub_folder, args) - else: - print(' Skipping = %s' % sub_folder) - -# Patterns used to recognize interesting lines in an asciidoc source file. -# These patterns are only compiled once. - -# Explicit Valid Usage list item with one or more leading asterisks -# The re.DOTALL is needed to prevent vuPat.search() from stripping -# the trailing newline. -vuPat = re.compile(r'^(?P<head> [*]+)( *)(?P<tail>.*)', re.DOTALL) - -# VUID with the numeric portion captured in the match object -vuidPat = re.compile(r'VUID-[^-]+-[^-]+-(?P<vuid>[0-9]+)') - -# Pattern matching leading nested bullet points -global nestedVuPat -nestedVuPat = re.compile(r'^ \*\*') - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - - parser.add_argument('-diag', action='store', dest='diagFile', - help='Set the diagnostic file') - parser.add_argument('-warn', action='store', dest='warnFile', - help='Set the warning file') - parser.add_argument('-log', action='store', dest='logFile', - help='Set the log file for both diagnostics and warnings') - parser.add_argument('-overwrite', action='store_true', - help='Overwrite input filenames instead of writing different output filenames') - parser.add_argument('-out', action='store', dest='outDir', - default='out', - help='Set the output directory in which updated files are generated (default: out)') - parser.add_argument('-nowrite', action='store_true', - help='Do not write output files, for use with -check') - parser.add_argument('-check', action='store', dest='check', - help='Run markup checks and warn if WARN option is given, error exit if FAIL option is given') - parser.add_argument('-checkVUID', action='store', dest='checkVUID', - help='Detect duplicated VUID numbers and warn if WARN option is given, error exit if FAIL option is given') - parser.add_argument('-tagvu', action='store_true', - help='Tag un-tagged Valid Usage statements starting at the value wired into reflow.py') - parser.add_argument('-nextvu', action='store', dest='nextvu', type=int, - default=None, - help='Specify start VUID to use instead of the value wired into vuidCounts.py') - parser.add_argument('-maxvu', action='store', dest='maxvu', type=int, - default=None, - help='Specify maximum VUID instead of the value wired into vuidCounts.py') - parser.add_argument('-branch', action='store', dest='branch', - help='Specify branch to assign VUIDs for') - parser.add_argument('-noflow', action='store_true', dest='noflow', - help='Do not reflow text. Other actions may apply') - parser.add_argument('-margin', action='store', type=int, dest='margin', - default='76', - help='Width to reflow text, defaults to 76 characters') - parser.add_argument('-suffix', action='store', dest='suffix', - default='', - help='Set the suffix added to updated file names (default: none)') - parser.add_argument('files', metavar='filename', nargs='*', - help='a filename to reflow text in') - parser.add_argument('--version', action='version', version='%(prog)s 1.0') - - args = parser.parse_args() - - setLogFile(True, True, args.logFile) - setLogFile(True, False, args.diagFile) - setLogFile(False, True, args.warnFile) - - print('args.margin = ', args.margin) - - if args.overwrite: - logWarn("reflow.py: will overwrite all input files") - - errors = '' - if args.branch is None: - (args.branch, errors) = getBranch() - if args.branch is None: - # This is not fatal unless VUID assignment is required - if args.tagvu: - logErr('Cannot determine current git branch, so cannot assign VUIDs:', errors) - - if args.tagvu and args.nextvu is None: - # Moved here since vuidCounts is only needed in the internal - # repository - from vuidCounts import vuidCounts - - if args.branch not in vuidCounts: - logErr('Branch', args.branch, 'not in vuidCounts, cannot continue') - maxVUID = vuidCounts[args.branch][1] - startVUID = vuidCounts[args.branch][2] - args.nextvu = startVUID - args.maxvu = maxVUID - - if args.nextvu is not None: - logWarn('Tagging untagged Valid Usage statements starting at', args.nextvu) - - # Count of markup check warnings encountered - # This is added to the argparse structure - args.warnCount = 0 - - # Dictionary of VUID numbers found, containing a list of (file, line) on - # which that number was found - # This is added to the argparse structure - args.vuidDict = {} - - # If no files are specified, reflow the entire specification chapters folder - if not args.files: - folder_to_reflow = conventions.spec_reflow_path - logWarn('Reflowing all asciidoc files under', folder_to_reflow) - reflowAllAdocFiles(folder_to_reflow, args) - else: - for file in args.files: - reflowFile(file, args) - - if args.warnCount > 0: - if args.check == 'FAIL': - logErr('Failed with', args.warnCount, 'markup errors detected.\n' + - 'To fix these, you can take actions such as:\n' + - ' * Moving conditionals outside VU start / end without changing VU meaning\n' + - ' * Refactor conditional text using terminology defined conditionally outside the VU itself\n' + - ' * Remove the conditional (allowable when this just affects command / structure / enum names)\n') - else: - logWarn('Total warning count for markup issues is', args.warnCount) - - # Look for duplicated VUID numbers - if args.checkVUID: - dupVUIDs = 0 - for vuid in sorted(args.vuidDict): - found = args.vuidDict[vuid] - if len(found) > 1: - logWarn('Duplicate VUID number {} found in files:'.format(vuid)) - for (file, line) in found: - logWarn(' {}: {}'.format(file, line)) - dupVUIDs = dupVUIDs + 1 - - if dupVUIDs > 0: - if args.checkVUID == 'FAIL': - logErr('Failed with', dupVUIDs, 'duplicated VUID numbers found.\n' + - 'To fix this, either convert these to commonvalidity VUs if possible, or strip\n' + - 'the VUIDs from all but one of the duplicates and regenerate new ones.') - else: - logWarn('Total number of duplicated VUID numbers is', dupVUIDs) - - if args.nextvu is not None and args.nextvu != startVUID: - # Update next free VUID to assign - vuidCounts[args.branch][2] = args.nextvu - try: - reflow_count_file_path = os.path.dirname(os.path.realpath(__file__)) - reflow_count_file_path += '/vuidCounts.py' - reflow_count_file = open(reflow_count_file_path, 'w', encoding='utf8') - print('# Do not edit this file!', file=reflow_count_file) - print('# VUID ranges reserved for branches', file=reflow_count_file) - print('# Key is branch name, value is [ start, end, nextfree ]', file=reflow_count_file) - print('vuidCounts = {', file=reflow_count_file) - for key in sorted(vuidCounts): - print(" '{}': [ {}, {}, {} ],".format( - key, - vuidCounts[key][0], - vuidCounts[key][1], - vuidCounts[key][2]), - file=reflow_count_file) - print('}', file=reflow_count_file) - reflow_count_file.close() - except: - logWarn('Cannot open output count file vuidCounts.py', ':', sys.exc_info()[0]) |