diff --git a/.bzrignore b/.bzrignore index 88fc9188fc..77c465b105 100644 --- a/.bzrignore +++ b/.bzrignore @@ -2,6 +2,7 @@ .check-cache.pickle src/calibre/plugins resources/images.qrc +resources/compiled_coffeescript.zip src/calibre/ebooks/oeb/display/test/*.js src/calibre/manual/.build/ src/calibre/manual/cli/ @@ -16,7 +17,6 @@ resources/ebook-convert-complete.pickle resources/builtin_recipes.xml resources/builtin_recipes.zip resources/template-functions.json -resources/display/*.js setup/installer/windows/calibre/build.log src/calibre/translations/.errors src/cssutils/.svn/ diff --git a/setup/resources.py b/setup/resources.py index 0e91a9faa7..869ea979d8 100644 --- a/setup/resources.py +++ b/setup/resources.py @@ -26,7 +26,7 @@ def get_opts_from_parser(parser): class Coffee(Command): # {{{ description = 'Compile coffeescript files into javascript' - COFFEE_DIRS = {'ebooks/oeb/display': 'display'} + COFFEE_DIRS = ('ebooks/oeb/display',) def add_options(self, parser): parser.add_option('--watch', '-w', action='store_true', default=False, @@ -47,49 +47,69 @@ class Coffee(Command): # {{{ except KeyboardInterrupt: pass - def show_js(self, jsfile): + def show_js(self, raw): from pygments.lexers import JavascriptLexer from pygments.formatters import TerminalFormatter from pygments import highlight - with open(jsfile, 'rb') as f: - raw = f.read() print highlight(raw, JavascriptLexer(), TerminalFormatter()) def do_coffee_compile(self, opts, timestamp=False, ignore_errors=False): - for toplevel, dest in self.COFFEE_DIRS.iteritems(): - dest = self.j(self.RESOURCES, dest) - for x in glob.glob(self.j(self.SRC, __appname__, toplevel, '*.coffee')): - js = self.j(dest, os.path.basename(x.rpartition('.')[0]+'.js')) - if self.newer(js, x): - print ('\t%sCompiling %s'%(time.strftime('[%H:%M:%S] ') if - timestamp else '', os.path.basename(x))) - try: - cs = subprocess.check_output(self.compiler + - [x]).decode('utf-8') - except Exception as e: - print ('\n\tCompilation of %s failed'%os.path.basename(x)) - print (e) - if ignore_errors: - with open(js, 'wb') as f: - f.write('# Compilation from coffeescript failed') - else: - raise SystemExit(1) - else: - with open(js, 'wb') as f: - f.write(cs.encode('utf-8')) - if opts.show_js: - self.show_js(js) - print ('#'*80) - print ('#'*80) + src_files = {} + for src in self.COFFEE_DIRS: + for f in glob.glob(self.j(self.SRC, __appname__, src, + '*.coffee')): + bn = os.path.basename(f).rpartition('.')[0] + arcname = src.replace('/', '.') + '.' + bn + '.js' + src_files[arcname] = (f, os.stat(f).st_mtime) + + existing = {} + dest = self.j(self.RESOURCES, 'compiled_coffeescript.zip') + if os.path.exists(dest): + with zipfile.ZipFile(dest, 'r') as zf: + for info in zf.infolist(): + mtime = time.mktime(info.date_time + (0, 0, -1)) + arcname = info.filename + if (arcname in src_files and src_files[arcname][1] < + mtime): + existing[arcname] = (zf.read(info), info) + + todo = set(src_files) - set(existing) + updated = {} + for arcname in todo: + name = arcname.rpartition('.')[0] + print ('\t%sCompiling %s'%(time.strftime('[%H:%M:%S] ') if + timestamp else '', name)) + src = src_files[arcname][0] + try: + js = subprocess.check_output(self.compiler + + [src]).decode('utf-8') + except Exception as e: + print ('\n\tCompilation of %s failed'%name) + print (e) + if ignore_errors: + js = u'# Compilation from coffeescript failed' + else: + raise SystemExit(1) + else: + if opts.show_js: + self.show_js(js) + print ('#'*80) + print ('#'*80) + zi = zipfile.ZipInfo() + zi.filename = arcname + zi.date_time = time.localtime()[:6] + updated[arcname] = (js.encode('utf-8'), zi) + if updated: + with zipfile.ZipFile(dest, 'w', zipfile.ZIP_STORED) as zf: + for raw, zi in updated.itervalues(): + zf.writestr(zi, raw) + for raw, zi in existing.itervalues(): + zf.writestr(zi, raw) def clean(self): - for toplevel, dest in self.COFFEE_DIRS.iteritems(): - dest = self.j(self.RESOURCES, dest) - for x in glob.glob(self.j(self.SRC, __appname__, toplevel, '*.coffee')): - x = x.rpartition('.')[0] + '.js' - x = self.j(dest, os.path.basename(x)) - if os.path.exists(x): - os.remove(x) + x = self.j(self.RESOURCES, 'compiled_coffeescript.zip') + if os.path.exists(x): + os.remove(x) # }}} class Kakasi(Command): # {{{ diff --git a/src/calibre/gui2/viewer/javascript.py b/src/calibre/gui2/viewer/javascript.py index f5a29e26cc..18dd516a8b 100644 --- a/src/calibre/gui2/viewer/javascript.py +++ b/src/calibre/gui2/viewer/javascript.py @@ -11,6 +11,7 @@ import os, zipfile import calibre from calibre.utils.localization import lang_as_iso639_1 +from calibre.utils.resources import compiled_coffeescript class JavaScriptLoader(object): @@ -27,7 +28,7 @@ class JavaScriptLoader(object): }.iteritems()} CS = { - 'cfi':('ebooks/oeb/display/cfi.coffee', 'display/cfi.js'), + 'cfi':'ebooks.oeb.display.cfi', } ORDER = ('jquery', 'jquery_scrollTo', 'bookmarks', 'referencing', 'images', @@ -59,21 +60,9 @@ class JavaScriptLoader(object): ans = P(src, data=True, allow_user_override=False).decode('utf-8') else: - f = getattr(calibre, '__file__', None) - if self._dynamic_coffeescript and f and os.path.exists(f): - src = src[0] - src = os.path.join(os.path.dirname(f), *(src.split('/'))) - from calibre.utils.serve_coffee import compile_coffeescript - with open(src, 'rb') as f: - cs, errors = compile_coffeescript(f.read(), src) - if errors: - for line in errors: - print (line) - raise Exception('Failed to compile coffeescript' - ': %s'%src) - ans = cs - else: - ans = P(src[1], data=True, allow_user_override=False) + dynamic = (self._dynamic_coffeescript and + os.path.exists(calibre.__file__)) + ans = compiled_coffeescript(src, dynamic=dynamic).decode('utf-8') self._cache[name] = ans return ans diff --git a/src/calibre/utils/resources.py b/src/calibre/utils/resources.py index 00777973bb..d053136204 100644 --- a/src/calibre/utils/resources.py +++ b/src/calibre/utils/resources.py @@ -72,5 +72,37 @@ def get_path(path, data=False, allow_user_override=True): def get_image_path(path, data=False, allow_user_override=True): return get_path('images/'+path, data=data) +def _compile_coffeescript(name): + from calibre.utils.serve_coffee import compile_coffeescript + path = (u'/'.join(name.split('.'))) + '.coffee' + d = os.path.dirname + base = d(d(os.path.abspath(__file__))) + src = os.path.join(base, path) + with open(src, 'rb') as f: + cs, errors = compile_coffeescript(f.read(), src) + if errors: + for line in errors: + print (line) + raise Exception('Failed to compile coffeescript' + ': %s'%src) + return cs + +def compiled_coffeescript(name, dynamic=False): + if dynamic: + return _compile_coffeescript(name) + else: + import zipfile + zipf = get_path('compiled_coffeescript.zip', allow_user_override=False) + try: + with zipfile.ZipFile(zipf, 'r') as zf: + return zf.read(name+'.js') + except EnvironmentError: + # zipfile does not exist, probably someone running with + # CALIBRE_DEVELOP_FROM and an outdated binary install, so try to + # compile from source + if os.path.exists(zipf): raise + return _compile_coffeescript(name) + + __builtin__.__dict__['P'] = get_path __builtin__.__dict__['I'] = get_image_path