diff --git a/setup.py b/setup.py index 6bc4109277..16500a76f7 100644 --- a/setup.py +++ b/setup.py @@ -47,350 +47,15 @@ main_functions = { if __name__ == '__main__': from setuptools import setup, find_packages - from setuptools.command.build_py import build_py as _build_py, convert_path - from distutils.command.build import build as _build - from distutils.core import Command as _Command from pyqtdistutils import PyQtExtension, build_ext, Extension - import subprocess, glob + from upload2 import sdist, pot, build, build_py, manual, \ + resources, clean, gui, translations, update, \ + tag_release, upload_demo, build_linux, build_windows, \ + build_osx, upload_installers, upload_user_manual, \ + upload_to_pypi, stage3, stage2, stage1, upload - def newer(targets, sources): - ''' - Return True is sources is newer that targets or if targets - does not exist. - ''' - for f in targets: - if not os.path.exists(f): - return True - ttimes = map(lambda x: os.stat(x).st_mtime, targets) - stimes = map(lambda x: os.stat(x).st_mtime, sources) - newest_source, oldest_target = max(stimes), min(ttimes) - return newest_source > oldest_target - - class build_py(_build_py): - - def find_data_files(self, package, src_dir): - """ - Return filenames for package's data files in 'src_dir' - Modified to treat data file specs as paths not globs - """ - globs = (self.package_data.get('', []) - + self.package_data.get(package, [])) - files = self.manifest_files.get(package, [])[:] - for pattern in globs: - # Each pattern has to be converted to a platform-specific path - pattern = os.path.join(src_dir, convert_path(pattern)) - next = glob.glob(pattern) - files.extend(next if next else [pattern]) - - return self.exclude_data_files(package, src_dir, files) - - - class Command(_Command): - user_options = [] - def initialize_options(self): pass - def finalize_options(self): pass - - class sdist(Command): - - description = "create a source distribution using bzr" - - def run(self): - name = 'dist/calibre-%s.tar.gz'%VERSION - subprocess.check_call(('bzr export '+name).split()) - self.distribution.dist_files.append(('sdist', '', name)) - - class pot(Command): - description = '''Create the .pot template for all translatable strings''' - - PATH = os.path.join('src', APPNAME, 'translations') - - def source_files(self): - ans = [] - for root, dirs, files in os.walk(os.path.dirname(self.PATH)): - for name in files: - if name.endswith('.py'): - ans.append(os.path.abspath(os.path.join(root, name))) - return ans - - - def run(self): - sys.path.insert(0, os.path.abspath(self.PATH)) - try: - from pygettext import main as pygettext - files = self.source_files() - buf = cStringIO.StringIO() - print 'Creating translations template' - tempdir = tempfile.mkdtemp() - pygettext(buf, ['-k', '__', '-p', tempdir]+files) - src = buf.getvalue() - pot = os.path.join(tempdir, 'calibre.pot') - f = open(pot, 'wb') - f.write(src) - f.close() - print 'Translations template:', pot - return pot - finally: - sys.path.remove(os.path.abspath(self.PATH)) - - class manual(Command): - description='''Build the User Manual ''' - - def run(self): - cwd = os.path.abspath(os.getcwd()) - os.chdir(os.path.join('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') - subprocess.check_call(['sphinx-build', '-b', 'custom', '-d', - '.build/doctrees', '.', '.build/html']) - finally: - os.chdir(cwd) - - @classmethod - def clean(cls): - path = os.path.join('src', 'calibre', 'manual', '.build') - if os.path.exists(path): - shutil.rmtree(path) - - class resources(Command): - description='''Compile various resource files used in calibre. ''' - - RESOURCES = dict( - opf_template = 'ebooks/metadata/opf.xml', - ncx_template = 'ebooks/metadata/ncx.xml', - fb2_xsl = 'ebooks/lrf/fb2/fb2.xsl', - metadata_sqlite = 'library/metadata_sqlite.sql', - jquery = 'gui2/viewer/jquery.js', - jquery_scrollTo = 'gui2/viewer/jquery_scrollTo.js', - html_css = 'ebooks/oeb/html.css', - ) - - DEST = os.path.join('src', APPNAME, 'resources.py') - - def get_qt_translations(self): - data = {} - translations_found = False - for TPATH in ('/usr/share/qt4/translations', '/usr/lib/qt4/translations'): - if os.path.exists(TPATH): - files = glob.glob(TPATH + '/qt_??.qm') - for f in files: - key = os.path.basename(f).partition('.')[0] - data[key] = f - translations_found = True - break - if not translations_found: - print 'WARNING: Could not find Qt transations' - return data - - def get_static_resources(self): - sdir = os.path.join('src', 'calibre', 'library', 'static') - resources, max = {}, 0 - for f in os.listdir(sdir): - resources[f] = open(os.path.join(sdir, f), 'rb').read() - mtime = os.stat(os.path.join(sdir, f)).st_mtime - max = mtime if mtime > max else max - return resources, max - - def get_recipes(self): - sdir = os.path.join('src', 'calibre', 'web', 'feeds', 'recipes') - resources, max = {}, 0 - for f in os.listdir(sdir): - if f.endswith('.py') and f != '__init__.py': - resources[f.replace('.py', '')] = open(os.path.join(sdir, f), 'rb').read() - mtime = os.stat(os.path.join(sdir, f)).st_mtime - max = mtime if mtime > max else max - return resources, max - - def run(self): - data, dest, RESOURCES = {}, self.DEST, self.RESOURCES - for key in RESOURCES: - path = RESOURCES[key] - if not os.path.isabs(path): - RESOURCES[key] = os.path.join('src', APPNAME, path) - translations = self.get_qt_translations() - RESOURCES.update(translations) - static, smax = self.get_static_resources() - recipes, rmax = self.get_recipes() - amax = max(rmax, smax) - if newer([dest], RESOURCES.values()) or os.stat(dest).st_mtime < amax: - print 'Compiling resources...' - with open(dest, 'wb') as f: - for key in RESOURCES: - data = open(RESOURCES[key], 'rb').read() - f.write(key + ' = ' + repr(data)+'\n\n') - f.write('server_resources = %s\n\n'%repr(static)) - f.write('recipes = %s\n\n'%repr(recipes)) - f.write('build_time = "%s"\n\n'%time.strftime('%d %m %Y %H%M%S')) - else: - print 'Resources are up to date' - - @classmethod - def clean(cls): - path = cls.DEST - for path in glob.glob(path+'*'): - if os.path.exists(path): - os.remove(path) - - class translations(Command): - description='''Compile the translations''' - PATH = os.path.join('src', APPNAME, 'translations') - DEST = os.path.join(PATH, 'compiled.py') - - def run(self): - sys.path.insert(0, os.path.abspath(self.PATH)) - try: - files = glob.glob(os.path.join(self.PATH, '*.po')) - if newer([self.DEST], files): - from msgfmt import main as msgfmt - translations = {} - print 'Compiling translations...' - for po in files: - lang = os.path.basename(po).partition('.')[0] - buf = cStringIO.StringIO() - print 'Compiling', lang - msgfmt(buf, [po]) - translations[lang] = buf.getvalue() - open(self.DEST, 'wb').write('translations = '+repr(translations)) - else: - print 'Translations up to date' - finally: - sys.path.remove(os.path.abspath(self.PATH)) - - - @classmethod - def clean(cls): - path = cls.DEST - if os.path.exists(path): - os.remove(path) - - - class gui(Command): - description='''Compile all GUI forms and images''' - PATH = os.path.join('src', APPNAME, 'gui2') - IMAGES_DEST = os.path.join(PATH, 'images_rc.py') - QRC = os.path.join(PATH, 'images.qrc') - - @classmethod - def find_forms(cls): - forms = [] - for root, dirs, files in os.walk(cls.PATH): - for name in files: - if name.endswith('.ui'): - forms.append(os.path.abspath(os.path.join(root, name))) - - return forms - - @classmethod - def form_to_compiled_form(cls, form): - return form.rpartition('.')[0]+'_ui.py' - - def run(self): - self.build_forms() - self.build_images() - - def build_images(self): - cwd, images = os.getcwd(), os.path.basename(self.IMAGES_DEST) - try: - os.chdir(self.PATH) - sources, files = [], [] - for root, dirs, files in os.walk('images'): - for name in files: - sources.append(os.path.join(root, name)) - if newer([images], sources): - print 'Compiling images...' - for s in sources: - alias = ' alias="library"' if s.endswith('images'+os.sep+'library.png') else '' - files.append('%s'%(alias, s)) - manifest = '\n\n%s\n\n'%'\n'.join(files) - with open('images.qrc', 'wb') as f: - f.write(manifest) - subprocess.check_call(['pyrcc4', '-o', images, 'images.qrc']) - else: - print 'Images are up to date' - finally: - os.chdir(cwd) - - - def build_forms(self): - from PyQt4.uic import compileUi - forms = self.find_forms() - for form in forms: - compiled_form = self.form_to_compiled_form(form) - if not os.path.exists(compiled_form) or os.stat(form).st_mtime > os.stat(compiled_form).st_mtime: - print 'Compiling form', form - buf = cStringIO.StringIO() - compileUi(form, buf) - dat = buf.getvalue() - dat = dat.replace('__appname__', APPNAME) - dat = dat.replace('import images_rc', 'from calibre.gui2 import images_rc') - dat = dat.replace('from library import', 'from calibre.gui2.library import') - dat = dat.replace('from widgets import', 'from calibre.gui2.widgets import') - dat = re.compile(r'QtGui.QApplication.translate\(.+?,\s+"(.+?)(?' +__docformat__ = 'restructuredtext en' + +import shutil, os, glob, re, cStringIO, sys, tempfile, time, textwrap, socket, \ + struct +from setuptools.command.build_py import build_py as _build_py, convert_path +from distutils.core import Command +from subprocess import check_call, call, Popen +from distutils.command.build import build as _build + +raw = open(os.path.join('src', 'calibre', 'constants.py'), 'rb').read() +__version__ = re.search(r'__version__\s+=\s+[\'"]([^\'"]+)[\'"]', raw).group(1) +__appname__ = re.search(r'__appname__\s+=\s+[\'"]([^\'"]+)[\'"]', raw).group(1) + +PREFIX = "/var/www/calibre.kovidgoyal.net" +DOWNLOADS = PREFIX+"/htdocs/downloads" +DOCS = PREFIX+"/htdocs/apidocs" +USER_MANUAL = PREFIX+'/htdocs/user_manual' +HTML2LRF = "src/calibre/ebooks/lrf/html/demo" +TXT2LRF = "src/calibre/ebooks/lrf/txt/demo" +MOBILEREAD = 'ftp://dev.mobileread.com/calibre/' + def get_ip_address(ifname): + import fcntl s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) return socket.inet_ntoa(fcntl.ioctl( s.fileno(), @@ -16,252 +35,664 @@ def get_ip_address(ifname): try: HOST=get_ip_address('eth0') except: - HOST=get_ip_address('wlan0') -PROJECT=os.path.basename(os.getcwd()) + try: + HOST=get_ip_address('wlan0') + except: + HOST='unknown' + +def newer(targets, sources): + ''' + Return True is sources is newer that targets or if targets + does not exist. + ''' + for f in targets: + if not os.path.exists(f): + return True + ttimes = map(lambda x: os.stat(x).st_mtime, targets) + stimes = map(lambda x: os.stat(x).st_mtime, sources) + newest_source, oldest_target = max(stimes), min(ttimes) + return newest_source > oldest_target + -from calibre import __version__, __appname__ - -PREFIX = "/var/www/calibre.kovidgoyal.net" -DOWNLOADS = PREFIX+"/htdocs/downloads" -DOCS = PREFIX+"/htdocs/apidocs" -USER_MANUAL = PREFIX+'/htdocs/user_manual' -HTML2LRF = "src/calibre/ebooks/lrf/html/demo" -TXT2LRF = "src/calibre/ebooks/lrf/txt/demo" -MOBILEREAD = 'ftp://dev.mobileread.com/calibre/' -BUILD_SCRIPT ='''\ -#!/bin/bash -export CALIBRE_BUILDBOT=1 -cd ~/build && \ -rsync -avz --exclude src/calibre/plugins --exclude calibre/src/calibre.egg-info --exclude docs --exclude .bzr --exclude .build --exclude build --exclude dist --exclude "*.pyc" --exclude "*.pyo" rsync://%(host)s/work/%(project)s . && \ -cd %(project)s && \ -%%s && \ -rm -rf build/* dist/* && \ -%%s %%s -'''%dict(host=HOST, project=PROJECT) -check_call = partial(_check_call, shell=True) -#h = Host(hostType=VIX_SERVICEPROVIDER_VMWARE_WORKSTATION) +class OptionlessCommand(Command): + user_options = [] + def initialize_options(self): pass + def finalize_options(self): pass + + def run(self): + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) -def tag_release(): - print 'Tagging release' - check_call('bzr tag '+__version__) - check_call('bzr commit --unchanged -m "IGN:Tag release"') +class sdist(OptionlessCommand): + + description = 'Create a source distribution using bzr' + + def run(self): + name = os.path.join('dist', '%s-%s.tar.gz'%(__appname__, __version__)) + check_call(('bzr export '+name).split()) + self.distribution.dist_files.append(('sdist', '', name)) + print 'Source distribution created in', os.path.abspath(name) +class pot(OptionlessCommand): + description = '''Create the .pot template for all translatable strings''' + + PATH = os.path.join('src', __appname__, 'translations') + + def source_files(self): + ans = [] + for root, _, files in os.walk(os.path.dirname(self.PATH)): + for name in files: + if name.endswith('.py'): + ans.append(os.path.abspath(os.path.join(root, name))) + return ans + + + def run(self): + sys.path.insert(0, os.path.abspath(self.PATH)) + try: + pygettext = __import__('pygettext', fromlist=['main']).main + files = self.source_files() + buf = cStringIO.StringIO() + print 'Creating translations template' + tempdir = tempfile.mkdtemp() + pygettext(buf, ['-k', '__', '-p', tempdir]+files) + src = buf.getvalue() + pot = os.path.join(tempdir, __appname__+'.pot') + f = open(pot, 'wb') + f.write(src) + f.close() + print 'Translations template:', pot + return pot + finally: + sys.path.remove(os.path.abspath(self.PATH)) + +class manual(OptionlessCommand): + + description='''Build the User Manual ''' + + def run(self): + cwd = os.path.abspath(os.getcwd()) + os.chdir(os.path.join('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') + check_call(['sphinx-build', '-b', 'custom', '-d', + '.build/doctrees', '.', '.build/html']) + finally: + os.chdir(cwd) + + @classmethod + def clean(cls): + path = os.path.join('src', 'calibre', 'manual', '.build') + if os.path.exists(path): + shutil.rmtree(path) + +class resources(OptionlessCommand): + description='''Compile various resource files used in calibre. ''' + + RESOURCES = dict( + opf_template = 'ebooks/metadata/opf.xml', + ncx_template = 'ebooks/metadata/ncx.xml', + fb2_xsl = 'ebooks/lrf/fb2/fb2.xsl', + metadata_sqlite = 'library/metadata_sqlite.sql', + jquery = 'gui2/viewer/jquery.js', + jquery_scrollTo = 'gui2/viewer/jquery_scrollTo.js', + html_css = 'ebooks/oeb/html.css', + ) + + DEST = os.path.join('src', __appname__, 'resources.py') + + def get_qt_translations(self): + data = {} + translations_found = False + for TPATH in ('/usr/share/qt4/translations', '/usr/lib/qt4/translations'): + if os.path.exists(TPATH): + files = glob.glob(TPATH + '/qt_??.qm') + for f in files: + key = os.path.basename(f).partition('.')[0] + data[key] = f + translations_found = True + break + if not translations_found: + print 'WARNING: Could not find Qt transations' + return data + + def get_static_resources(self): + sdir = os.path.join('src', 'calibre', 'library', 'static') + resources, max = {}, 0 + for f in os.listdir(sdir): + resources[f] = open(os.path.join(sdir, f), 'rb').read() + mtime = os.stat(os.path.join(sdir, f)).st_mtime + max = mtime if mtime > max else max + return resources, max + + def get_recipes(self): + sdir = os.path.join('src', 'calibre', 'web', 'feeds', 'recipes') + resources, max = {}, 0 + for f in os.listdir(sdir): + if f.endswith('.py') and f != '__init__.py': + resources[f.replace('.py', '')] = open(os.path.join(sdir, f), 'rb').read() + mtime = os.stat(os.path.join(sdir, f)).st_mtime + max = mtime if mtime > max else max + return resources, max + + def run(self): + data, dest, RESOURCES = {}, self.DEST, self.RESOURCES + for key in RESOURCES: + path = RESOURCES[key] + if not os.path.isabs(path): + RESOURCES[key] = os.path.join('src', __appname__, path) + translations = self.get_qt_translations() + RESOURCES.update(translations) + static, smax = self.get_static_resources() + recipes, rmax = self.get_recipes() + amax = max(rmax, smax) + if newer([dest], RESOURCES.values()) or os.stat(dest).st_mtime < amax: + print 'Compiling resources...' + with open(dest, 'wb') as f: + for key in RESOURCES: + data = open(RESOURCES[key], 'rb').read() + f.write(key + ' = ' + repr(data)+'\n\n') + f.write('server_resources = %s\n\n'%repr(static)) + f.write('recipes = %s\n\n'%repr(recipes)) + f.write('build_time = "%s"\n\n'%time.strftime('%d %m %Y %H%M%S')) + else: + print 'Resources are up to date' + + @classmethod + def clean(cls): + path = cls.DEST + for path in glob.glob(path+'*'): + if os.path.exists(path): + os.remove(path) + +class translations(OptionlessCommand): + description='''Compile the translations''' + PATH = os.path.join('src', __appname__, 'translations') + DEST = os.path.join(PATH, 'compiled.py') + + def run(self): + sys.path.insert(0, os.path.abspath(self.PATH)) + try: + files = glob.glob(os.path.join(self.PATH, '*.po')) + if newer([self.DEST], files): + msgfmt = __import__('msgfmt', fromlist=['main']).main + translations = {} + print 'Compiling translations...' + for po in files: + lang = os.path.basename(po).partition('.')[0] + buf = cStringIO.StringIO() + print 'Compiling', lang + msgfmt(buf, [po]) + translations[lang] = buf.getvalue() + open(self.DEST, 'wb').write('translations = '+repr(translations)) + else: + print 'Translations up to date' + finally: + sys.path.remove(os.path.abspath(self.PATH)) + + + @classmethod + def clean(cls): + path = cls.DEST + if os.path.exists(path): + os.remove(path) + + +class gui(OptionlessCommand): + description='''Compile all GUI forms and images''' + PATH = os.path.join('src', __appname__, 'gui2') + IMAGES_DEST = os.path.join(PATH, 'images_rc.py') + QRC = os.path.join(PATH, 'images.qrc') + + @classmethod + def find_forms(cls): + forms = [] + for root, _, files in os.walk(cls.PATH): + for name in files: + if name.endswith('.ui'): + forms.append(os.path.abspath(os.path.join(root, name))) + + return forms + + @classmethod + def form_to_compiled_form(cls, form): + return form.rpartition('.')[0]+'_ui.py' + + def run(self): + self.build_forms() + self.build_images() + + def build_images(self): + cwd, images = os.getcwd(), os.path.basename(self.IMAGES_DEST) + try: + os.chdir(self.PATH) + sources, files = [], [] + for root, _, files in os.walk('images'): + for name in files: + sources.append(os.path.join(root, name)) + if newer([images], sources): + print 'Compiling images...' + for s in sources: + alias = ' alias="library"' if s.endswith('images'+os.sep+'library.png') else '' + files.append('%s'%(alias, s)) + manifest = '\n\n%s\n\n'%'\n'.join(files) + with open('images.qrc', 'wb') as f: + f.write(manifest) + check_call(['pyrcc4', '-o', images, 'images.qrc']) + else: + print 'Images are up to date' + finally: + os.chdir(cwd) + + + def build_forms(self): + from PyQt4.uic import compileUi + forms = self.find_forms() + for form in forms: + compiled_form = self.form_to_compiled_form(form) + if not os.path.exists(compiled_form) or os.stat(form).st_mtime > os.stat(compiled_form).st_mtime: + print 'Compiling form', form + buf = cStringIO.StringIO() + compileUi(form, buf) + dat = buf.getvalue() + dat = dat.replace('__appname__', __appname__) + dat = dat.replace('import images_rc', 'from calibre.gui2 import images_rc') + dat = dat.replace('from library import', 'from calibre.gui2.library import') + dat = dat.replace('from widgets import', 'from calibre.gui2.widgets import') + dat = re.compile(r'QtGui.QApplication.translate\(.+?,\s+"(.+?)(? %s/latest_version'''%(__version__, DOWNLOADS)) - - -def upload_docs(): - check_call('''epydoc --config epydoc.conf''') - check_call('''scp -r docs/html divok:%s/'''%(DOCS,)) - check_call('''epydoc -v --config epydoc-pdf.conf''') - check_call('''scp docs/pdf/api.pdf divok:%s/'''%(DOCS,)) - -def upload_user_manual(): - check_call('python setup.py manual') - check_call('scp -r src/calibre/manual/.build/html/* divok:%s'%USER_MANUAL) + return b.getvalue().split() if listonly else b.getvalue().splitlines() -def build_src_tarball(): - check_call('bzr export dist/calibre-%s.tar.gz'%__version__) + def curl_delete_file(self, path, url=MOBILEREAD): + import pycurl + c = pycurl.Curl() + c.setopt(pycurl.URL, url) + c.setopt(c.FTP_USE_EPSV, 1) + c.setopt(c.NETRC, c.NETRC_REQUIRED) + print 'Deleting file %s on %s'%(path, url) + c.setopt(c.QUOTE, ['dele '+ path]) + c.perform() + c.close() + + + def curl_upload_file(self, stream, url): + import pycurl + c = pycurl.Curl() + c.setopt(pycurl.URL, url) + c.setopt(pycurl.UPLOAD, 1) + c.setopt(c.NETRC, c.NETRC_REQUIRED) + c.setopt(pycurl.READFUNCTION, stream.read) + stream.seek(0, 2) + c.setopt(pycurl.INFILESIZE_LARGE, stream.tell()) + stream.seek(0) + c.setopt(c.NOPROGRESS, 0) + c.setopt(c.FTP_CREATE_MISSING_DIRS, 1) + print 'Uploading file %s to url %s' % (getattr(stream, 'name', ''), url) + try: + c.perform() + c.close() + except: + pass + files = self.curl_list_dir(listonly=0) + for line in files: + line = line.split() + if url.endswith(line[-1]): + size = long(line[4]) + stream.seek(0,2) + if size != stream.tell(): + raise RuntimeError('curl failed to upload %s correctly'%getattr(stream, 'name', '')) + + def upload_installer(self, name): + if not os.path.exists(name): + return + bname = os.path.basename(name) + pat = re.compile(bname.replace(__version__, r'\d+\.\d+\.\d+')) + for f in self.curl_list_dir(): + if pat.search(f): + self.curl_delete_file('/calibre/'+f) + self.curl_upload_file(open(name, 'rb'), MOBILEREAD+os.path.basename(name)) -def upload_src_tarball(): - check_call('ssh divok rm -f %s/calibre-\*.tar.gz'%DOWNLOADS) - check_call('scp dist/calibre-*.tar.gz divok:%s/'%DOWNLOADS) + def run(self): + print 'Uploading installers...' + for i in ('dmg', 'exe', 'tar.bz2'): + self.upload_installer(installer_name(i)) + + check_call('''ssh divok echo %s \\> %s/latest_version'''\ + %(__version__, DOWNLOADS), shell=True) -def stage_one(): - check_call('sudo rm -rf build src/calibre/plugins/*', shell=True) - os.mkdir('build') - shutil.rmtree('docs') - os.mkdir('docs') - check_call('python setup.py build_ext build', shell=True) - check_call('sudo python setup.py develop', shell=True) - tag_release() - upload_demo() - -def stage_two(): - subprocess.check_call('rm -rf dist/*', shell=True) - build_installers() - -def stage_three(): - print 'Uploading installers...' - upload_installers() - print 'Uploading documentation...' - #upload_docs() - upload_user_manual() - print 'Uploading to PyPI...' - check_call('rm -f dist/*') - check_call('python setup.py register') - check_call('sudo rm -rf build src/calibre/plugins/*') - os.mkdir('build') - check_call('python2.5 setup.py build_ext bdist_egg --exclude-source-files upload') - check_call('sudo rm -rf build src/calibre/plugins/*') - os.mkdir('build') - check_call('python setup.py build_ext bdist_egg --exclude-source-files upload') - check_call('python setup.py sdist upload') - upload_src_tarball() - check_call('''rm -rf dist/* build/*''') - check_call('''ssh divok bzr update /var/www/calibre.kovidgoyal.net/calibre/''') - -def betas(): - subprocess.check_call('rm -f dist/*', shell=True) - build_installers() - check_call('ssh divok rm -f /var/www/calibre.kovidgoyal.net/htdocs/downloads/betas/*') - check_call('scp dist/* divok:/var/www/calibre.kovidgoyal.net/htdocs/downloads/betas/') - -def main(args=sys.argv): - print 'Starting stage one...' - stage_one() - print 'Starting stage two...' - stage_two() - print 'Starting stage three...' - stage_three() - print 'Finished' - return 0 - - -if __name__ == '__main__': - sys.exit(main()) +class upload_user_manual(OptionlessCommand): + + sub_commands = [('manual', None)] + + def run(self): + OptionlessCommand.run(self) + check_call(' '.join(['scp', '-r', 'src/calibre/manual/.build/html/*', + 'divok:%s'%USER_MANUAL]), shell=True) + +class upload_to_pypi(OptionlessCommand): + + def run(self): + check_call('python setup.py register') + check_call('rm -f dist/*', shell=True) + check_call('sudo rm -rf build src/calibre/plugins/*', shell=True) + os.mkdir('build') + check_call('python2.5 setup.py build_ext bdist_egg --exclude-source-files upload'.split()) + check_call('sudo rm -rf build src/calibre/plugins/*', shell=True) + os.mkdir('build') + check_call('python setup.py build_ext bdist_egg --exclude-source-files upload'.split()) + check_call('python setup.py sdist upload'.split()) + +class stage3(OptionlessCommand): + + sub_commands = [ + ('upload_installers', None), + ('upload_user_manual', None), + ('upload_to_pypi', None), + ] + + def run(self): + OptionlessCommand.run(self) + check_call('ssh divok rm -f %s/calibre-\*.tar.gz'%DOWNLOADS, shell=True) + check_call('scp dist/calibre-*.tar.gz divok:%s/'%DOWNLOADS, shell=True) + check_call('''rm -rf dist/* build/*''', shell=True) + check_call('ssh divok bzr update /var/www/calibre.kovidgoyal.net/calibre/', + shell=True) + +class stage2(OptionlessCommand): + + sub_commands = [ + ('build_linux', None), + ('build_windows', None), + ('build_osx', None), + ('upload_installers', None), + ] + + def run(self): + check_call('rm -rf dist/*', shell=True) + OptionlessCommand.run(self) + +class stage1(OptionlessCommand): + + sub_commands = [ + ('update', None), + ('tag_release', None), + ('upload_demo', None), + ] + +class upload(OptionlessCommand): + + sub_commands = [ + ('stage1', None), + ('stage2', None), + ('stage3', None) + ] \ No newline at end of file