#!/usr/bin/env python # # Process a list of Python and/or XML files containing SCons documentation. # # This script creates formatted lists of the Builders, functions, Tools # or construction variables documented in the specified XML files. # # Dependening on the options, the lists are output in either # DocBook-formatted generated XML files containing the summary text # and/or .mod files contining the ENTITY definitions for each item, # or in man-page-formatted output. # import os import sys import getopt import re import xml.sax try: from io import StringIO except ImportError: # No 'io' module or no StringIO in io exec('from cStringIO import StringIO') import SConsDoc base_sys_path = [os.getcwd() + '/build/test-tar-gz/lib/scons'] + sys.path helpstr = """\ Usage: scons-proc.py [--man|--xml] [-b file(s)] [-f file(s)] [-t file(s)] [-v file(s)] [infile ...] Options: -b file(s) dump builder information to the specified file(s) -f file(s) dump function information to the specified file(s) -t file(s) dump tool information to the specified file(s) -v file(s) dump variable information to the specified file(s) --man print info in man page format, each -[btv] argument is a single file name --xml (default) print info in SML format, each -[btv] argument is a pair of comma-separated .gen,.mod file names """ opts, args = getopt.getopt(sys.argv[1:], "b:f:ht:v:", ['builders=', 'help', 'man', 'xml', 'tools=', 'variables=']) buildersfiles = None functionsfiles = None output_type = '--xml' toolsfiles = None variablesfiles = None for o, a in opts: if o in ['-b', '--builders']: buildersfiles = a elif o in ['-f', '--functions']: functionsfiles = a elif o in ['-h', '--help']: sys.stdout.write(helpstr) sys.exit(0) elif o in ['--man', '--xml']: output_type = o elif o in ['-t', '--tools']: toolsfiles = a elif o in ['-v', '--variables']: variablesfiles = a h = SConsDoc.SConsDocHandler() saxparser = xml.sax.make_parser() saxparser.setContentHandler(h) saxparser.setErrorHandler(h) xml_preamble = """\ """ xml_postamble = """\ """ for f in args: _, ext = os.path.splitext(f) if ext == '.py': dir, _ = os.path.split(f) if dir: sys.path = [dir] + base_sys_path module = SConsDoc.importfile(f) h.set_file_info(f, len(xml_preamble.split('\n'))) try: content = module.__scons_doc__ except AttributeError: content = None else: del module.__scons_doc__ else: h.set_file_info(f, len(xml_preamble.split('\n'))) content = open(f).read() if content: content = content.replace('&', '&') # Strip newlines after comments so they don't turn into # spurious paragraph separators. content = content.replace('-->\n', '-->') input = xml_preamble + content + xml_postamble try: saxparser.parse(StringIO(input)) except: sys.stderr.write("error in %s\n" % f) raise Warning = """\ """ Regular_Entities_Header = """\ """ Link_Entities_Header = """\ """ class SCons_XML(object): def __init__(self, entries, **kw): self.values = entries for k, v in kw.items(): setattr(self, k, v) def fopen(self, name): if name == '-': return sys.stdout return open(name, 'w') class SCons_XML_to_XML(SCons_XML): def write(self, files): gen, mod = files.split(',') g.write_gen(gen) g.write_mod(mod) def write_gen(self, filename): if not filename: return f = self.fopen(filename) for v in self.values: f.write('\n\n' % (v.prefix, v.idfunc())) for term in v.termfunc(): f.write('<%s>%s\n' % (v.tag, term, v.tag)) f.write('\n') for chunk in v.summary.body: f.write(str(chunk)) if v.sets: s = ['&cv-link-%s;' % x for x in v.sets] f.write('\n') f.write('Sets: ' + ', '.join(s) + '.\n') f.write('\n') if v.uses: u = ['&cv-link-%s;' % x for x in v.uses] f.write('\n') f.write('Uses: ' + ', '.join(u) + '.\n') f.write('\n') f.write('\n') f.write('\n') def write_mod(self, filename): description = self.values[0].description if not filename: return f = self.fopen(filename) f.write(Warning) f.write('\n') f.write(Regular_Entities_Header % description) f.write('\n') for v in self.values: f.write('%s">\n' % (v.prefix, v.idfunc(), v.tag, v.entityfunc(), v.tag)) if self.env_signatures: f.write('\n') for v in self.values: f.write('env.%s">\n' % (v.prefix, v.idfunc(), v.tag, v.entityfunc(), v.tag)) f.write('\n') f.write(Warning) f.write('\n') f.write(Link_Entities_Header % description) f.write('\n') for v in self.values: f.write('<%s>%s\'>\n' % (v.prefix, v.idfunc(), v.prefix, v.idfunc(), v.tag, v.entityfunc(), v.tag)) if self.env_signatures: f.write('\n') for v in self.values: f.write('<%s>env.%s\'>\n' % (v.prefix, v.idfunc(), v.prefix, v.idfunc(), v.tag, v.entityfunc(), v.tag)) f.write('\n') f.write(Warning) class SCons_XML_to_man(SCons_XML): def write(self, filename): if not filename: return f = self.fopen(filename) chunks = [] for v in self.values: chunks.extend(v.mansep()) chunks.extend(v.initial_chunks()) chunks.extend(list(map(str, v.summary.body))) body = ''.join(chunks) body = body.replace('', '.ES') body = body.replace('', '.EE') body = body.replace('\n\n\n', '\n\n') body = body.replace('\n', '') body = body.replace('', '\n') body = body.replace('\n', '') body = body.replace('\n', '.RS 10\n') # Handling needs to be rationalized and made # consistent. Right now, the values map to arbitrary, # ad-hoc idioms in the current man page. body = re.compile(r'\n([^<]*)\n\n').sub(r'.TP 6\n.B \1\n', body) body = re.compile(r'\n([^<]*)\n\n').sub(r'.IP \1\n', body) body = re.compile(r'\n([^<]*)\n\n').sub(r'.HP 6\n.B \1\n', body) body = body.replace('\n', '') body = body.replace('\n', '') body = body.replace('\n', '.RE\n') body = re.sub(r'\.EE\n\n+(?!\.IP)', '.EE\n.IP\n', body) body = body.replace('\n.IP\n\'\\"', '\n\n\'\\"') body = re.sub('&(scons|SConstruct|SConscript|jar|Make|lambda);', r'\\fB\1\\fP', body) body = re.sub('&(TARGET|TARGETS|SOURCE|SOURCES);', r'\\fB$\1\\fP', body) body = body.replace('&Dir;', r'\fBDir\fP') body = body.replace('⌖', r'\fItarget\fP') body = body.replace('&source;', r'\fIsource\fP') body = re.sub('&b(-link)?-([^;]*);', r'\\fB\2\\fP()', body) body = re.sub('&cv(-link)?-([^;]*);', r'$\2', body) body = re.sub('&f(-link)?-env-([^;]*);', r'\\fBenv.\2\\fP()', body) body = re.sub('&f(-link)?-([^;]*);', r'\\fB\2\\fP()', body) body = re.sub(r'<(application|command|envar|filename|function|literal|option)>([^<]*)', r'\\fB\2\\fP', body) body = re.sub(r'<(classname|emphasis|varname)>([^<]*)', r'\\fI\2\\fP', body) body = re.compile(r'^\\f([BI])([^\\]* [^\\]*)\\fP\s*$', re.M).sub(r'.\1 "\2"', body) body = re.compile(r'^\\f([BI])(.*)\\fP\s*$', re.M).sub(r'.\1 \2', body) body = re.compile(r'^\\f([BI])(.*)\\fP(\S+)$', re.M).sub(r'.\1R \2 \3', body) body = re.compile(r'^(\S+)\\f([BI])(.*)\\fP$', re.M).sub(r'.R\2 \1 \3', body) body = re.compile(r'^(\S+)\\f([BI])(.*)\\fP([^\s\\]+)$', re.M).sub(r'.R\2 \1 \3 \4', body) body = re.compile(r'^(\.R[BI].*[\S])\s+$;', re.M).sub(r'\1', body) body = body.replace('<', '<') body = body.replace('>', '>') body = re.sub(r'\\([^f])', r'\\\\\1', body) body = re.compile("^'\\\\\\\\", re.M).sub("'\\\\", body) body = re.compile(r'^\.([BI]R?) --', re.M).sub(r'.\1 \-\-', body) body = re.compile(r'^\.([BI]R?) -', re.M).sub(r'.\1 \-', body) body = re.compile(r'^\.([BI]R?) (\S+)\\\\(\S+)$', re.M).sub(r'.\1 "\2\\\\\\\\\2"', body) body = re.compile(r'\\f([BI])-', re.M).sub(r'\\f\1\-', body) f.write(body) class Proxy(object): def __init__(self, subject): """Wrap an object as a Proxy object""" self.__subject = subject def __getattr__(self, name): """Retrieve an attribute from the wrapped object. If the named attribute doesn't exist, AttributeError is raised""" return getattr(self.__subject, name) def get(self): """Retrieve the entire wrapped object""" return self.__subject def __cmp__(self, other): if issubclass(other.__class__, self.__subject.__class__): return cmp(self.__subject, other) return cmp(self.__dict__, other.__dict__) class Builder(Proxy): description = 'builder' prefix = 'b-' tag = 'function' def idfunc(self): return self.name def termfunc(self): return ['%s()' % self.name, 'env.%s()' % self.name] def entityfunc(self): return self.name def mansep(self): return ['\n', "'\\" + '"'*69 + '\n'] def initial_chunks(self): return [ '.IP %s\n' % t for t in self.termfunc() ] class Function(Proxy): description = 'function' prefix = 'f-' tag = 'function' def idfunc(self): return self.name def termfunc(self): return ['%s()' % self.name, 'env.%s()' % self.name] def entityfunc(self): return self.name def mansep(self): return ['\n', "'\\" + '"'*69 + '\n'] def initial_chunks(self): try: arguments = self.arguments except AttributeError: arguments = ['()'] result = [] for arg in arguments: try: signature = arg.signature except AttributeError: signature = "both" if signature in ('both', 'global'): result.append('.TP\n.RI %s%s\n' % (self.name, arg)) if signature in ('both', 'env'): result.append('.TP\n.IR env .%s%s\n' % (self.name, arg)) return result class Tool(Proxy): description = 'tool' prefix = 't-' tag = 'literal' def idfunc(self): return self.name.replace('+', 'X') def termfunc(self): return [self.name] def entityfunc(self): return self.name def mansep(self): return ['\n'] def initial_chunks(self): return ['.IP %s\n' % self.name] class Variable(Proxy): description = 'construction variable' prefix = 'cv-' tag = 'envar' def idfunc(self): return self.name def termfunc(self): return [self.name] def entityfunc(self): return '$' + self.name def mansep(self): return ['\n'] def initial_chunks(self): return ['.IP %s\n' % self.name] if output_type == '--man': processor_class = SCons_XML_to_man elif output_type == '--xml': processor_class = SCons_XML_to_XML else: sys.stderr.write("Unknown output type '%s'\n" % output_type) sys.exit(1) if buildersfiles: g = processor_class([ Builder(b) for b in sorted(h.builders.values()) ], env_signatures=True) g.write(buildersfiles) if functionsfiles: g = processor_class([ Function(b) for b in sorted(h.functions.values()) ], env_signatures=True) g.write(functionsfiles) if toolsfiles: g = processor_class([ Tool(t) for t in sorted(h.tools.values()) ], env_signatures=False) g.write(toolsfiles) if variablesfiles: g = processor_class([ Variable(v) for v in sorted(h.cvars.values()) ], env_signatures=False) g.write(variablesfiles) # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: