[pypy-svn] r58220 - in pypy/branch/new-platformcheck/pypy/rpython/tool: . test

nshepperd at codespeak.net nshepperd at codespeak.net
Thu Sep 18 12:53:23 CEST 2008


Author: nshepperd
Date: Thu Sep 18 12:53:21 2008
New Revision: 58220

Modified:
   pypy/branch/new-platformcheck/pypy/rpython/tool/rffi_platform.py
   pypy/branch/new-platformcheck/pypy/rpython/tool/test/test_rffi_platform.py
Log:
Tentative support for obtaining constants from extrnal libraries (that 
is, using -lstuff). Expect refactoring.


Modified: pypy/branch/new-platformcheck/pypy/rpython/tool/rffi_platform.py
==============================================================================
--- pypy/branch/new-platformcheck/pypy/rpython/tool/rffi_platform.py	(original)
+++ pypy/branch/new-platformcheck/pypy/rpython/tool/rffi_platform.py	Thu Sep 18 12:53:21 2008
@@ -2,7 +2,7 @@
 
 import os, py, sys
 from subprocess import Popen, PIPE
-from tempfile import NamedTemporaryFile
+from tempfile import NamedTemporaryFile, mkdtemp
 from string import atoi
 from pypy.rpython.lltypesystem import lltype
 from pypy.rpython.lltypesystem import rffi
@@ -215,9 +215,8 @@
     for key in dir(CConfig):
         value = getattr(CConfig, key)
         if isinstance(value, CConfigExternEntry):
-            print (key, value)
             value.eci = CConfig._compilation_info_
-            res[key] = value.build_result()
+            res[key] = value.build_result(locals().get('result'))
 
     return res
 
@@ -267,47 +266,120 @@
     def bool(self, data):
         return data[0] != '\x00'
 
-class CConfigExternEntry(object):
-    """Abstract base class."""
-    def get(self, name, CC='gcc', NM=None, OBJCOPY=None):
-        if not NM:
-            NM = CC.replace('gcc', 'nm')
-        if not OBJCOPY:
-            OBJCOPY = CC.replace('gcc', 'objcopy')
-        libs = self.eci.link_extra
-        for obj in libs:
-            nm = Popen(NM + ' -f sysv ' + obj, shell=True, stdout=PIPE)
-            nm.wait()
-            nm = nm.stdout.read()
-            for line in nm.splitlines():
-                line = [part.strip() for part in line.split('|')]
-                if len(line) == 7 and line[0] == name and line[2] != 'U':
-                    # only if the symbol is not a 'depends on' symbol ^
-                    offset = atoi(line[1], 16)
-                    minsize = atoi(line[4], 16)
-                    section = line[6]
-                    break
-            else:
+def get_symbol_data(eci, name, NM, OBJCOPY):
+    link_extra = ' '.join(list(eci.link_extra) + ['-static', '-Wl,--trace', '-Wl,--whole-archive'])
+    libraries = ' '.join(['-l' + lib for lib in eci.libraries])
+    libdirs = ' '.join(['-L' + _dir for _dir in eci.library_dirs])
+    dir = mkdtemp()
+    srcpath = os.path.join(dir, 'main.c')
+    objpath = os.path.join(dir, 'main.o')
+    exepath = os.path.join(dir, 'main.exe')
+    src = open(srcpath, 'w')
+    src.write('int main() {}\n')
+    src.close()
+    if os.system('gcc -c -o %s %s' % (objpath, srcpath)):
+        raise CompilationError('gcc does not work')
+    linkcmd = ' '.join(['gcc -o ', exepath, objpath, link_extra, libdirs, libraries])
+    link = Popen(linkcmd, shell=True, stdout=PIPE, stderr=PIPE)
+    
+    linked = []
+    for line in link.stdout.readlines():
+        line = line.strip()
+        if os.path.exists(line):
+            linked.append(line)
+        sub = line.split('(')
+        if len(sub) >= 2:
+            sub = sub[1].split(')')[0].strip()
+            if os.path.exists(sub) and sub not in linked:
+                linked.append(sub)
+
+    for obj in linked:
+        nm = Popen(NM + ' -f sysv ' + obj, shell=True, stdout=PIPE, stderr=PIPE)
+        nm = list(nm.stdout.readlines())
+        is_archive = False
+        for line in nm:
+            if '[' in line and ']' in line:
+                is_archive = True
+                member = line.partition('[')[2].partition(']')[0]
                 continue
-            break
+            line = [part.strip() for part in line.split('|')]
+            if len(line) == 7 and line[0] == name and line[2] != 'U':
+                # only if the symbol is a real symbol .....^
+                offset = atoi(line[1], 16)
+                maxsize = atoi(line[4], 16)
+                section = line[6]
+                break
         else:
-            return None
-        sec = NamedTemporaryFile()
-        try:
-            if os.system('%s -O binary -j \'%s\' %s %s' %
-                            (OBJCOPY, section, obj, sec.name)):
-                raise Exception('objcopy error')
-            sec.seek(offset)
-            return sec.read(minsize)
-        finally:
-            sec.close()
+            continue
+        base_offset = offset
+        this_member = None
+        for line in nm:
+            if is_archive:
+                if '[' in line and ']' in line:
+                    this_member = line.partition('[')[2].partition(']')[0]
+                    continue
+            if not is_archive or this_member == member:
+                line = [part.strip() for part in line.split('|')]
+                if len(line) == 7 and line[6] == section and line[2] != 'U':
+                    base_offset = min(base_offset, atoi(line[1], 16))
+        offset = offset - base_offset
+        break
+    else:
+        return None
+    sec = NamedTemporaryFile()
+    if is_archive:
+        obj2 = NamedTemporaryFile()
+        ar = Popen('ar p %s %s' % (obj, member), shell=True, stdout=PIPE)
+        data = ar.stdout.read()
+        obj2.write(data)
+        obj2.flush()
+        obj = obj2.name
+    cmd = '%s -O binary -j \'%s\' %s %s' % (OBJCOPY, section, obj, sec.name)
+    if os.system(cmd):
+        raise CompilationError('objcopy error')
+    sec.seek(offset)
+    return sec.read(maxsize)
+
+class CConfigExternEntry(object):
+    """Abstract base class."""
+    def get(self, name):
+        return get_symbol_data(self.eci, name, 'nm', 'objcopy')
 
 class ExternString(CConfigExternEntry):
     def __init__(self, name):
         self.name = name
     
-    def build_result(self, *args, **kw):
-        return self.get(self.name, *args, **kw)
+    def build_result(self, config_result):
+        data = self.get(self.name)
+        if data:
+            return data.partition('\0')[0]
+        else:
+            return None
+
+class ExternStruct(CConfigExternEntry):
+    def __init__(self, name, cconfig_entry=None, rffi_struct=None):
+        if not (cconfig_entry or rffi_struct):
+            raise TypeError('ExternStruct takes 3 arguments')
+        self.entry = cconfig_entry
+        self.rffi_struct = rffi_struct
+        self.name = name
+    
+    def build_result(self, config_result):
+        if not self.rffi_struct:
+            rffi_struct = config_result.get_entry_result(self.entry)
+        else:
+            rffi_struct = self.rffi_struct
+        data = self.get(self.name)
+        if not data:
+            return None
+        class StructResult(object): pass
+        res = StructResult()
+        for (fld_name, fld_offset) in zip(
+                rffi_struct._names, rffi_struct._hints['fieldoffsets']):
+            fld_type = rffi_struct._flds[fld_name]
+            fld_data = data[fld_offset:fld_offset+rffi.sizeof(fld_type)]
+            setattr(res, fld_name, rffi.cast(fld_type, fld_data[0]))
+        return res
 
 class Struct(CConfigEntry):
     """An entry in a CConfig class that stands for an externally

Modified: pypy/branch/new-platformcheck/pypy/rpython/tool/test/test_rffi_platform.py
==============================================================================
--- pypy/branch/new-platformcheck/pypy/rpython/tool/test/test_rffi_platform.py	(original)
+++ pypy/branch/new-platformcheck/pypy/rpython/tool/test/test_rffi_platform.py	Thu Sep 18 12:53:21 2008
@@ -245,6 +245,37 @@
     src.close()
     os.system('gcc -c -o %s %s' % (objpath, srcpath))
     class CConfig:
-        _compilation_info_ = ExternalCompilationInfo(link_extra = [objpath])
+        _compilation_info_ = ExternalCompilationInfo(link_extra=[objpath])
         STRING = rffi_platform.ExternString('stuff')
-    assert rffi_platform.configure(CConfig)['STRING'] == 'Success!\0'
+    assert rffi_platform.configure(CConfig)['STRING'] == 'Success!'
+
+def test_extern_fail():
+    class CConfig:
+        _compilation_info_ = ExternalCompilationInfo()
+        STRING = rffi_platform.ExternString('stuff')
+    assert rffi_platform.configure(CConfig)['STRING'] is None
+
+def test_extern_struct():
+    from tempfile import mkdtemp
+    dir = mkdtemp()
+    srcpath = os.path.join(dir, 'test.c')
+    objpath = os.path.join(dir, 'test.o')
+    hpath = os.path.join(dir, 'test.h')
+    h = open(hpath, 'w')
+    print >> h, 'typedef struct Stuff {'
+    print >> h, ' int a;'
+    print >> h, ' int b;'
+    print >> h, '} Stuff;'
+    h.close()
+    src = open(srcpath, 'w')
+    print >> src, '#include "' + hpath + '"'
+    print >> src, 'struct Stuff stuff = { 1, 2 };'
+    src.close()
+    os.system('gcc -c -o %s %s' % (objpath, srcpath))
+    class CConfig:
+        _compilation_info_ = ExternalCompilationInfo(link_extra=[objpath], includes=[hpath])
+        STRUCT = rffi_platform.Struct('Stuff', [('a', rffi.INT), ('b', rffi.INT)])
+        STUFF = rffi_platform.ExternStruct('stuff', STRUCT)
+    assert rffi_platform.configure(CConfig)['STUFF'].c_a == 1
+    assert rffi_platform.configure(CConfig)['STUFF'].c_b == 2
+



More information about the Pypy-commit mailing list