[pypy] * Wed Jan 5 2011 David Malcolm <dmalcolm at redhat.com> - 1.4.1-4 - rebuild pypy using itself, for spe

dmalcolm dmalcolm at fedoraproject.org
Thu Jan 6 22:12:22 UTC 2011


commit 00e57e74f81587a2c49341341a7a291a54e22dc8
Author: David Malcolm <dmalcolm at redhat.com>
Date:   Thu Jan 6 17:09:03 2011 -0500

    * Wed Jan  5 2011 David Malcolm <dmalcolm at redhat.com> - 1.4.1-4
    - rebuild pypy using itself, for speed, with a boolean to break this cycle in
    the build-requirement graph (falling back to using "python-devel" aka CPython)
    - add work-in-progress patch to try to make generated c more readable
    (rhbz#666963)
    - capture the RPython source code files from the build within the debuginfo
    package (rhbz#666975)

 pypy-1.4.1-more-readable-c-code.patch |  695 +++++++++++++++++++++++++++++++++
 pypy.spec                             |   81 ++++-
 2 files changed, 773 insertions(+), 3 deletions(-)
---
diff --git a/pypy-1.4.1-more-readable-c-code.patch b/pypy-1.4.1-more-readable-c-code.patch
new file mode 100644
index 0000000..45fa534
--- /dev/null
+++ b/pypy-1.4.1-more-readable-c-code.patch
@@ -0,0 +1,695 @@
+diff -r cd083843b67a pypy/interpreter/pycode.py
+--- a/pypy/interpreter/pycode.py	Mon Dec 20 17:17:45 2010 +0100
++++ b/pypy/interpreter/pycode.py	Wed Jan 05 16:14:35 2011 -0500
+@@ -14,6 +14,7 @@
+ from pypy.interpreter.astcompiler.consts import (CO_OPTIMIZED,
+     CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS, CO_NESTED,
+     CO_GENERATOR, CO_CONTAINSGLOBALS)
++from pypy.interpreter.pytraceback import offset2lineno
+ from pypy.rlib.rarithmetic import intmask
+ from pypy.rlib.debug import make_sure_not_resized
+ from pypy.rlib import jit
+@@ -81,6 +82,7 @@
+         self.hidden_applevel = hidden_applevel
+         self.magic = magic
+         self._signature = cpython_code_signature(self)
++        self._cached_source = None
+         self._initialize()
+ 
+     def _initialize(self):
+@@ -403,3 +405,25 @@
+     def repr(self, space):
+         return space.wrap(self.get_repr())
+     repr.unwrap_spec = ['self', ObjSpace]
++
++    def get_linenum_for_offset(self, offset):
++        # Given a bytecode offset, return a 1-based index into the lines of the
++        # source code
++        return offset2lineno(self, offset)
++
++    def _ensure_source(self):
++        # Lazily grab the source lines into self._cached_source (or raise
++        # an IOError)
++        if not self._cached_source:
++            f = open(self.co_filename, 'r')
++            source = [line.rstrip() for line in f.readlines()]
++            f.close()
++            self._cached_source = source
++    
++    def get_source_text(self, linenum):
++        # Given a 1-based index, get the corresponding line of source code (or
++        # raise an IOError)
++        self._ensure_source()
++        return self._cached_source[linenum - 1]
++
++        
+diff -r cd083843b67a pypy/objspace/flow/model.py
+--- a/pypy/objspace/flow/model.py	Mon Dec 20 17:17:45 2010 +0100
++++ b/pypy/objspace/flow/model.py	Wed Jan 05 16:14:35 2011 -0500
+@@ -31,6 +31,120 @@
+ 
+ __metaclass__ = type
+ 
++class SourceLoc(object):
++    # A srcloc is a specific location within the RPython source code,
++    # intended for human display
++    __slots__ = ('code', # code object
++                 'linenum' # 1-based index, as displayed to a user
++                 )
++    def __init__(self, code, linenum):
++        self.code = code
++        self.linenum = linenum
++
++    def get_text(self):
++        # Get the actual source text of this line
++        return self.code.get_source_text(self.linenum)
++
++    def __eq__(self, other):
++        return self.code == other.code and self.linenum == other.linenum
++
++    def __ne__(self, other):
++        if other:
++            return self.code != other.code or self.linenum != other.linenum
++        else:
++            return True
++
++class CodeLoc(object):
++    # A codeloc is a specific location within the RPython bytecode
++    __slots__ = ('code', # code object
++                 'offset' # int index into bytecode, or -1
++                 )
++
++    def __init__(self, code, offset):
++        self.code = code
++        self.offset = offset
++
++    def __str__(self):
++        if self.offset >= 0:
++            return "%s@%d" % (self.code.co_name, self.offset)
++        else:
++            return ""
++
++    def __ne__(self, other):
++        if other:
++            return self.code != other.code or self.offset != other.offset
++        else:
++            return True
++
++    def __cmp__(self, other):
++        # Partial ordering, for those locations that have an offset:
++        if other:
++            if self.offset >= 0 and other.offset >= 0:
++                return self.offset - other.offset
++        return 0
++
++    def get_source_loc(self):
++        # Convert to a SourceLoc:
++        return SourceLoc(self.code, self.code.get_linenum_for_offset(self.offset))
++
++class OperationLoc(object):
++    # An oploc is the location within the RPython source code of a given
++    # operation
++    # 
++    # This is a list consisting of CodeLoc instances, some of which may be None
++    #
++    # For the simple case, this is list of length 1 with a single CodeLoc
++    #
++    # For an operation inside an inlined callsite, we have a list of length 2:
++    #    [codeloc of callsite,
++    #     codeloc of operation within inlined body]
++    #
++    # For more interesting inlined cases, we have a chain of source locations:
++    #    [codeloc of callsite,
++    #     codeloc of inner callsite,
++    #     ... ,
++    #     codeloc of innermost inlined callsite,
++    #     codeloc of operation within inlined body]
++    #
++
++    __slots__ = ('codelocs', )
++
++    def __init__(self, codelocs):
++        self.codelocs = codelocs
++
++    def __str__(self):
++        return '[' + ' > '.join(str(codeloc) for codeloc in self.codelocs) + ']'
++
++    def __cmp__(self, other):
++        return cmp(self.codelocs, other.codelocs)
++
++def block_comparator(blk0, blk1):
++    '''
++    Sort function for blocks, putting them in an ordering that attempts to
++    maximize readability of the generated C code
++    '''
++    # print 'comparing %r and %r' % (blk0, blk1)
++    # Put the start/end block at the top/bottom:
++    if blk0.isstartblock:
++        return -1
++
++    if blk1.isstartblock:
++        return 1
++
++    # Order blocks by the offset, where present:
++    if blk0.operations:
++        if blk1.operations:
++            return cmp(blk0.operations[0].oploc, blk1.operations[0].oploc)
++        else:
++            return -1
++    else:
++        if blk1.operations:
++            return 1
++        else:
++            return 0
++
++def edge_comparator(edge0, edge1):
++    return block_comparator(edge0.target, edge1.target)
+ 
+ class FunctionGraph(object):
+     __slots__ = ['startblock', 'returnblock', 'exceptblock', '__dict__']
+@@ -94,6 +208,21 @@
+                 seen[block] = True
+                 stack += block.exits[::-1]
+ 
++    def iterblocks_by_source(self):
++        # Try to preserve logical source ordering in the blocks
++        block = self.startblock
++        yield block
++        seen = {block: True}
++        stack = list(block.exits[::-1])
++        stack.sort(edge_comparator)
++        while stack:
++            block = stack.pop().target
++            if block not in seen:
++                yield block
++                seen[block] = True
++                stack += block.exits[::-1]
++                stack.sort(edge_comparator)
++
+     def iterlinks(self):
+         block = self.startblock
+         seen = {block: True}
+@@ -183,14 +312,14 @@
+         self.exits      = []              # list of Link(s)
+ 
+     def at(self):
+-        if self.operations and self.operations[0].offset >= 0:
+-            return "@%d" % self.operations[0].offset
++        if self.operations:
++            return str(self.operations[0].oploc)
+         else:
+             return ""
+ 
+     def __str__(self):
+         if self.operations:
+-            txt = "block@%d" % self.operations[0].offset
++            txt = "block%s" % self.operations[0].oploc
+         else:
+             if (not self.exits) and len(self.inputargs) == 1:
+                 txt = "return block"
+@@ -245,6 +374,21 @@
+         from pypy.translator.tool.graphpage import try_show
+         try_show(self)
+ 
++    def isreturnblock(self):
++        return (not self.operations) and (not self.exits) and len(self.inputargs) == 1
++
++    def get_base_label(self, blocknum):
++        # Generate a more friendly C label for this block
++        if self.operations:
++            txt = "block"
++        elif (not self.exits) and len(self.inputargs) == 1:
++            txt = "return_block"
++        elif (not self.exits) and len(self.inputargs) == 2:
++            txt = "raise_block"
++        else:
++            txt = "codeless_block"
++        return '%s%d' % (txt, blocknum)
++
+ 
+ class Variable(object):
+     __slots__ = ["_name", "_nr", "concretetype"]
+@@ -331,13 +475,15 @@
+ 
+ 
+ class SpaceOperation(object):
+-    __slots__ = "opname args result offset".split()
++    __slots__ = "opname args result oploc".split()
+ 
+-    def __init__(self, opname, args, result, offset=-1):
++    def __init__(self, opname, args, result, oploc=None):
+         self.opname = intern(opname)      # operation name
+         self.args   = list(args)  # mixed list of var/const
+         self.result = result      # either Variable or Constant instance
+-        self.offset = offset      # offset in code string
++        if oploc is None:
++            oploc = OperationLoc([None])
++        self.oploc = oploc
+ 
+     def __eq__(self, other):
+         return (self.__class__ is other.__class__ and 
+@@ -352,8 +498,9 @@
+         return hash((self.opname,tuple(self.args),self.result))
+ 
+     def __repr__(self):
+-        return "%r = %s(%s)" % (self.result, self.opname,
+-                                ", ".join(map(repr, self.args)))
++        return "%r = %s(%s) (%s)" % (self.result, self.opname,
++                                     ", ".join(map(repr, self.args)),
++                                     self.oploc)
+ 
+ class Atom(object):
+     def __init__(self, name):
+@@ -448,8 +595,7 @@
+                 for op in oplist:
+                     copyop = SpaceOperation(op.opname,
+                                             [copyvar(v) for v in op.args],
+-                                            copyvar(op.result), op.offset)
+-                    #copyop.offset = op.offset
++                                            copyvar(op.result), op.oploc)
+                     result.append(copyop)
+                 return result
+             newblock.operations = copyoplist(block.operations)
+diff -r cd083843b67a pypy/objspace/flow/objspace.py
+--- a/pypy/objspace/flow/objspace.py	Mon Dec 20 17:17:45 2010 +0100
++++ b/pypy/objspace/flow/objspace.py	Wed Jan 05 16:14:35 2011 -0500
+@@ -310,7 +310,9 @@
+     def do_operation(self, name, *args_w):
+         spaceop = SpaceOperation(name, args_w, Variable())
+         if hasattr(self, 'executioncontext'):  # not here during bootstrapping
+-            spaceop.offset = self.executioncontext.crnt_offset
++            codeloc = CodeLoc(self.executioncontext.code,
++                              self.executioncontext.crnt_offset)
++            spaceop.oploc = OperationLoc([codeloc])
+             self.executioncontext.recorder.append(spaceop)
+         return spaceop.result
+ 
+diff -r cd083843b67a pypy/objspace/flow/test/test_model.py
+--- a/pypy/objspace/flow/test/test_model.py	Mon Dec 20 17:17:45 2010 +0100
++++ b/pypy/objspace/flow/test/test_model.py	Wed Jan 05 16:14:35 2011 -0500
+@@ -132,3 +132,25 @@
+     assert v2.renamed
+     assert v2.name.startswith("foobar_") and v2.name != v.name
+     assert v2.name.split('_', 1)[1].isdigit()
++
++def test_source_locations():
++    # Invent some random offsets into the code:
++    co = sample_function.__code__
++    codelocA = CodeLoc(co, 42)
++    codelocB = CodeLoc(co, 87)
++
++    assert str(codelocA) == 'sample_function at 42'
++    assert str(codelocB) == 'sample_function at 87'
++
++    assert cmp(codelocA, codelocB) < 0
++    assert cmp(codelocB, codelocA) > 0
++    
++    oplocA = OperationLoc([codelocA])
++    oplocB = OperationLoc([codelocB])
++
++    assert str(oplocA) == '[sample_function at 42]'
++    assert str(oplocB) == '[sample_function at 87]'
++
++    assert cmp(oplocA, oplocB) < 0
++    assert cmp(oplocB, oplocA) > 0
++
+diff -r cd083843b67a pypy/rpython/rtyper.py
+--- a/pypy/rpython/rtyper.py	Mon Dec 20 17:17:45 2010 +0100
++++ b/pypy/rpython/rtyper.py	Wed Jan 05 16:14:35 2011 -0500
+@@ -800,7 +800,7 @@
+         return vars
+ 
+     def genop(self, opname, args_v, resulttype=None):
+-        return self.llops.genop(opname, args_v, resulttype)
++        return self.llops.genop(opname, args_v, resulttype, self.spaceop.oploc)
+ 
+     def gendirectcall(self, ll_function, *args_v):
+         return self.llops.gendirectcall(ll_function, *args_v)
+@@ -935,7 +935,7 @@
+                                                     v.concretetype))
+         return v
+ 
+-    def genop(self, opname, args_v, resulttype=None):
++    def genop(self, opname, args_v, resulttype=None, oploc=None):
+         try:
+             for v in args_v:
+                 v.concretetype
+@@ -944,7 +944,7 @@
+                                  " and pass its result to genop(),"
+                                  " never hop.args_v directly.")
+         vresult = Variable()
+-        self.append(SpaceOperation(opname, args_v, vresult))
++        self.append(SpaceOperation(opname, args_v, vresult, oploc))
+         if resulttype is None:
+             vresult.concretetype = Void
+             return None
+diff -r cd083843b67a pypy/translator/backendopt/inline.py
+--- a/pypy/translator/backendopt/inline.py	Mon Dec 20 17:17:45 2010 +0100
++++ b/pypy/translator/backendopt/inline.py	Wed Jan 05 16:14:35 2011 -0500
+@@ -4,6 +4,7 @@
+ from pypy.translator.unsimplify import copyvar
+ from pypy.objspace.flow.model import Variable, Constant, Block, Link
+ from pypy.objspace.flow.model import SpaceOperation, c_last_exception
++from pypy.objspace.flow.model import OperationLoc
+ from pypy.objspace.flow.model import FunctionGraph
+ from pypy.objspace.flow.model import traverse, mkentrymap, checkgraph
+ from pypy.annotation import model as annmodel
+@@ -231,6 +232,7 @@
+         self.varmap = {}
+         self._copied_blocks = {}
+         self.op = block.operations[index_operation]
++        self.callsite_oploc = self.op.oploc
+         self.graph_to_inline = self.get_graph_from_op(self.op)
+         self.exception_guarded = False
+         if (block.exitswitch == c_last_exception and
+@@ -297,7 +299,9 @@
+         
+     def copy_operation(self, op):
+         args = [self.get_new_name(arg) for arg in op.args]
+-        result = SpaceOperation(op.opname, args, self.get_new_name(op.result))
++        new_oploc = OperationLoc(self.callsite_oploc.codelocs[:] + op.oploc.codelocs[:])
++        result = SpaceOperation(op.opname, args, self.get_new_name(op.result), 
++                                new_oploc)
+         return result
+ 
+     def copy_block(self, block):
+diff -r cd083843b67a pypy/translator/c/funcgen.py
+--- a/pypy/translator/c/funcgen.py	Mon Dec 20 17:17:45 2010 +0100
++++ b/pypy/translator/c/funcgen.py	Wed Jan 05 16:14:35 2011 -0500
+@@ -1,4 +1,6 @@
+ import sys
++import inspect
++import dis
+ from pypy.translator.c.support import USESLOTS # set to False if necessary while refactoring
+ from pypy.translator.c.support import cdecl
+ from pypy.translator.c.support import llvalue_from_constant, gen_assignments
+@@ -22,6 +24,38 @@
+ 
+ KEEP_INLINED_GRAPHS = False
+ 
++def block_comparator(blk0, blk1):
++    '''
++    Sort function for blocks, putting them in an ordering that attempts to
++    maximize readability of the generated C code
++    '''
++    # print 'comparing %r and %r' % (blk0, blk1)
++    # Put the start/end block at the top/bottom:
++    if blk0.isstartblock:
++        return -1
++
++    if blk1.isstartblock:
++        return 1
++
++    # Order blocks by the offset, where present:
++    if blk0.operations:
++        if blk1.operations:
++            return cmp(blk0.operations[0].oploc, blk1.operations[0].oploc)
++        else:
++            return -1
++    else:
++        if blk1.operations:
++            return 1
++        else:
++            return 0
++
++def escape_c_comments(py_src):
++    # Escape C comments within RPython source, to avoid generating bogus
++    # comments in our generated C source:
++    py_src = py_src.replace('/*', '')
++    py_src = py_src.replace('*/', '')
++    return py_src
++
+ class FunctionCodeGenerator(object):
+     """
+     Collects information about a function which we have to generate
+@@ -210,14 +244,57 @@
+ 
+     def cfunction_body(self):
+         graph = self.graph
+-        yield 'goto block0;'    # to avoid a warning "this label is not used"
++        # Try to print python source code:
++        if hasattr(graph, 'func'):
++            filename = inspect.getfile(graph.func)
++            #yield '/* name: %r */' % filename
++            try:
++                src, startline = inspect.getsourcelines(graph.func)
++            except IOError:
++                pass # No source found
++            except IndexError:
++                pass # Bulletproofing
++            else:
++                yield '/* Python source %r' % filename
++                for i, line in enumerate(src):
++                    line = line.rstrip()
++                    line = escape_c_comments(line)
++                    # FuncNode.funcgen_implementation treats lines ending in ':'
++                    # as C blocks, which messes up the formatting.
++                    # Work around this:
++                    if line.endswith(':'):
++                        line += ' '
++                    yield ' * %4d : %s' % (startline + i, line)
++                yield ' */'
++
++        label = graph.startblock.get_base_label(self.blocknum[graph.startblock])
++        yield 'goto %s;' % label # to avoid a warning "this label is not used"
++
++        # Sort the blocks into a (hopefully) readable order:
++        blocks = list(graph.iterblocks_by_source())
++        blocks.sort(block_comparator)
+ 
+         # generate the body of each block
+-        for block in graph.iterblocks():
++        for block in blocks:
++            cursrcloc = None
+             myblocknum = self.blocknum[block]
+             yield ''
+-            yield 'block%d:' % myblocknum
++            yield '%s:' % block.get_base_label(myblocknum)
++            #yield "/* repr(block): %r */" % (block, )
++            #yield "/* type(block): %r */" % (type(block), )
+             for i, op in enumerate(block.operations):
++                #yield "/* type(op): %r */" % (type(op), )
++                #yield "/* op.oploc: %s */" % (op.oploc, )
++                codeloc = op.oploc.codelocs[-1]
++                if codeloc:
++                    srcloc = codeloc.get_source_loc()
++                    if srcloc != cursrcloc:
++                        try:
++                            yield "/* %s:%d : %s */" % (codeloc.code.co_name, srcloc.linenum, escape_c_comments(srcloc.get_text()))
++                            cursrcloc = srcloc
++                        except IOError:
++                            pass
++
+                 for line in self.gen_op(op):
+                     yield line
+             if len(block.exits) == 0:
+@@ -310,7 +387,7 @@
+             assignments.append((a2typename, dest, src))
+         for line in gen_assignments(assignments):
+             yield line
+-        label = 'block%d' % self.blocknum[link.target]
++        label = link.target.get_base_label(self.blocknum[link.target])
+         if link.target in self.innerloops:
+             loop = self.innerloops[link.target]
+             if link is loop.links[-1]:   # link that ends a loop
+diff -r cd083843b67a pypy/translator/c/test/test_genc.py
+--- a/pypy/translator/c/test/test_genc.py	Mon Dec 20 17:17:45 2010 +0100
++++ b/pypy/translator/c/test/test_genc.py	Wed Jan 05 16:14:35 2011 -0500
+@@ -1,4 +1,5 @@
+ import autopath, sys, os, py
++import re
+ from pypy.rpython.lltypesystem.lltype import *
+ from pypy.annotation import model as annmodel
+ from pypy.translator.translator import TranslationContext
+@@ -498,3 +499,130 @@
+     else:
+         assert 0, "the call was not found in the C source"
+     assert 'PYPY_INHIBIT_TAIL_CALL();' in lines[i+1]
++
++def get_generated_c_source(fn, types):
++    # Return a (optimized fn, c source code, c source filename) 3-tuple
++    t = Translation(fn)
++    t.annotate(types)
++    c_filename_path = t.source_c()
++    h = c_filename_path.open()
++    src = h.read()
++    h.close()
++    c_fn = t.compile_c()
++    return (c_fn, src, c_filename_path)
++
++def extract_c_function(c_src, fname):
++    # Extract the source for a given C function out of a the given src string
++    # Makes assumptions about the layout of the source
++    pattern = '^(.+) \**%s\(.*\) {$' % fname
++    within_fn = False
++    result = ''
++    for line in c_src.splitlines():
++        if within_fn:
++            result += line + '\n'
++            if line.startswith('}'):
++                return result
++        else:
++            m = re.match(pattern, line)
++            if m:
++                within_fn = True
++                result += line + '\n'
++    return result
++    
++    
++
++def test_generated_c_source():
++    # Verify that generate C source "looks good"
++    # We'll use is_perfect_number, as it contains a loop and a conditional
++
++    # Generate C source code
++    from pypy.translator.test.snippet import is_perfect_number
++    c_fn, c_src, c_filename_path = get_generated_c_source(is_perfect_number,
++                                                        [int])
++
++    # Locate the C source for the type-specialized function:
++    c_fn_src = extract_c_function(c_src, 'pypy_g_is_perfect_number')
++    
++    # Verify that the C source contains embedded comments containing the lines
++    # of the python source:
++    expected_comment_lines = [
++        '/* is_perfect_number:31 :     while div < n: */',
++        '/* is_perfect_number:32 :         if n % div == 0: */',
++        '/* is_perfect_number:33 :             sum += div */',
++        '/* is_perfect_number:34 :         div += 1 */',
++        '/* is_perfect_number:35 :     return n == sum */']
++    for exp_line in expected_comment_lines:
++        assert exp_line in c_fn_src
++        
++    # Verify that the lines occur in the correct order
++    # ...we do this by filtering the function's generated C source to just
++    # those lines containing our comments (and dropping whitespace):
++    lines = c_fn_src.splitlines()
++    lines = [line.strip()
++             for line in lines
++             if '/* is_perfect_number:' in line]
++
++    # ...we should now have exact equality: the ordering should be as expected,
++    # and each comment should appear exactly once:
++    assert lines == expected_comment_lines
++
++    # Ensure that the generated C function does the right thing:
++    assert c_fn(5) == False
++    assert c_fn(6) == True
++    assert c_fn(7) == False
++
++    assert c_fn(5.0) == False
++    assert c_fn(6.0) == True
++    assert c_fn(7.0) == False
++
++    assert c_fn(5L) == False
++    assert c_fn(6L) == True
++    assert c_fn(7L) == False
++
++    try:
++        c_fn('hello world')
++    except:
++        pass
++    else:
++        raise 'Was expected exception'
++    
++def test_escaping_c_comments():
++    # Ensure that c comments within RPython code get escaped when we generate
++    # our .c code (to avoid generating bogus C)
++    # See e.g. pypy.module.cpyext.dictobject's PyDict_Next, which has a
++    # docstring embedding a C comment
++    def c_style_comment(a, b):
++        '''Here is a C-style comment within an RPython docstring:
++                /* hello world */
++        '''
++        # and here's one in a string literal:
++        return '/* hello world a:%s b:%s */' % (a, b)
++
++    def cplusplus_style_comment(a, b):
++        '''Here is a C++-style comment within an RPython docstring:
++                // hello world
++        '''
++        # and here are some in string literals, and one as the floor division
++        # operator:
++        return '// hello world: a // b = %s' % (a // b)
++
++    for fn_name, exp_output in [('c_style_comment',
++                                 '/* hello world a:6 b:3 */'),
++                                ('cplusplus_style_comment',
++                                 '// hello world: a // b = 2')]:
++        fn = locals()[fn_name]
++
++        c_fn, c_src, c_filename_path = get_generated_c_source(fn, [int, int])
++        # If the above survived, then the C compiler managed to handle
++        # the generated C code
++
++        # Verify that the generated code works (i.e. that we didn't
++        # accidentally change the meaning):
++        assert c_fn(6, 3) == exp_output
++
++        # Ensure that at least part of the docstrings made it into the C
++        # code:
++        c_fn_src = extract_c_function(c_src, 'pypy_g_' + fn_name)
++        assert 'Here is a ' in c_fn_src
++        assert 'style comment within an RPython docstring' in c_fn_src
++        
+diff -r cd083843b67a pypy/translator/driver.py
+--- a/pypy/translator/driver.py	Mon Dec 20 17:17:45 2010 +0100
++++ b/pypy/translator/driver.py	Wed Jan 05 16:14:35 2011 -0500
+@@ -539,6 +539,7 @@
+             dstname = self.compute_exe_name() + '.staticdata.info'
+             shutil.copy(str(fname), str(dstname))
+             self.log.info('Static data info written to %s' % dstname)
++        return c_source_filename
+ 
+     #
+     task_source_c = taskdef(task_source_c, ['database_c'], "Generating c source")
+diff -r cd083843b67a pypy/translator/gensupp.py
+--- a/pypy/translator/gensupp.py	Mon Dec 20 17:17:45 2010 +0100
++++ b/pypy/translator/gensupp.py	Wed Jan 05 16:14:35 2011 -0500
+@@ -16,8 +16,8 @@
+     def visit(block):
+         if isinstance(block, Block):
+             # first we order by offset in the code string
+-            if block.operations:
+-                ofs = block.operations[0].offset
++            if block.operations and block.operations[0].oploc.codelocs[0]:
++                ofs = block.operations[0].oploc.codelocs[0].offset
+             else:
+                 ofs = sys.maxint
+             # then we order by input variable name or value
+diff -r cd083843b67a pypy/translator/interactive.py
+--- a/pypy/translator/interactive.py	Mon Dec 20 17:17:45 2010 +0100
++++ b/pypy/translator/interactive.py	Wed Jan 05 16:14:35 2011 -0500
+@@ -138,7 +138,7 @@
+     def source_c(self, argtypes=None, **kwds):
+         self.update_options(argtypes, kwds)
+         self.ensure_backend('c')
+-        self.driver.source_c()
++        return self.driver.source_c()
+ 
+     def source_cl(self, argtypes=None, **kwds):
+         self.update_options(argtypes, kwds)
+diff -r cd083843b67a pypy/translator/llsupport/wrapper.py
+--- a/pypy/translator/llsupport/wrapper.py	Mon Dec 20 17:17:45 2010 +0100
++++ b/pypy/translator/llsupport/wrapper.py	Wed Jan 05 16:14:35 2011 -0500
+@@ -59,6 +59,8 @@
+     # "return result"
+     block = Block(wrapper_inputargs)
+     wgraph = FunctionGraph('pyfn_' + (newname or func.func_name), block)
++    if hasattr(graph, 'func'):
++        wgraph.func = graph.func
+     translator.update_call_graph(wgraph, graph, object())
+     translator.graphs.append(wgraph)
+     block.operations[:] = newops
+diff -r cd083843b67a pypy/translator/simplify.py
+--- a/pypy/translator/simplify.py	Mon Dec 20 17:17:45 2010 +0100
++++ b/pypy/translator/simplify.py	Wed Jan 05 16:14:35 2011 -0500
+@@ -294,7 +294,7 @@
+                 return renaming.get(v, v)
+             def rename_op(op):
+                 args = [rename(a) for a in op.args]
+-                op = SpaceOperation(op.opname, args, rename(op.result), op.offset)
++                op = SpaceOperation(op.opname, args, rename(op.result), op.oploc)
+                 # special case...
+                 if op.opname == 'indirect_call':
+                     if isinstance(op.args[0], Constant):
diff --git a/pypy.spec b/pypy.spec
index 96354e2..162ed5f 100644
--- a/pypy.spec
+++ b/pypy.spec
@@ -1,6 +1,6 @@
 Name:           pypy
 Version:        1.4.1
-Release:        3%{?dist}
+Release:        4%{?dist}
 Summary:        Python implementation with a Just-In-Time compiler
 
 Group:          Development/Languages
@@ -136,9 +136,33 @@ Patch2: fix-test_commands-expected-ls-output-issue7108.patch
 #   https://codespeak.net/issue/pypy-dev/issue614
 Patch3: pypy-1.4.1-add-LIBRARY_INSTALLATION_PATH.patch
 
+# Try to improve the readability of the generated .c code, by adding in the
+# RPython source as comments where possible.
+# A version of this was sent upstream as:
+#  http://codespeak.net/pipermail/pypy-dev/2010q4/006532.html
+# TODO: get this into the upstream bug tracker, and finish inlining
+# support (rhbz#666963)
+Patch4: pypy-1.4.1-more-readable-c-code.patch
+
+
 # Build-time requirements:
 
-BuildRequires:  python-devel
+# pypy's can be rebuilt using itself, rather than with CPython; doing so
+# halves the build time.
+#
+# Turn it off with this boolean, to revert back to rebuilding using CPython
+# and avoid a cycle in the build-time dependency graph:
+#
+%global use_self_when_building 1
+%if 0%{use_self_when_building}
+BuildRequires: pypy
+%global bootstrap_python_interp pypy
+%else
+BuildRequires: python-devel
+%global bootstrap_python_interp python
+%endif
+
+
 
 # FIXME: I'm seeing errors like this in the logs:
 #   [translation:WARNING] The module '_rawffi' is disabled
@@ -215,6 +239,8 @@ sed -i \
   -e 's|LIBRARY_INSTALLATION_PATH|"%{pypyprefix}"|' \
   pypy/translator/goal/app_main.py
 
+%patch4 -p1 -b .more-readable-c-code
+
 
 # Replace /usr/local/bin/python shebangs with /usr/bin/python:
 find -name "*.py" -exec \
@@ -304,6 +330,7 @@ BuildPyPy() {
   # doesn't interract well with the results of using our standard build flags.
   # For now, filter our CFLAGS of everything that could be conflicting with
   # pypy.  Need to check these and reenable ones that are okay later.
+  # Filed as https://bugzilla.redhat.com/show_bug.cgi?id=666966
   export CFLAGS=$(echo "$RPM_OPT_FLAGS" | sed -e 's/-Wp,-D_FORTIFY_SOURCE=2//' -e 's/-fexceptions//' -e 's/-fstack-protector//' -e 's/--param=ssp-buffer-size=4//' -e 's/-O2//' -e 's/-fasynchronous-unwind-tables//' -e 's/-march=i686//' -e 's/-mtune=atom//')
 
   # If we're already built the JIT-enabled "pypy", then use it for subsequent
@@ -311,7 +338,10 @@ BuildPyPy() {
   if test -x './pypy' ; then
     INTERP='./pypy'
   else
-    INTERP='python'
+    # First pypy build within this rpm build?
+    # Fall back to using the bootstrap python interpreter, which might be a
+    # system copy of pypy from an earlier rpm, or be cpython's /usr/bin/python:
+    INTERP='%{bootstrap_python_interp}'
   fi
 
   # Here's where we actually invoke the build:
@@ -484,6 +514,34 @@ mkdir -p %{buildroot}/%{pypyprefix}/site-packages
   %{buildroot}/%{_bindir}/pypy \
   0
 
+# Capture the RPython source code files from the build within the debuginfo
+# package (rhbz#666975)
+%global pypy_debuginfo_dir /usr/src/debug/pypy-%{version}-src
+mkdir -p %{buildroot}%{pypy_debuginfo_dir}
+
+# copy over everything:
+cp -a pypy %{buildroot}%{pypy_debuginfo_dir}
+
+# ...then delete files that aren't .py files:
+find \
+  %{buildroot}%{pypy_debuginfo_dir} \
+  \( -type f                        \
+     -a                             \
+     \! -name "*.py"                \
+  \)                                \
+  -delete
+
+# We don't need bytecode for these files; they are being included for reference
+# purposes.
+# There are some rpmlint warnings from these files:
+#   non-executable-script
+#   wrong-script-interpreter
+#   zero-length
+#   script-without-shebang
+#   dangling-symlink
+# but given that the objective is to preserve a copy of the source code, those
+# are acceptable.
+
 %check
 topdir=$(pwd)
 
@@ -511,6 +569,7 @@ CheckPyPy() {
 
     # Gather a list of tests to skip, due to known failures
     # TODO: report these failures to pypy upstream
+    # See also rhbz#666967 and rhbz#666969
     TESTS_TO_SKIP=""
 
     # Test failures relating to missing codecs
@@ -622,6 +681,14 @@ CheckPyPy() {
       #     AssertionError: ValueError not raised
       SkipTest test_zlib
 
+    %if 0%{use_self_when_building}
+    # Patch 3 prioritizes the installed copy of pypy's libraries over the
+    # build copy.
+    # This leads to test failures of test_pep263 and test_tarfile
+    # For now, suppress these when building using pypy itself:
+    SkipTest test_pep263   # on-disk encoding issues
+    SkipTest test_tarfile  # permissions issues
+    %endif
 
     # Run the built binary through the selftests:
     time ./$ExeName -m test.regrtest -x $TESTS_TO_SKIP
@@ -696,6 +763,14 @@ rm -rf $RPM_BUILD_ROOT
 
 
 %changelog
+* Wed Jan  5 2011 David Malcolm <dmalcolm at redhat.com> - 1.4.1-4
+- rebuild pypy using itself, for speed, with a boolean to break this cycle in
+the build-requirement graph (falling back to using "python-devel" aka CPython)
+- add work-in-progress patch to try to make generated c more readable
+(rhbz#666963)
+- capture the RPython source code files from the build within the debuginfo
+package (rhbz#666975)
+
 * Wed Dec 22 2010 David Malcolm <dmalcolm at redhat.com> - 1.4.1-3
 - try to respect the FHS by installing libraries below libdir, rather than
 datadir; patch app_main.py to look in this installation location first when


More information about the scm-commits mailing list