From 1a532ea24283253feddfb9a5baefc90caec301b9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 8 Sep 2009 17:33:58 -0600 Subject: [PATCH] More commands ported tot he new setup framework --- jsmin.py | 218 --------------------------------- setup/__init__.py | 10 +- setup/commands.py | 11 +- setup/gui.py | 4 +- setup/install.py | 117 +++++++++++++++--- setup/publish.py | 43 +++++++ src/calibre/manual/conf.py | 3 +- src/calibre/manual/custom.py | 4 + src/calibre/utils/resources.py | 11 +- 9 files changed, 178 insertions(+), 243 deletions(-) delete mode 100644 jsmin.py create mode 100644 setup/publish.py diff --git a/jsmin.py b/jsmin.py deleted file mode 100644 index ae7581413a..0000000000 --- a/jsmin.py +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/python - -# This code is original from jsmin by Douglas Crockford, it was translated to -# Python by Baruch Even. The original code had the following copyright and -# license. -# -# /* jsmin.c -# 2007-05-22 -# -# Copyright (c) 2002 Douglas Crockford (www.crockford.com) -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -# of the Software, and to permit persons to whom the Software is furnished to do -# so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# The Software shall be used for Good, not Evil. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# */ - -from StringIO import StringIO - -def jsmin(js): - ins = StringIO(js) - outs = StringIO() - JavascriptMinify().minify(ins, outs) - str = outs.getvalue() - if len(str) > 0 and str[0] == '\n': - str = str[1:] - return str - -def isAlphanum(c): - """return true if the character is a letter, digit, underscore, - dollar sign, or non-ASCII character. - """ - return ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or - (c >= 'A' and c <= 'Z') or c == '_' or c == '$' or c == '\\' or (c is not None and ord(c) > 126)); - -class UnterminatedComment(Exception): - pass - -class UnterminatedStringLiteral(Exception): - pass - -class UnterminatedRegularExpression(Exception): - pass - -class JavascriptMinify(object): - - def _outA(self): - self.outstream.write(self.theA) - def _outB(self): - self.outstream.write(self.theB) - - def _get(self): - """return the next character from stdin. Watch out for lookahead. If - the character is a control character, translate it to a space or - linefeed. - """ - c = self.theLookahead - self.theLookahead = None - if c == None: - c = self.instream.read(1) - if c >= ' ' or c == '\n': - return c - if c == '': # EOF - return '\000' - if c == '\r': - return '\n' - return ' ' - - def _peek(self): - self.theLookahead = self._get() - return self.theLookahead - - def _next(self): - """get the next character, excluding comments. peek() is used to see - if an unescaped '/' is followed by a '/' or '*'. - """ - c = self._get() - if c == '/' and self.theA != '\\': - p = self._peek() - if p == '/': - c = self._get() - while c > '\n': - c = self._get() - return c - if p == '*': - c = self._get() - while 1: - c = self._get() - if c == '*': - if self._peek() == '/': - self._get() - return ' ' - if c == '\000': - raise UnterminatedComment() - - return c - - def _action(self, action): - """do something! What you do is determined by the argument: - 1 Output A. Copy B to A. Get the next B. - 2 Copy B to A. Get the next B. (Delete A). - 3 Get the next B. (Delete B). - action treats a string as a single character. Wow! - action recognizes a regular expression if it is preceded by ( or , or =. - """ - if action <= 1: - self._outA() - - if action <= 2: - self.theA = self.theB - if self.theA == "'" or self.theA == '"': - while 1: - self._outA() - self.theA = self._get() - if self.theA == self.theB: - break - if self.theA <= '\n': - raise UnterminatedStringLiteral() - if self.theA == '\\': - self._outA() - self.theA = self._get() - - - if action <= 3: - self.theB = self._next() - if self.theB == '/' and (self.theA == '(' or self.theA == ',' or - self.theA == '=' or self.theA == ':' or - self.theA == '[' or self.theA == '?' or - self.theA == '!' or self.theA == '&' or - self.theA == '|' or self.theA == ';' or - self.theA == '{' or self.theA == '}' or - self.theA == '\n'): - self._outA() - self._outB() - while 1: - self.theA = self._get() - if self.theA == '/': - break - elif self.theA == '\\': - self._outA() - self.theA = self._get() - elif self.theA <= '\n': - raise UnterminatedRegularExpression() - self._outA() - self.theB = self._next() - - - def _jsmin(self): - """Copy the input to the output, deleting the characters which are - insignificant to JavaScript. Comments will be removed. Tabs will be - replaced with spaces. Carriage returns will be replaced with linefeeds. - Most spaces and linefeeds will be removed. - """ - self.theA = '\n' - self._action(3) - - while self.theA != '\000': - if self.theA == ' ': - if isAlphanum(self.theB): - self._action(1) - else: - self._action(2) - elif self.theA == '\n': - if self.theB in ['{', '[', '(', '+', '-']: - self._action(1) - elif self.theB == ' ': - self._action(3) - else: - if isAlphanum(self.theB): - self._action(1) - else: - self._action(2) - else: - if self.theB == ' ': - if isAlphanum(self.theA): - self._action(1) - else: - self._action(3) - elif self.theB == '\n': - if self.theA in ['}', ']', ')', '+', '-', '"', '\'']: - self._action(1) - else: - if isAlphanum(self.theA): - self._action(1) - else: - self._action(3) - else: - self._action(1) - - def minify(self, instream, outstream): - self.instream = instream - self.outstream = outstream - self.theA = '\n' - self.theB = None - self.theLookahead = None - - self._jsmin() - self.instream.close() - -if __name__ == '__main__': - import sys - jsm = JavascriptMinify() - jsm.minify(sys.stdin, sys.stdout) diff --git a/setup/__init__.py b/setup/__init__.py index 3be9df08c2..dc613edcb9 100644 --- a/setup/__init__.py +++ b/setup/__init__.py @@ -134,13 +134,19 @@ class Command(object): def pre_sub_commands(self, opts): pass + def running(self, cmd): + from setup.commands import command_names + self.info('\n*') + self.info('* Running', command_names[cmd]) + self.info('*\n') + def run_all(self, opts): self.pre_sub_commands(opts) for cmd in self.sub_commands: - self.info('Running', cmd.__class__.__name__) + self.running(cmd) cmd.run(opts) - self.info('Running', self.__class__.__name__) + self.running(self) self.run(opts) def add_all_options(self, parser): diff --git a/setup/commands.py b/setup/commands.py index fbbc15a27c..824228ba3a 100644 --- a/setup/commands.py +++ b/setup/commands.py @@ -10,9 +10,11 @@ __all__ = [ 'pot', 'translations', 'get_translations', 'iso639', 'build', 'gui', - 'develop', + 'develop', 'install', 'resources', 'check', + 'sdist', + 'manual', ] @@ -25,8 +27,10 @@ iso639 = ISO639() from setup.extensions import Build build = Build() -from setup.install import Develop +from setup.install import Develop, Install, Sdist develop = Develop() +install = Install() +sdist = Sdist() from setup.gui import GUI gui = GUI() @@ -37,6 +41,9 @@ check = Check() from setup.resources import Resources resources = Resources() +from setup.publish import Manual +manual = Manual() + commands = {} for x in __all__: commands[x] = locals()[x] diff --git a/setup/gui.py b/setup/gui.py index dd0bdfd204..a73c3466e3 100644 --- a/setup/gui.py +++ b/setup/gui.py @@ -38,8 +38,8 @@ class GUI(Command): try: os.chdir(self.RESOURCES) sources, files = [], [] - for root, _, files in os.walk('images'): - for name in files: + for root, _, files2 in os.walk('images'): + for name in files2: sources.append(os.path.join(root, name)) if self.newer(self.QRC, sources): self.info('Creating images.qrc') diff --git a/setup/install.py b/setup/install.py index bf726899c0..a85e2b1223 100644 --- a/setup/install.py +++ b/setup/install.py @@ -6,9 +6,10 @@ __license__ = 'GPL v3' __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import sys, os, textwrap, subprocess +import sys, os, textwrap, subprocess, shutil, tempfile, atexit -from setup import Command, islinux, basenames, modules, functions +from setup import Command, islinux, basenames, modules, functions, \ + __appname__, __version__ TEMPLATE = '''\ #!/usr/bin/env python @@ -39,7 +40,7 @@ class Develop(Command): ''') MODE = 0755 - sub_commands = ['build', 'translations'] + sub_commands = ['build', 'resources', 'gui'] def add_options(self, parser): parser.add_option('--prefix', @@ -61,9 +62,13 @@ class Develop(Command): self.regain_privileges() self.find_locations(opts) self.write_templates(opts) + self.install_files(opts) self.run_postinstall() self.success() + def install_files(self, opts): + pass + def run_postinstall(self): subprocess.check_call(['calibre_postinstall']) @@ -79,15 +84,99 @@ class Develop(Command): for typ in ('console', 'gui'): for name, mod, func in zip(basenames[typ], modules[typ], functions[typ]): - script = TEMPLATE.format( - module=mod, func=func, - path=self.path, resources=self.resources, - extensions=self.extensions) - prefix = opts.prefix - if prefix is None: - prefix = sys.prefix - path = self.j(prefix, 'bin', name) - self.info('Installing binary:', path) - open(path, 'wb').write(script) - os.chmod(path, self.MODE) + self.write_template(opts, name, mod, func) + if islinux: + self.write_template(opts, 'calibre_postinstall', 'calibre.linux', 'main') + + def write_template(self, opts, name, mod, func): + script = TEMPLATE.format( + module=mod, func=func, + path=self.path, resources=self.resources, + extensions=self.extensions) + prefix = opts.prefix + if prefix is None: + prefix = sys.prefix + path = self.j(prefix, 'bin', name) + self.info('Installing binary:', path) + open(path, 'wb').write(script) + os.chmod(path, self.MODE) + + +class Install(Develop): + + description = textwrap.dedent('''\ + Install calibre to your system. By default, calibre + is installed to /bin, /lib/calibre, + /share/calibre. These can all be controlled via options. + + The default is the prefix of your python installation. + ''') + + sub_commands = ['build'] + + def add_options(self, parser): + parser.add_option('--prefix', help='Installation prefix') + parser.add_option('--libdir', help='Where to put calibre library files') + parser.add_option('--bindir', help='Where to install calibre binaries') + parser.add_option('--sharedir', help='Where to install calibre data files') + + def find_locations(self, opts): + if opts.prefix is None: + opts.prefix = sys.prefix + if opts.libdir is None: + opts.libdir = self.j(opts.prefix, 'lib', 'calibre') + if opts.bindir is None: + opts.bindir = self.j(opts.prefix, 'bin') + if opts.sharedir is None: + opts.sharedir = self.j(opts.prefix, 'share', 'calibre') + self.path = opts.libdir + self.resources = opts.sharedir + self.extensions = self.j(self.path, 'calibre', 'plugins') + + def install_files(self, opts): + dest = self.path + if os.path.exists(dest): + shutil.rmtree(dest) + shutil.copytree(self.SRC, dest) + dest = self.resources + if os.path.exists(dest): + shutil.rmtree(dest) + shutil.copytree(self.RESOURCES, dest) + + def success(self): + self.info('\n\ncalibre successfully installed. You can start' + ' it by running the command calibre') + +class Sdist(Command): + + description = 'Create a source distribution' + DEST = os.path.join('dist', '%s-%s.tar.gz'%(__appname__, __version__)) + + + def run(self, opts): + if not self.e(self.d(self.DEST)): + os.makedirs(self.d(self.DEST)) + tdir = tempfile.mkdtemp() + atexit.register(shutil.rmtree, tdir) + self.info('\tRunning bzr export...') + subprocess.check_call(['bzr', 'export', '--format', 'dir', tdir]) + for x in open('.bzrignore').readlines(): + if not x.startswith('resources/'): continue + p = x.strip().replace('/', os.sep) + d = self.j(tdir, os.path.dirname(p)) + if not self.e(d): + os.makedirs(d) + if os.path.isdir(p): + shutil.copytree(p, self.j(tdir, p)) + else: + shutil.copy2(p, d) + self.info('\tCreating tarfile...') + subprocess.check_call(' '.join(['tar', '-czf', self.a(self.DEST), '*']), + cwd=tdir, shell=True) + + def clean(self): + if os.path.exists(self.DEST): + os.remove(self.DEST) + + diff --git a/setup/publish.py b/setup/publish.py new file mode 100644 index 0000000000..e150bb915f --- /dev/null +++ b/setup/publish.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import with_statement + +__license__ = 'GPL v3' +__copyright__ = '2009, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import os, shutil, subprocess + +from setup import Command, __appname__, __version__ + +class Manual(Command): + + description='''Build the User Manual ''' + + def run(self, opts): + cwd = os.path.abspath(os.getcwd()) + os.chdir(os.path.join(self.SRC, 'calibre', 'manual')) + try: + for d in ('.build', 'cli'): + if os.path.exists(d): + shutil.rmtree(d) + os.makedirs(d) + if not os.path.exists('.build'+os.sep+'html'): + os.makedirs('.build'+os.sep+'html') + os.environ['__appname__']= __appname__ + os.environ['__version__']= __version__ + subprocess.check_call(['sphinx-build', '-b', 'custom', '-t', 'online', + '-d', '.build/doctrees', '.', '.build/html']) + subprocess.check_call(['sphinx-build', '-b', 'epub', '-d', + '.build/doctrees', '.', '.build/epub']) + shutil.copyfile(self.j('.build', 'epub', 'calibre.epub'), self.j('.build', + 'html', 'calibre.epub')) + finally: + os.chdir(cwd) + + def clean(self): + path = os.path.join(self.SRC, 'calibre', 'manual', '.build') + if os.path.exists(path): + shutil.rmtree(path) + + diff --git a/src/calibre/manual/conf.py b/src/calibre/manual/conf.py index 8bea871349..690323694c 100644 --- a/src/calibre/manual/conf.py +++ b/src/calibre/manual/conf.py @@ -16,7 +16,8 @@ import sys, os # If your extensions are in another directory, add it here. sys.path.append(os.path.abspath('../../../')) sys.path.append(os.path.abspath('.')) -from calibre import __appname__, __version__ +__appname__ = os.environ.get('__appname__', 'calibre') +__version__ = os.environ.get('__version__', '0.0.0') import custom custom # General configuration diff --git a/src/calibre/manual/custom.py b/src/calibre/manual/custom.py index 6e519c9b2b..0df8ea4fa8 100644 --- a/src/calibre/manual/custom.py +++ b/src/calibre/manual/custom.py @@ -5,6 +5,10 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' import sys, os, inspect, re, textwrap +sys.path.insert(0, os.path.abspath('../../')) +sys.extensions_location = '../plugins' +sys.resources_location = '../../../resources' + from sphinx.builder import StandaloneHTMLBuilder from qthelp import QtHelpBuilder from epub import EPUBHelpBuilder diff --git a/src/calibre/utils/resources.py b/src/calibre/utils/resources.py index 0baaac014b..adfbebd9f0 100644 --- a/src/calibre/utils/resources.py +++ b/src/calibre/utils/resources.py @@ -9,12 +9,15 @@ __docformat__ = 'restructuredtext en' import __builtin__, sys, os -def get_path(path): +def get_path(path, data=False): path = path.replace(os.sep, '/') - return os.path.join(sys.resources_location, *path.split('/')) + path = os.path.join(sys.resources_location, *path.split('/')) + if data: + return open(path, 'rb').read() + return path -def get_image_path(path): - return get_path('images/'+path) +def get_image_path(path, data=False): + return get_path('images/'+path, data=data) __builtin__.__dict__['P'] = get_path __builtin__.__dict__['I'] = get_image_path