mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
The Apple build is working
This commit is contained in:
parent
066ff4127b
commit
411da596b4
@ -37,20 +37,25 @@ QT_FRAMEWORKS = [x.replace('5', '') for x in QT_DLLS]
|
|||||||
ENV = dict(
|
ENV = dict(
|
||||||
FONTCONFIG_PATH='@executable_path/../Resources/fonts',
|
FONTCONFIG_PATH='@executable_path/../Resources/fonts',
|
||||||
FONTCONFIG_FILE='@executable_path/../Resources/fonts/fonts.conf',
|
FONTCONFIG_FILE='@executable_path/../Resources/fonts/fonts.conf',
|
||||||
PYTHONIOENCODING='UTF-8',
|
|
||||||
SSL_CERT_FILE='@executable_path/../Resources/resources/mozilla-ca-certs.pem',
|
SSL_CERT_FILE='@executable_path/../Resources/resources/mozilla-ca-certs.pem',
|
||||||
)
|
)
|
||||||
APPNAME, VERSION = calibre_constants['appname'], calibre_constants['version']
|
APPNAME, VERSION = calibre_constants['appname'], calibre_constants['version']
|
||||||
basenames, main_modules, main_functions = calibre_constants['basenames'], calibre_constants['modules'], calibre_constants['functions']
|
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')
|
print('\tCompiling calibre_launcher.dylib')
|
||||||
fd = join(contents_dir, 'Frameworks')
|
env, env_vals = [], []
|
||||||
dest = join(fd, 'calibre-launcher.dylib')
|
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')
|
src = join(base, 'util.c')
|
||||||
cmd = [gcc] + '-Wall -dynamiclib -std=gnu99'.split() + [src] + \
|
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] + \
|
['-I%s/python/Python.framework/Versions/Current/Headers' % PREFIX] + \
|
||||||
'-current_version 1.0 -compatibility_version 1.0'.split() + \
|
'-current_version 1.0 -compatibility_version 1.0'.split() + \
|
||||||
'-fvisibility=hidden -o'.split() + [dest] + \
|
'-fvisibility=hidden -o'.split() + [dest] + \
|
||||||
@ -68,33 +73,19 @@ gcc = os.environ.get('CC', 'clang')
|
|||||||
|
|
||||||
def compile_launchers(contents_dir, xprograms, pyver):
|
def compile_launchers(contents_dir, xprograms, pyver):
|
||||||
base = dirname(abspath(__file__))
|
base = dirname(abspath(__file__))
|
||||||
lib = compile_launcher_lib(contents_dir, gcc, base)
|
lib = compile_launcher_lib(contents_dir, gcc, base, pyver)
|
||||||
with open(join(base, 'launcher.c'), 'rb') as f:
|
src = join(base, 'launcher.c')
|
||||||
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)
|
|
||||||
programs = [lib]
|
programs = [lib]
|
||||||
for program, x in xprograms.items():
|
for program, x in xprograms.items():
|
||||||
module, func, ptype = x
|
module, func, ptype = x
|
||||||
print('\tCompiling', program)
|
print('\tCompiling', program)
|
||||||
out = join(contents_dir, 'MacOS', program)
|
out = join(contents_dir, 'MacOS', program)
|
||||||
programs.append(out)
|
programs.append(out)
|
||||||
psrc = src.replace('**PROGRAM**', program)
|
is_gui = 'true' if ptype == 'gui' else 'false'
|
||||||
psrc = psrc.replace('**MODULE**', module)
|
cmd = [
|
||||||
psrc = psrc.replace('**FUNCTION**', func)
|
gcc, '-Wall', f'-DPROGRAM=L"{program}"', f'-DMODULE=L"{module}"', f'-DFUNCTION=L"{func}"', f'-DIS_GUI={is_gui}',
|
||||||
psrc = psrc.replace('**PYVER**', pyver)
|
'-I' + base, src, lib, '-o', out, '-headerpad_max_install_names'
|
||||||
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']
|
|
||||||
# print('\t'+' '.join(cmd))
|
# print('\t'+' '.join(cmd))
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
subprocess.check_call(cmd)
|
subprocess.check_call(cmd)
|
||||||
@ -194,9 +185,9 @@ class Freeze(object):
|
|||||||
self.add_misc_libraries()
|
self.add_misc_libraries()
|
||||||
|
|
||||||
self.add_resources()
|
self.add_resources()
|
||||||
|
self.copy_site()
|
||||||
self.compile_py_modules()
|
self.compile_py_modules()
|
||||||
|
|
||||||
self.copy_site()
|
|
||||||
self.create_exe()
|
self.create_exe()
|
||||||
if not test_launchers and not self.dont_strip:
|
if not test_launchers and not self.dont_strip:
|
||||||
self.strip_files()
|
self.strip_files()
|
||||||
@ -258,6 +249,8 @@ class Freeze(object):
|
|||||||
for x, is_id in self.get_dependencies(path_to_lib):
|
for x, is_id in self.get_dependencies(path_to_lib):
|
||||||
if x.startswith('@rpath/Qt'):
|
if x.startswith('@rpath/Qt'):
|
||||||
yield x, x[len('@rpath/'):], is_id
|
yield x, x[len('@rpath/'):], is_id
|
||||||
|
elif x == 'libunrar.dylib' and not is_id:
|
||||||
|
yield x, x, is_id
|
||||||
else:
|
else:
|
||||||
for y in (PREFIX + '/lib/', PREFIX + '/python/Python.framework/'):
|
for y in (PREFIX + '/lib/', PREFIX + '/python/Python.framework/'):
|
||||||
if x.startswith(y):
|
if x.startswith(y):
|
||||||
@ -500,10 +493,10 @@ class Freeze(object):
|
|||||||
@flush
|
@flush
|
||||||
def add_misc_libraries(self):
|
def add_misc_libraries(self):
|
||||||
for x in (
|
for x in (
|
||||||
'usb-1.0.0', 'mtp.9', 'chm.0', 'sqlite3.0', 'hunspell-1.7.0',
|
'usb-1.0.0', 'mtp.9', 'chm.0', 'sqlite3.0', 'hunspell-1.7.0',
|
||||||
'icudata.64', 'icui18n.64', 'icuio.64', 'icuuc.64', 'hyphen.0',
|
'icudata.64', 'icui18n.64', 'icuio.64', 'icuuc.64', 'hyphen.0',
|
||||||
'xslt.1', 'exslt.0', 'xml2.2', 'z.1', 'unrar', 'lzma.5',
|
'xslt.1', 'exslt.0', 'xml2.2', 'z.1', 'unrar', 'lzma.5',
|
||||||
'crypto.1.0.0', 'ssl.1.0.0', 'iconv.2', # 'ltdl.7'
|
'crypto.1.0.0', 'ssl.1.0.0', 'iconv.2', # 'ltdl.7'
|
||||||
):
|
):
|
||||||
print('\nAdding', x)
|
print('\nAdding', x)
|
||||||
x = 'lib%s.dylib' % x
|
x = 'lib%s.dylib' % x
|
||||||
|
@ -10,32 +10,8 @@
|
|||||||
#define arraysz(x) (sizeof(x)/sizeof(x[0]))
|
#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
|
int
|
||||||
main(int argc, char* const *argv, const char **envp) {
|
main(int argc, char * const *argv) {
|
||||||
char pathbuf[PROC_PIDPATHINFO_MAXSIZE], realpath_buf[PROC_PIDPATHINFO_MAXSIZE * 5];
|
run(PROGRAM, MODULE, FUNCTION, IS_GUI, argc, argv);
|
||||||
pid_t pid = getpid();
|
return 0;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
@ -1,131 +1,14 @@
|
|||||||
"""
|
import builtins
|
||||||
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 os
|
import os
|
||||||
|
import sys
|
||||||
|
import _sitebuiltins
|
||||||
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)()
|
|
||||||
|
|
||||||
|
|
||||||
def read_user_env_vars():
|
def read_user_env_vars():
|
||||||
try:
|
try:
|
||||||
with open(os.path.expanduser('~/Library/Preferences/calibre/macos-env.txt'), 'rb') as f:
|
with open(os.path.expanduser('~/Library/Preferences/calibre/macos-env.txt'), 'rb') as f:
|
||||||
raw = f.read().decode('utf-8', 'replace')
|
raw = f.read().decode('utf-8', 'replace')
|
||||||
except EnvironmentError as err:
|
except EnvironmentError:
|
||||||
return
|
return
|
||||||
for line in raw.splitlines():
|
for line in raw.splitlines():
|
||||||
if line.startswith('#'):
|
if line.startswith('#'):
|
||||||
@ -136,52 +19,46 @@ def read_user_env_vars():
|
|||||||
os.environ[key] = os.path.expandvars(os.path.expanduser(val))
|
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():
|
def nuke_stdout():
|
||||||
# Redirect stdout, stdin and stderr to /dev/null
|
# Redirect stdout, stdin and stderr to /dev/null
|
||||||
from calibre.constants import plugins
|
from calibre.constants import plugins
|
||||||
plugins['speedup'][0].detach(os.devnull)
|
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():
|
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
|
dfv = os.environ.get('CALIBRE_DEVELOP_FROM')
|
||||||
# starting via launch services
|
if dfv and os.path.exists(dfv):
|
||||||
for arg in tuple(sys.argv[1:]):
|
sys.path.insert(0, os.path.abspath(dfv))
|
||||||
if arg.startswith('-psn_'):
|
set_helper()
|
||||||
sys.argv.remove(arg)
|
set_quit()
|
||||||
|
if sys.gui_app and not (
|
||||||
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 (
|
|
||||||
sys.stdout.isatty() or sys.stderr.isatty() or sys.stdin.isatty()
|
sys.stdout.isatty() or sys.stderr.isatty() or sys.stdin.isatty()
|
||||||
):
|
):
|
||||||
nuke_stdout()
|
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()
|
||||||
|
@ -1,43 +1,25 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include <stdlib.h>
|
#include "../run-python.h"
|
||||||
#include <strings.h>
|
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#include <mach-o/dyld.h>
|
#include <mach-o/dyld.h>
|
||||||
#include <Python.h>
|
#include <libproc.h>
|
||||||
|
|
||||||
#define EXPORT __attribute__((visibility("default")))
|
#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/.."
|
#define EXE "@executable_path/.."
|
||||||
|
|
||||||
|
static const char *env_vars[] = { ENV_VARS };
|
||||||
|
static const char *env_var_vals[] = { ENV_VAR_VALS };
|
||||||
|
|
||||||
static void
|
static void
|
||||||
set_env_vars(const char **ENV_VARS, const char **ENV_VAR_VALS, const char* exe_path) {
|
set_env_vars(const char* contents_path) {
|
||||||
int i = 0;
|
|
||||||
char buf[3*PATH_MAX];
|
char buf[3*PATH_MAX];
|
||||||
const char *env_var, *val;
|
const char *env_var, *val;
|
||||||
|
|
||||||
while(1) {
|
for (size_t i = 0; i < arraysz(env_vars); i++) {
|
||||||
env_var = ENV_VARS[i];
|
env_var = env_vars[i]; val = env_var_vals[i];
|
||||||
if (env_var == NULL) break;
|
if (strstr(val, EXE) == val && strlen(val) >= sizeof(EXE)) {
|
||||||
val = ENV_VAR_VALS[i++];
|
snprintf(buf, sizeof(buf) - 1, "%s%s", contents_path, val + sizeof(EXE) - 1);
|
||||||
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);
|
setenv(env_var, buf, 1);
|
||||||
} else
|
} else
|
||||||
setenv(env_var, val, 1);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void initialize_interpreter(const char **ENV_VARS, const char **ENV_VAR_VALS,
|
static void
|
||||||
char *PROGRAM, const char *MODULE, const char *FUNCTION, const char *PYVER, int IS_GUI,
|
get_paths() {
|
||||||
const char* exe_path, const char *rpath, int argc, char* const *argv) {
|
char pathbuf[PROC_PIDPATHINFO_MAXSIZE], realpath_buf[PROC_PIDPATHINFO_MAXSIZE * 5];
|
||||||
PyObject *pargv, *v;
|
pid_t pid = getpid();
|
||||||
int i;
|
int ret = proc_pidpath(pid, pathbuf, sizeof(pathbuf));
|
||||||
Py_OptimizeFlag = 2;
|
if (ret <= 0) fatal("failed to get executable path for current pid with error: %s", strerror(errno));
|
||||||
Py_NoSiteFlag = 1;
|
char *path = realpath(pathbuf, realpath_buf);
|
||||||
Py_DontWriteBytecodeFlag = 1;
|
if (path == NULL) fatal("failed to get realpath for executable path with error: %s", strerror(errno));
|
||||||
Py_IgnoreEnvironmentFlag = 1;
|
decode_char_buf(path, interpreter_data.exe_path);
|
||||||
Py_NoUserSiteDirectory = 1;
|
for (unsigned i = 0; i < 3; i++) {
|
||||||
Py_HashRandomizationFlag = 1;
|
char *t = rindex(path, '/');
|
||||||
|
if (t == NULL) fatal("Failed to determine bundle path.");
|
||||||
//Py_VerboseFlag = 1;
|
>>>>>>> The Apple build is working
|
||||||
//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.");
|
|
||||||
*t = '\0';
|
*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
|
// We are one of the duplicate executables created to workaround codesign's limitations
|
||||||
for (i = 0; i < 2; i++) {
|
for (unsigned i = 0; i < 2; i++) {
|
||||||
t = rindex(full_exe_path, '/');
|
char *t = rindex(path, '/');
|
||||||
if (t == NULL) return report_error("Failed to resolve bundle path in dummy executable");
|
if (t == NULL) fatal("Failed to resolve bundle path in dummy executable");
|
||||||
*t = '\0';
|
*t = '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#define cat_literal(func, path, literal) func(path, literal, arraysz(literal) - 1)
|
||||||
char rpath[PATH_MAX+1], exe_path[PATH_MAX+1];
|
cat_literal(strncat, path, "/Contents");
|
||||||
snprintf(exe_path, PATH_MAX+1, "%s/Contents", full_exe_path);
|
set_env_vars(path);
|
||||||
snprintf(rpath, PATH_MAX+1, "%s/Resources", exe_path);
|
decode_char_buf(path, interpreter_data.bundle_resource_path);
|
||||||
initialize_interpreter(ENV_VARS, ENV_VAR_VALS, PROGRAM, MODULE, FUNCTION, PYVER, IS_GUI,
|
#define set_path(which, fmt, ...) swprintf(interpreter_data.which, arraysz(interpreter_data.which), fmt, interpreter_data.bundle_resource_path, __VA_ARGS__)
|
||||||
exe_path, rpath, argc, argv);
|
set_path(python_home_path, L"%ls/Resources/Python", NULL);
|
||||||
|
set_path(frameworks_path, L"%ls/Frameworks", NULL);
|
||||||
site = PyImport_ImportModule("site");
|
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);
|
||||||
if (site == NULL)
|
set_path(resources_path, L"%ls/Resources/resources", NULL);
|
||||||
ret = calibre_show_python_error("Failed to import site module", -1);
|
set_path(executables_path, L"%ls/MacOS", NULL);
|
||||||
else {
|
#undef set_path
|
||||||
Py_XINCREF(site);
|
cat_literal(wcsncat, interpreter_data.bundle_resource_path, L"/Resources");
|
||||||
|
#undef cat_literal
|
||||||
mainf = PyObject_GetAttrString(site, "main");
|
}
|
||||||
if (mainf == NULL || !PyCallable_Check(mainf))
|
|
||||||
ret = calibre_show_python_error("site module has no main function", -1);
|
EXPORT
|
||||||
else {
|
void
|
||||||
Py_XINCREF(mainf);
|
run(const wchar_t *program, const wchar_t *module, const wchar_t *function, bool gui_app, int argc, char * const *argv) {
|
||||||
res = PyObject_CallObject(mainf, NULL);
|
interpreter_data.argc = argc;
|
||||||
|
interpreter_data.argv = argv;
|
||||||
if (res == NULL)
|
interpreter_data.basename = program; interpreter_data.module = module; interpreter_data.function = function;
|
||||||
ret = calibre_show_python_error("Python function terminated unexpectedly", -1);
|
pre_initialize_interpreter(gui_app);
|
||||||
else {
|
get_paths();
|
||||||
}
|
run_interpreter();
|
||||||
}
|
|
||||||
}
|
|
||||||
PyErr_Clear();
|
|
||||||
Py_Finalize();
|
|
||||||
|
|
||||||
//printf("11111 Returning: %d\r\n", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <wchar.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
int run(const char **ENV_VARS, const char **ENV_VAR_VALS, char *PROGRAM,
|
void run(const wchar_t *program, const wchar_t *module, const wchar_t *function, bool is_gui, int argc, char * const *argv);
|
||||||
const char *MODULE, const char *FUNCTION, const char *PYVER, int IS_GUI,
|
|
||||||
int argc, char *const *argv, const char **envp, char *full_exe_path);
|
|
||||||
|
@ -111,12 +111,15 @@ typedef struct {
|
|||||||
size_t sys_paths_count;
|
size_t sys_paths_count;
|
||||||
wchar_t exe_path[PATH_MAX], python_home_path[PATH_MAX], python_lib_path[PATH_MAX];
|
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];
|
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;
|
const wchar_t *basename, *module, *function;
|
||||||
int argc;
|
int argc;
|
||||||
char * const *argv;
|
char * const *argv;
|
||||||
} InterpreterData;
|
} InterpreterData;
|
||||||
|
|
||||||
static InterpreterData interpreter_data = {0};
|
static InterpreterData interpreter_data = {{0}};
|
||||||
|
|
||||||
static wchar_t*
|
static wchar_t*
|
||||||
add_sys_path() {
|
add_sys_path() {
|
||||||
@ -133,7 +136,12 @@ static void
|
|||||||
add_sys_paths() {
|
add_sys_paths() {
|
||||||
swprintf(add_sys_path(), PATH_MAX, L"%ls", interpreter_data.python_lib_path);
|
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);
|
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);
|
swprintf(add_sys_path(), PATH_MAX, L"%ls/site-packages", interpreter_data.python_lib_path);
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -176,6 +184,9 @@ run_interpreter() {
|
|||||||
set_sys_string("resources_location", interpreter_data.resources_path);
|
set_sys_string("resources_location", interpreter_data.resources_path);
|
||||||
set_sys_string("executables_location", interpreter_data.executables_path);
|
set_sys_string("executables_location", interpreter_data.executables_path);
|
||||||
#ifdef __APPLE__
|
#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
|
#elif _WIN32
|
||||||
#else
|
#else
|
||||||
set_sys_string("frozen_path", interpreter_data.executables_path);
|
set_sys_string("frozen_path", interpreter_data.executables_path);
|
||||||
|
@ -58,7 +58,7 @@ class Worker(object):
|
|||||||
return os.path.join(os.path.dirname(sys.executable),
|
return os.path.join(os.path.dirname(sys.executable),
|
||||||
e+'.exe' if isfrozen else 'Scripts\\%s.exe'%e)
|
e+'.exe' if isfrozen else 'Scripts\\%s.exe'%e)
|
||||||
if isosx:
|
if isosx:
|
||||||
return os.path.join(sys.binaries_path, e)
|
return os.path.join(sys.executables_location, e)
|
||||||
|
|
||||||
if isfrozen:
|
if isfrozen:
|
||||||
return os.path.join(sys.executables_location, e)
|
return os.path.join(sys.executables_location, e)
|
||||||
@ -73,13 +73,13 @@ class Worker(object):
|
|||||||
def gui_executable(self):
|
def gui_executable(self):
|
||||||
if isosx and not hasattr(sys, 'running_from_setup'):
|
if isosx and not hasattr(sys, 'running_from_setup'):
|
||||||
if self.job_name == 'ebook-viewer':
|
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)
|
return os.path.join(base, 'ebook-viewer.app/Contents/MacOS/', self.exe_name)
|
||||||
if self.job_name == 'ebook-edit':
|
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(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
|
return self.executable
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user