/ scripts / genRef.py
genRef.py
  1  #!/usr/bin/python3
  2  #
  3  # Copyright (c) 2016-2019 The Khronos Group Inc.
  4  #
  5  # Licensed under the Apache License, Version 2.0 (the "License");
  6  # you may not use this file except in compliance with the License.
  7  # You may obtain a copy of the License at
  8  #
  9  #     http://www.apache.org/licenses/LICENSE-2.0
 10  #
 11  # Unless required by applicable law or agreed to in writing, software
 12  # distributed under the License is distributed on an "AS IS" BASIS,
 13  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  # See the License for the specific language governing permissions and
 15  # limitations under the License.
 16  
 17  # genRef.py - create API ref pages from spec source files
 18  #
 19  # Usage: genRef.py files
 20  
 21  import argparse
 22  import io
 23  import os
 24  import re
 25  import sys
 26  from collections import OrderedDict
 27  from reflib import (findRefs, fixupRefs, loadFile, logDiag, logWarn,
 28                      printPageInfo, setLogFile)
 29  from reg import Registry
 30  import vkapi as api
 31  from vkconventions import VulkanConventions as APIConventions
 32  
 33  def makeExtensionInclude(name):
 34      """Return an include command, given an extension name."""
 35      return 'include::{}/refpage.{}{}[]'.format(
 36          conventions.specification_path,
 37          name,
 38          conventions.file_suffix)
 39  
 40  # Return True if name is an API extension name (ends with an upper-case
 41  # author ID). This assumes that author IDs are at least two characters.
 42  def isextension(name):
 43      return name[-2:].isalpha() and name[-2:].isupper()
 44  
 45  # Print Khronos CC-BY copyright notice on open file fp. If comment is
 46  # True, print as an asciidoc comment block, which copyrights the source
 47  # file. Otherwise print as an asciidoc include of the copyright in markup,
 48  # which copyrights the outputs. Also include some asciidoc boilerplate
 49  # needed by all the standalone ref pages.
 50  def printCopyrightSourceComments(fp):
 51      print('// Copyright (c) 2014-2019 Khronos Group. This work is licensed under a', file=fp)
 52      print('// Creative Commons Attribution 4.0 International License; see', file=fp)
 53      print('// http://creativecommons.org/licenses/by/4.0/', file=fp)
 54      print('', file=fp)
 55  
 56  def printFooter(fp):
 57      print('include::footer.txt[]', file=fp)
 58      print('', file=fp)
 59  
 60  
 61  # Add a spec asciidoc macro prefix to an API name, depending on its type
 62  # (protos, structs, enums, etc.). If the name is not recognized, use
 63  # the generic link macro 'reflink:'.
 64  def macroPrefix(name):
 65      if name in api.basetypes:
 66          return 'basetype:' + name
 67      elif name in api.defines:
 68          return 'dlink:' + name
 69      elif name in api.enums:
 70          return 'elink:' + name
 71      elif name in api.flags:
 72          return 'elink:' + name
 73      elif name in api.funcpointers:
 74          return 'tlink:' + name
 75      elif name in api.handles:
 76          return 'slink:' + name
 77      elif name in api.protos:
 78          return 'flink:' + name
 79      elif name in api.structs:
 80          return 'slink:' + name
 81      elif name == 'TBD':
 82          return 'No cross-references are available'
 83      else:
 84          return 'reflink:' + name
 85  
 86  # Return an asciidoc string with a list of 'See Also' references for the
 87  # API entity 'apiName', based on the relationship mapping in the api module.
 88  # 'explicitRefs' is a list of additional cross-references.
 89  # If no relationships are available, return None.
 90  def seeAlsoList(apiName, explicitRefs = None):
 91      refs = {}
 92  
 93      # Add all the implicit references to refs
 94      if apiName in api.mapDict:
 95          for name in sorted(api.mapDict[apiName]):
 96              refs[name] = None
 97  
 98      # Add all the explicit references
 99      if explicitRefs is not None:
100          if isinstance(explicitRefs, str):
101              explicitRefs = explicitRefs.split()
102          for name in explicitRefs:
103              refs[name] = None
104  
105      if not refs:
106          return None
107      return ', '.join(macroPrefix(name) for name in sorted(refs.keys())) + '\n'
108  
109  # Remap include directives in a list of lines so they can be extracted to a
110  # different directory. Returns remapped lines.
111  #
112  # lines - text to remap
113  # baseDir - target directory
114  # specDir - source directory
115  def remapIncludes(lines, baseDir, specDir):
116      # This should be compiled only once
117      includePat = re.compile(r'^include::(?P<path>.*)\[\]')
118  
119      newLines = []
120      for line in lines:
121          matches = includePat.search(line)
122          if matches is not None:
123              path = matches.group('path')
124  
125              if path[0] != '{':
126                  # Relative path to include file from here
127                  incPath = specDir + '/' + path
128                  # Remap to be relative to baseDir
129                  newPath = os.path.relpath(incPath, baseDir)
130                  newLine = 'include::' + newPath + '[]\n'
131                  logDiag('remapIncludes: remapping', line, '->', newLine)
132                  newLines.append(newLine)
133              else:
134                  # An asciidoctor variable starts the path.
135                  # This must be an absolute path, not needing to be rewritten.
136                  newLines.append(line)
137          else:
138              newLines.append(line)
139      return newLines
140  
141  
142  def refPageShell(pageName, pageDesc, fp, sections=None, tail_content=None, man_section=3):
143      printCopyrightSourceComments(fp)
144  
145      print(':data-uri:',
146            ':icons: font',
147            conventions.extra_refpage_headers,
148            '',
149            sep='\n', file=fp)
150  
151      s = '{}({})'.format(pageName, man_section)
152      print('= ' + s,
153            '',
154            sep='\n', file=fp)
155      if pageDesc.strip() == '':
156          pageDesc = 'NO SHORT DESCRIPTION PROVIDED'
157          logWarn('refPageHead: no short description provided for', pageName)
158  
159      print('== Name',
160            '{} - {}'.format(pageName, pageDesc),
161            '',
162            sep='\n', file=fp)
163  
164      if sections:
165          for title, content in sections.items():
166              print('== {}'.format(title),
167                    '',
168                    content,
169                    '',
170                    sep='\n', file=fp)
171  
172      if tail_content:
173          print(tail_content,
174                '',
175                sep='\n', file=fp)
176  
177  
178  # Generate header of a reference page
179  # pageName - string name of the page
180  # pageDesc - string short description of the page
181  # specType - string containing 'spec' field from refpage open block, or None.
182  #   Used to determine containing spec name and URL.
183  # specText - string that goes in the "C Specification" section
184  # fieldName - string heading an additional section following specText, if not None
185  # fieldText - string that goes in the additional section
186  # descText - string that goes in the "Description" section
187  # fp - file to write to
188  def refPageHead(pageName, pageDesc, specText, fieldName, fieldText, descText, fp):
189      sections = OrderedDict()
190  
191      if specText is not None:
192          sections['C Specification'] = specText
193  
194      if fieldName is not None:
195          sections[fieldName] = fieldText
196  
197      if descText is None or descText.strip() == '':
198          logWarn('refPageHead: no description provided for', pageName)
199  
200      if descText is not None:
201          sections['Description'] = descText
202  
203      refPageShell(pageName, pageDesc, fp, sections=sections)
204  
205  # specType is None or the 'spec' attribute from the refpage open block,
206  #   identifying the specification name and URL this refpage links to.
207  # specAnchor is None or the 'anchor' attribute from the refpage open block,
208  #   identifying the anchor in the specification this refpage links to. If
209  #   None, the pageName is assumed to be a valid anchor.
210  def refPageTail(pageName,
211                  specType = None,
212                  specAnchor = None,
213                  seeAlso = None,
214                  fp = None,
215                  auto = False):
216  
217      specName = conventions.api_name(specType)
218      specURL = conventions.specURL(specType)
219      if specAnchor is None:
220          specAnchor = pageName
221  
222      if seeAlso is None:
223          seeAlso = 'No cross-references are available\n'
224  
225      notes = [
226          'For more information, see the ' + specName + ' Specification at URL',
227          '',
228          '{}#{}'.format(specURL, specAnchor),
229          '',
230          ]
231  
232      if auto:
233          notes.extend((
234              'This page is a generated document.',
235              'Fixes and changes should be made to the generator scripts, '
236              'not directly.',
237              ))
238      else:
239          notes.extend((
240              'This page is extracted from the ' + specName + ' Specification. ',
241              'Fixes and changes should be made to the Specification, '
242              'not directly.',
243              ))
244  
245      print('== See Also',
246            '',
247            seeAlso,
248            '',
249            sep='\n', file=fp)
250  
251      print('== Document Notes',
252            '',
253            '\n'.join(notes),
254            '',
255            sep='\n', file=fp)
256  
257      printFooter(fp)
258  
259  # Extract a single reference page into baseDir
260  #   baseDir - base directory to emit page into
261  #   specDir - directory extracted page source came from
262  #   pi - pageInfo for this page relative to file
263  #   file - list of strings making up the file, indexed by pi
264  def emitPage(baseDir, specDir, pi, file):
265      pageName = baseDir + '/' + pi.name + '.txt'
266  
267      # Add a dictionary entry for this page
268      global genDict
269      genDict[pi.name] = None
270      logDiag('emitPage:', pageName)
271  
272      # Short description
273      if pi.desc is None:
274          pi.desc = '(no short description available)'
275  
276      # Member/parameter section label and text, if there is one
277      field = None
278      fieldText = None
279  
280      if pi.type != 'freeform':
281          # Not sure how this happens yet
282          if pi.include is None:
283              logWarn('emitPage:', pageName, 'INCLUDE is None, no page generated')
284              return
285  
286          # Specification text
287          lines = remapIncludes(file[pi.begin:pi.include+1], baseDir, specDir)
288          specText = ''.join(lines)
289  
290          if pi.param is not None:
291              if pi.type == 'structs':
292                  field = 'Members'
293              elif pi.type in ['protos', 'funcpointers']:
294                  field = 'Parameters'
295              else:
296                  logWarn('emitPage: unknown field type:', pi.type,
297                      'for', pi.name)
298              lines = remapIncludes(file[pi.param:pi.body], baseDir, specDir)
299              fieldText = ''.join(lines)
300  
301          # Description text
302          if pi.body != pi.include:
303              lines = remapIncludes(file[pi.body:pi.end+1], baseDir, specDir)
304              descText = ''.join(lines)
305          else:
306              descText = None
307              logWarn('emitPage: INCLUDE == BODY, so description will be empty for', pi.name)
308              if pi.begin != pi.include:
309                  logWarn('emitPage: Note: BEGIN != INCLUDE, so the description might be incorrectly located before the API include!')
310      else:
311          specText = None
312          descText = ''.join(file[pi.begin:pi.end+1])
313  
314      specURL = conventions.specURL(pi.spec)
315  
316      # Substitute xrefs to point at the main spec
317      specLinksPattern = re.compile(r'<<([^>,]+)[,]?[ \t\n]*([^>,]*)>>')
318      specLinksSubstitute = r'link:{}#\1[\2]'.format(specURL)
319      if specText is not None:
320          specText, _ = specLinksPattern.subn(specLinksSubstitute, specText)
321      if fieldText is not None:
322          fieldText, _ = specLinksPattern.subn(specLinksSubstitute, fieldText)
323      if descText is not None:
324          descText, _ = specLinksPattern.subn(specLinksSubstitute, descText)
325  
326      fp = open(pageName, 'w', encoding='utf-8')
327      refPageHead(pi.name,
328                  pi.desc,
329                  specText,
330                  field, fieldText,
331                  descText,
332                  fp)
333      refPageTail(pageName=pi.name,
334                  specType=pi.spec,
335                  specAnchor=pi.anchor,
336                  seeAlso=seeAlsoList(pi.name, pi.refs),
337                  fp=fp,
338                  auto=False)
339      fp.close()
340  
341  # Autogenerate a single reference page in baseDir
342  # Script only knows how to do this for /enums/ pages, at present
343  #   baseDir - base directory to emit page into
344  #   pi - pageInfo for this page relative to file
345  #   file - list of strings making up the file, indexed by pi
346  def autoGenEnumsPage(baseDir, pi, file):
347      pageName = baseDir + '/' + pi.name + '.txt'
348      fp = open(pageName, 'w', encoding='utf-8')
349  
350      # Add a dictionary entry for this page
351      global genDict
352      genDict[pi.name] = None
353      logDiag('autoGenEnumsPage:', pageName)
354  
355      # Short description
356      if pi.desc is None:
357          pi.desc = '(no short description available)'
358  
359      # Description text. Allow for the case where an enum definition
360      # is not embedded.
361      if not pi.embed:
362          embedRef = ''
363      else:
364          embedRef = ''.join((
365                             '  * The reference page for ',
366                             macroPrefix(pi.embed),
367                             ', where this interface is defined.\n' ))
368  
369      txt = ''.join((
370          'For more information, see:\n\n',
371          embedRef,
372          '  * The See Also section for other reference pages using this type.\n',
373          '  * The ' + apiName + ' Specification.\n' ))
374  
375      refPageHead(pi.name,
376                  pi.desc,
377                  ''.join(file[pi.begin:pi.include+1]),
378                  None, None,
379                  txt,
380                  fp)
381      refPageTail(pageName=pi.name,
382                  specType=pi.spec,
383                  specAnchor=pi.anchor,
384                  seeAlso=seeAlsoList(pi.name, pi.refs),
385                  fp=fp,
386                  auto=True)
387      fp.close()
388  
389  
390  # Pattern to break apart an API *Flags{authorID} name, used in
391  # autoGenFlagsPage.
392  flagNamePat = re.compile(r'(?P<name>\w+)Flags(?P<author>[A-Z]*)')
393  
394  
395  # Autogenerate a single reference page in baseDir for an API *Flags type
396  #   baseDir - base directory to emit page into
397  #   flagName - API *Flags name
398  def autoGenFlagsPage(baseDir, flagName):
399      pageName = baseDir + '/' + flagName + '.txt'
400      fp = open(pageName, 'w', encoding='utf-8')
401  
402      # Add a dictionary entry for this page
403      global genDict
404      genDict[flagName] = None
405      logDiag('autoGenFlagsPage:', pageName)
406  
407      # Short description
408      matches = flagNamePat.search(flagName)
409      if matches is not None:
410          name = matches.group('name')
411          author = matches.group('author')
412          logDiag('autoGenFlagsPage: split name into', name, 'Flags', author)
413          flagBits = name + 'FlagBits' + author
414          desc = 'Bitmask of ' + flagBits
415      else:
416          logWarn('autoGenFlagsPage:', pageName, 'does not end in "Flags{author ID}". Cannot infer FlagBits type.')
417          flagBits = None
418          desc = 'Unknown ' + apiName + ' flags type'
419  
420      # Description text
421      if flagBits is not None:
422          txt = ''.join((
423              'etext:' + flagName,
424              ' is a mask of zero or more elink:' + flagBits + '.\n',
425              'It is used as a member and/or parameter of the structures and commands\n',
426              'in the See Also section below.\n' ))
427      else:
428          txt = ''.join((
429              'etext:' + flagName,
430              ' is an unknown ' + apiName + ' type, assumed to be a bitmask.\n' ))
431  
432      refPageHead(flagName,
433                  desc,
434                  'include::../api/flags/' + flagName + '.txt[]\n',
435                  None, None,
436                  txt,
437                  fp)
438      refPageTail(pageName=flagName,
439                  specType=pi.spec,
440                  specAnchor=pi.anchor,
441                  seeAlso=seeAlsoList(flagName, None),
442                  fp=fp,
443                  auto=True)
444      fp.close()
445  
446  # Autogenerate a single handle page in baseDir for an API handle type
447  #   baseDir - base directory to emit page into
448  #   handleName - API handle name
449  # @@ Need to determine creation function & add handles/ include for the
450  # @@ interface in generator.py.
451  def autoGenHandlePage(baseDir, handleName):
452      pageName = baseDir + '/' + handleName + '.txt'
453      fp = open(pageName, 'w', encoding='utf-8')
454  
455      # Add a dictionary entry for this page
456      global genDict
457      genDict[handleName] = None
458      logDiag('autoGenHandlePage:', pageName)
459  
460      # Short description
461      desc = apiName + ' object handle'
462  
463      descText = ''.join((
464          'sname:' + handleName,
465          ' is an object handle type, referring to an object used\n',
466          'by the ' + apiName + ' implementation. These handles are created or allocated\n',
467          'by the @@ TBD @@ function, and used by other ' + apiName + ' structures\n',
468          'and commands in the See Also section below.\n' ))
469  
470      refPageHead(handleName,
471                  desc,
472                  'include::../api/handles/' + handleName + '.txt[]\n',
473                  None, None,
474                  descText,
475                  fp)
476      refPageTail(pageName=handleName,
477                  specType=pi.spec,
478                  specAnchor=pi.anchor,
479                  seeAlso=seeAlsoList(handleName, None),
480                  fp=fp,
481                  auto=True)
482      fp.close()
483  
484  # Extract reference pages from a spec asciidoc source file
485  #   specFile - filename to extract from
486  #   baseDir - output directory to generate page in
487  #
488  def genRef(specFile, baseDir):
489      file = loadFile(specFile)
490      if file is None:
491          return
492  
493      # Save the path to this file for later use in rewriting relative includes
494      specDir = os.path.dirname(os.path.abspath(specFile))
495  
496      pageMap = findRefs(file, specFile)
497      logDiag(specFile + ': found', len(pageMap.keys()), 'potential pages')
498  
499      sys.stderr.flush()
500  
501      # Fix up references in pageMap
502      fixupRefs(pageMap, specFile, file)
503  
504      # Create each page, if possible
505  
506      for name in sorted(pageMap):
507          pi = pageMap[name]
508  
509          printPageInfo(pi, file)
510  
511          if pi.Warning:
512              logDiag('genRef:', pi.name + ':', pi.Warning)
513  
514          if pi.extractPage:
515              emitPage(baseDir, specDir, pi, file)
516          elif pi.type == 'enums':
517              autoGenEnumsPage(baseDir, pi, file)
518          elif pi.type == 'flags':
519              autoGenFlagsPage(baseDir, pi.name)
520          else:
521              # Don't extract this page
522              logWarn('genRef: Cannot extract or autogenerate:', pi.name)
523  
524  # Generate baseDir/apispec.txt, the single-page version of the ref pages.
525  # This assumes there's a page for everything in the api module dictionaries.
526  # Extensions (KHR, EXT, etc.) are currently skipped
527  def genSinglePageRef(baseDir):
528      # Accumulate head of page
529      head = io.StringIO()
530  
531      printCopyrightSourceComments(head)
532  
533      print('= ' + apiName + ' API Reference Pages',
534            ':data-uri:',
535            ':icons: font',
536            ':doctype: book',
537            ':numbered!:',
538            ':max-width: 200',
539            ':data-uri:',
540            ':toc2:',
541            ':toclevels: 2',
542            '',
543            sep='\n', file=head)
544  
545      print('include::copyright-ccby.txt[]', file=head)
546      print('', file=head)
547      # Inject the table of contents. Asciidoc really ought to be generating
548      # this for us.
549  
550      sections = [
551          [ api.protos,       'protos',       apiName + ' Commands' ],
552          [ api.handles,      'handles',      'Object Handles' ],
553          [ api.structs,      'structs',      'Structures' ],
554          [ api.enums,        'enums',        'Enumerations' ],
555          [ api.flags,        'flags',        'Flags' ],
556          [ api.funcpointers, 'funcpointers', 'Function Pointer Types' ],
557          [ api.basetypes,    'basetypes',    apiName + ' Scalar types' ],
558          [ api.defines,      'defines',      'C Macro Definitions' ],
559          [ extensions,       'extensions',   apiName + ' Extensions' ]
560        ]
561  
562      # Accumulate body of page
563      body = io.StringIO()
564  
565      for (apiDict,label,title) in sections:
566          # Add section title/anchor header to body
567          anchor = '[[' + label + ',' + title + ']]'
568          print(anchor,
569                '== ' + title,
570                '',
571                ':leveloffset: 2',
572                '',
573                sep='\n', file=body)
574  
575          if label == 'extensions':
576              # preserve order of extensions since we already sorted the way we want.
577              keys = apiDict.keys()
578          else:
579              keys = sorted(apiDict.keys())
580  
581          for refPage in keys:
582              # Don't generate links for aliases, which are included with the
583              # aliased page
584              if refPage not in api.alias:
585                  # Add page to body
586                  if 'FlagBits' in refPage and conventions.unified_flag_refpages:
587                      # OpenXR does not create separate ref pages for FlagBits:
588                      # the FlagBits includes go in the Flags refpage.
589                      # Previously the Vulkan script would only emit non-empty
590                      # Vk*Flags pages, via the logic
591                      #   if refPage not in api.flags or api.flags[refPage] is not None
592                      #       emit page
593                      # Now, all are emitted.
594                      continue
595                  else:
596                      print('include::' + refPage + '.txt[]', file=body)
597              else:
598                  # Alternatively, we could (probably should) link to the
599                  # aliased refpage
600                  logWarn('(Benign) Not including', refPage,
601                          'in single-page reference',
602                          'because it is an alias of', api.alias[refPage])
603  
604          print('\n' + ':leveloffset: 0' + '\n', file=body)
605  
606      # Write head and body to the output file
607      pageName = baseDir + '/apispec.txt'
608      fp = open(pageName, 'w', encoding='utf-8')
609  
610      print(head.getvalue(), file=fp, end='')
611      print(body.getvalue(), file=fp, end='')
612  
613      head.close()
614      body.close()
615      fp.close()
616  
617  
618  def genExtension(baseDir, name, info):
619      # Add a dictionary entry for this page
620      global genDict
621      genDict[name] = None
622      declares = []
623      elem = info.elem
624  
625      ext_type = elem.get('type')
626  
627      for required in elem.find('require'):
628          req_name = required.get('name')
629          if not req_name:
630              # This isn't what we're looking for
631              continue
632          if req_name.endswith('_SPEC_VERSION') or req_name.endswith('_EXTENSION_NAME'):
633              # Don't link to spec version or extension name - those ref pages aren't created.
634              continue
635  
636          if required.get('extends'):
637              # These are either extensions of enums,
638              # or enum values: neither of which get a ref page.
639              continue
640  
641          if req_name not in genDict:
642              logWarn('ERROR: {} (in extension {}) does not have a ref page.'.format(req_name, name))
643  
644          declares.append(req_name)
645      pageName = baseDir + '/' + name + '.txt'
646      logDiag('genExtension:', pageName)
647  
648      fp = open(pageName, 'w', encoding='utf-8')
649  
650      sections = OrderedDict()
651      sections['Specification'] = 'See link:{html_spec_relative}#%s[ %s] in the main specification for complete information.' % (
652          name, name)
653      refPageShell(name,
654                   "{} extension".format(ext_type),
655                   fp,
656                   sections=sections,
657                   tail_content=makeExtensionInclude(name))
658      refPageTail(pageName=name,
659                  specType=None,
660                  specAnchor=name,
661                  seeAlso=seeAlsoList(name, declares),
662                  fp=fp,
663                  auto=True)
664      fp.close()
665  
666  
667  if __name__ == '__main__':
668      global genDict, extensions, conventions, apiName
669      genDict = {}
670      extensions = OrderedDict()
671      conventions = APIConventions()
672      apiName = conventions.api_name('api')
673  
674      parser = argparse.ArgumentParser()
675  
676      parser.add_argument('-diag', action='store', dest='diagFile',
677                          help='Set the diagnostic file')
678      parser.add_argument('-warn', action='store', dest='warnFile',
679                          help='Set the warning file')
680      parser.add_argument('-log', action='store', dest='logFile',
681                          help='Set the log file for both diagnostics and warnings')
682      parser.add_argument('-basedir', action='store', dest='baseDir',
683                          default='man',
684                          help='Set the base directory in which pages are generated')
685      parser.add_argument('-noauto', action='store_true',
686                          help='Don\'t generate inferred ref pages automatically')
687      parser.add_argument('files', metavar='filename', nargs='*',
688                          help='a filename to extract ref pages from')
689      parser.add_argument('--version', action='version', version='%(prog)s 1.0')
690      parser.add_argument('-extension', action='append',
691                          default=[],
692                          help='Specify an extension or extensions to add to targets')
693      parser.add_argument('-registry', action='store',
694                          default=conventions.registry_path,
695                          help='Use specified registry file instead of default')
696  
697      results = parser.parse_args()
698  
699      setLogFile(True,  True, results.logFile)
700      setLogFile(True, False, results.diagFile)
701      setLogFile(False, True, results.warnFile)
702  
703      baseDir = results.baseDir
704  
705      for file in results.files:
706          genRef(file, baseDir)
707  
708      # Now figure out which pages *weren't* generated from the spec.
709      # This relies on the dictionaries of API constructs in the api module.
710  
711      if not results.noauto:
712  
713          registry = Registry()
714          registry.loadFile(results.registry)
715  
716          if conventions.write_refpage_include:
717              # Only extensions with a supported="..." attribute in this set
718              # will be considered for extraction/generation.
719              supported_strings = set((conventions.xml_supported_name_of_api,))
720              ext_names = set(k for k, v in registry.extdict.items()
721                              if v.supported in supported_strings)
722  
723              desired_extensions = ext_names.intersection(set(results.extension))
724              for prefix in conventions.extension_index_prefixes:
725                  # Splits up into chunks, sorted within each chunk.
726                  filtered_extensions = sorted(
727                      [name for name in desired_extensions
728                       if name.startswith(prefix) and name not in extensions])
729                  for name in filtered_extensions:
730                      extensions[name] = None
731                      genExtension(baseDir, name, registry.extdict[name])
732  
733          # autoGenFlagsPage is no longer needed because they are added to
734          # the spec sources now.
735          # for page in api.flags:
736          #     if page not in genDict:
737          #         autoGenFlagsPage(baseDir, page)
738  
739          # autoGenHandlePage is no longer needed because they are added to
740          # the spec sources now.
741          # for page in api.structs:
742          #    if typeCategory[page] == 'handle':
743          #        autoGenHandlePage(baseDir, page)
744  
745          sections = [
746              ( api.flags,        'Flag Types' ),
747              ( api.enums,        'Enumerated Types' ),
748              ( api.structs,      'Structures' ),
749              ( api.protos,       'Prototypes' ),
750              ( api.funcpointers, 'Function Pointers' ),
751              ( api.basetypes,    apiName + ' Scalar Types' ),
752              ( extensions,       apiName + ' Extensions'),
753            ]
754  
755          # Summarize pages that weren't generated, for good or bad reasons
756  
757          for (apiDict,title) in sections:
758              # OpenXR was keeping a 'flagged' state which only printed out a
759              # warning for the first non-generated page, but was otherwise
760              # unused. This doesn't seem helpful.
761              for page in apiDict:
762                  if page not in genDict:
763                      # Page was not generated - why not?
764                      if page in api.alias:
765                          logWarn('(Benign, is an alias) Ref page for', title, page, 'is aliased into', api.alias[page])
766                      elif page in api.flags and api.flags[page] is None:
767                          logWarn('(Benign, no FlagBits defined) No ref page generated for ', title,
768                                  page)
769                      else:
770                          # Could introduce additional logic to detect
771                          # external types and not emit them.
772                          logWarn('No ref page generated for  ', title, page)
773  
774          genSinglePageRef(baseDir)