mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Update new os x app bundle build script
This commit is contained in:
parent
0a8f15225c
commit
5f255fe646
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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()
|
@ -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,23 +151,27 @@ 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):
|
||||
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()
|
||||
|
||||
@ -158,28 +188,29 @@ class Py2App(object):
|
||||
|
||||
self.add_site_packages()
|
||||
self.add_stdlib()
|
||||
self.add_resources()
|
||||
self.compile_py_modules()
|
||||
|
||||
self.create_console_app()
|
||||
|
||||
self.copy_launcher_and_site()
|
||||
self.copy_site()
|
||||
self.create_exe()
|
||||
self.thin_to_x86_64()
|
||||
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
|
||||
|
||||
|
||||
|
@ -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):
|
||||
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__)
|
||||
del m
|
||||
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()
|
||||
|
||||
|
||||
|
220
setup/installer/osx/app/util.c
Normal file
220
setup/installer/osx/app/util.c
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
5
setup/installer/osx/app/util.h
Normal file
5
setup/installer/osx/app/util.h
Normal 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);
|
Loading…
x
Reference in New Issue
Block a user