Re: [Pyobjc-dev] execing /usr/bin/python
Brought to you by:
ronaldoussoren
From: <bb...@ma...> - 2002-11-01 15:35:21
|
On Friday, November 1, 2002, at 07:05 AM, Jack Jansen wrote: >> - The python sources are copied to the Resources directory of >> the application >> >> Do we have any control over this? I'd like to be able to convert the >> sources to bytecode (slightly faster startup, and this makes it >> harder to change the code). > Please have a look at BuildApplication (and the underlying freeze > modules), so you don't go reinventing the wheel. BuildApplication > first recursively collects all modules used by a script, then > byte-compiles these and then stuffs them into OS9 resources. The last > step needs to be modified for OSX, but the first step (which is > basically shared with freeze) can definitely be used. I think we are talking about two different ways of building applications. The BuildApplication script, if I understand it correctly, builds an application without using any of the OS X development tools-- at least, none of the GUI level tools. The second means of building applications-- and the one that Ronald was asking about-- uses Project Builder to manage and build the application in the same fashion as a normal project. Having the ability to build standalone Cocoa-Python (or Carbon-Python) applications via BuildApplication and the MacPython IDE is certainly a very attractive feature -- it would be nice to be able to ship a "non-developer's development environment" that would work entirely standalone and on machines that do not otherwise have the development tools installed! In any case, I had a look at how to automatically compile all of the code in the app wrapper. It turned out to be even easier than expected and revealed a very powerful feature of Project Builder. - add a "Shell Script Build Phase" as the last build phase of the app target (select the last build phase before you add the new phase and it'll be added at the end) - check the 'run only when installing' box - set the shell to '/usr/bin/python' - set the script to: from os import * from os.path import * import compileall, re, sys def head(p): h, t = split(p) if h: p = head(h) return p appPath = join( environ['INSTALL_DIR'], head( environ['INNER_PRODUCT_SUBPATH']) ) compileall.compile_dir(appPath, rx=re.compile('pyobjc')) This will recompile the python code found within the app wrapper, but skip the 'pyobjc' subdirectory (since it is already compiled). That's it! Doing the same during a regular build requires using a different set of environment variables. You might want to 'print os.environ' to see what is available (make sure you have the build logging preference in Project Builder set to maximum verbosity). Removing the .py files from the appPath would be an easy addition. > On vrijdag, nov 1, 2002, at 03:53 Europe/Amsterdam, bb...@ma... wrote: >> On Thursday, October 31, 2002, at 04:42 PM, Ronald Oussoren wrote: >>> On Thursday, Oct 31, 2002, at 14:20 Europe/Amsterdam, bb...@ma... >>> wrote: >>>> ..... > I think there's a misunderstanding somewhere in the me->ronald->bill > conversation, as I don't see why Bill's method would be different from > mine. In other words, please explain if there is... > > Here's my scheme, with an example: > /Applications/foo.app/Contents/MacOS/foo looks for __main__.py in > Resources, and does > execve("/Applications/foo.app/Contents/MacOS/python", > ["/Applications/foo.app/Contents/MacOS/python", > "/Applications/foo.app/Contents/Resources/__main__.py", > rest of original argv]) > and Contents/MacOS/python is a symlink to /usr/bin/python. In terms of passing control to the python interpreter, the two solutions are doing same thing excepting for the symlink and the direct insertion of the python binary into the app wrapper. The key difference is in the information contained within the executable found within the app wrapper. In my solution, the executable automatically carries the information about what frameworks are used into the python interpreter by simply linking the custom executable against the appropriate frameworks. This also allows the developer to work directly with the Project Builder indices -- for example, If I write "bundle = NSBundle.bundleWithPath_(path)", I can command-double-click on the word NSBundle and Project Builder takes me straight to the declaration for NSBundle (option-double-click displays the documentation). One of the disadvantages of carrying along the python interpreter within the app wrapper is that it requires an entire python distribution to be carried along or there is the potential for a version mismatch. The alternative is to use a symlink to /usr/bin/python -- now that Apple ships python with the system, this shouldn't cause any problems but a symlink to some other interpreter becomes an issue. In any case, all of this is just compensation for the lack of embeddable interpreter shipping w/OS X [at least, AFAICT]. > I would think this works the same as Bills scheme, with the only > exception that sys.executable points to a python interpreter, so > something like system(sys.executable + " myscript.py") will work. Two solutions: (1) In Main.py (or __main__.py -- is that "more standard"?), set sys.executable to whatever python interpreter the user has selected (in my solution, that would mean reading the 'PythonBinPath' user default in a similar fashion as the bootstrap executable). (2) Make it such that the second invocation of sys.executable [which points to the bootstrap binary in my app wrapper] simply calls execve() with a command line that passes control to python along with the arguments. I.e. put recursion protection on that executable. The overhead of an extra execve() is trivial in comparison to the overhead of a full blown GUI. b.bum |