Fix #3523 (Please re-add ./setup.py install --root option) and clean up install/post-install code. Post install now no longer runs in a separate process.

This commit is contained in:
Kovid Goyal 2009-09-27 10:20:36 -06:00
parent 0ff0ce51e8
commit ef06fbee81
16 changed files with 501 additions and 367 deletions

View File

@ -41,7 +41,14 @@ def clean_backups():
def main(args=sys.argv):
if len(args) == 1 or args[1] in ('-h', '--help'):
print 'Usage: python', args[0], 'command', '[options]'
print '\nWhere command is one of:', ', '.join(commands.__all__)
print '\nWhere command is one of:'
print
for x in sorted(commands.__all__):
print '%-20s -'%x,
c = getattr(commands, x)
desc = getattr(c, 'short_description', c.description)
print desc
print '\nTo get help on a particular command, run:'
print '\tpython', args[0], 'command -h'
return 1
@ -83,7 +90,7 @@ def main(args=sys.argv):
prints('There were', len(warnings), 'warning(s):')
print
for args, kwargs in warnings:
prints(*args, **kwargs)
prints('*', *args, **kwargs)
print
return 0

View File

@ -111,6 +111,7 @@ class Command(object):
self.b = os.path.basename
self.s = os.path.splitext
self.e = os.path.exists
self.orig_euid = os.geteuid()
self.real_uid = os.environ.get('SUDO_UID', None)
self.real_gid = os.environ.get('SUDO_GID', None)
self.real_user = os.environ.get('SUDO_USER', None)
@ -121,19 +122,19 @@ class Command(object):
if self.real_user is not None:
self.info('Dropping privileges to those of', self.real_user+':',
self.real_uid)
if self.real_gid is not None:
os.setegid(int(self.real_gid))
if self.real_uid is not None:
os.seteuid(int(self.real_uid))
#if self.real_gid is not None:
# os.setegid(int(self.real_gid))
def regain_privileges(self):
if not islinux or isosx:
return
if os.geteuid() != 0:
if os.geteuid() != 0 and self.orig_euid == 0:
self.info('Trying to get root privileges')
os.seteuid(0)
#if os.getegid() != 0:
# os.setegid(0)
if os.getegid() != 0:
os.setegid(0)
def pre_sub_commands(self, opts):
pass

View File

@ -37,6 +37,8 @@ def check_for_python_errors(filename, builtins):
class Check(Command):
description = 'Check for errors in the calibre source code'
BUILTINS = ['_', '__', 'dynamic_property', 'I', 'P']
CACHE = '.check-cache.pickle'

View File

@ -175,6 +175,8 @@ if iswindows:
class Build(Command):
short_description = 'Build calibre C/C++ extension modules'
description = textwrap.dedent('''\
calibre depends on several python extensions written in C/C++.
This command will compile them. You can influence the compile

View File

@ -6,7 +6,7 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import sys, os, textwrap, subprocess, shutil, tempfile, atexit
import sys, os, textwrap, subprocess, shutil, tempfile, atexit, stat
from setup import Command, islinux, basenames, modules, functions, \
__appname__, __version__
@ -19,28 +19,26 @@ This is the standard runscript for all of calibre's tools.
Do not modify it unless you know what you are doing.
"""
import sys
import sys, os
path = os.environ.get('CALIBRE_PYTHON_PATH', {path!r})
sys.path.insert(0, path)
sys.resources_location = os.environ.get('CALIBRE_RESOURCES_PATH', {resources!r})
sys.extensions_location = os.environ.get('CALIBRE_EXTENSIONS_PATH', {extensions!r})
'''
TEMPLATE = HEADER+'''
sys.path.insert(0, {path!r})
sys.resources_location = {resources!r}
sys.extensions_location = {extensions!r}
from {module} import {func!s}
sys.exit({func!s}())
'''
COMPLETE_TEMPLATE = HEADER+'''
import os
sys.path.insert(0, {path!r})
sys.path.insert(0, os.path.join({path!r}, 'calibre', 'utils'))
sys.path.insert(0, os.path.join(path, 'calibre', 'utils'))
import complete
sys.path = sys.path[1:]
sys.resources_location = {resources!r}
sys.extensions_location = {extensions!r}
sys.exit(complete.main())
'''
@ -53,93 +51,123 @@ class Develop(Command):
the prefix of your python installation. This can be controlled
via the --prefix option.
''')
short_description = 'Setup a development environment for calibre'
MODE = 0755
sub_commands = ['build', 'resources', 'gui']
def add_postinstall_options(self, parser):
parser.add_option('--make-errors-fatal', action='store_true', default=False,
dest='fatal_errors', help='If set die on post install errors.')
parser.add_option('--no-postinstall', action='store_false',
dest='postinstall', default=True,
help='Don\'t run post install actions like creating MAN pages, setting'+
' up desktop integration and so on')
def add_options(self, parser):
parser.add_option('--prefix',
help='Binaries will be installed in <prefix>/bin')
self.root = ''
self.add_postinstall_options(parser)
def consolidate_paths(self):
opts = self.opts
if not opts.prefix:
opts.prefix = sys.prefix
self.libdir = getattr(opts, 'libdir', None)
if self.libdir is None:
self.libdir = self.j(opts.prefix, 'lib')
self.bindir = getattr(opts, 'bindir', None)
if self.bindir is None:
self.bindir = self.j(opts.prefix, 'bin')
self.sharedir = getattr(opts, 'sharedir', None)
if self.sharedir is None:
self.sharedir = self.j(opts.prefix, 'share')
if not getattr(opts, 'staging_root', None):
opts.staging_root = opts.prefix
self.staging_libdir = getattr(opts, 'staging_libdir', None)
if self.staging_libdir is None:
self.staging_libdir = opts.staging_libdir = self.j(opts.staging_root, 'lib')
self.staging_bindir = getattr(opts, 'staging_bindir', None)
if self.staging_bindir is None:
self.staging_bindir = opts.staging_bindir = self.j(opts.staging_root, 'bin')
self.staging_sharedir = getattr(opts, 'staging_sharedir', None)
if self.staging_sharedir is None:
self.staging_sharedir = opts.staging_sharedir = self.j(opts.staging_root, 'share')
if self.__class__.__name__ == 'Develop':
self.libdir = self.SRC
self.sharedir = self.RESOURCES
def pre_sub_commands(self, opts):
if not islinux:
self.info('\nSetting up a development environment is only '
'supported on linux. On other platforms, install the calibre '
'binary and use the calibre-debug command.')
self.info('\nSetting up a source based development environment is only '
'supported on linux. On other platforms, see the User Manual'
' for help with setting up a development environment.')
raise SystemExit(1)
if not os.geteuid() == 0:
self.info('\nError: This command must be run as root.')
raise SystemExit(1)
self.drop_privileges()
if os.geteuid() == 0:
self.drop_privileges()
# Ensure any calibre config files are created as correct user
import calibre.utils.config as c
c
def run(self, opts):
self.opts = opts
self.regain_privileges()
self.find_locations(opts)
self.write_templates(opts)
self.consolidate_paths()
self.write_templates()
self.setup_mount_helper()
self.install_files(opts)
self.install_files()
self.run_postinstall()
self.success()
def setup_mount_helper(self):
def warn():
self.warn('Failed to compile mount helper. Auto mounting of',
'devices will not work')
' devices will not work')
if os.geteuid() != 0:
return warn()
import stat
return self.warn('Must be run as root to compile mount helper. Auto '
'mounting of devices will not work.')
src = os.path.join(self.SRC, 'calibre', 'devices', 'linux_mount_helper.c')
dest = self.root + os.path.join(self.bindir, 'calibre-mount-helper')
dest = os.path.join(self.staging_bindir, 'calibre-mount-helper')
self.info('Installing mount helper to '+ dest)
p = subprocess.Popen(['gcc', '-Wall', src, '-o', dest])
p = subprocess.Popen(['gcc', '-Wall', '-pedantic', src, '-o', dest])
ret = p.wait()
if ret != 0:
return warn()
os.chown(dest, 0, 0)
os.chmod(dest,
stat.S_ISUID|stat.S_ISGID|stat.S_IRUSR|stat.S_IWUSR|stat.S_IXUSR|stat.S_IXGRP|stat.S_IXOTH)
os.chmod(dest, stat.S_ISUID|stat.S_ISGID|stat.S_IRUSR|stat.S_IWUSR|\
stat.S_IXUSR|stat.S_IXGRP|stat.S_IXOTH)
return dest
def install_files(self, opts):
def install_files(self):
pass
def run_postinstall(self):
env = dict(**os.environ)
env['DESTDIR'] = self.prefix
subprocess.check_call(['calibre_postinstall', '--use-destdir'], env=env)
if self.opts.postinstall:
from calibre.linux import PostInstall
PostInstall(self.opts, info=self.info, warn=self.warn)
def success(self):
self.info('\nDevelopment environment successfully setup')
def find_locations(self, opts):
self.prefix = opts.prefix
if self.prefix is None:
self.prefix = sys.prefix
self.path = self.SRC
self.resources = self.j(self.d(self.SRC), 'resources')
self.extensions = self.j(self.SRC, 'calibre', 'plugins')
self.bindir = self.j(self.prefix, 'bin')
def write_templates(self, opts):
def write_templates(self):
for typ in ('console', 'gui'):
for name, mod, func in zip(basenames[typ], modules[typ],
functions[typ]):
self.write_template(opts, name, mod, func)
if islinux:
self.write_template(opts, 'calibre_postinstall', 'calibre.linux', 'main')
self.write_template(name, mod, func)
def write_template(self, opts, name, mod, func):
def write_template(self, name, mod, func):
template = COMPLETE_TEMPLATE if name == 'calibre-complete' else TEMPLATE
script = template.format(
module=mod, func=func,
path=self.path, resources=self.resources,
extensions=self.extensions)
path = self.root + self.j(self.bindir, name)
if not os.path.exists(self.bindir):
os.makedirs(self.bindir)
path=self.libdir, resources=self.sharedir,
extensions=self.j(self.libdir, 'calibre', 'plugins'))
path = self.j(self.staging_bindir, name)
if not os.path.exists(self.staging_bindir):
os.makedirs(self.staging_bindir)
self.info('Installing binary:', path)
open(path, 'wb').write(script)
os.chmod(path, self.MODE)
@ -154,49 +182,45 @@ class Install(Develop):
The default <prefix> is the prefix of your python installation.
''')
short_description = 'Install calibre from source'
sub_commands = ['build', 'gui']
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')
parser.add_option('--root', default='',
help='Use a different installation root (mainly for packaging)')
self.root = ''
parser.add_option('--prefix', help='Installation prefix.')
parser.add_option('--libdir',
help='Where to put calibre library files. Default is <prefix>/lib')
parser.add_option('--bindir',
help='Where to put the calibre binaries. Default is <prefix>/bin')
parser.add_option('--sharedir',
help='Where to put the calibre data files. Default is <prefix>/share')
parser.add_option('--staging-root', '--root', default=None,
help=('Use a different installation root (mainly for packaging).'
' The prefix option controls the paths written into '
'the launcher scripts. This option controls the prefix '
'to which the install will actually copy files. By default '
'it is set to the value of --prefix.'))
parser.add_option('--staging-libdir',
help='Where to put calibre library files. Default is <root>/lib')
parser.add_option('--staging-bindir',
help='Where to put the calibre binaries. Default is <root>/bin')
parser.add_option('--staging-sharedir',
help='Where to put the calibre data files. Default is <root>/share')
self.add_postinstall_options(parser)
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.prefix = opts.prefix
self.bindir = opts.bindir
self.path = opts.libdir
self.resources = opts.sharedir
self.extensions = self.j(self.path, 'calibre', 'plugins')
self.root = opts.root
def install_files(self, opts):
dest = self.root + self.path
def install_files(self):
dest = self.staging_libdir
if os.path.exists(dest):
shutil.rmtree(dest)
shutil.copytree(self.SRC, dest)
for x in ('calibre/manual', 'calibre/trac',
'calibre/ebooks/lrf/html/demo'):
x = self.j(dest, x)
if os.path.exists(dest):
shutil.rmtree(x)
for x in os.walk(dest):
for x in os.walk(self.SRC):
reldir = os.path.relpath(x[0], self.SRC)
destdir = os.path.join(dest, reldir)
for f in x[-1]:
if os.path.splitext(f)[1] in ('.c', '.cpp', '.h'):
os.remove(self.j(x[0], f))
dest = self.root + self.resources
if os.path.splitext(f)[1] in ('.py', '.so'):
if not os.path.exists(destdir):
os.makedirs(destdir)
shutil.copy2(self.j(x[0], f), destdir)
dest = self.staging_sharedir
if os.path.exists(dest):
shutil.rmtree(dest)
shutil.copytree(self.RESOURCES, dest)

View File

@ -13,6 +13,8 @@ from setup import Command, installer_name
class Linux32(VMInstaller):
description = 'Build 32bit linux binary installer'
INSTALLER_EXT = 'tar.bz2'
VM_NAME = 'gentoo32_build'
VM = '/vmware/bin/gentoo32_build'
@ -21,6 +23,8 @@ class Linux32(VMInstaller):
class Linux64(Command):
description = 'Build 64bit linux binary installer'
sub_commands = ['linux_freeze']
def run(self, opts):
@ -31,4 +35,6 @@ class Linux64(Command):
class Linux(Command):
description = 'Build linux binary installers'
sub_commands = ['linux64', 'linux32']

View File

@ -12,6 +12,8 @@ from setup.installer import VMInstaller
class OSX(Command):
description = 'Build OS X binary installers'
sub_commands = ['osx32']
def run(self, opts):
@ -20,6 +22,8 @@ class OSX(Command):
class OSX32(VMInstaller):
description = 'Build 32 bit OS X binary installer'
INSTALLER_EXT = 'dmg'
VM_NAME = 'tiger_build'
VM = '/vmware/bin/%s'%VM_NAME

View File

@ -21,6 +21,8 @@ info = warn = None
class OSX32_Freeze(Command):
description = 'Freeze OSX calibre installation'
def run(self, opts):
global info, warn
info, warn = self.info, self.warn

View File

@ -14,6 +14,8 @@ from setup.installer.windows import build_installer
class Win(Command):
description = 'Build windows binary installers'
sub_commands = ['win32']
def run(self, opts):
@ -22,6 +24,8 @@ class Win(Command):
class Win32(VMInstaller):
description = 'Build 32bit windows binary installer'
INSTALLER_EXT = 'exe'
VM_NAME = 'xp_build'
VM = '/vmware/bin/%s'%VM_NAME

View File

@ -52,6 +52,8 @@ info = warn = None
class Win32Freeze(Command):
description = 'Freeze windows calibre installation'
def run(self, opts):
global info, warn
info, warn = self.info, self.warn

View File

