~ubuntu-branches/ubuntu/karmic/pypy/karmic

« back to all changes in this revision

Viewing changes to pypy/rpython/rctypes/tool/ctypes_platform.py

  • Committer: Bazaar Package Importer
  • Author(s): Alexandre Fayolle
  • Date: 2007-04-13 09:33:09 UTC
  • Revision ID: james.westby@ubuntu.com-20070413093309-yoojh4jcoocu2krz
Tags: upstream-1.0.0
ImportĀ upstreamĀ versionĀ 1.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/env python
 
2
 
 
3
import os, py, sys
 
4
import ctypes
 
5
from pypy.translator.tool.cbuild import build_executable
 
6
from pypy.tool.udir import udir
 
7
 
 
8
# ____________________________________________________________
 
9
#
 
10
# Helpers for simple cases
 
11
 
 
12
def getstruct(name, c_header_source, interesting_fields):
 
13
    class CConfig:
 
14
        _header_ = c_header_source
 
15
        STRUCT = Struct(name, interesting_fields)
 
16
    return configure(CConfig)['STRUCT']
 
17
 
 
18
def getsimpletype(name, c_header_source, ctype_hint=ctypes.c_int):
 
19
    class CConfig:
 
20
        _header_ = c_header_source
 
21
        TYPE = SimpleType(name, ctype_hint)
 
22
    return configure(CConfig)['TYPE']
 
23
 
 
24
def getconstantinteger(name, c_header_source):
 
25
    class CConfig:
 
26
        _header_ = c_header_source
 
27
        CONST = ConstantInteger(name)
 
28
    return configure(CConfig)['CONST']
 
29
 
 
30
def getdefined(macro, c_header_source):
 
31
    class CConfig:
 
32
        _header_ = c_header_source
 
33
        DEFINED = Defined(macro)
 
34
    return configure(CConfig)['DEFINED']
 
35
 
 
36
# ____________________________________________________________
 
37
#
 
38
# General interface
 
39
 
 
40
class ConfigResult:
 
41
    def __init__(self, CConfig, info, entries):
 
42
        self.CConfig = CConfig
 
43
        self.result = {}
 
44
        self.info = info
 
45
        self.entries = entries
 
46
        
 
47
    def get_entry_result(self, entry):
 
48
        try:
 
49
            return self.result[entry]
 
50
        except KeyError:
 
51
            pass
 
52
        name = self.entries[entry]
 
53
        info = self.info[name]
 
54
        self.result[entry] = entry.build_result(info, self)
 
55
 
 
56
    def get_result(self):
 
57
        return dict([(name, self.result[entry])
 
58
                     for entry, name in self.entries.iteritems()])
 
59
    
 
60
def configure(CConfig):
 
61
    """Examine the local system by running the C compiler.
 
62
    The CConfig class contains CConfigEntry attribues that describe
 
63
    what should be inspected; configure() returns a dict mapping
 
64
    names to the results.
 
65
    """
 
66
    entries = []
 
67
    for key in dir(CConfig):
 
68
        value = getattr(CConfig, key)
 
69
        if isinstance(value, CConfigEntry):
 
70
            entries.append((key, value))
 
71
 
 
72
    filepath = uniquefilepath()
 
73
    f = filepath.open('w')
 
74
    print >> f, C_HEADER
 
75
    print >> f
 
76
    for path in getattr(CConfig, '_includes_', ()):   # optional
 
77
        print >> f, '#include <%s>' % (path,)
 
78
    print >> f, getattr(CConfig, '_header_', '')      # optional
 
79
    print >> f
 
80
    for key, entry in entries:
 
81
        print >> f, 'void dump_section_%s(void) {' % (key,)
 
82
        for line in entry.prepare_code():
 
83
            if line and line[0] != '#':
 
84
                line = '\t' + line
 
85
            print >> f, line
 
86
        print >> f, '}'
 
87
        print >> f
 
88
 
 
89
    print >> f, 'int main(void) {'
 
90
    for key, entry in entries:
 
91
        print >> f, '\tprintf("-+- %s\\n");' % (key,)
 
92
        print >> f, '\tdump_section_%s();' % (key,)
 
93
        print >> f, '\tprintf("---\\n");'
 
94
    print >> f, '\treturn 0;'
 
95
    print >> f, '}'
 
96
    f.close()
 
97
 
 
98
    include_dirs = getattr(CConfig, '_include_dirs_', [])
 
99
    infolist = list(run_example_code(filepath, include_dirs))
 
100
    assert len(infolist) == len(entries)
 
