Linux installer: Use pkg-config to get the location for installing the calibre bash completion scripts. pkg-config is used only when installing to /usr. Fixes #1340023 [bash-completion missing](https://bugs.launchpad.net/calibre/+bug/1340023)

This commit is contained in:
Kovid Goyal 2014-07-10 12:45:14 +05:30
parent e013415923
commit a106f15d95

View File

@ -4,11 +4,11 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
''' Post installation script for linux ''' ''' Post installation script for linux '''
import sys, os, cPickle, textwrap, stat, errno import sys, os, cPickle, textwrap, stat, errno
from subprocess import check_call from subprocess import check_call, check_output
from functools import partial from functools import partial
from calibre import __appname__, prints, guess_type from calibre import __appname__, prints, guess_type
from calibre.constants import islinux, isnetbsd, isbsd from calibre.constants import islinux, isbsd
from calibre.customize.ui import all_input_formats from calibre.customize.ui import all_input_formats
from calibre.ptempfile import TemporaryDirectory from calibre.ptempfile import TemporaryDirectory
from calibre import CurrentDir from calibre import CurrentDir
@ -162,6 +162,8 @@ if mimetype_icons and raw_input('Remove the ebook format icons? [y/n]:').lower()
# }}} # }}}
# Completion {{{
class ZshCompleter(object): # {{{ class ZshCompleter(object): # {{{
def __init__(self, opts): def __init__(self, opts):
@ -434,6 +436,154 @@ class ZshCompleter(object): # {{{
f.write('esac\n') f.write('esac\n')
# }}} # }}}
def get_bash_completion_path(root, share, info):
if root == '/usr':
# Try to get the system bash completion dir since we are installing to
# /usr
try:
path = check_output('pkg-config --variable=completionsdir bash-completion'.split()).strip().partition(os.pathsep)[0]
except Exception:
info('Failed to find directory to install bash completions, skipping.')
return None
else:
if os.path.exists(path):
return os.path.join(path, 'calibre')
else:
# Use the default bash-completion dir under staging_share
return os.path.join(share, 'bash-completion', 'completions', 'calibre')
def write_completion(bash_comp_dest, zsh):
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.gui2.viewer.main import option_parser as viewer_op
from calibre.gui2.tweak_book.main import option_parser as tweak_op
from calibre.ebooks.metadata.sources.cli 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
from calibre.library.server.main import option_parser as serv_op
from calibre.ebooks.oeb.polish.main import option_parser as polish_op, SUPPORTED
from calibre.ebooks.oeb.polish.import_book import IMPORTABLE
from calibre.debug import option_parser as debug_op
from calibre.ebooks import BOOK_EXTENSIONS
from calibre.customize.ui import available_input_formats
input_formats = sorted(all_input_formats())
tweak_formats = sorted(x.lower() for x in SUPPORTED|IMPORTABLE)
if bash_comp_dest and not os.path.exists(os.path.dirname(bash_comp_dest)):
os.makedirs(os.path.dirname(bash_comp_dest))
complete = 'calibre-complete'
if getattr(sys, 'frozen_path', None):
complete = os.path.join(getattr(sys, 'frozen_path'), complete)
with open(bash_comp_dest or os.devnull, 'wb') as f:
def o_and_e(*args, **kwargs):
f.write(opts_and_exts(*args, **kwargs))
zsh.opts_and_exts(*args, **kwargs)
def o_and_w(*args, **kwargs):
f.write(opts_and_words(*args, **kwargs))
zsh.opts_and_words(*args, **kwargs)
f.write('# calibre Bash Shell Completion\n')
o_and_e('calibre', guiop, BOOK_EXTENSIONS)
o_and_e('lrf2lrs', lrf2lrsop, ['lrf'], file_map={'--output':['lrs']})
o_and_e('ebook-meta', metaop,
list(meta_filetypes()), cover_opts=['--cover', '-c'],
opf_opts=['--to-opf', '--from-opf'])
o_and_e('ebook-polish', polish_op,
[x.lower() for x in SUPPORTED], cover_opts=['--cover', '-c'],
opf_opts=['--opf', '-o'])
o_and_e('lrfviewer', lrfviewerop, ['lrf'])
o_and_e('ebook-viewer', viewer_op, input_formats)
o_and_e('ebook-edit', tweak_op, tweak_formats)
o_and_w('fetch-ebook-metadata', fem_op, [])
o_and_w('calibre-smtp', smtp_op, [])
o_and_w('calibre-server', serv_op, [])
o_and_e('calibre-debug', debug_op, ['py', 'recipe', 'mobi', 'azw', 'azw3', 'docx'], file_map={
'--tweak-book':['epub', 'azw3', 'mobi'],
'--subset-font':['ttf', 'otf'],
'--exec-file':['py', 'recipe'],
'--add-simple-plugin':['py'],
'--inspect-mobi':['mobi', 'azw', 'azw3'],
'--viewer':list(available_input_formats()),
})
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} == dev:* ]]; then
COMPREPLY=( $(_ebook_device_ls "${cur:7}") )
return 0
else
_filedir
return 0
fi
;;
dev )
COMPREPLY=( $(compgen -W "cp ls rm mkdir touch cat info books df" "${cur}") )
return 0
;;
* )
if [[ ${cur} == dev:* ]]; then
COMPREPLY=( $(_ebook_device_ls "${cur:7}") )
return 0
else
if [[ ${prev} == dev:* ]]; then
_filedir
return 0
else
COMPREPLY=( $(compgen -W "dev:" "${cur}") )
return 0
fi
return 0
fi
;;
esac
}
complete -o nospace -F _ebook_device ebook-device
complete -o nospace -C %s ebook-convert
''')%complete)
# }}}
class PostInstall: class PostInstall:
def task_failed(self, msg): def task_failed(self, msg):
@ -539,151 +689,15 @@ class PostInstall:
def setup_completion(self): # {{{ def setup_completion(self): # {{{
try: try:
self.info('Setting up command-line completion...') self.info('Setting up command-line 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.gui2.viewer.main import option_parser as viewer_op
from calibre.gui2.tweak_book.main import option_parser as tweak_op
from calibre.ebooks.metadata.sources.cli 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
from calibre.library.server.main import option_parser as serv_op
from calibre.ebooks.oeb.polish.main import option_parser as polish_op, SUPPORTED
from calibre.ebooks.oeb.polish.import_book import IMPORTABLE
from calibre.debug import option_parser as debug_op
from calibre.ebooks import BOOK_EXTENSIONS
from calibre.customize.ui import available_input_formats
input_formats = sorted(all_input_formats())
tweak_formats = sorted(x.lower() for x in SUPPORTED|IMPORTABLE)
zsh = ZshCompleter(self.opts) zsh = ZshCompleter(self.opts)
bc = os.path.join(os.path.dirname(self.opts.staging_sharedir),
'bash-completion')
if os.path.exists(bc):
f = os.path.join(bc, 'calibre')
else:
if isnetbsd:
f = os.path.join(self.opts.staging_root, 'share/bash_completion.d/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))
bash_comp_dest, zsh_comp_dest = f, None
if zsh.dest: if zsh.dest:
self.info('Installing zsh completion to:', zsh.dest) self.info('Installing zsh completion to:', zsh.dest)
self.manifest.append(zsh.dest) self.manifest.append(zsh.dest)
zsh_comp_dest = zsh.dest bash_comp_dest = get_bash_completion_path(self.opts.staging_root, os.path.dirname(self.opts.staging_sharedir), self.info)
complete = 'calibre-complete' if bash_comp_dest is not None:
if getattr(sys, 'frozen_path', None): self.info('Installing bash completion to:', bash_comp_dest)
complete = os.path.join(getattr(sys, 'frozen_path'), complete) self.manifest.append(bash_comp_dest)
write_completion(bash_comp_dest, zsh)
self.info('Installing bash completion to', f)
with open(f, 'wb') as f:
def o_and_e(*args, **kwargs):
f.write(opts_and_exts(*args, **kwargs))
zsh.opts_and_exts(*args, **kwargs)
def o_and_w(*args, **kwargs):
f.write(opts_and_words(*args, **kwargs))
zsh.opts_and_words(*args, **kwargs)
f.write('# calibre Bash Shell Completion\n')
o_and_e('calibre', guiop, BOOK_EXTENSIONS)
o_and_e('lrf2lrs', lrf2lrsop, ['lrf'], file_map={'--output':['lrs']})
o_and_e('ebook-meta', metaop,
list(meta_filetypes()), cover_opts=['--cover', '-c'],
opf_opts=['--to-opf', '--from-opf'])
o_and_e('ebook-polish', polish_op,
[x.lower() for x in SUPPORTED], cover_opts=['--cover', '-c'],
opf_opts=['--opf', '-o'])
o_and_e('lrfviewer', lrfviewerop, ['lrf'])
o_and_e('ebook-viewer', viewer_op, input_formats)
o_and_e('ebook-edit', tweak_op, tweak_formats)
o_and_w('fetch-ebook-metadata', fem_op, [])
o_and_w('calibre-smtp', smtp_op, [])
o_and_w('calibre-server', serv_op, [])
o_and_e('calibre-debug', debug_op, ['py', 'recipe', 'mobi', 'azw', 'azw3', 'docx'], file_map={
'--tweak-book':['epub', 'azw3', 'mobi'],
'--subset-font':['ttf', 'otf'],
'--exec-file':['py', 'recipe'],
'--add-simple-plugin':['py'],
'--inspect-mobi':['mobi', 'azw', 'azw3'],
'--viewer':list(available_input_formats()),
})
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} == dev:* ]]; then
COMPREPLY=( $(_ebook_device_ls "${cur:7}") )
return 0
else
_filedir
return 0
fi
;;
dev )
COMPREPLY=( $(compgen -W "cp ls rm mkdir touch cat info books df" "${cur}") )
return 0
;;
* )
if [[ ${cur} == dev:* ]]; then
COMPREPLY=( $(_ebook_device_ls "${cur:7}") )
return 0
else
if [[ ${prev} == dev:* ]]; then
_filedir
return 0
else
COMPREPLY=( $(compgen -W "dev:" "${cur}") )
return 0
fi
return 0
fi
;;
esac
}
complete -o nospace -F _ebook_device ebook-device
complete -o nospace -C %s ebook-convert
''')%complete)
zsh.write()
self.manifest.extend((bash_comp_dest, zsh_comp_dest))
except TypeError as err: except TypeError as err:
if 'resolve_entities' in str(err): if 'resolve_entities' in str(err):
print 'You need python-lxml >= 2.0.5 for calibre' print 'You need python-lxml >= 2.0.5 for calibre'
@ -692,6 +706,9 @@ class PostInstall:
except EnvironmentError as e: except EnvironmentError as e:
if e.errno == errno.EACCES: if e.errno == errno.EACCES:
self.warning('Failed to setup completion, permission denied') self.warning('Failed to setup completion, permission denied')
if self.opts.fatal_errors:
raise
self.task_failed('Setting up completion failed')
except: except:
if self.opts.fatal_errors: if self.opts.fatal_errors:
raise raise