mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
In OS X 10.8 Apple stopped redirecting stdout/stderr to Console.app for applications launched by launch services. Re-enable the redirection, useful for debugging.
209 lines
6.9 KiB
Python
209 lines
6.9 KiB
Python
"""
|
|
Append module search paths for third-party packages to sys.path.
|
|
|
|
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__
|
|
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 = []
|
|
_dirs_in_sys_path = {}
|
|
dir = dircase = None # sys.path may be empty at this point
|
|
for dir in sys.path:
|
|
# Filter out duplicate paths (on case-insensitive file systems also
|
|
# if they only differ in case); turn relative paths into absolute
|
|
# paths.
|
|
dir, dircase = makepath(dir)
|
|
if not dircase in _dirs_in_sys_path:
|
|
L.append(dir)
|
|
_dirs_in_sys_path[dircase] = 1
|
|
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 = {}
|
|
for dir in sys.path:
|
|
if dir and not os.path.isdir(dir):
|
|
continue
|
|
dir, dircase = makepath(dir)
|
|
d[dircase] = 1
|
|
|
|
def addsitedir(sitedir):
|
|
global _dirs_in_sys_path
|
|
if _dirs_in_sys_path is None:
|
|
_init_pathinfo()
|
|
reset = 1
|
|
else:
|
|
reset = 0
|
|
sitedir, sitedircase = makepath(sitedir)
|
|
if not sitedircase in _dirs_in_sys_path:
|
|
sys.path.append(sitedir) # Add path component
|
|
try:
|
|
names = os.listdir(sitedir)
|
|
except os.error:
|
|
return
|
|
names.sort()
|
|
for name in names:
|
|
if name[-4:] == os.extsep + "pth":
|
|
addpackage(sitedir, name)
|
|
if reset:
|
|
_dirs_in_sys_path = None
|
|
|
|
def addpackage(sitedir, name):
|
|
global _dirs_in_sys_path
|
|
if _dirs_in_sys_path is None:
|
|
_init_pathinfo()
|
|
reset = 1
|
|
else:
|
|
reset = 0
|
|
fullname = os.path.join(sitedir, name)
|
|
try:
|
|
f = open(fullname)
|
|
except IOError:
|
|
return
|
|
while True:
|
|
dir = f.readline()
|
|
if not dir:
|
|
break
|
|
if dir[0] == '#':
|
|
continue
|
|
if dir.startswith("import"):
|
|
exec dir
|
|
continue
|
|
if dir[-1] == '\n':
|
|
dir = dir[:-1]
|
|
dir, dircase = makepath(sitedir, dir)
|
|
if not dircase in _dirs_in_sys_path and os.path.exists(dir):
|
|
sys.path.append(dir)
|
|
_dirs_in_sys_path[dircase] = 1
|
|
if reset:
|
|
_dirs_in_sys_path = None
|
|
|
|
|
|
# Remove sys.setdefaultencoding() so that users cannot change the
|
|
# encoding after initialization. The test for presence is needed when
|
|
# this module is run as a script, because this code is executed twice.
|
|
#
|
|
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 setup_asl():
|
|
# On Mac OS X 10.8 or later the contents of stdout and stderr
|
|
# do not end up in Console.app
|
|
# This function detects if "asl_log_descriptor" is available
|
|
# (introduced in 10.8), and if it is configures ASL to redirect
|
|
# all writes to stdout/stderr to Console.app
|
|
import ctypes
|
|
try:
|
|
syslib = ctypes.CDLL("/usr/lib/libSystem.dylib")
|
|
except EnvironmentError:
|
|
import ctypes.util
|
|
syslib = ctypes.CDLL(ctypes.util.find_library('System'))
|
|
|
|
asl_log_descriptor_proto = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_uint32)
|
|
try:
|
|
asl_log_descriptor = asl_log_descriptor_proto(('asl_log_descriptor', syslib), ((1, 'asl'), (1, 'msg'), (1, 'level'), (1, 'descriptor'), (1, 'fd_type')))
|
|
except AttributeError:
|
|
# OS X < 10.8 no need to redirect
|
|
return
|
|
asl_open_proto = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint32)
|
|
asl_open = asl_open_proto(('asl_open', syslib), ((1, "ident"), (1, "facility"), (1, 'opts')))
|
|
asl_new_proto = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_uint32)
|
|
asl_new = asl_new_proto(('asl_new', syslib), ((1, "type"),))
|
|
asl_set_proto = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p)
|
|
asl_set = asl_set_proto(('asl_set', syslib), ((1, "msg"), (1, "key"), (1, "value")))
|
|
|
|
CONSOLE = b'com.apple.console'
|
|
# Taken from asl.h
|
|
ASL_OPT_NO_DELAY = 2
|
|
ASL_TYPE_MSG = 0
|
|
ASL_KEY_FACILITY = b'Facility'
|
|
ASL_KEY_LEVEL = b'Level'
|
|
ASL_KEY_READ_UID = b'ReadUID'
|
|
ASL_STRING_NOTICE = b'Notice'
|
|
ASL_LEVEL_NOTICE = 4
|
|
ASL_LOG_DESCRIPTOR_WRITE = 2
|
|
|
|
cl = asl_open(ident=getattr(sys, 'calibre_basename', b'calibre'), facility=CONSOLE, opts=ASL_OPT_NO_DELAY)
|
|
if cl is None:
|
|
return
|
|
|
|
# Create an ASL template message for the STDOUT/STDERR redirection.
|
|
msg = asl_new(ASL_TYPE_MSG)
|
|
if msg is None:
|
|
return
|
|
if asl_set(msg, ASL_KEY_FACILITY, CONSOLE) != 0:
|
|
return
|
|
if asl_set(msg, ASL_KEY_LEVEL, ASL_STRING_NOTICE) != 0:
|
|
return
|
|
if asl_set(msg, ASL_KEY_READ_UID, bytes('%d' % os.getuid())) != 0:
|
|
return
|
|
# Redirect the STDOUT/STDERR file descriptors to ASL
|
|
if asl_log_descriptor(cl, msg, ASL_LEVEL_NOTICE, sys.stdout.fileno(), ASL_LOG_DESCRIPTOR_WRITE) != 0:
|
|
return
|
|
if asl_log_descriptor(cl, msg, ASL_LEVEL_NOTICE, sys.stderr.fileno(), ASL_LOG_DESCRIPTOR_WRITE) != 0:
|
|
return
|
|
|
|
def main():
|
|
global __file__
|
|
base = sys.resourcepath
|
|
|
|
sys.frozen = 'macosx_app'
|
|
sys.new_app_bundle = True
|
|
abs__file__()
|
|
|
|
add_calibre_vars(base)
|
|
addsitedir(sys.site_packages)
|
|
|
|
launched_by_launch_services = False
|
|
|
|
for arg in tuple(sys.argv[1:]):
|
|
if arg.startswith('-psn_'):
|
|
sys.argv.remove(arg)
|
|
launched_by_launch_services = True
|
|
if launched_by_launch_services:
|
|
try:
|
|
setup_asl()
|
|
except:
|
|
pass # Failure to log to Console.app is not critical
|
|
|
|
return run_entry_point()
|