From 00506bfe5fbc4cfc3690bcb151e67b5a410b3331 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 21 Dec 2009 07:48:05 -0700 Subject: [PATCH 01/11] New recipe for Watching America by kwetal --- resources/recipes/watchingamerica.recipe | 96 ++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 resources/recipes/watchingamerica.recipe diff --git a/resources/recipes/watchingamerica.recipe b/resources/recipes/watchingamerica.recipe new file mode 100644 index 0000000000..9048e2550c --- /dev/null +++ b/resources/recipes/watchingamerica.recipe @@ -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('') + if oldSoup.head.title: + freshSoup.head.title.append(self.tag_to_string(oldSoup.head.title)) + return freshSoup From 1804fd02dd1a50164ba8b47b92c163e7ffbd1238 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 21 Dec 2009 08:31:31 -0700 Subject: [PATCH 02/11] ... --- src/calibre/gui2/dialogs/config/config.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/dialogs/config/config.ui b/src/calibre/gui2/dialogs/config/config.ui index 77c1d9eccd..f593df5e14 100644 --- a/src/calibre/gui2/dialogs/config/config.ui +++ b/src/calibre/gui2/dialogs/config/config.ui @@ -7,7 +7,7 @@ 0 0 - 800 + 838 730 @@ -89,7 +89,7 @@ 0 0 - 524 + 562 683 From 033de33a99dd4133aaff01bfb120e771ae0ddb7c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 21 Dec 2009 10:22:13 -0700 Subject: [PATCH 03/11] Image for Nook --- resources/images/devices/nook.jpg | Bin 0 -> 13454 bytes src/calibre/devices/nook/driver.py | 1 + 2 files changed, 1 insertion(+) create mode 100644 resources/images/devices/nook.jpg diff --git a/resources/images/devices/nook.jpg b/resources/images/devices/nook.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1ca38d81190bebc6f3a6f5330ff817fa7a93c27a GIT binary patch literal 13454 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 R#)D~(!?|P*Jvg;F{y)nkUTpvX literal 0 HcmV?d00001 diff --git a/src/calibre/devices/nook/driver.py b/src/calibre/devices/nook/driver.py index 001cc06b8e..1ce5db93a6 100644 --- a/src/calibre/devices/nook/driver.py +++ b/src/calibre/devices/nook/driver.py @@ -16,6 +16,7 @@ class NOOK(USBMS): gui_name = _('The Nook') description = _('Communicate with the Nook eBook reader.') author = 'John Schember' + icon = I('devices/nook.jpg') supported_platforms = ['windows', 'linux', 'osx'] # Ordered list of supported formats From 480da4436fada2ecd9d362fea358779eff523df4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 21 Dec 2009 11:14:49 -0700 Subject: [PATCH 04/11] Improved error reporting for comic conversion --- src/calibre/ebooks/comic/input.py | 12 ++++++++---- src/calibre/utils/PythonMagickWand.py | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/calibre/ebooks/comic/input.py b/src/calibre/ebooks/comic/input.py index f0acfe3a06..85590a7bae 100755 --- a/src/calibre/ebooks/comic/input.py +++ b/src/calibre/ebooks/comic/input.py @@ -8,6 +8,7 @@ Based on ideas from comiclrf created by FangornUK. ''' import os, shutil, traceback, textwrap, time +from ctypes import byref from Queue import Empty from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation @@ -75,7 +76,10 @@ class PageProcessor(list): if img < 0: raise RuntimeError('Cannot create wand.') 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) height = pw.MagickGetImageHeight(img) if self.num == 0: # First image so create a thumbnail from it @@ -363,14 +367,14 @@ class ComicInput(InputFormatPlugin): else: new_pages, failures = process_pages(new_pages, self.opts, self.report_progress, tdir2) - if not new_pages: - raise ValueError('Could not find any valid pages in comic: %s' - % comic) if failures: self.log.warning('Could not process the following pages ' '(run with --verbose to see why):') for f in failures: 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.'+self.opts.output_format.lower()) if not os.access(thumbnail, os.R_OK): diff --git a/src/calibre/utils/PythonMagickWand.py b/src/calibre/utils/PythonMagickWand.py index 16ca8d2935..9920334b0a 100644 --- a/src/calibre/utils/PythonMagickWand.py +++ b/src/calibre/utils/PythonMagickWand.py @@ -828,7 +828,7 @@ else: IsMagickWand = _magick.IsMagickWand # MagickGetException try: - _magick.MagickGetException.restype = ctypes.POINTER(ctypes.c_char) + _magick.MagickGetException.restype = ctypes.c_char_p _magick.MagickGetException.argtypes = (MagickWand,ctypes.POINTER(ExceptionType)) except AttributeError,e: pass From b72ddfbb713cc916db30a178cc79d57e5c2814e0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 21 Dec 2009 11:16:34 -0700 Subject: [PATCH 05/11] New recipe for Strategy and Businnes by kwetal --- resources/recipes/strategy-business.recipe | 64 ++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 resources/recipes/strategy-business.recipe diff --git a/resources/recipes/strategy-business.recipe b/resources/recipes/strategy-business.recipe new file mode 100644 index 0000000000..c5b5e7c629 --- /dev/null +++ b/resources/recipes/strategy-business.recipe @@ -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' From 65744341635b49747ba640405aa2c0f19989c0ee Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 21 Dec 2009 11:18:52 -0700 Subject: [PATCH 06/11] ... --- resources/recipes/strategy-business.recipe | 128 ++++++++++----------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/resources/recipes/strategy-business.recipe b/resources/recipes/strategy-business.recipe index c5b5e7c629..1ea85d6f21 100644 --- a/resources/recipes/strategy-business.recipe +++ b/resources/recipes/strategy-business.recipe @@ -1,64 +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' +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' From 0a8f15225c69e1436263b21da0dfb1cdb6a9ccca Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 21 Dec 2009 11:19:50 -0700 Subject: [PATCH 07/11] ... --- src/calibre/ptempfile.py | 5 ++++- src/calibre/utils/osx_symlinks.py | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/calibre/ptempfile.py b/src/calibre/ptempfile.py index fe69949f99..f4bcfa8675 100644 --- a/src/calibre/ptempfile.py +++ b/src/calibre/ptempfile.py @@ -46,7 +46,10 @@ class PersistentTemporaryFile(object): self.close() def __del__(self): - self.close() + try: + self.close() + except: + pass def PersistentTemporaryDirectory(suffix='', prefix='', dir=None): diff --git a/src/calibre/utils/osx_symlinks.py b/src/calibre/utils/osx_symlinks.py index 2cfdd72fa3..63863cb810 100644 --- a/src/calibre/utils/osx_symlinks.py +++ b/src/calibre/utils/osx_symlinks.py @@ -7,6 +7,7 @@ __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' import sys, os, cPickle +from calibre.constants import isnewosx AUTHTOOL="""#!/usr/bin/python import os @@ -27,7 +28,7 @@ for s, l in zip(scripts, links): DEST_PATH = '/usr/bin' 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(): return cPickle.load(open(P('scripts.pickle'), 'rb')) From 5f255fe64612b80dc07f198bcc479babea40818b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 21 Dec 2009 11:20:50 -0700 Subject: [PATCH 08/11] Update new os x app bundle build script --- setup/installer/osx/app/launcher.c | 135 +---------------- setup/installer/osx/app/launcher.py | 58 ------- setup/installer/osx/app/main.py | 227 +++++++++++++++------------- setup/installer/osx/app/site.py | 53 ++++++- setup/installer/osx/app/util.c | 220 +++++++++++++++++++++++++++ setup/installer/osx/app/util.h | 5 + 6 files changed, 405 insertions(+), 293 deletions(-) delete mode 100644 setup/installer/osx/app/launcher.py create mode 100644 setup/installer/osx/app/util.c create mode 100644 setup/installer/osx/app/util.h diff --git a/setup/installer/osx/app/launcher.c b/setup/installer/osx/app/launcher.c index 47d1c723c2..c36909efde 100644 --- a/setup/installer/osx/app/launcher.c +++ b/setup/installer/osx/app/launcher.c @@ -1,138 +1,17 @@ +#include "util.h" #include -#include -#include -#include -#include -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); } + diff --git a/setup/installer/osx/app/launcher.py b/setup/installer/osx/app/launcher.py deleted file mode 100644 index 14d304e7ee..0000000000 --- a/setup/installer/osx/app/launcher.py +++ /dev/null @@ -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 ' -__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() diff --git a/setup/installer/osx/app/main.py b/setup/installer/osx/app/main.py index e1819c9bfc..bde50376f7 100644 --- a/setup/installer/osx/app/main.py +++ b/setup/installer/osx/app/main.py @@ -10,33 +10,58 @@ import sys, os, shutil, plistlib, subprocess, glob, zipfile, tempfile, \ py_compile, stat, operator abspath, join, basename = os.path.abspath, os.path.join, os.path.basename -#TODO: WMF support in ImageMagick - -l = {} -exec open('setup.py').read() in l -VERSION = l['VERSION'] -APPNAME = l['APPNAME'] -scripts = l['scripts'] -basenames = l['basenames'] -main_functions = l['main_functions'] -main_modules = l['main_modules'] +from setup import __version__ as VERSION, __appname__ as APPNAME, basenames, \ + modules as main_modules, Command, SRC, functions as main_functions LICENSE = open('LICENSE', 'rb').read() ENV = dict( - PYTHONPATH='@executable_path/../Resources/Python/site-packages', - PYTHONHOME='@executable_path/../Resources/Python', FC_CONFIG_DIR='@executable_path/../Resources/fonts', MAGICK_HOME='@executable_path/../Frameworks/ImageMagick', QT_PLUGIN_PATH='@executable_path/../MacOS', - PYTHONDONTWRITEBYTECODE='1', - PYTHONIOENCODING='utf-8:replace', - PYTHONOPTIMIZE='2', + PYTHONIOENCODING='UTF-8', ) SW = os.environ.get('SW', '/sw') -def compile_launchers(contents_dir, xprograms): +info = warn = None + +class OSX32_Freeze(Command): + + description = 'Freeze OSX calibre installation' + + def add_options(self, parser): + parser.add_option('--test-launchers', default=False, + action='store_true', + help='Only build launchers') + + + def run(self, opts): + global info, warn + info, warn = self.info, self.warn + main(opts.test_launchers) + +def compile_launcher_lib(contents_dir, gcc, base): + info('\tCompiling calibre_launcher.dylib') + fd = join(contents_dir, 'Frameworks') + dest = join(fd, 'calibre-launcher.dylib') + src = join(base, 'util.c') + cmd = [gcc] + '-Wall -arch i386 -arch ppc -dynamiclib -std=gnu99'.split() + [src] + \ + ['-I'+base] + \ + ['-I%s/python/Python.framework/Headers'%SW] + \ + '-current_version 1.0 -compatibility_version 1.0'.split() + \ + '-fvisibility=hidden -o'.split() + [dest, '-F%s/python'%SW] + \ + ['-install_name', + '@executable_path/../Frameworks/'+os.path.basename(dest)] + \ + ['-framework', 'Python', '-framework', 'CoreFoundation', '-headerpad_max_install_names'] + info('\t'+' '.join(cmd)) + sys.stdout.flush() + subprocess.check_call(cmd) + return dest + + +def compile_launchers(contents_dir, xprograms, pyver): gcc = os.environ.get('CC', 'gcc') base = os.path.dirname(__file__) + lib = compile_launcher_lib(contents_dir, gcc, base) src = open(join(base, 'launcher.c'), 'rb').read() env, env_vals = [], [] for key, val in ENV.items(): @@ -46,22 +71,23 @@ def compile_launchers(contents_dir, xprograms): env_vals = ', '.join(env_vals)+', ' src = src.replace('/*ENV_VARS*/', env) src = src.replace('/*ENV_VAR_VALS*/', env_vals) - programs = [] - for program, module in xprograms.items(): - print '\tCompiling', program + programs = [lib] + for program, x in xprograms.items(): + module, func = x + info('\tCompiling', program) out = join(contents_dir, 'MacOS', program) programs.append(out) psrc = src.replace('**PROGRAM**', program) psrc = psrc.replace('**MODULE**', module) + psrc = psrc.replace('**FUNCTION**', func) + psrc = psrc.replace('**PYVER**', pyver) fsrc = '/tmp/%s.c'%program with open(fsrc, 'wb') as f: f.write(psrc) - cmd = [gcc, '-Wall', '-arch', 'x86_64', - '-I%s/python/Python.framework/Headers'%SW, - fsrc, '-o', out, '-F%s/python'%SW, - '-framework', 'Python', '-framework', 'CoreFoundation', + cmd = [gcc, '-Wall', '-arch', 'ppc', '-arch', 'i386', + '-I'+base, fsrc, lib, '-o', out, '-headerpad_max_install_names'] - print ' '.join(cmd) + info('\t'+' '.join(cmd)) sys.stdout.flush() subprocess.check_call(cmd) return programs @@ -81,7 +107,7 @@ def flipwritable(fn, mode=None): def thin(path): try: subprocess.check_call(['lipo', path, '-verify_arch', 'ppc64']) - print '\tThinning', path + info('\tThinning', path) except: return else: @@ -92,7 +118,7 @@ def strip_files(files, argv_max=(256 * 1024)): """ Strip a list of files """ - tostrip = [(fn, flipwritable(fn)) for fn in files] + tostrip = [(fn, flipwritable(fn)) for fn in files if os.path.exists(fn)] while tostrip: cmd = list(STRIPCMD) flips = [] @@ -125,61 +151,66 @@ class Py2App(object): FID = '@executable_path/../Frameworks' - def __init__(self, build_dir): + def __init__(self, build_dir, test_launchers=False): self.build_dir = build_dir self.contents_dir = join(self.build_dir, 'Contents') self.resources_dir = join(self.contents_dir, 'Resources') self.frameworks_dir = join(self.contents_dir, 'Frameworks') self.version_info = '.'.join(map(str, sys.version_info[:2])) + self.site_packages = join(self.resources_dir, 'Python', 'site-packages') self.to_strip = [] self.warnings = [] + self.run(test_launchers) + def warn(self, *args): - self.warnings.append(args) - prefix = '' if args and args[0].startswith('WARNING:') else 'WARNING: ' - sys.stdout.write(prefix+' '.join(args)+'\n') - sys.stdout.flush() + warn(*args) + def run(self, test_launchers): + ret = 0 + if not test_launchers: + if os.path.exists(self.build_dir): + shutil.rmtree(self.build_dir) + os.makedirs(self.build_dir) + self.create_skeleton() + self.create_plist() - def run(self): - self.create_skeleton() - self.create_plist() + self.add_python_framework() + self.add_qt_frameworks() + self.add_calibre_plugins() + self.add_podofo() + self.add_poppler() + self.add_libjpeg() + self.add_libpng() + self.add_fontconfig() + self.add_imagemagick() + self.add_misc_libraries() - self.add_python_framework() - self.add_qt_frameworks() - self.add_calibre_plugins() - self.add_podofo() - self.add_poppler() - self.add_libjpeg() - self.add_libpng() - self.add_fontconfig() - self.add_imagemagick() - self.add_misc_libraries() + self.add_site_packages() + self.add_stdlib() + self.add_resources() + self.compile_py_modules() - self.add_site_packages() - self.add_stdlib() - self.compile_py_modules() + self.create_console_app() - self.create_console_app() - - self.copy_launcher_and_site() + self.copy_site() self.create_exe() - self.thin_to_x86_64() - self.strip_files() + if not test_launchers: + #self.thin_to_x86_64() + self.strip_files() - ret = self.makedmg(self.build_dir, APPNAME+'-'+VERSION+'-x86_64') - sys.stdout.flush() - sys.stderr.flush() + ret = self.makedmg(self.build_dir, APPNAME+'-'+VERSION) - print '\nThere were', len(self.warnings), 'warnings' - for w in list(self.warnings): - print - self.warn(*w) return ret + @flush + def add_resources(self): + shutil.copytree('resources', os.path.join(self.resources_dir, + 'resources')) + @flush def thin_to_x86_64(self): - print '\nThinning to x86_64' + info('\nThinning to x86_64') for y in (self.frameworks_dir, join(self.resources_dir, 'Python')): for x in os.walk(y): for f in x[-1]: @@ -192,24 +223,22 @@ class Py2App(object): @flush def strip_files(self): - print '\nStripping files...' + info('\nStripping files...') strip_files(self.to_strip) @flush def create_exe(self): - print '\nCreating launchers' + info('\nCreating launchers') programs = {} - for program, module in zip(basenames['console'], - main_modules['console'])+zip(basenames['gui'], - main_modules['gui']): - programs[program] = module - programs = compile_launchers(self.contents_dir, programs) + progs = [] + for x in ('console', 'gui'): + progs += list(zip(basenames[x], main_modules[x], main_functions[x])) + for program, module, func in progs: + programs[program] = (module, func) + programs = compile_launchers(self.contents_dir, programs, + self.version_info) for out in programs: self.fix_dependencies_in_lib(out) - for module in main_modules['console'] + main_modules['gui']: - base = join(*module.split('.'))+'.py' - shutil.copy2(join('src', base), - join(self.resources_dir, 'Python', 'site-packages', base)) @flush def set_id(self, path_to_lib, new_id): @@ -233,20 +262,20 @@ class Py2App(object): def get_local_dependencies(self, path_to_lib): for x in self.get_dependencies(path_to_lib): for y in (SW+'/lib/', '/usr/local/lib/', SW+'/qt/lib/', - SW+'/python/'): + SW+'/python/', SW+'/freetype/lib/'): if x.startswith(y): yield x, x[len(y):] break @flush def change_dep(self, old_dep, new_dep, path_to_lib): - print '\tResolving dependency %s to'%old_dep, new_dep + info('\tResolving dependency %s to'%old_dep, new_dep) subprocess.check_call(['install_name_tool', '-change', old_dep, new_dep, path_to_lib]) @flush def fix_dependencies_in_lib(self, path_to_lib): - print '\nFixing dependencies in', path_to_lib + info('\nFixing dependencies in', path_to_lib) self.to_strip.append(path_to_lib) old_mode = flipwritable(path_to_lib) for dep, bname in self.get_local_dependencies(path_to_lib): @@ -259,7 +288,7 @@ class Py2App(object): @flush def add_python_framework(self): - print '\nAdding Python framework' + info('\nAdding Python framework') src = join(SW, 'python', 'Python.framework') x = join(self.frameworks_dir, 'Python.framework') curr = os.path.realpath(join(src, 'Versions', 'Current')) @@ -274,7 +303,7 @@ class Py2App(object): @flush def add_qt_frameworks(self): for f in ('QtCore', 'QtGui', 'QtXml', 'QtNetwork', 'QtSvg', 'QtWebkit', - 'phonon'): + 'QtXmlPatterns', 'phonon'): self.add_qt_framework(f) for d in glob.glob(join(SW, 'qt', 'plugins', '*')): shutil.copytree(d, join(self.contents_dir, 'MacOS', basename(d))) @@ -353,30 +382,30 @@ class Py2App(object): @flush def add_podofo(self): - print '\nAdding PoDoFo' + info('\nAdding PoDoFo') pdf = join(SW, 'lib', 'libpodofo.0.6.99.dylib') self.install_dylib(pdf) @flush def add_poppler(self): - print '\nAdding poppler' - for x in ('libpoppler.4.dylib', 'libpoppler-qt4.3.dylib'): + info('\nAdding poppler') + for x in ('libpoppler.5.dylib', 'libpoppler-qt4.3.dylib'): self.install_dylib(os.path.join(SW, 'lib', x)) self.install_dylib(os.path.join(SW, 'bin', 'pdftohtml'), False) @flush def add_libjpeg(self): - print '\nAdding libjpeg' + info('\nAdding libjpeg') self.install_dylib(os.path.join(SW, 'lib', 'libjpeg.7.dylib')) @flush def add_libpng(self): - print '\nAdding libpng' + info('\nAdding libpng') self.install_dylib(os.path.join(SW, 'lib', 'libpng12.0.dylib')) @flush def add_fontconfig(self): - print '\nAdding fontconfig' + info('\nAdding fontconfig') for x in ('fontconfig.1', 'freetype.6', 'expat.1'): src = os.path.join(SW, 'lib', 'lib'+x+'.dylib') self.install_dylib(src) @@ -400,7 +429,7 @@ class Py2App(object): @flush def add_imagemagick(self): - print '\nAdding ImageMagick' + info('\nAdding ImageMagick') for x in ('Wand', 'Core'): self.install_dylib(os.path.join(SW, 'lib', 'libMagick%s.2.dylib'%x)) idir = glob.glob(os.path.join(SW, 'lib', 'ImageMagick-*'))[-1] @@ -419,16 +448,15 @@ class Py2App(object): @flush def add_misc_libraries(self): - for x in ('usb', 'unrar', 'readline.6.0'): - print '\nAdding', x + for x in ('usb', 'unrar', 'readline.6.0', 'wmflite-0.2.7'): + info('\nAdding', x) x = 'lib%s.dylib'%x shutil.copy2(join(SW, 'lib', x), self.frameworks_dir) self.set_id(join(self.frameworks_dir, x), self.FID+'/'+x) @flush def add_site_packages(self): - print '\nAdding site-packages' - self.site_packages = join(self.resources_dir, 'Python', 'site-packages') + info('\nAdding site-packages') os.makedirs(self.site_packages) paths = reversed(map(abspath, [x for x in sys.path if x.startswith('/')])) upaths = [] @@ -455,12 +483,12 @@ class Py2App(object): finally: if tdir is not None: shutil.rmtree(tdir) + shutil.rmtree(os.path.join(self.site_packages, 'calibre', 'plugins')) self.remove_bytecode(join(self.resources_dir, 'Python', 'site-packages')) @flush def add_modules_from_dir(self, src): for x in glob.glob(join(src, '*.py'))+glob.glob(join(src, '*.so')): - dest = join(self.site_packages, basename(x)) shutil.copy2(x, self.site_packages) if x.endswith('.so'): self.fix_dependencies_in_lib(x) @@ -507,7 +535,7 @@ class Py2App(object): @flush def add_stdlib(self): - print '\nAdding python stdlib' + info('\nAdding python stdlib') src = join(SW, 'python/Python.framework/Versions/Current/lib/python') src += self.version_info dest = join(self.resources_dir, 'Python', 'lib', 'python') @@ -537,7 +565,7 @@ class Py2App(object): @flush def compile_py_modules(self): - print '\nCompiling Python modules' + info( '\nCompiling Python modules') base = join(self.resources_dir, 'Python') for x in os.walk(base): root = x[0] @@ -553,7 +581,7 @@ class Py2App(object): @flush def create_console_app(self): - print '\nCreating console.app' + info( '\nCreating console.app') cc_dir = os.path.join(self.contents_dir, 'console.app', 'Contents') os.makedirs(cc_dir) for x in os.listdir(self.contents_dir): @@ -568,9 +596,8 @@ class Py2App(object): join(cc_dir, x)) @flush - def copy_launcher_and_site(self): + def copy_site(self): base = os.path.dirname(__file__) - shutil.copy2(join(base, 'launcher.py'), self.resources_dir) shutil.copy2(join(base, 'site.py'), join(self.resources_dir, 'Python', 'lib', 'python'+self.version_info)) @@ -581,7 +608,7 @@ class Py2App(object): internet_enable=True, format='UDBZ'): ''' Copy a directory d into a dmg named volname ''' - print '\nCreating dmg' + info('\nCreating dmg') sys.stdout.flush() if not os.path.exists(destdir): os.makedirs(destdir) @@ -593,7 +620,7 @@ class Py2App(object): if internet_enable: subprocess.check_call(['/usr/bin/hdiutil', 'internet-enable', '-yes', dmg]) size = os.stat(dmg).st_size/(1024*1024.) - print '\nInstaller size: %.2fMB\n'%size + info('\nInstaller size: %.2fMB\n'%size) return dmg def test_exe(): @@ -603,15 +630,11 @@ def test_exe(): return 0 -def main(): +def main(test=False): if 'test_exe' in sys.argv: return test_exe() - build_dir = abspath(join('build', APPNAME+'.app')) - if os.path.exists(build_dir): - shutil.rmtree(build_dir) - os.makedirs(build_dir) - py2app = Py2App(build_dir) - py2app.run() + build_dir = abspath(join(os.path.dirname(SRC), 'build', APPNAME+'.app')) + Py2App(build_dir, test_launchers=test) return 0 diff --git a/setup/installer/osx/app/site.py b/setup/installer/osx/app/site.py index 4c98a871ca..5232706727 100644 --- a/setup/installer/osx/app/site.py +++ b/setup/installer/osx/app/site.py @@ -11,11 +11,15 @@ def makepath(*paths): dir = os.path.abspath(os.path.join(*paths)) return dir, os.path.normcase(dir) -for m in sys.modules.values(): - f = getattr(m, '__file__', None) - if isinstance(f, basestring) and os.path.exists(f): - m.__file__ = os.path.abspath(m.__file__) -del m +def abs__file__(): + """Set all module __file__ attribute to an absolute path""" + for m in sys.modules.values(): + if hasattr(m, '__loader__'): + continue # don't mess with a PEP 302-supplied __file__ + try: + m.__file__ = os.path.abspath(m.__file__) + except AttributeError: + continue # This ensures that the initial path provided by the interpreter contains # only absolute pathnames, even if we're running from the build directory. @@ -104,3 +108,42 @@ sys.setdefaultencoding('utf-8') # if hasattr(sys, "setdefaultencoding"): del sys.setdefaultencoding + +def run_entry_point(): + bname, mod, func = sys.calibre_basename, sys.calibre_module, sys.calibre_function + sys.argv[0] = bname + pmod = __import__(mod, fromlist=[1], level=0) + return getattr(pmod, func)() + +def add_calibre_vars(base): + sys.frameworks_dir = os.path.join(os.path.dirname(base), 'Frameworks') + sys.resources_location = os.path.abspath(os.path.join(base, 'resources')) + sys.extensions_location = os.path.join(sys.frameworks_dir, 'plugins') + sys.binaries_path = os.path.join(os.path.dirname(base), 'MacOS') + sys.console_binaries_path = os.path.join(os.path.dirname(base), + 'console.app', 'Contents', 'MacOS') + + dv = os.environ.get('CALIBRE_DEVELOP_FROM', None) + if dv and os.path.exists(dv): + sys.path.insert(0, os.path.abspath(dv)) + + +def main(): + global __file__ + base = sys.resourcepath + + sys.frozen = 'macosx_app' + sys.new_app_bundle = True + abs__file__() + + add_calibre_vars(base) + addsitedir(sys.site_packages) + + + for arg in list(sys.argv[1:]): + if arg.startswith('-psn'): + sys.argv.remove(arg) + + return run_entry_point() + + diff --git a/setup/installer/osx/app/util.c b/setup/installer/osx/app/util.c new file mode 100644 index 0000000000..763e07a309 --- /dev/null +++ b/setup/installer/osx/app/util.c @@ -0,0 +1,220 @@ +#include "util.h" +#include +#include +#include +#include +#include + +#define EXPORT __attribute__((visibility("default"))) + +static const char *ERR_OOM = "Out of memory"; + +static int +report_error(const char *msg) { + fprintf(stderr, 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; +} + + + diff --git a/setup/installer/osx/app/util.h b/setup/installer/osx/app/util.h new file mode 100644 index 0000000000..636af4db13 --- /dev/null +++ b/setup/installer/osx/app/util.h @@ -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); From 5fc7a22212933793e9d51cc16ded6a657a1eab71 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 21 Dec 2009 14:03:25 -0700 Subject: [PATCH 09/11] Driver for the Boox reader --- src/calibre/customize/builtins.py | 2 + src/calibre/devices/boox/__init__.py | 0 src/calibre/devices/boox/driver.py | 87 ++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 src/calibre/devices/boox/__init__.py create mode 100644 src/calibre/devices/boox/driver.py diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 9e53a433ad..5b95f8f489 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -417,6 +417,7 @@ from calibre.devices.nokia.driver import N770, N810 from calibre.devices.eslick.driver import ESLICK from calibre.devices.nuut2.driver import NUUT2 from calibre.devices.iriver.driver import IRIVER_STORY +from calibre.devices.boox.driver import BOOX from calibre.ebooks.metadata.fetch import GoogleBooks, ISBNDB, Amazon plugins = [HTML2ZIP, PML2PMLZ, GoogleBooks, ISBNDB, Amazon] @@ -481,6 +482,7 @@ plugins += [ ITALICA, SHINEBOOK, ECLICTO, + BOOX, EB600, ] plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ diff --git a/src/calibre/devices/boox/__init__.py b/src/calibre/devices/boox/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/calibre/devices/boox/driver.py b/src/calibre/devices/boox/driver.py new file mode 100644 index 0000000000..85495c459c --- /dev/null +++ b/src/calibre/devices/boox/driver.py @@ -0,0 +1,87 @@ +__license__ = 'GPL v3' +__copyright__ = '2009, Jesus Manuel Marinho Valcarce ' +__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) + + + From 1725e621fb3292d6e9d5e557fc87b37a283b559e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 21 Dec 2009 15:05:43 -0700 Subject: [PATCH 10/11] Setup default IPython environment for calibre-debug and delay loading of PyQt --- src/calibre/__init__.py | 61 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index ccef0f39e4..f63bbd9ade 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -11,8 +11,6 @@ from math import floor warnings.simplefilter('ignore', DeprecationWarning) -from PyQt4.QtCore import QUrl -from PyQt4.QtGui import QDesktopServices from calibre.startup import plugins, winutil, winutilerror from calibre.constants import iswindows, isosx, islinux, isfrozen, \ terminal_controller, preferred_encoding, \ @@ -140,9 +138,6 @@ def prints(*args, **kwargs): class CommandLineError(Exception): pass - - - def setup_cli_handlers(logger, level): if os.environ.get('CALIBRE_WORKER', None) is not None and logger.handlers: return @@ -347,6 +342,8 @@ def detect_ncpus(): def launch(path_or_url): + from PyQt4.QtCore import QUrl + from PyQt4.QtGui import QDesktopServices if os.path.exists(path_or_url): path_or_url = 'file:'+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 ipydir = os.path.join(config_dir, ('_' if iswindows else '.')+'ipython') 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 ipshell = IPShellEmbed(user_ns=user_ns) ipshell() From f6ee36e5a40e590ac486658e9b167e0b072fceab Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 21 Dec 2009 16:06:00 -0700 Subject: [PATCH 11/11] Put filename regexp tester into its own scroll area --- src/calibre/gui2/filename_pattern.ui | 213 +++++++++++++++------------ 1 file changed, 116 insertions(+), 97 deletions(-) diff --git a/src/calibre/gui2/filename_pattern.ui b/src/calibre/gui2/filename_pattern.ui index d0a4cf74b0..d120ca80b2 100644 --- a/src/calibre/gui2/filename_pattern.ui +++ b/src/calibre/gui2/filename_pattern.ui @@ -7,7 +7,7 @@ 0 0 335 - 540 + 570 @@ -84,104 +84,123 @@ p, li { white-space: pre-wrap; } - - - - Title: - - - - - - - Regular expression (?P&lt;title&gt;) - - - No match - - - true - - - - - - - Authors: - - - - - - - Regular expression (?P<author>) - - - No match - - - true - - - - - - - Series: - - - - - - - Regular expression (?P<series>) - - - No match - - - true - - - - - - - Series index: - - - - - - - Regular expression (?P<series_index>) - - - No match - - - true - - - - - - - ISBN: - - - - - - - Regular expression (?P<isbn>) - - - No match - - + + + true + + + + 0 + 0 + 301 + 234 + + + + + + + Title: + + + + + + + Regular expression (?P&lt;title&gt;) + + + No match + + + true + + + + + + + Authors: + + + + + + + Regular expression (?P<author>) + + + No match + + + true + + + + + + + Series: + + + + + + + Regular expression (?P<series>) + + + No match + + + true + + + + + + + Series index: + + + + + + + Regular expression (?P<series_index>) + + + No match + + + true + + + + + + + ISBN: + + + + + + + Regular expression (?P<isbn>) + + + No match + + + true + + + + +