summaryrefslogtreecommitdiff
path: root/bin/SConsExamples.py
diff options
context:
space:
mode:
Diffstat (limited to 'bin/SConsExamples.py')
-rw-r--r--bin/SConsExamples.py898
1 files changed, 898 insertions, 0 deletions
diff --git a/bin/SConsExamples.py b/bin/SConsExamples.py
new file mode 100644
index 0000000..9823a05
--- /dev/null
+++ b/bin/SConsExamples.py
@@ -0,0 +1,898 @@
+# !/usr/bin/env python
+#
+# Copyright (c) 2010 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#
+#
+# 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 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.
+#
+# An "SCons example" looks like this, and essentially describes a set of
+# input files (program source files as well as SConscript files):
+#
+# <scons_example name="ex1">
+# <file name="SConstruct" printme="1">
+# env = Environment()
+# env.Program('foo')
+# </file>
+# <file name="foo.c">
+# int main() { printf("foo.c\n"); }
+# </file>
+# </scons_example>
+#
+# The <file> contents within the <scons_example> tag will get written
+# into a temporary directory whenever example output needs to be
+# generated. By default, the <file> contents are not inserted into text
+# directly, unless you set the "printme" attribute on one or more files,
+# in which case they will get inserted within a <programlisting> tag.
+# This makes it easy to define the example at the appropriate
+# point in the text where you intend to show the SConstruct file.
+#
+# Note that you should usually give the <scons_example> a "name"
+# attribute so that you can refer to the example configuration later to
+# run SCons and generate output.
+#
+# If you just want to show a file's contents without worry about running
+# SCons, there's a shorter <sconstruct> tag:
+#
+# <sconstruct>
+# env = Environment()
+# env.Program('foo')
+# </sconstruct>
+#
+# This is essentially equivalent to <scons_example><file printme="1">,
+# but it's more straightforward.
+#
+# SCons output is generated from the following sort of tag:
+#
+# <scons_output example="ex1" os="posix">
+# <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
+# give it a list of <scons_output_command> tags to execute. You can also
+# 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 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.
+# Error output gets passed through to your error output so you
+# can see if there are any problems executing the command.
+#
+
+import os
+import re
+import sys
+import time
+
+import SConsDoc
+from SConsDoc import tf as stf
+
+#
+# The available types for ExampleFile entries
+#
+FT_FILE = 0 # a physical file (=<file>)
+FT_FILEREF = 1 # a reference (=<scons_example_file>)
+
+class ExampleFile:
+ def __init__(self, type_=FT_FILE):
+ self.type = type_
+ self.name = ''
+ self.content = ''
+ self.chmod = ''
+
+ def isFileRef(self):
+ return self.type == FT_FILEREF
+
+class ExampleFolder:
+ def __init__(self):
+ self.name = ''
+ self.chmod = ''
+
+class ExampleCommand:
+ def __init__(self):
+ self.edit = None
+ self.environment = ''
+ self.output = ''
+ self.cmd = ''
+
+class ExampleOutput:
+ def __init__(self):
+ self.name = ''
+ self.tools = ''
+ self.os = 'posix'
+ self.preserve = None
+ self.suffix = ''
+ self.commands = []
+
+class ExampleInfo:
+ def __init__(self):
+ self.name = ''
+ self.files = []
+ self.folders = []
+ self.outputs = []
+
+ def getFileContents(self, fname):
+ for f in self.files:
+ if fname == f.name and not f.isFileRef():
+ return f.content
+
+ return ''
+
+def readExampleInfos(fpath, examples):
+ """ Add the example infos for the file fpath to the
+ global dictionary examples.
+ """
+
+ # Create doctree
+ t = SConsDoc.SConsDocTree()
+ t.parseXmlFile(fpath)
+
+ # Parse scons_examples
+ for e in stf.findAll(t.root, "scons_example", SConsDoc.dbxid,
+ t.xpath_context, t.nsmap):
+ n = ''
+ if stf.hasAttribute(e, 'name'):
+ n = stf.getAttribute(e, 'name')
+ if n and n not in examples:
+ i = ExampleInfo()
+ i.name = n
+ examples[n] = i
+
+ # Parse file and directory entries
+ for f in stf.findAll(e, "file", SConsDoc.dbxid,
+ t.xpath_context, t.nsmap):
+ fi = ExampleFile()
+ if stf.hasAttribute(f, 'name'):
+ fi.name = stf.getAttribute(f, 'name')
+ if stf.hasAttribute(f, 'chmod'):
+ fi.chmod = stf.getAttribute(f, 'chmod')
+ fi.content = stf.getText(f)
+ examples[n].files.append(fi)
+ for d in stf.findAll(e, "directory", SConsDoc.dbxid,
+ t.xpath_context, t.nsmap):
+ di = ExampleFolder()
+ if stf.hasAttribute(d, 'name'):
+ di.name = stf.getAttribute(d, 'name')
+ if stf.hasAttribute(d, 'chmod'):
+ di.chmod = stf.getAttribute(d, 'chmod')
+ examples[n].folders.append(di)
+
+
+ # Parse scons_example_files
+ for f in stf.findAll(t.root, "scons_example_file", SConsDoc.dbxid,
+ t.xpath_context, t.nsmap):
+ if stf.hasAttribute(f, 'example'):
+ e = stf.getAttribute(f, 'example')
+ else:
+ continue
+ fi = ExampleFile(FT_FILEREF)
+ if stf.hasAttribute(f, 'name'):
+ fi.name = stf.getAttribute(f, 'name')
+ if stf.hasAttribute(f, 'chmod'):
+ fi.chmod = stf.getAttribute(f, 'chmod')
+ fi.content = stf.getText(f)
+ examples[e].files.append(fi)
+
+
+ # Parse scons_output
+ for o in stf.findAll(t.root, "scons_output", SConsDoc.dbxid,
+ t.xpath_context, t.nsmap):
+ if stf.hasAttribute(o, 'example'):
+ n = stf.getAttribute(o, 'example')
+ else:
+ continue
+
+ eout = ExampleOutput()
+ if stf.hasAttribute(o, 'name'):
+ eout.name = stf.getAttribute(o, 'name')
+ if stf.hasAttribute(o, 'tools'):
+ eout.tools = stf.getAttribute(o, 'tools')
+ if stf.hasAttribute(o, 'os'):
+ eout.os = stf.getAttribute(o, 'os')
+ if stf.hasAttribute(o, 'suffix'):
+ eout.suffix = stf.getAttribute(o, 'suffix')
+
+ for c in stf.findAll(o, "scons_output_command", SConsDoc.dbxid,
+ t.xpath_context, t.nsmap):
+ oc = ExampleCommand()
+ if stf.hasAttribute(c, 'edit'):
+ oc.edit = stf.getAttribute(c, 'edit')
+ if stf.hasAttribute(c, 'environment'):
+ oc.environment = stf.getAttribute(c, 'environment')
+ if stf.hasAttribute(c, 'output'):
+ oc.output = stf.getAttribute(c, 'output')
+ if stf.hasAttribute(c, 'cmd'):
+ oc.cmd = stf.getAttribute(c, 'cmd')
+ else:
+ oc.cmd = stf.getText(c)
+
+ eout.commands.append(oc)
+
+ examples[n].outputs.append(eout)
+
+def readAllExampleInfos(dpath):
+ """ Scan for XML files in the given directory and
+ collect together all relevant infos (files/folders,
+ output commands) in a map, which gets returned.
+ """
+ examples = {}
+ for path, dirs, files in os.walk(dpath):
+ for f in files:
+ if f.endswith('.xml'):
+ fpath = os.path.join(path, f)
+ if SConsDoc.isSConsXml(fpath):
+ readExampleInfos(fpath, examples)
+
+ return examples
+
+generated_examples = os.path.join('doc', 'generated', 'examples')
+
+def ensureExampleOutputsExist(dpath):
+ """ Scan for XML files in the given directory and
+ ensure that for every example output we have a
+ corresponding output file in the 'generated/examples'
+ folder.
+ """
+ # Ensure that the output folder exists
+ if not os.path.isdir(generated_examples):
+ os.mkdir(generated_examples)
+
+ examples = readAllExampleInfos(dpath)
+ for key, value in examples.iteritems():
+ # Process all scons_output tags
+ for o in value.outputs:
+ cpath = os.path.join(generated_examples,
+ key + '_' + o.suffix + '.xml')
+ if not os.path.isfile(cpath):
+ # Start new XML file
+ s = stf.newXmlTree("screen")
+ stf.setText(s, "NO OUTPUT YET! Run the script to generate/update all examples.")
+ # Write file
+ stf.writeTree(s, cpath)
+
+ # Process all scons_example_file tags
+ for r in value.files:
+ if r.isFileRef():
+ # Get file's content
+ content = value.getFileContents(r.name)
+ fpath = os.path.join(generated_examples,
+ key + '_' + r.name.replace("/", "_"))
+ # Write file
+ f = open(fpath, 'w')
+ f.write("%s\n" % content)
+ f.close()
+
+perc = "%"
+
+def createAllExampleOutputs(dpath):
+ """ Scan for XML files in the given directory and
+ creates all output files for every example in
+ the 'generated/examples' folder.
+ """
+ # Ensure that the output folder exists
+ if not os.path.isdir(generated_examples):
+ os.mkdir(generated_examples)
+
+ examples = readAllExampleInfos(dpath)
+ total = len(examples)
+ idx = 0
+ for key, value in examples.iteritems():
+ # Process all scons_output tags
+ print "%.2f%s (%d/%d) %s" % (float(idx + 1) * 100.0 / float(total),
+ perc, idx + 1, total, key)
+
+ create_scons_output(value)
+ # Process all scons_example_file tags
+ for r in value.files:
+ if r.isFileRef():
+ # Get file's content
+ content = value.getFileContents(r.name)
+ fpath = os.path.join(generated_examples,
+ key + '_' + r.name.replace("/", "_"))
+ # Write file
+ f = open(fpath, 'w')
+ f.write("%s\n" % content)
+ f.close()
+ idx += 1
+
+def collectSConsExampleNames(fpath):
+ """ Return a set() of example names, used in the given file fpath.
+ """
+ names = set()
+ suffixes = {}
+ failed_suffixes = False
+
+ # Create doctree
+ t = SConsDoc.SConsDocTree()
+ t.parseXmlFile(fpath)
+
+ # Parse it
+ for e in stf.findAll(t.root, "scons_example", SConsDoc.dbxid,
+ t.xpath_context, t.nsmap):
+ n = ''
+ if stf.hasAttribute(e, 'name'):
+ n = stf.getAttribute(e, 'name')
+ if n:
+ names.add(n)
+ if n not in suffixes:
+ suffixes[n] = []
+ else:
+ print "Error: Example in file '%s' is missing a name!" % fpath
+ failed_suffixes = True
+
+ for o in stf.findAll(t.root, "scons_output", SConsDoc.dbxid,
+ t.xpath_context, t.nsmap):
+ n = ''
+ if stf.hasAttribute(o, 'example'):
+ n = stf.getAttribute(o, 'example')
+ else:
+ print "Error: scons_output in file '%s' is missing an example name!" % fpath
+ failed_suffixes = True
+
+ if n not in suffixes:
+ print "Error: scons_output in file '%s' is referencing non-existent example '%s'!" % (fpath, n)
+ failed_suffixes = True
+ continue
+
+ s = ''
+ if stf.hasAttribute(o, 'suffix'):
+ s = stf.getAttribute(o, 'suffix')
+ else:
+ print "Error: scons_output in file '%s' (example '%s') is missing a suffix!" % (fpath, n)
+ failed_suffixes = True
+
+ if s not in suffixes[n]:
+ suffixes[n].append(s)
+ else:
+ print "Error: scons_output in file '%s' (example '%s') is using a duplicate suffix '%s'!" % (fpath, n, s)
+ failed_suffixes = True
+
+ return names, failed_suffixes
+
+def exampleNamesAreUnique(dpath):
+ """ Scan for XML files in the given directory and
+ check whether the scons_example names are unique.
+ """
+ unique = True
+ allnames = set()
+ for path, dirs, files in os.walk(dpath):
+ for f in files:
+ if f.endswith('.xml'):
+ fpath = os.path.join(path, f)
+ if SConsDoc.isSConsXml(fpath):
+ names, failed_suffixes = collectSConsExampleNames(fpath)
+ if failed_suffixes:
+ unique = False
+ i = allnames.intersection(names)
+ if i:
+ print "Not unique in %s are: %s" % (fpath, ', '.join(i))
+ unique = False
+
+ allnames |= names
+
+ return unique
+
+# ###############################################################
+#
+# In the second half of this module (starting here)
+# we define the variables and functions that are required
+# to actually run the examples, collect their output and
+# write it into the files in doc/generated/examples...
+# which then get included by our UserGuide.
+#
+# ###############################################################
+
+sys.path.append(os.path.join(os.getcwd(), 'QMTest'))
+sys.path.append(os.path.join(os.getcwd(), 'build', 'QMTest'))
+
+scons_py = os.path.join('bootstrap', 'src', 'script', 'scons.py')
+if not os.path.exists(scons_py):
+ scons_py = os.path.join('src', 'script', 'scons.py')
+
+scons_lib_dir = os.path.join(os.getcwd(), 'bootstrap', 'src', 'engine')
+if not os.path.exists(scons_lib_dir):
+ scons_lib_dir = os.path.join(os.getcwd(), 'src', 'engine')
+
+os.environ['SCONS_LIB_DIR'] = scons_lib_dir
+
+import TestCmd
+
+Prompt = {
+ 'posix' : '% ',
+ 'win32' : 'C:\\>'
+}
+
+# The magick SCons hackery that makes this work.
+#
+# So that our examples can still use the default SConstruct file, we
+# actually feed the following into SCons via stdin and then have it
+# SConscript() the SConstruct file. This stdin wrapper creates a set
+# of ToolSurrogates for the tools for the appropriate platform. These
+# Surrogates print output like the real tools and behave like them
+# without actually having to be on the right platform or have the right
+# tool installed.
+#
+# The upshot: The wrapper transparently changes the world out from
+# under the top-level SConstruct file in an example just so we can get
+# the command output.
+
+Stdin = """\
+import os
+import re
+import SCons.Action
+import SCons.Defaults
+import SCons.Node.FS
+
+platform = '%(osname)s'
+
+Sep = {
+ 'posix' : '/',
+ 'win32' : '\\\\',
+}[platform]
+
+
+# Slip our own __str__() method into the EntryProxy class used to expand
+# $TARGET{S} and $SOURCE{S} to translate the path-name separators from
+# what's appropriate for the system we're running on to what's appropriate
+# for the example system.
+orig = SCons.Node.FS.EntryProxy
+class MyEntryProxy(orig):
+ def __str__(self):
+ return str(self._subject).replace(os.sep, Sep)
+SCons.Node.FS.EntryProxy = MyEntryProxy
+
+# Slip our own RDirs() method into the Node.FS.File class so that the
+# expansions of $_{CPPINC,F77INC,LIBDIR}FLAGS will have the path-name
+# separators translated from what's appropriate for the system we're
+# running on to what's appropriate for the example system.
+orig_RDirs = SCons.Node.FS.File.RDirs
+def my_RDirs(self, pathlist, orig_RDirs=orig_RDirs):
+ return [str(x).replace(os.sep, Sep) for x in orig_RDirs(self, pathlist)]
+SCons.Node.FS.File.RDirs = my_RDirs
+
+class Curry(object):
+ def __init__(self, fun, *args, **kwargs):
+ self.fun = fun
+ self.pending = args[:]
+ self.kwargs = kwargs.copy()
+
+ def __call__(self, *args, **kwargs):
+ if kwargs and self.kwargs:
+ kw = self.kwargs.copy()
+ kw.update(kwargs)
+ else:
+ kw = kwargs or self.kwargs
+
+ return self.fun(*self.pending + args, **kw)
+
+def Str(target, source, env, cmd=""):
+ result = []
+ for cmd in env.subst_list(cmd, target=target, source=source):
+ result.append(' '.join(map(str, cmd)))
+ return '\\n'.join(result)
+
+class ToolSurrogate(object):
+ def __init__(self, tool, variable, func, varlist):
+ self.tool = tool
+ if not isinstance(variable, list):
+ variable = [variable]
+ self.variable = variable
+ self.func = func
+ self.varlist = varlist
+ def __call__(self, env):
+ t = Tool(self.tool)
+ t.generate(env)
+ for v in self.variable:
+ orig = env[v]
+ try:
+ strfunction = orig.strfunction
+ except AttributeError:
+ strfunction = Curry(Str, cmd=orig)
+ # Don't call Action() through its global function name, because
+ # that leads to infinite recursion in trying to initialize the
+ # Default Environment.
+ env[v] = SCons.Action.Action(self.func,
+ strfunction=strfunction,
+ varlist=self.varlist)
+ def __repr__(self):
+ # This is for the benefit of printing the 'TOOLS'
+ # variable through env.Dump().
+ return repr(self.tool)
+
+def Null(target, source, env):
+ pass
+
+def Cat(target, source, env):
+ target = str(target[0])
+ f = open(target, "wb")
+ for src in map(str, source):
+ f.write(open(src, "rb").read())
+ f.close()
+
+def CCCom(target, source, env):
+ target = str(target[0])
+ fp = open(target, "wb")
+ def process(source_file, fp=fp):
+ for line in open(source_file, "rb").readlines():
+ m = re.match(r'#include\s[<"]([^<"]+)[>"]', line)
+ if m:
+ include = m.group(1)
+ for d in [str(env.Dir('$CPPPATH')), '.']:
+ f = os.path.join(d, include)
+ if os.path.exists(f):
+ process(f)
+ break
+ elif line[:11] != "STRIP CCCOM":
+ fp.write(line)
+ for src in map(str, source):
+ process(src)
+ fp.write('debug = ' + ARGUMENTS.get('debug', '0') + '\\n')
+ fp.close()
+
+public_class_re = re.compile('^public class (\S+)', re.MULTILINE)
+
+def JavaCCom(target, source, env):
+ # This is a fake Java compiler that just looks for
+ # public class FooBar
+ # lines in the source file(s) and spits those out
+ # to .class files named after the class.
+ tlist = list(map(str, target))
+ not_copied = {}
+ for t in tlist:
+ not_copied[t] = 1
+ for src in map(str, source):
+ contents = open(src, "rb").read()
+ classes = public_class_re.findall(contents)
+ for c in classes:
+ for t in [x for x in tlist if x.find(c) != -1]:
+ open(t, "wb").write(contents)
+ del not_copied[t]
+ for t in not_copied.keys():
+ open(t, "wb").write("\\n")
+
+def JavaHCom(target, source, env):
+ tlist = map(str, target)
+ slist = map(str, source)
+ for t, s in zip(tlist, slist):
+ open(t, "wb").write(open(s, "rb").read())
+
+def JarCom(target, source, env):
+ target = str(target[0])
+ class_files = []
+ for src in map(str, source):
+ for dirpath, dirnames, filenames in os.walk(src):
+ class_files.extend([ os.path.join(dirpath, f)
+ for f in filenames if f.endswith('.class') ])
+ f = open(target, "wb")
+ for cf in class_files:
+ f.write(open(cf, "rb").read())
+ f.close()
+
+# XXX Adding COLOR, COLORS and PACKAGE to the 'cc' varlist(s) by hand
+# here is bogus. It's for the benefit of doc/user/command-line.in, which
+# uses examples that want to rebuild based on changes to these variables.
+# It would be better to figure out a way to do it based on the content of
+# the generated command-line, or else find a way to let the example markup
+# language in doc/user/command-line.in tell this script what variables to
+# add, but that's more difficult than I want to figure out how to do right
+# now, so let's just use the simple brute force approach for the moment.
+
+ToolList = {
+ 'posix' : [('cc', ['CCCOM', 'SHCCCOM'], CCCom, ['CCFLAGS', 'CPPDEFINES', 'COLOR', 'COLORS', 'PACKAGE']),
+ ('link', ['LINKCOM', 'SHLINKCOM'], Cat, []),
+ ('ar', ['ARCOM', 'RANLIBCOM'], Cat, []),
+ ('tar', 'TARCOM', Null, []),
+ ('zip', 'ZIPCOM', Null, []),
+ ('BitKeeper', 'BITKEEPERCOM', Cat, []),
+ ('CVS', 'CVSCOM', Cat, []),
+ ('RCS', 'RCS_COCOM', Cat, []),
+ ('SCCS', 'SCCSCOM', Cat, []),
+ ('javac', 'JAVACCOM', JavaCCom, []),
+ ('javah', 'JAVAHCOM', JavaHCom, []),
+ ('jar', 'JARCOM', JarCom, []),
+ ('rmic', 'RMICCOM', Cat, []),
+ ],
+ 'win32' : [('msvc', ['CCCOM', 'SHCCCOM', 'RCCOM'], CCCom, ['CCFLAGS', 'CPPDEFINES', 'COLOR', 'COLORS', 'PACKAGE']),
+ ('mslink', ['LINKCOM', 'SHLINKCOM'], Cat, []),
+ ('mslib', 'ARCOM', Cat, []),
+ ('tar', 'TARCOM', Null, []),
+ ('zip', 'ZIPCOM', Null, []),
+ ('BitKeeper', 'BITKEEPERCOM', Cat, []),
+ ('CVS', 'CVSCOM', Cat, []),
+ ('RCS', 'RCS_COCOM', Cat, []),
+ ('SCCS', 'SCCSCOM', Cat, []),
+ ('javac', 'JAVACCOM', JavaCCom, []),
+ ('javah', 'JAVAHCOM', JavaHCom, []),
+ ('jar', 'JARCOM', JarCom, []),
+ ('rmic', 'RMICCOM', Cat, []),
+ ],
+}
+
+toollist = ToolList[platform]
+filter_tools = '%(tools)s'.split()
+if filter_tools:
+ toollist = [x for x in toollist if x[0] in filter_tools]
+
+toollist = [ToolSurrogate(*t) for t in toollist]
+
+toollist.append('install')
+
+def surrogate_spawn(sh, escape, cmd, args, env):
+ pass
+
+def surrogate_pspawn(sh, escape, cmd, args, env, stdout, stderr):
+ pass
+
+SCons.Defaults.ConstructionEnvironment.update({
+ 'PLATFORM' : platform,
+ 'TOOLS' : toollist,
+ 'SPAWN' : surrogate_spawn,
+ 'PSPAWN' : surrogate_pspawn,
+})
+
+SConscript('SConstruct')
+"""
+
+# "Commands" that we will execute in our examples.
+def command_scons(args, c, test, dict):
+ save_vals = {}
+ delete_keys = []
+ try:
+ ce = c.environment
+ except AttributeError:
+ pass
+ else:
+ for arg in c.environment.split():
+ key, val = arg.split('=')
+ try:
+ save_vals[key] = os.environ[key]
+ except KeyError:
+ delete_keys.append(key)
+ os.environ[key] = val
+ test.run(interpreter=sys.executable,
+ program=scons_py,
+ # We use ToolSurrogates to capture win32 output by "building"
+ # examples using a fake win32 tool chain. Suppress the
+ # warnings that come from the new revamped VS support so
+ # we can build doc on (Linux) systems that don't have
+ # Visual C installed.
+ arguments='--warn=no-visual-c-missing -f - ' + ' '.join(args),
+ chdir=test.workpath('WORK'),
+ stdin=Stdin % dict)
+ os.environ.update(save_vals)
+ for key in delete_keys:
+ del(os.environ[key])
+ out = test.stdout()
+ out = out.replace(test.workpath('ROOT'), '')
+ out = out.replace(test.workpath('WORK/SConstruct'),
+ '/home/my/project/SConstruct')
+ lines = out.split('\n')
+ if lines:
+ while lines[-1] == '':
+ lines = lines[:-1]
+ # err = test.stderr()
+ # if err:
+ # sys.stderr.write(err)
+ return lines
+
+def command_touch(args, c, test, dict):
+ if args[0] == '-t':
+ t = int(time.mktime(time.strptime(args[1], '%Y%m%d%H%M')))
+ times = (t, t)
+ args = args[2:]
+ else:
+ time.sleep(1)
+ times = None
+ for file in args:
+ if not os.path.isabs(file):
+ file = os.path.join(test.workpath('WORK'), file)
+ if not os.path.exists(file):
+ open(file, 'wb')
+ os.utime(file, times)
+ return []
+
+def command_edit(args, c, test, dict):
+ if c.edit is None:
+ add_string = 'void edit(void) { ; }\n'
+ else:
+ add_string = c.edit[:]
+ if add_string[-1] != '\n':
+ add_string = add_string + '\n'
+ for file in args:
+ if not os.path.isabs(file):
+ file = os.path.join(test.workpath('WORK'), file)
+ contents = open(file, 'rb').read()
+ open(file, 'wb').write(contents + add_string)
+ return []
+
+def command_ls(args, c, test, dict):
+ def ls(a):
+ return [' '.join(sorted([x for x in os.listdir(a) if x[0] != '.']))]
+ if args:
+ l = []
+ for a in args:
+ l.extend(ls(test.workpath('WORK', a)))
+ return l
+ else:
+ return ls(test.workpath('WORK'))
+
+def command_sleep(args, c, test, dict):
+ time.sleep(int(args[0]))
+
+CommandDict = {
+ 'scons' : command_scons,
+ 'touch' : command_touch,
+ 'edit' : command_edit,
+ 'ls' : command_ls,
+ 'sleep' : command_sleep,
+}
+
+def ExecuteCommand(args, c, t, dict):
+ try:
+ func = CommandDict[args[0]]
+ except KeyError:
+ func = lambda args, c, t, dict: []
+ return func(args[1:], c, t, dict)
+
+
+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.folders:
+ 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
+ # Skip empty lines
+ while data[i] == '':
+ i = i + 1
+ 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):
+ path = t.workpath('WORK', path)
+ dir, name = os.path.split(path)
+ if dir and not os.path.exists(dir):
+ os.makedirs(dir)
+ content = '\n'.join(lines)
+ content = content.replace('__ROOT__', t.rootpath)
+ path = t.workpath('WORK', path)
+ t.write(path, content)
+ if hasattr(f, 'chmod'):
+ if len(f.chmod):
+ os.chmod(path, int(f.chmod, 0))
+
+ # 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+)')
+
+ # Root element for our subtree
+ sroot = stf.newEtreeNode("screen", True)
+ curchild = None
+ content = ""
+ for c in o.commands:
+ content += Prompt[o.os]
+ if curchild is not None:
+ if not c.output:
+ # Append content as tail
+ curchild.tail = content
+ content = "\n"
+ # Add new child for userinput tag
+ curchild = stf.newEtreeNode("userinput")
+ d = c.cmd.replace('__ROOT__', '')
+ curchild.text = d
+ sroot.append(curchild)
+ else:
+ content += c.output + '\n'
+ else:
+ if not c.output:
+ # Add first text to root
+ sroot.text = content
+ content = "\n"
+ # Add new child for userinput tag
+ curchild = stf.newEtreeNode("userinput")
+ d = c.cmd.replace('__ROOT__', '')
+ curchild.text = d
+ sroot.append(curchild)
+ else:
+ content += c.output + '\n'
+ # Execute command and capture its output
+ cmd_work = c.cmd.replace('__ROOT__', t.workpath('ROOT'))
+ args = cmd_work.split()
+ lines = ExecuteCommand(args, c, t, {'osname':o.os, 'tools':o.tools})
+ if not c.output and lines:
+ ncontent = '\n'.join(lines)
+ ncontent = address_re.sub(r' at 0x700000&gt;', ncontent)
+ ncontent = engine_re.sub(r' File "bootstrap/src/engine/SCons/', ncontent)
+ ncontent = file_re.sub(r'\1 <module>', ncontent)
+ ncontent = nodelist_re.sub(r"\1 'NodeList' object \2", ncontent)
+ ncontent = ncontent.replace('__ROOT__', '')
+ content += ncontent + '\n'
+ # Add last piece of content
+ if len(content):
+ if curchild is not None:
+ curchild.tail = content
+ else:
+ sroot.text = content
+
+ # Construct filename
+ fpath = os.path.join(generated_examples,
+ e.name + '_' + o.suffix + '.xml')
+ # Expand Element tree
+ s = stf.decorateWithHeader(stf.convertElementTree(sroot)[0])
+ # Write it to file
+ stf.writeTree(s, fpath)
+
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: