[pypy-svn] r42600 - in pypy/dist/pypy/translator: . cli cli/test

antocuni at codespeak.net antocuni at codespeak.net
Wed May 2 23:47:08 CEST 2007


Author: antocuni
Date: Wed May  2 23:47:07 2007
New Revision: 42600

Added:
   pypy/dist/pypy/translator/cli/silverpython.py   (contents, props changed)
   pypy/dist/pypy/translator/cli/test/test_silverpython.py   (contents, props changed)
Modified:
   pypy/dist/pypy/translator/cli/cts.py
   pypy/dist/pypy/translator/cli/entrypoint.py
   pypy/dist/pypy/translator/cli/function.py
   pypy/dist/pypy/translator/cli/gencli.py
   pypy/dist/pypy/translator/cli/rte.py
   pypy/dist/pypy/translator/cli/test/runtest.py
   pypy/dist/pypy/translator/driver.py
Log:
- add support to driver for building libraries instead of standalone
executables (i.e., allowing more than one entry-point)

- first steps to write a gencli's frontend which allow to compile a
rpython module to a .NET dll; the draft name is SilveRPython,
suggestions are welcome :-)



Modified: pypy/dist/pypy/translator/cli/cts.py
==============================================================================
--- pypy/dist/pypy/translator/cli/cts.py	(original)
+++ pypy/dist/pypy/translator/cli/cts.py	Wed May  2 23:47:07 2007
@@ -196,6 +196,9 @@
         ret_type, ret_var = self.llvar_to_cts(graph.getreturnvar())
         func_name = func_name or graph.name
         func_name = self.escape_name(func_name)
+        namespace = getattr(graph.func, '_namespace_', None)
+        if namespace:
+            func_name = '%s::%s' % (namespace, func_name)
 
         args = [arg for arg in graph.getargs() if arg.concretetype is not ootype.Void]
         if is_method:

Modified: pypy/dist/pypy/translator/cli/entrypoint.py
==============================================================================
--- pypy/dist/pypy/translator/cli/entrypoint.py	(original)
+++ pypy/dist/pypy/translator/cli/entrypoint.py	Wed May  2 23:47:07 2007
@@ -19,6 +19,12 @@
         self.db = db
         self.cts = CTS(db)
 