101
 
 
102
    resultinfo = {}
 
103
    resultentries = {}
 
104
    for info, (key, entry) in zip(infolist, entries):
 
105
        resultinfo[key] = info
 
106
        resultentries[entry] = key
 
107
        
 
108
    result = ConfigResult(CConfig, resultinfo, resultentries)
 
109
    for name, entry in entries:
 
110
        result.get_entry_result(entry)
 
111
    return result.get_result()
 
112
 
 
113
# ____________________________________________________________
 
114
 
 
115
 
 
116
class CConfigEntry(object):
 
117
    "Abstract base class."
 
118
 
 
119
 
 
120
class Struct(CConfigEntry):
 
121
    """An entry in a CConfig class that stands for an externally
 
122
    defined structure.
 
123
    """
 
124
    def __init__(self, name, interesting_fields, ifdef=None):
 
125
        self.name = name
 
126
        self.interesting_fields = interesting_fields
 
127
        self.ifdef = ifdef
 
128
 
 
129
    def prepare_code(self):
 
130
        if self.ifdef is not None:
 
131
            yield '#ifdef %s' % (self.ifdef,)
 
132
            yield 'dump("defined", 1);'
 
133
        yield 'typedef %s ctypesplatcheck_t;' % (self.name,)
 
134
        yield 'typedef struct {'
 
135
        yield '    char c;'
 
136
        yield '    ctypesplatcheck_t s;'
 
137
        yield '} ctypesplatcheck2_t;'
 
138
        yield ''
 
139
        yield 'ctypesplatcheck_t s;'
 
140
        yield 'dump("align", offsetof(ctypesplatcheck2_t, s));'
 
141
        yield 'dump("size",  sizeof(ctypesplatcheck_t));'
 
142
        for fieldname, fieldtype in self.interesting_fields:
 
143
            yield 'dump("fldofs %s", offsetof(ctypesplatcheck_t, %s));'%(
 
144
                fieldname, fieldname)
 
145
            yield 'dump("fldsize %s",   sizeof(s.%s));' % (
 
146
                fieldname, fieldname)
 
147
            if fieldtype in integer_class:
 
148
                yield 's.%s = 0; s.%s = ~s.%s;' % (fieldname,
 
149
                                                   fieldname,
 
150
                                                   fieldname)
 
151
                yield 'dump("fldunsigned %s", s.%s > 0);' % (fieldname,
 
152
                                                             fieldname)
 
153
        if self.ifdef is not None:
 
154
            yield '#else'
 
155
            yield 'dump("defined", 0);'
 
156
            yield '#endif'
 
157
 
 
158
    def build_result(self, info, config_result):
 
159
        if self.ifdef is not None:
 
160
            if not info['defined']:
 
161
                return None
 
162
        alignment = 1
 
163
        layout = [None] * info['size']
 
164
        for fieldname, fieldtype in self.interesting_fields:
 
165
            if isinstance(fieldtype, Struct):
 
166
                offset = info['fldofs '  + fieldname]
 
167
                size   = info['fldsize ' + fieldname]
 
168
                c_fieldtype = config_result.get_entry_result(fieldtype)
 
169
                layout_addfield(layout, offset, c_fieldtype, fieldname)
 
170
                alignment = max(alignment, ctype_alignment(c_fieldtype))
 
171
            else:
 
172
                offset = info['fldofs '  + fieldname]
 
173
                size   = info['fldsize ' + fieldname]
 
174
                sign   = info.get('fldunsigned ' + fieldname, False)
 
175
                if (size, sign) != size_and_sign(fieldtype):
 
176
                    fieldtype = fixup_ctype(fieldtype, fieldname, (size, sign))
 
177
                layout_addfield(layout, offset, fieldtype, fieldname)
 
178
                alignment = max(alignment, ctype_alignment(fieldtype))
 
179
 
 
180
        # try to enforce the same alignment as the one of the original
 
181
        # structure
 
182
        if alignment < info['align']:
 
183
            choices = [ctype for ctype in alignment_types
 
184
                             if ctype_alignment(ctype) == info['align']]
 
185
            assert choices, "unsupported alignment %d" % (info['align'],)
 
186
            choices = [(ctypes.sizeof(ctype), i, ctype)
 
187
                       for i, ctype in enumerate(choices)]
 
188
            csize, _, ctype = min(choices)
 
189
            for i in range(0, info['size'] - csize + 1, info['align']):
 
190
                if layout[i:i+csize] == [None] * csize:
 
191
                    layout_addfield(layout, i, ctype, '_alignment')
 
