diff --git a/installer/osx/py2app/launcher.c b/installer/osx/py2app/launcher.c new file mode 100644 index 0000000000..47d1c723c2 --- /dev/null +++ b/installer/osx/py2app/launcher.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include + +static const char *ERR_UNKNOWNPYTHONEXCEPTION = "An uncaught exception was raised during execution of the main script, but its class or name could not be determined"; + +static int +report_error(const char *msg) { + fprintf(stderr, msg); + fprintf(stderr, "\n"); + fflush(stderr); + return -1; +} + +// These variable must be filled in before compiling +static const char *ENV_VARS[] = { /*ENV_VARS*/ NULL }; +static const char *ENV_VAR_VALS[] = { /*ENV_VAR_VALS*/ NULL}; +static char PROGRAM[] = "**PROGRAM**"; +static const char MODULE[] = "**MODULE**"; + +#define EXE "@executable_path/.." + +static void +set_env_vars(const char* exe_path, const char* rpath) { + int i = 0; + char buf[3*PATH_MAX]; + const char *env_var, *val; + + while(1) { + env_var = ENV_VARS[i]; + if (env_var == NULL) break; + val = ENV_VAR_VALS[i++]; + if (strstr(val, EXE) == val && strlen(val) >= strlen(EXE)+1) { + strncpy(buf, exe_path, 3*PATH_MAX-150); + strncpy(buf+strlen(exe_path), val+strlen(EXE), 150); + setenv(env_var, buf, 1); + } else + setenv(env_var, val, 1); + } + setenv("CALIBRE_LAUNCH_MODULE", MODULE, 1); + setenv("RESOURCEPATH", rpath, 1); + return; +} + +int +main(int argc, char * const *argv, char * const *envp) { + char *pathPtr = NULL; + char buf[3*PATH_MAX]; + int ret, i; + + + uint32_t buf_size = PATH_MAX+1; + char *ebuf = calloc(buf_size, sizeof(char)); + ret = _NSGetExecutablePath(ebuf, &buf_size); + if (ret == -1) { + free(ebuf); + ebuf = calloc(buf_size, sizeof(char)); + if (_NSGetExecutablePath(ebuf, &buf_size) != 0) + return report_error("Failed to find real path of executable."); + } + pathPtr = realpath(ebuf, buf); + if (pathPtr == NULL) { + return report_error(strerror(errno)); + } + char *t; + for (i = 0; i < 3; i++) { + t = rindex(pathPtr, '/'); + if (t == NULL) return report_error("Failed to determine bundle path."); + *t = '\0'; + } + + + + char rpath[PATH_MAX+1]; + strncpy(rpath, pathPtr, strlen(pathPtr)); + strncat(rpath, "/Contents/Resources", 50); + char exe_path[PATH_MAX+1]; + strncpy(exe_path, pathPtr, strlen(pathPtr)); + strncat(exe_path, "/Contents", 50); + + set_env_vars(exe_path, rpath); + + char main_script[PATH_MAX+1]; + strncpy(main_script, rpath, strlen(rpath)); + strncat(main_script, "/launcher.py", 20); + + Py_SetProgramName(PROGRAM); + + Py_Initialize(); + + char **argv_new = calloc(argc+1, sizeof(char *)); + argv_new[argc] = NULL; + argv_new[0] = main_script; + memcpy(&argv_new[1], &argv[1], (argc - 1) * sizeof(char *)); + PySys_SetArgv(argc, argv_new); + + FILE *main_script_file = fopen(main_script, "r"); + int rval = PyRun_SimpleFileEx(main_script_file, main_script, 1); + + while (rval != 0) { + PyObject *exc, *exceptionClassName, *v, *exceptionName; + exc = PySys_GetObject("last_type"); + + if ( !exc ) { + rval = report_error(ERR_UNKNOWNPYTHONEXCEPTION); + break; + } + + exceptionClassName = PyObject_GetAttrString(exc, "__name__"); + if (!exceptionClassName) { + rval = report_error(ERR_UNKNOWNPYTHONEXCEPTION); + break; + } + + v = PySys_GetObject("last_value"); + exceptionName = (v ? PyObject_Str(v) : NULL); + + char *class = PyString_AsString(exceptionClassName); + char *exception = ""; + Py_DecRef(exceptionClassName); + if (exceptionName) { + exception = PyString_AsString(exceptionName); + Py_DecRef(exceptionName); + } + char msg[2000]; + strncpy(msg, "An unexpected error occurred: ", 100); + strncpy(msg, class, 500); + strncpy(msg, " : ", 3); + strncpy(msg, exception, 500); + rval = report_error(msg); + break; + + } + Py_Finalize(); + return rval; +} diff --git a/installer/osx/py2app/launcher.py b/installer/osx/py2app/launcher.py index 468feca940..14d304e7ee 100644 --- a/installer/osx/py2app/launcher.py +++ b/installer/osx/py2app/launcher.py @@ -35,22 +35,24 @@ _recipes_pil_prescript(['Hdf5StubImagePlugin', 'FitsStubImagePlugin', 'SunImageP def _run(): global __file__ - import os, sys, site - sys.frozen = 'macosx_app' + import os, sys base = os.environ['RESOURCEPATH'] + + sys.frozen = 'macosx_app' sys.frameworks_dir = os.path.join(os.path.dirname(base), 'Frameworks') sys.new_app_bundle = True - site.addsitedir(base) - site.addsitedir(os.path.join(base, 'Python', 'site-packages')) + sys.site_packages = os.path.join(base, 'Python', 'site-packages') + sys.binaries_path = os.path.join(os.path.dirname(base), 'MacOS') + sys.console_binaries_path = os.path.join(os.path.dirname(base), + 'console.app', 'Contents', 'MacOS') + exe = os.environ.get('CALIBRE_LAUNCH_MODULE', 'calibre.gui2.main') exe = os.path.join(base, 'Python', 'site-packages', *exe.split('.')) exe += '.py' sys.argv[0] = __file__ = exe - argv = os.environ.get('CALIBRE_LAUNCH_ARGV', None) - if argv is not None: - import cPickle - argv = cPickle.loads(argv) - sys.argv[1:] = argv + for arg in list(sys.argv[1:]): + if arg.startswith('-psn'): + sys.argv.remove(arg) execfile(exe, globals(), globals()) _run() diff --git a/installer/osx/py2app/loader.py b/installer/osx/py2app/loader.py deleted file mode 100644 index ea6565c5f5..0000000000 --- a/installer/osx/py2app/loader.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python - -__license__ = 'GPL v3' -__copyright__ = '2009, Kovid Goyal ' -__docformat__ = 'restructuredtext en' - - -import os, sys, cPickle - -ENV = {}##ENV## -MODULE = ''##MODULE## - -path = os.path.abspath(os.path.realpath(__file__)) -dirpath = os.path.dirname(path) -name = os.path.basename(path) -base_dir = os.path.dirname(os.path.dirname(dirpath)) -resources_dir = os.path.join(base_dir, 'Resources') -frameworks_dir = os.path.join(base_dir, 'Frameworks') -exe_dir = os.path.join(base_dir, 'MacOS') -base_name = os.path.splitext(name)[0] -python = os.path.join(base_dir, 'MacOS', 'Python') - -for key, val in ENV.items(): - if val.startswith('@exec'): - ENV[key] = os.path.normpath(val.replace('@executable_path', exe_dir)) -ENV['CALIBRE_LAUNCH_MODULE'] = MODULE -ENV['CALIBRE_LAUNCH_ARGV'] = cPickle.dumps(sys.argv[1:], -1) -ENV['RESOURCEPATH'] = resources_dir -os.environ.update(ENV) -launcher = os.path.join(resources_dir, 'launcher.py') -args = ['-OO', launcher] -os.execv(python, args) - diff --git a/installer/osx/py2app/main.c b/installer/osx/py2app/main.c deleted file mode 100644 index bc4ec1a434..0000000000 --- a/installer/osx/py2app/main.c +++ /dev/null @@ -1,1003 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -/* - Typedefs -*/ - -typedef int PyObject; -typedef void (*Py_DecRefPtr)(PyObject *); -typedef void (*Py_SetProgramNamePtr)(const char *); -typedef void (*Py_InitializePtr)(void); -typedef int (*PyRun_SimpleFilePtr)(FILE *, const char *); -typedef void (*Py_FinalizePtr)(void); -typedef PyObject *(*PySys_GetObjectPtr)(const char *); -typedef int *(*PySys_SetArgvPtr)(int argc, char **argv); -typedef PyObject *(*PyObject_StrPtr)(PyObject *); -typedef const char *(*PyString_AsStringPtr)(PyObject *); -typedef PyObject *(*PyObject_GetAttrStringPtr)(PyObject *, const char *); -static void DefaultDecRef(PyObject *op) { - if (op != NULL) --(*op); -} - -typedef CFTypeRef id; -typedef const char *SEL; -typedef signed char BOOL; -#define NSAlertAlternateReturn 0 - -/* - Forward declarations -*/ -static int report_error(const char *); -static CFTypeRef getKey(const char *key); - -/* - Strings -*/ -static const char *ERR_REALLYBADTITLE = "The application could not be launched."; -static const char *ERR_TITLEFORMAT = "%@ has encountered a fatal error, and will now terminate."; -static const char *ERR_NONAME = "The Info.plist file must have values for the CFBundleName or CFBundleExecutable strings."; -static const char *ERR_PYRUNTIMELOCATIONS = "The Info.plist file must have a PyRuntimeLocations array containing string values for preferred Python runtime locations. These strings should be \"otool -L\" style mach ids; \"@executable_stub\" and \"~\" prefixes will be translated accordingly."; -static const char *ERR_NOPYTHONRUNTIME = "A Python runtime could be located. You may need to install a framework build of Python, or edit the PyRuntimeLocations array in this application's Info.plist file."; -static const char *ERR_NOPYTHONSCRIPT = "A main script could not be located in the Resources folder.;"; -static const char *ERR_LINKERRFMT = "An internal error occurred while attempting to link:\r\r%s\r\r"; -static const char *ERR_UNKNOWNPYTHONEXCEPTION = "An uncaught exception was raised during execution of the main script, but its class or name could not be determined"; -static const char *ERR_PYTHONEXCEPTION = "An uncaught exception was raised during execution of the main script:\r\r%@: %@\r\rThis may mean that an unexpected error has occurred, or that you do not have all of the dependencies for this application.\r\rSee the Console for a detailed traceback."; -static const char *ERR_COLONPATH = "Python applications can not currently run from paths containing a '/' (or ':' from the Terminal)."; -static const char *ERR_DEFAULTURLTITLE = "Visit Website"; -static const char *ERR_CONSOLEAPP = "Console.app"; -static const char *ERR_CONSOLEAPPTITLE = "Open Console"; -static const char *ERR_TERMINATE = "Terminate"; - -/* - Constants -*/ - -#define PYMACAPP_DYLD_FLAGS RTLD_LAZY|RTLD_GLOBAL - -/* - Globals -*/ -static CFMutableArrayRef pool; - -#define USES(NAME) static __typeof__(&NAME) x ## NAME -/* ApplicationServices */ -USES(LSOpenFSRef); -USES(LSFindApplicationForInfo); -USES(GetCurrentProcess); -USES(SetFrontProcess); -/* CoreFoundation */ -USES(CFArrayRemoveValueAtIndex); -USES(CFStringCreateFromExternalRepresentation); -USES(CFStringAppendCString); -USES(CFStringCreateMutable); -USES(kCFTypeArrayCallBacks); -USES(CFArrayCreateMutable); -USES(CFRetain); -USES(CFRelease); -USES(CFBundleGetMainBundle); -USES(CFBundleGetValueForInfoDictionaryKey); -USES(CFArrayGetCount); -USES(CFStringCreateWithCString); -USES(CFArrayGetValueAtIndex); -USES(CFArrayAppendValue); -USES(CFStringFind); -USES(CFBundleCopyPrivateFrameworksURL); -USES(CFURLCreateWithFileSystemPathRelativeToBase); -USES(CFStringCreateWithSubstring); -USES(CFStringGetLength); -USES(CFURLGetFileSystemRepresentation); -USES(CFURLCreateWithFileSystemPath); -USES(CFShow); -USES(CFBundleCopyResourcesDirectoryURL); -USES(CFURLCreateFromFileSystemRepresentation); -USES(CFURLCreateFromFileSystemRepresentationRelativeToBase); -USES(CFStringGetCharacterAtIndex); -USES(CFURLCreateWithString); -USES(CFStringGetCString); -USES(CFStringCreateByCombiningStrings); -USES(CFDictionaryGetValue); -USES(CFBooleanGetValue); -USES(CFStringCreateArrayBySeparatingStrings); -USES(CFArrayAppendArray); -USES(CFStringCreateByCombiningStrings); -USES(CFStringCreateWithFormat); -USES(CFBundleCopyResourceURL); -USES(CFBundleCopyAuxiliaryExecutableURL); -USES(CFURLCreateCopyDeletingLastPathComponent); -USES(CFURLCreateCopyAppendingPathComponent); -USES(CFURLCopyLastPathComponent); -USES(CFStringGetMaximumSizeForEncoding); -#undef USES - -/* - objc -*/ - -#define CLS(name) xobjc_getClass(name) -#define MSG(receiver, selName, ...) \ - xobjc_msgSend(receiver, xsel_getUid(selName), ## __VA_ARGS__) -static id (*xobjc_getClass)(const char *name); -static SEL (*xsel_getUid)(const char *str); -static id (*xobjc_msgSend)(id self, SEL op, ...); - -/* - Cocoa -*/ -static void (*xNSLog)(CFStringRef format, ...); -static BOOL (*xNSApplicationLoad)(void); -static int (*xNSRunAlertPanel)(CFStringRef title, CFStringRef msg, CFStringRef defaultButton, CFStringRef alternateButton, CFStringRef otherButton, ...); - -/* - Functions -*/ - -static int bind_objc_Cocoa_ApplicationServices(void) { - static Boolean bound = false; - if (bound) return 0; - bound = true; - void *cf_dylib; - cf_dylib = dlopen("/usr/lib/libobjc.dylib", PYMACAPP_DYLD_FLAGS); - if (cf_dylib == NULL) return -1; -#define LOOKUP(NAME) do { \ - void *tmpSymbol = dlsym( \ - cf_dylib, #NAME); \ - if (tmpSymbol == NULL) return -1; \ - x ## NAME = (__typeof__(x ## NAME))(tmpSymbol); \ - } while (0) - - LOOKUP(objc_getClass); - LOOKUP(sel_getUid); - LOOKUP(objc_msgSend); - - cf_dylib = dlopen( - "/System/Library/Frameworks/Cocoa.framework/Cocoa", - PYMACAPP_DYLD_FLAGS); - if (cf_dylib == NULL) return -1; - LOOKUP(NSLog); - LOOKUP(NSApplicationLoad); - LOOKUP(NSRunAlertPanel); - -#undef LOOKUP - - cf_dylib = dlopen( - "/System/Library/Frameworks/ApplicationServices.framework/ApplicationServices", - PYMACAPP_DYLD_FLAGS); - if (cf_dylib == NULL) return -1; -#define LOOKUP(NAME) do { \ - void *tmpSymbol = dlsym( \ - cf_dylib, #NAME); \ - if (tmpSymbol == NULL) return -1; \ - x ## NAME = (__typeof__(&NAME))(tmpSymbol); \ - } while (0) - - LOOKUP(GetCurrentProcess); - LOOKUP(SetFrontProcess); - LOOKUP(LSOpenFSRef); - LOOKUP(LSFindApplicationForInfo); -#undef LOOKUP - return 0; -} - -static int bind_CoreFoundation(void) { - static Boolean bound = false; - void *cf_dylib; - if (bound) return 0; - bound = true; - cf_dylib = dlopen( - "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", - PYMACAPP_DYLD_FLAGS); - if (cf_dylib == NULL) return -1; - -#define LOOKUP(NAME) do { \ - void *tmpSymbol = dlsym( \ - cf_dylib, #NAME); \ - if (tmpSymbol == NULL) return -1; \ - x ## NAME = (__typeof__(&NAME))(tmpSymbol); \ - } while (0) - - LOOKUP(CFArrayRemoveValueAtIndex); - LOOKUP(CFStringCreateFromExternalRepresentation); - LOOKUP(CFStringAppendCString); - LOOKUP(CFStringCreateMutable); - LOOKUP(kCFTypeArrayCallBacks); - LOOKUP(CFArrayCreateMutable); - LOOKUP(CFRetain); - LOOKUP(CFRelease); - LOOKUP(CFBundleGetMainBundle); - LOOKUP(CFBundleGetValueForInfoDictionaryKey); - LOOKUP(CFArrayGetCount); - LOOKUP(CFStringCreateWithCString); - LOOKUP(CFArrayGetValueAtIndex); - LOOKUP(CFArrayAppendValue); - LOOKUP(CFStringFind); - LOOKUP(CFBundleCopyPrivateFrameworksURL); - LOOKUP(CFURLCreateWithFileSystemPathRelativeToBase); - LOOKUP(CFStringCreateWithSubstring); - LOOKUP(CFStringGetLength); - LOOKUP(CFURLGetFileSystemRepresentation); - LOOKUP(CFURLCreateWithFileSystemPath); - LOOKUP(CFShow); - LOOKUP(CFBundleCopyResourcesDirectoryURL); - LOOKUP(CFURLCreateFromFileSystemRepresentation); - LOOKUP(CFURLCreateFromFileSystemRepresentationRelativeToBase); - LOOKUP(CFStringGetCharacterAtIndex); - LOOKUP(CFURLCreateWithString); - LOOKUP(CFStringGetCString); - LOOKUP(CFStringCreateByCombiningStrings); - LOOKUP(CFDictionaryGetValue); - LOOKUP(CFBooleanGetValue); - LOOKUP(CFStringCreateArrayBySeparatingStrings); - LOOKUP(CFArrayAppendArray); - LOOKUP(CFStringCreateByCombiningStrings); - LOOKUP(CFStringCreateWithFormat); - LOOKUP(CFBundleCopyResourceURL); - LOOKUP(CFBundleCopyAuxiliaryExecutableURL); - LOOKUP(CFURLCreateCopyDeletingLastPathComponent); - LOOKUP(CFURLCreateCopyAppendingPathComponent); - LOOKUP(CFURLCopyLastPathComponent); - LOOKUP(CFStringGetMaximumSizeForEncoding); - -#undef LOOKUP - - return 0; -} - -#define AUTORELEASE(obj) ((obj == NULL) ? NULL : ( \ - xCFArrayAppendValue(pool, (const void *)obj), \ - xCFRelease(obj), \ - obj)) - -#define xCFSTR(s) AUTORELEASE( \ - xCFStringCreateWithCString(NULL, s, kCFStringEncodingUTF8)) - -static int openConsole(void) { - OSStatus err; - FSRef consoleRef; - err = xLSFindApplicationForInfo( - kLSUnknownCreator, - NULL, - xCFSTR(ERR_CONSOLEAPP), - &consoleRef, - NULL); - if (err != noErr) return err; - return xLSOpenFSRef((const FSRef *)&consoleRef, NULL); -} - -static CFTypeRef getKey(const char *key) { - CFTypeRef rval; - CFStringRef cfKey = xCFStringCreateWithCString(NULL, - key, kCFStringEncodingUTF8); - if (!cfKey) return NULL; - rval = xCFBundleGetValueForInfoDictionaryKey( - xCFBundleGetMainBundle(), - cfKey); - xCFRelease(cfKey); - return rval; -} - -static CFStringRef getApplicationName(void) { - static CFStringRef name = NULL; - if (name) return name; - name = (CFStringRef)getKey("CFBundleName"); - if (!name) name = (CFStringRef)getKey("CFBundleExecutable"); - return AUTORELEASE(name); -} - - -static CFStringRef getErrorTitle(CFStringRef applicationName) { - CFStringRef res; - if (!applicationName) return xCFSTR(ERR_REALLYBADTITLE); - res = xCFStringCreateWithFormat( - NULL, NULL, xCFSTR(ERR_TITLEFORMAT), applicationName); - AUTORELEASE(res); - return res; -} - -static void ensureGUI(void) { - ProcessSerialNumber psn; - id app = MSG(CLS("NSApplication"), "sharedApplication"); - xNSApplicationLoad(); - MSG(app, "activateIgnoringOtherApps:", (BOOL)1); - if (xGetCurrentProcess(&psn) == noErr) { - xSetFrontProcess(&psn); - } -} - -static int report_error(const char *error) { - int choice; - id releasePool; - if (bind_objc_Cocoa_ApplicationServices()) { - fprintf(stderr, "%s\n", error); - return -1; - } - releasePool = MSG(MSG(CLS("NSAutoreleasePool"), "alloc"), "init"); - xNSLog(xCFSTR("%@"), xCFSTR(error)); - if (!xNSApplicationLoad()) { - xNSLog(xCFSTR("NSApplicationLoad() failed")); - } else { - ensureGUI(); - choice = xNSRunAlertPanel( - getErrorTitle(getApplicationName()), - xCFSTR("%@"), - xCFSTR(ERR_TERMINATE), - xCFSTR(ERR_CONSOLEAPPTITLE), - NULL, - xCFSTR(error)); - if (choice == NSAlertAlternateReturn) openConsole(); - } - MSG(releasePool, "release"); - return -1; -} - -static CFStringRef pathFromURL(CFURLRef anURL) { - UInt8 buf[PATH_MAX]; - xCFURLGetFileSystemRepresentation(anURL, true, buf, sizeof(buf)); - return xCFStringCreateWithCString(NULL, (char *)buf, kCFStringEncodingUTF8); -} - -static CFStringRef pyStandardizePath(CFStringRef pyLocation) { - CFRange foundRange; - CFURLRef fmwkURL; - CFURLRef locURL; - CFStringRef subpath; - static CFStringRef prefix = NULL; - if (!prefix) prefix = xCFSTR("@executable_path/"); - foundRange = xCFStringFind(pyLocation, prefix, 0); - if (foundRange.location == kCFNotFound || foundRange.length == 0) { - return NULL; - } - fmwkURL = xCFBundleCopyPrivateFrameworksURL(xCFBundleGetMainBundle()); - foundRange.location = foundRange.length; - foundRange.length = xCFStringGetLength(pyLocation) - foundRange.length; - subpath = xCFStringCreateWithSubstring(NULL, pyLocation, foundRange); - locURL = xCFURLCreateWithFileSystemPathRelativeToBase( - NULL, - subpath, - kCFURLPOSIXPathStyle, - false, - fmwkURL); - xCFRelease(subpath); - xCFRelease(fmwkURL); - subpath = pathFromURL(locURL); - xCFRelease(locURL); - return subpath; -} - -static Boolean doesPathExist(CFStringRef path) { - struct stat st; - CFURLRef locURL; - UInt8 buf[PATH_MAX]; - locURL = xCFURLCreateWithFileSystemPath( - NULL, path, kCFURLPOSIXPathStyle, false); - xCFURLGetFileSystemRepresentation(locURL, true, buf, sizeof(buf)); - xCFRelease(locURL); - return (stat((const char *)buf, &st) == -1 ? false : true); -} - -static CFStringRef findPyLocation(CFArrayRef pyLocations) { - int i; - int cnt = xCFArrayGetCount(pyLocations); - for (i = 0; i < cnt; i++) { - CFStringRef newLoc; - CFStringRef pyLocation = xCFArrayGetValueAtIndex(pyLocations, i); - newLoc = pyStandardizePath(pyLocation); - if (!newLoc) newLoc = pyLocation; - if (doesPathExist(newLoc)) { - if (newLoc == pyLocation) xCFRetain(newLoc); - return newLoc; - } - if (newLoc) xCFRelease(newLoc); - } - return NULL; -} - -static CFStringRef tildeExpand(CFStringRef path) { - CFURLRef pathURL; - char buf[PATH_MAX]; - CFURLRef fullPathURL; - struct passwd *pwnam; - char tmp; - char *dir = NULL; - - - xCFStringGetCString(path, buf, sizeof(buf), kCFStringEncodingUTF8); - - int i; - if (buf[0] != '~') { - return xCFStringCreateWithCString( - NULL, buf, kCFStringEncodingUTF8); - } - /* user in path */ - i = 1; - while (buf[i] != '\0' && buf[i] != '/') { - i++; - } - if (i == 1) { - dir = getenv("HOME"); - } else { - tmp = buf[i]; - buf[i] = '\0'; - pwnam = getpwnam((const char *)&buf[1]); - if (pwnam) dir = pwnam->pw_dir; - buf[i] = tmp; - } - if (!dir) { - return xCFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8); - } - pathURL = xCFURLCreateFromFileSystemRepresentation( - NULL, (const UInt8*)dir, strlen(dir), false); - fullPathURL = xCFURLCreateFromFileSystemRepresentationRelativeToBase( - NULL, (const UInt8*)&buf[i + 1], strlen(&buf[i + 1]), false, pathURL); - xCFRelease(pathURL); - path = pathFromURL(fullPathURL); - xCFRelease(fullPathURL); - return path; -} - -static void setcfenv(char *name, CFStringRef value) { - char buf[PATH_MAX]; - xCFStringGetCString(value, buf, sizeof(buf), kCFStringEncodingUTF8); - setenv(name, buf, 1); -} - -static void setPythonPath(void) { - CFMutableArrayRef paths; - CFURLRef resDir; - CFStringRef resPath; - CFArrayRef resPackages; - CFDictionaryRef options; - - paths = xCFArrayCreateMutable(NULL, 0, xkCFTypeArrayCallBacks); - - resDir = xCFBundleCopyResourcesDirectoryURL(xCFBundleGetMainBundle()); - resPath = pathFromURL(resDir); - xCFArrayAppendValue(paths, resPath); - xCFRelease(resPath); - - resPackages = getKey("PyResourcePackages"); - if (resPackages) { - int i; - int cnt = xCFArrayGetCount(resPackages); - for (i = 0; i < cnt; i++) { - resPath = tildeExpand(xCFArrayGetValueAtIndex(resPackages, i)); - if (xCFStringGetLength(resPath)) { - if (xCFStringGetCharacterAtIndex(resPath, 0) != '/') { - CFURLRef absURL = xCFURLCreateWithString( - NULL, resPath, resDir); - xCFRelease(resPath); - resPath = pathFromURL(absURL); - xCFRelease(absURL); - } - xCFArrayAppendValue(paths, resPath); - } - xCFRelease(resPath); - } - } - - xCFRelease(resDir); - - options = getKey("PyOptions"); - if (options) { - CFBooleanRef use_pythonpath; - use_pythonpath = xCFDictionaryGetValue( - options, xCFSTR("use_pythonpath")); - if (use_pythonpath && xCFBooleanGetValue(use_pythonpath)) { - char *ppath = getenv("PYTHONPATH"); - if (ppath) { - CFArrayRef oldPath; - oldPath = xCFStringCreateArrayBySeparatingStrings( - NULL, xCFSTR(ppath), xCFSTR(":")); - if (oldPath) { - CFRange rng; - rng.location = 0; - rng.length = xCFArrayGetCount(oldPath); - xCFArrayAppendArray(paths, oldPath, rng); - xCFRelease(oldPath); - } - } - } - } - - if (xCFArrayGetCount(paths)) { - resPath = xCFStringCreateByCombiningStrings(NULL, paths, xCFSTR(":")); - setcfenv("PYTHONPATH", resPath); - xCFRelease(resPath); - } - xCFRelease(paths); -} - - - -static void setResourcePath(void) { - CFURLRef resDir; - CFStringRef resPath; - resDir = xCFBundleCopyResourcesDirectoryURL(xCFBundleGetMainBundle()); - resPath = pathFromURL(resDir); - xCFRelease(resDir); - setcfenv("RESOURCEPATH", resPath); - xCFRelease(resPath); -} - -static void setExecutablePath(void) { - char executable_path[PATH_MAX]; - uint32_t bufsize = PATH_MAX; - if (!_NSGetExecutablePath(executable_path, &bufsize)) { - executable_path[bufsize] = '\0'; - setenv("EXECUTABLEPATH", executable_path, 1); - } -} - -static CFStringRef getMainScript(void) { - CFMutableArrayRef possibleMains; - CFBundleRef bndl; - CFStringRef e_py, e_pyc, e_pyo, path; - int i, cnt; - possibleMains = xCFArrayCreateMutable(NULL, 0, xkCFTypeArrayCallBacks); - CFArrayRef firstMains = getKey("PyMainFileNames"); - if (firstMains) { - CFRange rng; - rng.location = 0; - rng.length = xCFArrayGetCount(firstMains); - xCFArrayAppendArray(possibleMains, firstMains, rng); - } - xCFArrayAppendValue(possibleMains, xCFSTR("__main__")); - xCFArrayAppendValue(possibleMains, xCFSTR("__realmain__")); - xCFArrayAppendValue(possibleMains, xCFSTR("launcher")); - - e_py = xCFSTR("py"); - e_pyc = xCFSTR("pyc"); - e_pyo = xCFSTR("pyo"); - - cnt = xCFArrayGetCount(possibleMains); - bndl = xCFBundleGetMainBundle(); - path = NULL; - for (i = 0; i < cnt; i++) { - CFStringRef base; - CFURLRef resURL; - base = xCFArrayGetValueAtIndex(possibleMains, i); - resURL = xCFBundleCopyResourceURL(bndl, base, e_py, NULL); - if (resURL == NULL) { - resURL = xCFBundleCopyResourceURL(bndl, base, e_pyc, NULL); - } - if (resURL == NULL) { - resURL = xCFBundleCopyResourceURL(bndl, base, e_pyo, NULL); - } - if (resURL != NULL) { - path = pathFromURL(resURL); - xCFRelease(resURL); - break; - } - } - xCFRelease(possibleMains); - return path; -} - -static int report_linkEdit_error(void) { - CFStringRef errString; - const char *errorString; - char *buf; - errorString = dlerror(); - fprintf(stderr, errorString); - errString = xCFStringCreateWithFormat( - NULL, NULL, xCFSTR(ERR_LINKERRFMT), errString); - buf = alloca(xCFStringGetMaximumSizeForEncoding( - xCFStringGetLength(errString), kCFStringEncodingUTF8)); - xCFStringGetCString(errString, buf, sizeof(buf), kCFStringEncodingUTF8); - xCFRelease(errString); - return report_error(buf); -} - -static CFStringRef getPythonInterpreter(CFStringRef pyLocation) { - CFBundleRef bndl; - CFStringRef auxName; - CFURLRef auxURL; - CFStringRef path; - - auxName = getKey("PyExecutableName"); - if (!auxName) auxName = xCFSTR("python"); - bndl = xCFBundleGetMainBundle(); - auxURL = xCFBundleCopyAuxiliaryExecutableURL(bndl, auxName); - if (auxURL) { - path = pathFromURL(auxURL); - xCFRelease(auxURL); - return path; - } - return NULL; -} - -static CFStringRef getErrorScript(void) { - CFMutableArrayRef errorScripts; - CFBundleRef bndl; - CFStringRef path; - int i, cnt; - errorScripts = xCFArrayCreateMutable(NULL, 0, xkCFTypeArrayCallBacks); - CFArrayRef firstErrorScripts = getKey("PyErrorScripts"); - if (firstErrorScripts) { - CFRange rng; - rng.location = 0; - rng.length = xCFArrayGetCount(firstErrorScripts); - xCFArrayAppendArray(errorScripts, firstErrorScripts, rng); - } - xCFArrayAppendValue(errorScripts, xCFSTR("__error__")); - xCFArrayAppendValue(errorScripts, xCFSTR("__error__.py")); - xCFArrayAppendValue(errorScripts, xCFSTR("__error__.pyc")); - xCFArrayAppendValue(errorScripts, xCFSTR("__error__.pyo")); - xCFArrayAppendValue(errorScripts, xCFSTR("__error__.sh")); - - cnt = xCFArrayGetCount(errorScripts); - bndl = xCFBundleGetMainBundle(); - path = NULL; - for (i = 0; i < cnt; i++) { - CFStringRef base; - CFURLRef resURL; - base = xCFArrayGetValueAtIndex(errorScripts, i); - resURL = xCFBundleCopyResourceURL(bndl, base, NULL, NULL); - if (resURL) { - path = pathFromURL(resURL); - xCFRelease(resURL); - break; - } - } - xCFRelease(errorScripts); - return path; - -} - -static CFMutableArrayRef get_trimmed_lines(CFStringRef output) { - CFMutableArrayRef lines; - CFArrayRef tmp; - CFRange rng; - lines = xCFArrayCreateMutable(NULL, 0, xkCFTypeArrayCallBacks); - tmp = xCFStringCreateArrayBySeparatingStrings( - NULL, output, xCFSTR("\n")); - rng.location = 0; - rng.length = xCFArrayGetCount(tmp); - xCFArrayAppendArray(lines, tmp, rng); - while (true) { - CFIndex cnt = xCFArrayGetCount(lines); - CFStringRef last; - /* Nothing on stdout means pass silently */ - if (cnt <= 0) { - xCFRelease(lines); - return NULL; - } - last = xCFArrayGetValueAtIndex(lines, cnt - 1); - if (xCFStringGetLength(last) > 0) break; - xCFArrayRemoveValueAtIndex(lines, cnt - 1); - } - return lines; -} - -static int report_script_error(const char *msg, CFStringRef cls, CFStringRef name) { - CFStringRef errorScript; - CFMutableArrayRef lines; - CFRange foundRange; - CFStringRef lastLine; - CFStringRef output = NULL; - CFIndex lineCount; - CFURLRef buttonURL = NULL; - CFStringRef buttonString = NULL; - CFStringRef title = NULL; - CFStringRef errmsg = NULL; - id releasePool; - int errBinding; - int status = 0; - char *buf; - - - if (cls && name) { - CFStringRef errString = xCFStringCreateWithFormat( - NULL, NULL, xCFSTR(msg), cls, name); - buf = alloca(xCFStringGetMaximumSizeForEncoding( - xCFStringGetLength(errString), kCFStringEncodingUTF8)); - xCFStringGetCString( - errString, buf, sizeof(buf), kCFStringEncodingUTF8); - xCFRelease(errString); - } else { - buf = (char *)msg; - } - - errorScript = getErrorScript(); - if (!errorScript) return report_error(buf); - - errBinding = bind_objc_Cocoa_ApplicationServices(); - if (!errBinding) { - id task, stdoutPipe, taskData; - CFMutableArrayRef argv; - releasePool = MSG(MSG(CLS("NSAutoreleasePool"), "alloc"), "init"); - task = MSG(MSG(CLS("NSTask"), "alloc"), "init"); - stdoutPipe = MSG(CLS("NSPipe"), "pipe"); - MSG(task, "setLaunchPath:", xCFSTR("/bin/sh")); - MSG(task, "setStandardOutput:", stdoutPipe); - argv = xCFArrayCreateMutable(NULL, 0, xkCFTypeArrayCallBacks); - xCFArrayAppendValue(argv, errorScript); - xCFArrayAppendValue(argv, getApplicationName()); - if (cls && name) { - xCFArrayAppendValue(argv, cls); - xCFArrayAppendValue(argv, name); - } - MSG(task, "setArguments:", argv); - /* This could throw, in theory, but /bin/sh should prevent that */ - MSG(task, "launch"); - MSG(task, "waitUntilExit"); - taskData = MSG( - MSG(stdoutPipe, "fileHandleForReading"), - "readDataToEndOfFile"); - xCFRelease(argv); - - status = (int)MSG(task, "terminationStatus"); - xCFRelease(task); - if (!status && taskData) { - output = xCFStringCreateFromExternalRepresentation( - NULL, taskData, kCFStringEncodingUTF8); - } - - MSG(releasePool, "release"); - } - - xCFRelease(errorScript); - if (status || !output) return report_error(buf); - - lines = get_trimmed_lines(output); - xCFRelease(output); - /* Nothing on stdout means pass silently */ - if (!lines) return -1; - lineCount = xCFArrayGetCount(lines); - lastLine = xCFArrayGetValueAtIndex(lines, lineCount - 1); - foundRange = xCFStringFind(lastLine, xCFSTR("ERRORURL: "), 0); - if (foundRange.location != kCFNotFound && foundRange.length != 0) { - CFMutableArrayRef buttonArr; - CFArrayRef tmp; - CFRange rng; - buttonArr = xCFArrayCreateMutable(NULL, 0, xkCFTypeArrayCallBacks); - tmp = xCFStringCreateArrayBySeparatingStrings( - NULL, lastLine, xCFSTR(" ")); - lineCount -= 1; - xCFArrayRemoveValueAtIndex(lines, lineCount); - rng.location = 1; - rng.length = xCFArrayGetCount(tmp) - 1; - xCFArrayAppendArray(buttonArr, tmp, rng); - xCFRelease(tmp); - while (true) { - CFStringRef tmpstr; - if (xCFArrayGetCount(buttonArr) <= 0) break; - tmpstr = xCFArrayGetValueAtIndex(buttonArr, 0); - if (xCFStringGetLength(tmpstr) == 0) { - xCFArrayRemoveValueAtIndex(buttonArr, 0); - } else { - break; - } - } - - buttonURL = xCFURLCreateWithString( - NULL, xCFArrayGetValueAtIndex(buttonArr, 0), NULL); - if (buttonURL) { - xCFArrayRemoveValueAtIndex(buttonArr, 0); - while (true) { - CFStringRef tmpstr; - if (xCFArrayGetCount(buttonArr) <= 0) break; - tmpstr = xCFArrayGetValueAtIndex(buttonArr, 0); - if (xCFStringGetLength(tmpstr) == 0) { - xCFArrayRemoveValueAtIndex(buttonArr, 0); - } else { - break; - } - } - if (xCFArrayGetCount(buttonArr) > 0) { - buttonString = xCFStringCreateByCombiningStrings( - NULL, buttonArr, xCFSTR(" ")); - } - if (!buttonString) buttonString = xCFSTR(ERR_DEFAULTURLTITLE); - } - xCFRelease(buttonArr); - - } - if (lineCount <= 0 || errBinding) { - xCFRelease(lines); - return report_error(buf); - } - - releasePool = MSG(MSG(CLS("NSAutoreleasePool"), "alloc"), "init"); - - title = xCFArrayGetValueAtIndex(lines, 0); - xCFRetain(title); - AUTORELEASE(title); - lineCount -= 1; - xCFArrayRemoveValueAtIndex(lines, lineCount); - xNSLog(xCFSTR("%@"), title); - if (lineCount > 0) { - CFStringRef showerr; - errmsg = xCFStringCreateByCombiningStrings( - NULL, lines, xCFSTR("\r")); - AUTORELEASE(errmsg); - showerr = MSG( - MSG(errmsg, "componentsSeparatedByString:", xCFSTR("\r")), - "componentsJoinedByString:", xCFSTR("\n")); - xNSLog(xCFSTR("%@"), showerr); - } else { - errmsg = xCFSTR(""); - } - - ensureGUI(); - if (!buttonURL) { - int choice = xNSRunAlertPanel( - title, xCFSTR("%@"), xCFSTR(ERR_TERMINATE), - xCFSTR(ERR_CONSOLEAPPTITLE), NULL, errmsg); - if (choice == NSAlertAlternateReturn) openConsole(); - } else { - int choice = xNSRunAlertPanel( - title, xCFSTR("%@"), xCFSTR(ERR_TERMINATE), - buttonString, NULL, errmsg); - if (choice == NSAlertAlternateReturn) { - id ws = MSG(CLS("NSWorkspace"), "sharedWorkspace"); - MSG(ws, "openURL:", buttonURL); - } - } - MSG(releasePool, "release"); - xCFRelease(lines); - return -1; -} - -static int py2app_main(int argc, char * const *argv, char * const *envp) { - CFArrayRef pyLocations; - CFStringRef pyLocation; - CFStringRef mainScript; - CFStringRef pythonInterpreter; - char *resource_path; - char buf[PATH_MAX]; - char c_pythonInterpreter[PATH_MAX]; - char c_mainScript[PATH_MAX]; - char **argv_new; - struct stat sb; - void *py_dylib; - void *tmpSymbol; - int rval; - FILE *mainScriptFile; - - - if (!getApplicationName()) return report_error(ERR_NONAME); - pyLocations = (CFArrayRef)getKey("PyRuntimeLocations"); - if (!pyLocations) return report_error(ERR_PYRUNTIMELOCATIONS); - printf("1111111\n;"); - pyLocation = findPyLocation(pyLocations); - if (!pyLocation) return report_error(ERR_NOPYTHONRUNTIME); - printf("2222222\n"); - setExecutablePath(); - setResourcePath(); - /* check for ':' in path, not compatible with Python due to Py_GetPath */ - /* XXX: Could work-around by creating something in /tmp I guess */ - resource_path = getenv("RESOURCEPATH"); - if ((resource_path == NULL) || (strchr(resource_path, ':') != NULL)) { - return report_error(ERR_COLONPATH); - } - setPythonPath(); - setenv("ARGVZERO", argv[0], 1); - - mainScript = getMainScript(); - if (!mainScript) return report_error(ERR_NOPYTHONSCRIPT); - - pythonInterpreter = getPythonInterpreter(pyLocation); - xCFStringGetCString( - pythonInterpreter, c_pythonInterpreter, - sizeof(c_pythonInterpreter), kCFStringEncodingUTF8); - xCFRelease(pythonInterpreter); - if (lstat(c_pythonInterpreter, &sb) == 0) { - if (!((sb.st_mode & S_IFLNK) == S_IFLNK)) { - setenv("PYTHONHOME", resource_path, 1); - } - } - - xCFStringGetCString(pyLocation, buf, sizeof(buf), kCFStringEncodingUTF8); - py_dylib = dlopen(buf, PYMACAPP_DYLD_FLAGS); - if (py_dylib == NULL) return report_linkEdit_error(); - printf("3333333\n"); -#define LOOKUP_SYMBOL(NAME) \ - tmpSymbol = dlsym(py_dylib, # NAME) -#define LOOKUP_DEFINEADDRESS(NAME, ADDRESS) \ - NAME ## Ptr NAME = (NAME ## Ptr)ADDRESS -#define LOOKUP_DEFINE(NAME) \ - LOOKUP_DEFINEADDRESS(NAME, (tmpSymbol)) -#define LOOKUP(NAME) \ - LOOKUP_SYMBOL(NAME); \ - if ( tmpSymbol == NULL) \ - return report_linkEdit_error(); \ - LOOKUP_DEFINE(NAME) - - LOOKUP_SYMBOL(Py_DecRef); - LOOKUP_DEFINEADDRESS(Py_DecRef, (tmpSymbol ? (tmpSymbol) : &DefaultDecRef)); - LOOKUP(Py_SetProgramName); - LOOKUP(Py_Initialize); - LOOKUP(PyRun_SimpleFile); - LOOKUP(Py_Finalize); - LOOKUP(PySys_GetObject); - LOOKUP(PySys_SetArgv); - LOOKUP(PyObject_Str); - LOOKUP(PyString_AsString); - LOOKUP(PyObject_GetAttrString); - -#undef LOOKUP -#undef LOOKUP_DEFINE -#undef LOOKUP_DEFINEADDRESS -#undef LOOKUP_SYMBOL - - Py_SetProgramName(c_pythonInterpreter); - - Py_Initialize(); - - xCFStringGetCString( - mainScript, c_mainScript, - sizeof(c_mainScript), kCFStringEncodingUTF8); - xCFRelease(mainScript); - - argv_new = alloca((argc + 1) * sizeof(char *)); - argv_new[argc] = NULL; - argv_new[0] = c_mainScript; - memcpy(&argv_new[1], &argv[1], (argc - 1) * sizeof(char *)); - PySys_SetArgv(argc, argv_new); - - mainScriptFile = fopen(c_mainScript, "r"); - rval = PyRun_SimpleFile(mainScriptFile, c_mainScript); - fclose(mainScriptFile); - - while (rval) { - PyObject *exc, *exceptionClassName, *v, *exceptionName; - CFStringRef clsName, excName; - - exc = PySys_GetObject("last_type"); - if ( !exc ) { - rval = report_error(ERR_UNKNOWNPYTHONEXCEPTION); - break; - } - - exceptionClassName = PyObject_GetAttrString(exc, "__name__"); - if (!exceptionClassName) { - rval = report_error(ERR_UNKNOWNPYTHONEXCEPTION); - break; - } - - v = PySys_GetObject("last_value"); - exceptionName = (v ? PyObject_Str(v) : NULL); - - clsName = xCFSTR(PyString_AsString(exceptionClassName)); - Py_DecRef(exceptionClassName); - if (exceptionName) { - excName = xCFSTR(PyString_AsString(exceptionName)); - Py_DecRef(exceptionName); - } else { - excName = xCFSTR(""); - } - rval = report_script_error(ERR_PYTHONEXCEPTION, clsName, excName); - break; - } - - Py_Finalize(); - - return rval; -} - -int main(int argc, char * const *argv, char * const *envp) -{ - int rval; - if (bind_CoreFoundation()) { - fprintf(stderr, "CoreFoundation not found or functions missing\n"); - return -1; - } - if (!xCFBundleGetMainBundle()) { - fprintf(stderr, "Not bundled, exiting\n"); - return -1; - } - pool = xCFArrayCreateMutable(NULL, 0, xkCFTypeArrayCallBacks); - if (!pool) { - fprintf(stderr, "Couldn't create global pool\n"); - return -1; - } - rval = py2app_main(argc, argv, envp); - xCFRelease(pool); - return rval; -} diff --git a/installer/osx/py2app/main.py b/installer/osx/py2app/main.py index fb00b4dd9b..56ab04e7f6 100644 --- a/installer/osx/py2app/main.py +++ b/installer/osx/py2app/main.py @@ -20,17 +20,50 @@ main_functions = l['main_functions'] main_modules = l['main_modules'] LICENSE = open('LICENSE', 'rb').read() ENV = dict( - FC_CONFIG_DIR='@executable_path/../Resources/fonts', - MAGICK_HOME='@executable_path/../Frameworks/ImageMagick', - PYTHONDONTWRITEBYTECODE='1', - PYTHONIOENCODING='utf-8:replace', PYTHONPATH='@executable_path/../Resources/Python/site-packages', PYTHONHOME='@executable_path/../Resources/Python', + FC_CONFIG_DIR='@executable_path/../Resources/fonts', + MAGICK_HOME='@executable_path/../Frameworks/ImageMagick', + QT_PLUGIN_PATH='@executable_path/../MacOS', + PYTHONDONTWRITEBYTECODE='1', + PYTHONIOENCODING='utf-8:replace', PYTHONOPTIMIZE='2', - QT_PLUGIN_PATH='@executable_path' ) -SW = os.environ.get('SW') +SW = os.environ.get('SW', '/sw') + +def compile_launchers(contents_dir, xprograms): + gcc = os.environ.get('CC', 'gcc') + base = os.path.dirname(__file__) + src = open(join(base, 'launcher.c'), 'rb').read() + env, env_vals = [], [] + for key, val in ENV.items(): + env.append('"%s"'% key) + env_vals.append('"%s"'% val) + env = ', '.join(env)+', ' + env_vals = ', '.join(env_vals)+', ' + src = src.replace('/*ENV_VARS*/', env) + src = src.replace('/*ENV_VAR_VALS*/', env_vals) + programs = [] + for program, module in xprograms.items(): + print '\tCompiling', program + out = join(contents_dir, 'MacOS', program) + programs.append(out) + psrc = src.replace('**PROGRAM**', program) + psrc = psrc.replace('**MODULE**', module) + fsrc = '/tmp/%s.c'%program + with open(fsrc, 'wb') as f: + f.write(psrc) + cmd = [gcc, '-Wall', '-arch', 'x86_64', + '-I%s/python/Python.framework/Headers'%SW, + fsrc, '-o', out, '-F%s/python'%SW, + '-framework', 'Python', '-framework', 'CoreFoundation', + '-headerpad_max_install_names'] + print ' '.join(cmd) + sys.stdout.flush() + subprocess.check_call(cmd) + return programs + def flipwritable(fn, mode=None): """ @@ -43,6 +76,15 @@ def flipwritable(fn, mode=None): os.chmod(fn, stat.S_IWRITE | old_mode) return old_mode +def thin(path): + try: + subprocess.check_call(['lipo', path, '-verify_arch', 'ppc64']) + print '\tThinning', path + except: + return + else: + subprocess.check_call(['lipo', path, '-thin', 'x86_64', '-output', path]) + STRIPCMD = ['/usr/bin/strip', '-x', '-S', '-'] def strip_files(files, argv_max=(256 * 1024)): """ @@ -120,8 +162,8 @@ class Py2App(object): self.copy_launcher_and_site() self.create_exe() + self.thin_to_x86_64() self.strip_files() - self.create_launchers() ret = self.makedmg(self.build_dir, APPNAME+'-'+VERSION+'-x86_64') sys.stdout.flush() @@ -134,23 +176,17 @@ class Py2App(object): return ret @flush - def create_launchers(self): - print '\nCreating launchers' - all_names = basenames['console'] + basenames['gui'] - all_modules = main_modules['console'] + main_modules['gui'] - launcher = join(os.path.dirname(__file__), 'loader.py') - launcher = open(launcher, 'rb').read() - launcher = launcher.replace('{}##ENV##', repr(ENV)) - os.mkdir(join(self.resources_dir, 'loaders')) - for basename, module in zip(all_names, all_modules): - py_file = join('src', *module.split('.'))+'.py' - shutil.copy2(py_file, join(self.resources_dir, 'Python', - 'site-packages', *module.split('.'))+'.py') - raw = launcher.replace("''##MODULE##", repr(module)) - path = join(self.resources_dir, 'loaders', basename) - open(path, 'wb').write(raw) - os.chmod(path, stat.S_IXUSR|stat.S_IXGRP|stat.S_IXOTH|stat.S_IREAD\ - |stat.S_IWUSR|stat.S_IROTH|stat.S_IRGRP) + def thin_to_x86_64(self): + print '\nThinning to x86_64' + for y in (self.frameworks_dir, join(self.resources_dir, 'Python')): + for x in os.walk(y): + for f in x[-1]: + f = join(x[0], f) + if not os.path.isfile(f): continue + for t in ('.so', '.dylib', '/Python'): + if f.endswith(t): + thin(f) + break @flush def strip_files(self): @@ -159,13 +195,19 @@ class Py2App(object): @flush def create_exe(self): - print '\nCreating executable' - gcc = os.environ.get('CC', 'gcc') - base = os.path.dirname(__file__) - out = join(self.contents_dir, 'MacOS', 'calibre') - subprocess.check_call([gcc, '-Wall', '-arch', 'x86_64', join(base, - 'main.c'), '-o', out]) - self.to_strip.append(out) + print '\nCreating launchers' + programs = {} + for program, module in zip(basenames['console'], + main_modules['console'])+zip(basenames['gui'], + main_modules['gui']): + programs[program] = module + programs = compile_launchers(self.contents_dir, programs) + for out in programs: + self.fix_dependencies_in_lib(out) + for module in main_modules['console'] + main_modules['gui']: + base = join(*module.split('.'))+'.py' + shutil.copy2(join('src', base), + join(self.resources_dir, 'Python', 'site-packages', base)) @flush def set_id(self, path_to_lib, new_id): @@ -226,10 +268,6 @@ class Py2App(object): shutil.copy2(join(curr, 'Python'), currd) self.set_id(join(currd, 'Python'), self.FID+'/Python.framework/Versions/%s/Python'%basename(curr)) - python = '%s/python/Python.framework/Versions/%s/Resources/Python.app/Contents/MacOS/Python'\ - % (SW, self.version_info) - shutil.copy2(python, join(self.contents_dir, 'MacOS')) - self.fix_dependencies_in_lib(join(self.contents_dir, 'MacOS', 'Python')) @flush def add_qt_frameworks(self): @@ -279,6 +317,9 @@ class Py2App(object): @flush def create_plist(self): + env = dict(**ENV) + env['CALIBRE_LAUNCHED_FROM_BUNDLE']='1'; + pl = dict( CFBundleDevelopmentRegion='English', CFBundleDisplayName=APPNAME, @@ -289,7 +330,6 @@ class Py2App(object): CFBundleSignature='????', CFBundleExecutable='calibre', LSMinimumSystemVersion='10.5.2', - PyRuntimeLocations=[self.FID+'/Python.framework/Versions/%s/Python'%self.version_info], LSRequiresNativeExecution=True, NSAppleScriptEnabled=False, NSHumanReadableCopyright='Copyright 2008, Kovid Goyal', @@ -297,7 +337,7 @@ class Py2App(object): 'application. Visit http://calibre.kovidgoyal.net for details.'), CFBundleIconFile='library.icns', LSMultipleInstancesProhibited=True, - LSEnvironment=ENV + LSEnvironment=env ) plistlib.writePlist(pl, join(self.contents_dir, 'Info.plist')) @@ -554,9 +594,16 @@ class Py2App(object): print '\nInstaller size: %.2fMB\n'%size return dmg +def test_exe(): + build_dir = abspath(join('build', APPNAME+'.app')) + py2app = Py2App(build_dir) + py2app.create_exe() + return 0 def main(): + if 'test_exe' in sys.argv: + return test_exe() build_dir = abspath(join('build', APPNAME+'.app')) if os.path.exists(build_dir): shutil.rmtree(build_dir) diff --git a/setup.py b/setup.py index 27adc64016..54daad2da7 100644 --- a/setup.py +++ b/setup.py @@ -73,7 +73,12 @@ if __name__ == '__main__': upload_to_pypi, stage3, stage2, stage1, upload, \ upload_rss, betas, build_linux32, build_linux64, \ build_osx64 - resources.SCRIPTS = list(basenames['console']+basenames['gui']) + resources.SCRIPTS = {} + for x in ('console', 'gui'): + for name in basenames[x]: + resources.SCRIPTS[name] = x + + list(basenames['console']+basenames['gui']) entry_points['console_scripts'].append( 'calibre_postinstall = calibre.linux:post_install') diff --git a/src/calibre/debug.py b/src/calibre/debug.py index 0b17920cd8..4ed47a2399 100644 --- a/src/calibre/debug.py +++ b/src/calibre/debug.py @@ -6,7 +6,7 @@ __copyright__ = '2008, Kovid Goyal ' Embedded console for debugging. ''' -import sys, os, re +import sys, os, re, shutil from calibre.utils.config import OptionParser from calibre.constants import iswindows, isosx from calibre.libunzip import update @@ -45,6 +45,9 @@ def update_zipfile(zipfile, mod, path): name = mod.replace('.', '/') + os.path.splitext(path)[-1] update(zipfile, [pat], [path], [name]) +def update_site_packages(sp, mod, path): + dest = os.path.join(sp, *mod.split('.'))+'.py' + shutil.copy2(path, dest) def update_module(mod, path): if not hasattr(sys, 'frozen'): @@ -52,6 +55,8 @@ def update_module(mod, path): zp = None if iswindows: zp = os.path.join(os.path.dirname(sys.executable), 'library.zip') + elif getattr(sys, 'new_app_bundle', False): + update_site_packages(sys.site_packages, mod, path) elif isosx: zp = os.path.join(os.path.dirname(getattr(sys, 'frameworks_dir')), 'Resources', 'lib', diff --git a/src/calibre/gui2/dialogs/config/__init__.py b/src/calibre/gui2/dialogs/config/__init__.py index df1ed65025..a38fe0473e 100644 --- a/src/calibre/gui2/dialogs/config/__init__.py +++ b/src/calibre/gui2/dialogs/config/__init__.py @@ -465,8 +465,10 @@ class ConfigDialog(QDialog, Ui_Dialog): from calibre.utils.osx_symlinks import create_symlinks loc, paths = create_symlinks() info_dialog(self, _('Command line tools installed'), - _('Command line tools installed in')+' '+loc, - det_msg=paths, show=True) + '

