1#!/usr/bin/python3 2# 3# Copyright (c) 2013-2014 The Khronos Group Inc. 4# 5# Permission is hereby granted, free of charge, to any person obtaining a 6# copy of this software and/or associated documentation files (the 7# "Materials"), to deal in the Materials without restriction, including 8# without limitation the rights to use, copy, modify, merge, publish, 9# distribute, sublicense, and/or sell copies of the Materials, and to 10# permit persons to whom the Materials are furnished to do so, subject to 11# the following conditions: 12# 13# The above copyright notice and this permission notice shall be included 14# in all copies or substantial portions of the Materials. 15# 16# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22# MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 23 24import io, os, re, string, sys; 25 26if __name__ == '__main__': 27 if (len(sys.argv) != 5): 28 print('Usage:', sys.argv[0], ' gendir srcdir accordfilename flatfilename', file=sys.stderr) 29 exit(1) 30 else: 31 gendir = sys.argv[1] 32 srcdir = sys.argv[2] 33 accordfilename = sys.argv[3] 34 flatfilename = sys.argv[4] 35 # print(' gendir = ', gendir, ' srcdir = ', srcdir, 'accordfilename = ', accordfilename, 'flatfilename = ', flatfilename) 36else: 37 print('Unknown invocation mode', file=sys.stderr) 38 exit(1) 39 40# Various levels of indentation in generated HTML 41ind1 = ' ' 42ind2 = ind1 + ind1 43ind3 = ind2 + ind1 44ind4 = ind2 + ind2 45 46# Symbolic names 47notAlias = False 48isAlias = True 49 50# Page title 51pageTitle = 'EGL Reference Pages' 52 53# Docbook source and generated HTML 5 file extensions 54srcext = '.xml' 55genext = '.xhtml' 56 57# List of generated files 58files = os.listdir(gendir) 59 60# Feature - class representing a command or function to be indexed, used 61# as dictionary values keyed by the feature name to be indexed. 62# 63# Members 64# file - name of file containing the feature 65# feature - feature name for the index (basis for the dictionary key). 66# alias - True if this is an alias of another feature in the file. 67# Usually if alias is False, feature is the basename of file. 68# apiCommand - True if this is an API command, or should be grouped 69# like one 70class Feature: 71 def __init__(self, 72 file = None, 73 feature = None, 74 alias = False, 75 apiCommand = None): 76 self.file = file 77 self.feature = feature 78 self.alias = alias 79 self.apiCommand = apiCommand 80 # This is the API-dependent command prefix 81 self.prefix = 'egl' 82 self.prefixLen = len(self.prefix) 83 def makeKey(self): 84 # Return dictionary / sort key based on the feature name 85 if (self.apiCommand and self.feature[0:self.prefixLen] 86 == self.prefix): 87 return self.feature[self.prefixLen:] 88 else: 89 return self.feature 90 91# Add dictionary entry for specified Feature. 92# The key used is the feature name, with the leading 'gl' stripped 93# off if this is an API command 94def addkey(dict, feature): 95 key = feature.makeKey() 96 if (key in dict.keys()): 97 print('Key', key, ' already exists in dictionary!') 98 else: 99 dict[key] = feature 100 101# Create list of entry point names to be indexed. 102# Unlike the old Perl script, this proceeds as follows: 103# - Each .xhtml page with a parent .xml page gets an 104# index entry for its base name. 105# - Additionally, each <function> tag inside a <funcdef> 106# in the parent page gets an aliased index entry. 107# - Each .xhtml page *without* a parent is reported but 108# not indexed. 109# - Each collision in index terms is reported. 110# - Index terms are keys in a dictionary whose entries 111# are [ pagename, alias, glPrefix ] where pagename is 112# the base name of the indexed page and alias is True 113# if this index isn't the same as pagename. 114# - API keys have their glPrefix value set to True, 115# GLSL keys to False. There is a simplistic way of 116# telling the files apart based on the file name: 117# 118# * Everything starting with 'egl[A-Z]' is API 119# * 'removedTypes.*' is API (more may be added) 120# * Everything else is GLSL 121 122def isAPIfile(entrypoint): 123 if (re.match('^egl[A-Z]', entrypoint) or entrypoint == 'removedTypes'): 124 return True 125 else: 126 return False 127 128# Dictionary of all keys mapped to Feature values 129refIndex = {} 130 131for file in files: 132 # print('Processing file', file) 133 (entrypoint,ext) = os.path.splitext(file) 134 if (ext == genext): 135 parent = srcdir + '/' + entrypoint + srcext 136 # Determine if this is an API or GLSL page 137 apiCommand = isAPIfile(entrypoint) 138 if (os.path.exists(parent)): 139 addkey(refIndex, Feature(file, entrypoint, False, apiCommand)) 140 # Search parent file for <function> tags inside <funcdef> tags 141 # This doesn't search for <varname> inside <fieldsynopsis>, because 142 # those aren't on the same line and it's hard. 143 fp = open(parent) 144 for line in fp.readlines(): 145 # Look for <function> tag contents and add as aliases 146 # Don't add the same key twice 147 for m in re.finditer(r"<funcdef>.*<function>(.*)</function>.*</funcdef>", line): 148 funcname = m.group(1) 149 if (funcname != entrypoint): 150 addkey(refIndex, Feature(file, funcname, True, apiCommand)) 151 fp.close() 152 else: 153 print('No parent page for', file, ', will not be indexed') 154 155# Some utility functions for generating the navigation table 156# Opencl_tofc.html uses style.css instead of style-index.css 157# flatMenu - if True, don't include accordion JavaScript, 158# generating a flat (expanded) menu. 159# letters - if not None, include per-letter links to within 160# the indices for each letter in the list. 161# altMenu - if not None, the name of the alternate index to 162# link to. 163def printHeader(fp, flatMenu = False, letters = None, altMenu = None): 164 if (flatMenu): 165 scriptInclude = ' <!-- Don\'t include accord.js -->' 166 else: 167 scriptInclude = ' <?php include \'accord.js\'; ?>' 168 169 print('<html>', 170 '<head>', 171 ' <link rel="stylesheet" type="text/css" href="style-index.css" />', 172 ' <title>' + pageTitle + '</title>', 173 scriptInclude, 174 '</head>', 175 '<body>', 176 sep='\n', file=fp) 177 178 if (altMenu): 179 if (flatMenu): 180 altLabel = '(accordion-style)' 181 else: 182 altLabel = '(flat)' 183 print(' <a href="' + altMenu + '">' + 184 'Use alternate ' + altLabel + ' index' + 185 '</a>', file=fp) 186 187 if (letters): 188 print(' <center>\n<div id="container">', file=fp) 189 for letter in letters: 190 print(' <b><a href="#' + 191 letter + 192 '" style="text-decoration:none">' + 193 letter + 194 '</a></b> ', file=fp) 195 print(' </div>\n</center>', file=fp) 196 197 print(' <div id="navwrap">', 198 ' <ul id="containerul"> <!-- Must wrap entire list for expand/contract -->', 199 ' <li class="Level1">', 200 ' <a href="start.html" target="pagedisplay">Introduction</a>', 201 ' </li>', 202 sep='\n', file=fp) 203 204def printFooter(fp, flatMenu = False): 205 print(' </div> <!-- End containerurl -->', file=fp) 206 if (not flatMenu): 207 print(' <script type="text/javascript">initiate();</script>', file=fp) 208 print('</body>', 209 '</html>', 210 sep='\n', file=fp) 211 212# Add a nav table entry. key = link name, feature = Feature info for key 213def addMenuLink(key, feature, fp): 214 file = feature.file 215 linkname = feature.feature 216 217 print(ind4 + '<li><a href="' + file + '" target="pagedisplay">' 218 + linkname + '</a></li>', 219 sep='\n', file=fp) 220 221# Begin index section for a letter, include an anchor to link to 222def beginLetterSection(letter, fp): 223 print(ind2 + '<a name="' + letter + '"></a>', 224 ind2 + '<li>' + letter, 225 ind3 + '<ul class="Level3">', 226 sep='\n', file=fp) 227 228# End index section for a letter 229def endLetterSection(opentable, fp): 230 if (opentable == 0): 231 return 232 print(ind3 + '</ul> <!-- End Level3 -->', 233 ind2 + '</li>', 234 sep='\n', file=fp) 235 236# Return the keys in a dictionary sorted by name. 237# Select only keys matching whichKeys (see genDict below) 238def sortedKeys(dict, whichKeys): 239 list = [] 240 for key in dict.keys(): 241 if (whichKeys == 'all' or 242 (whichKeys == 'api' and dict[key].apiCommand) or 243 (whichKeys == 'glsl' and not dict[key].apiCommand)): 244 list.append(key) 245 list.sort(key=str.lower) 246 return list 247 248# Generate accordion menu for this dictionary, titled as specified. 249# 250# If whichKeys is 'all', generate index for all features 251# If whichKeys is 'api', generate index only for API features 252# If whichKeys is 'glsl', generate index only for GLSL features 253# 254# fp is the file to write to 255def genDict(dict, title, whichKeys, fp): 256 print(ind1 + '<li class="Level1">' + title, 257 ind2 + '<ul class="Level2">', 258 sep='\n', file=fp) 259 260 # Print links for sorted keys in each letter section 261 curletter = '' 262 opentable = 0 263 264 # Determine which letters are in the table of contents for this 265 # dictionary. If apiPrefix is set, strip the API prefix from each 266 # key containing it first. 267 268 # Generatesorted list of page indexes. Select keys matching whichKeys. 269 keys = sortedKeys(dict, whichKeys) 270 271 # print('@ Sorted list of page indexes:\n', keys) 272 273 for key in keys: 274 # Character starting this key 275 c = str.lower(key[0]) 276 277 if (c != curletter): 278 endLetterSection(opentable, fp) 279 # Start a new subtable for this letter 280 beginLetterSection(c, fp) 281 opentable = 1 282 curletter = c 283 addMenuLink(key, dict[key], fp) 284 endLetterSection(opentable, fp) 285 286 print(ind2 + '</ul> <!-- End Level2 -->', 287 ind1 + '</li> <!-- End Level1 -->', 288 sep='\n', file=fp) 289 290###################################################################### 291 292# Generate the accordion menu 293fp = open(accordfilename, 'w') 294printHeader(fp, flatMenu = False, altMenu = flatfilename) 295 296genDict(refIndex, 'EGL Entry Points', 'all', fp) 297 298printFooter(fp, flatMenu = False) 299fp.close() 300 301###################################################################### 302 303# Generate the non-accordion menu, with combined API and GLSL sections 304fp = open(flatfilename, 'w') 305 306# Set containing all index letters 307indices = { key[0].lower() for key in refIndex.keys() } 308letters = [c for c in indices] 309letters.sort() 310 311printHeader(fp, flatMenu = True, letters = letters, altMenu = accordfilename) 312 313genDict(refIndex, 'EGL Entry Points', 'all', fp) 314 315printFooter(fp, flatMenu = True) 316fp.close() 317