192
                    break
 
193
            else:
 
194
                raise AssertionError("unenforceable alignment %d" % (
 
195
                    info['align'],))
 
196
 
 
197
        n = 0
 
198
        for i, cell in enumerate(layout):
 
199
            if cell is not None:
 
200
                continue
 
201
            layout_addfield(layout, i, ctypes.c_char, '_pad%d' % (n,))
 
202
            n += 1
 
203
 
 
204
        # build the ctypes Structure
 
205
        seen = {}
 
206
        fields = []
 
207
        for cell in layout:
 
208
            if cell in seen:
 
209
                continue
 
210
            fields.append((cell.name, cell.ctype))
 
211
            seen[cell] = True
 
212
 
 
213
        class S(ctypes.Structure):
 
214
            _fields_ = fields
 
215
        name = self.name
 
216
        if name.startswith('struct '):
 
217
            name = name[7:]
 
218
        S.__name__ = name
 
219
        return S
 
220
 
 
221
 
 
222
class SimpleType(CConfigEntry):
 
223
    """An entry in a CConfig class that stands for an externally
 
224
    defined simple numeric type.
 
225
    """
 
226
    def __init__(self, name, ctype_hint=ctypes.c_int, ifdef=None):
 
227
        self.name = name
 
228
        self.ctype_hint = ctype_hint
 
229
        self.ifdef = ifdef
 
230
        
 
231
    def prepare_code(self):
 
232
        if self.ifdef is not None:
 
233
            yield '#ifdef %s' % (self.ifdef,)
 
234
            yield 'dump("defined", 1);'
 
235
        yield 'typedef %s ctypesplatcheck_t;' % (self.name,)
 
236
        yield ''
 
237
        yield 'ctypesplatcheck_t x;'
 
238
        yield 'dump("size",  sizeof(ctypesplatcheck_t));'
 
239
        if self.ctype_hint in integer_class:
 
240
            yield 'x = 0; x = ~x;'
 
241
            yield 'dump("unsigned", x > 0);'
 
242
        if self.ifdef is not None:
 
243
            yield '#else'
 
244
            yield 'dump("defined", 0);'
 
245
            yield '#endif'
 
246
 
 
247
    def build_result(self, info, config_result):
 
248
        if self.ifdef is not None and not info['defined']:
 
249
            return None
 
250
        size = info['size']
 
251
        sign = info.get('unsigned', False)
 
252
        ctype = self.ctype_hint
 
253
        if (size, sign) != size_and_sign(ctype):
 
254
            ctype = fixup_ctype(ctype, self.name, (size, sign))
 
255
        return ctype
 
256
 
 
257
 
 
258
class ConstantInteger(CConfigEntry):
 
259
    """An entry in a CConfig class that stands for an externally
 
260
    defined integer constant.
 
261
    """
 
262
    def __init__(self, name):
 
263
        self.name = name
 
264
 
 
265
    def prepare_code(self):
 
266
        yield 'if ((%s) < 0) {' % (self.name,)
 
267
        yield '    long long x = (long long)(%s);' % (self.name,)
 
268
        yield '    printf("value: %lld\\n", x);'
 
269
        yield '} else {'
 
270
        yield '    unsigned long long x = (unsigned long long)(%s);' % (
 
271
                        self.name,)
 
272
        yield '    printf("value: %llu\\n", x);'
 
273
        yield '}'
 
274
 
 
275
    def build_result(self, info, config_result):
 
276
        return info['value']
 
277
 
 
278
class DefinedConstantInteger(CConfigEntry):
 
279
    """An entry in a CConfig class that stands for an externally
 
280
    defined integer constant. If not #defined the value will be None.
 
281
    """
 
282
    def __init__(self, macro):
 
283
        self.name = self.macro = macro
 
284
 
 
285
    def prepare_code(self):
 
286
        yield '#ifdef %s' % self.macro
 
287
        yield 'dump("defined", 1);'
 
288
        yield 'if ((%s) < 0) {' % (self.macro,)
 
289
        yield '    long long x = (long long)(%s);' % (self.macro,)
 
290
        yield '    printf("value: %lld\\n", x);'
 
291
        yield '} else {'
 
292
        yield '    unsigned long long x = (unsigned long long)(%s);' % (
 
293
                        self.macro,)
 
294
        yield '    printf("value: %llu\\n", x);'
 
295
        yield '}'
 
296
        yield '#else'
 
297
        yield 'dump("defined", 0);'
 
298
        yield '#endif'
 