'+_('Command line tools installed in')+' '+loc+ + '
'+ _('If you move calibre.app, you have to re-install ' + 'the command line tools.'), + det_msg='\n'.join(paths), show=True) def setup_conversion_options(self): self.conversion_options = ConfigTabs(self) diff --git a/src/calibre/gui2/dialogs/config/config.ui b/src/calibre/gui2/dialogs/config/config.ui index 62979c8a5c..6e655c3468 100644 --- a/src/calibre/gui2/dialogs/config/config.ui +++ b/src/calibre/gui2/dialogs/config/config.ui @@ -699,10 +699,7 @@ - - - Free unused diskspace from the database - + &Check database integrity diff --git a/src/calibre/utils/ipc/launch.py b/src/calibre/utils/ipc/launch.py index 7a03010dc4..13ac7b23cb 100644 --- a/src/calibre/utils/ipc/launch.py +++ b/src/calibre/utils/ipc/launch.py @@ -16,6 +16,8 @@ if iswindows: import win32process _windows_null_file = open(os.devnull, 'wb') +isnewosx = isosx and getattr(sys, 'new_app_bundle') + class Worker(object): ''' Platform independent object for launching child processes. All processes @@ -45,6 +47,9 @@ class Worker(object): return os.path.join(os.path.dirname(sys.executable), 'calibre-parallel.exe' if isfrozen else \ 'Scripts\\calibre-parallel.exe') + if isnewosx: + return os.path.join(sys.console_binaries_path, 'calibre-parallel') + if isosx: if not isfrozen: return 'calibre-parallel' contents = os.path.join(self.osx_contents_dir, @@ -56,6 +61,9 @@ class Worker(object): @property def gui_executable(self): + if isnewosx: + return os.path.join(sys.binaries_path, 'calibre-parallel') + if isfrozen and isosx: return os.path.join(self.osx_contents_dir, 'MacOS', self.osx_interpreter) @@ -98,7 +106,7 @@ class Worker(object): def __init__(self, env, gui=False): self._env = {} self.gui = gui - if isosx and isfrozen: + if isosx and isfrozen and not isnewosx: contents = os.path.join(self.osx_contents_dir, 'console.app', 'Contents') resources = os.path.join(contents, 'Resources') fd = os.path.join(contents, 'Frameworks') @@ -133,7 +141,7 @@ class Worker(object): if priority is None: priority = prefs['worker_process_priority'] cmd = [exe] - if isosx: + if isosx and not isnewosx: cmd += ['-c', self.osx_prefix + 'from calibre.utils.ipc.worker import main; main()'] args = { 'env' : env, @@ -155,7 +163,7 @@ class Worker(object): ret = self._file.name if iswindows and 'stdin' not in args: - # On windows when usingthepythonw interpreter, + # On windows when using the pythonw interpreter, # stdout, stderr and stdin may not be valid args['stdin'] = subprocess.PIPE args['stdout'] = _windows_null_file diff --git a/src/calibre/utils/osx_symlinks.py b/src/calibre/utils/osx_symlinks.py index 36fa0e7b45..b40f2e6a99 100644 --- a/src/calibre/utils/osx_symlinks.py +++ b/src/calibre/utils/osx_symlinks.py @@ -6,7 +6,9 @@ __license__ = 'GPL v3' __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' -AUTHTOOL="""#!%s +import sys, os + +AUTHTOOL="""#!/usr/bin/python import os scripts = %s links = %s @@ -23,15 +25,31 @@ for s, l in zip(scripts, links): DEST_PATH = '/usr/bin' def create_symlinks(): - import os, tempfile, traceback, sys - from Authorization import Authorization, kAuthorizationFlagDestroyRights + return create_symlinks_new() if getattr(sys, 'new_app_bundle', False) else create_symlinks_old() + +def create_symlinks_new(): from calibre.resources import scripts + links = [os.path.join(DEST_PATH, i) for i in scripts] + scripts = [os.path.join( + sys.binaries_path if scripts[i] == 'gui' else sys.console_binaries_path, i) for i in scripts] + + return do_it(scripts, links) + + +def create_symlinks_old(): + from calibre.resources import scripts resources_path = os.environ['RESOURCEPATH'] links = [os.path.join(DEST_PATH, i) for i in scripts] scripts = [os.path.join(resources_path, 'loaders', i) for i in scripts] + return do_it(scripts, links) + +def do_it(scripts, links): + import os, tempfile, traceback + from Authorization import Authorization, kAuthorizationFlagDestroyRights + bad = False for s, l in zip(scripts, links): if os.path.exists(l) and os.path.exists(os.path.realpath(l)): @@ -39,19 +57,28 @@ def create_symlinks(): bad = True break if bad: + ph, pp = os.environ.get('PYTHONHOME', None), os.environ.get('PYTHONPATH', None) auth = Authorization(destroyflags=(kAuthorizationFlagDestroyRights,)) fd, name = tempfile.mkstemp('.py') - os.write(fd, AUTHTOOL % (sys.executable, repr(scripts), repr(links))) + os.write(fd, AUTHTOOL % (repr(scripts), repr(links))) os.close(fd) os.chmod(name, 0700) try: - pipe = auth.executeWithPrivileges(sys.executable, name) + if pp: + del os.environ['PYTHONPATH'] + if ph: + del os.environ['PYTHONHOME'] + pipe = auth.executeWithPrivileges(name) sys.stdout.write(pipe.read()) pipe.close() except: traceback.print_exc() finally: os.unlink(name) + if pp: + os.environ['PYTHONPATH'] = pp + if ph: + os.environ['PYTHONHOME'] = ph return DEST_PATH, links