+    def ilasm_flags(self):
+        return []
+
+    def output_filename(self, il_filename):
+        return il_filename.replace('.il', '.exe')
+
 class StandaloneEntryPoint(BaseEntryPoint):
     """
     This class produces a 'main' method that converts the argv in a
@@ -59,3 +65,21 @@
         ilasm.opcode('ret')
         ilasm.end_function()
         self.db.pending_function(self.graph)
+
+class DllEntryPoint(BaseEntryPoint):
+    def __init__(self, name, graphs):
+        self.name = name
+        self.graphs = graphs
+
+    def get_name(self):
+        return self.name
+
+    def ilasm_flags(self):
+        return ['/dll']
+
+    def output_filename(self, il_filename):
+        return il_filename.replace('.il', '.dll')
+
+    def render(self, ilasm):
+        for graph in self.graphs:
+            self.db.pending_function(graph)

Modified: pypy/dist/pypy/translator/cli/function.py
==============================================================================
--- pypy/dist/pypy/translator/cli/function.py	(original)
+++ pypy/dist/pypy/translator/cli/function.py	Wed May  2 23:47:07 2007
@@ -173,7 +173,18 @@
         OOFunction.__init__(self, *args, **kwargs)
         self._set_args()
         self._set_locals()
-                 
+        namespace = getattr(self.graph.func, '_namespace_', None)
+        str
+        if namespace:
+            if '.' in namespace:
+                self.namespace, self.classname = namespace.rsplit('.', 1)
+            else:
+                self.namespace = None
+                self.classname = namespace
+        else:
+            self.namespace = None
+            self.classname = None
+
     def _create_generator(self, ilasm):
         return self # Function implements the Generator interface
 
@@ -192,11 +203,20 @@
         else:
             args = self.args
             meth_type = 'static'
-        self.ilasm.begin_function(self.name, args, returntype, self.is_entrypoint, meth_type)        
+
+        if self.namespace:
+            self.ilasm.begin_namespace(self.namespace)
+        if self.classname:
+            self.ilasm.begin_class(self.classname)
+        self.ilasm.begin_function(self.name, args, returntype, self.is_entrypoint, meth_type)
         self.ilasm.locals(self.locals)
 
     def end_render(self):
         self.ilasm.end_function()
+        if self.classname:
+            self.ilasm.end_class()
+        if self.namespace:
+            self.ilasm.end_namespace()
 
     def set_label(self, label):
         self.ilasm.label(label)

Modified: pypy/dist/pypy/translator/cli/gencli.py
==============================================================================
--- pypy/dist/pypy/translator/cli/gencli.py	(original)
+++ pypy/dist/pypy/translator/cli/gencli.py	Wed May  2 23:47:07 2007
@@ -87,20 +87,20 @@
 
         ilasm = SDK.ilasm()
         tmpfile = self.tmpfile.strpath
-        self._exec_helper(ilasm, tmpfile,
+        self._exec_helper(ilasm, [tmpfile]+self.entrypoint.ilasm_flags(),
                           'ilasm failed to assemble (%s):\n%s\n%s',
                           timeout = 900)
         # Mono's ilasm occasionally deadlocks.  We set a timer to avoid
         # blocking automated test runs forever.
 
-        exefile = tmpfile.replace('.il', '.exe')
+        self.outfile = self.entrypoint.output_filename(tmpfile)
         if getoption('verify'):
             peverify = SDK.peverify()
-            self._exec_helper(peverify, exefile, 'peverify failed to verify (%s):\n%s\n%s')
-        return exefile
+            self._exec_helper(peverify, [outfile], 'peverify failed to verify (%s):\n%s\n%s')
+        return self.outfile
 
-    def _exec_helper(self, helper, filename, msg, timeout=None):
-        args = [helper, filename]
+    def _exec_helper(self, helper, args, msg, timeout=None):
+        args = [helper] + args
         if timeout and not sys.platform.startswith('win'):
             import os
             from pypy.tool import autopath
@@ -109,5 +109,5 @@
         proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
         stdout, stderr = proc.communicate()
         retval = proc.wait()
-        assert retval == 0, msg % (filename, stdout, stderr)
+        assert retval == 0, msg % (args[0], stdout, stderr)
         

Modified: pypy/dist/pypy/translator/cli/rte.py
==============================================================================
--- pypy/dist/pypy/translator/cli/rte.py	(original)
+++ pypy/dist/pypy/translator/cli/rte.py	Wed May  2 23:47:07 2007
@@ -11,14 +11,9 @@
 from py.compat import subprocess
 from pypy.translator.cli.sdk import SDK
 from pypy.tool.ansi_print import ansi_log
-log = py.log.Producer("cli") 
-py.log.setconsumer("cli", ansi_log) 
+log = py.log.Producer("cli")
+py.log.setconsumer("cli", ansi_log)
 
-SRC_DIR = os.path.join(os.path.dirname(__file__), 'src/')
-
-def _filename(name, path=None):
-    rel_path =  os.path.join(SRC_DIR, name)
-    return os.path.abspath(rel_path)
 
 class Target:
     SOURCES = []
@@ -26,6 +21,12 @@
     ALIAS = None
     FLAGS = []
     DEPENDENCIES = []
+    SRC_DIR = os.path.join(os.path.dirname(__file__), 'src/')
+
+    def _filename(cls, name, path=None):
+        rel_path =  os.path.join(cls.SRC_DIR, name)
+        return os.path.abspath(rel_path)
+    _filename = classmethod(_filename)
 
     def get_COMPILER(cls):
         return SDK.csc()
@@ -34,9 +35,9 @@
     def get(cls):
         for dep in cls.DEPENDENCIES:
             dep.get()
-        sources = [_filename(src) for src in cls.SOURCES]
-        out = _filename(cls.OUTPUT)
-        alias = _filename(cls.ALIAS or cls.OUTPUT)
+        sources = [cls._filename(src) for src in cls.SOURCES]
+        out = cls._filename(cls.OUTPUT)
+        alias = cls._filename(cls.ALIAS or cls.OUTPUT)
         recompile = True
         try:
             src_mtime = max([os.stat(src).st_mtime for src in sources])
@@ -54,14 +55,14 @@
     def compile(cls, sources, out):
         log.red("Compiling %s" % (cls.ALIAS or cls.OUTPUT))
         oldcwd = os.getcwd()
-        os.chdir(SRC_DIR)
+        os.chdir(cls.SRC_DIR)
         compiler = subprocess.Popen([cls.get_COMPILER()] + cls.FLAGS + ['/out:%s' % out] + sources,
                                     stdout=subprocess.PIPE, stderr=subprocess.PIPE)
         stdout, stderr = compiler.communicate()
         retval = compiler.wait()
         assert retval == 0, 'Failed to compile %s: the compiler said:\n %s' % (cls.OUTPUT, stderr)
         if cls.ALIAS is not None:
-            alias = _filename(cls.ALIAS)
+            alias = cls._filename(cls.ALIAS)
             shutil.copy(out, alias)
         os.chdir(oldcwd)
 

Added: pypy/dist/pypy/translator/cli/silverpython.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/cli/silverpython.py	Wed May  2 23:47:07 2007
@@ -0,0 +1,44 @@
+from pypy.translator.driver import TranslationDriver
+from pypy.translator.cli.entrypoint import DllEntryPoint
+
+class DllDef:
+    def __init__(self, name, namespace, functions=[], classes=[]):
+        self.name = name
+        self.namespace = namespace
+        self.functions = functions # [(function, annotation), ...]
+
+    def add_function(self, func, inputtypes):
+        self.functions.append((func, inputtypes))
+
+    def get_entrypoint(self, bk):
+        graphs = [bk.getdesc(f).cachedgraph(None) for f, _ in self.functions]
+        return DllEntryPoint(self.name, graphs)
+
+    def compile(self):
+        # add all functions to the appropriate namespace
+        for func, _ in self.functions:
+            if not hasattr(func, '_namespace_'):
+                func._namespace_ = self.namespace
+        driver = TranslationDriver()
+        driver.setup_library(self)
+        driver.proceed(['compile_cli'])
+        return driver
+
+
+class MyClass:
+    def __init__(self, x):
+        self.x = x
+
+    def foo(self):
+        return self.x
+
+def main():
+    dll = DllDef('mylibrary', 'foo', [], [
+        (MyClass, [int]),
+        ])
+    driver = dll.compile()
+    driver.copy_cli_dll()
+    
+
+if __name__ == '__main__':
+    main()

Modified: pypy/dist/pypy/translator/cli/test/runtest.py
==============================================================================
--- pypy/dist/pypy/translator/cli/test/runtest.py	(original)
+++ pypy/dist/pypy/translator/cli/test/runtest.py	Wed May  2 23:47:07 2007
@@ -137,15 +137,15 @@
             assert False, 'Input type %s not supported' % arg_type
 
 
-def compile_function(func, annotation=[], graph=None, backend_opt={}):
+def compile_function(func, annotation=[], graph=None, backendopt=True):
     olddefs = patch()
-    gen = _build_gen(func, annotation, graph, backend_opt)
+    gen = _build_gen(func, annotation, graph, backendopt)
     gen.generate_source()
     exe_name = gen.build_exe()
     unpatch(*olddefs) # restore original values
     return CliFunctionWrapper(exe_name)
 
-def _build_gen(func, annotation, graph=None, backend_opt={}):
+def _build_gen(func, annotation, graph=None, backendopt=True):
     try: 
         func = func.im_func
     except AttributeError: 
@@ -165,8 +165,9 @@
        t.view()
 
     t.buildrtyper(type_system="ootype").specialize()
-    check_virtual_methods(ootype.ROOT)
-    backend_optimizations(t)
+    if backendopt:
+        check_virtual_methods(ootype.ROOT)
+        backend_optimizations(t)
     
     main_graph = t.graphs[0]
 
@@ -242,13 +243,13 @@
         self._ann = None
         self._cli_func = None
 
-    def _compile(self, fn, args, ann=None):
+    def _compile(self, fn, args, ann=None, backendopt=True):
         if ann is None:
             ann = [lltype_to_annotation(typeOf(x)) for x in args]
         if self._func is fn and self._ann == ann:
             return self._cli_func
         else:
-            self._cli_func = compile_function(fn, ann)
+            self._cli_func = compile_function(fn, ann, backendopt=backendopt)
             self._func = fn
             self._ann = ann
             return self._cli_func
@@ -261,8 +262,8 @@
         if platform.processor() == 'powerpc':
             py.test.skip('PowerPC --> %s' % reason)
 
-    def interpret(self, fn, args, annotation=None):
-        f = self._compile(fn, args, annotation)
+    def interpret(self, fn, args, annotation=None, backendopt=True):
+        f = self._compile(fn, args, annotation, backendopt)
         res = f(*args)
         if isinstance(res, ExceptionWrapper):
             raise res

Added: pypy/dist/pypy/translator/cli/test/test_silverpython.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/cli/test/test_silverpython.py	Wed May  2 23:47:07 2007
@@ -0,0 +1,58 @@
+from pypy.tool import udir
+from pypy.translator.cli.rte import Target
+from pypy.translator.cli.silverpython import DllDef
+from pypy.translator.cli.test.runtest import CliFunctionWrapper, CliTest
+
+TEMPLATE = """
+using System;
+class SilveRPytonTest {
+    public static void Main() {
+        %s
+    }
+}
+"""
+
+class TestSilveRPython(CliTest):
+    
+    def _csharp(self, reference, source):
+        tmpfile = udir.udir.join('tmp.cs')
+        tmpfile.write(TEMPLATE % source)
+        if reference is None:
+            flags = []
+        else:
+            flags = ['/r:%s' % reference]
+
+        class MyTarget(Target):
+            SOURCES = [str(tmpfile)]
+            FLAGS = flags
+            OUTPUT = 'tmp.exe'
+            SRC_DIR = str(udir.udir)
+
+        func = CliFunctionWrapper(MyTarget.get())
+        return func()
+
+
+    def test_compilation(self):
+        res = self._csharp(None, 'Console.WriteLine(42);')
+        assert res == 42
+
+    def test_func_namespace(self):
+        def foo(x):
+            return x+1
+        def bar(x):
+            return foo(x)
+        foo._namespace_ = 'MyNamespace.MyClass'
+        bar._namespace_ = 'MyClass'
+        res = self.interpret(bar, [41], backendopt=False)
+        assert res == 42
+
+    def test_simple_functions(self):
+        def foo(x):
+            return x+1
+        def bar(x):
+            return x*2
+        dll = DllDef('test', 'Test', [(foo, [int]),
+                                      (bar, [int])])
+        dll.compile()
+        res = self._csharp('test', 'Console.WriteLine("{0}, {1}", Test.foo(42), Test.bar(42));')
+        assert res == (43, 84)

Modified: pypy/dist/pypy/translator/driver.py
==============================================================================
--- pypy/dist/pypy/translator/driver.py	(original)
+++ pypy/dist/pypy/translator/driver.py	Wed May  2 23:47:07 2007
@@ -209,9 +209,14 @@
 
         self.entry_point = entry_point
         self.translator = translator
+        self.libdef = None
 
         self.translator.driver_instrument_result = self.instrument_result
 
+    def setup_library(self, libdef, policy=None, extra={}, empty_translator=None):
+        self.setup(None, None, policy, extra, empty_translator)
+        self.libdef = libdef
+
     def instrument_result(self, args):
         backend, ts = self.get_backend_and_type_system()
         if backend != 'c' or sys.platform == 'win32':
@@ -275,16 +280,22 @@
 
         annmodel.DEBUG = self.config.translation.debug
         annotator = translator.buildannotator(policy=policy)
-        
-        s = annotator.build_types(self.entry_point, self.inputtypes)
-        
-        self.sanity_check_annotation()
-        if self.standalone and s.knowntype != int:
-            raise Exception("stand-alone program entry point must return an "
-                            "int (and not, e.g., None or always raise an "
-                            "exception).")
+
+        if self.entry_point:
+            s = annotator.build_types(self.entry_point, self.inputtypes)
+
+            self.sanity_check_annotation()
+            if self.standalone and s.knowntype != int:
+                raise Exception("stand-alone program entry point must return an "
+                                "int (and not, e.g., None or always raise an "
+                                "exception).")
+            return s
+        else:
+            assert self.libdef is not None
+            for func, inputtypes in self.libdef.functions:
+                annotator.build_types(func, inputtypes)
+            self.sanity_check_annotation()
         annotator.simplify()
-        return s
     #
     task_annotate = taskdef(task_annotate, [], "Annotating&simplifying")
 
@@ -619,9 +630,16 @@
         from pypy.translator.cli.gencli import GenCli
         from pypy.translator.cli.entrypoint import get_entrypoint
 
-        entry_point_graph = self.translator.graphs[0]
-        self.gen = GenCli(udir, self.translator, get_entrypoint(entry_point_graph),
-                          config=self.config)
+        if self.entry_point is not None: # executable mode
+            entry_point_graph = self.translator.graphs[0]
+            entry_point = get_entrypoint(entry_point_graph)
+        else:
+            # library mode
+            assert self.libdef is not None
+            bk = self.translator.annotator.bookkeeper
+            entry_point = self.libdef.get_entrypoint(bk)
+
+        self.gen = GenCli(udir, self.translator, entry_point, config=self.config)
         filename = self.gen.generate_source()
         self.log.info("Wrote %s" % (filename,))
     task_source_cli = taskdef(task_source_cli, ["?" + OOBACKENDOPT, OOTYPE],
@@ -667,6 +685,15 @@
         f.close()
         os.chmod(newexename, 0755)
 
+    def copy_cli_dll(self):
+        import os.path
+        import shutil
+        dllname = self.gen.outfile
+        usession_path, dll_name = os.path.split(dllname)
+        pypylib_dll = os.path.join(usession_path, 'pypylib.dll')
+        shutil.copy(dllname, '.')
+        shutil.copy(pypylib_dll, '.')
+
     def task_run_cli(self):
         pass
     task_run_cli = taskdef(task_run_cli, ['compile_cli'],


More information about the pypy-svn mailing list