Update new os x app bundle build script

This commit is contained in:
Kovid Goyal 2009-12-21 11:20:50 -07:00
parent 0a8f15225c
commit 5f255fe646
6 changed files with 405 additions and 293 deletions

View File

@ -1,138 +1,17 @@
#include "util.h"
#include <stdlib.h>
#include <strings.h>
#include <CoreFoundation/CoreFoundation.h>
#include <mach-o/dyld.h>
#include <Python.h>
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
// 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**";
#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;
main(int argc, const char **argv, const char **envp) {
return run(ENV_VARS, ENV_VAR_VALS, PROGRAM, MODULE, FUNCTION, PYVER, argc, argv, envp);
}

View File

@ -1,58 +0,0 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
def _disable_linecache():
import linecache
def fake_getline(*args, **kwargs):
return ''
linecache.orig_getline = linecache.getline
linecache.getline = fake_getline
_disable_linecache()
def _recipes_pil_prescript(plugins):
from PIL import Image
import sys
def init():
if Image._initialized >= 2:
return
for plugin in plugins:
try:
__import__(plugin, globals(), locals(), [])
except ImportError:
if Image.DEBUG:
print 'Image: failed to import'
print plugin, ':', sys.exc_info()[1]
if Image.OPEN or Image.SAVE:
Image._initialized = 2
Image.init = init
_recipes_pil_prescript(['Hdf5StubImagePlugin', 'FitsStubImagePlugin', 'SunImagePlugin', 'GbrImagePlugin', 'PngImagePlugin', 'MicImagePlugin', 'FpxImagePlugin', 'PcxImagePlugin', 'ImImagePlugin', 'SpiderImagePlugin', 'PsdImagePlugin', 'BufrStubImagePlugin', 'SgiImagePlugin', 'McIdasImagePlugin', 'XpmImagePlugin', 'BmpImagePlugin', 'TgaImagePlugin', 'PalmImagePlugin', 'XVThumbImagePlugin', 'GribStubImagePlugin', 'ArgImagePlugin', 'PdfImagePlugin', 'ImtImagePlugin', 'GifImagePlugin', 'CurImagePlugin', 'WmfImagePlugin', 'MpegImagePlugin', 'IcoImagePlugin', 'TiffImagePlugin', 'PpmImagePlugin', 'MspImagePlugin', 'EpsImagePlugin', 'JpegImagePlugin', 'PixarImagePlugin', 'PcdImagePlugin', 'IptcImagePlugin', 'XbmImagePlugin', 'DcxImagePlugin', 'IcnsImagePlugin', 'FliImagePlugin'])
def _run():
global __file__
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
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
for arg in list(sys.argv[1:]):
if arg.startswith('-psn'):
sys.argv.remove(arg)
execfile(exe, globals(), globals())
_run()

View File

