mirror of
https://github.com/boostorg/litre.git
synced 2026-01-19 04:22:08 +00:00
added litre
[SVN r34125]
This commit is contained in:
96
.gitattributes
vendored
Normal file
96
.gitattributes
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
* text=auto !eol svneol=native#text/plain
|
||||
*.gitattributes text svneol=native#text/plain
|
||||
|
||||
# Scriptish formats
|
||||
*.bat text svneol=native#text/plain
|
||||
*.bsh text svneol=native#text/x-beanshell
|
||||
*.cgi text svneol=native#text/plain
|
||||
*.cmd text svneol=native#text/plain
|
||||
*.js text svneol=native#text/javascript
|
||||
*.php text svneol=native#text/x-php
|
||||
*.pl text svneol=native#text/x-perl
|
||||
*.pm text svneol=native#text/x-perl
|
||||
*.py text svneol=native#text/x-python
|
||||
*.sh eol=lf svneol=LF#text/x-sh
|
||||
configure eol=lf svneol=LF#text/x-sh
|
||||
|
||||
# Image formats
|
||||
*.bmp binary svneol=unset#image/bmp
|
||||
*.gif binary svneol=unset#image/gif
|
||||
*.ico binary svneol=unset#image/ico
|
||||
*.jpeg binary svneol=unset#image/jpeg
|
||||
*.jpg binary svneol=unset#image/jpeg
|
||||
*.png binary svneol=unset#image/png
|
||||
*.tif binary svneol=unset#image/tiff
|
||||
*.tiff binary svneol=unset#image/tiff
|
||||
*.svg text svneol=native#image/svg%2Bxml
|
||||
|
||||
# Data formats
|
||||
*.pdf binary svneol=unset#application/pdf
|
||||
*.avi binary svneol=unset#video/avi
|
||||
*.doc binary svneol=unset#application/msword
|
||||
*.dsp text svneol=crlf#text/plain
|
||||
*.dsw text svneol=crlf#text/plain
|
||||
*.eps binary svneol=unset#application/postscript
|
||||
*.gz binary svneol=unset#application/gzip
|
||||
*.mov binary svneol=unset#video/quicktime
|
||||
*.mp3 binary svneol=unset#audio/mpeg
|
||||
*.ppt binary svneol=unset#application/vnd.ms-powerpoint
|
||||
*.ps binary svneol=unset#application/postscript
|
||||
*.psd binary svneol=unset#application/photoshop
|
||||
*.rdf binary svneol=unset#text/rdf
|
||||
*.rss text svneol=unset#text/xml
|
||||
*.rtf binary svneol=unset#text/rtf
|
||||
*.sln text svneol=native#text/plain
|
||||
*.swf binary svneol=unset#application/x-shockwave-flash
|
||||
*.tgz binary svneol=unset#application/gzip
|
||||
*.vcproj text svneol=native#text/xml
|
||||
*.vcxproj text svneol=native#text/xml
|
||||
*.vsprops text svneol=native#text/xml
|
||||
*.wav binary svneol=unset#audio/wav
|
||||
*.xls binary svneol=unset#application/vnd.ms-excel
|
||||
*.zip binary svneol=unset#application/zip
|
||||
|
||||
# Text formats
|
||||
.htaccess text svneol=native#text/plain
|
||||
*.bbk text svneol=native#text/xml
|
||||
*.cmake text svneol=native#text/plain
|
||||
*.css text svneol=native#text/css
|
||||
*.dtd text svneol=native#text/xml
|
||||
*.htm text svneol=native#text/html
|
||||
*.html text svneol=native#text/html
|
||||
*.ini text svneol=native#text/plain
|
||||
*.log text svneol=native#text/plain
|
||||
*.mak text svneol=native#text/plain
|
||||
*.qbk text svneol=native#text/plain
|
||||
*.rst text svneol=native#text/plain
|
||||
*.sql text svneol=native#text/x-sql
|
||||
*.txt text svneol=native#text/plain
|
||||
*.xhtml text svneol=native#text/xhtml%2Bxml
|
||||
*.xml text svneol=native#text/xml
|
||||
*.xsd text svneol=native#text/xml
|
||||
*.xsl text svneol=native#text/xml
|
||||
*.xslt text svneol=native#text/xml
|
||||
*.xul text svneol=native#text/xul
|
||||
*.yml text svneol=native#text/plain
|
||||
boost-no-inspect text svneol=native#text/plain
|
||||
CHANGES text svneol=native#text/plain
|
||||
COPYING text svneol=native#text/plain
|
||||
INSTALL text svneol=native#text/plain
|
||||
Jamfile text svneol=native#text/plain
|
||||
Jamroot text svneol=native#text/plain
|
||||
Jamfile.v2 text svneol=native#text/plain
|
||||
Jamrules text svneol=native#text/plain
|
||||
Makefile* text svneol=native#text/plain
|
||||
README text svneol=native#text/plain
|
||||
TODO text svneol=native#text/plain
|
||||
|
||||
# Code formats
|
||||
*.c text svneol=native#text/plain
|
||||
*.cpp text svneol=native#text/plain
|
||||
*.h text svneol=native#text/plain
|
||||
*.hpp text svneol=native#text/plain
|
||||
*.ipp text svneol=native#text/plain
|
||||
*.tpp text svneol=native#text/plain
|
||||
*.jam text svneol=native#text/plain
|
||||
*.java text svneol=native#text/plain
|
||||
733
cplusplus.py
Normal file
733
cplusplus.py
Normal file
@@ -0,0 +1,733 @@
|
||||
# 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, line_offset, line_hash = '#'):
|
||||
# A list of text fragments comprising the Example. Start with a #line
|
||||
# directive
|
||||
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', 'run', '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)
|
||||
|
||||
#
|
||||
# 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_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, 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())
|
||||
|
||||
#
|
||||
# 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 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 '\n\n ---------------- Unhandled Fragment ------------ \n\n'.join(
|
||||
[''] # generates a leading announcement
|
||||
+ [ str(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__]))
|
||||
|
||||
61
litre.py
Normal file
61
litre.py
Normal file
@@ -0,0 +1,61 @@
|
||||
from docutils import writers
|
||||
from docutils import nodes
|
||||
|
||||
class LitreTranslator(nodes.GenericNodeVisitor):
|
||||
|
||||
def __init__(self, document, config):
|
||||
nodes.GenericNodeVisitor.__init__(self,document)
|
||||
self._config = config
|
||||
|
||||
def default_visit(self, node):
|
||||
pass
|
||||
# print '**visiting:', repr(node)
|
||||
|
||||
def default_departure(self, node):
|
||||
pass
|
||||
# print '**departing:', repr(node)
|
||||
|
||||
def visit_raw(self, node):
|
||||
if node.has_key('format'):
|
||||
key = node['format'].lower()
|
||||
if key == 'litre':
|
||||
# This is probably very evil ;-)
|
||||
#if node.has_key('source'):
|
||||
# node.file = node.attributes['source']
|
||||
|
||||
self._handle_code(node, node.astext())
|
||||
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_comment(self, node):
|
||||
code = node.astext()
|
||||
if code[0] == '@':
|
||||
self._handle_code(node, code[1:].strip())
|
||||
|
||||
def _handle_code(self, node, code):
|
||||
start_line = node.line or 0
|
||||
start_line -= code.count('\n') + 2 # docutils bug workaround?
|
||||
try:
|
||||
self._execute(compile( start_line*'\n' + code, str(node.source), 'exec'))
|
||||
except:
|
||||
print '\n------- begin offending Python source -------'
|
||||
print code
|
||||
print '------- end offending Python source -------'
|
||||
raise
|
||||
|
||||
def _execute(self, code):
|
||||
"""Override this to set up local variable context for code before
|
||||
invoking it
|
||||
"""
|
||||
eval(code)
|
||||
|
||||
class Writer(writers.Writer):
|
||||
translator = LitreTranslator
|
||||
_config = None
|
||||
|
||||
def translate(self):
|
||||
visitor = self.translator(self.document, self._config)
|
||||
self.document.walkabout(visitor)
|
||||
self.output = visitor.astext()
|
||||
|
||||
|
||||
59
tool.py
Normal file
59
tool.py
Normal file
@@ -0,0 +1,59 @@
|
||||
try:
|
||||
import locale
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
import sys
|
||||
import docutils.writers
|
||||
import cplusplus
|
||||
import os
|
||||
|
||||
from docutils.core import publish_cmdline, default_description
|
||||
|
||||
class config:
|
||||
pass
|
||||
|
||||
description = ('Literate programming from ReStructuredText '
|
||||
'sources. ' + default_description)
|
||||
|
||||
def _pop_option(prefix):
|
||||
found = None
|
||||
for opt in sys.argv:
|
||||
if opt.startswith(prefix):
|
||||
sys.argv = [ x for x in sys.argv if x != opt ]
|
||||
found = opt
|
||||
if prefix.endswith('='):
|
||||
found = opt[len(prefix):]
|
||||
return found
|
||||
|
||||
|
||||
dump_dir = _pop_option('--dump_dir=')
|
||||
max_output_lines = _pop_option('--max_output_lines=')
|
||||
|
||||
if dump_dir:
|
||||
|
||||
cplusplus.Writer.translator = cplusplus.DumpTranslator
|
||||
if _pop_option('--workaround'):
|
||||
cplusplus.Writer.translator = cplusplus.WorkaroundTranslator
|
||||
config.includes.insert(0, os.path.join(os.path.split(dump_dir)[0], 'patches'))
|
||||
|
||||
config.dump_dir = os.path.abspath(dump_dir)
|
||||
if _pop_option('--cleanup_source'):
|
||||
config.line_hash = None
|
||||
|
||||
if not os.path.exists(config.dump_dir):
|
||||
os.makedirs(config.dump_dir)
|
||||
|
||||
if max_output_lines:
|
||||
config.max_output_lines = int(max_output_lines)
|
||||
|
||||
config.bjam_options = _pop_option('--bjam=')
|
||||
|
||||
config.includes = []
|
||||
|
||||
publish_cmdline(
|
||||
writer=cplusplus.Writer(config),
|
||||
description=description
|
||||
)
|
||||
Reference in New Issue
Block a user