summaryrefslogtreecommitdiff
path: root/bin/scons-doc.py
diff options
context:
space:
mode:
Diffstat (limited to 'bin/scons-doc.py')
-rw-r--r--bin/scons-doc.py280
1 files changed, 53 insertions, 227 deletions
diff --git a/bin/scons-doc.py b/bin/scons-doc.py
index ff06c043..95da5f30 100644
--- a/bin/scons-doc.py
+++ b/bin/scons-doc.py
@@ -22,31 +22,11 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
-# scons-doc.py - an SGML preprocessor for capturing SCons output
-# and inserting it into examples in our DocBook
-# documentation
#
-# Synopsis:
-#
-# scons-doc [OPTIONS] [.in files]
-#
-# When no input files are given, the folder doc/user/* is searched for .in files.
-#
-# Available options:
-#
-# -d, --diff create examples for the .in file and output a unified
-# diff against the related .xml file
-# -r, --run create examples for the .in file, but do not change
-# any files
-# -s, --simple_diff use a simpler output for the diff mode (no unified
-# diff!)
-# -u, --update create examples for the .in file and update the
-# related .xml file
-#
-# This script looks for some SGML tags that describe SCons example
+# This script looks for some XML tags that describe SCons example
# configurations and commands to execute in those configurations, and
# uses TestCmd.py to execute the commands and insert the output from
-# those commands into the SGML that we output. This way, we can run a
+# those commands into the XML that we output. This way, we can run a
# script and update all of our example documentation output without
# a lot of laborious by-hand checking.
#
@@ -89,8 +69,8 @@
# SCons output is generated from the following sort of tag:
#
# <scons_output example="ex1" os="posix">
-# <scons_output_command>scons -Q foo</scons_output_command>
-# <scons_output_command>scons -Q foo</scons_output_command>
+# <scons_output_command suffix="1">scons -Q foo</scons_output_command>
+# <scons_output_command suffix="2">scons -Q foo</scons_output_command>
# </scons_output>
#
# You tell it which example to use with the "example" attribute, and then
@@ -98,7 +78,7 @@
# supply an "os" tag, which specifies the type of operating system this
# example is intended to show; if you omit this, default value is "posix".
#
-# The generated SGML will show the command line (with the appropriate
+# The generated XML will show the command line (with the appropriate
# command-line prompt for the operating system), execute the command in
# a temporary directory with the example files, capture the standard
# output from SCons, and insert it into the text as appropriate.
@@ -106,13 +86,10 @@
# can see if there are any problems executing the command.
#
-import optparse
import os
import re
-import sgmllib
import sys
import time
-import glob
sys.path.append(os.path.join(os.getcwd(), 'QMTest'))
sys.path.append(os.path.join(os.getcwd(), 'build', 'QMTest'))
@@ -129,56 +106,6 @@ os.environ['SCONS_LIB_DIR'] = scons_lib_dir
import TestCmd
-# The regular expression that identifies entity references in the
-# standard sgmllib omits the underscore from the legal characters.
-# Override it with our own regular expression that adds underscore.
-sgmllib.entityref = re.compile('&([a-zA-Z][-_.a-zA-Z0-9]*)[^-_a-zA-Z0-9]')
-
-# Classes for collecting different types of data we're interested in.
-class DataCollector(object):
- """Generic class for collecting data between a start tag and end
- tag. We subclass for various types of tags we care about."""
- def __init__(self):
- self.data = ""
- def afunc(self, data):
- self.data = self.data + data
-
-class Example(DataCollector):
- """An SCons example. This is essentially a list of files that
- will get written to a temporary directory to collect output
- from one or more SCons runs."""
- def __init__(self):
- DataCollector.__init__(self)
- self.files = []
- self.dirs = []
-
-class File(DataCollector):
- """A file, that will get written out to a temporary directory
- for one or more SCons runs."""
- def __init__(self, name):
- DataCollector.__init__(self)
- self.name = name
-
-class Directory(DataCollector):
- """A directory, that will get created in a temporary directory
- for one or more SCons runs."""
- def __init__(self, name):
- DataCollector.__init__(self)
- self.name = name
-
-class Output(DataCollector):
- """Where the command output goes. This is essentially
- a list of commands that will get executed."""
- def __init__(self):
- DataCollector.__init__(self)
- self.commandlist = []
-
-class Command(DataCollector):
- """A tag for where the command output goes. This is essentially
- a list of commands that will get executed."""
- def __init__(self):
- DataCollector.__init__(self)
- self.output = None
Prompt = {
'posix' : '% ',
@@ -517,115 +444,51 @@ def ExecuteCommand(args, c, t, dict):
func = lambda args, c, t, dict: []
return func(args[1:], c, t, dict)
-class MySGML(sgmllib.SGMLParser):
- """A subclass of the standard Python sgmllib SGML parser.
-
- This extends the standard sgmllib parser to recognize, and do cool
- stuff with, the added tags that describe our SCons examples,
- commands, and other stuff.
- """
- def __init__(self, outfp):
- sgmllib.SGMLParser.__init__(self)
- self.examples = {}
- self.afunclist = []
- self.outfp = outfp
-
- # The first set of methods here essentially implement pass-through
- # handling of most of the stuff in an SGML file. We're really
- # only concerned with the tags specific to SCons example processing,
- # the methods for which get defined below.
-
- def handle_data(self, data):
- try:
- f = self.afunclist[-1]
- except IndexError:
- self.outfp.write(data)
- else:
- f(data)
- def handle_comment(self, data):
- self.outfp.write('<!--' + data + '-->')
+def for_display(contents):
+ contents = contents.replace('__ROOT__', '')
+ contents = contents.replace('<', '&lt;')
+ contents = contents.replace('>', '&gt;')
+ return contents
- def handle_decl(self, data):
- self.outfp.write('<!' + data + '>')
- def unknown_starttag(self, tag, attrs):
- try:
- f = self.example.afunc
- except AttributeError:
- f = self.outfp.write
- if not attrs:
- f('<' + tag + '>')
- else:
- f('<' + tag)
- for name, value in attrs:
- f(' ' + name + '=' + '"' + value + '"')
- f('>')
-
- def unknown_endtag(self, tag):
- self.outfp.write('</' + tag + '>')
-
- def unknown_entityref(self, ref):
- self.outfp.write('&' + ref + ';')
-
- def unknown_charref(self, ref):
- self.outfp.write('&#' + ref + ';')
-
- # Here is where the heavy lifting begins. The following methods
- # handle the begin-end tags of our SCons examples.
-
- def for_display(self, contents):
- contents = contents.replace('__ROOT__', '')
- contents = contents.replace('<', '&lt;')
- contents = contents.replace('>', '&gt;')
- return contents
-
-
- def start_scons_output(self, attrs):
- t = [t for t in attrs if t[0] == 'example']
- if not t:
- self.error("no <scons_output> example attribute found")
- exname = t[0][1]
- try:
- e = self.examples[exname]
- except KeyError:
- self.error("unknown example name '%s'" % exname)
- # Default values for an example.
- o = Output()
- o.preserve = None
- o.os = 'posix'
- o.tools = ''
- o.e = e
- # Locally-set.
- for name, value in attrs:
- setattr(o, name, value)
- self.o = o
- self.afunclist.append(o.afunc)
-
- def end_scons_output(self):
- # The real raison d'etre for this script, this is where we
- # actually execute SCons to fetch the output.
- o = self.o
- e = o.e
+def create_scons_output(e):
+ # The real raison d'etre for this script, this is where we
+ # actually execute SCons to fetch the output.
+
+ # Loop over all outputs for the example
+ for o in e.outputs:
+ # Create new test directory
t = TestCmd.TestCmd(workdir='', combine=1)
if o.preserve:
t.preserve()
t.subdir('ROOT', 'WORK')
t.rootpath = t.workpath('ROOT').replace('\\', '\\\\')
-
+
for d in e.dirs:
dir = t.workpath('WORK', d.name)
if not os.path.exists(dir):
os.makedirs(dir)
-
+
for f in e.files:
+ if f.isFileRef():
+ continue
+ #
+ # Left-align file's contents, starting on the first
+ # non-empty line
+ #
+ data = f.content.split('\n')
i = 0
- while f.data[i] == '\n':
+ # Skip empty lines
+ while data[i] == '':
i = i + 1
- lines = f.data[i:].split('\n')
+ lines = data[i:]
i = 0
+ # Scan first line for the number of spaces
+ # that this block is indented
while lines[0][i] == ' ':
i = i + 1
+ # Left-align block
lines = [l[i:] for l in lines]
path = f.name.replace('__ROOT__', t.rootpath)
if not os.path.isabs(path):
@@ -639,89 +502,52 @@ class MySGML(sgmllib.SGMLParser):
t.write(path, content)
if hasattr(f, 'chmod'):
os.chmod(path, int(f.chmod, 0))
-
- i = len(o.prefix)
- while o.prefix[i-1] != '\n':
- i = i - 1
-
- self.outfp.write('<screen>' + o.prefix[:i])
- p = o.prefix[i:]
-
+
# Regular expressions for making the doc output consistent,
# regardless of reported addresses or Python version.
-
+
# Massage addresses in object repr strings to a constant.
address_re = re.compile(r' at 0x[0-9a-fA-F]*\>')
-
+
# Massage file names in stack traces (sometimes reported as absolute
# paths) to a consistent relative path.
engine_re = re.compile(r' File ".*/src/engine/SCons/')
-
+
# Python 2.5 changed the stack trace when the module is read
# from standard input from read "... line 7, in ?" to
# "... line 7, in <module>".
file_re = re.compile(r'^( *File ".*", line \d+, in) \?$', re.M)
-
+
# Python 2.6 made UserList a new-style class, which changes the
# AttributeError message generated by our NodeList subclass.
nodelist_re = re.compile(r'(AttributeError:) NodeList instance (has no attribute \S+)')
-
+
for c in o.commandlist:
- self.outfp.write(p + Prompt[o.os])
- d = c.data.replace('__ROOT__', '')
- self.outfp.write('<userinput>' + d + '</userinput>\n')
-
- e = c.data.replace('__ROOT__', t.workpath('ROOT'))
- args = e.split()
+ # Open new output file
+ fpath = os.path.join(SConsExamples.generated_examples,
+ e.name+'_'+c.suffix+'.out','w')
+ outfp = open(fpath)
+ outfp.write(Prompt[o.os])
+ d = c.cmd.replace('__ROOT__', '')
+ outfp.write('<userinput>' + d + '</userinput>\n')
+
+ cmd_work = c.cmd.replace('__ROOT__', t.workpath('ROOT'))
+ args = cmd_work.split()
lines = ExecuteCommand(args, c, t, {'osname':o.os, 'tools':o.tools})
content = None
if c.output:
content = c.output
elif lines:
- content = ( '\n' + p).join(lines)
+ content = '\n'.join(lines)
if content:
content = address_re.sub(r' at 0x700000&gt;', content)
content = engine_re.sub(r' File "bootstrap/src/engine/SCons/', content)
content = file_re.sub(r'\1 <module>', content)
content = nodelist_re.sub(r"\1 'NodeList' object \2", content)
- content = self.for_display(content)
- self.outfp.write(p + content + '\n')
-
- if o.data[0] == '\n':
- o.data = o.data[1:]
- self.outfp.write(o.data + '</screen>')
- delattr(self, 'o')
- self.afunclist = self.afunclist[:-1]
-
- def start_scons_output_command(self, attrs):
- try:
- o = self.o
- except AttributeError:
- self.error("<scons_output_command> tag outside of <scons_output>")
- try:
- o.prefix
- except AttributeError:
- o.prefix = o.data
- o.data = ""
- c = Command()
- for name, value in attrs:
- setattr(c, name, value)
- o.commandlist.append(c)
- self.afunclist.append(c.afunc)
-
- def end_scons_output_command(self):
- self.o.data = ""
- self.afunclist = self.afunclist[:-1]
-
-
-
-def main():
- argv = sys.argv
-
-
-if __name__ == "__main__":
- sys.exit(main())
-
+ content = for_display(content)
+ outfp.write(content + '\n')
+ outfp.close()
+
# Local Variables:
# tab-width:4
# indent-tabs-mode:nil