Re: [Pyobjc-dev] execing /usr/bin/python
Brought to you by:
ronaldoussoren
From: <bb...@ma...> - 2002-11-01 21:18:12
|
>> 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 bit I don't understand. First of all, if you are able to "carry > information about what frameworks are used into the python > interpreter": why would that only work if you exec() /usr/bin/python, > and not when you exec() ..../Contents/MacOS/python which is a symlink > to /usr/bin/python? Second, I don't understand how you carry this > information over. Once you exec() the running binary is blown out of > the air, and anything it is linked against disappears. And if the > carrying-over somehow happens through open files, or argv[0] or some > such it will work just as well in "my" scheme.... The symlink would work fine except for the implied fragility. If it is just a symlink to /usr/bin/python, it isn't a problem. If it is a symlink to an alternative python binary somewhere, it'll blow up on some user's systems (but will be useful in the development environment). I added some comments to the source and have included it... it is relatively self explanatory. The main() -- the Main.py file follows (it'll be renamed __main__.py in the near future). int pyobjc_main(int argc, char * const *argv, char * const *envp) { [[NSAutoreleasePool alloc] init]; // never released, execve() overlays the process const char **childArgv = alloca(sizeof(char *) * (argc + 5)); char **childEnvp = (char **)envp; const char *pythonBinPathPtr; const char *mainPyPathPtr; NSEnumerator *bundleEnumerator = [[NSBundle allFrameworks] reverseObjectEnumerator]; NSBundle *aBundle; NSBundle *mainBundle = [NSBundle mainBundle]; NSMutableArray *bundlePaths = [NSMutableArray array]; int i; // if this is set, it is most likely because of PBX or because the developer is doing something.... if ( !getenv("DYLD_FRAMEWORK_PATH") ) { // if not, put the DYLD environment into a state where we can actually load frameworks from within the app // wrapper where the frameworks may have inter-dependencies. NSArray *paths = [NSArray arrayWithObjects: [mainBundle sharedFrameworksPath], [mainBundle privateFrameworksPath], nil]; NSString *joinedPaths = [paths componentsJoinedByString: @":"]; const char *dyldFrameworkPath = [[NSString stringWithFormat: @"DYLD_FRAMEWORK_PATH=%@", joinedPaths] UTF8String]; const char *dyldLibraryPath = [[NSString stringWithFormat: @"DYLD_LIBRARY_PATH=%@", joinedPaths] UTF8String]; for(i=0; envp[i]; i++); childEnvp = malloc( sizeof(char *) * (i+5) ); bcopy( envp, childEnvp, ( i * sizeof(char *) ) ); childEnvp[i++] = (char *)dyldFrameworkPath; childEnvp[i++] = (char *)dyldLibraryPath; //! childEnvp[i++] = "DYLD_NO_FIX_PREBINDING=1"; Can't decide if this is a good idea. // useful for debugging-- set this as a default. if ([[NSUserDefaults standardUserDefaults] boolForKey: @"DYLD_PRINT_LIBRARIES"]) childEnvp[i++] = (char *)"DYLD_PRINT_LIBRARIES=1"; childEnvp[i++] = NULL; } // grab a list of all frameworks that were linked into this executable while ( aBundle = [bundleEnumerator nextObject] ) { if ( [[[aBundle bundlePath] pathExtension] isEqualToString: @"framework"] ) [bundlePaths addObject: [aBundle bundlePath]]; } // figure out which python interpreter to use NSString *pythonBinPath = [[NSUserDefaults standardUserDefaults] stringForKey: @"PythonBinPath"]; pythonBinPath = pythonBinPath ? pythonBinPath : @"/usr/bin/python"; pythonBinPathPtr = [pythonBinPath UTF8String]; // figure out the entry point -- all this goes away as it will be standardized on __main__.py NSString *mainPyFile = [[mainBundle infoDictionary] objectForKey: @"PrincipalPythonFile"]; NSString *mainPyPath = nil; if (mainPyFile) mainPyPath = [mainBundle pathForResource: mainPyFile ofType: nil]; if ( !mainPyPath ) mainPyPath = [mainBundle pathForResource: @"Main.py" ofType: nil]; if ( !mainPyPath ) [NSException raise: NSInternalInconsistencyException format: @"%s:%d pyobjc_main() Failed to find main python entry point for application. Exiting.", __FILE__, __LINE__]; mainPyPathPtr = [mainPyPath UTF8String]; // construct argv for the child // the path to the executable in the app wrapper -- must be in the app wrapper or CFBundle does not initialize correctly childArgv[0] = argv[0]; // path to the python file that acts as the main entry point childArgv[1] = mainPyPathPtr; // pass original arguments (such as -NSOpen) verbatum for (i = 1; i<argc; i++) childArgv[i+1] = argv[i]; // add an argument that lists all frameworks childArgv[i+1] = "-PyFrameworkPaths"; childArgv[i+2] = [[bundlePaths componentsJoinedByString: @":"] UTF8String]; // terminate the arg list childArgv[i+3] = NULL; // print a nice debugging helper message, if enabled if ([[[NSProcessInfo processInfo] environment] objectForKey: @"SHOWPID"]) NSLog(@"Process ID is: %d (\n\tgdb %s %d\n to debug)", getpid(), pythonBinPathPtr, getpid()); // pass control to the python interpreter return execve(pythonBinPathPtr, (char **)childArgv, childEnvp); } int main(int argc, char * const *argv, char * const *envp) { return pyobjc_main(argc, argv, envp); } Add a Main.py: # # Upon an installation build, the project copies the PyObjC modules # into the 'pyobjc' directory within the Resources directory of # the app wrapper. The following adjusts sys.path to include that # directory. # import sys import os.path sys.path.insert(0, os.path.join(sys.path[0], "pyobjc")) del sys.argv[0] # # Import the components of the Python<->ObjC bridge. # import objc import Foundation import AppKit # # Automatically load any frameworks identified by the bootstrap # process (the code in bin-python-main.m). # # If any of the frameworks have an Init.py, it will be executed # and, as such, can be used to load/initialize any of the # frameworks in the Python interpreter context. # pyFrameworkPathsIndex = sys.argv.index("-PyFrameworkPaths") if not (pyFrameworkPathsIndex == -1): import string from Foundation import NSBundle paths = string.split(sys.argv[pyFrameworkPathsIndex + 1], ":") del sys.argv[ pyFrameworkPathsIndex : pyFrameworkPathsIndex + 2 ] count = 0 for path in paths: bundle = NSBundle.bundleWithPath_(path) bundle.principalClass() sys.path.insert(count, bundle.resourcePath()) count = count + 1 initPath = bundle.pathForResource_ofType_( "Init", "py") if initPath: execfile(initPath, globals(), locals()) # # Import application specific componentry. At the least, all # classes necessary to load the main NIB file must be loaded here. # ..... # # Pass control to the Appkit. # # From this point on, application intiailization, execution and # termination works exactly like any other Cocoa application. # sys.exit( AppKit.NSApplicationMain(sys.argv) ) |