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