@ -24,6 +24,8 @@ def get_opts_from_parser(parser):
class Resources(Command):
description = 'Compile various needed calibre resources'
def get_recipes(self):
sdir = os.path.join('src', 'calibre', 'web', 'feeds', 'recipes')
resources= {}

View File

@ -196,6 +196,7 @@ class GetTranslations(Translations):
class ISO639(Command):
description = 'Compile translations for ISO 639 codes'
XML = '/usr/lib/python2.6/site-packages/pycountry/databases/iso639.xml'
def run(self, opts):

View File

@ -127,6 +127,8 @@ class UploadDemo(Command):
class UploadToServer(Command):
description = 'Upload miscellaneous data to calibre server'
def run(self, opts):
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)

View File

@ -179,8 +179,8 @@ def main(args=sys.argv):
elif opts.add_simple_plugin is not None:
add_simple_plugin(opts.add_simple_plugin)
elif opts.paths:
prints('CALIBRE_RESOURCES_LOCATION='+sys.resources_location)
prints('CALIBRE_EXTENSIONS_LOCATION='+sys.extensions_location)
prints('CALIBRE_RESOURCES_PATH='+sys.resources_location)
prints('CALIBRE_EXTENSIONS_PATH='+sys.extensions_location)
prints('CALIBRE_PYTHON_PATH='+os.pathsep.join(sys.path))
elif opts.pdfreflow:
from calibre.ebooks.pdf.reflow import option_parser as px, run

View File

