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): def main(args=sys.argv):
if len(args) == 1 or args[1] in ('-h', '--help'): if len(args) == 1 or args[1] in ('-h', '--help'):
print 'Usage: python', args[0], 'command', '[options]' 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 '\nTo get help on a particular command, run:'
print '\tpython', args[0], 'command -h' print '\tpython', args[0], 'command -h'
return 1 return 1
@ -83,7 +90,7 @@ def main(args=sys.argv):
prints('There were', len(warnings), 'warning(s):') prints('There were', len(warnings), 'warning(s):')
print print
for args, kwargs in warnings: for args, kwargs in warnings:
prints(*args, **kwargs) prints('*', *args, **kwargs)
print print
return 0 return 0

View File

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

View File

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

View File

@ -175,6 +175,8 @@ if iswindows:
class Build(Command): class Build(Command):
short_description = 'Build calibre C/C++ extension modules'
description = textwrap.dedent('''\ description = textwrap.dedent('''\
calibre depends on several python extensions written in C/C++. calibre depends on several python extensions written in C/C++.
This command will compile them. You can influence the compile 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>' __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __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, \ from setup import Command, islinux, basenames, modules, functions, \
__appname__, __version__ __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. 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+''' TEMPLATE = HEADER+'''
sys.path.insert(0, {path!r})
sys.resources_location = {resources!r}
sys.extensions_location = {extensions!r}
from {module} import {func!s} from {module} import {func!s}
sys.exit({func!s}()) sys.exit({func!s}())
''' '''
COMPLETE_TEMPLATE = HEADER+''' COMPLETE_TEMPLATE = HEADER+'''
import os sys.path.insert(0, os.path.join(path, 'calibre', 'utils'))
sys.path.insert(0, {path!r})
sys.path.insert(0, os.path.join({path!r}, 'calibre', 'utils'))
import complete import complete
sys.path = sys.path[1:] sys.path = sys.path[1:]
sys.resources_location = {resources!r}
sys.extensions_location = {extensions!r}
sys.exit(complete.main()) sys.exit(complete.main())
''' '''
@ -53,93 +51,123 @@ class Develop(Command):
the prefix of your python installation. This can be controlled the prefix of your python installation. This can be controlled
via the --prefix option. via the --prefix option.
''') ''')
short_description = 'Setup a development environment for calibre'
MODE = 0755 MODE = 0755
sub_commands = ['build', 'resources', 'gui'] 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): def add_options(self, parser):
parser.add_option('--prefix', parser.add_option('--prefix',
help='Binaries will be installed in <prefix>/bin') 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): def pre_sub_commands(self, opts):
if not islinux: if not islinux:
self.info('\nSetting up a development environment is only ' self.info('\nSetting up a source based development environment is only '
'supported on linux. On other platforms, install the calibre ' 'supported on linux. On other platforms, see the User Manual'
'binary and use the calibre-debug command.') ' for help with setting up a development environment.')
raise SystemExit(1) raise SystemExit(1)
if not os.geteuid() == 0: if os.geteuid() == 0:
self.info('\nError: This command must be run as root.')
raise SystemExit(1)
self.drop_privileges() self.drop_privileges()
# Ensure any calibre config files are created as correct user
import calibre.utils.config as c
c
def run(self, opts): def run(self, opts):
self.opts = opts
self.regain_privileges() self.regain_privileges()
self.find_locations(opts) self.consolidate_paths()
self.write_templates(opts) self.write_templates()
self.setup_mount_helper() self.setup_mount_helper()
self.install_files(opts) self.install_files()
self.run_postinstall() self.run_postinstall()
self.success() self.success()
def setup_mount_helper(self): def setup_mount_helper(self):
def warn(): def warn():
self.warn('Failed to compile mount helper. Auto mounting of', self.warn('Failed to compile mount helper. Auto mounting of',
'devices will not work') ' devices will not work')
if os.geteuid() != 0: if os.geteuid() != 0:
return warn() return self.warn('Must be run as root to compile mount helper. Auto '
import stat 'mounting of devices will not work.')
src = os.path.join(self.SRC, 'calibre', 'devices', 'linux_mount_helper.c') 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) 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() ret = p.wait()
if ret != 0: if ret != 0:
return warn() return warn()
os.chown(dest, 0, 0) os.chown(dest, 0, 0)
os.chmod(dest, os.chmod(dest, stat.S_ISUID|stat.S_ISGID|stat.S_IRUSR|stat.S_IWUSR|\
stat.S_ISUID|stat.S_ISGID|stat.S_IRUSR|stat.S_IWUSR|stat.S_IXUSR|stat.S_IXGRP|stat.S_IXOTH) stat.S_IXUSR|stat.S_IXGRP|stat.S_IXOTH)
return dest return dest
def install_files(self, opts): def install_files(self):
pass pass
def run_postinstall(self): def run_postinstall(self):
env = dict(**os.environ) if self.opts.postinstall:
env['DESTDIR'] = self.prefix from calibre.linux import PostInstall
subprocess.check_call(['calibre_postinstall', '--use-destdir'], env=env) PostInstall(self.opts, info=self.info, warn=self.warn)
def success(self): def success(self):
self.info('\nDevelopment environment successfully setup') self.info('\nDevelopment environment successfully setup')
def find_locations(self, opts): def write_templates(self):
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):
for typ in ('console', 'gui'): for typ in ('console', 'gui'):
for name, mod, func in zip(basenames[typ], modules[typ], for name, mod, func in zip(basenames[typ], modules[typ],
functions[typ]): functions[typ]):
self.write_template(opts, name, mod, func) self.write_template(name, mod, func)
if islinux:
self.write_template(opts, 'calibre_postinstall', 'calibre.linux', 'main')
def write_template(self, opts, name, mod, func): def write_template(self, name, mod, func):
template = COMPLETE_TEMPLATE if name == 'calibre-complete' else TEMPLATE template = COMPLETE_TEMPLATE if name == 'calibre-complete' else TEMPLATE
script = template.format( script = template.format(
module=mod, func=func, module=mod, func=func,
path=self.path, resources=self.resources, path=self.libdir, resources=self.sharedir,
extensions=self.extensions) extensions=self.j(self.libdir, 'calibre', 'plugins'))
path = self.root + self.j(self.bindir, name) path = self.j(self.staging_bindir, name)
if not os.path.exists(self.bindir): if not os.path.exists(self.staging_bindir):
os.makedirs(self.bindir) os.makedirs(self.staging_bindir)
self.info('Installing binary:', path) self.info('Installing binary:', path)
open(path, 'wb').write(script) open(path, 'wb').write(script)
os.chmod(path, self.MODE) os.chmod(path, self.MODE)
@ -154,49 +182,45 @@ class Install(Develop):
The default <prefix> is the prefix of your python installation. The default <prefix> is the prefix of your python installation.
''') ''')
short_description = 'Install calibre from source'
sub_commands = ['build', 'gui'] sub_commands = ['build', 'gui']
def add_options(self, parser): def add_options(self, parser):
parser.add_option('--prefix', help='Installation prefix') parser.add_option('--prefix', help='Installation prefix.')
parser.add_option('--libdir', help='Where to put calibre library files') parser.add_option('--libdir',
parser.add_option('--bindir', help='Where to install calibre binaries') help='Where to put calibre library files. Default is <prefix>/lib')
parser.add_option('--sharedir', help='Where to install calibre data files') parser.add_option('--bindir',
parser.add_option('--root', default='', help='Where to put the calibre binaries. Default is <prefix>/bin')
help='Use a different installation root (mainly for packaging)') parser.add_option('--sharedir',
self.root = '' 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): def install_files(self):
if opts.prefix is None: dest = self.staging_libdir
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
if os.path.exists(dest): if os.path.exists(dest):
shutil.rmtree(dest) shutil.rmtree(dest)
shutil.copytree(self.SRC, dest) for x in os.walk(self.SRC):
for x in ('calibre/manual', 'calibre/trac', reldir = os.path.relpath(x[0], self.SRC)
'calibre/ebooks/lrf/html/demo'): destdir = os.path.join(dest, reldir)
x = self.j(dest, x)
if os.path.exists(dest):
shutil.rmtree(x)
for x in os.walk(dest):
for f in x[-1]: for f in x[-1]:
if os.path.splitext(f)[1] in ('.c', '.cpp', '.h'): if os.path.splitext(f)[1] in ('.py', '.so'):
os.remove(self.j(x[0], f)) if not os.path.exists(destdir):
dest = self.root + self.resources os.makedirs(destdir)
shutil.copy2(self.j(x[0], f), destdir)
dest = self.staging_sharedir
if os.path.exists(dest): if os.path.exists(dest):
shutil.rmtree(dest) shutil.rmtree(dest)
shutil.copytree(self.RESOURCES, dest) shutil.copytree(self.RESOURCES, dest)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -127,6 +127,8 @@ class UploadDemo(Command):
class UploadToServer(Command): class UploadToServer(Command):
description = 'Upload miscellaneous data to calibre server'
def run(self, opts): def run(self, opts):
check_call('ssh divok rm -f %s/calibre-\*.tar.gz'%DOWNLOADS, shell=True) 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('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: elif opts.add_simple_plugin is not None:
add_simple_plugin(opts.add_simple_plugin) add_simple_plugin(opts.add_simple_plugin)
elif opts.paths: elif opts.paths:
prints('CALIBRE_RESOURCES_LOCATION='+sys.resources_location) prints('CALIBRE_RESOURCES_PATH='+sys.resources_location)
prints('CALIBRE_EXTENSIONS_LOCATION='+sys.extensions_location) prints('CALIBRE_EXTENSIONS_PATH='+sys.extensions_location)
prints('CALIBRE_PYTHON_PATH='+os.pathsep.join(sys.path)) prints('CALIBRE_PYTHON_PATH='+os.pathsep.join(sys.path))
elif opts.pdfreflow: elif opts.pdfreflow:
from calibre.ebooks.pdf.reflow import option_parser as px, run 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) { int do_mount(char *dev, char *mp) {
char options[1000]; char options[1000];
char marker[2000]; char marker[2000];
int errsv;
if (exists(dev) == 0) { if (exists(dev) == 0) {
fprintf(stderr, "Specified device node does not exist\n"); fprintf(stderr, "Specified device node does not exist\n");
return EXIT_FAILURE; return EXIT_FAILURE;
@ -55,19 +56,19 @@ int do_mount(char *dev, char *mp) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
execlp("mount", "mount", "-t", "vfat", "-o", options, dev, mp, NULL); 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)); fprintf(stderr, "Failed to mount with error: %s\n", strerror(errsv));
return EXIT_FAILURE; return EXIT_FAILURE;
} }
int do_eject(char *dev, char*mp) { int do_eject(char *dev, char*mp) {
char marker[2000]; char marker[2000];
int status = EXIT_FAILURE, ret; int status = EXIT_FAILURE, ret, pid, errsv, i, rmd;
if (get_root() != 0) { if (get_root() != 0) {
fprintf(stderr, "Failed to elevate to root privileges\n"); fprintf(stderr, "Failed to elevate to root privileges\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
int pid = fork(); pid = fork();
if (pid == -1) { if (pid == -1) {
fprintf(stderr, "Failed to fork\n"); fprintf(stderr, "Failed to fork\n");
return EXIT_FAILURE; return EXIT_FAILURE;
@ -78,11 +79,10 @@ int do_eject(char *dev, char*mp) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
execlp("eject", "eject", "-s", dev, NULL); execlp("eject", "eject", "-s", dev, NULL);
int errsv = errno; errsv = errno;
fprintf(stderr, "Failed to eject with error: %s\n", strerror(errsv)); fprintf(stderr, "Failed to eject with error: %s\n", strerror(errsv));
return EXIT_FAILURE; return EXIT_FAILURE;
} else { } else {
int i;
for (i =0; i < 7; i++) { for (i =0; i < 7; i++) {
sleep(1); sleep(1);
ret = waitpid(pid, &status, WNOHANG); 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)); fprintf(stderr, "Failed to unlink marker: %s\n", strerror(errno));
return EXIT_FAILURE; return EXIT_FAILURE;
} }
int rmd = rmdir(mp); rmd = rmdir(mp);
if (rmd == -1) { if (rmd == -1) {
fprintf(stderr, "Failed to remove mount point: %s\n", strerror(errno)); fprintf(stderr, "Failed to remove mount point: %s\n", strerror(errno));
return EXIT_FAILURE; return EXIT_FAILURE;

View File

@ -1,17 +1,13 @@
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
''' Post installation script for linux ''' ''' Post installation script for linux '''
import sys, os, shutil
import sys, os, shutil, cPickle, textwrap
from subprocess import check_call from subprocess import check_call
from calibre import __version__, __appname__ from calibre import __version__, __appname__, prints
from calibre.customize.ui import device_plugins
DEVICES = device_plugins()
DESTDIR = ''
if os.environ.has_key('DESTDIR'):
DESTDIR = os.environ['DESTDIR']
entry_points = { entry_points = {
'console_scripts': [ \ '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): def options(option_parser):
parser = 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" 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(): def post_install():
parser = option_parser() parser = option_parser()
@ -317,13 +454,6 @@ def post_install():
global use_destdir global use_destdir
use_destdir = opts.destdir use_destdir = opts.destdir
manifest = [] 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: try:
from PyQt4 import Qt from PyQt4 import Qt
@ -335,26 +465,9 @@ def post_install():
if opts.save_manifest_to: if opts.save_manifest_to:
open(opts.save_manifest_to, 'wb').write('\n'.join(manifest)+'\n') 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(): def binary_install():
manifest = os.path.join(getattr(sys, 'frozen_path'), 'manifest') 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() post_install()
return 0 return 0
@ -431,50 +544,12 @@ def render_svg(image, dest):
painter.end() painter.end()
image.save(dest) image.save(dest)
def setup_desktop_integration(fatal_errors): def main():
try: p = option_parser()
from PyQt4.QtCore import QFile opts, args = p.parse_args()
from tempfile import mkdtemp PostInstall(opts)
return 0
print '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 --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__': if __name__ == '__main__':
post_install() sys.exit(main())