aboutsummaryrefslogtreecommitdiff
blob: 94a0ac5f280141ba5a75f00fcf0189d3f9022641 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
import py

import os, sys, subprocess

import pypy
from pypy.interpreter import gateway
from pypy.interpreter.error import OperationError
from pypy.tool.ann_override import PyPyAnnotatorPolicy
from rpython.config.config import to_optparse, make_dict, SUPPRESS_USAGE
from rpython.config.config import ConflictConfigError
from pypy.tool.option import make_objspace
from pypy import pypydir
from rpython.rlib import rthread
from pypy.module.thread import os_thread

thisdir = py.path.local(__file__).dirpath()

try:
    this_dir = os.path.dirname(__file__)
except NameError:
    this_dir = os.path.dirname(sys.argv[0])

def debug(msg):
    try:
        os.write(2, "debug: " + msg + '\n')
    except OSError:
        pass     # bah, no working stderr :-(

# __________  Entry point  __________


def create_entry_point(space, w_dict):
    if w_dict is not None: # for tests
        w_entry_point = space.getitem(w_dict, space.newtext('entry_point'))
        w_run_toplevel = space.getitem(w_dict, space.newtext('run_toplevel'))
        w_initstdio = space.getitem(w_dict, space.newtext('initstdio'))
        withjit = space.config.objspace.usemodules.pypyjit
        hashfunc = space.config.objspace.hash
    else:
        w_initstdio = space.appexec([], """():
            return lambda unbuffered: None
        """)

    def entry_point(argv):
        if withjit:
            from rpython.jit.backend.hlinfo import highleveljitinfo
            highleveljitinfo.sys_executable = argv[0]

        if hashfunc == "siphash24":
            from rpython.rlib import rsiphash
            rsiphash.enable_siphash24()

        #debug("entry point starting")
        #for arg in argv:
        #    debug(" argv -> " + arg)
        if len(argv) > 2 and argv[1] == '--heapsize':
            # Undocumented option, handled at interp-level.
            # It has silently no effect with some GCs.
            # It works in Boehm and in the semispace or generational GCs
            # (but see comments in semispace.py:set_max_heap_size()).
            # At the moment this option exists mainly to support sandboxing.
            from rpython.rlib import rgc
            rgc.set_max_heap_size(int(argv[2]))
            argv = argv[:1] + argv[3:]
        try:
            try:
                space.startup()
                w_executable = space.newtext(argv[0])
                w_argv = space.newlist([space.newtext(s) for s in argv[1:]])
                w_exitcode = space.call_function(w_entry_point, w_executable, w_argv)
                exitcode = space.int_w(w_exitcode)
                # try to pull it all in
            ##    from pypy.interpreter import main, interactive, error
            ##    con = interactive.PyPyConsole(space)
            ##    con.interact()
            except OperationError as e:
                debug("OperationError:")
                debug(" operror-type: " + e.w_type.getname(space))
                debug(" operror-value: " + space.text_w(space.str(e.get_w_value(space))))
                return 1
        finally:
            try:
                space.finish()
            except OperationError as e:
                debug("OperationError:")
                debug(" operror-type: " + e.w_type.getname(space))
                debug(" operror-value: " + space.text_w(space.str(e.get_w_value(space))))
                return 1
        return exitcode

    return entry_point, get_additional_entrypoints(space, w_initstdio)


def get_additional_entrypoints(space, w_initstdio):
    # register the minimal equivalent of running a small piece of code. This
    # should be used as sparsely as possible, just to register callbacks
    from rpython.rlib.entrypoint import entrypoint_highlevel
    from rpython.rtyper.lltypesystem import rffi, lltype

    if space.config.objspace.disable_entrypoints:
        return {}

    @entrypoint_highlevel('main', [rffi.CCHARP, rffi.INT],
                          c_name='pypy_setup_home')
    def pypy_setup_home(ll_home, verbose):
        from pypy.module.sys.initpath import pypy_find_stdlib
        verbose = rffi.cast(lltype.Signed, verbose)
        if ll_home and ord(ll_home[0]):
            home1 = rffi.charp2str(ll_home)
            home = os.path.join(home1, 'x') # <- so that 'll_home' can be
                                            # directly the root directory
        else:
            home1 = "pypy's shared library location"
            home = '*'
        w_path = pypy_find_stdlib(space, home)
        if space.is_none(w_path):
            if verbose:
                debug("pypy_setup_home: directories 'lib-python' and 'lib_pypy'"
                      " not found in %s or in any parent directory" % home1)
            return rffi.cast(rffi.INT, 1)
        space.startup()
        must_leave = space.threadlocals.try_enter_thread(space)
        try:
            # initialize sys.{path,executable,stdin,stdout,stderr}
            # (in unbuffered mode, to avoid troubles) and import site
            space.appexec([w_path, space.newtext(home), w_initstdio],
            r"""(path, home, initstdio):
                import sys
                # don't import anything more above this: sys.path is not set
                sys.path[:] = path
                sys.executable = home
                initstdio(unbuffered=True)
                try:
                    import site
                except Exception as e:
                    sys.stderr.write("'import site' failed:\n")
                    import traceback
                    traceback.print_exc()
            """)
            return rffi.cast(rffi.INT, 0)
        except OperationError as e:
            if verbose:
                debug("OperationError:")
                debug(" operror-type: " + e.w_type.getname(space))
                debug(" operror-value: " + space.text_w(space.str(e.get_w_value(space))))
            return rffi.cast(rffi.INT, -1)
        finally:
            if must_leave:
                space.threadlocals.leave_thread(space)

    @entrypoint_highlevel('main', [rffi.CCHARP], c_name='pypy_execute_source')
    def pypy_execute_source(ll_source):
        return pypy_execute_source_ptr(ll_source, 0)

    @entrypoint_highlevel('main', [rffi.CCHARP, lltype.Signed],
                          c_name='pypy_execute_source_ptr')
    def pypy_execute_source_ptr(ll_source, ll_ptr):
        source = rffi.charp2str(ll_source)
        res = _pypy_execute_source(source, ll_ptr)
        return rffi.cast(rffi.INT, res)

    @entrypoint_highlevel('main', [], c_name='pypy_init_threads')
    def pypy_init_threads():
        if not space.config.objspace.usemodules.thread:
            return
        os_thread.setup_threads(space)

    @entrypoint_highlevel('main', [], c_name='pypy_thread_attach')
    def pypy_thread_attach():
        if not space.config.objspace.usemodules.thread:
            return
        os_thread.setup_threads(space)
        os_thread.bootstrapper.acquire(space, None, None)
        # XXX this doesn't really work.  Don't use os.fork(), and
        # if your embedder program uses fork(), don't use any PyPy
        # code in the fork
        rthread.gc_thread_start()
        os_thread.bootstrapper.nbthreads += 1
        os_thread.bootstrapper.release()

    def _pypy_execute_source(source, c_argument):
        try:
            w_globals = space.newdict(module=True)
            space.setitem(w_globals, space.newtext('__builtins__'),
                          space.builtin_modules['__builtin__'])
            space.setitem(w_globals, space.newtext('c_argument'),
                          space.newint(c_argument))
            space.appexec([space.newtext(source), w_globals], """(src, glob):
                import sys
                stmt = compile(src, 'c callback', 'exec')
                if not hasattr(sys, '_pypy_execute_source'):
                    sys._pypy_execute_source = []
                sys._pypy_execute_source.append(glob)
                exec stmt in glob
            """)
        except OperationError as e:
            debug("OperationError:")
            debug(" operror-type: " + e.w_type.getname(space))
            debug(" operror-value: " + space.text_w(space.str(e.get_w_value(space))))
            return -1
        return 0

    return {'pypy_execute_source': pypy_execute_source,
            'pypy_execute_source_ptr': pypy_execute_source_ptr,
            'pypy_init_threads': pypy_init_threads,
            'pypy_thread_attach': pypy_thread_attach,
            'pypy_setup_home': pypy_setup_home}


# _____ Define and setup target ___

# for now this will do for option handling

class PyPyTarget(object):

    usage = SUPPRESS_USAGE

    take_options = True
    space = None

    def opt_parser(self, config):
        parser = to_optparse(config, useoptions=["objspace.*"],
                             parserkwargs={'usage': self.usage})
        return parser

    def handle_config(self, config, translateconfig):
        if (not translateconfig.help and
            translateconfig._cfgimpl_value_owners['opt'] == 'default'):
            raise Exception("You have to specify the --opt level.\n"
                    "Try --opt=2 or --opt=jit, or equivalently -O2 or -Ojit .")
        self.translateconfig = translateconfig
        # set up the objspace optimizations based on the --opt argument
        from pypy.config.pypyoption import set_pypy_opt_level
        set_pypy_opt_level(config, translateconfig.opt)

    def print_help(self, config):
        self.opt_parser(config).print_help()

    def get_additional_config_options(self):
        from pypy.config.pypyoption import pypy_optiondescription
        return pypy_optiondescription

    def target(self, driver, args):
        driver.exe_name = 'pypy-%(backend)s'

        config = driver.config
        parser = self.opt_parser(config)

        parser.parse_args(args)

        # expose the following variables to ease debugging
        global space, entry_point

        if config.objspace.allworkingmodules:
            from pypy.config.pypyoption import enable_allworkingmodules
            enable_allworkingmodules(config)
        if config.objspace.translationmodules:
            from pypy.config.pypyoption import enable_translationmodules
            enable_translationmodules(config)

        config.translation.suggest(check_str_without_nul=True)
        config.translation.suggest(shared=True)
        config.translation.suggest(icon=os.path.join(this_dir, 'pypy.ico'))
        if config.translation.shared:
            if config.translation.output is not None:
                raise Exception("Cannot use the --output option with PyPy "
                                "when --shared is on (it is by default). "
                                "See issue #1971.")

        # if both profopt and profoptpath are specified then we keep them as they are with no other changes
        if config.translation.profopt:
            if config.translation.profoptargs is None:
                config.translation.profoptargs = "$(RPYDIR)/../lib-python/2.7/test/regrtest.py --pgo -x test_asyncore test_gdb test_multiprocessing test_subprocess || true"
        elif config.translation.profoptargs is not None:
            raise Exception("Cannot use --profoptargs without specifying --profopt as well")

        if sys.platform == 'win32':
            libdir = thisdir.join('..', '..', 'libs')
            libdir.ensure(dir=1)
            config.translation.libname = str(libdir.join('python27.lib'))

        if config.translation.thread:
            config.objspace.usemodules.thread = True
        elif config.objspace.usemodules.thread:
            try:
                config.translation.thread = True
            except ConflictConfigError:
                # If --allworkingmodules is given, we reach this point
                # if threads cannot be enabled (e.g. they conflict with
                # something else).  In this case, we can try setting the
                # usemodules.thread option to False again.  It will
                # cleanly fail if that option was set to True by the
                # command-line directly instead of via --allworkingmodules.
                config.objspace.usemodules.thread = False

        if config.translation.continuation:
            config.objspace.usemodules._continuation = True
        elif config.objspace.usemodules._continuation:
            try:
                config.translation.continuation = True
            except ConflictConfigError:
                # Same as above: try to auto-disable the _continuation
                # module if translation.continuation cannot be enabled
                config.objspace.usemodules._continuation = False

        if not config.translation.rweakref:
            config.objspace.usemodules._weakref = False

        if config.translation.jit:
            config.objspace.usemodules.pypyjit = True
        elif config.objspace.usemodules.pypyjit:
            config.translation.jit = True

        if config.translation.sandbox:
            assert 0, ("--sandbox is not tested nor maintained.  If you "
                       "really want to try it anyway, remove this line in "
                       "pypy/goal/targetpypystandalone.py.")
            config.objspace.lonepycfiles = False

        if config.objspace.usemodules.cpyext:
            if config.translation.gc not in ('incminimark', 'boehm'):
                raise Exception("The 'cpyext' module requires the 'incminimark'"
                    " or 'boehm' GC.  You need either 'targetpypystandalone.py"
                    " --withoutmod-cpyext', or use one of these two GCs.")

        config.translating = True

        import translate
        translate.log_config(config.objspace, "PyPy config object")

        # obscure hack to stuff the translation options into the translated PyPy
        from pypy.module.sys.moduledef import Module as SysModule
        options = make_dict(config)
        wrapstr = 'space.wrap(%r)' % (options)  # import time
        SysModule.interpleveldefs['pypy_translation_info'] = wrapstr
        if config.objspace.usemodules._cffi_backend:
            self.hack_for_cffi_modules(driver)

        return self.get_entry_point(config)

    def hack_for_cffi_modules(self, driver):
        # HACKHACKHACK
        # ugly hack to modify target goal from compile_* to build_cffi_imports
        # this should probably get cleaned up and merged with driver.create_exe
        from rpython.tool.runsubprocess import run_subprocess
        from rpython.translator.driver import taskdef
        import types

        compile_goal, = driver.backend_select_goals(['compile'])
        @taskdef([compile_goal], "Create cffi bindings for modules")
        def task_build_cffi_imports(self):
            ''' Use cffi to compile cffi interfaces to modules'''
            filename = os.path.join(pypydir, '..', 'lib_pypy', 'pypy_tools',
                                   'build_cffi_imports.py')
            if sys.platform == 'darwin':
                argv = [filename, '--embed-dependencies']
            else:
                argv = [filename,]
            status, out, err = run_subprocess(str(driver.compute_exe_name()),
                                              argv)
            sys.stdout.write(out)
            sys.stderr.write(err)
            # otherwise, ignore errors
        driver.task_build_cffi_imports = types.MethodType(task_build_cffi_imports, driver)
        driver.tasks['build_cffi_imports'] = driver.task_build_cffi_imports, [compile_goal]
        driver.default_goal = 'build_cffi_imports'
        # HACKHACKHACK end

    def jitpolicy(self, driver):
        from pypy.module.pypyjit.policy import PyPyJitPolicy
        from pypy.module.pypyjit.hooks import pypy_hooks
        return PyPyJitPolicy(pypy_hooks)

    def get_gchooks(self):
        from pypy.module.gc.hook import LowLevelGcHooks
        if self.space is None:
            raise Exception("get_gchooks must be called after get_entry_point")
        return self.space.fromcache(LowLevelGcHooks)

    def get_entry_point(self, config):
        self.space = make_objspace(config)

        # manually imports app_main.py
        filename = os.path.join(pypydir, 'interpreter', 'app_main.py')
        app = gateway.applevel(open(filename).read(), 'app_main.py', 'app_main')
        app.hidden_applevel = False
        w_dict = app.getwdict(self.space)
        entry_point, _ = create_entry_point(self.space, w_dict)

        return entry_point, None, PyPyAnnotatorPolicy()

    def interface(self, ns):
        for name in ['take_options', 'handle_config', 'print_help', 'target',
                     'jitpolicy', 'get_entry_point',
                     'get_additional_config_options']:
            ns[name] = getattr(self, name)
        ns['get_gchooks'] = self.get_gchooks

PyPyTarget().interface(globals())