299
 
 
300
    def build_result(self, info, config_result):
 
301
        if info["defined"]:
 
302
            return info['value']
 
303
        return None
 
304
 
 
305
 
 
306
class DefinedConstantString(CConfigEntry):
 
307
    """
 
308
    """
 
309
    def __init__(self, macro):
 
310
        self.macro = macro
 
311
        self.name = macro
 
312
 
 
313
    def prepare_code(self):
 
314
        yield '#ifdef %s' % self.macro
 
315
        yield 'int i;'
 
316
        yield 'char *p = %s;' % self.macro
 
317
        yield 'dump("defined", 1);'
 
318
        yield 'for (i = 0; p[i] != 0; i++ ) {'
 
319
        yield '  printf("value_%d: %d\\n", i, (int)(unsigned char)p[i]);'
 
320
        yield '}'
 
321
        yield '#else'
 
322
        yield 'dump("defined", 0);'
 
323
        yield '#endif'
 
324
 
 
325
    def build_result(self, info, config_result):
 
326
        if info["defined"]:
 
327
            string = ''
 
328
            d = 0
 
329
            while info.has_key('value_%d' % d):
 
330
                string += chr(info['value_%d' % d])
 
331
                d += 1
 
332
            return string
 
333
        return None
 
334
 
 
335
 
 
336
class Defined(CConfigEntry):
 
337
    """A boolean, corresponding to an #ifdef.
 
338
    """
 
339
    def __init__(self, macro):
 
340
        self.macro = macro
 
341
        self.name = macro
 
342
 
 
343
    def prepare_code(self):
 
344
        yield '#ifdef %s' % (self.macro,)
 
345
        yield 'dump("defined", 1);'
 
346
        yield '#else'
 
347
        yield 'dump("defined", 0);'
 
348
        yield '#endif'
 
349
 
 
350
    def build_result(self, info, config_result):
 
351
        return bool(info['defined'])
 
352
 
 
353
 
 
354
class Library(CConfigEntry):
 
355
    """The loaded CTypes library object.
 
356
    """
 
357
    def __init__(self, name):
 
358
        self.name = name
 
359
 
 
360
    def prepare_code(self):
 
361
        # XXX should check that we can link against the lib
 
362
        return []
 
363
 
 
364
    def build_result(self, info, config_result):
 
365
        from pypy.rpython.rctypes.tool import util
 
366
        path = util.find_library(self.name)
 
367
        mylib = ctypes.cdll.LoadLibrary(path)
 
368
 
 
369
        class _FuncPtr(ctypes._CFuncPtr):
 
370
            _flags_ = ctypes._FUNCFLAG_CDECL
 
371
            _restype_ = ctypes.c_int # default, can be overridden in instances
 
372
            includes = tuple(config_result.CConfig._includes_)
 
373
            libraries = (self.name,)
 
374
        
 
375
        mylib._FuncPtr = _FuncPtr
 
376
        return mylib
 
377
 
 
378
# ____________________________________________________________
 
379
#
 
380
# internal helpers
 
381
 
 
382
def ctype_alignment(c_type):
 
383
    if issubclass(c_type, ctypes.Structure):
 
384
        return max([ctype_alignment(fld_type)
 
385
                     for fld_name, fld_type in c_type._fields_])
 
386
    
 
387
    return ctypes.alignment(c_type)
 
388
 
 
389
def uniquefilepath(LAST=[0]):
 
390
    i = LAST[0]
 
391
    LAST[0] += 1
 
392
    return udir.join('ctypesplatcheck_%d.c' % i)
 
393
 
 
394
alignment_types = [
 
395
    ctypes.c_short,
 
396
    ctypes.c_int,
 
397
    ctypes.c_long,
 
398
    ctypes.c_float,
 
399
    ctypes.c_double,
 
400
    ctypes.c_char_p,
 
401
    ctypes.c_void_p,
 
402
    ctypes.c_longlong,
 
403
    ctypes.c_wchar,
 
404
    ctypes.c_wchar_p,
 
405
    ]
 
406
 
 
407
integer_class = [ctypes.c_byte,     ctypes.c_ubyte,
 
408
                 ctypes.c_short,    ctypes.c_ushort,
 
409
                 ctypes.c_int,      ctypes.c_uint,
 
410
                 ctypes.c_long,     ctypes.c_ulong,
 
411
                 ctypes.c_longlong, ctypes.c_ulonglong,
 
412
                 ]
 
413
float_class = [ctypes.c_float, ctypes.c_double]
 
414
 
 
415
class Field(object):
 
