[pypy-svn] r26136 - in pypy/dist/pypy/rpython/rctypes/tool: . test

arigo at codespeak.net arigo at codespeak.net
Sat Apr 22 13:31:36 CEST 2006


Author: arigo
Date: Sat Apr 22 13:31:35 2006
New Revision: 26136

Modified:
   pypy/dist/pypy/rpython/rctypes/tool/ctypes_platform.py
   pypy/dist/pypy/rpython/rctypes/tool/test/test_ctypes_platform.py
Log:
An easy refactoring of ctypes_platform.py to allow many queries -- a
whole configuration -- to be performed in a single pass through the C
compiler.  This also gives a natural place to put some new general
settings, like _include_dirs_ for a list of include dirs to pass to the
compiler.



Modified: pypy/dist/pypy/rpython/rctypes/tool/ctypes_platform.py
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/tool/ctypes_platform.py	(original)
+++ pypy/dist/pypy/rpython/rctypes/tool/ctypes_platform.py	Sat Apr 22 13:31:35 2006
@@ -5,6 +5,241 @@
 from pypy.translator.tool.cbuild import build_executable
 from pypy.tool.udir import udir
 
+# ____________________________________________________________
+#
+# Helpers for simple cases
+
+def getstruct(name, c_header_source, interesting_fields):
+    class CConfig:
+        _header_ = c_header_source
+        STRUCT = Struct(name, interesting_fields)
+    return configure(CConfig)['STRUCT']
+
+def getsimpletype(name, c_header_source, ctype_hint=ctypes.c_int):
+    class CConfig:
+        _header_ = c_header_source
+        TYPE = SimpleType(name, ctype_hint)
+    return configure(CConfig)['TYPE']
+
+def getconstantinteger(name, c_header_source):
+    class CConfig:
+        _header_ = c_header_source
+        CONST = ConstantInteger(name)
+    return configure(CConfig)['CONST']
+
+def getdefined(macro, c_header_source):
+    class CConfig:
+        _header_ = c_header_source
+        DEFINED = Defined(macro)
+    return configure(CConfig)['DEFINED']
+
+# ____________________________________________________________
+#
+# General interface
+
+def configure(CConfig):
+    """Examine the local system by running the C compiler.
+    The CConfig class contains CConfigEntry attribues that describe
+    what should be inspected; configure() returns a dict mapping
+    names to the results.
+    """
+    entries = []
+    for key in dir(CConfig):
+        value = getattr(CConfig, key)
+        if isinstance(value, CConfigEntry):
+            entries.append((key, value))
+
+    filepath = uniquefilepath()
+    f = filepath.open('w')
+    print >> f, C_HEADER
+    print >> f
+    print >> f, CConfig._header_    # mandatory
+    print >> f
+    for key, entry in entries:
+        print >> f, 'void dump_section_%s(void) {' % (key,)
+        print >> f, '\tprintf("-+- %s\\n");' % (key,)
+        for line in entry.prepare_code():
+            if line and line[0] != '#':
+                line = '\t' + line
+            print >> f, line
+        print >> f, '\tprintf("---\\n");'
+        print >> f, '}'
+        print >> f
+
+    print >> f, 'int main(void) {'
+    for key, entry in entries:
+        print >> f, '\tdump_section_%s();' % (key,)
+    print >> f, '\treturn 0;'
+    print >> f, '}'
+    f.close()
+
+    include_dirs = getattr(CConfig, '_include_dirs_', [])
+    infolist = list(run_example_code(filepath, include_dirs))
+    assert len(infolist) == len(entries)
+
+    result = {}
+    for info, (key, entry) in zip(infolist, entries):
+        result[key] = entry.build_result(info)
+    return result
+
+# ____________________________________________________________
+
+
+class CConfigEntry(object):
+    "Abstract base class."
+
+
+class Struct(CConfigEntry):
+    """An entry in a CConfig class that stands for an externally
+    defined structure.
+    """
+    def __init__(self, name, interesting_fields):
+        self.name = name
+        self.interesting_fields = interesting_fields
+
+    def prepare_code(self):
+        yield 'typedef %s ctypesplatcheck_t;' % (self.name,)
+        yield 'typedef struct {'
+        yield '    char c;'
+        yield '    ctypesplatcheck_t s;'
+        yield '} ctypesplatcheck2_t;'
+        yield ''
+        yield 'ctypesplatcheck_t s;'
+        yield 'dump("align", offsetof(ctypesplatcheck2_t, s));'
+        yield 'dump("size",  sizeof(ctypesplatcheck_t));'
+        for fieldname, fieldtype in self.interesting_fields:
+            yield 'dump("fldofs %s", offsetof(ctypesplatcheck_t, %s));'%(
+                fieldname, fieldname)
+            yield 'dump("fldsize %s",   sizeof(s.%s));' % (
+                fieldname, fieldname)
+            if fieldtype in integer_class:
+                yield 's.%s = 0; s.%s = ~s.%s;' % (fieldname,
+                                                   fieldname,
+                                                   fieldname)
+                yield 'dump("fldunsigned %s", s.%s > 0);' % (fieldname,
+                                                             fieldname)
+
+    def build_result(self, info):
+        alignment = 1
+        layout = [None] * info['size']
+        for fieldname, fieldtype in self.interesting_fields:
+            offset = info['fldofs '  + fieldname]
+            size   = info['fldsize ' + fieldname]
+            sign   = info.get('fldunsigned ' + fieldname, False)
+            if (size, sign) != size_and_sign(fieldtype):
+                fieldtype = fixup_ctype(fieldtype, fieldname, (size, sign))
+            layout_addfield(layout, offset, fieldtype, fieldname)
+            alignment = max(alignment, ctypes.alignment(fieldtype))
+
+        # try to enforce the same alignment as the one of the original
+        # structure
+        if alignment < info['align']:
+            choices = [ctype for ctype in alignment_types
+                             if ctypes.alignment(ctype) == info['align']]
+            assert choices, "unsupported alignment %d" % (info['align'],)
+            choices = [(ctypes.sizeof(ctype), i, ctype)
+                       for i, ctype in enumerate(choices)]
+            csize, _, ctype = min(choices)
+            for i in range(0, info['size'] - csize + 1, info['align']):
+                if layout[i:i+csize] == [None] * csize:
+                    layout_addfield(layout, i, ctype, '_alignment')
+                    break
+            else:
+                raise AssertionError("unenforceable alignment %d" % (
+                    info['align'],))
+
+        n = 0
+        for i, cell in enumerate(layout):
+            if cell is not None:
+                continue
+            layout_addfield(layout, i, ctypes.c_char, '_pad%d' % (n,))
+            n += 1
+
+        # build the ctypes Structure
+        seen = {}
+        fields = []
+        for cell in layout:
+            if cell in seen:
+                continue
+            fields.append((cell.name, cell.ctype))
+            seen[cell] = True
+
+        class S(ctypes.Structure):
+            _fields_ = fields
+        name = self.name
+        if name.startswith('struct '):
+            name = name[7:]
+        S.__name__ = name
+        return S
+
+
+class SimpleType(CConfigEntry):
+    """An entry in a CConfig class that stands for an externally
+    defined simple numeric type.
+    """
+    def __init__(self, name, ctype_hint=ctypes.c_int):
+        self.name = name
+        self.ctype_hint = ctype_hint
+
+    def prepare_code(self):
+        yield 'typedef %s ctypesplatcheck_t;' % (self.name,)
+        yield ''
+        yield 'ctypesplatcheck_t x;'
+        yield 'dump("size",  sizeof(ctypesplatcheck_t));'
+        if self.ctype_hint in integer_class:
+            yield 'x = 0; x = ~x;'
+            yield 'dump("unsigned", x > 0);'
+
+    def build_result(self, info):
+        size = info['size']
+        sign = info.get('unsigned', False)
+        ctype = self.ctype_hint
+        if (size, sign) != size_and_sign(ctype):
+            ctype = fixup_ctype(ctype, self.name, (size, sign))
+        return ctype
+
+
+class ConstantInteger(CConfigEntry):
+    """An entry in a CConfig class that stands for an externally
+    defined integer constant.
+    """
+    def __init__(self, name):
+        self.name = name
+
+    def prepare_code(self):
+        yield '    if ((%s) < 0) {' % (self.name,)
+        yield '        long long x = (long long)(%s);' % (self.name,)
+        yield '        printf("value: %lld\\n", x);'
+        yield '    } else {'
+        yield '        unsigned long long x = (unsigned long long)(%s);' % (
+                            self.name,)
+        yield '        printf("value: %llu\\n", x);'
+        yield '    }'
+
+    def build_result(self, info):
+        return info['value']
+
+
+class Defined(CConfigEntry):
+    """A boolean, corresponding to an #ifdef.
+    """
+    def __init__(self, macro):
+        self.macro = macro
+        self.name = macro
+
+    def prepare_code(self):
+        yield '#ifdef %s' % (self.macro,)
+        yield 'dump("defined", 1);'
+        yield '#else'
+        yield 'dump("defined", 0);'
+        yield '#endif'
+
+    def build_result(self, info):
+        return bool(info['defined'])
+
+# ____________________________________________________________
+#
+# internal helpers
 
 def uniquefilepath(LAST=[0]):
     i = LAST[0]