@ -27,6 +27,7 @@ int get_root() {
int do_mount(char *dev, char *mp) {
char options[1000];
char marker[2000];
int errsv;
if (exists(dev) == 0) {
fprintf(stderr, "Specified device node does not exist\n");
return EXIT_FAILURE;
@ -55,19 +56,19 @@ int do_mount(char *dev, char *mp) {
return EXIT_FAILURE;
}
execlp("mount", "mount", "-t", "vfat", "-o", options, dev, mp, NULL);
int errsv = errno;
errsv = errno;
fprintf(stderr, "Failed to mount with error: %s\n", strerror(errsv));
return EXIT_FAILURE;
}
int do_eject(char *dev, char*mp) {
char marker[2000];
int status = EXIT_FAILURE, ret;
int status = EXIT_FAILURE, ret, pid, errsv, i, rmd;
if (get_root() != 0) {
fprintf(stderr, "Failed to elevate to root privileges\n");
return EXIT_FAILURE;
}
int pid = fork();
pid = fork();
if (pid == -1) {
fprintf(stderr, "Failed to fork\n");
return EXIT_FAILURE;
@ -78,11 +79,10 @@ int do_eject(char *dev, char*mp) {
return EXIT_FAILURE;
}
execlp("eject", "eject", "-s", dev, NULL);
int errsv = errno;
errsv = errno;
fprintf(stderr, "Failed to eject with error: %s\n", strerror(errsv));
return EXIT_FAILURE;
} else {
int i;
for (i =0; i < 7; i++) {
sleep(1);
ret = waitpid(pid, &status, WNOHANG);
@ -99,7 +99,7 @@ int do_eject(char *dev, char*mp) {
fprintf(stderr, "Failed to unlink marker: %s\n", strerror(errno));
return EXIT_FAILURE;
}
int rmd = rmdir(mp);
rmd = rmdir(mp);
if (rmd == -1) {
fprintf(stderr, "Failed to remove mount point: %s\n", strerror(errno));
return EXIT_FAILURE;

View File

@ -1,17 +1,13 @@
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
''' Post installation script for linux '''
import sys, os, shutil
import sys, os, shutil, cPickle, textwrap
from subprocess import check_call
from calibre import __version__, __appname__
from calibre.customize.ui import device_plugins
from calibre import __version__, __appname__, prints
DEVICES = device_plugins()
DESTDIR = ''
if os.environ.has_key('DESTDIR'):
DESTDIR = os.environ['DESTDIR']
entry_points = {
'console_scripts': [ \
@ -40,6 +36,335 @@ entry_points = {
],
}
UNINSTALL = '''\
#!{python}
euid = {euid}
import os
if os.geteuid() != euid:
print 'WARNING: uninstaller must be run as', euid, 'to remove all files'
for x in {manifest!r}:
if not os.path.exists(x): continue
try:
if os.path.isdir(x):
shutil.rmtree(x)
else:
os.unlink(x)
except Exception, e:
print 'Failed to delete', x
print '\t', e
'''
class PostInstall:
def task_failed(self, msg):
self.warn(msg, 'with error:')
import traceback
tb = '\n\t'.join(traceback.format_exc().splitlines())
self.info('\t'+tb)
print
def warning(self, *args, **kwargs):
print '\n'+'_'*20, 'WARNING','_'*20
prints(*args, **kwargs)
print '_'*50
self.warnings.append((args, kwargs))
sys.stdout.flush()
def __init__(self, opts, info=prints, warn=None, manifest=None):
self.opts = opts
self.info = info
self.warn = warn
self.warnings = []
if self.warn is None:
self.warn = self.warning
if not self.opts.staging_bindir:
self.opts.staging_bindir = os.path.join(self.opts.staging_root,
'bin')
if not self.opts.staging_sharedir:
self.opts.staging_sharedir = os.path.join(self.opts.staging_root,
'etc')
self.opts.staging_etc = '/etc' if self.opts.staging_root == '/usr' else \
os.path.join(self.opts.staging_root, 'etc')
scripts = cPickle.loads(P('scripts.pickle', data=True))
if getattr(sys, 'frozen_path', False):
self.info('Creating symlinks...')
for exe in scripts.keys():
dest = os.path.join(self.opts.staging_bindir, exe)
if os.path.exists(dest):
os.unlink(dest)
tgt = os.path.join(getattr(sys, 'frozen_path'), exe)
self.info('\tSymlinking %s to %s'%(tgt, dest))
os.symlink(tgt, dest)
if manifest is None:
manifest = [os.path.abspath(os.path.join(opts.staging_bindir, x)) for x in
scripts.keys()]
self.manifest = manifest
self.icon_resources = []
self.menu_resources = []
self.mime_resources = []
self.setup_completion()
self.setup_udev_rules()
self.install_man_pages()
self.setup_desktop_integration()
from calibre.utils.config import config_dir
if os.path.exists(config_dir):
os.chdir(config_dir)
for f in os.listdir('.'):
if os.stat(f).st_uid == 0:
os.rmdir(f) if os.path.isdir(f) else os.unlink(f)
if os.stat(config_dir).st_uid == 0:
os.rmdir(config_dir)
if warn is None and self.warnings:
self.info('There were %d warnings'%len(self.warnings))
for args, kwargs in self.warnings:
self.info('*', *args, **kwargs)
print
def setup_completion(self):
try:
self.info('Setting up bash completion...')
from calibre.ebooks.metadata.cli import option_parser as metaop, filetypes as meta_filetypes
from calibre.ebooks.lrf.lrfparser import option_parser as lrf2lrsop
from calibre.gui2.lrf_renderer.main import option_parser as lrfviewerop
from calibre.web.fetch.simple import option_parser as web2disk
from calibre.web.feeds.recipes import titles as feed_titles
from calibre.ebooks.metadata.fetch import option_parser as fem_op
from calibre.gui2.main import option_parser as guiop
from calibre.utils.smtp import option_parser as smtp_op
any_formats = ['epub', 'htm', 'html', 'xhtml', 'xhtm', 'rar', 'zip',
'txt', 'lit', 'rtf', 'pdf', 'prc', 'mobi', 'fb2', 'odt']
if os.path.exists(os.path.join(self.opts.staging_sharedir,
'bash-completion')):
f = os.path.join(self.opts.staging_sharedir,
'bash-completion', 'calibre')
else:
f = os.path.join(self.opts.staging_etc,
'bash_completion.d/calibre')
if not os.path.exists(os.path.dirname(f)):
os.makedirs(os.path.dirname(f))
self.manifest.append(f)
with open(f, 'wb') as f:
f.write('# calibre Bash Shell Completion\n')
f.write(opts_and_exts('calibre', guiop, any_formats))
f.write(opts_and_exts('lrf2lrs', lrf2lrsop, ['lrf']))
f.write(opts_and_exts('ebook-meta', metaop, list(meta_filetypes())))
f.write(opts_and_exts('lrfviewer', lrfviewerop, ['lrf']))
f.write(opts_and_words('web2disk', web2disk, feed_titles))
f.write(opts_and_words('fetch-ebook-metadata', fem_op, []))
f.write(opts_and_words('calibre-smtp', smtp_op, []))
f.write(textwrap.dedent('''
_ebook_device_ls()
{
local pattern search listing prefix
pattern="$1"
search="$1"
if [[ -n "{$pattern}" ]]; then
if [[ "${pattern:(-1)}" == "/" ]]; then
pattern=""
else
pattern="$(basename ${pattern} 2> /dev/null)"
search="$(dirname ${search} 2> /dev/null)"
fi
fi
if [[ "x${search}" == "x" || "x${search}" == "x." ]]; then
search="/"
fi
listing="$(ebook-device ls ${search} 2>/dev/null)"
prefix="${search}"
if [[ "x${prefix:(-1)}" != "x/" ]]; then
prefix="${prefix}/"
fi
echo $(compgen -P "${prefix}" -W "${listing}" "${pattern}")
}
_ebook_device()
{
local cur prev
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
COMPREPLY=()
case "${prev}" in
ls|rm|mkdir|touch|cat )
COMPREPLY=( $(_ebook_device_ls "${cur}") )
return 0
;;
cp )
if [[ ${cur} == prs500:* ]]; then
COMPREPLY=( $(_ebook_device_ls "${cur:7}") )
return 0
else
_filedir
return 0
fi
;;
prs500 )
COMPREPLY=( $(compgen -W "cp ls rm mkdir touch cat info books df" "${cur}") )
return 0
;;
* )
if [[ ${cur} == prs500:* ]]; then
COMPREPLY=( $(_ebook_device_ls "${cur:7}") )
return 0
else
if [[ ${prev} == prs500:* ]]; then
_filedir
return 0
else
COMPREPLY=( $(compgen -W "prs500:" "${cur}") )
return 0
fi
return 0
fi
;;
esac
}
complete -o nospace -F _ebook_device ebook-device
complete -o nospace -C calibre-complete ebook-convert
'''))
except TypeError, err:
if 'resolve_entities' in str(err):
print 'You need python-lxml >= 2.0.5 for calibre'
sys.exit(1)
raise
except:
if self.opts.fatal_errors:
raise
self.task_failed('Setting up completion failed')
def setup_udev_rules(self):
self.info('Trying to setup udev rules...')
try:
group_file = os.path.join(self.opts.staging_etc, 'group')
groups = open(group_file, 'rb').read()
group = 'plugdev' if 'plugdev' in groups else 'usb'
old_udev = '/etc/udev/rules.d/95-calibre.rules'
if os.path.exists(old_udev):
os.remove(old_udev)
if self.opts.staging_root == '/usr':
base = '/lib'
else:
base = os.path.join(self.opts.staging_root, 'lib')
base = os.path.join(base, 'udev', 'rules.d')
if not os.path.exists(base):
os.makedirs(base)
with open(os.path.join(base, '95-calibre.rules'), 'wb') as udev:
self.manifest.append(udev.name)
udev.write('''# Sony Reader PRS-500\n'''
'''BUS=="usb", SYSFS{idProduct}=="029b", SYSFS{idVendor}=="054c", MODE="660", GROUP="%s"\n'''%(group,)
)
except:
if self.opts.fatal_errors:
raise
self.task_failed('Setting up udev rules failed')
def install_man_pages(self):
try:
from calibre.utils.help2man import create_man_page
manpath = os.path.join(self.opts.staging_sharedir, 'man/man1')
if not os.path.exists(manpath):
os.makedirs(manpath)
self.info('Installing MAN pages...')
for src in entry_points['console_scripts']:
prog, right = src.split('=')
prog = prog.strip()
module = __import__(right.split(':')[0].strip(), fromlist=['a'])
parser = getattr(module, 'option_parser', None)
if parser is None:
continue
parser = parser()
raw = create_man_page(prog, parser)
manfile = os.path.join(manpath, prog+'.1'+__appname__+'.bz2')
self.info('\tInstalling MAN page for', prog)
open(manfile, 'wb').write(raw)
self.manifest.append(manfile)
except:
if self.opts.fatal_errors:
raise
self.task_failed('Installing MAN pages failed')
def setup_desktop_integration(self):
try:
from PyQt4.QtCore import QFile
from tempfile import mkdtemp
self.info('Setting up desktop integration...')
tdir = mkdtemp()
cwd = os.getcwdu()
try:
os.chdir(tdir)
render_svg(QFile(I('mimetypes/lrf.svg')), os.path.join(tdir, 'calibre-lrf.png'))
check_call('xdg-icon-resource install --noupdate --context mimetypes --size 128 calibre-lrf.png application-lrf', shell=True)
self.icon_resources.append(('mimetypes', 'application-lrf'))
check_call('xdg-icon-resource install --noupdate --context mimetypes --size 128 calibre-lrf.png text-lrs', shell=True)
self.icon_resources.append(('mimetypes', 'application-lrs'))
QFile(I('library.png')).copy(os.path.join(tdir, 'calibre-gui.png'))
check_call('xdg-icon-resource install --noupdate --size 128 calibre-gui.png calibre-gui', shell=True)
self.icon_resources.append(('apps', 'calibre-gui'))
render_svg(QFile(I('viewer.svg')), os.path.join(tdir, 'calibre-viewer.png'))
check_call('xdg-icon-resource install --size 128 calibre-viewer.png calibre-viewer', shell=True)
self.icon_resources.append(('apps', 'calibre-viewer'))
f = open('calibre-lrfviewer.desktop', 'wb')
f.write(VIEWER)
f.close()
f = open('calibre-ebook-viewer.desktop', 'wb')
f.write(EVIEWER)
f.close()
f = open('calibre-gui.desktop', 'wb')
f.write(GUI)
f.close()
des = ('calibre-gui.desktop', 'calibre-lrfviewer.desktop',
'calibre-ebook-viewer.desktop')
for x in des:
cmd = ['xdg-desktop-menu', 'install', './'+x]
if x != des[-1]:
cmd.insert(2, '--noupdate')
check_call(' '.join(cmd), shell=True)
self.menu_resources.append(x)
f = open('calibre-mimetypes', 'wb')
f.write(MIME)
f.close()
self.mime_resources.append('calibre-mimetypes')
check_call('xdg-mime install ./calibre-mimetypes', shell=True)
finally:
os.chdir(cwd)
shutil.rmtree(tdir)
except Exception, err:
if self.opts.fatal_errors:
raise
self.task_failed('Setting up desktop integration failed')
def option_parser():
from calibre.utils.config import OptionParser
parser = OptionParser()
parser.add_option('--make-errors-fatal', action='store_true', default=False,
dest='fatal_errors', help='If set die on errors.')
parser.add_option('--root', dest='staging_root', default='/usr',
help='Prefix under which to install files')
parser.add_option('--bindir', default=None, dest='staging_bindir',
help='Location where calibre launcher scripts were installed')
parser.add_option('--sharedir', default=None, dest='staging_sharedir',
help='Location where calibre resources were installed')
return parser
def options(option_parser):
parser = option_parser()
@ -121,194 +446,6 @@ def opts_and_exts(name, op, exts):
}
complete -o filenames -F _'''%(opts,exts) + name + ' ' + name +"\n\n"
use_destdir = False
def open_file(path, mode='wb'):
if use_destdir:
if os.path.isabs(path):
path = path[1:]
path = os.path.join(DESTDIR, path)
if not os.path.exists(os.path.dirname(path)):
os.makedirs(os.path.dirname(path))
return open(path, mode)
def setup_completion(fatal_errors):
manifest = []
try:
print 'Setting up bash completion...',
sys.stdout.flush()
from calibre.ebooks.metadata.cli import option_parser as metaop, filetypes as meta_filetypes
from calibre.ebooks.lrf.lrfparser import option_parser as lrf2lrsop
from calibre.gui2.lrf_renderer.main import option_parser as lrfviewerop
from calibre.web.fetch.simple import option_parser as web2disk
from calibre.web.feeds.recipes import titles as feed_titles
from calibre.ebooks.metadata.fetch import option_parser as fem_op
from calibre.gui2.main import option_parser as guiop
from calibre.utils.smtp import option_parser as smtp_op
any_formats = ['epub', 'htm', 'html', 'xhtml', 'xhtm', 'rar', 'zip',
'txt', 'lit', 'rtf', 'pdf', 'prc', 'mobi', 'fb2', 'odt']
if os.path.exists('/usr/share/bash-completion'):
f = open_file('/usr/share/bash-completion/calibre')
else:
f = open_file('/etc/bash_completion.d/calibre')
manifest.append(f.name)
f.write('# calibre Bash Shell Completion\n')
f.write(opts_and_exts('calibre', guiop, any_formats))
f.write(opts_and_exts('lrf2lrs', lrf2lrsop, ['lrf']))
f.write(opts_and_exts('ebook-meta', metaop, list(meta_filetypes())))
f.write(opts_and_exts('lrfviewer', lrfviewerop, ['lrf']))
f.write(opts_and_words('web2disk', web2disk, feed_titles))
f.write(opts_and_words('fetch-ebook-metadata', fem_op, []))
f.write(opts_and_words('calibre-smtp', smtp_op, []))
f.write('''
_prs500_ls()
{
local pattern search listing prefix
pattern="$1"
search="$1"
if [[ -n "{$pattern}" ]]; then
if [[ "${pattern:(-1)}" == "/" ]]; then
pattern=""
else
pattern="$(basename ${pattern} 2> /dev/null)"
search="$(dirname ${search} 2> /dev/null)"
fi
fi
if [[ "x${search}" == "x" || "x${search}" == "x." ]]; then
search="/"
fi
listing="$(prs500 ls ${search} 2>/dev/null)"
prefix="${search}"
if [[ "x${prefix:(-1)}" != "x/" ]]; then
prefix="${prefix}/"
fi
echo $(compgen -P "${prefix}" -W "${listing}" "${pattern}")
}
_prs500()
{
local cur prev
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
COMPREPLY=()
case "${prev}" in
ls|rm|mkdir|touch|cat )
COMPREPLY=( $(_prs500_ls "${cur}") )
return 0
;;
cp )
if [[ ${cur} == prs500:* ]]; then
COMPREPLY=( $(_prs500_ls "${cur:7}") )
return 0
else
_filedir
return 0
fi
;;
prs500 )
COMPREPLY=( $(compgen -W "cp ls rm mkdir touch cat info books df" "${cur}") )
return 0
;;
* )
if [[ ${cur} == prs500:* ]]; then
COMPREPLY=( $(_prs500_ls "${cur:7}") )
return 0
else
if [[ ${prev} == prs500:* ]]; then
_filedir
return 0
else
COMPREPLY=( $(compgen -W "prs500:" "${cur}") )
return 0
fi
return 0
fi
;;
esac
}
complete -o nospace -F _prs500 ebook-device
complete -o nospace -C calibre-complete ebook-convert
''')
f.close()
print 'done'
except TypeError, err:
if 'resolve_entities' in str(err):
print 'You need python-lxml >= 2.0.5 for calibre'
sys.exit(1)
raise
except:
if fatal_errors:
raise
print 'failed'
import traceback
traceback.print_exc()
return manifest
def setup_udev_rules(group_file, reload, fatal_errors):
print 'Trying to setup udev rules...'
manifest = []
sys.stdout.flush()
groups = open(group_file, 'rb').read()
group = 'plugdev' if 'plugdev' in groups else 'usb'
old_udev = '/etc/udev/rules.d/95-calibre.rules'
if os.path.exists(old_udev):
os.remove(old_udev)
if os.path.exists('/lib/udev/rules.d'):
udev = open_file('/lib/udev/rules.d/95-calibre.rules')
else:
udev = open_file(old_udev)
manifest.append(udev.name)
udev.write('''# Sony Reader PRS-500\n'''
'''BUS=="usb", SYSFS{idProduct}=="029b", SYSFS{idVendor}=="054c", MODE="660", GROUP="%s"\n'''%(group,)
)
udev.close()
return manifest
def option_parser():
from optparse import OptionParser
parser = OptionParser()
parser.add_option('--use-destdir', action='store_true', default=False, dest='destdir',
help='If set, respect the environment variable DESTDIR when installing files')
parser.add_option('--do-not-reload-udev-hal', action='store_true', dest='dont_reload', default=False,
help='Does nothing. Present for legacy reasons.')
parser.add_option('--group-file', default='/etc/group', dest='group_file',
help='File from which to read group information. Default: %default')
parser.add_option('--dont-check-root', action='store_true', default=False, dest='no_root',
help='If set, do not check if we are root.')
parser.add_option('--make-errors-fatal', action='store_true', default=False,
dest='fatal_errors', help='If set die on errors.')
parser.add_option('--save-manifest-to', default=None,
help='Save a manifest of all installed files to the specified location')
return parser
def install_man_pages(fatal_errors, use_destdir=False):
from calibre.utils.help2man import create_man_page
prefix = os.environ.get('DESTDIR', '/') if use_destdir else '/'
manpath = os.path.join(prefix, 'usr/share/man/man1')
if not os.path.exists(manpath):
os.makedirs(manpath)
print 'Installing MAN pages...'
manifest = []
for src in entry_points['console_scripts']:
prog, right = src.split('=')
prog = prog.strip()
module = __import__(right.split(':')[0].strip(), fromlist=['a'])
parser = getattr(module, 'option_parser', None)
if parser is None:
continue
parser = parser()
raw = create_man_page(prog, parser)
manfile = os.path.join(manpath, prog+'.1'+__appname__+'.bz2')
print '\tInstalling MAN page for', prog
open(manfile, 'wb').write(raw)
manifest.append(manfile)
return manifest
def post_install():
parser = option_parser()
@ -317,13 +454,6 @@ def post_install():
global use_destdir
use_destdir = opts.destdir
manifest = []
setup_desktop_integration(opts.fatal_errors)
if opts.no_root or os.geteuid() == 0:
manifest += install_man_pages(opts.fatal_errors, use_destdir)
manifest += setup_udev_rules(opts.group_file, not opts.dont_reload, opts.fatal_errors)
manifest += setup_completion(opts.fatal_errors)
else:
print "Skipping udev, completion, and man-page install for non-root user."
try:
from PyQt4 import Qt
@ -335,26 +465,9 @@ def post_install():
if opts.save_manifest_to:
open(opts.save_manifest_to, 'wb').write('\n'.join(manifest)+'\n')
from calibre.utils.config import config_dir
if os.path.exists(config_dir):
os.chdir(config_dir)
for f in os.listdir('.'):
if os.stat(f).st_uid == 0:
os.rmdir(f) if os.path.isdir(f) else os.unlink(f)
if os.stat(config_dir).st_uid == 0:
os.rmdir(config_dir)
def binary_install():
manifest = os.path.join(getattr(sys, 'frozen_path'), 'manifest')
exes = [x.strip() for x in open(manifest).readlines()]
print 'Creating symlinks...'
for exe in exes:
dest = os.path.join('/usr', 'bin', exe)
if os.path.exists(dest):
os.unlink(dest)
tgt = os.path.join(getattr(sys, 'frozen_path'), exe)
print '\tSymlinking %s to %s'%(tgt, dest)
os.symlink(tgt, dest)
post_install()
return 0
@ -431,50 +544,12 @@ def render_svg(image, dest):
painter.end()
image.save(dest)
def setup_desktop_integration(fatal_errors):
try:
from PyQt4.QtCore import QFile
from tempfile import mkdtemp
print 'Setting up desktop integration...'
def main():
p = option_parser()
opts, args = p.parse_args()
PostInstall(opts)
return 0
tdir = mkdtemp()
cwd = os.getcwdu()
try:
os.chdir(tdir)
render_svg(QFile(I('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(I('library.png')).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(I('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)
f.close()
f = open('calibre-ebook-viewer.desktop', 'wb')
f.write(EVIEWER)
f.close()
f = open('calibre-gui.desktop', 'wb')
f.write(GUI)
f.close()
check_call('xdg-desktop-menu install ./calibre-gui.desktop ./calibre-lrfviewer.desktop', shell=True)
f = open('calibre-mimetypes', 'wb')
f.write(MIME)
f.close()
check_call('xdg-mime install calibre-mimetypes', shell=True)
finally:
os.chdir(cwd)
shutil.rmtree(tdir)
except Exception, err:
if fatal_errors:
raise
print >>sys.stderr, 'Could not setup desktop integration. Error:'
print err
main = post_install
if __name__ == '__main__':
post_install()
sys.exit(main())