mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 02:34:06 -04:00
Sync to trunk.
This commit is contained in:
commit
fd81ca32a9
BIN
resources/images/devices/nook.jpg
Normal file
BIN
resources/images/devices/nook.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
64
resources/recipes/strategy-business.recipe
Normal file
64
resources/recipes/strategy-business.recipe
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class StrategyBusinessRecipe(BasicNewsRecipe):
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__author__ = 'kwetal'
|
||||||
|
language = 'en'
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
title = u'Strategy+Business'
|
||||||
|
publisher = u' Booz & Company'
|
||||||
|
category = u'Business'
|
||||||
|
description = u'Business magazine for senior business executives and the people who influence them.'
|
||||||
|
|
||||||
|
oldest_article = 13 * 7 # 3 months
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
use_embedded_content = False
|
||||||
|
remove_empty_feeds = True
|
||||||
|
|
||||||
|
no_stylesheets = True
|
||||||
|
remove_javascript = True
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
body{font-family:verdana,arial,helvetica,geneva,sans-serif ;}
|
||||||
|
a {text-decoration: none; color: blue;}
|
||||||
|
h1 {margin: 0em; padding: 0em;}
|
||||||
|
h2 {font-size: medium; font-weight: bold;}
|
||||||
|
#sb-date {font-size: xx-small; color: #696969}
|
||||||
|
#category {font-style: italic; font-size: small; color: black; margin: 0em; padding: 0em;}
|
||||||
|
#byline {font-size: small; color: #666666}
|
||||||
|
div.profiles {font-size: small; font-style: italic; color: #696969}
|
||||||
|
div.profiles h2 {font-size: medium; font-style: normal; font-weight: bold; color: black}
|
||||||
|
'''
|
||||||
|
|
||||||
|
feeds = []
|
||||||
|
feeds.append((u'Finance', u'http://feeds.feedburner.com/StrategyBusiness-Finance?format=xml'))
|
||||||
|
feeds.append((u'Global Perspective', u'http://feeds.feedburner.com/StrategyBusiness-GlobalPerspective?format=xml'))
|
||||||
|
feeds.append((u'Innovation', u'http://feeds.feedburner.com/StrategyBusiness-Innovation?format=xml'))
|
||||||
|
feeds.append((u'Marketing And Sales', u'http://feeds.feedburner.com/StrategyBusiness-MarketingAndSales?format=xml'))
|
||||||
|
feeds.append((u'Operations And Manufacturing', u'http://feeds.feedburner.com/StrategyBusiness-OperationsAndManufacturing?format=xml'))
|
||||||
|
feeds.append((u'Organizations And People', u'http://feeds.feedburner.com/StrategyBusiness-OrganizationsAndPeople?format=xml'))
|
||||||
|
feeds.append((u'Strategy And Leadership', u'http://feeds.feedburner.com/StrategyBusiness-StrategyAndLeadership?format=xml'))
|
||||||
|
feeds.append((u'Sustainability', u'http://feeds.feedburner.com/StrategyBusiness-Sustainability?format=xml'))
|
||||||
|
feeds.append((u'Auto, Airlines And Transport', u'http://feeds.feedburner.com/StrategyBusiness-AutoAirlinesAndTransport?format=xml'))
|
||||||
|
feeds.append((u'Consumer Products', u'http://feeds.feedburner.com/StrategyBusiness-ConsumerProducts?format=xml'))
|
||||||
|
feeds.append((u'Energy', u'http://feeds.feedburner.com/StrategyBusiness-Energy?format=xml'))
|
||||||
|
feeds.append((u'Health Care', u'http://feeds.feedburner.com/StrategyBusiness-HealthCare?format=xml'))
|
||||||
|
feeds.append((u'Technology', u'http://feeds.feedburner.com/StrategyBusiness-Technology?format=xml'))
|
||||||
|
feeds.append((u'Thought Leaders', u'http://feeds.feedburner.com/StrategyBusiness-ThoughtLeaders?format=xml'))
|
||||||
|
feeds.append((u'Business Literature', u'http://feeds.feedburner.com/StrategyBusiness-BusinessLiterature?format=xml'))
|
||||||
|
feeds.append((u'Recent Research', u'http://feeds.feedburner.com/StrategyBusiness-RecentResearch?format=xml'))
|
||||||
|
|
||||||
|
|
||||||
|
keep_only_tags = []
|
||||||
|
keep_only_tags.append(dict(name = 'div', attrs = {'id': 'sb-column2'}))
|
||||||
|
|
||||||
|
remove_tags = []
|
||||||
|
remove_tags.append(dict(name = 'img', attrs = {'class': 'content1'}))
|
||||||
|
remove_tags.append(dict(name = 'img', attrs = {'src': '/media/image/end_of_story.gif'}))
|
||||||
|
remove_tags.append(dict(name = 'div', attrs = {'class': 'sb-adarea468'}))
|
||||||
|
remove_tags.append(dict(name = 'div', attrs = {'id': 'sb-paging'}))
|
||||||
|
remove_tags.append(dict(name = 'div', attrs = {'id': 'textsize'}))
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url + '?pg=all'
|
96
resources/recipes/watchingamerica.recipe
Normal file
96
resources/recipes/watchingamerica.recipe
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
||||||
|
|
||||||
|
class WatchingAmericaRecipe(BasicNewsRecipe):
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__author__ = 'kwetal'
|
||||||
|
language = 'en'
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
title = u'Watching America'
|
||||||
|
publisher = u'watchingamerica.com'
|
||||||
|
category = u'News'
|
||||||
|
description = u'Global opinion about the United States'
|
||||||
|
|
||||||
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
use_embedded_content = False
|
||||||
|
|
||||||
|
no_stylesheets = True
|
||||||
|
remove_javascript = True
|
||||||
|
remove_attributes = ['style']
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
body{font-family:verdana,arial,helvetica,geneva,sans-serif ;}
|
||||||
|
.main_content em {font-size: x-small; font-style: italic; color: #696969;}
|
||||||
|
.main_content span strong {font-size: x-large; font-weight: bold;}
|
||||||
|
.insideitro {font-size: xx-small; font-style: italic; color: #666666;}
|
||||||
|
span {padding: 0em; margin 0em;}
|
||||||
|
'''
|
||||||
|
|
||||||
|
INDEX = u'http://watchingamerica.com/News/'
|
||||||
|
|
||||||
|
def parse_index(self):
|
||||||
|
answer = []
|
||||||
|
|
||||||
|
soup = self.index_to_soup(self.INDEX)
|
||||||
|
|
||||||
|
articles = []
|
||||||
|
feature = soup.find('div', attrs = {'id': 'headzone'})
|
||||||
|
if feature:
|
||||||
|
link = feature.find('a', attrs = {'class': 'feature'})
|
||||||
|
url = link.get('href', None)
|
||||||
|
title = self.tag_to_string(link)
|
||||||
|
description = self.tag_to_string(feature.find('h1', attrs = {'class': 'pull'}))
|
||||||
|
article = {'title': title, 'date': u'', 'url': url, 'description': description}
|
||||||
|
articles.append(article)
|
||||||
|
answer.append(('Feature', articles))
|
||||||
|
|
||||||
|
feed_titles = ['Translations from the West', 'Translations from the East']
|
||||||
|
for i in range(1, 3):
|
||||||
|
articles = []
|
||||||
|
div = soup.find('div', attrs = {'class': 'newscol' + str(i)})
|
||||||
|
if div:
|
||||||
|
for link in div.findAll('a', attrs = {'class': 'headline'}):
|
||||||
|
url = link.get('href', None)
|
||||||
|
title = self.tag_to_string(link)
|
||||||
|
|
||||||
|
description = None
|
||||||
|
h3 = link.findNextSibling('h3')
|
||||||
|
if h3:
|
||||||
|
description = self.tag_to_string(h3)
|
||||||
|
|
||||||
|
article = {'title': title, 'date': u'', 'url': url, 'description': description}
|
||||||
|
articles.append(article)
|
||||||
|
answer.append((feed_titles[i - 1], articles))
|
||||||
|
|
||||||
|
return answer
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
freshSoup = self.get_fresh_soup(soup)
|
||||||
|
article = soup.find('p', attrs = {'class': 'MsoNormal'}).parent
|
||||||
|
if article:
|
||||||
|
article.name = 'div'
|
||||||
|
del article['width']
|
||||||
|
article['class'] = 'main_content'
|
||||||
|
org = article.find('a', attrs = {'href': '?SHOW_ORIGINAL_TEXT'})
|
||||||
|
if org:
|
||||||
|
org.parent.extract()
|
||||||
|
|
||||||
|
intro = article.find('span', attrs = {'class': 'insideitro'})
|
||||||
|
if intro:
|
||||||
|
for el in intro.findAll(['strong', 'em', 'br']):
|
||||||
|
if el.name == 'br':
|
||||||
|
el.extract()
|
||||||
|
else:
|
||||||
|
el.name = 'div'
|
||||||
|
|
||||||
|
freshSoup.body.append(article)
|
||||||
|
|
||||||
|
return freshSoup
|
||||||
|
|
||||||
|
def get_fresh_soup(self, oldSoup):
|
||||||
|
freshSoup = BeautifulSoup('<html><head><title></title></head><body></body></html>')
|
||||||
|
if oldSoup.head.title:
|
||||||
|
freshSoup.head.title.append(self.tag_to_string(oldSoup.head.title))
|
||||||
|
return freshSoup
|
@ -1,138 +1,17 @@
|
|||||||
|
#include "util.h"
|
||||||
#include <stdlib.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";
|
// These variables must be filled in before compiling
|
||||||
|
|
||||||
static int
|
|
||||||
report_error(const char *msg) {
|
|
||||||
fprintf(stderr, msg);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
fflush(stderr);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// These variable must be filled in before compiling
|
|
||||||
static const char *ENV_VARS[] = { /*ENV_VARS*/ NULL };
|
static const char *ENV_VARS[] = { /*ENV_VARS*/ NULL };
|
||||||
static const char *ENV_VAR_VALS[] = { /*ENV_VAR_VALS*/ NULL};
|
static const char *ENV_VAR_VALS[] = { /*ENV_VAR_VALS*/ NULL};
|
||||||
static char PROGRAM[] = "**PROGRAM**";
|
static char PROGRAM[] = "**PROGRAM**";
|
||||||
static const char MODULE[] = "**MODULE**";
|
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
|
int
|
||||||
main(int argc, char * const *argv, char * const *envp) {
|
main(int argc, const char **argv, const char **envp) {
|
||||||
char *pathPtr = NULL;
|
return run(ENV_VARS, ENV_VAR_VALS, PROGRAM, MODULE, FUNCTION, PYVER, argc, argv, envp);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
py_compile, stat, operator
|
||||||
abspath, join, basename = os.path.abspath, os.path.join, os.path.basename
|
abspath, join, basename = os.path.abspath, os.path.join, os.path.basename
|
||||||
|
|
||||||
#TODO: WMF support in ImageMagick
|
from setup import __version__ as VERSION, __appname__ as APPNAME, basenames, \
|
||||||
|
modules as main_modules, Command, SRC, functions as main_functions
|
||||||
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']
|
|
||||||
LICENSE = open('LICENSE', 'rb').read()
|
LICENSE = open('LICENSE', 'rb').read()
|
||||||
ENV = dict(
|
ENV = dict(
|
||||||
PYTHONPATH='@executable_path/../Resources/Python/site-packages',
|
|
||||||
PYTHONHOME='@executable_path/../Resources/Python',
|
|
||||||
FC_CONFIG_DIR='@executable_path/../Resources/fonts',
|
FC_CONFIG_DIR='@executable_path/../Resources/fonts',
|
||||||
MAGICK_HOME='@executable_path/../Frameworks/ImageMagick',
|
MAGICK_HOME='@executable_path/../Frameworks/ImageMagick',
|
||||||
QT_PLUGIN_PATH='@executable_path/../MacOS',
|
QT_PLUGIN_PATH='@executable_path/../MacOS',
|
||||||
PYTHONDONTWRITEBYTECODE='1',
|
PYTHONIOENCODING='UTF-8',
|
||||||
PYTHONIOENCODING='utf-8:replace',
|
|
||||||
PYTHONOPTIMIZE='2',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SW = os.environ.get('SW', '/sw')
|
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')
|
gcc = os.environ.get('CC', 'gcc')
|
||||||
base = os.path.dirname(__file__)
|
base = os.path.dirname(__file__)
|
||||||
|
lib = compile_launcher_lib(contents_dir, gcc, base)
|
||||||
src = open(join(base, 'launcher.c'), 'rb').read()
|
src = open(join(base, 'launcher.c'), 'rb').read()
|
||||||
env, env_vals = [], []
|
env, env_vals = [], []
|
||||||
for key, val in ENV.items():
|
for key, val in ENV.items():
|
||||||
@ -46,22 +71,23 @@ def compile_launchers(contents_dir, xprograms):
|
|||||||
env_vals = ', '.join(env_vals)+', '
|
env_vals = ', '.join(env_vals)+', '
|
||||||
src = src.replace('/*ENV_VARS*/', env)
|
src = src.replace('/*ENV_VARS*/', env)
|
||||||
src = src.replace('/*ENV_VAR_VALS*/', env_vals)
|
src = src.replace('/*ENV_VAR_VALS*/', env_vals)
|
||||||
programs = []
|
programs = [lib]
|
||||||
for program, module in xprograms.items():
|
for program, x in xprograms.items():
|
||||||
print '\tCompiling', program
|
module, func = x
|
||||||
|
info('\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)
|
psrc = src.replace('**PROGRAM**', program)
|
||||||
psrc = psrc.replace('**MODULE**', module)
|
psrc = psrc.replace('**MODULE**', module)
|
||||||
|
psrc = psrc.replace('**FUNCTION**', func)
|
||||||
|
psrc = psrc.replace('**PYVER**', pyver)
|
||||||
fsrc = '/tmp/%s.c'%program
|
fsrc = '/tmp/%s.c'%program
|
||||||
with open(fsrc, 'wb') as f:
|
with open(fsrc, 'wb') as f:
|
||||||
f.write(psrc)
|
f.write(psrc)
|
||||||
cmd = [gcc, '-Wall', '-arch', 'x86_64',
|
cmd = [gcc, '-Wall', '-arch', 'ppc', '-arch', 'i386',
|
||||||
'-I%s/python/Python.framework/Headers'%SW,
|
'-I'+base, fsrc, lib, '-o', out,
|
||||||
fsrc, '-o', out, '-F%s/python'%SW,
|
|
||||||
'-framework', 'Python', '-framework', 'CoreFoundation',
|
|
||||||
'-headerpad_max_install_names']
|
'-headerpad_max_install_names']
|
||||||
print ' '.join(cmd)
|
info('\t'+' '.join(cmd))
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
subprocess.check_call(cmd)
|
subprocess.check_call(cmd)
|
||||||
return programs
|
return programs
|
||||||
@ -81,7 +107,7 @@ def flipwritable(fn, mode=None):
|
|||||||
def thin(path):
|
def thin(path):
|
||||||
try:
|
try:
|
||||||
subprocess.check_call(['lipo', path, '-verify_arch', 'ppc64'])
|
subprocess.check_call(['lipo', path, '-verify_arch', 'ppc64'])
|
||||||
print '\tThinning', path
|
info('\tThinning', path)
|
||||||
except:
|
except:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
@ -92,7 +118,7 @@ def strip_files(files, argv_max=(256 * 1024)):
|
|||||||
"""
|
"""
|
||||||
Strip a list of files
|
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:
|
while tostrip:
|
||||||
cmd = list(STRIPCMD)
|
cmd = list(STRIPCMD)
|
||||||
flips = []
|
flips = []
|
||||||
@ -125,61 +151,66 @@ class Py2App(object):
|
|||||||
|
|
||||||
FID = '@executable_path/../Frameworks'
|
FID = '@executable_path/../Frameworks'
|
||||||
|
|
||||||
def __init__(self, build_dir):
|
def __init__(self, build_dir, test_launchers=False):
|
||||||
self.build_dir = build_dir
|
self.build_dir = build_dir
|
||||||
self.contents_dir = join(self.build_dir, 'Contents')
|
self.contents_dir = join(self.build_dir, 'Contents')
|
||||||
self.resources_dir = join(self.contents_dir, 'Resources')
|
self.resources_dir = join(self.contents_dir, 'Resources')
|
||||||
self.frameworks_dir = join(self.contents_dir, 'Frameworks')
|
self.frameworks_dir = join(self.contents_dir, 'Frameworks')
|
||||||
self.version_info = '.'.join(map(str, sys.version_info[:2]))
|
self.version_info = '.'.join(map(str, sys.version_info[:2]))
|
||||||
|
self.site_packages = join(self.resources_dir, 'Python', 'site-packages')
|
||||||
self.to_strip = []
|
self.to_strip = []
|
||||||
self.warnings = []
|
self.warnings = []
|
||||||
|
|
||||||
|
self.run(test_launchers)
|
||||||
|
|
||||||
def warn(self, *args):
|
def warn(self, *args):
|
||||||
self.warnings.append(args)
|
warn(*args)
|
||||||
prefix = '' if args and args[0].startswith('WARNING:') else 'WARNING: '
|
|
||||||
sys.stdout.write(prefix+' '.join(args)+'\n')
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
|
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.add_python_framework()
|
||||||
self.create_skeleton()
|
self.add_qt_frameworks()
|
||||||
self.create_plist()
|
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_site_packages()
|
||||||
self.add_qt_frameworks()
|
self.add_stdlib()
|
||||||
self.add_calibre_plugins()
|
self.add_resources()
|
||||||
self.add_podofo()
|
self.compile_py_modules()
|
||||||
self.add_poppler()
|
|
||||||
self.add_libjpeg()
|
|
||||||
self.add_libpng()
|
|
||||||
self.add_fontconfig()
|
|
||||||
self.add_imagemagick()
|
|
||||||
self.add_misc_libraries()
|
|
||||||
|
|
||||||
self.add_site_packages()
|
self.create_console_app()
|
||||||
self.add_stdlib()
|
|
||||||
self.compile_py_modules()
|
|
||||||
|
|
||||||
self.create_console_app()
|
self.copy_site()
|
||||||
|
|
||||||
self.copy_launcher_and_site()
|
|
||||||
self.create_exe()
|
self.create_exe()
|
||||||
self.thin_to_x86_64()
|
if not test_launchers:
|
||||||
self.strip_files()
|
#self.thin_to_x86_64()
|
||||||
|
self.strip_files()
|
||||||
|
|
||||||
ret = self.makedmg(self.build_dir, APPNAME+'-'+VERSION+'-x86_64')
|
ret = self.makedmg(self.build_dir, APPNAME+'-'+VERSION)
|
||||||
sys.stdout.flush()
|
|
||||||
sys.stderr.flush()
|
|
||||||
|
|
||||||
print '\nThere were', len(self.warnings), 'warnings'
|
|
||||||
for w in list(self.warnings):
|
|
||||||
print
|
|
||||||
self.warn(*w)
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@flush
|
||||||
|
def add_resources(self):
|
||||||
|
shutil.copytree('resources', os.path.join(self.resources_dir,
|
||||||
|
'resources'))
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
def thin_to_x86_64(self):
|
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 y in (self.frameworks_dir, join(self.resources_dir, 'Python')):
|
||||||
for x in os.walk(y):
|
for x in os.walk(y):
|
||||||
for f in x[-1]:
|
for f in x[-1]:
|
||||||
@ -192,24 +223,22 @@ class Py2App(object):
|
|||||||
|
|
||||||
@flush
|
@flush
|
||||||
def strip_files(self):
|
def strip_files(self):
|
||||||
print '\nStripping files...'
|
info('\nStripping files...')
|
||||||
strip_files(self.to_strip)
|
strip_files(self.to_strip)
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
def create_exe(self):
|
def create_exe(self):
|
||||||
print '\nCreating launchers'
|
info('\nCreating launchers')
|
||||||
programs = {}
|
programs = {}
|
||||||
for program, module in zip(basenames['console'],
|
progs = []
|
||||||
main_modules['console'])+zip(basenames['gui'],
|
for x in ('console', 'gui'):
|
||||||
main_modules['gui']):
|
progs += list(zip(basenames[x], main_modules[x], main_functions[x]))
|
||||||
programs[program] = module
|
for program, module, func in progs:
|
||||||
programs = compile_launchers(self.contents_dir, programs)
|
programs[program] = (module, func)
|
||||||
|
programs = compile_launchers(self.contents_dir, programs,
|
||||||
|
self.version_info)
|
||||||
for out in programs:
|
for out in programs:
|
||||||
self.fix_dependencies_in_lib(out)
|
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
|
@flush
|
||||||
def set_id(self, path_to_lib, new_id):
|
def set_id(self, path_to_lib, new_id):
|
||||||
@ -233,20 +262,20 @@ class Py2App(object):
|
|||||||
def get_local_dependencies(self, path_to_lib):
|
def get_local_dependencies(self, path_to_lib):
|
||||||
for x in self.get_dependencies(path_to_lib):
|
for x in self.get_dependencies(path_to_lib):
|
||||||
for y in (SW+'/lib/', '/usr/local/lib/', SW+'/qt/lib/',
|
for y in (SW+'/lib/', '/usr/local/lib/', SW+'/qt/lib/',
|
||||||
SW+'/python/'):
|
SW+'/python/', SW+'/freetype/lib/'):
|
||||||
if x.startswith(y):
|
if x.startswith(y):
|
||||||
yield x, x[len(y):]
|
yield x, x[len(y):]
|
||||||
break
|
break
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
def change_dep(self, old_dep, new_dep, path_to_lib):
|
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,
|
subprocess.check_call(['install_name_tool', '-change', old_dep, new_dep,
|
||||||
path_to_lib])
|
path_to_lib])
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
def fix_dependencies_in_lib(self, path_to_lib):
|
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)
|
self.to_strip.append(path_to_lib)
|
||||||
old_mode = flipwritable(path_to_lib)
|
old_mode = flipwritable(path_to_lib)
|
||||||
for dep, bname in self.get_local_dependencies(path_to_lib):
|
for dep, bname in self.get_local_dependencies(path_to_lib):
|
||||||
@ -259,7 +288,7 @@ class Py2App(object):
|
|||||||
|
|
||||||
@flush
|
@flush
|
||||||
def add_python_framework(self):
|
def add_python_framework(self):
|
||||||
print '\nAdding Python framework'
|
info('\nAdding Python framework')
|
||||||
src = join(SW, 'python', 'Python.framework')
|
src = join(SW, 'python', 'Python.framework')
|
||||||
x = join(self.frameworks_dir, 'Python.framework')
|
x = join(self.frameworks_dir, 'Python.framework')
|
||||||
curr = os.path.realpath(join(src, 'Versions', 'Current'))
|
curr = os.path.realpath(join(src, 'Versions', 'Current'))
|
||||||
@ -274,7 +303,7 @@ class Py2App(object):
|
|||||||
@flush
|
@flush
|
||||||
def add_qt_frameworks(self):
|
def add_qt_frameworks(self):
|
||||||
for f in ('QtCore', 'QtGui', 'QtXml', 'QtNetwork', 'QtSvg', 'QtWebkit',
|
for f in ('QtCore', 'QtGui', 'QtXml', 'QtNetwork', 'QtSvg', 'QtWebkit',
|
||||||
'phonon'):
|
'QtXmlPatterns', 'phonon'):
|
||||||
self.add_qt_framework(f)
|
self.add_qt_framework(f)
|
||||||
for d in glob.glob(join(SW, 'qt', 'plugins', '*')):
|
for d in glob.glob(join(SW, 'qt', 'plugins', '*')):
|
||||||
shutil.copytree(d, join(self.contents_dir, 'MacOS', basename(d)))
|
shutil.copytree(d, join(self.contents_dir, 'MacOS', basename(d)))
|
||||||
@ -353,30 +382,30 @@ class Py2App(object):
|
|||||||
|
|
||||||
@flush
|
@flush
|
||||||
def add_podofo(self):
|
def add_podofo(self):
|
||||||
print '\nAdding PoDoFo'
|
info('\nAdding PoDoFo')
|
||||||
pdf = join(SW, 'lib', 'libpodofo.0.6.99.dylib')
|
pdf = join(SW, 'lib', 'libpodofo.0.6.99.dylib')
|
||||||
self.install_dylib(pdf)
|
self.install_dylib(pdf)
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
def add_poppler(self):
|
def add_poppler(self):
|
||||||
print '\nAdding poppler'
|
info('\nAdding poppler')
|
||||||
for x in ('libpoppler.4.dylib', 'libpoppler-qt4.3.dylib'):
|
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, 'lib', x))
|
||||||
self.install_dylib(os.path.join(SW, 'bin', 'pdftohtml'), False)
|
self.install_dylib(os.path.join(SW, 'bin', 'pdftohtml'), False)
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
def add_libjpeg(self):
|
def add_libjpeg(self):
|
||||||
print '\nAdding libjpeg'
|
info('\nAdding libjpeg')
|
||||||
self.install_dylib(os.path.join(SW, 'lib', 'libjpeg.7.dylib'))
|
self.install_dylib(os.path.join(SW, 'lib', 'libjpeg.7.dylib'))
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
def add_libpng(self):
|
def add_libpng(self):
|
||||||
print '\nAdding libpng'
|
info('\nAdding libpng')
|
||||||
self.install_dylib(os.path.join(SW, 'lib', 'libpng12.0.dylib'))
|
self.install_dylib(os.path.join(SW, 'lib', 'libpng12.0.dylib'))
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
def add_fontconfig(self):
|
def add_fontconfig(self):
|
||||||
print '\nAdding fontconfig'
|
info('\nAdding fontconfig')
|
||||||
for x in ('fontconfig.1', 'freetype.6', 'expat.1'):
|
for x in ('fontconfig.1', 'freetype.6', 'expat.1'):
|
||||||
src = os.path.join(SW, 'lib', 'lib'+x+'.dylib')
|
src = os.path.join(SW, 'lib', 'lib'+x+'.dylib')
|
||||||
self.install_dylib(src)
|
self.install_dylib(src)
|
||||||
@ -400,7 +429,7 @@ class Py2App(object):
|
|||||||
|
|
||||||
@flush
|
@flush
|
||||||
def add_imagemagick(self):
|
def add_imagemagick(self):
|
||||||
print '\nAdding ImageMagick'
|
info('\nAdding ImageMagick')
|
||||||
for x in ('Wand', 'Core'):
|
for x in ('Wand', 'Core'):
|
||||||
self.install_dylib(os.path.join(SW, 'lib', 'libMagick%s.2.dylib'%x))
|
self.install_dylib(os.path.join(SW, 'lib', 'libMagick%s.2.dylib'%x))
|
||||||
idir = glob.glob(os.path.join(SW, 'lib', 'ImageMagick-*'))[-1]
|
idir = glob.glob(os.path.join(SW, 'lib', 'ImageMagick-*'))[-1]
|
||||||
@ -419,16 +448,15 @@ class Py2App(object):
|
|||||||
|
|
||||||
@flush
|
@flush
|
||||||
def add_misc_libraries(self):
|
def add_misc_libraries(self):
|
||||||
for x in ('usb', 'unrar', 'readline.6.0'):
|
for x in ('usb', 'unrar', 'readline.6.0', 'wmflite-0.2.7'):
|
||||||
print '\nAdding', x
|
info('\nAdding', x)
|
||||||
x = 'lib%s.dylib'%x
|
x = 'lib%s.dylib'%x
|
||||||
shutil.copy2(join(SW, 'lib', x), self.frameworks_dir)
|
shutil.copy2(join(SW, 'lib', x), self.frameworks_dir)
|
||||||
self.set_id(join(self.frameworks_dir, x), self.FID+'/'+x)
|
self.set_id(join(self.frameworks_dir, x), self.FID+'/'+x)
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
def add_site_packages(self):
|
def add_site_packages(self):
|
||||||
print '\nAdding site-packages'
|
info('\nAdding site-packages')
|
||||||
self.site_packages = join(self.resources_dir, 'Python', 'site-packages')
|
|
||||||
os.makedirs(self.site_packages)
|
os.makedirs(self.site_packages)
|
||||||
paths = reversed(map(abspath, [x for x in sys.path if x.startswith('/')]))
|
paths = reversed(map(abspath, [x for x in sys.path if x.startswith('/')]))
|
||||||
upaths = []
|
upaths = []
|
||||||
@ -455,12 +483,12 @@ class Py2App(object):
|
|||||||
finally:
|
finally:
|
||||||
if tdir is not None:
|
if tdir is not None:
|
||||||
shutil.rmtree(tdir)
|
shutil.rmtree(tdir)
|
||||||
|
shutil.rmtree(os.path.join(self.site_packages, 'calibre', 'plugins'))
|
||||||
self.remove_bytecode(join(self.resources_dir, 'Python', 'site-packages'))
|
self.remove_bytecode(join(self.resources_dir, 'Python', 'site-packages'))
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
def add_modules_from_dir(self, src):
|
def add_modules_from_dir(self, src):
|
||||||
for x in glob.glob(join(src, '*.py'))+glob.glob(join(src, '*.so')):
|
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)
|
shutil.copy2(x, self.site_packages)
|
||||||
if x.endswith('.so'):
|
if x.endswith('.so'):
|
||||||
self.fix_dependencies_in_lib(x)
|
self.fix_dependencies_in_lib(x)
|
||||||
@ -507,7 +535,7 @@ class Py2App(object):
|
|||||||
|
|
||||||
@flush
|
@flush
|
||||||
def add_stdlib(self):
|
def add_stdlib(self):
|
||||||
print '\nAdding python stdlib'
|
info('\nAdding python stdlib')
|
||||||
src = join(SW, 'python/Python.framework/Versions/Current/lib/python')
|
src = join(SW, 'python/Python.framework/Versions/Current/lib/python')
|
||||||
src += self.version_info
|
src += self.version_info
|
||||||
dest = join(self.resources_dir, 'Python', 'lib', 'python')
|
dest = join(self.resources_dir, 'Python', 'lib', 'python')
|
||||||
@ -537,7 +565,7 @@ class Py2App(object):
|
|||||||
|
|
||||||
@flush
|
@flush
|
||||||
def compile_py_modules(self):
|
def compile_py_modules(self):
|
||||||
print '\nCompiling Python modules'
|
info( '\nCompiling Python modules')
|
||||||
base = join(self.resources_dir, 'Python')
|
base = join(self.resources_dir, 'Python')
|
||||||
for x in os.walk(base):
|
for x in os.walk(base):
|
||||||
root = x[0]
|
root = x[0]
|
||||||
@ -553,7 +581,7 @@ class Py2App(object):
|
|||||||
|
|
||||||
@flush
|
@flush
|
||||||
def create_console_app(self):
|
def create_console_app(self):
|
||||||
print '\nCreating console.app'
|
info( '\nCreating console.app')
|
||||||
cc_dir = os.path.join(self.contents_dir, 'console.app', 'Contents')
|
cc_dir = os.path.join(self.contents_dir, 'console.app', 'Contents')
|
||||||
os.makedirs(cc_dir)
|
os.makedirs(cc_dir)
|
||||||
for x in os.listdir(self.contents_dir):
|
for x in os.listdir(self.contents_dir):
|
||||||
@ -568,9 +596,8 @@ class Py2App(object):
|
|||||||
join(cc_dir, x))
|
join(cc_dir, x))
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
def copy_launcher_and_site(self):
|
def copy_site(self):
|
||||||
base = os.path.dirname(__file__)
|
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',
|
shutil.copy2(join(base, 'site.py'), join(self.resources_dir, 'Python',
|
||||||
'lib', 'python'+self.version_info))
|
'lib', 'python'+self.version_info))
|
||||||
|
|
||||||
@ -581,7 +608,7 @@ class Py2App(object):
|
|||||||
internet_enable=True,
|
internet_enable=True,
|
||||||
format='UDBZ'):
|
format='UDBZ'):
|
||||||
''' Copy a directory d into a dmg named volname '''
|
''' Copy a directory d into a dmg named volname '''
|
||||||
print '\nCreating dmg'
|
info('\nCreating dmg')
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
if not os.path.exists(destdir):
|
if not os.path.exists(destdir):
|
||||||
os.makedirs(destdir)
|
os.makedirs(destdir)
|
||||||
@ -593,7 +620,7 @@ class Py2App(object):
|
|||||||
if internet_enable:
|
if internet_enable:
|
||||||
subprocess.check_call(['/usr/bin/hdiutil', 'internet-enable', '-yes', dmg])
|
subprocess.check_call(['/usr/bin/hdiutil', 'internet-enable', '-yes', dmg])
|
||||||
size = os.stat(dmg).st_size/(1024*1024.)
|
size = os.stat(dmg).st_size/(1024*1024.)
|
||||||
print '\nInstaller size: %.2fMB\n'%size
|
info('\nInstaller size: %.2fMB\n'%size)
|
||||||
return dmg
|
return dmg
|
||||||
|
|
||||||
def test_exe():
|
def test_exe():
|
||||||
@ -603,15 +630,11 @@ def test_exe():
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main(test=False):
|
||||||
if 'test_exe' in sys.argv:
|
if 'test_exe' in sys.argv:
|
||||||
return test_exe()
|
return test_exe()
|
||||||
build_dir = abspath(join('build', APPNAME+'.app'))
|
build_dir = abspath(join(os.path.dirname(SRC), 'build', APPNAME+'.app'))
|
||||||
if os.path.exists(build_dir):
|
Py2App(build_dir, test_launchers=test)
|
||||||
shutil.rmtree(build_dir)
|
|
||||||
os.makedirs(build_dir)
|
|
||||||
py2app = Py2App(build_dir)
|
|
||||||
py2app.run()
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,11 +11,15 @@ def makepath(*paths):
|
|||||||
dir = os.path.abspath(os.path.join(*paths))
|
dir = os.path.abspath(os.path.join(*paths))
|
||||||
return dir, os.path.normcase(dir)
|
return dir, os.path.normcase(dir)
|
||||||
|
|
||||||
for m in sys.modules.values():
|
def abs__file__():
|
||||||
f = getattr(m, '__file__', None)
|
"""Set all module __file__ attribute to an absolute path"""
|
||||||
if isinstance(f, basestring) and os.path.exists(f):
|
for m in sys.modules.values():
|
||||||
m.__file__ = os.path.abspath(m.__file__)
|
if hasattr(m, '__loader__'):
|
||||||
del m
|
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
|
# This ensures that the initial path provided by the interpreter contains
|
||||||
# only absolute pathnames, even if we're running from the build directory.
|
# only absolute pathnames, even if we're running from the build directory.
|
||||||
@ -104,3 +108,42 @@ sys.setdefaultencoding('utf-8')
|
|||||||
#
|
#
|
||||||
if hasattr(sys, "setdefaultencoding"):
|
if hasattr(sys, "setdefaultencoding"):
|
||||||
del 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);
|
@ -11,8 +11,6 @@ from math import floor
|
|||||||
warnings.simplefilter('ignore', DeprecationWarning)
|
warnings.simplefilter('ignore', DeprecationWarning)
|
||||||
|
|
||||||
|
|
||||||
from PyQt4.QtCore import QUrl
|
|
||||||
from PyQt4.QtGui import QDesktopServices
|
|
||||||
from calibre.startup import plugins, winutil, winutilerror
|
from calibre.startup import plugins, winutil, winutilerror
|
||||||
from calibre.constants import iswindows, isosx, islinux, isfrozen, \
|
from calibre.constants import iswindows, isosx, islinux, isfrozen, \
|
||||||
terminal_controller, preferred_encoding, \
|
terminal_controller, preferred_encoding, \
|
||||||
@ -140,9 +138,6 @@ def prints(*args, **kwargs):
|
|||||||
class CommandLineError(Exception):
|
class CommandLineError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def setup_cli_handlers(logger, level):
|
def setup_cli_handlers(logger, level):
|
||||||
if os.environ.get('CALIBRE_WORKER', None) is not None and logger.handlers:
|
if os.environ.get('CALIBRE_WORKER', None) is not None and logger.handlers:
|
||||||
return
|
return
|
||||||
@ -347,6 +342,8 @@ def detect_ncpus():
|
|||||||
|
|
||||||
|
|
||||||
def launch(path_or_url):
|
def launch(path_or_url):
|
||||||
|
from PyQt4.QtCore import QUrl
|
||||||
|
from PyQt4.QtGui import QDesktopServices
|
||||||
if os.path.exists(path_or_url):
|
if os.path.exists(path_or_url):
|
||||||
path_or_url = 'file:'+path_or_url
|
path_or_url = 'file:'+path_or_url
|
||||||
QDesktopServices.openUrl(QUrl(path_or_url))
|
QDesktopServices.openUrl(QUrl(path_or_url))
|
||||||
@ -456,6 +453,60 @@ def ipython(user_ns=None):
|
|||||||
from calibre.utils.config import config_dir
|
from calibre.utils.config import config_dir
|
||||||
ipydir = os.path.join(config_dir, ('_' if iswindows else '.')+'ipython')
|
ipydir = os.path.join(config_dir, ('_' if iswindows else '.')+'ipython')
|
||||||
os.environ['IPYTHONDIR'] = ipydir
|
os.environ['IPYTHONDIR'] = ipydir
|
||||||
|
if not os.path.exists(ipydir):
|
||||||
|
os.makedirs(ipydir)
|
||||||
|
rc = os.path.join(ipydir, 'ipythonrc')
|
||||||
|
if not os.path.exists(rc):
|
||||||
|
open(rc, 'wb').write(' ')
|
||||||
|
UC = '''
|
||||||
|
import IPython.ipapi
|
||||||
|
ip = IPython.ipapi.get()
|
||||||
|
|
||||||
|
# You probably want to uncomment this if you did %upgrade -nolegacy
|
||||||
|
import ipy_defaults
|
||||||
|
|
||||||
|
import os, re, sys
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Handy tab-completers for %cd, %run, import etc.
|
||||||
|
# Try commenting this out if you have completion problems/slowness
|
||||||
|
import ipy_stock_completers
|
||||||
|
|
||||||
|
# uncomment if you want to get ipython -p sh behaviour
|
||||||
|
# without having to use command line switches
|
||||||
|
|
||||||
|
import ipy_profile_sh
|
||||||
|
|
||||||
|
|
||||||
|
# Configure your favourite editor?
|
||||||
|
# Good idea e.g. for %edit os.path.isfile
|
||||||
|
|
||||||
|
import ipy_editors
|
||||||
|
|
||||||
|
# Choose one of these:
|
||||||
|
|
||||||
|
#ipy_editors.scite()
|
||||||
|
#ipy_editors.scite('c:/opt/scite/scite.exe')
|
||||||
|
#ipy_editors.komodo()
|
||||||
|
#ipy_editors.idle()
|
||||||
|
# ... or many others, try 'ipy_editors??' after import to see them
|
||||||
|
|
||||||
|
# Or roll your own:
|
||||||
|
#ipy_editors.install_editor("c:/opt/jed +$line $file")
|
||||||
|
|
||||||
|
ipy_editors.kate()
|
||||||
|
|
||||||
|
o = ip.options
|
||||||
|
# An example on how to set options
|
||||||
|
#o.autocall = 1
|
||||||
|
o.system_verbose = 0
|
||||||
|
o.confirm_exit = 0
|
||||||
|
|
||||||
|
main()
|
||||||
|
'''
|
||||||
|
uc = os.path.join(ipydir, 'ipy_user_conf.py')
|
||||||
|
if not os.path.exists(uc):
|
||||||
|
open(uc, 'wb').write(UC)
|
||||||
from IPython.Shell import IPShellEmbed
|
from IPython.Shell import IPShellEmbed
|
||||||
ipshell = IPShellEmbed(user_ns=user_ns)
|
ipshell = IPShellEmbed(user_ns=user_ns)
|
||||||
ipshell()
|
ipshell()
|
||||||
|
@ -417,6 +417,7 @@ from calibre.devices.nokia.driver import N770, N810
|
|||||||
from calibre.devices.eslick.driver import ESLICK
|
from calibre.devices.eslick.driver import ESLICK
|
||||||
from calibre.devices.nuut2.driver import NUUT2
|
from calibre.devices.nuut2.driver import NUUT2
|
||||||
from calibre.devices.iriver.driver import IRIVER_STORY
|
from calibre.devices.iriver.driver import IRIVER_STORY
|
||||||
|
from calibre.devices.boox.driver import BOOX
|
||||||
|
|
||||||
from calibre.ebooks.metadata.fetch import GoogleBooks, ISBNDB, Amazon
|
from calibre.ebooks.metadata.fetch import GoogleBooks, ISBNDB, Amazon
|
||||||
plugins = [HTML2ZIP, PML2PMLZ, GoogleBooks, ISBNDB, Amazon]
|
plugins = [HTML2ZIP, PML2PMLZ, GoogleBooks, ISBNDB, Amazon]
|
||||||
@ -481,6 +482,7 @@ plugins += [
|
|||||||
ITALICA,
|
ITALICA,
|
||||||
SHINEBOOK,
|
SHINEBOOK,
|
||||||
ECLICTO,
|
ECLICTO,
|
||||||
|
BOOX,
|
||||||
EB600,
|
EB600,
|
||||||
]
|
]
|
||||||
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
|
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
|
||||||
|
0
src/calibre/devices/boox/__init__.py
Normal file
0
src/calibre/devices/boox/__init__.py
Normal file
87
src/calibre/devices/boox/driver.py
Normal file
87
src/calibre/devices/boox/driver.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2009, Jesus Manuel Marinho Valcarce <jjjesss at gmail.com>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
'''
|
||||||
|
Device driver for BOOX
|
||||||
|
'''
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from calibre.devices.usbms.driver import USBMS
|
||||||
|
|
||||||
|
class BOOX(USBMS):
|
||||||
|
|
||||||
|
name = 'BOOX driver'
|
||||||
|
gui_name = 'BOOX'
|
||||||
|
description = _('Communicate with the BOOX eBook reader.')
|
||||||
|
author = 'Jesus Manuel Marinho Valcarce'
|
||||||
|
supported_platforms = ['windows', 'osx', 'linux']
|
||||||
|
|
||||||
|
# Ordered list of supported formats
|
||||||
|
FORMATS = ['ebub', 'pdf', 'html', 'txt', 'rtf', 'mobi', 'prc', 'chm']
|
||||||
|
|
||||||
|
VENDOR_ID = [0x0525]
|
||||||
|
PRODUCT_ID = [0xa4a5]
|
||||||
|
BCD = [0x322]
|
||||||
|
|
||||||
|
VENDOR_NAME = 'Linux 2.6.26-466-ga04670e with fsl-usb2-udc'
|
||||||
|
WINDOWS_MAIN_MEM = 'FILE-STOR_GADGET'
|
||||||
|
WINDOWS_CARD_A_MEM = 'FILE-STOR_GADGET'
|
||||||
|
|
||||||
|
OSX_MAIN_MEM = 'Linux File-Stor Gadget Media'
|
||||||
|
OSX_CARD_A_MEM = 'Linux File-Stor Gadget Media'
|
||||||
|
|
||||||
|
MAIN_MEMORY_VOLUME_LABEL = 'BOOX Internal Memory'
|
||||||
|
STORAGE_CARD_VOLUME_LABEL = 'BOOX Storage Card'
|
||||||
|
|
||||||
|
EBOOK_DIR_MAIN = 'MyBooks'
|
||||||
|
EBOOK_DIR_CARD_A = 'MyBooks'
|
||||||
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
|
||||||
|
def windows_sort_drives(self, drives):
|
||||||
|
main = drives.get('main', None)
|
||||||
|
card = drives.get('carda', None)
|
||||||
|
if card and main and card > main:
|
||||||
|
drives['main'] = card
|
||||||
|
drives['carda'] = main
|
||||||
|
|
||||||
|
if card and not main:
|
||||||
|
drives['main'] = card
|
||||||
|
drives['carda'] = None
|
||||||
|
|
||||||
|
return drives
|
||||||
|
|
||||||
|
def osx_sort_names(self, names):
|
||||||
|
main = names.get('main', None)
|
||||||
|
card = names.get('carda', None)
|
||||||
|
|
||||||
|
try:
|
||||||
|
main_num = int(re.findall('\d+', main)[0]) if main else None
|
||||||
|
except:
|
||||||
|
main_num = None
|
||||||
|
try:
|
||||||
|
card_num = int(re.findall('\d+', card)[0]) if card else None
|
||||||
|
except:
|
||||||
|
card_num = None
|
||||||
|
|
||||||
|
if card_num is not None and main_num is not None and card_num > main_num:
|
||||||
|
names['main'] = card
|
||||||
|
names['carda'] = main
|
||||||
|
|
||||||
|
if card and not main:
|
||||||
|
names['main'] = card
|
||||||
|
names['carda'] = None
|
||||||
|
|
||||||
|
return names
|
||||||
|
|
||||||
|
def linux_swap_drives(self, drives):
|
||||||
|
if len(drives) < 2: return drives
|
||||||
|
drives = list(drives)
|
||||||
|
t = drives[0]
|
||||||
|
drives[0] = drives[1]
|
||||||
|
drives[1] = t
|
||||||
|
return tuple(drives)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -23,6 +23,7 @@ class NOOK(USBMS):
|
|||||||
gui_name = _('The Nook')
|
gui_name = _('The Nook')
|
||||||
description = _('Communicate with the Nook eBook reader.')
|
description = _('Communicate with the Nook eBook reader.')
|
||||||
author = 'John Schember'
|
author = 'John Schember'
|
||||||
|
icon = I('devices/nook.jpg')
|
||||||
supported_platforms = ['windows', 'linux', 'osx']
|
supported_platforms = ['windows', 'linux', 'osx']
|
||||||
|
|
||||||
# Ordered list of supported formats
|
# Ordered list of supported formats
|
||||||
|
@ -8,6 +8,7 @@ Based on ideas from comiclrf created by FangornUK.
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
import os, shutil, traceback, textwrap, time
|
import os, shutil, traceback, textwrap, time
|
||||||
|
from ctypes import byref
|
||||||
from Queue import Empty
|
from Queue import Empty
|
||||||
|
|
||||||
from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation
|
from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation
|
||||||
@ -75,7 +76,10 @@ class PageProcessor(list):
|
|||||||
if img < 0:
|
if img < 0:
|
||||||
raise RuntimeError('Cannot create wand.')
|
raise RuntimeError('Cannot create wand.')
|
||||||
if not pw.MagickReadImage(img, self.path_to_page):
|
if not pw.MagickReadImage(img, self.path_to_page):
|
||||||
raise IOError('Failed to read image from: %'%self.path_to_page)
|
severity = pw.ExceptionType(0)
|
||||||
|
msg = pw.MagickGetException(img, byref(severity))
|
||||||
|
raise IOError('Failed to read image from: %s: %s'
|
||||||
|
%(self.path_to_page, msg))
|
||||||
width = pw.MagickGetImageWidth(img)
|
width = pw.MagickGetImageWidth(img)
|
||||||
height = pw.MagickGetImageHeight(img)
|
height = pw.MagickGetImageHeight(img)
|
||||||
if self.num == 0: # First image so create a thumbnail from it
|
if self.num == 0: # First image so create a thumbnail from it
|
||||||
@ -363,14 +367,14 @@ class ComicInput(InputFormatPlugin):
|
|||||||
else:
|
else:
|
||||||
new_pages, failures = process_pages(new_pages, self.opts,
|
new_pages, failures = process_pages(new_pages, self.opts,
|
||||||
self.report_progress, tdir2)
|
self.report_progress, tdir2)
|
||||||
if not new_pages:
|
|
||||||
raise ValueError('Could not find any valid pages in comic: %s'
|
|
||||||
% comic)
|
|
||||||
if failures:
|
if failures:
|
||||||
self.log.warning('Could not process the following pages '
|
self.log.warning('Could not process the following pages '
|
||||||
'(run with --verbose to see why):')
|
'(run with --verbose to see why):')
|
||||||
for f in failures:
|
for f in failures:
|
||||||
self.log.warning('\t', f)
|
self.log.warning('\t', f)
|
||||||
|
if not new_pages:
|
||||||
|
raise ValueError('Could not find any valid pages in comic: %s'
|
||||||
|
% comic)
|
||||||
thumbnail = os.path.join(tdir2,
|
thumbnail = os.path.join(tdir2,
|
||||||
'thumbnail.'+self.opts.output_format.lower())
|
'thumbnail.'+self.opts.output_format.lower())
|
||||||
if not os.access(thumbnail, os.R_OK):
|
if not os.access(thumbnail, os.R_OK):
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>800</width>
|
<width>838</width>
|
||||||
<height>730</height>
|
<height>730</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@ -89,7 +89,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>524</width>
|
<width>562</width>
|
||||||
<height>683</height>
|
<height>683</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>335</width>
|
<width>335</width>
|
||||||
<height>540</height>
|
<height>570</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@ -84,104 +84,123 @@ p, li { white-space: pre-wrap; }
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0" colspan="2">
|
||||||
<widget class="QLabel" name="label_3">
|
<widget class="QScrollArea" name="scrollArea">
|
||||||
<property name="text">
|
<property name="widgetResizable">
|
||||||
<string>Title:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1" colspan="2">
|
|
||||||
<widget class="QLineEdit" name="title">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Regular expression (?P&lt;title&gt;)</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>No match</string>
|
|
||||||
</property>
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="label_4">
|
|
||||||
<property name="text">
|
|
||||||
<string>Authors:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1" colspan="2">
|
|
||||||
<widget class="QLineEdit" name="authors">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Regular expression (?P<author>)</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>No match</string>
|
|
||||||
</property>
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="text">
|
|
||||||
<string>Series:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1" colspan="2">
|
|
||||||
<widget class="QLineEdit" name="series">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Regular expression (?P<series>)</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>No match</string>
|
|
||||||
</property>
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0" rowspan="2">
|
|
||||||
<widget class="QLabel" name="label_6">
|
|
||||||
<property name="text">
|
|
||||||
<string>Series index:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1" rowspan="2" colspan="2">
|
|
||||||
<widget class="QLineEdit" name="series_index">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Regular expression (?P<series_index>)</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>No match</string>
|
|
||||||
</property>
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="0">
|
|
||||||
<widget class="QLabel" name="label_7">
|
|
||||||
<property name="text">
|
|
||||||
<string>ISBN:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="1">
|
|
||||||
<widget class="QLineEdit" name="isbn">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Regular expression (?P<isbn>)</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>No match</string>
|
|
||||||
</property>
|
|
||||||
<property name="readOnly">
|
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>301</width>
|
||||||
|
<height>234</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Title:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="title">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Regular expression (?P&lt;title&gt;)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No match</string>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Authors:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="authors">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Regular expression (?P<author>)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No match</string>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Series:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="series">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Regular expression (?P<series>)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No match</string>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>Series index:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="series_index">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Regular expression (?P<series_index>)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No match</string>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>ISBN:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QLineEdit" name="isbn">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Regular expression (?P<isbn>)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No match</string>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
@ -46,7 +46,10 @@ class PersistentTemporaryFile(object):
|
|||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self.close()
|
try:
|
||||||
|
self.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def PersistentTemporaryDirectory(suffix='', prefix='', dir=None):
|
def PersistentTemporaryDirectory(suffix='', prefix='', dir=None):
|
||||||
|
@ -828,7 +828,7 @@ else:
|
|||||||
IsMagickWand = _magick.IsMagickWand
|
IsMagickWand = _magick.IsMagickWand
|
||||||
# MagickGetException
|
# MagickGetException
|
||||||
try:
|
try:
|
||||||
_magick.MagickGetException.restype = ctypes.POINTER(ctypes.c_char)
|
_magick.MagickGetException.restype = ctypes.c_char_p
|
||||||
_magick.MagickGetException.argtypes = (MagickWand,ctypes.POINTER(ExceptionType))
|
_magick.MagickGetException.argtypes = (MagickWand,ctypes.POINTER(ExceptionType))
|
||||||
except AttributeError,e:
|
except AttributeError,e:
|
||||||
pass
|
pass
|
||||||
|
@ -7,6 +7,7 @@ __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import sys, os, cPickle
|
import sys, os, cPickle
|
||||||
|
from calibre.constants import isnewosx
|
||||||
|
|
||||||
AUTHTOOL="""#!/usr/bin/python
|
AUTHTOOL="""#!/usr/bin/python
|
||||||
import os
|
import os
|
||||||
@ -27,7 +28,7 @@ for s, l in zip(scripts, links):
|
|||||||
DEST_PATH = '/usr/bin'
|
DEST_PATH = '/usr/bin'
|
||||||
|
|
||||||
def create_symlinks():
|
def create_symlinks():
|
||||||
return create_symlinks_new() if getattr(sys, 'new_app_bundle', False) else create_symlinks_old()
|
return create_symlinks_new() if isnewosx else create_symlinks_old()
|
||||||
|
|
||||||
def get_scripts():
|
def get_scripts():
|
||||||
return cPickle.load(open(P('scripts.pickle'), 'rb'))
|
return cPickle.load(open(P('scripts.pickle'), 'rb'))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user