diff --git a/bypy/macos/__main__.py b/bypy/macos/__main__.py index f68d74622e..6af9214d74 100644 --- a/bypy/macos/__main__.py +++ b/bypy/macos/__main__.py @@ -37,20 +37,25 @@ QT_FRAMEWORKS = [x.replace('5', '') for x in QT_DLLS] ENV = dict( FONTCONFIG_PATH='@executable_path/../Resources/fonts', FONTCONFIG_FILE='@executable_path/../Resources/fonts/fonts.conf', - PYTHONIOENCODING='UTF-8', SSL_CERT_FILE='@executable_path/../Resources/resources/mozilla-ca-certs.pem', ) APPNAME, VERSION = calibre_constants['appname'], calibre_constants['version'] basenames, main_modules, main_functions = calibre_constants['basenames'], calibre_constants['modules'], calibre_constants['functions'] -def compile_launcher_lib(contents_dir, gcc, base): +def compile_launcher_lib(contents_dir, gcc, base, pyver): print('\tCompiling calibre_launcher.dylib') - fd = join(contents_dir, 'Frameworks') - dest = join(fd, 'calibre-launcher.dylib') + env, env_vals = [], [] + for key, val in ENV.items(): + env.append(f'"{key}"'), env_vals.append(f'"{val}"') + env = ','.join(env) + env_vals = ','.join(env_vals) + + dest = join(contents_dir, 'Frameworks', 'calibre-launcher.dylib') src = join(base, 'util.c') cmd = [gcc] + '-Wall -dynamiclib -std=gnu99'.split() + [src] + \ - ['-I' + base] + \ + ['-I' + base] + '-DPY_VERSION_MAJOR={} -DPY_VERSION_MINOR={}'.format(*pyver.split('.')).split() + \ + [f'-DENV_VARS={env}', f'-DENV_VAR_VALS={env_vals}'] + \ ['-I%s/python/Python.framework/Versions/Current/Headers' % PREFIX] + \ '-current_version 1.0 -compatibility_version 1.0'.split() + \ '-fvisibility=hidden -o'.split() + [dest] + \ @@ -68,33 +73,19 @@ gcc = os.environ.get('CC', 'clang') def compile_launchers(contents_dir, xprograms, pyver): base = dirname(abspath(__file__)) - lib = compile_launcher_lib(contents_dir, gcc, base) - with open(join(base, 'launcher.c'), 'rb') as f: - src = f.read().decode('utf-8') - 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) + lib = compile_launcher_lib(contents_dir, gcc, base, pyver) + src = join(base, 'launcher.c') programs = [lib] for program, x in xprograms.items(): module, func, ptype = x print('\tCompiling', program) out = join(contents_dir, 'MacOS', program) programs.append(out) - psrc = src.replace('**PROGRAM**', program) - psrc = psrc.replace('**MODULE**', module) - psrc = psrc.replace('**FUNCTION**', func) - psrc = psrc.replace('**PYVER**', pyver) - psrc = psrc.replace('**IS_GUI**', ('1' if ptype == 'gui' else '0')) - fsrc = '/tmp/%s.c' % program - with open(fsrc, 'wb') as f: - f.write(psrc.encode('utf-8')) - cmd = [gcc, '-Wall', '-I' + base, fsrc, lib, '-o', out, - '-headerpad_max_install_names'] + is_gui = 'true' if ptype == 'gui' else 'false' + cmd = [ + gcc, '-Wall', f'-DPROGRAM=L"{program}"', f'-DMODULE=L"{module}"', f'-DFUNCTION=L"{func}"', f'-DIS_GUI={is_gui}', + '-I' + base, src, lib, '-o', out, '-headerpad_max_install_names' + ] # print('\t'+' '.join(cmd)) sys.stdout.flush() subprocess.check_call(cmd) @@ -194,9 +185,9 @@ class Freeze(object): self.add_misc_libraries() self.add_resources() + self.copy_site() self.compile_py_modules() - self.copy_site() self.create_exe() if not test_launchers and not self.dont_strip: self.strip_files() @@ -258,6 +249,8 @@ class Freeze(object): for x, is_id in self.get_dependencies(path_to_lib): if x.startswith('@rpath/Qt'): yield x, x[len('@rpath/'):], is_id + elif x == 'libunrar.dylib' and not is_id: + yield x, x, is_id else: for y in (PREFIX + '/lib/', PREFIX + '/python/Python.framework/'): if x.startswith(y): @@ -500,10 +493,10 @@ class Freeze(object): @flush def add_misc_libraries(self): for x in ( - 'usb-1.0.0', 'mtp.9', 'chm.0', 'sqlite3.0', 'hunspell-1.7.0', - 'icudata.64', 'icui18n.64', 'icuio.64', 'icuuc.64', 'hyphen.0', - 'xslt.1', 'exslt.0', 'xml2.2', 'z.1', 'unrar', 'lzma.5', - 'crypto.1.0.0', 'ssl.1.0.0', 'iconv.2', # 'ltdl.7' + 'usb-1.0.0', 'mtp.9', 'chm.0', 'sqlite3.0', 'hunspell-1.7.0', + 'icudata.64', 'icui18n.64', 'icuio.64', 'icuuc.64', 'hyphen.0', + 'xslt.1', 'exslt.0', 'xml2.2', 'z.1', 'unrar', 'lzma.5', + 'crypto.1.0.0', 'ssl.1.0.0', 'iconv.2', # 'ltdl.7' ): print('\nAdding', x) x = 'lib%s.dylib' % x diff --git a/bypy/macos/launcher.c b/bypy/macos/launcher.c index c7d6d0375e..805330cb15 100644 --- a/bypy/macos/launcher.c +++ b/bypy/macos/launcher.c @@ -10,32 +10,8 @@ #define arraysz(x) (sizeof(x)/sizeof(x[0])) -// These variables 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**"; -static const char FUNCTION[] = "**FUNCTION**"; -static const char PYVER[] = "**PYVER**"; - - int -main(int argc, char* const *argv, const char **envp) { - char pathbuf[PROC_PIDPATHINFO_MAXSIZE], realpath_buf[PROC_PIDPATHINFO_MAXSIZE * 5]; - pid_t pid = getpid(); - int ret = proc_pidpath(pid, pathbuf, arraysz(pathbuf)); - if (ret <= 0) fatal("failed to get executable path for current pid with error: %s", strerror(errno)); - char *path = realpath(pathbuf, realpath_buf); - if (path == NULL) fatal("failed to get realpath for executable path with error: %s", strerror(errno)); - // We re-exec using an absolute path because the Qt WebEngine sandbox does not work - // when running via symlink - const int is_gui = **IS_GUI**; - if (!is_gui && strcmp(PROGRAM, "calibre-parallel") != 0 && strcmp(argv[0], path) != 0) { - char* new_argv[1024] = {0}; - new_argv[0] = path; - for (int i = 1; i < argc && i < arraysz(new_argv) - 1; i++) new_argv[i] = argv[i]; - execv(path, new_argv); - } - - return run(ENV_VARS, ENV_VAR_VALS, PROGRAM, MODULE, FUNCTION, PYVER, is_gui, argc, argv, envp, path); +main(int argc, char * const *argv) { + run(PROGRAM, MODULE, FUNCTION, IS_GUI, argc, argv); + return 0; } diff --git a/bypy/macos/site.py b/bypy/macos/site.py index 3ea1c138a1..da270e4517 100644 --- a/bypy/macos/site.py +++ b/bypy/macos/site.py @@ -1,131 +1,14 @@ -""" -Append module search paths for third-party packages to sys.path. - -This is stripped down and customized for use in py2app applications -""" - -import sys +import builtins import os - - -def makepath(*paths): - dir = os.path.abspath(os.path.join(*paths)) - return dir, os.path.normcase(dir) - - -def abs__file__(): - """Set all module __file__ attribute to an absolute path""" - for m in sys.modules.values(): - if hasattr(m, '__loader__'): - continue # don't mess with a PEP 302-supplied __file__ - try: - m.__file__ = os.path.abspath(m.__file__) - except AttributeError: - continue - - -# This ensures that the initial path provided by the interpreter contains -# only absolute pathnames, even if we're running from the build directory. -L = [] -_dirs_in_sys_path = {} -dir = dircase = None # sys.path may be empty at this point -for dir in sys.path: - # Filter out duplicate paths (on case-insensitive file systems also - # if they only differ in case); turn relative paths into absolute - # paths. - dir, dircase = makepath(dir) - if dircase not in _dirs_in_sys_path: - L.append(dir) - _dirs_in_sys_path[dircase] = 1 -sys.path[:] = L -del dir, dircase, L -_dirs_in_sys_path = None - - -def _init_pathinfo(): - global _dirs_in_sys_path - _dirs_in_sys_path = d = {} - for dir in sys.path: - if dir and not os.path.isdir(dir): - continue - dir, dircase = makepath(dir) - d[dircase] = 1 - - -def addsitedir(sitedir): - global _dirs_in_sys_path - if _dirs_in_sys_path is None: - _init_pathinfo() - reset = 1 - else: - reset = 0 - sitedir, sitedircase = makepath(sitedir) - if sitedircase not in _dirs_in_sys_path: - sys.path.append(sitedir) # Add path component - try: - names = os.listdir(sitedir) - except os.error: - return - names.sort() - for name in names: - if name[-4:] == os.extsep + "pth": - addpackage(sitedir, name) - if reset: - _dirs_in_sys_path = None - - -def addpackage(sitedir, name): - global _dirs_in_sys_path - if _dirs_in_sys_path is None: - _init_pathinfo() - reset = 1 - else: - reset = 0 - fullname = os.path.join(sitedir, name) - try: - f = open(fullname) - except IOError: - return - while True: - dir = f.readline() - if not dir: - break - if dir[0] == '#': - continue - if dir.startswith("import"): - exec dir - continue - if dir[-1] == '\n': - dir = dir[:-1] - dir, dircase = makepath(sitedir, dir) - if dircase not in _dirs_in_sys_path and os.path.exists(dir): - sys.path.append(dir) - _dirs_in_sys_path[dircase] = 1 - if reset: - _dirs_in_sys_path = None - - -# Remove sys.setdefaultencoding() so that users cannot change the -# encoding after initialization. The test for presence is needed when -# this module is run as a script, because this code is executed twice. -# -if hasattr(sys, "setdefaultencoding"): - sys.setdefaultencoding('utf-8') - del sys.setdefaultencoding - - -def run_entry_point(): - bname, mod, func = sys.calibre_basename, sys.calibre_module, sys.calibre_function - sys.argv[0] = bname - pmod = __import__(mod, fromlist=[1], level=0) - return getattr(pmod, func)() +import sys +import _sitebuiltins def read_user_env_vars(): try: with open(os.path.expanduser('~/Library/Preferences/calibre/macos-env.txt'), 'rb') as f: raw = f.read().decode('utf-8', 'replace') - except EnvironmentError as err: + except EnvironmentError: return for line in raw.splitlines(): if line.startswith('#'): @@ -136,52 +19,46 @@ def read_user_env_vars(): os.environ[key] = os.path.expandvars(os.path.expanduser(val)) -def add_calibre_vars(base): - sys.frameworks_dir = os.path.join(os.path.dirname(base), 'Frameworks') - sys.resources_location = os.path.abspath(os.path.join(base, 'resources')) - sys.extensions_location = os.path.join(sys.frameworks_dir, 'plugins') - sys.binaries_path = os.path.join(os.path.dirname(base), 'MacOS') - - try: - read_user_env_vars() - except Exception as err: - try: - sys.stderr.write('Failed to read user env vars with error: {}\n'.format(err)) - sys.stderr.flush() - except Exception: - pass - - dv = os.environ.get('CALIBRE_DEVELOP_FROM', None) - if dv and os.path.exists(dv): - sys.path.insert(0, os.path.abspath(dv)) - - def nuke_stdout(): # Redirect stdout, stdin and stderr to /dev/null from calibre.constants import plugins plugins['speedup'][0].detach(os.devnull) +def set_helper(): + builtins.help = _sitebuiltins._Helper() + + +def set_quit(): + eof = 'Ctrl-D (i.e. EOF)' + builtins.quit = _sitebuiltins.Quitter('quit', eof) + builtins.exit = _sitebuiltins.Quitter('exit', eof) + + def main(): - global __file__ + sys.argv[0] = sys.calibre_basename + try: + read_user_env_vars() + except Exception as err: + try: + print('Failed to read user env vars with error:', err, file=sys.stderr) + sys.stderr.flush() + except Exception: + pass - # Needed on OS X <= 10.8, which passes -psn_... as a command line arg when - # starting via launch services - for arg in tuple(sys.argv[1:]): - if arg.startswith('-psn_'): - sys.argv.remove(arg) - - base = sys.resourcepath - sys.frozen = 'macosx_app' - sys.new_app_bundle = True - abs__file__() - - add_calibre_vars(base) - addsitedir(sys.site_packages) - - if sys.calibre_is_gui_app and not ( + dfv = os.environ.get('CALIBRE_DEVELOP_FROM') + if dfv and os.path.exists(dfv): + sys.path.insert(0, os.path.abspath(dfv)) + set_helper() + set_quit() + if sys.gui_app and not ( sys.stdout.isatty() or sys.stderr.isatty() or sys.stdin.isatty() ): nuke_stdout() + mod = __import__(sys.calibre_module, fromlist=[1]) + func = getattr(mod, sys.calibre_function) + return func() - return run_entry_point() + +if __name__ == '__main__': + main() diff --git a/bypy/macos/util.c b/bypy/macos/util.c index 0b3a40bbf9..95f63c0291 100644 --- a/bypy/macos/util.c +++ b/bypy/macos/util.c @@ -1,43 +1,25 @@ #include "util.h" -#include -#include +#include "../run-python.h" #include #include -#include +#include #define EXPORT __attribute__((visibility("default"))) -static const char *ERR_OOM = "Out of memory"; - -static int -report_error(const char *msg) { - fprintf(stderr, "%s\n", msg); - fflush(stderr); - return -1; -} - -static int -report_code(const char *preamble, const char* msg, int code) { - fprintf(stderr, "%s: %s\n", preamble, msg); - fflush(stderr); - return code; -} - #define EXE "@executable_path/.." +static const char *env_vars[] = { ENV_VARS }; +static const char *env_var_vals[] = { ENV_VAR_VALS }; + static void -set_env_vars(const char **ENV_VARS, const char **ENV_VAR_VALS, const char* exe_path) { - int i = 0; +set_env_vars(const char* contents_path) { 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); + for (size_t i = 0; i < arraysz(env_vars); i++) { + env_var = env_vars[i]; val = env_var_vals[i]; + if (strstr(val, EXE) == val && strlen(val) >= sizeof(EXE)) { + snprintf(buf, sizeof(buf) - 1, "%s%s", contents_path, val + sizeof(EXE) - 1); setenv(env_var, buf, 1); } else setenv(env_var, val, 1); @@ -45,168 +27,52 @@ set_env_vars(const char **ENV_VARS, const char **ENV_VAR_VALS, const char* exe_p return; } -void initialize_interpreter(const char **ENV_VARS, const char **ENV_VAR_VALS, - char *PROGRAM, const char *MODULE, const char *FUNCTION, const char *PYVER, int IS_GUI, - const char* exe_path, const char *rpath, int argc, char* const *argv) { - PyObject *pargv, *v; - int i; - Py_OptimizeFlag = 2; - Py_NoSiteFlag = 1; - Py_DontWriteBytecodeFlag = 1; - Py_IgnoreEnvironmentFlag = 1; - Py_NoUserSiteDirectory = 1; - Py_HashRandomizationFlag = 1; - - //Py_VerboseFlag = 1; - //Py_DebugFlag = 1; - - Py_SetProgramName(PROGRAM); - - char pyhome[1000]; - snprintf(pyhome, 1000, "%s/Python", rpath); - Py_SetPythonHome(pyhome); - - set_env_vars(ENV_VARS, ENV_VAR_VALS, exe_path); - - //printf("Path before Py_Initialize(): %s\r\n\n", Py_GetPath()); - Py_Initialize(); - - char *dummy_argv[1] = {""}; - PySys_SetArgv(1, dummy_argv); - //printf("Path after Py_Initialize(): %s\r\n\n", Py_GetPath()); - char path[3000]; - snprintf(path, 3000, "%s/lib/python%s:%s/lib/python%s/lib-dynload:%s/site-packages", pyhome, PYVER, pyhome, PYVER, pyhome); - - PySys_SetPath(path); - //printf("Path set by me: %s\r\n\n", path); - - PySys_SetObject("calibre_basename", PyBytes_FromString(PROGRAM)); - PySys_SetObject("calibre_module", PyBytes_FromString(MODULE)); - PySys_SetObject("calibre_function", PyBytes_FromString(FUNCTION)); - PySys_SetObject("calibre_is_gui_app", ((IS_GUI) ? Py_True : Py_False)); - PySys_SetObject("resourcepath", PyBytes_FromString(rpath)); - snprintf(path, 3000, "%s/site-packages", pyhome); - PySys_SetObject("site_packages", PyBytes_FromString(pyhome)); - - - pargv = PyList_New(argc); - if (pargv == NULL) exit(report_error(ERR_OOM)); - for (i = 0; i < argc; i++) { - v = PyBytes_FromString(argv[i]); - if (v == NULL) exit(report_error(ERR_OOM)); - PyList_SetItem(pargv, i, v); - } - PySys_SetObject("argv", pargv); - -} - - -int pyobject_to_int(PyObject *res) { - int ret; PyObject *tmp; - tmp = PyNumber_Int(res); - if (tmp == NULL) ret = (PyObject_IsTrue(res)) ? 1 : 0; - else ret = (int)PyInt_AS_LONG(tmp); - - return ret; -} - -int handle_sysexit(PyObject *e) { - PyObject *code; - - code = PyObject_GetAttrString(e, "code"); - if (!code) return 0; - if (!PyInt_Check(code)) { - PyObject_Print(code, stderr, Py_PRINT_RAW); - fflush(stderr); - } - return pyobject_to_int(code); -} - -int calibre_show_python_error(const char *preamble, int code) { - PyObject *exc, *val, *tb, *str; - int ret, issysexit = 0; char *i; - - if (!PyErr_Occurred()) return code; - issysexit = PyErr_ExceptionMatches(PyExc_SystemExit); - - PyErr_Fetch(&exc, &val, &tb); - - if (exc != NULL) { - PyErr_NormalizeException(&exc, &val, &tb); - - if (issysexit) { - return (val) ? handle_sysexit(val) : 0; - } - if (val != NULL) { - str = PyObject_Unicode(val); - if (str == NULL) { - PyErr_Clear(); - str = PyObject_Str(val); - } - i = PyString_AsString(str); - ret = report_code(preamble, (i==NULL)?ERR_OOM:i, code); - if (tb != NULL) { - PyErr_Restore(exc, val, tb); - PyErr_Print(); - } - return ret; - } - } - return report_code(preamble, "", code); -} - -EXPORT -int -run(const char **ENV_VARS, const char **ENV_VAR_VALS, char *PROGRAM, - const char *MODULE, const char *FUNCTION, const char *PYVER, - int IS_GUI, int argc, char * const *argv, const char **envp, char *full_exe_path) { - char *t = NULL; - int ret = 0, i; - PyObject *site, *mainf, *res; - - for (i = 0; i < 3; i++) { - t = rindex(full_exe_path, '/'); - if (t == NULL) return report_error("Failed to determine bundle path."); +static void +get_paths() { + char pathbuf[PROC_PIDPATHINFO_MAXSIZE], realpath_buf[PROC_PIDPATHINFO_MAXSIZE * 5]; + pid_t pid = getpid(); + int ret = proc_pidpath(pid, pathbuf, sizeof(pathbuf)); + if (ret <= 0) fatal("failed to get executable path for current pid with error: %s", strerror(errno)); + char *path = realpath(pathbuf, realpath_buf); + if (path == NULL) fatal("failed to get realpath for executable path with error: %s", strerror(errno)); + decode_char_buf(path, interpreter_data.exe_path); + for (unsigned i = 0; i < 3; i++) { + char *t = rindex(path, '/'); + if (t == NULL) fatal("Failed to determine bundle path."); +>>>>>>> The Apple build is working *t = '\0'; } - if (strstr(full_exe_path, "/calibre.app/Contents/") != NULL) { + if (strstr(path, "/calibre.app/Contents/") != NULL) { // We are one of the duplicate executables created to workaround codesign's limitations - for (i = 0; i < 2; i++) { - t = rindex(full_exe_path, '/'); - if (t == NULL) return report_error("Failed to resolve bundle path in dummy executable"); + for (unsigned i = 0; i < 2; i++) { + char *t = rindex(path, '/'); + if (t == NULL) fatal("Failed to resolve bundle path in dummy executable"); *t = '\0'; } } - - char rpath[PATH_MAX+1], exe_path[PATH_MAX+1]; - snprintf(exe_path, PATH_MAX+1, "%s/Contents", full_exe_path); - snprintf(rpath, PATH_MAX+1, "%s/Resources", exe_path); - initialize_interpreter(ENV_VARS, ENV_VAR_VALS, PROGRAM, MODULE, FUNCTION, PYVER, IS_GUI, - exe_path, rpath, argc, argv); - - site = PyImport_ImportModule("site"); - - if (site == NULL) - ret = calibre_show_python_error("Failed to import site module", -1); - else { - Py_XINCREF(site); - - mainf = PyObject_GetAttrString(site, "main"); - if (mainf == NULL || !PyCallable_Check(mainf)) - ret = calibre_show_python_error("site module has no main function", -1); - else { - Py_XINCREF(mainf); - res = PyObject_CallObject(mainf, NULL); - - if (res == NULL) - ret = calibre_show_python_error("Python function terminated unexpectedly", -1); - else { - } - } - } - PyErr_Clear(); - Py_Finalize(); - - //printf("11111 Returning: %d\r\n", ret); - return ret; +#define cat_literal(func, path, literal) func(path, literal, arraysz(literal) - 1) + cat_literal(strncat, path, "/Contents"); + set_env_vars(path); + decode_char_buf(path, interpreter_data.bundle_resource_path); +#define set_path(which, fmt, ...) swprintf(interpreter_data.which, arraysz(interpreter_data.which), fmt, interpreter_data.bundle_resource_path, __VA_ARGS__) + set_path(python_home_path, L"%ls/Resources/Python", NULL); + set_path(frameworks_path, L"%ls/Frameworks", NULL); + set_path(python_lib_path, L"%ls/Resources/Python/lib/python%d.%d", PY_VERSION_MAJOR, PY_VERSION_MINOR); + set_path(extensions_path, L"%ls/Frameworks/plugins", NULL); + set_path(resources_path, L"%ls/Resources/resources", NULL); + set_path(executables_path, L"%ls/MacOS", NULL); +#undef set_path + cat_literal(wcsncat, interpreter_data.bundle_resource_path, L"/Resources"); +#undef cat_literal +} + +EXPORT +void +run(const wchar_t *program, const wchar_t *module, const wchar_t *function, bool gui_app, int argc, char * const *argv) { + interpreter_data.argc = argc; + interpreter_data.argv = argv; + interpreter_data.basename = program; interpreter_data.module = module; interpreter_data.function = function; + pre_initialize_interpreter(gui_app); + get_paths(); + run_interpreter(); } diff --git a/bypy/macos/util.h b/bypy/macos/util.h index 73102f0275..be506030f6 100644 --- a/bypy/macos/util.h +++ b/bypy/macos/util.h @@ -1,5 +1,5 @@ #pragma once +#include +#include -int run(const char **ENV_VARS, const char **ENV_VAR_VALS, char *PROGRAM, - const char *MODULE, const char *FUNCTION, const char *PYVER, int IS_GUI, - int argc, char *const *argv, const char **envp, char *full_exe_path); +void run(const wchar_t *program, const wchar_t *module, const wchar_t *function, bool is_gui, int argc, char * const *argv); diff --git a/bypy/run-python.h b/bypy/run-python.h index 72a836ac33..3df7e71b87 100644 --- a/bypy/run-python.h +++ b/bypy/run-python.h @@ -111,12 +111,15 @@ typedef struct { size_t sys_paths_count; wchar_t exe_path[PATH_MAX], python_home_path[PATH_MAX], python_lib_path[PATH_MAX]; wchar_t extensions_path[PATH_MAX], resources_path[PATH_MAX], executables_path[PATH_MAX]; +#ifdef __APPLE__ + wchar_t bundle_resource_path[PATH_MAX], frameworks_path[PATH_MAX]; +#endif const wchar_t *basename, *module, *function; int argc; char * const *argv; } InterpreterData; -static InterpreterData interpreter_data = {0}; +static InterpreterData interpreter_data = {{0}}; static wchar_t* add_sys_path() { @@ -133,7 +136,12 @@ static void add_sys_paths() { swprintf(add_sys_path(), PATH_MAX, L"%ls", interpreter_data.python_lib_path); swprintf(add_sys_path(), PATH_MAX, L"%ls/lib-dynload", interpreter_data.python_lib_path); +#ifdef __APPLE__ + swprintf(add_sys_path(), PATH_MAX, L"%ls/Python/site-packages", interpreter_data.bundle_resource_path); +#else swprintf(add_sys_path(), PATH_MAX, L"%ls/site-packages", interpreter_data.python_lib_path); +#endif + } #endif @@ -176,6 +184,9 @@ run_interpreter() { set_sys_string("resources_location", interpreter_data.resources_path); set_sys_string("executables_location", interpreter_data.executables_path); #ifdef __APPLE__ + set_sys_string("resourcepath", interpreter_data.bundle_resource_path); + set_sys_string("frameworks_dir", interpreter_data.frameworks_path); + set_sys_bool("new_app_bundle", true); #elif _WIN32 #else set_sys_string("frozen_path", interpreter_data.executables_path); diff --git a/src/calibre/utils/ipc/launch.py b/src/calibre/utils/ipc/launch.py index b31494a1b6..050462bd1e 100644 --- a/src/calibre/utils/ipc/launch.py +++ b/src/calibre/utils/ipc/launch.py @@ -58,7 +58,7 @@ class Worker(object): return os.path.join(os.path.dirname(sys.executable), e+'.exe' if isfrozen else 'Scripts\\%s.exe'%e) if isosx: - return os.path.join(sys.binaries_path, e) + return os.path.join(sys.executables_location, e) if isfrozen: return os.path.join(sys.executables_location, e) @@ -73,13 +73,13 @@ class Worker(object): def gui_executable(self): if isosx and not hasattr(sys, 'running_from_setup'): if self.job_name == 'ebook-viewer': - base = os.path.dirname(sys.binaries_path) + base = os.path.dirname(sys.executables_location) return os.path.join(base, 'ebook-viewer.app/Contents/MacOS/', self.exe_name) if self.job_name == 'ebook-edit': - base = os.path.dirname(sys.binaries_path) + base = os.path.dirname(sys.executables_location) return os.path.join(base, 'ebook-viewer.app/Contents/ebook-edit.app/Contents/MacOS/', self.exe_name) - return os.path.join(sys.binaries_path, self.exe_name) + return os.path.join(sys.executables_location, self.exe_name) return self.executable