@@ -75,157 +310,24 @@
 }
 """
 
-def run_example_code(filepath):
-    executable = build_executable([filepath])
+def run_example_code(filepath, include_dirs=[]):
+    executable = build_executable([filepath], include_dirs=include_dirs)
     output = py.process.cmdexec(executable)
-    info = {}
+    section = None
     for line in output.splitlines():
-        key, value = line.strip().split(': ')
-        info[key] = int(value)
-    return info
-
-def getstruct(name, c_header_source, interesting_fields):
-    filepath = uniquefilepath()
-    f = filepath.open('w')
-    print >> f, C_HEADER
-    print >> f
-    print >> f, c_header_source
-    print >> f
-    print >> f, 'typedef %s ctypesplatcheck_t;' % (name,)
-    print >> f, 'typedef struct {'
-    print >> f, '    char c;'
-    print >> f, '    ctypesplatcheck_t s;'
-    print >> f, '} ctypesplatcheck2_t;'
-    print >> f
-    print >> f, 'int main(void) {'
-    print >> f, '    ctypesplatcheck_t s;'
-    print >> f, '    dump("align", offsetof(ctypesplatcheck2_t, s));'
-    print >> f, '    dump("size",  sizeof(ctypesplatcheck_t));'
-    for fieldname, fieldtype in interesting_fields:
-        print >> f, '    dump("fldofs %s", offsetof(ctypesplatcheck_t, %s));'%(
-            fieldname, fieldname)
-        print >> f, '    dump("fldsize %s",   sizeof(s.%s));' % (
-            fieldname, fieldname)
-        if fieldtype in integer_class:
-            print >> f, '    s.%s = 0; s.%s = ~s.%s;' % (fieldname,
-                                                         fieldname,
-                                                         fieldname)
-            print >> f, '    dump("fldunsigned %s", s.%s > 0);' % (fieldname,
-                                                                   fieldname)
-    print >> f, '    return 0;'
-    print >> f, '}'
-    f.close()
-
-    info = run_example_code(filepath)
-
-    alignment = 1
-    layout = [None] * info['size']
-    for fieldname, fieldtype in interesting_fields:
-        offset = info['fldofs '  + fieldname]
-        size   = info['fldsize ' + fieldname]
-        sign   = info.get('fldunsigned ' + fieldname, False)
-        if (size, sign) != size_and_sign(fieldtype):
-            fieldtype = fixup_ctype(fieldtype, fieldname, (size, sign))
-        layout_addfield(layout, offset, fieldtype, fieldname)
-        alignment = max(alignment, ctypes.alignment(fieldtype))
-
-    # try to enforce the same alignment as the one of the original structure
-    if alignment < info['align']:
-        choices = [ctype for ctype in alignment_types
-                         if ctypes.alignment(ctype) == info['align']]
-        assert choices, "unsupported alignment %d" % (info['align'],)
-        choices = [(ctypes.sizeof(ctype), i, ctype)
-                   for i, ctype in enumerate(choices)]
-        csize, _, ctype = min(choices)
-        for i in range(0, info['size'] - csize + 1, info['align']):
-            if layout[i:i+csize] == [None] * csize:
-                layout_addfield(layout, i, ctype, '_alignment')
-                break
-        else:
-            raise AssertionError("unenforceable alignment %d" % (
-                info['align'],))
-
-    n = 0
-    for i, cell in enumerate(layout):
-        if cell is not None:
-            continue
-        layout_addfield(layout, i, ctypes.c_char, '_pad%d' % (n,))
-        n += 1
-
-    # build the ctypes Structure
-    seen = {}
-    fields = []
-    for cell in layout:
-        if cell in seen:
-            continue
-        fields.append((cell.name, cell.ctype))
-        seen[cell] = True
-
-    class S(ctypes.Structure):
-        _fields_ = fields
-    if name.startswith('struct '):
-        name = name[7:]
-    S.__name__ = name
-    return S
-
-
-def getsimpletype(name, c_header_source, ctype_hint):
-    filepath = uniquefilepath()
-    f = filepath.open('w')
-    print >> f, C_HEADER
-    print >> f
-    print >> f, c_header_source
-    print >> f
-    print >> f, 'typedef %s ctypesplatcheck_t;' % (name,)
-    print >> f
-    print >> f, 'int main(void) {'
-    print >> f, '    ctypesplatcheck_t x;'
-    print >> f, '    dump("size",  sizeof(ctypesplatcheck_t));'
-    if ctype_hint in integer_class:
-        print >> f, '    x = 0; x = ~x;'
-        print >> f, '    dump("unsigned", x > 0);'
-    print >> f, '    return 0;'
-    print >> f, '}'
-    f.close()
-
-    info = run_example_code(filepath)
-
-    size = info['size']
-    sign = info.get('unsigned', False)
-    if (size, sign) != size_and_sign(ctype_hint):
-        ctype_hint = fixup_ctype(ctype_hint, name, (size, sign))
-    return ctype_hint
-
-
-def getconstantinteger(name, c_header_source):
-    filepath = uniquefilepath()
-    f = filepath.open('w')
-    print >> f, C_HEADER
-    print >> f
-    print >> f, c_header_source
-    print >> f
-    print >> f, 'int main(void) {'
-    print >> f, '    if ((%s) < 0) {' % (name,)
-    print >> f, '        long long x = (long long)(%s);' % (name,)
-    print >> f, '        printf("value: %lld\\n", x);'
-    print >> f, '    } else {'
-    print >> f, '        unsigned long long x = (unsigned long long)(%s);' % (
-                            name,)
-    print >> f, '        printf("value: %llu\\n", x);'
-    print >> f, '    }'
-    print >> f, '    return 0;'
-    print >> f, '}'
-    f.close()
-
-    info = run_example_code(filepath)
-
-    return info['value']
-
-
-def getdefined(name, c_header_source):
-    return getconstantinteger('\n#ifdef %s\n1\n#else\n0\n#endif\n' % (name),
-                              c_header_source)
+        line = line.strip()
+        if line.startswith('-+- '):      # start of a new section
+            section = {}
+        elif line == '---':              # section end
+            assert section is not None
+            yield section
+            section = None
+        elif line:
+            assert section is not None
+            key, value = line.split(': ')
+            section[key] = int(value)
 
+# ____________________________________________________________
 
 if __name__ == '__main__':
     doc = """Example:

Modified: pypy/dist/pypy/rpython/rctypes/tool/test/test_ctypes_platform.py
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/tool/test/test_ctypes_platform.py	(original)
+++ pypy/dist/pypy/rpython/rctypes/tool/test/test_ctypes_platform.py	Sat Apr 22 13:31:35 2006
@@ -1,5 +1,6 @@
 import py, sys, struct
 from pypy.rpython.rctypes.tool import ctypes_platform
+from pypy.tool.udir import udir
 import ctypes
 
 
@@ -102,3 +103,24 @@
     res = ctypes_platform.getdefined('ALFKJLKJFLKJFKLEJDLKEWMECEE',
                                      '#define ALFKJLKJFLKJFKLEJDLKEWMECEE')
     assert res
+
+def test_configure():
+    test_h = udir.join('test_ctypes_platform.h')
+    test_h.write('#define XYZZY 42\n')
+
+    class CConfig:
+        _header_ = """ /* a C comment */
+                       #include <stdio.h>
+                       #include <test_ctypes_platform.h>
+                   """
+        _include_dirs_ = [str(udir)]
+
+        FILE = ctypes_platform.Struct('FILE', [])
+        ushort = ctypes_platform.SimpleType('unsigned short')
+        XYZZY = ctypes_platform.ConstantInteger('XYZZY')
+
+    res = ctypes_platform.configure(CConfig)
+    assert issubclass(res['FILE'], ctypes.Structure)
+    assert res == {'FILE': res['FILE'],
+                   'ushort': ctypes.c_ushort,
+                   'XYZZY': 42}



More information about the Pypy-commit mailing list