Simplify launching of console utilities on macOS

This commit is contained in:
Kovid Goyal 2019-08-31 15:05:14 +05:30
parent b8968a82f8
commit a7453e877a
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
9 changed files with 27 additions and 31 deletions

View File

@ -203,7 +203,6 @@ class Freeze(object):
if not test_launchers and not self.dont_strip:
self.strip_files()
if not test_launchers:
self.create_console_app()
self.create_gui_apps()
self.run_tests()
@ -213,8 +212,7 @@ class Freeze(object):
@flush
def run_tests(self):
cc_dir = join(self.contents_dir, 'calibre-debug.app', 'Contents')
self.test_runner(join(cc_dir, 'MacOS', 'calibre-debug'), self.contents_dir)
self.test_runner(join(self.contents_dir, 'MacOS', 'calibre-debug'), self.contents_dir)
@flush
def add_resources(self):
@ -677,14 +675,6 @@ class Freeze(object):
else:
os.symlink(join('../..', x), join(cc_dir, x))
@flush
def create_console_app(self):
def specialise_plist(plist):
plist['LSBackgroundOnly'] = '1'
plist['CFBundleIdentifier'] = 'com.calibre-ebook.console'
plist['CFBundleExecutable'] = 'calibre-parallel'
self.create_app_clone('console.app', specialise_plist)
@flush
def create_gui_apps(self):
input_formats = sorted(set(json.loads(
@ -696,19 +686,17 @@ class Freeze(object):
def specialise_plist(launcher, remove_types, plist):
plist['CFBundleDisplayName'] = plist['CFBundleName'] = {
'ebook-viewer': 'E-book Viewer', 'ebook-edit': 'Edit Book', 'calibre-debug': 'calibre (debug)',
'ebook-viewer': 'E-book Viewer', 'ebook-edit': 'Edit Book',
}[launcher]
plist['CFBundleExecutable'] = launcher
if launcher != 'calibre-debug':
plist['CFBundleIconFile'] = launcher + '.icns'
plist['CFBundleIdentifier'] = 'com.calibre-ebook.' + launcher
plist['CFBundleIconFile'] = launcher + '.icns'
if not remove_types:
e = plist['CFBundleDocumentTypes'][0]
exts = 'epub azw3'.split() if launcher == 'ebook-edit' else input_formats
e['CFBundleTypeExtensions'] = exts
for launcher in ('ebook-viewer', 'ebook-edit', 'calibre-debug'):
remove_types = launcher == 'calibre-debug'
self.create_app_clone(launcher + '.app', partial(specialise_plist, launcher, remove_types), remove_doc_types=remove_types)
for launcher in ('ebook-viewer', 'ebook-edit'):
self.create_app_clone(launcher + '.app', partial(specialise_plist, launcher, False), remove_doc_types=False)
@flush
def copy_site(self):

View File

@ -7,20 +7,23 @@ This is stripped down and customized for use in py2app applications
import sys
import os
def makepath(*paths):
dir = os.path.abspath(os.path.join(*paths))
return dir, os.path.normcase(dir)
def abs__file__():
"""Set all module __file__ attribute to an absolute path"""
for m in sys.modules.values():
if hasattr(m, '__loader__'):
continue # don't mess with a PEP 302-supplied __file__
continue # don't mess with a PEP 302-supplied __file__
try:
m.__file__ = os.path.abspath(m.__file__)
except AttributeError:
continue
# This ensures that the initial path provided by the interpreter contains
# only absolute pathnames, even if we're running from the build directory.
L = []
@ -38,6 +41,7 @@ sys.path[:] = L
del dir, dircase, L
_dirs_in_sys_path = None
def _init_pathinfo():
global _dirs_in_sys_path
_dirs_in_sys_path = d = {}
@ -47,6 +51,7 @@ def _init_pathinfo():
dir, dircase = makepath(dir)
d[dircase] = 1
def addsitedir(sitedir):
global _dirs_in_sys_path
if _dirs_in_sys_path is None:
@ -56,7 +61,7 @@ def addsitedir(sitedir):
reset = 0
sitedir, sitedircase = makepath(sitedir)
if sitedircase not in _dirs_in_sys_path:
sys.path.append(sitedir) # Add path component
sys.path.append(sitedir) # Add path component
try:
names = os.listdir(sitedir)
except os.error:
@ -68,6 +73,7 @@ def addsitedir(sitedir):
if reset:
_dirs_in_sys_path = None
def addpackage(sitedir, name):
global _dirs_in_sys_path
if _dirs_in_sys_path is None:
@ -107,29 +113,31 @@ if hasattr(sys, "setdefaultencoding"):
sys.setdefaultencoding('utf-8')
del sys.setdefaultencoding
def run_entry_point():
bname, mod, func = sys.calibre_basename, sys.calibre_module, sys.calibre_function
sys.argv[0] = bname
pmod = __import__(mod, fromlist=[1], level=0)
return getattr(pmod, func)()
def add_calibre_vars(base):
sys.frameworks_dir = os.path.join(os.path.dirname(base), 'Frameworks')
sys.resources_location = os.path.abspath(os.path.join(base, 'resources'))
sys.extensions_location = os.path.join(sys.frameworks_dir, 'plugins')
sys.binaries_path = os.path.join(os.path.dirname(base), 'MacOS')
sys.console_binaries_path = os.path.join(os.path.dirname(base),
'console.app', 'Contents', 'MacOS')
dv = os.environ.get('CALIBRE_DEVELOP_FROM', None)
if dv and os.path.exists(dv):
sys.path.insert(0, os.path.abspath(dv))
def nuke_stdout():
# Redirect stdout, stdin and stderr to /dev/null
from calibre.constants import plugins
plugins['speedup'][0].detach(os.devnull)
def main():
global __file__
@ -148,7 +156,8 @@ def main():
addsitedir(sys.site_packages)
if sys.calibre_is_gui_app and not (
sys.stdout.isatty() or sys.stderr.isatty() or sys.stdin.isatty()):
sys.stdout.isatty() or sys.stderr.isatty() or sys.stdin.isatty()
):
nuke_stdout()
return run_entry_point()

View File

@ -37,7 +37,7 @@ the directory in which you created :file:`__init__.py`::
.. note::
On macOS, the command line tools are inside the calibre bundle, for example,
if you installed calibre in :file:`/Applications` the command line tools
are in :file:`/Applications/calibre.app/Contents/console.app/Contents/MacOS/`.
are in :file:`/Applications/calibre.app/Contents/MacOS/`.
You can download the Hello World plugin from
`helloworld_plugin.zip <https://calibre-ebook.com/downloads/helloworld_plugin.zip>`_.
@ -325,4 +325,3 @@ Sharing your plugins with others
If you would like to share the plugins you have created with other users of calibre, post your plugin in a new thread in the
`calibre plugins forum <https://www.mobileread.com/forums/forumdisplay.php?f=237>`_.

View File

@ -198,7 +198,7 @@ the previously checked out calibre code directory, for example::
calibre is the directory that contains the src and resources sub-directories.
The calibre command line tools are found inside the calibre app bundle, in
:file:`/Applications/calibre.app/Contents/console.app/Contents/MacOS`
:file:`/Applications/calibre.app/Contents/MacOS`
you should add this directory to your PATH environment variable, if you want to
run the command line tools easily.

View File

@ -311,7 +311,7 @@ If you're satisfied with your recipe, and you feel there is enough demand to jus
.. note::
On macOS, the command line tools are inside the calibre bundle, for example,
if you installed calibre in :file:`/Applications` the command line tools
are in :file:`/Applications/calibre.app/Contents/console.app/Contents/MacOS/`.
are in :file:`/Applications/calibre.app/Contents/MacOS/`.
.. seealso::

View File

@ -17,8 +17,6 @@ from polyglot.builtins import exec_path, raw_input, unicode_type, getcwd
def get_debug_executable():
if hasattr(sys, 'frameworks_dir'):
base = os.path.dirname(sys.frameworks_dir)
if 'calibre-debug.app' not in base:
base = os.path.join(base, 'calibre-debug.app', 'Contents')
return os.path.join(base, 'MacOS', 'calibre-debug')
if getattr(sys, 'frozen', False):
return os.path.join(os.path.dirname(os.path.abspath(sys.executable)), 'calibre-debug' + ('.exe' if iswindows else ''))

View File

@ -1201,6 +1201,8 @@ def ensure_app(headless=True):
has_headless = isosx or islinux or isbsd
if headless and has_headless:
args += ['-platformpluginpath', plugins_loc, '-platform', 'headless']
if isosx:
os.environ['QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM'] = '1'
_store_app = QApplication(args)
if headless and has_headless:
_store_app.headless = True

View File

@ -1183,7 +1183,7 @@ def cli_index_strings():
return _('Command Line Interface'), _(
'On macOS, the command line tools are inside the calibre bundle, for example,'
' if you installed calibre in :file:`/Applications` the command line tools'
' are in :file:`/Applications/calibre.app/Contents/console.app/Contents/MacOS/`.'), _(
' are in :file:`/Applications/calibre.app/Contents/MacOS/`.'), _(
'Documented commands'), _('Undocumented commands'), _(
'You can see usage for undocumented commands by executing them without arguments in a terminal.'), _(
'Change language'), _('Search')

View File

@ -59,7 +59,7 @@ class Worker(object):
return os.path.join(os.path.dirname(sys.executable),
e+'.exe' if isfrozen else 'Scripts\\%s.exe'%e)
if isosx:
return os.path.join(sys.console_binaries_path, e)
return os.path.join(sys.binaries_path, e)
if isfrozen:
return os.path.join(sys.executables_location, e)
@ -74,7 +74,7 @@ class Worker(object):
def gui_executable(self):
if isosx and not hasattr(sys, 'running_from_setup'):
if self.job_name in {'ebook-viewer', 'ebook-edit'}:
return self.executable.replace('/console.app/', '/%s.app/' % self.job_name)
return self.executable.replace('/Contents/', '/Contents/%s.app/Contents/' % self.job_name)
return os.path.join(sys.binaries_path, self.exe_name)
return self.executable