@ -10,33 +10,58 @@ import sys, os, shutil, plistlib, subprocess, glob, zipfile, tempfile, \
py_compile, stat, operator
abspath, join, basename = os.path.abspath, os.path.join, os.path.basename
#TODO: WMF support in ImageMagick
l = {}
exec open('setup.py').read() in l
VERSION = l['VERSION']
APPNAME = l['APPNAME']
scripts = l['scripts']
basenames = l['basenames']
main_functions = l['main_functions']
main_modules = l['main_modules']
from setup import __version__ as VERSION, __appname__ as APPNAME, basenames, \
modules as main_modules, Command, SRC, functions as main_functions
LICENSE = open('LICENSE', 'rb').read()
ENV = dict(
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',
PYTHONIOENCODING='UTF-8',
)
SW = os.environ.get('SW', '/sw')
def compile_launchers(contents_dir, xprograms):
info = warn = None
class OSX32_Freeze(Command):
description = 'Freeze OSX calibre installation'
def add_options(self, parser):
parser.add_option('--test-launchers', default=False,
action='store_true',
help='Only build launchers')
def run(self, opts):
global info, warn
info, warn = self.info, self.warn
main(opts.test_launchers)
def compile_launcher_lib(contents_dir, gcc, base):
info('\tCompiling calibre_launcher.dylib')
fd = join(contents_dir, 'Frameworks')
dest = join(fd, 'calibre-launcher.dylib')
src = join(base, 'util.c')
cmd = [gcc] + '-Wall -arch i386 -arch ppc -dynamiclib -std=gnu99'.split() + [src] + \
['-I'+base] + \
['-I%s/python/Python.framework/Headers'%SW] + \
'-current_version 1.0 -compatibility_version 1.0'.split() + \
'-fvisibility=hidden -o'.split() + [dest, '-F%s/python'%SW] + \
['-install_name',
'@executable_path/../Frameworks/'+os.path.basename(dest)] + \
['-framework', 'Python', '-framework', 'CoreFoundation', '-headerpad_max_install_names']
info('\t'+' '.join(cmd))
sys.stdout.flush()
subprocess.check_call(cmd)
return dest
def compile_launchers(contents_dir, xprograms, pyver):
gcc = os.environ.get('CC', 'gcc')
base = os.path.dirname(__file__)
lib = compile_launcher_lib(contents_dir, gcc, base)
src = open(join(base, 'launcher.c'), 'rb').read()
env, env_vals = [], []
for key, val in ENV.items():
@ -46,22 +71,23 @@ def compile_launchers(contents_dir, xprograms):
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
programs = [lib]
for program, x in xprograms.items():
module, func = x
info('\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)
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',
cmd = [gcc, '-Wall', '-arch', 'ppc', '-arch', 'i386',
'-I'+base, fsrc, lib, '-o', out,
'-headerpad_max_install_names']
print ' '.join(cmd)
info('\t'+' '.join(cmd))
sys.stdout.flush()
subprocess.check_call(cmd)
return programs
@ -81,7 +107,7 @@ def flipwritable(fn, mode=None):
def thin(path):
try:
subprocess.check_call(['lipo', path, '-verify_arch', 'ppc64'])
print '\tThinning', path
info('\tThinning', path)
except:
return
else:
@ -92,7 +118,7 @@ def strip_files(files, argv_max=(256 * 1024)):
"""
Strip a list of files
"""
tostrip = [(fn, flipwritable(fn)) for fn in files]
tostrip = [(fn, flipwritable(fn)) for fn in files if os.path.exists(fn)]
while tostrip:
cmd = list(STRIPCMD)
flips = []
@ -125,61 +151,66 @@ class Py2App(object):
FID = '@executable_path/../Frameworks'
def __init__(self, build_dir):
def __init__(self, build_dir, test_launchers=False):
self.build_dir = build_dir
self.contents_dir = join(self.build_dir, 'Contents')
self.resources_dir = join(self.contents_dir, 'Resources')
self.frameworks_dir = join(self.contents_dir, 'Frameworks')
self.version_info = '.'.join(map(str, sys.version_info[:2]))
self.site_packages = join(self.resources_dir, 'Python', 'site-packages')
self.to_strip = []
self.warnings = []
self.run(test_launchers)
def warn(self, *args):
self.warnings.append(args)
prefix = '' if args and args[0].startswith('WARNING:') else 'WARNING: '
sys.stdout.write(prefix+' '.join(args)+'\n')
sys.stdout.flush()
warn(*args)
def run(self, test_launchers):
ret = 0
if not test_launchers:
if os.path.exists(self.build_dir):
shutil.rmtree(self.build_dir)
os.makedirs(self.build_dir)
self.create_skeleton()
self.create_plist()
def run(self):
self.create_skeleton()
self.create_plist()
self.add_python_framework()
self.add_qt_frameworks()
self.add_calibre_plugins()
self.add_podofo()
self.add_poppler()
self.add_libjpeg()
self.add_libpng()
self.add_fontconfig()
self.add_imagemagick()
self.add_misc_libraries()
self.add_python_framework()
self.add_qt_frameworks()
self.add_calibre_plugins()
self.add_podofo()
self.add_poppler()
self.add_libjpeg()
self.add_libpng()
self.add_fontconfig()
self.add_imagemagick()
self.add_misc_libraries()
self.add_site_packages()
self.add_stdlib()
self.add_resources()
self.compile_py_modules()
self.add_site_packages()
self.add_stdlib()
self.compile_py_modules()
self.create_console_app()
self.create_console_app()
self.copy_launcher_and_site()
self.copy_site()
self.create_exe()
self.thin_to_x86_64()
self.strip_files()
if not test_launchers:
#self.thin_to_x86_64()
self.strip_files()
ret = self.makedmg(self.build_dir, APPNAME+'-'+VERSION+'-x86_64')
sys.stdout.flush()
sys.stderr.flush()
ret = self.makedmg(self.build_dir, APPNAME+'-'+VERSION)
print '\nThere were', len(self.warnings), 'warnings'
for w in list(self.warnings):
print
self.warn(*w)
return ret
@flush
def add_resources(self):
shutil.copytree('resources', os.path.join(self.resources_dir,
'resources'))
@flush
def thin_to_x86_64(self):
print '\nThinning to x86_64'
info('\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]:
@ -192,24 +223,22 @@ class Py2App(object):
@flush
def strip_files(self):
print '\nStripping files...'
info('\nStripping files...')
strip_files(self.to_strip)
@flush
def create_exe(self):
print '\nCreating launchers'
info('\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)
progs = []
for x in ('console', 'gui'):
progs += list(zip(basenames[x], main_modules[x], main_functions[x]))
for program, module, func in progs:
programs[program] = (module, func)
programs = compile_launchers(self.contents_dir, programs,
self.version_info)
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):
@ -233,20 +262,20 @@ class Py2App(object):
def get_local_dependencies(self, path_to_lib):
for x in self.get_dependencies(path_to_lib):
for y in (SW+'/lib/', '/usr/local/lib/', SW+'/qt/lib/',
SW+'/python/'):
SW+'/python/', SW+'/freetype/lib/'):
if x.startswith(y):
yield x, x[len(y):]
break
@flush
def change_dep(self, old_dep, new_dep, path_to_lib):
print '\tResolving dependency %s to'%old_dep, new_dep
info('\tResolving dependency %s to'%old_dep, new_dep)
subprocess.check_call(['install_name_tool', '-change', old_dep, new_dep,
path_to_lib])
@flush
def fix_dependencies_in_lib(self, path_to_lib):
print '\nFixing dependencies in', path_to_lib
info('\nFixing dependencies in', path_to_lib)
self.to_strip.append(path_to_lib)
old_mode = flipwritable(path_to_lib)
for dep, bname in self.get_local_dependencies(path_to_lib):
@ -259,7 +288,7 @@ class Py2App(object):
@flush
def add_python_framework(self):
print '\nAdding Python framework'
info('\nAdding Python framework')
src = join(SW, 'python', 'Python.framework')
x = join(self.frameworks_dir, 'Python.framework')
curr = os.path.realpath(join(src, 'Versions', 'Current'))
@ -274,7 +303,7 @@ class Py2App(object):
@flush
def add_qt_frameworks(self):
for f in ('QtCore', 'QtGui', 'QtXml', 'QtNetwork', 'QtSvg', 'QtWebkit',
'phonon'):
'QtXmlPatterns', 'phonon'):
self.add_qt_framework(f)
for d in glob.glob(join(SW, 'qt', 'plugins', '*')):
shutil.copytree(d, join(self.contents_dir, 'MacOS', basename(d)))
@ -353,30 +382,30 @@ class Py2App(object):
@flush
def add_podofo(self):
print '\nAdding PoDoFo'
info('\nAdding PoDoFo')
pdf = join(SW, 'lib', 'libpodofo.0.6.99.dylib')
self.install_dylib(pdf)
@flush
def add_poppler(self):
print '\nAdding poppler'
for x in ('libpoppler.4.dylib', 'libpoppler-qt4.3.dylib'):
info('\nAdding poppler')
for x in ('libpoppler.5.dylib', 'libpoppler-qt4.3.dylib'):
self.install_dylib(os.path.join(SW, 'lib', x))
self.install_dylib(os.path.join(SW, 'bin', 'pdftohtml'), False)
@flush
def add_libjpeg(self):
print '\nAdding libjpeg'
info('\nAdding libjpeg')
self.install_dylib(os.path.join(SW, 'lib', 'libjpeg.7.dylib'))
@flush
def add_libpng(self):
print '\nAdding libpng'
info('\nAdding libpng')
self.install_dylib(os.path.join(SW, 'lib', 'libpng12.0.dylib'))
@flush
def add_fontconfig(self):
print '\nAdding fontconfig'
info('\nAdding fontconfig')
for x in ('fontconfig.1', 'freetype.6', 'expat.1'):
src = os.path.join(SW, 'lib', 'lib'+x+'.dylib')
self.install_dylib(src)
@ -400,7 +429,7 @@ class Py2App(object):
@flush
def add_imagemagick(self):
print '\nAdding ImageMagick'
info('\nAdding ImageMagick')
for x in ('Wand', 'Core'):
self.install_dylib(os.path.join(SW, 'lib', 'libMagick%s.2.dylib'%x))
idir = glob.glob(os.path.join(SW, 'lib', 'ImageMagick-*'))[-1]
@ -419,16 +448,15 @@ class Py2App(object):
@flush
def add_misc_libraries(self):
for x in ('usb', 'unrar', 'readline.6.0'):
print '\nAdding', x
for x in ('usb', 'unrar', 'readline.6.0', 'wmflite-0.2.7'):
info('\nAdding', x)
x = 'lib%s.dylib'%x
shutil.copy2(join(SW, 'lib', x), self.frameworks_dir)
self.set_id(join(self.frameworks_dir, x), self.FID+'/'+x)
@flush
def add_site_packages(self):
print '\nAdding site-packages'
self.site_packages = join(self.resources_dir, 'Python', 'site-packages')
info('\nAdding site-packages')
os.makedirs(self.site_packages)
paths = reversed(map(abspath, [x for x in sys.path if x.startswith('/')]))
upaths = []
@ -455,12 +483,12 @@ class Py2App(object):
finally:
if tdir is not None:
shutil.rmtree(tdir)
shutil.rmtree(os.path.join(self.site_packages, 'calibre', 'plugins'))
self.remove_bytecode(join(self.resources_dir, 'Python', 'site-packages'))
@flush
def add_modules_from_dir(self, src):
for x in glob.glob(join(src, '*.py'))+glob.glob(join(src, '*.so')):
dest = join(self.site_packages, basename(x))
shutil.copy2(x, self.site_packages)
if x.endswith('.so'):
self.fix_dependencies_in_lib(x)
@ -507,7 +535,7 @@ class Py2App(object):
@flush
def add_stdlib(self):
print '\nAdding python stdlib'
info('\nAdding python stdlib')
src = join(SW, 'python/Python.framework/Versions/Current/lib/python')
src += self.version_info
dest = join(self.resources_dir, 'Python', 'lib', 'python')
@ -537,7 +565,7 @@ class Py2App(object):
@flush
def compile_py_modules(self):
print '\nCompiling Python modules'
info( '\nCompiling Python modules')
base = join(self.resources_dir, 'Python')
for x in os.walk(base):
root = x[0]
@ -553,7 +581,7 @@ class Py2App(object):
@flush
def create_console_app(self):
print '\nCreating console.app'
info( '\nCreating console.app')
cc_dir = os.path.join(self.contents_dir, 'console.app', 'Contents')
os.makedirs(cc_dir)
for x in os.listdir(self.contents_dir):
@ -568,9 +596,8 @@ class Py2App(object):
join(cc_dir, x))
@flush
def copy_launcher_and_site(self):
def copy_site(self):
base = os.path.dirname(__file__)
shutil.copy2(join(base, 'launcher.py'), self.resources_dir)
shutil.copy2(join(base, 'site.py'), join(self.resources_dir, 'Python',
'lib', 'python'+self.version_info))
@ -581,7 +608,7 @@ class Py2App(object):
internet_enable=True,
format='UDBZ'):
''' Copy a directory d into a dmg named volname '''
print '\nCreating dmg'
info('\nCreating dmg')
sys.stdout.flush()
if not os.path.exists(destdir):
os.makedirs(destdir)
@ -593,7 +620,7 @@ class Py2App(object):
if internet_enable:
subprocess.check_call(['/usr/bin/hdiutil', 'internet-enable', '-yes', dmg])
size = os.stat(dmg).st_size/(1024*1024.)
print '\nInstaller size: %.2fMB\n'%size
info('\nInstaller size: %.2fMB\n'%size)
return dmg
def test_exe():
@ -603,15 +630,11 @@ def test_exe():
return 0
def main():
def main(test=False):
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)
os.makedirs(build_dir)
py2app = Py2App(build_dir)
py2app.run()
build_dir = abspath(join(os.path.dirname(SRC), 'build', APPNAME+'.app'))
Py2App(build_dir, test_launchers=test)
return 0