416
    def __init__(self, name, ctype):
 
417
        self.name = name
 
418
        self.ctype = ctype
 
419
    def __repr__(self):
 
420
        return '<field %s: %s>' % (self.name, self.ctype)
 
421
 
 
422
def layout_addfield(layout, offset, ctype, prefix):
 
423
    size = ctypes.sizeof(ctype)
 
424
    name = prefix
 
425
    i = 0
 
426
    while name in layout:
 
427
        i += 1
 
428
        name = '%s_%d' % (prefix, i)
 
429
    field = Field(name, ctype)
 
430
    for i in range(offset, offset+size):
 
431
        assert layout[i] is None, "%s overlaps %r" % (fieldname, layout[i])
 
432
        layout[i] = field
 
433
    return field
 
434
 
 
435
def size_and_sign(ctype):
 
436
    return (ctypes.sizeof(ctype),
 
437
            ctype in integer_class and ctype(-1).value > 0)
 
438
 
 
439
def fixup_ctype(fieldtype, fieldname, expected_size_and_sign):
 
440
    for typeclass in [integer_class, float_class]:
 
441
        if fieldtype in typeclass:
 
442
            for ctype in typeclass:
 
443
                if size_and_sign(ctype) == expected_size_and_sign:
 
444
                    return ctype
 
445
    if (hasattr(fieldtype, '_length_')
 
446
        and getattr(fieldtype, '_type_', None) == ctypes.c_char):
 
447
        # for now, assume it is an array of chars; otherwise we'd also
 
448
        # have to check the exact integer type of the elements of the array
 
449
        size, sign = expected_size_and_sign
 
450
        return ctypes.c_char * size
 
451
    if (hasattr(fieldtype, '_length_')
 
452
        and getattr(fieldtype, '_type_', None) == ctypes.c_ubyte):
 
453
        # grumble, fields of type 'c_char array' have automatic cast-to-
 
454
        # Python-string behavior in ctypes, which may not be what you
 
455
        # want, so here is the same with c_ubytes instead...
 
456
        size, sign = expected_size_and_sign
 
457
        return ctypes.c_ubyte * size
 
458
    raise TypeError("conflicting field type %r for %r" % (fieldtype,
 
459
                                                          fieldname))
 
460
 
 
461
 
 
462
C_HEADER = """
 
463
#include <stdio.h>
 
464
#include <stddef.h>   /* for offsetof() */
 
465
 
 
466
void dump(char* key, int value) {
 
467
    printf("%s: %d\\n", key, value);
 
468
}
 
469
"""
 
470
 
 
471
def run_example_code(filepath, include_dirs=[]):
 
472
    executable = build_executable([filepath], include_dirs=include_dirs)
 
473
    output = py.process.cmdexec(executable)
 
474
    section = None
 
475
    for line in output.splitlines():
 
476
        line = line.strip()
 
477
        if line.startswith('-+- '):      # start of a new section
 
478
            section = {}
 
479
        elif line == '---':              # section end
 
480
            assert section is not None
 
481
            yield section
 
482
            section = None
 
483
        elif line:
 
484
            assert section is not None
 
485
            key, value = line.split(': ')
 
486
            section[key] = int(value)
 
487
 
 
488
# ____________________________________________________________
 
489
 
 
490
def get_python_include_dir():
 
491
    from distutils import sysconfig
 
492
    gcv = sysconfig.get_config_vars()
 
493
    return gcv['INCLUDEPY']
 
494
 
 
495
if __name__ == '__main__':
 
496
    doc = """Example:
 
497
    
 
498
       ctypes_platform.py  -h sys/types.h  -h netinet/in.h
 
499
                           'struct sockaddr_in'
 
500
                           sin_port  c_int
 
501
    """
 
502
    import sys, getopt
 
503
    opts, args = getopt.gnu_getopt(sys.argv[1:], 'h:')
 
504
    if not args:
 
505
        print >> sys.stderr, doc
 
506
    else:
 
507
        assert len(args) % 2 == 1
 
508
        headers = []
 
509
        for opt, value in opts:
 
510
            if opt == '-h':
 
511
                headers.append('#include <%s>' % (value,))
 
512
        name = args[0]
 
513
        fields = []
 
514
        for i in range(1, len(args), 2):
 
515
            ctype = getattr(ctypes, args[i+1])
 
516
            fields.append((args[i], ctype))
 
517
 
 
518
        S = getstruct(name, '\n'.join(headers), fields)
 
519
 
 
520
        for key, value in S._fields_:
 
521
            print key, value