Skip to content

Commit

Permalink
Add support for generating Python pageMap and new script to modify va…
Browse files Browse the repository at this point in the history
…lidusage.json
  • Loading branch information
oddhack committed Oct 25, 2024
1 parent f3a114c commit b327877
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 32 deletions.
10 changes: 6 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ RBAPIMAP = $(GENERATED)/apimap.rb
PYXREFMAP = $(GENERATED)/xrefMap.py
JSXREFMAP = $(GENERATED)/xrefMap.cjs
JSPAGEMAP = $(GENERATED)/pageMap.cjs
PYPAGEMAP = $(GENERATED)/pageMap.py

# PDF Equations are written to SVGs, this dictates the location to store those files (temporary)
PDFMATHDIR = $(OUTDIR)/equations_temp
Expand Down Expand Up @@ -771,7 +772,8 @@ setup_spec_antora pagemap $(JSPAGEMAP): $(JSAPIMAP)
-component $(shell realpath antora/spec/modules/ROOT) \
-xrefpath $(GENERATED) \
-pageHeaders antora/pageHeaders-spec.adoc \
-pagemappath $(JSPAGEMAP) \
-jspagemap $(JSPAGEMAP) \
-pypagemap $(PYPAGEMAP) \
./config/attribs.adoc \
./config/copyright-ccby.adoc \
./config/copyright-spec.adoc \
Expand Down Expand Up @@ -858,13 +860,14 @@ CLEAN_GEN_PATHS = \
$(JSAPIMAP) \
$(PYAPIMAP) \
$(RBAPIMAP) \
$(REQSDEPEND) \
$(ATTRIBFILE)
$(REQSDEPEND)

clean_generated:
$(QUIET)$(RMRF) $(CLEAN_GEN_PATHS)

# Files generated by 'setup_antora' target
# Omit antora/features/modules/features/nav.adoc which is generated, but
# also checked in.
CLEAN_ANTORA_PATHS = \
antora/spec/modules/ROOT/images \
antora/spec/modules/ROOT/pages/appendices \
Expand All @@ -875,7 +878,6 @@ CLEAN_ANTORA_PATHS = \
antora/features/modules/features/pages/proposals \
antora/features/modules/features/partials \
antora/features/modules/features/images \
antora/features/modules/features/nav.adoc \
$(JSXREFMAP) \
$(PYXREFMAP) \
$(JSPAGEMAP)
Expand Down
3 changes: 2 additions & 1 deletion antora/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ spec_pages:
-component $(shell realpath antora/spec/modules/ROOT) \
-xrefpath antora/spec \
-pageHeaders antora/pageHeaders-spec.adoc \
-pagemappath $(JSPAGEMAP) \
-jspagemap $(JSPAGEMAP) \
-pypagemap $(PYPAGEMAP) \
./config/attribs.adoc \
./config/copyright-ccby.adoc \
./config/copyright-spec.adoc \
Expand Down
133 changes: 133 additions & 0 deletions scripts/add_validusage_pages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#!/usr/bin/env python3
#
# Copyright 2024 The Khronos Group Inc.
# SPDX-License-Identifier: Apache-2.0

"""add_validusage_pages.py - adds 'page' key content to validusage.json
based on the generated xrefMap and pageMap files.
Usage: add_validusage_pages.py -xrefmap path -pagemap path -validusage path
- -xrefmap is the path to xrefMap.py, an externally generated dictionary
containing a map of asciidoc anchors in the spec markup to anchors of the
pages (spec chapters and appendices) they appear in.
- -pagemap is the path to pageMap.py, an externally generated dictionary
containing a map of page anchors to paths in the Antora document source
tree they correspond to.
- -validusage is the path to a validusage.json file. The file is overwritten
by adding a path to the 'page' key for each VU statement, allowing
converting a Vulkan VUID into a link to the docs.vulkan.org page where
that VUID is defined.
NOTE: the validusage file is always overwritten, in a non-destructive
fashion since the 'page' key is otherwise unused. If you don't want this
behavior, make a copy before running this script and specify that copy.
"""

# For error and file-loading interfaces only
import argparse
import importlib
import json
import os
import re
import sys
from generator import enquote
from reflib import loadFile, logDiag, logWarn, logErr, setLogFile, getBranch
from pathlib import Path

titleAnchorPat = re.compile(r'^\[\[(?P<anchor>[^,]+).*\]\]$')
titlePat = re.compile(r'^[=#] (?P<title>[A-Z].*)')
subtitlePat = re.compile(r'^[=#]{2,} (?P<title>[A-Z].*)')

