b477f20b03
Jamfile. [SVN r35541]
811 lines
24 KiB
Python
811 lines
24 KiB
Python
# Copyright David Abrahams 2004.
|
|
# Copyright Daniel Wallin 2006.
|
|
# Distributed under the Boost
|
|
# Software License, Version 1.0. (See accompanying
|
|
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
import os
|
|
import tempfile
|
|
import litre
|
|
import re
|
|
import sys
|
|
import traceback
|
|
|
|
# Thanks to Jean Brouwers for this snippet
|
|
def _caller(up=0):
|
|
'''Get file name, line number, function name and
|
|
source text of the caller's caller as 4-tuple:
|
|
(file, line, func, text).
|
|
|
|
The optional argument 'up' allows retrieval of
|
|
a caller further back up into the call stack.
|
|
|
|
Note, the source text may be None and function
|
|
name may be '?' in the returned result. In
|
|
Python 2.3+ the file name may be an absolute
|
|
path.
|
|
'''
|
|
try: # just get a few frames'
|
|
f = traceback.extract_stack(limit=up+2)
|
|
if f:
|
|
return f[0]
|
|
except:
|
|
pass
|
|
# running with psyco?
|
|
return ('', 0, '', None)
|
|
|
|
class Example:
|
|
closed = False
|
|
in_emph = None
|
|
|
|
def __init__(self, node, section, line_offset, line_hash = '#'):
|
|
# A list of text fragments comprising the Example. Start with a #line
|
|
# directive
|
|
self.section = section
|
|
self.line_hash = line_hash
|
|
self.node = node
|
|
self.body = []
|
|
self.line_offset = line_offset
|
|
self._number_of_prefixes = 0
|
|
|
|
self.emphasized = [] # indices of text strings that have been
|
|
# emphasized. These are generally expected to be
|
|
# invalid C++ and will need special treatment
|
|
|
|
def begin_emphasis(self):
|
|
self.in_emph = len(self.body)
|
|
|
|
def end_emphasis(self):
|
|
self.emphasized.append( (self.in_emph, len(self.body)) )
|
|
|
|
def append(self, s):
|
|
self.append_raw(self._make_line(s))
|
|
|
|
def prepend(self, s):
|
|
self.prepend_raw(self._make_line(s))
|
|
|
|
def append_raw(self, s):
|
|
self.body.append(s)
|
|
|
|
def prepend_raw(self, s):
|
|
self.body.insert(0,s)
|
|
self.emphasized = [ (x[0]+1,x[1]+1) for x in self.emphasized ]
|
|
self._number_of_prefixes += 1
|
|
|
|
def replace(self, s1, s2):
|
|
self.body = [x.replace(s1,s2) for x in self.body]
|
|
|
|
def sub(self, pattern, repl, count = 1, flags = re.MULTILINE):
|
|
pat = re.compile(pattern, flags)
|
|
for i,txt in enumerate(self.body):
|
|
if count > 0:
|
|
x, subs = pat.subn(repl, txt, count)
|
|
self.body[i] = x
|
|
count -= subs
|
|
|
|
def wrap(self, s1, s2):
|
|
self.append_raw(self._make_line(s2))
|
|
self.prepend_raw(self._make_line(s1, offset = -s1.count('\n')))
|
|
|
|
def replace_emphasis(self, s, index = 0):
|
|
"""replace the index'th emphasized text with s"""
|
|
e = self.emphasized[index]
|
|
self.body[e[0]:e[1]] = [s]
|
|
del self.emphasized[index]
|
|
|
|
elipsis = re.compile('^([ \t]*)([.][.][.][ \t]*)$', re.MULTILINE)
|
|
|
|
def __str__(self):
|
|
# Comment out any remaining emphasized sections
|
|
b = [self.elipsis.sub(r'\1// \2', s) for s in self.body]
|
|
emph = self.emphasized
|
|
emph.reverse()
|
|
for e in emph:
|
|
b.insert(e[1], ' */')
|
|
b.insert(e[0], '/* ')
|
|
emph.reverse()
|
|
|
|
# Add initial #line
|
|
b.insert(
|
|
self._number_of_prefixes,
|
|
self._line_directive(self.node.line, self.node.source)
|
|
)
|
|
|
|
# Add trailing newline to avoid warnings
|
|
b.append('\n')
|
|
return ''.join(b)
|
|
|
|
def __repr__(self):
|
|
return "Example: " + repr(str(self))
|
|
|
|
def raw(self):
|
|
return ''.join(self.body)
|
|
|
|
def _make_line(self, s, offset = 0):
|
|
c = _caller(2)[1::-1]
|
|
offset -= s.count('\n')
|
|
return '\n%s%s\n' % (self._line_directive(offset = offset, *c), s.strip('\n'))
|
|
|
|
def _line_directive(self, line, source, offset = None):
|
|
if self.line_hash is None:
|
|
return '\n'
|
|
|
|
if offset is None:
|
|
offset = self.line_offset
|
|
|
|
if line is None or line <= -offset:
|
|
line = 1
|
|
else:
|
|
line += offset
|
|
|
|
if source is None:
|
|
return '%sline %d\n' % (self.line_hash, line)
|
|
else:
|
|
return '%sline %d "%s"\n' % (self.line_hash, line, source)
|
|
|
|
|
|
def syscmd(
|
|
cmd
|
|
, expect_error = False
|
|
, input = None
|
|
, max_output_lines = None
|
|
):
|
|
|
|
# On windows close() returns the exit code, on *nix it doesn't so
|
|
# we need to use popen2.Popen4 instead.
|
|
if sys.platform == 'win32':
|
|
stdin, stdout_stderr = os.popen4(cmd)
|
|
if input: stdin.write(input)
|
|
stdin.close()
|
|
|
|
out = stdout_stderr.read()
|
|
status = stdout_stderr.close()
|
|
else:
|
|
import popen2
|
|
process = popen2.Popen4(cmd)
|
|
if input: process.tochild.write(input)
|
|
out = process.fromchild.read()
|
|
status = process.wait()
|
|
|
|
if max_output_lines is not None:
|
|
out = '\n'.join(out.split('\n')[:max_output_lines])
|
|
|
|
if expect_error:
|
|
status = not status
|
|
|
|
if status:
|
|
print
|
|
print '========== offending command ==========='
|
|
print cmd
|
|
print '------------ stdout/stderr -------------'
|
|
print expect_error and 'Error expected, but none seen' or out
|
|
elif expect_error > 1:
|
|
print
|
|
print '------ Output of Expected Error --------'
|
|
print out
|
|
print '----------------------------------------'
|
|
|
|
sys.stdout.flush()
|
|
|
|
return (status,out)
|
|
|
|
|
|
def expand_vars(path):
|
|
if os.name == 'nt':
|
|
re_env = re.compile(r'%\w+%')
|
|
return re_env.sub(
|
|
lambda m: os.environ.get( m.group(0)[1:-1] )
|
|
, path
|
|
)
|
|
else:
|
|
return os.path.expandvars(path)
|
|
|
|
def remove_directory_and_contents(path):
|
|
for root, dirs, files in os.walk(path, topdown=False):
|
|
for name in files:
|
|
os.remove(os.path.join(root, name))
|
|
for name in dirs:
|
|
os.rmdir(os.path.join(root, name))
|
|
os.rmdir(path)
|
|
|
|
class BuildResult:
|
|
def __init__(self, path):
|
|
self.path = path
|
|
|
|
def __repr__(self):
|
|
return self.path
|
|
|
|
def __del__(self):
|
|
remove_directory_and_contents(self.path)
|
|
|
|
class CPlusPlusTranslator(litre.LitreTranslator):
|
|
|
|
_exposed_attrs = ['compile', 'test', 'ignore', 'match_stdout', 'stack', 'config'
|
|
, 'example', 'prefix', 'preprocessors', 'litre_directory',
|
|
'litre_translator', 'includes', 'build', 'jam_prefix',
|
|
'run_python']
|
|
|
|
last_run_output = ''
|
|
|
|
"""Attributes that will be made available to litre code"""
|
|
|
|
def __init__(self, document, config):
|
|
litre.LitreTranslator.__init__(self, document, config)
|
|
self.in_literal = False
|
|
self.in_table = True
|
|
self.preprocessors = []
|
|
self.stack = []
|
|
self.example = None
|
|
self.prefix = []
|
|
self.includes = config.includes
|
|
self.litre_directory = os.path.split(__file__)[0]
|
|
self.config = config
|
|
self.litre_translator = self
|
|
self.line_offset = 0
|
|
self.last_source = None
|
|
self.jam_prefix = []
|
|
|
|
self.globals = { 'test_literals_in_tables' : False }
|
|
for m in self._exposed_attrs:
|
|
self.globals[m] = getattr(self, m)
|
|
|
|
self.examples = {}
|
|
self.current_section = None
|
|
|
|
#
|
|
# Stuff for use by docutils writer framework
|
|
#
|
|
def visit_emphasis(self, node):
|
|
if self.in_literal:
|
|
self.example.begin_emphasis()
|
|
|
|
def depart_emphasis(self, node):
|
|
if self.in_literal:
|
|
self.example.end_emphasis()
|
|
|
|
def visit_section(self, node):
|
|
self.current_section = node['ids'][0]
|
|
|
|
def visit_literal_block(self, node):
|
|
if node.source is None:
|
|
node.source = self.last_source
|
|
self.last_source = node.source
|
|
|
|
# create a new example
|
|
self.example = Example(node, self.current_section, line_offset = self.line_offset, line_hash = self.config.line_hash)
|
|
|
|
self.stack.append(self.example)
|
|
|
|
self.in_literal = True
|
|
|
|
def depart_literal_block(self, node):
|
|
self.in_literal = False
|
|
|
|
def visit_literal(self, node):
|
|
if self.in_table and self.globals['test_literals_in_tables']:
|
|
self.visit_literal_block(node)
|
|
else:
|
|
litre.LitreTranslator.visit_literal(self,node)
|
|
|
|
def depart_literal(self, node):
|
|
if self.in_table and self.globals['test_literals_in_tables']:
|
|
self.depart_literal_block(node)
|
|
else:
|
|
litre.LitreTranslator.depart_literal(self,node)
|
|
|
|
def visit_table(self,node):
|
|
self.in_table = True
|
|
litre.LitreTranslator.visit_table(self,node)
|
|
|
|
def depart_table(self,node):
|
|
self.in_table = False
|
|
litre.LitreTranslator.depart_table(self,node)
|
|
|
|
def visit_Text(self, node):
|
|
if self.in_literal:
|
|
self.example.append_raw(node.astext())
|
|
|
|
def depart_document(self, node):
|
|
self.write_examples()
|
|
|
|
#
|
|
# Private stuff
|
|
#
|
|
|
|
def handled(self, n = 1):
|
|
r = self.stack[-n:]
|
|
del self.stack[-n:]
|
|
return r
|
|
|
|
def _execute(self, code):
|
|
"""Override of litre._execute; sets up variable context before
|
|
evaluating code
|
|
"""
|
|
self.globals['example'] = self.example
|
|
eval(code, self.globals)
|
|
|
|
#
|
|
# Stuff for use by embedded python code
|
|
#
|
|
|
|
def match_stdout(self, expected = None):
|
|
|
|
if expected is None:
|
|
expected = self.example.raw()
|
|
self.handled()
|
|
|
|
if not re.search(expected, self.last_run_output, re.MULTILINE):
|
|
#if self.last_run_output.strip('\n') != expected.strip('\n'):
|
|
print 'output failed to match example'
|
|
print '-------- Actual Output -------------'
|
|
print repr(self.last_run_output)
|
|
print '-------- Expected Output -----------'
|
|
print repr(expected)
|
|
print '------------------------------------'
|
|
sys.stdout.flush()
|
|
|
|
def ignore(self, n = 1):
|
|
if n == 'all':
|
|
n = len(self.stack)
|
|
return self.handled(n)
|
|
|
|
def wrap(self, n, s1, s2):
|
|
self.stack[-1].append(s2)
|
|
self.stack[-n].prepend(s1)
|
|
|
|
|
|
def compile(
|
|
self
|
|
, howmany = 1
|
|
, pop = -1
|
|
, expect_error = False
|
|
, extension = '.o'
|
|
, options = ['-c']
|
|
, built_handler = lambda built_file: None
|
|
, source_file = None
|
|
, source_suffix = '.cpp'
|
|
# C-style comments by default; handles C++ and YACC
|
|
, make_comment = lambda text: '/*\n%s\n*/' % text
|
|
, built_file = None
|
|
, command = None
|
|
):
|
|
"""
|
|
Compile examples on the stack, whose topmost item is the last example
|
|
seen but not yet handled so far.
|
|
|
|
:howmany: How many of the topmost examples on the stack to compile.
|
|
You can pass a number, or 'all' to indicate that all examples should
|
|
be compiled.
|
|
|
|
:pop: How many of the topmost examples to discard. By default, all of
|
|
the examples that are compiled are discarded.
|
|
|
|
:expect_error: Whether a compilation error is to be expected. Any value
|
|
> 1 will cause the expected diagnostic's text to be dumped for
|
|
diagnostic purposes. It's common to expect an error but see a
|
|
completely unrelated one because of bugs in the example (you can get
|
|
this behavior for all examples by setting show_expected_error_output
|
|
in your config).
|
|
|
|
:extension: The extension of the file to build (set to .exe for
|
|
run)
|
|
|
|
:options: Compiler flags
|
|
|
|
:built_file: A path to use for the built file. By default, a temp
|
|
filename is conjured up
|
|
|
|
:built_handler: A function that's called with the name of the built file
|
|
upon success.
|
|
|
|
:source_file: The full name of the source file to write
|
|
|
|
:source_suffix: If source_file is None, the suffix to use for the source file
|
|
|
|
:make_comment: A function that transforms text into an appropriate comment.
|
|
|
|
:command: A function that is passed (includes, opts, target, source), where
|
|
opts is a string representing compiler options, target is the name of
|
|
the file to build, and source is the name of the file into which the
|
|
example code is written. By default, the function formats
|
|
litre.config.compiler with its argument tuple.
|
|
"""
|
|
|
|
# Grab one example by default
|
|
if howmany == 'all':
|
|
howmany = len(self.stack)
|
|
|
|
source = '\n'.join(
|
|
self.prefix
|
|
+ [str(x) for x in self.stack[-howmany:]]
|
|
)
|
|
|
|
source = reduce(lambda s, f: f(s), self.preprocessors, source)
|
|
|
|
if pop:
|
|
if pop < 0:
|
|
pop = howmany
|
|
del self.stack[-pop:]
|
|
|
|
if len(self.stack):
|
|
self.example = self.stack[-1]
|
|
|
|
cpp = self._source_file_path(source_file, source_suffix)
|
|
|
|
if built_file is None:
|
|
built_file = self._output_file_path(source_file, extension)
|
|
|
|
opts = ' '.join(options)
|
|
|
|
includes = ' '.join(['-I%s' % d for d in self.includes])
|
|
if not command:
|
|
command = self.config.compiler
|
|
|
|
if type(command) == str:
|
|
command = lambda i, o, t, s, c = command: c % (i, o, t, s)
|
|
|
|
cmd = command(includes, opts, expand_vars(built_file), expand_vars(cpp))
|
|
|
|
if expect_error and self.config.show_expected_error_output:
|
|
expect_error += 1
|
|
|
|
|
|
comment_cmd = command(includes, opts, built_file, os.path.basename(cpp))
|
|
comment = make_comment(config.comment_text(comment_cmd, expect_error))
|
|
|
|
self._write_source(cpp, '\n'.join([comment, source]))
|
|
|
|
#print 'wrote in', cpp
|
|
#print 'trying command', cmd
|
|
|
|
status, output = syscmd(cmd, expect_error)
|
|
|
|
if status or expect_error > 1:
|
|
print
|
|
if expect_error and expect_error < 2:
|
|
print 'Compilation failure expected, but none seen'
|
|
print '------------ begin offending source ------------'
|
|
print open(cpp).read()
|
|
print '------------ end offending source ------------'
|
|
|
|
if self.config.save_cpp:
|
|
print 'saved in', repr(cpp)
|
|
else:
|
|
self._remove_source(cpp)
|
|
|
|
sys.stdout.flush()
|
|
else:
|
|
print '.',
|
|
sys.stdout.flush()
|
|
built_handler(built_file)
|
|
|
|
self._remove_source(cpp)
|
|
|
|
try:
|
|
self._unlink(built_file)
|
|
except:
|
|
if not expect_error:
|
|
print 'failed to unlink', built_file
|
|
|
|
return status
|
|
|
|
def test(
|
|
self
|
|
, rule = 'run'
|
|
, howmany = 1
|
|
, pop = -1
|
|
, expect_error = False
|
|
, requirements = ''
|
|
, input = ''
|
|
):
|
|
|
|
# Grab one example by default
|
|
if howmany == 'all':
|
|
howmany = len(self.stack)
|
|
|
|
source = '\n'.join(
|
|
self.prefix
|
|
+ [str(x) for x in self.stack[-howmany:]]
|
|
)
|
|
|
|
source = reduce(lambda s, f: f(s), self.preprocessors, source)
|
|
|
|
id = self.example.section
|
|
if not id:
|
|
id = 'top-level'
|
|
|
|
if not self.examples.has_key(self.example.section):
|
|
self.examples[id] = [(rule, source)]
|
|
else:
|
|
self.examples[id].append((rule, source))
|
|
|
|
if pop:
|
|
if pop < 0:
|
|
pop = howmany
|
|
del self.stack[-pop:]
|
|
|
|
if len(self.stack):
|
|
self.example = self.stack[-1]
|
|
|
|
def write_examples(self):
|
|
jam = open(os.path.join(self.config.dump_dir, 'Jamfile.v2'), 'w')
|
|
|
|
jam.write('''
|
|
import testing ;
|
|
|
|
''')
|
|
|
|
for id,examples in self.examples.items():
|
|
for i in range(len(examples)):
|
|
cpp = '%s%d.cpp' % (id, i)
|
|
|
|
jam.write('%s %s ;\n' % (examples[i][0], cpp))
|
|
|
|
outfile = os.path.join(self.config.dump_dir, cpp)
|
|
print cpp,
|
|
try:
|
|
if open(outfile, 'r').read() == examples[i][1]:
|
|
print ' .. skip'
|
|
continue
|
|
except:
|
|
pass
|
|
|
|
open(outfile, 'w').write(examples[i][1])
|
|
print ' .. written'
|
|
|
|
jam.close()
|
|
|
|
def build(
|
|
self
|
|
, howmany = 1
|
|
, pop = -1
|
|
, source_file = 'example.cpp'
|
|
, expect_error = False
|
|
, target_rule = 'obj'
|
|
, requirements = ''
|
|
, input = ''
|
|
, output = 'example_output'
|
|
):
|
|
|
|
# Grab one example by default
|
|
if howmany == 'all':
|
|
howmany = len(self.stack)
|
|
|
|
source = '\n'.join(
|
|
self.prefix
|
|
+ [str(x) for x in self.stack[-howmany:]]
|
|
)
|
|
|
|
source = reduce(lambda s, f: f(s), self.preprocessors, source)
|
|
|
|
if pop:
|
|
if pop < 0:
|
|
pop = howmany
|
|
del self.stack[-pop:]
|
|
|
|
if len(self.stack):
|
|
self.example = self.stack[-1]
|
|
|
|
dir = tempfile.mkdtemp()
|
|
cpp = os.path.join(dir, source_file)
|
|
self._write_source(cpp, source)
|
|
self._write_jamfile(
|
|
dir
|
|
, target_rule = target_rule
|
|
, requirements = requirements
|
|
, input = input
|
|
, output = output
|
|
)
|
|
|
|
cmd = 'bjam'
|
|
if self.config.bjam_options:
|
|
cmd += ' %s' % self.config.bjam_options
|
|
|
|
os.chdir(dir)
|
|
status, output = syscmd(cmd, expect_error)
|
|
|
|
if status or expect_error > 1:
|
|
print
|
|
if expect_error and expect_error < 2:
|
|
print 'Compilation failure expected, but none seen'
|
|
print '------------ begin offending source ------------'
|
|
print open(cpp).read()
|
|
print '------------ begin offending Jamfile -----------'
|
|
print open(os.path.join(dir, 'Jamroot')).read()
|
|
print '------------ end offending Jamfile -------------'
|
|
|
|
sys.stdout.flush()
|
|
else:
|
|
print '.',
|
|
sys.stdout.flush()
|
|
|
|
if status: return None
|
|
else: return BuildResult(dir)
|
|
|
|
def _write_jamfile(self, path, target_rule, requirements, input, output):
|
|
jamfile = open(os.path.join(path, 'Jamroot'), 'w')
|
|
contents = r"""
|
|
import modules ;
|
|
|
|
BOOST_ROOT = [ modules.peek : BOOST_ROOT ] ;
|
|
use-project /boost : $(BOOST_ROOT) ;
|
|
|
|
%s
|
|
|
|
%s %s
|
|
: example.cpp %s
|
|
: <include>.
|
|
%s
|
|
%s
|
|
;
|
|
""" % (
|
|
'\n'.join(self.jam_prefix)
|
|
, target_rule
|
|
, output
|
|
, input
|
|
, ' '.join(['<include>%s' % d for d in self.includes])
|
|
, requirements
|
|
)
|
|
|
|
jamfile.write(contents)
|
|
|
|
def run_python(
|
|
self
|
|
, howmany = 1
|
|
, pop = -1
|
|
, module_path = []
|
|
, expect_error = False
|
|
):
|
|
# Grab one example by default
|
|
if howmany == 'all':
|
|
howmany = len(self.stack)
|
|
|
|
if module_path == None: module_path = []
|
|
|
|
if isinstance(module_path, BuildResult) or type(module_path) == str:
|
|
module_path = [module_path]
|
|
|
|
module_path = map(lambda p: str(p), module_path)
|
|
|
|
source = '\n'.join(
|
|
self.prefix
|
|
+ [str(x) for x in self.stack[-howmany:]]
|
|
)
|
|
|
|
if pop:
|
|
if pop < 0:
|
|
pop = howmany
|
|
del self.stack[-pop:]
|
|
|
|
if len(self.stack):
|
|
self.example = self.stack[-1]
|
|
|
|
r = re.compile(r'^(>>>|\.\.\.) (.*)$', re.MULTILINE)
|
|
source = r.sub(r'\2', source)
|
|
py = self._source_file_path(source_file = None, source_suffix = 'py')
|
|
open(py, 'w').write(source)
|
|
|
|
old_path = os.getenv('PYTHONPATH')
|
|
if old_path == None:
|
|
pythonpath = ':'.join(module_path)
|
|
old_path = ''
|
|
else:
|
|
pythonpath = old_path + ':%s' % ':'.join(module_path)
|
|
|
|
os.putenv('PYTHONPATH', pythonpath)
|
|
status, output = syscmd('python %s' % py)
|
|
|
|
if status or expect_error > 1:
|
|
print
|
|
if expect_error and expect_error < 2:
|
|
print 'Compilation failure expected, but none seen'
|
|
print '------------ begin offending source ------------'
|
|
print open(py).read()
|
|
print '------------ end offending Jamfile -------------'
|
|
|
|
sys.stdout.flush()
|
|
else:
|
|
print '.',
|
|
sys.stdout.flush()
|
|
|
|
self.last_run_output = output
|
|
os.putenv('PYTHONPATH', old_path)
|
|
self._unlink(py)
|
|
|
|
def _write_source(self, filename, contents):
|
|
open(filename,'w').write(contents)
|
|
|
|
def _remove_source(self, source_path):
|
|
os.unlink(source_path)
|
|
|
|
def _source_file_path(self, source_file, source_suffix):
|
|
if source_file is None:
|
|
cpp = tempfile.mktemp(suffix=source_suffix)
|
|
else:
|
|
cpp = os.path.join(tempfile.gettempdir(), source_file)
|
|
return cpp
|
|
|
|
def _output_file_path(self, source_file, extension):
|
|
return tempfile.mktemp(suffix=extension)
|
|
|
|
def _unlink(self, file):
|
|
file = expand_vars(file)
|
|
if os.path.exists(file):
|
|
os.unlink(file)
|
|
|
|
def _launch(self, exe, stdin = None):
|
|
status, output = syscmd(exe, input = stdin)
|
|
self.last_run_output = output
|
|
|
|
def run_(self, howmany = 1, stdin = None, **kw):
|
|
new_kw = { 'options':[], 'extension':'.exe' }
|
|
new_kw.update(kw)
|
|
|
|
self.compile(
|
|
howmany
|
|
, built_handler = lambda exe: self._launch(exe, stdin = stdin)
|
|
, **new_kw
|
|
)
|
|
|
|
def astext(self):
|
|
return ""
|
|
return '\n\n ---------------- Unhandled Fragment ------------ \n\n'.join(
|
|
[''] # generates a leading announcement
|
|
+ [ unicode(s) for s in self.stack]
|
|
)
|
|
|
|
class DumpTranslator(CPlusPlusTranslator):
|
|
example_index = 1
|
|
|
|
def _source_file_path(self, source_file, source_suffix):
|
|
if source_file is None:
|
|
source_file = 'example%s%s' % (self.example_index, source_suffix)
|
|
self.example_index += 1
|
|
|
|
cpp = os.path.join(config.dump_dir, source_file)
|
|
return cpp
|
|
|
|
def _output_file_path(self, source_file, extension):
|
|
chapter = os.path.basename(config.dump_dir)
|
|
return '%%TEMP%%\metaprogram-%s-example%s%s' \
|
|
% ( chapter, self.example_index - 1, extension)
|
|
|
|
def _remove_source(self, source_path):
|
|
pass
|
|
|
|
|
|
class WorkaroundTranslator(DumpTranslator):
|
|
"""Translator used to test/dump workaround examples for vc6 and vc7. Just
|
|
like a DumpTranslator except that we leave existing files alone.
|
|
|
|
Warning: not sensitive to changes in .rst source!! If you change the actual
|
|
examples in source files you will have to move the example files out of the
|
|
way and regenerate them, then re-incorporate the workarounds.
|
|
"""
|
|
def _write_source(self, filename, contents):
|
|
if not os.path.exists(filename):
|
|
DumpTranslator._write_source(self, filename, contents)
|
|
|
|
class Config:
|
|
save_cpp = False
|
|
line_hash = '#'
|
|
show_expected_error_output = False
|
|
max_output_lines = None
|
|
|
|
class Writer(litre.Writer):
|
|
translator = CPlusPlusTranslator
|
|
|
|
def __init__(
|
|
self
|
|
, config
|
|
):
|
|
litre.Writer.__init__(self)
|
|
self._config = Config()
|
|
defaults = Config.__dict__
|
|
|
|
# update config elements
|
|
self._config.__dict__.update(config.__dict__)
|
|
# dict([i for i in config.__dict__.items()
|
|
# if i[0] in config.__all__]))
|
|
|