mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement linux binary installer
This commit is contained in:
parent
f77c2ade9d
commit
075ada2488
5
Makefile
5
Makefile
@ -27,14 +27,13 @@ pictureflow :
|
||||
mkdir -p src/calibre/plugins && rm -f src/calibre/plugins/*pictureflow* && \
|
||||
cd src/calibre/gui2/pictureflow && rm -f *.o && \
|
||||
mkdir -p .build && cd .build && rm -f * && \
|
||||
qmake ../pictureflow.pro && make && \
|
||||
qmake ../pictureflow.pro && make staticlib && \
|
||||
cd ../PyQt && \
|
||||
mkdir -p .build && \
|
||||
cd .build && rm -f * && \
|
||||
python ../configure.py && make && \
|
||||
cd ../../../../../.. && \
|
||||
cp src/calibre/gui2/pictureflow/.build/libpictureflow.so.?.?.? src/calibre/gui2/pictureflow/PyQt/.build/pictureflow.so src/calibre/plugins/ && \
|
||||
python -c "import os, glob; lp = glob.glob('src/calibre/plugins/libpictureflow.so.*')[0]; os.rename(lp, lp[:-4])" && \
|
||||
cp src/calibre/gui2/pictureflow/PyQt/.build/pictureflow.so src/calibre/plugins/ && \
|
||||
rm -rf src/calibre/gui2/pictureflow/.build rm -rf src/calibre/gui2/pictureflow/PyQt/.build
|
||||
|
||||
|
||||
|
@ -549,16 +549,12 @@ def strftime(fmt, t=time.localtime()):
|
||||
return unicode(result, locale.getpreferredencoding(), 'replace')
|
||||
except:
|
||||
return unicode(result, 'utf-8', 'replace')
|
||||
|
||||
if islinux:
|
||||
|
||||
if islinux and not getattr(sys, 'frozen', False):
|
||||
import pkg_resources
|
||||
if not os.environ.has_key('LD_LIBRARY_PATH'):
|
||||
os.environ['LD_LIBRARY_PATH'] = ''
|
||||
plugins = pkg_resources.resource_filename(__appname__, 'plugins')
|
||||
os.environ['LD_LIBRARY_PATH'] = plugins + ':' + os.environ['LD_LIBRARY_PATH']
|
||||
sys.path.insert(1, plugins)
|
||||
cwd = os.getcwd()
|
||||
os.chdir(plugins)
|
||||
|
||||
if iswindows and hasattr(sys, 'frozen'):
|
||||
sys.path.insert(1, os.path.dirname(sys.executable))
|
||||
|
||||
@ -569,8 +565,6 @@ except Exception, err:
|
||||
pictureflow = None
|
||||
pictureflowerror = str(err)
|
||||
|
||||
if islinux:
|
||||
os.chdir(cwd)
|
||||
|
||||
def entity_to_unicode(match, exceptions=[], encoding='cp1252'):
|
||||
'''
|
||||
|
@ -109,7 +109,7 @@ class PRS505(Device):
|
||||
devname = re.search(r'BSD Name.*=\s+"(\S+)"', src).group(1)
|
||||
self._main_prefix = re.search('/dev/%s(\w*)\s+on\s+([^\(]+)\s+'%(devname,), mount).group(2) + os.sep
|
||||
except:
|
||||
raise DeviceError('Unable to find %s. Is it connected?'%(self.__class__.__name__,))
|
||||
raise DeviceError(_('Unable to detect the %s disk drive. Try rebooting.')%self.__class__.__name__)
|
||||
try:
|
||||
src = subprocess.Popen('ioreg -n "%s"'%(self.OSX_SD_NAME,),
|
||||
shell=True, stdout=subprocess.PIPE).stdout.read()
|
||||
@ -143,7 +143,7 @@ class PRS505(Device):
|
||||
|
||||
|
||||
if not drives:
|
||||
raise DeviceError('Unable to find %s. Is it connected?'%(self.__class__.__name__,))
|
||||
raise DeviceError(_('Unable to detect the %s disk drive. Try rebooting.')%self.__class__.__name__)
|
||||
|
||||
drives.sort(cmp=lambda a, b: cmp(a[0], b[0]))
|
||||
self._main_prefix = drives[0][1]
|
||||
@ -171,7 +171,7 @@ class PRS505(Device):
|
||||
|
||||
mm = hm.FindDeviceStringMatch(__appname__+'.mainvolume', self.__class__.__name__)
|
||||
if not mm:
|
||||
raise DeviceError('Unable to find %s. Is it connected?'%(self.__class__.__name__,))
|
||||
raise DeviceError(_('Unable to detect the %s disk drive. Try rebooting.')%(self.__class__.__name__,))
|
||||
self._main_prefix = None
|
||||
for dev in mm:
|
||||
try:
|
||||
|
@ -13,6 +13,8 @@ from calibre import sanitize_file_name
|
||||
|
||||
import sys, os, time
|
||||
|
||||
import parser
|
||||
|
||||
def option_parser():
|
||||
parser = feeds_option_parser()
|
||||
parser.remove_option('--output-dir')
|
||||
|
@ -5,7 +5,7 @@ import sys, logging, os
|
||||
from calibre import setup_cli_handlers, OptionParser
|
||||
from calibre.ebooks import ConversionError
|
||||
from calibre.ebooks.lrf.meta import get_metadata
|
||||
from calibre.ebooks.lrf.parser import LRFDocument
|
||||
from calibre.ebooks.lrf.lrfparser import LRFDocument
|
||||
from calibre.ebooks.metadata.opf import OPFCreator
|
||||
|
||||
from calibre.ebooks.lrf.objects import PageAttr, BlockAttr, TextAttr
|
||||
|
@ -8,11 +8,13 @@ from calibre.ebooks.lrf import option_parser as lrf_option_parser
|
||||
from calibre.ebooks import ConversionError
|
||||
from calibre.ebooks.lrf.html.convert_from import process_file as html_process_file
|
||||
from calibre.ebooks.metadata.opf import OPFReader
|
||||
from calibre import isosx, __appname__, setup_cli_handlers, iswindows
|
||||
from calibre import isosx, __appname__, setup_cli_handlers, iswindows, islinux
|
||||
|
||||
CLIT = 'clit'
|
||||
if isosx and hasattr(sys, 'frameworks_dir'):
|
||||
CLIT = os.path.join(getattr(sys, 'frameworks_dir'), CLIT)
|
||||
if islinux and getattr(sys, 'frozen_path', False):
|
||||
CLIT = os.path.join(getattr(sys, 'frozen_path'), 'clit')
|
||||
|
||||
def option_parser():
|
||||
return lrf_option_parser(
|
||||
|
@ -4,7 +4,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import sys, os, subprocess, logging
|
||||
from functools import partial
|
||||
from calibre import isosx, setup_cli_handlers, filename_to_utf8, iswindows
|
||||
from calibre import isosx, setup_cli_handlers, filename_to_utf8, iswindows, islinux
|
||||
from calibre.ebooks import ConversionError
|
||||
from calibre.ptempfile import PersistentTemporaryDirectory
|
||||
from calibre.ebooks.lrf import option_parser as lrf_option_parser
|
||||
@ -15,9 +15,10 @@ popen = subprocess.Popen
|
||||
if isosx and hasattr(sys, 'frameworks_dir'):
|
||||
PDFTOHTML = os.path.join(getattr(sys, 'frameworks_dir'), PDFTOHTML)
|
||||
if iswindows and hasattr(sys, 'frozen'):
|
||||
PDFTOHTML = os.path.join(os.path.dirname(sys.executable), 'pdftohtml.exe')
|
||||
popen = partial(subprocess.Popen, creationflags=0x08) # CREATE_NO_WINDOW=0x08 so that no ugly console is popped up
|
||||
|
||||
PDFTOHTML = os.path.join(os.path.dirname(sys.executable), 'pdftohtml.exe')
|
||||
popen = partial(subprocess.Popen, creationflags=0x08) # CREATE_NO_WINDOW=0x08 so that no ugly console is popped up
|
||||
if islinux and getattr(sys, 'frozen_path', False):
|
||||
PDFTOHTML = os.path.join(getattr(sys, 'frozen_path'), 'pdftohtml')
|
||||
|
||||
def generate_html(pathtopdf, logger):
|
||||
'''
|
||||
@ -93,4 +94,4 @@ def main(args=sys.argv, logger=None):
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
sys.exit(main())
|
||||
|
@ -8,7 +8,7 @@ from PyQt4.QtCore import Qt, QObject, SIGNAL, QCoreApplication, QThread, \
|
||||
QVariant
|
||||
|
||||
from calibre import __appname__, __version__, __author__, setup_cli_handlers, islinux, Settings
|
||||
from calibre.ebooks.lrf.parser import LRFDocument
|
||||
from calibre.ebooks.lrf.lrfparser import LRFDocument
|
||||
|
||||
from calibre.gui2 import ORG_NAME, APP_UID, error_dialog, choose_files, Application
|
||||
from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog
|
||||
|
@ -107,7 +107,6 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
QObject.connect(self.job_manager, SIGNAL('job_done(int)'), self.status_bar.job_done,
|
||||
Qt.QueuedConnection)
|
||||
QObject.connect(self.status_bar, SIGNAL('show_book_info()'), self.show_book_info)
|
||||
|
||||
####################### Setup Toolbar #####################
|
||||
sm = QMenu()
|
||||
sm.addAction(QIcon(':/images/reader.svg'), _('Send to main memory'))
|
||||
@ -198,7 +197,6 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
self.library_view.resizeColumnsToContents()
|
||||
self.library_view.resizeRowsToContents()
|
||||
self.search.setFocus(Qt.OtherFocusReason)
|
||||
|
||||
########################### Cover Flow ################################
|
||||
self.cover_flow = None
|
||||
if CoverFlow is not None:
|
||||
@ -219,7 +217,6 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
|
||||
self.setMaximumHeight(available_height())
|
||||
|
||||
|
||||
####################### Setup device detection ########################
|
||||
self.detector = DeviceDetector(sleep_time=2000)
|
||||
QObject.connect(self.detector, SIGNAL('connected(PyQt_PyObject, PyQt_PyObject)'),
|
||||
@ -1161,7 +1158,7 @@ def main(args=sys.argv):
|
||||
pid = os.fork() if islinux else -1
|
||||
if pid <= 0:
|
||||
app = Application(args)
|
||||
app.setWindowIcon(QIcon(':/library'))
|
||||
app.setWindowIcon(QIcon(':/library'))
|
||||
QCoreApplication.setOrganizationName(ORG_NAME)
|
||||
QCoreApplication.setApplicationName(APP_UID)
|
||||
single_instance = None if SingleApplication is None else SingleApplication('calibre GUI')
|
||||
@ -1170,19 +1167,19 @@ def main(args=sys.argv):
|
||||
single_instance.send_message('launched:'+repr(sys.argv)):
|
||||
return 0
|
||||
|
||||
QMessageBox.critical(None, 'Cannot Start '+__appname__,
|
||||
QMessageBox.critical(None, 'Cannot Start '+__appname__,
|
||||
'<p>%s is already running.</p>'%__appname__)
|
||||
return 1
|
||||
initialize_file_icon_provider()
|
||||
try:
|
||||
main = Main(single_instance)
|
||||
except DatabaseLocked, err:
|
||||
QMessageBox.critical(None, 'Cannot Start '+__appname__,
|
||||
QMessageBox.critical(None, 'Cannot Start '+__appname__,
|
||||
'<p>Another program is using the database. <br/>Perhaps %s is already running?<br/>If not try deleting the file %s'%(__appname__, err.lock_file_path))
|
||||
return 1
|
||||
sys.excepthook = main.unhandled_exception
|
||||
if len(sys.argv) > 1:
|
||||
main.add_filesystem_book(sys.argv[1])
|
||||
main.add_filesystem_book(sys.argv[1])
|
||||
return app.exec_()
|
||||
return 0
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
import os, sys
|
||||
import sipconfig
|
||||
if os.environ.get('PYQT4PATH', None):
|
||||
print os.environ['PYQT4PATH']
|
||||
sys.path.insert(0, os.environ['PYQT4PATH'])
|
||||
from PyQt4 import pyqtconfig
|
||||
|
||||
# The name of the SIP build file generated by SIP and used by the build
|
||||
|
@ -39,7 +39,7 @@ entry_points = {
|
||||
'fb22lrf = calibre.ebooks.lrf.fb2.convert_from:main',
|
||||
'fb2-meta = calibre.ebooks.metadata.fb2:main',
|
||||
'any2lrf = calibre.ebooks.lrf.any.convert_from:main',
|
||||
'lrf2lrs = calibre.ebooks.lrf.parser:main',
|
||||
'lrf2lrs = calibre.ebooks.lrf.lrfparser:main',
|
||||
'lrs2lrf = calibre.ebooks.lrf.lrs.convert_from:main',
|
||||
'pdfreflow = calibre.ebooks.lrf.pdf.reflow:main',
|
||||
'isbndb = calibre.ebooks.metadata.isbndb:main',
|
||||
@ -154,7 +154,7 @@ def setup_completion(fatal_errors):
|
||||
from calibre.ebooks.lrf.html.convert_from import option_parser as htmlop
|
||||
from calibre.ebooks.lrf.txt.convert_from import option_parser as txtop
|
||||
from calibre.ebooks.lrf.meta import option_parser as metaop
|
||||
from calibre.ebooks.lrf.parser import option_parser as lrf2lrsop
|
||||
from calibre.ebooks.lrf.lrfparser import option_parser as lrf2lrsop
|
||||
from calibre.gui2.lrf_renderer.main import option_parser as lrfviewerop
|
||||
from calibre.ebooks.lrf.pdf.reflow import option_parser as pdfhtmlop
|
||||
from calibre.ebooks.mobi.reader import option_parser as mobioeb
|
||||
@ -313,14 +313,17 @@ def setup_udev_rules(group_file, reload, fatal_errors):
|
||||
call(('/etc/rc.d/rc.hald', 'restart'))
|
||||
|
||||
try:
|
||||
check_call('udevcontrol reload_rules', shell=True)
|
||||
check_call('udevadm control --reload_rules', shell=True)
|
||||
except:
|
||||
try:
|
||||
check_call('/etc/init.d/udev reload', shell=True)
|
||||
check_call('udevcontrol reload_rules', shell=True)
|
||||
except:
|
||||
if fatal_errors:
|
||||
raise Exception("Couldn't reload udev, you may have to reboot")
|
||||
print >>sys.stderr, "Couldn't reload udev, you may have to reboot"
|
||||
try:
|
||||
check_call('/etc/init.d/udev reload', shell=True)
|
||||
except:
|
||||
if fatal_errors:
|
||||
raise Exception("Couldn't reload udev, you may have to reboot")
|
||||
print >>sys.stderr, "Couldn't reload udev, you may have to reboot"
|
||||
|
||||
def option_parser():
|
||||
from optparse import OptionParser
|
||||
@ -428,6 +431,19 @@ MIME = '''\
|
||||
</mime-info>
|
||||
'''
|
||||
|
||||
def render_svg(image, dest):
|
||||
from PyQt4.QtGui import QPainter, QImage
|
||||
from PyQt4.QtSvg import QSvgRenderer
|
||||
svg = QSvgRenderer(image.readAll())
|
||||
painter = QPainter()
|
||||
image = QImage(128,128,QImage.Format_ARGB32_Premultiplied)
|
||||
painter.begin(image)
|
||||
painter.setRenderHints(QPainter.Antialiasing|QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform|QPainter.HighQualityAntialiasing)
|
||||
painter.setCompositionMode(QPainter.CompositionMode_SourceOver)
|
||||
svg.render(painter)
|
||||
painter.end()
|
||||
image.save(dest)
|
||||
|
||||
def setup_desktop_integration(fatal_errors):
|
||||
try:
|
||||
from PyQt4.QtCore import QFile
|
||||
@ -438,25 +454,16 @@ def setup_desktop_integration(fatal_errors):
|
||||
|
||||
|
||||
tdir = mkdtemp()
|
||||
rsvg = 'rsvg --dpi-x 600 --dpi-y 600 -w 128 -h 128 -f png '
|
||||
cwd = os.getcwdu()
|
||||
try:
|
||||
os.chdir(tdir)
|
||||
if QFile(':/images/mimetypes/lrf.svg').copy(os.path.join(tdir, 'calibre-lrf.svg')):
|
||||
check_call(rsvg + 'calibre-lrf.svg calibre-lrf.png', shell=True)
|
||||
check_call('xdg-icon-resource install --context mimetypes --size 128 calibre-lrf.png application-lrf', shell=True)
|
||||
check_call('xdg-icon-resource install --context mimetypes --size 128 calibre-lrf.png text-lrs', shell=True)
|
||||
else:
|
||||
raise Exception('Could not create LRF mimetype icon')
|
||||
if QFile(':library').copy(os.path.join(tdir, 'calibre-gui.png')):
|
||||
check_call('xdg-icon-resource install --size 128 calibre-gui.png calibre-gui', shell=True)
|
||||
else:
|
||||
raise Exception('Could not creaet GUI icon')
|
||||
if QFile(':/images/viewer.svg').copy(os.path.join(tdir, 'calibre-viewer.svg')):
|
||||
check_call(rsvg + 'calibre-viewer.svg calibre-viewer.png', shell=True)
|
||||
check_call('xdg-icon-resource install --size 128 calibre-viewer.png calibre-viewer', shell=True)
|
||||
else:
|
||||
raise Exception('Could not creaet viewer icon')
|
||||
render_svg(QFile(':/images/mimetypes/lrf.svg'), os.path.join(tdir, 'calibre-lrf.png'))
|
||||
check_call('xdg-icon-resource install --context mimetypes --size 128 calibre-lrf.png application-lrf', shell=True)
|
||||
check_call('xdg-icon-resource install --context mimetypes --size 128 calibre-lrf.png text-lrs', shell=True)
|
||||
QFile(':library').copy(os.path.join(tdir, 'calibre-gui.png'))
|
||||
check_call('xdg-icon-resource install --size 128 calibre-gui.png calibre-gui', shell=True)
|
||||
render_svg(QFile(':/images/viewer.svg'), os.path.join(tdir, 'calibre-viewer.png'))
|
||||
check_call('xdg-icon-resource install --size 128 calibre-viewer.png calibre-viewer', shell=True)
|
||||
|
||||
f = open('calibre-lrfviewer.desktop', 'wb')
|
||||
f.write(VIEWER)
|
||||
|
270
src/calibre/linux_installer.py
Normal file
270
src/calibre/linux_installer.py
Normal file
@ -0,0 +1,270 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
Download and install the linux binary.
|
||||
'''
|
||||
import sys, os, shutil, tarfile, subprocess, tempfile, urllib2, re, stat
|
||||
|
||||
class TerminalController:
|
||||
"""
|
||||
A class that can be used to portably generate formatted output to
|
||||
a terminal.
|
||||
|
||||
`TerminalController` defines a set of instance variables whose
|
||||
values are initialized to the control sequence necessary to
|
||||
perform a given action. These can be simply included in normal
|
||||
output to the terminal:
|
||||
|
||||
>>> term = TerminalController()
|
||||
>>> print 'This is '+term.GREEN+'green'+term.NORMAL
|
||||
|
||||
Alternatively, the `render()` method can used, which replaces
|
||||
'${action}' with the string required to perform 'action':
|
||||
|
||||
>>> term = TerminalController()
|
||||
>>> print term.render('This is ${GREEN}green${NORMAL}')
|
||||
|
||||
If the terminal doesn't support a given action, then the value of
|
||||
the corresponding instance variable will be set to ''. As a
|
||||
result, the above code will still work on terminals that do not
|
||||
support color, except that their output will not be colored.
|
||||
Also, this means that you can test whether the terminal supports a
|
||||
given action by simply testing the truth value of the
|
||||
corresponding instance variable:
|
||||
|
||||
>>> term = TerminalController()
|
||||
>>> if term.CLEAR_SCREEN:
|
||||
... print 'This terminal supports clearning the screen.'
|
||||
|
||||
Finally, if the width and height of the terminal are known, then
|
||||
they will be stored in the `COLS` and `LINES` attributes.
|
||||
"""
|
||||
# Cursor movement:
|
||||
BOL = '' #: Move the cursor to the beginning of the line
|
||||
UP = '' #: Move the cursor up one line
|
||||
DOWN = '' #: Move the cursor down one line
|
||||
LEFT = '' #: Move the cursor left one char
|
||||
RIGHT = '' #: Move the cursor right one char
|
||||
|
||||
# Deletion:
|
||||
CLEAR_SCREEN = '' #: Clear the screen and move to home position
|
||||
CLEAR_EOL = '' #: Clear to the end of the line.
|
||||
CLEAR_BOL = '' #: Clear to the beginning of the line.
|
||||
CLEAR_EOS = '' #: Clear to the end of the screen
|
||||
|
||||
# Output modes:
|
||||
BOLD = '' #: Turn on bold mode
|
||||
BLINK = '' #: Turn on blink mode
|
||||
DIM = '' #: Turn on half-bright mode
|
||||
REVERSE = '' #: Turn on reverse-video mode
|
||||
NORMAL = '' #: Turn off all modes
|
||||
|
||||
# Cursor display:
|
||||
HIDE_CURSOR = '' #: Make the cursor invisible
|
||||
SHOW_CURSOR = '' #: Make the cursor visible
|
||||
|
||||
# Terminal size:
|
||||
COLS = None #: Width of the terminal (None for unknown)
|
||||
LINES = None #: Height of the terminal (None for unknown)
|
||||
|
||||
# Foreground colors:
|
||||
BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = ''
|
||||
|
||||
# Background colors:
|
||||
BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = ''
|
||||
BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = ''
|
||||
|
||||
_STRING_CAPABILITIES = """
|
||||
BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1
|
||||
CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold
|
||||
BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0
|
||||
HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split()
|
||||
_COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split()
|
||||
_ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split()
|
||||
|
||||
def __init__(self, term_stream=sys.stdout):
|
||||
"""
|
||||
Create a `TerminalController` and initialize its attributes
|
||||
with appropriate values for the current terminal.
|
||||
`term_stream` is the stream that will be used for terminal
|
||||
output; if this stream is not a tty, then the terminal is
|
||||
assumed to be a dumb terminal (i.e., have no capabilities).
|
||||
"""
|
||||
# Curses isn't available on all platforms
|
||||
try: import curses
|
||||
except: return
|
||||
|
||||
# If the stream isn't a tty, then assume it has no capabilities.
|
||||
if not hasattr(term_stream, 'isatty') or not term_stream.isatty(): return
|
||||
|
||||
# Check the terminal type. If we fail, then assume that the
|
||||
# terminal has no capabilities.
|
||||
try: curses.setupterm()
|
||||
except: return
|
||||
|
||||
# Look up numeric capabilities.
|
||||
self.COLS = curses.tigetnum('cols')
|
||||
self.LINES = curses.tigetnum('lines')
|
||||
|
||||
# Look up string capabilities.
|
||||
for capability in self._STRING_CAPABILITIES:
|
||||
(attrib, cap_name) = capability.split('=')
|
||||
setattr(self, attrib, self._tigetstr(cap_name) or '')
|
||||
|
||||
# Colors
|
||||
set_fg = self._tigetstr('setf')
|
||||
if set_fg:
|
||||
for i,color in zip(range(len(self._COLORS)), self._COLORS):
|
||||
setattr(self, color, curses.tparm(set_fg, i) or '')
|
||||
set_fg_ansi = self._tigetstr('setaf')
|
||||
if set_fg_ansi:
|
||||
for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
|
||||
setattr(self, color, curses.tparm(set_fg_ansi, i) or '')
|
||||
set_bg = self._tigetstr('setb')
|
||||
if set_bg:
|
||||
for i,color in zip(range(len(self._COLORS)), self._COLORS):
|
||||
setattr(self, 'BG_'+color, curses.tparm(set_bg, i) or '')
|
||||
set_bg_ansi = self._tigetstr('setab')
|
||||
if set_bg_ansi:
|
||||
for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
|
||||
setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '')
|
||||
|
||||
def _tigetstr(self, cap_name):
|
||||
# String capabilities can include "delays" of the form "$<2>".
|
||||
# For any modern terminal, we should be able to just ignore
|
||||
# these, so strip them out.
|
||||
import curses
|
||||
cap = curses.tigetstr(cap_name) or ''
|
||||
return re.sub(r'\$<\d+>[/*]?', '', cap)
|
||||
|
||||
def render(self, template):
|
||||
"""
|
||||
Replace each $-substitutions in the given template string with
|
||||
the corresponding terminal control string (if it's defined) or
|
||||
'' (if it's not).
|
||||
"""
|
||||
return re.sub(r'\$\$|\${\w+}', self._render_sub, template)
|
||||
|
||||
def _render_sub(self, match):
|
||||
s = match.group()
|
||||
if s == '$$': return s
|
||||
else: return getattr(self, s[2:-1])
|
||||
|
||||
#######################################################################
|
||||
# Example use case: progress bar
|
||||
#######################################################################
|
||||
|
||||
class ProgressBar:
|
||||
"""
|
||||
A 3-line progress bar, which looks like::
|
||||
|
||||
Header
|
||||
20% [===========----------------------------------]
|
||||
progress message
|
||||
|
||||
The progress bar is colored, if the terminal supports color
|
||||
output; and adjusts to the width of the terminal.
|
||||
"""
|
||||
BAR = '%3d%% ${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}\n'
|
||||
HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n'
|
||||
|
||||
def __init__(self, term, header):
|
||||
self.term = term
|
||||
if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL):
|
||||
raise ValueError("Terminal isn't capable enough -- you "
|
||||
"should use a simpler progress dispaly.")
|
||||
self.width = self.term.COLS or 75
|
||||
self.bar = term.render(self.BAR)
|
||||
self.header = self.term.render(self.HEADER % header.center(self.width))
|
||||
self.cleared = 1 #: true if we haven't drawn the bar yet.
|
||||
|
||||
def update(self, percent, message=''):
|
||||
if isinstance(message, unicode):
|
||||
message = message.encode('utf-8', 'ignore')
|
||||
if self.cleared:
|
||||
sys.stdout.write(self.header)
|
||||
self.cleared = 0
|
||||
n = int((self.width-10)*percent)
|
||||
msg = message.center(self.width)
|
||||
sys.stdout.write(
|
||||
self.term.BOL + self.term.UP + self.term.CLEAR_EOL +
|
||||
(self.bar % (100*percent, '='*n, '-'*(self.width-10-n))) +
|
||||
self.term.CLEAR_EOL + msg)
|
||||
sys.stdout.flush()
|
||||
|
||||
def clear(self):
|
||||
if not self.cleared:
|
||||
sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL +
|
||||
self.term.UP + self.term.CLEAR_EOL +
|
||||
self.term.UP + self.term.CLEAR_EOL)
|
||||
self.cleared = 1
|
||||
|
||||
|
||||
LAUNCHER='''\
|
||||
#!/bin/sh
|
||||
frozen_path=%s
|
||||
export ORIGWD=`pwd`
|
||||
export LD_LIBRARY_PATH=$frozen_path:$LD_LIBRARY_PATH
|
||||
cd $frozen_path
|
||||
./%s $*
|
||||
'''
|
||||
|
||||
def extract_tarball(tar, destdir):
|
||||
if hasattr(tar, 'read'):
|
||||
tarfile.open(fileobj=tar, mode='r').extractall(destdir)
|
||||
else:
|
||||
tarfile.open(tar, 'r').extractall(destdir)
|
||||
|
||||
def create_launchers(destdir, bindir='/usr/bin'):
|
||||
for launcher in open(os.path.join(destdir, 'manifest')).readlines():
|
||||
if 'postinstall' in launcher:
|
||||
continue
|
||||
launcher = launcher.strip()
|
||||
lp = os.path.join(bindir, launcher)
|
||||
print 'Creating', lp
|
||||
open(lp, 'wb').write(LAUNCHER%(destdir, launcher))
|
||||
os.chmod(lp, stat.S_IXUSR|stat.S_IXOTH|stat.S_IXGRP|stat.S_IREAD|stat.S_IWRITE)
|
||||
|
||||
def do_postinstall(destdir):
|
||||
cwd = os.getcwd()
|
||||
try:
|
||||
os.chdir(destdir)
|
||||
os.environ['LD_LIBRARY_PATH'] = destdir+':'+os.environ.get('LD_LIBRARY_PATH', '')
|
||||
subprocess.call((os.path.join(destdir, 'calibre_postinstall'),))
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
|
||||
def download_tarball():
|
||||
pb = ProgressBar(TerminalController(sys.stdout), 'Downloading calibre...')
|
||||
src = urllib2.urlopen('http://calibre.kovidgoyal.net/downloads/latest-linux-binary.tar.bz2')
|
||||
size = int(src.info()['content-length'])
|
||||
f = tempfile.NamedTemporaryFile()
|
||||
while f.tell() < size:
|
||||
f.write(src.read(4*1024))
|
||||
pb.update(f.tell()/float(size))
|
||||
f.seek(0)
|
||||
return f
|
||||
|
||||
def main(args=sys.argv):
|
||||
defdir = '/opt/calibre'
|
||||
destdir = raw_input('Enter the installation directory for calibre [%s]: '%defdir)
|
||||
if not destdir:
|
||||
destdir = defdir
|
||||
if os.path.exists(destdir):
|
||||
shutil.rmtree(destdir)
|
||||
os.makedirs(destdir)
|
||||
|
||||
f = download_tarball()
|
||||
|
||||
print 'Extracting...'
|
||||
extract_tarball(f, destdir)
|
||||
create_launchers(destdir)
|
||||
do_postinstall(destdir)
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
@ -11,7 +11,7 @@ from trac.util import Markup
|
||||
|
||||
__appname__ = 'calibre'
|
||||
DOWNLOAD_DIR = '/var/www/calibre.kovidgoyal.net/htdocs/downloads'
|
||||
|
||||
LINUX_INSTALLER = '/var/www/calibre.kovidgoyal.net/calibre/src/calibre/linux_installer.py'
|
||||
|
||||
|
||||
class OS(dict):
|
||||
@ -38,7 +38,6 @@ class Distribution(object):
|
||||
('dbus-python', '0.82.2', 'dbus-python', 'python-dbus', 'dbus-python'),
|
||||
('convertlit', '1.8', 'convertlit', None, None),
|
||||
('lxml', '1.3.3', 'lxml', 'python-lxml', 'python-lxml'),
|
||||
('librsvg', '2.0.0', 'librsvg', 'librsvg2-bin', 'librsvg2'),
|
||||
('genshi', '0.4.4', 'genshi', 'python-genshi', 'python-genshi'),
|
||||
('help2man', '1.36.4', 'help2man', 'help2man', 'help2man'),
|
||||
]
|
||||
@ -120,6 +119,8 @@ class Download(Component):
|
||||
add_stylesheet(req, 'dl/css/download.css')
|
||||
if req.path_info == '/download':
|
||||
return self.top_level(req)
|
||||
elif req.path_info == '/download_linux_binary_installer':
|
||||
req.send(open(LINUX_INSTALLER).read(), 'text/x-python')
|
||||
else:
|
||||
match = re.match(r'\/download_(\S+)', req.path_info)
|
||||
if match:
|
||||
|
137
upload.py
137
upload.py
@ -53,7 +53,9 @@ def build_installer(installer, vm, timeout=25):
|
||||
return os.path.basename(installer)
|
||||
|
||||
def installer_name(ext):
|
||||
return 'dist/%s-%s.%s'%(__appname__, __version__, ext)
|
||||
if ext in ('exe', 'dmg'):
|
||||
return 'dist/%s-%s.%s'%(__appname__, __version__, ext)
|
||||
return 'dist/%s-%s-i686.%s'%(__appname__, __version__, ext)
|
||||
|
||||
def build_windows():
|
||||
installer = installer_name('exe')
|
||||
@ -75,7 +77,134 @@ def build_osx():
|
||||
subprocess.Popen(('ssh', 'osx', 'sudo', '/sbin/shutdown', '-h', '+1'))
|
||||
return os.path.basename(installer)
|
||||
#return build_installer(installer, vm, 20)
|
||||
|
||||
def build_linux():
|
||||
cwd = os.getcwd()
|
||||
tbz2 = os.path.join(cwd, installer_name('tar.bz2'))
|
||||
SPEC="""\
|
||||
HOME = '%s'
|
||||
PYINSTALLER = HOME+'/build/pyinstaller'
|
||||
CALIBREPREFIX = HOME+'/work/calibre'
|
||||
CLIT = '/usr/bin/clit'
|
||||
PDFTOHTML = '/usr/bin/pdftohtml'
|
||||
LIBUNRAR = '/usr/lib/libunrar.so'
|
||||
QTDIR = '/usr/lib/qt4'
|
||||
QTDLLS = ('QtCore', 'QtGui', 'QtNetwork', 'QtSvg', 'QtXml')
|
||||
|
||||
import glob, sys, subprocess, tarfile
|
||||
CALIBRESRC = os.path.join(CALIBREPREFIX, 'src')
|
||||
CALIBREPLUGINS = os.path.join(CALIBRESRC, 'calibre', 'plugins')
|
||||
|
||||
subprocess.check_call(('/usr/bin/sudo', 'chown', '-R', 'kovid:users', glob.glob('/usr/lib/python*/site-packages/')[-1]))
|
||||
|
||||
loader = os.path.join('/tmp', 'calibre_installer_loader.py')
|
||||
if not os.path.exists(loader):
|
||||
open(loader, 'wb').write('''
|
||||
import sys, os
|
||||
sys.frozen_path = os.getcwd()
|
||||
os.chdir(os.environ.get("ORIGWD", "."))
|
||||
sys.path.insert(0, os.path.join(sys.frozen_path, "library.pyz"))
|
||||
sys.path.insert(0, sys.frozen_path)
|
||||
from PyQt4.QtCore import QCoreApplication
|
||||
QCoreApplication.setLibraryPaths([sys.frozen_path, os.path.join(sys.frozen_path, "plugins")])
|
||||
''')
|
||||
excludes = ['gtk._gtk', 'gtk.glade', 'qt', 'matplotlib.nxutils', 'matplotlib._cntr',
|
||||
'matplotlib.ttconv', 'matplotlib._image', 'matplotlib.ft2font',
|
||||
'matplotlib._transforms', 'matplotlib._agg', 'matplotlib.backends._backend_agg',
|
||||
'matplotlib.axes', 'matplotlib', 'matplotlib.pyparsing',
|
||||
'TKinter', 'atk', 'gobject._gobject', 'pango']
|
||||
temp = ['IPython.Extensions.ipy_profile_none']
|
||||
|
||||
recipes = ['calibre', 'web', 'feeds', 'recipes']
|
||||
prefix = '.'.join(recipes)+'.'
|
||||
for f in glob.glob(os.path.join(CALIBRESRC, *(recipes+['*.py']))):
|
||||
temp.append(prefix + os.path.basename(f).partition('.')[0])
|
||||
hook = '/tmp/hook-calibre.py'
|
||||
open(hook, 'wb').write('hiddenimports = %%s'%%repr(temp) + '\\n')
|
||||
|
||||
sys.path.insert(0, CALIBRESRC)
|
||||
from calibre.linux import entry_points
|
||||
|
||||
executables, scripts = ['calibre_postinstall'], [os.path.join(CALIBRESRC, 'calibre', 'linux.py')]
|
||||
|
||||
for entry in entry_points['console_scripts'] + entry_points['gui_scripts']:
|
||||
fields = entry.split('=')
|
||||
executables.append(fields[0].strip())
|
||||
scripts.append(os.path.join(CALIBRESRC, *map(lambda x: x.strip(), fields[1].split(':')[0].split('.')))+'.py')
|
||||
|
||||
recipes = Analysis(glob.glob(os.path.join(CALIBRESRC, 'calibre', 'web', 'feeds', 'recipes', '*.py')),
|
||||
pathex=[CALIBRESRC], hookspath=[os.path.dirname(hook)], excludes=excludes)
|
||||
analyses = [Analysis([os.path.join(HOMEPATH,'support/_mountzlib.py'), os.path.join(HOMEPATH,'support/useUnicode.py'), loader, script],
|
||||
pathex=[PYINSTALLER, CALIBRESRC, CALIBREPLUGINS], excludes=excludes) for script in scripts]
|
||||
|
||||
pyz = TOC()
|
||||
binaries = TOC()
|
||||
|
||||
for a in analyses:
|
||||
pyz = a.pure + pyz
|
||||
binaries = a.binaries + binaries
|
||||
pyz = PYZ(pyz + recipes.pure, name='library.pyz')
|
||||
|
||||
built_executables = []
|
||||
for script, exe, a in zip(scripts, executables, analyses):
|
||||
built_executables.append(EXE(PYZ(TOC()),
|
||||
a.scripts+[('O','','OPTION'),],
|
||||
exclude_binaries=1,
|
||||
name=os.path.join('buildcalibre', exe),
|
||||
debug=False,
|
||||
strip=True,
|
||||
upx=False,
|
||||
excludes=excludes,
|
||||
console=1))
|
||||
|
||||
print 'Adding plugins...'
|
||||
for f in glob.glob(os.path.join(CALIBREPLUGINS, '*.so')):
|
||||
binaries += [(os.path.basename(f), f, 'BINARY')]
|
||||
|
||||
print 'Adding external programs...'
|
||||
binaries += [('clit', CLIT, 'BINARY'), ('pdftohtml', PDFTOHTML, 'BINARY'),
|
||||
('libunrar.so', LIBUNRAR, 'BINARY')]
|
||||
qt = []
|
||||
for dll in QTDLLS:
|
||||
path = os.path.join(QTDIR, 'lib'+dll+'.so.4')
|
||||
qt.append((os.path.basename(path), path, 'BINARY'))
|
||||
binaries += qt
|
||||
|
||||
plugins = []
|
||||
plugdir = os.path.join(QTDIR, 'plugins')
|
||||
for dirpath, dirnames, filenames in os.walk(plugdir):
|
||||
for f in filenames:
|
||||
if not f.endswith('.so') or 'designer' in dirpath or 'codcs' in dirpath or 'sqldrivers' in dirpath : continue
|
||||
f = os.path.join(dirpath, f)
|
||||
plugins.append(('plugins/'+f.replace(plugdir, ''), f, 'BINARY'))
|
||||
binaries += plugins
|
||||
|
||||
manifest = '/tmp/manifest'
|
||||
open(manifest, 'wb').write('\\n'.join(executables))
|
||||
from calibre import __version__
|
||||
version = '/tmp/version'
|
||||
open(version, 'wb').write(__version__)
|
||||
coll = COLLECT(binaries, pyz, [('manifest', manifest, 'DATA'), ('version', version, 'DATA')],
|
||||
*built_executables,
|
||||
**dict(strip=True,
|
||||
upx=False,
|
||||
excludes=excludes,
|
||||
name='dist'))
|
||||
|
||||
print 'Building tarball...'
|
||||
tf = tarfile.open('%s', 'w:bz2')
|
||||
os.chdir(os.path.join(HOMEPATH, 'calibre', 'dist'))
|
||||
for f in os.listdir('.'):
|
||||
tf.add(f)
|
||||
|
||||
"""%('/home/kovid', tbz2)
|
||||
os.chdir(os.path.expanduser('~/build/pyinstaller'))
|
||||
open('calibre/calibre.spec', 'wb').write(SPEC)
|
||||
try:
|
||||
subprocess.check_call(('/usr/bin/python', '-O', 'Build.py', 'calibre/calibre.spec'))
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
return os.path.basename(tbz2)
|
||||
|
||||
def build_installers():
|
||||
return build_windows(), build_osx()
|
||||
@ -94,13 +223,17 @@ def upload_demo():
|
||||
check_call('''scp /tmp/txt-demo.zip divok:%s/'''%(DOWNLOADS,))
|
||||
|
||||
def upload_installers():
|
||||
exe, dmg = installer_name('exe'), installer_name('dmg')
|
||||
exe, dmg, tbz2 = installer_name('exe'), installer_name('dmg'), installer_name('tar.bz2')
|
||||
if exe and os.path.exists(exe):
|
||||
check_call('''ssh divok rm -f %s/calibre\*.exe'''%(DOWNLOADS,))
|
||||
check_call('''scp %s divok:%s/'''%(exe, DOWNLOADS))
|
||||
if dmg and os.path.exists(dmg):
|
||||
check_call('''ssh divok rm -f %s/calibre\*.dmg'''%(DOWNLOADS,))
|
||||
check_call('''scp %s divok:%s/'''%(dmg, DOWNLOADS))
|
||||
if tbz2 and os.path.exists(tbz2):
|
||||
check_call('''ssh divok rm -f %s/calibre-\*-i686.tar.bz2'''%(DOWNLOADS,))
|
||||
check_call('''scp %s divok:%s/'''%(tbz2, DOWNLOADS))
|
||||
check_call('''ssh divok ln -s %s/calibre-\*-i686.tar.bz2 %s/latest-linux-binary.tar.bz2'''%(DOWNLOADS,DOWNLOADS))
|
||||
check_call('''ssh divok chmod a+r %s/\*'''%(DOWNLOADS,))
|
||||
|
||||
def upload_docs():
|
||||
|
Loading…
x
Reference in New Issue
Block a user