Pages = 'pages'
Partials = 'partials'
Images = 'images'

def undefquote(s, default='undefined'):
"""Quote a string, or return a default value if the string is None."""

if s is not None:
return enquote(s)
else:
return 'undefined'

if __name__ == '__main__':
parser = argparse.ArgumentParser()

parser.add_argument('-xrefmap', action='store', dest='xrefmap',
default=None, required=True,
help='Specify path to xrefMap.py containing map of anchors to chapter anchors')
parser.add_argument('-pagemap', action='store', dest='pagemap',
default=None, required=True,
help='Specify path to pageMap.py containing map of chapter anchors to Antora filenames')
parser.add_argument('-validusage', action='store',
default=None, required=True,
help='Specify path to validusage.json file to rewrite in place')

args = parser.parse_args()

# Import the xrefmap and pagemap
def importFileModule(file):
"""importFileModule - import file as a module and return that module"""

(path, file) = os.path.split(file)
(module, extension) = os.path.splitext(file)
sys.path.append(path)

return importlib.import_module(module)

try:
xrefMap = importFileModule(args.xrefmap).xrefMap
except:
print(f'WARNING: Cannot load {args.xrefmap} containing xrefMap dictionary', file=sys.stderr)
sys.exit(1)

try:
pageMap = importFileModule(args.pagemap).pageMap
except:
print(f'WARNING: Cannot load {args.pagemap} containing pageMap dictionary', file=sys.stderr)
sys.exit(1)

try:
fp = open(args.validusage, 'r', encoding='utf-8')
vufile = json.load(fp)
fp.close()
except:
print(f'WARNING: Cannot load {args.validusage} containing valid usage statements', file=sys.stderr)
sys.exit(1)

# Iterate over top-level dictionary of command names
vuidErrors = 0
pageErrors = 0
allvus = vufile['validation']
for (command, commandvus) in allvus.items():

# Iterate over dictionary of profile? names
for (profile, profilevus) in commandvus.items():

# Iterate over individual VUs, updating their 'page' information
for vu in profilevus:
vuid = vu['vuid']

if vuid in xrefMap:
pageAnchor = xrefMap[vuid][0]

if pageAnchor in pageMap:
vu['page'] = pageMap[pageAnchor]
else:
print(f'Cannot map page anchor {pageAnchor} for VU {vuid}')
pageErrors += 1
else:
print(f'Cannot map VUID {vuid}')
vuidErrors += 1

# Report errors but proceed with updating validusage.json, anyway
if vuidErrors > 0 or pageErrors > 0:
print(f'WARNING: {vuidErrors} unmapped VUIDs in {args.xrefmap}, {pageErrors} unmapped page anchors in {args.pagemap}')