View File

@ -11,11 +11,15 @@ def makepath(*paths):
dir = os.path.abspath(os.path.join(*paths))
return dir, os.path.normcase(dir)
for m in sys.modules.values():
f = getattr(m, '__file__', None)
if isinstance(f, basestring) and os.path.exists(f):
m.__file__ = os.path.abspath(m.__file__)
del m
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.
@ -104,3 +108,42 @@ sys.setdefaultencoding('utf-8')
#
if hasattr(sys, "setdefaultencoding"):
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 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')
sys.console_binaries_path = os.path.join(os.path.dirname(base),
'console.app', 'Contents', 'MacOS')
dv = os.environ.get('CALIBRE_DEVELOP_FROM', None)
if dv and os.path.exists(dv):
sys.path.insert(0, os.path.abspath(dv))
def main():
global __file__
base = sys.resourcepath
sys.frozen = 'macosx_app'
sys.new_app_bundle = True
abs__file__()
add_calibre_vars(base)
addsitedir(sys.site_packages)
for arg in list(sys.argv[1:]):
if arg.startswith('-psn'):
sys.argv.remove(arg)
return run_entry_point()

View File

@ -0,0 +1,220 @@
#include "util.h"
#include <stdlib.h>
#include <strings.h>
#include <CoreFoundation/CoreFoundation.h>
#include <mach-o/dyld.h>
#include <Python.h>
#define EXPORT __attribute__((visibility("default")))
static const char *ERR_OOM = "Out of memory";
static int
report_error(const char *msg) {
fprintf(stderr, msg);
fprintf(stderr, "\n");
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 void
set_env_vars(const char **ENV_VARS, const char **ENV_VAR_VALS, const char* exe_path) {
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);
}
return;
}
void initialize_interpreter(const char **ENV_VARS, const char **ENV_VAR_VALS,
char *PROGRAM, const char *MODULE, const char *FUNCTION, const char *PYVER,
const char* exe_path, const char *rpath, int argc, const char **argv) {
PyObject *pargv, *v;
int i;
Py_OptimizeFlag = 2;
Py_NoSiteFlag = 1;
Py_DontWriteBytecodeFlag = 1;
Py_IgnoreEnvironmentFlag = 1;
Py_NoUserSiteDirectory = 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("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;
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 argc, const char **argv, const char **envp) {
char *pathPtr = NULL;
char buf[3*PATH_MAX];
int ret = 0, i;
PyObject *site, *mainf, *res;
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], exe_path[PATH_MAX+1];
snprintf(exe_path, PATH_MAX+1, "%s/Contents", pathPtr);
snprintf(rpath, PATH_MAX+1, "%s/Resources", exe_path);
initialize_interpreter(ENV_VARS, ENV_VAR_VALS, PROGRAM, MODULE, FUNCTION, PYVER,
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;
}

View File

@ -0,0 +1,5 @@
#pragma once
int run(const char **ENV_VARS, const char **ENV_VAR_VALS, char *PROGRAM,
const char *MODULE, const char *FUNCTION, const char *PYVER,
int argc, const char **argv, const char **envp);