try:
fp = open(args.validusage + '.new', 'w', encoding='utf-8')
json.dump(vufile, fp, ensure_ascii=False, indent=2)
except:
print(f'WARNING: Cannot write updated {args.validusage} containing valid usage statements', file=sys.stderr)
sys.exit(1)
93 changes: 66 additions & 27 deletions scripts/antora-prep.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,23 @@
format. Success is highly dependent on strict adherence to Vulkan spec
authoring conventions.
Usage: `antora-prep.py [-root path] -component path [-xrefpath path] [-pagemappath path] [-filelist file] files`
Usage: `antora-prep.py [options] files`
- `-root` is the root path (repository root, usually) relative to which spec
files are processed. Defaults to current directory if not specified.
files are processed.
Defaults to current directory if not specified.
- `-component` is the path to the module and component in which converted
files are written (e.g. the component directory under which pages/,
partials/, images/, etc. are located).
This option must be specified.
- `-pageHeaders` is the path to a file whose contents are injected after the
title of each converted page.
- `-xrefpath` is the path to xrefMap.py, an externally generated
dictionary containing a map of asciidoc anchors in the spec markup to
the pages (spec chapters and appendices) they appear in.
- `-pagemappath` is the path to generate a xrefMap.cjs file
corresponding to xrefMap.py, for use in the Antora build.
- `-jspagemap` and `-pypagemap` are paths to generate output page map source
(.cjs and .py) corresponding to the xrefMap, for use in the Antora build
and in augmenting validusage.json, respectively.
- `-filelist` is the path to a file containing a list of pathnames to
convert, one path/line.
- Remaining arguments are individual pathnames to convert.
Expand All @@ -27,12 +32,15 @@
Asciidoc markup files (.adoc) are scanned for the first title markup and
classified as partials or pages depending on whether it is a top-level title
or not. All .adoc files are rewritten to the component 'partials' directory, to
or not.
All .adoc files are rewritten to the component 'partials' directory, to
allow transclusion of pages to work (otherwise the transclusions would also
have to be rewritten).
pages then have additional markup injected immediately following the page
title to set custom attributes needed for the build. pages are then
Pages then have additional markup injected immediately following the page
title, to set custom attributes needed for the build.
Finally, pages are
symbolically linked from the component 'pages' directory to the actual
rewritten file in the 'partials' directory to follow Antora conventions.
"""
Expand All @@ -55,15 +63,51 @@
Partials = 'partials'
Images = 'images'

def undefquote(s):
"""Quote a string for JavaScript, or return the JavaScript undefined
value."""
def undefquote(s, default='undefined'):
"""Quote a string, or return a default value if the string is None."""

if s is not None:
return enquote(s)
else:
return 'undefined'

# Write the pageMap dictionary, which contains only string -> string
# mappings, to executable .cjs and .py files, respectively.
#

def write_cjs_dictionary(filename, dictionary, varname = 'exports.pageMap'):
try:
fp = open(filename, 'w', encoding='utf8')
except:
raise RuntimeError(f'Cannot open page map .cjs file {filename}')

def jsquote(s):
return undefquote(s, 'undefined')

print(f'{varname} = {{', file=fp)
for pageAnchor in sorted(dictionary):
pageName = dictionary[pageAnchor]
print(f' {jsquote(pageAnchor)} : {jsquote(pageName)},', file=fp)
print('}', file=fp)

fp.close()

def write_python_dictionary(filename, dictionary, varname = 'pageMap'):
try:
fp = open(filename, 'w', encoding='utf8')
except:
raise RuntimeError(f'Cannot open page map .py file {filename}')

def pyquote(s):
return undefquote(s, 'None')

print(f'{varname} = {{', file=fp)
for pageAnchor in sorted(dictionary):
pageName = dictionary[pageAnchor]
print(f' {pyquote(pageAnchor)} : {pyquote(pageName)},', file=fp)
print('}', file=fp)

fp.close()

# Track anchors we could not rewrite so they are only reported once
unresolvedAnchors = set()
Expand Down Expand Up @@ -490,9 +534,12 @@ def printFile(label, lines):
parser.add_argument('-xrefpath', action='store', dest='xrefpath',
default=None, required=False,
help='Specify path to xrefMap.py containing map of anchors to chapter anchors')
parser.add_argument('-pagemappath', action='store', dest='pagemappath',
parser.add_argument('-jspagemap', action='store', dest='jspagemap',
default=None, required=False,
help='Specify path to output page map as .cjs source containing map of anchors to chapter anchors')
parser.add_argument('-pypagemap', action='store', dest='pypagemap',
default=None, required=False,
help='Specify path to output pageMap.cjs containing map of anchors to chapter anchors')
help='Specify path to output page map as .py source containing map of chapter anchors to Antora filenames')
parser.add_argument('-filelist', action='store',
default=None, required=False,
help='Specify file containing a list of filenames to convert, one/line')
Expand Down Expand Up @@ -549,8 +596,9 @@ def printFile(label, lines):
# Save information about the file under its relpath
pageInfo[docFile.relpath] = docFile

# Save mapping from page anchor to its relpath
if docFile.titleAnchor is not None:
# Save mapping from page anchor to its relpath, if there is a page
# anchor.
if docFile.titleAnchor is not None and docFile.titleAnchor != '':
pageMap[docFile.titleAnchor] = docFile.relpath

# All files have been read and classified.
Expand All @@ -570,19 +618,10 @@ def printFile(label, lines):

# Write the pageMap to a .cjs file for use in the Antora build's
# specmacros extensions. The xrefMap is already written in JS form.
if args.pagemappath is not None:
try:
fp = open(args.pagemappath, 'w', encoding='utf8')
except:
raise RuntimeError(f'Cannot open output pageMap.cjs file {args.pagemappath}')

print('exports.pageMap = {', file=fp)
for pageAnchor in sorted(pageMap):
pageName = pageMap[pageAnchor]
print(f' {undefquote(pageAnchor)} : {undefquote(pageName)},', file=fp)
print('}', file=fp)

fp.close()
if args.jspagemap is not None:
write_cjs_dictionary(args.jspagemap, pageMap, 'exports.pageMap')
if args.pypagemap is not None:
write_python_dictionary(args.pypagemap, pageMap, 'pageMap')

## if not os.path.exists(args.xrefmap):
## raise UserWarning(f'Specified xrefmap {args.xrefmap} does not exist')
Expand Down

0 comments on commit b327877

Please sign in to comment.