mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
246 lines
9.8 KiB
Python
246 lines
9.8 KiB
Python
#!/usr/bin/env python
|
|
## Copyright (C) 2006 Kovid Goyal kovid@kovidgoyal.net
|
|
## This program is free software; you can redistribute it and/or modify
|
|
## it under the terms of the GNU General Public License as published by
|
|
## the Free Software Foundation; either version 2 of the License, or
|
|
## (at your option) any later version.
|
|
##
|
|
## This program is distributed in the hope that it will be useful,
|
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
## GNU General Public License for more details.
|
|
##
|
|
## You should have received a copy of the GNU General Public License along
|
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
''' Create an OSX installer '''
|
|
|
|
import sys, re, os, shutil, subprocess, stat
|
|
from setup import VERSION, APPNAME, scripts, main_modules, basenames, main_functions
|
|
from setuptools import setup
|
|
sys.argv[1:2] = ['py2app']
|
|
from py2app.build_app import py2app
|
|
from modulegraph.find_modules import find_modules
|
|
|
|
class BuildAPP(py2app):
|
|
QT_PREFIX = '/Users/kovid/qt'
|
|
LOADER_TEMPLATE = \
|
|
r'''#!/usr/bin/env python
|
|
import os, sys, glob
|
|
path = os.path.abspath(os.path.realpath(__file__))
|
|
dirpath = os.path.dirname(path)
|
|
name = os.path.basename(path)
|
|
base_dir = os.path.dirname(dirpath)
|
|
frameworks_dir = os.path.join(base_dir, 'Frameworks')
|
|
base_name = os.path.splitext(name)[0]
|
|
python = os.path.join(base_dir, 'MacOS', 'python')
|
|
loader_path = os.path.join(dirpath, base_name+'.py')
|
|
loader = open(loader_path, 'w')
|
|
site_packages = glob.glob(dirpath+'/*/*/site-packages.zip')[0]
|
|
print >>loader, '#!'+python
|
|
print >>loader, 'import sys'
|
|
print >>loader, 'sys.path.append(', repr(site_packages), ')'
|
|
print >>loader, 'sys.frozen = "macosx_app"'
|
|
print >>loader, 'sys.frameworks_dir =', repr(frameworks_dir)
|
|
print >>loader, 'import os'
|
|
print >>loader, 'base =', repr(dirpath)
|
|
print >>loader, 'from %(module)s import %(function)s'
|
|
print >>loader, '%(function)s()'
|
|
loader.close()
|
|
os.chmod(loader_path, 0700)
|
|
os.environ['PYTHONHOME'] = dirpath
|
|
os.execv(loader_path, sys.argv)
|
|
'''
|
|
CHECK_SYMLINKS_PRESCRIPT = \
|
|
r'''
|
|
def _check_symlinks_prescript():
|
|
import os, tempfile, traceback, sys
|
|
from Authorization import Authorization, kAuthorizationFlagDestroyRights
|
|
|
|
AUTHTOOL="""#!%(sp)s
|
|
import os
|
|
scripts = %(sp)s
|
|
links = %(sp)s
|
|
os.setuid(0)
|
|
for s, l in zip(scripts, links):
|
|
if os.path.lexists(l):
|
|
os.remove(l)
|
|
print 'Creating link:', l, '->', s
|
|
os.symlink(s, l)
|
|
"""
|
|
|
|
dest_path = %(dest_path)s
|
|
resources_path = os.environ['RESOURCEPATH']
|
|
scripts = %(scripts)s
|
|
links = [os.path.join(dest_path, i) for i in scripts]
|
|
scripts = [os.path.join(resources_path, i) for i in scripts]
|
|
|
|
bad = False
|
|
for s, l in zip(scripts, links):
|
|
if os.path.exists(l) and os.path.exists(os.path.realpath(l)):
|
|
continue
|
|
bad = True
|
|
break
|
|
if bad:
|
|
auth = Authorization(destroyflags=(kAuthorizationFlagDestroyRights,))
|
|
fd, name = tempfile.mkstemp('.py')
|
|
os.write(fd, AUTHTOOL %(pp)s (sys.executable, repr(scripts), repr(links)))
|
|
os.close(fd)
|
|
os.chmod(name, 0700)
|
|
try:
|
|
pipe = auth.executeWithPrivileges(name)
|
|
sys.stdout.write(pipe.read())
|
|
pipe.close()
|
|
except:
|
|
traceback.print_exc()
|
|
finally:
|
|
os.unlink(name)
|
|
_check_symlinks_prescript()
|
|
'''
|
|
def get_modulefinder(self):
|
|
if self.debug_modulegraph:
|
|
debug = 4
|
|
else:
|
|
debug = 0
|
|
return find_modules(
|
|
scripts=scripts['console'] + scripts['gui'],
|
|
includes=list(self.includes) + main_modules['console'],
|
|
packages=self.packages,
|
|
excludes=self.excludes,
|
|
debug=debug,
|
|
)
|
|
|
|
@classmethod
|
|
def makedmg(cls, d, volname,
|
|
destdir='dist',
|
|
internet_enable=True,
|
|
format='UDBZ'):
|
|
''' Copy a directory d into a dmg named volname '''
|
|
dmg = os.path.join(destdir, volname+'.dmg')
|
|
if os.path.exists(dmg):
|
|
os.unlink(dmg)
|
|
subprocess.check_call(['hdiutil', 'create', '-srcfolder', os.path.abspath(d),
|
|
'-volname', volname, '-format', format, dmg])
|
|
if internet_enable:
|
|
subprocess.check_call(['hdiutil', 'internet-enable', '-yes', dmg])
|
|
return dmg
|
|
|
|
@classmethod
|
|
def qt_dependencies(cls, path):
|
|
pipe = subprocess.Popen('otool -L '+path, shell=True, stdout=subprocess.PIPE).stdout
|
|
deps = []
|
|
for l in pipe.readlines():
|
|
match = re.search(r'(.*)\(', l)
|
|
if not match:
|
|
continue
|
|
lib = match.group(1).strip()
|
|
if lib.startswith(BuildAPP.QT_PREFIX):
|
|
deps.append(lib)
|
|
return deps
|
|
|
|
@classmethod
|
|
def fix_qt_dependencies(cls, path, deps):
|
|
fp = '@executable_path/../Frameworks/'
|
|
print 'Fixing qt dependencies for:', os.path.basename(path)
|
|
for dep in deps:
|
|
module = re.search(r'(Qt\w+?)\.framework', dep).group(1)
|
|
newpath = fp + '%s.framework/Versions/Current/%s'%(module, module)
|
|
cmd = ' '.join(['install_name_tool', '-change', dep, newpath, path])
|
|
subprocess.check_call(cmd, shell=True)
|
|
|
|
|
|
def add_qt_plugins(self):
|
|
macos_dir = os.path.join(self.dist_dir, APPNAME + '.app', 'Contents', 'MacOS')
|
|
for root, dirs, files in os.walk(BuildAPP.QT_PREFIX+'/plugins'):
|
|
for name in files:
|
|
if name.endswith('.dylib'):
|
|
path = os.path.join(root, name)
|
|
dir = os.path.basename(root)
|
|
dest_dir = os.path.join(macos_dir, dir)
|
|
if not os.path.exists(dest_dir):
|
|
os.mkdir(dest_dir)
|
|
target = os.path.join(dest_dir, name)
|
|
shutil.copyfile(path, target)
|
|
shutil.copymode(path, target)
|
|
deps = BuildAPP.qt_dependencies(target)
|
|
BuildAPP.fix_qt_dependencies(target, deps)
|
|
|
|
|
|
#deps = BuildAPP.qt_dependencies(path)
|
|
|
|
|
|
def run(self):
|
|
py2app.run(self)
|
|
self.add_qt_plugins()
|
|
resource_dir = os.path.join(self.dist_dir,
|
|
APPNAME + '.app', 'Contents', 'Resources')
|
|
frameworks_dir = os.path.join(os.path.dirname(resource_dir), 'Frameworks')
|
|
all_scripts = scripts['console'] + scripts['gui']
|
|
all_names = basenames['console'] + basenames['gui']
|
|
all_modules = main_modules['console'] + main_modules['gui']
|
|
all_functions = main_functions['console'] + main_functions['gui']
|
|
print
|
|
for name, module, function in zip(all_names, all_modules, all_functions):
|
|
path = os.path.join(resource_dir, name)
|
|
print 'Creating loader:', path
|
|
f = open(path, 'w')
|
|
f.write(BuildAPP.LOADER_TEMPLATE % dict(module=module,
|
|
function=function))
|
|
f.close()
|
|
os.chmod(path, stat.S_IXUSR|stat.S_IXGRP|stat.S_IXOTH|stat.S_IREAD\
|
|
|stat.S_IWUSR|stat.S_IROTH|stat.S_IRGRP)
|
|
|
|
print
|
|
print 'Adding clit'
|
|
os.link(os.path.expanduser('~/clit'), os.path.join(frameworks_dir, 'clit'))
|
|
print
|
|
print 'Installing prescipt'
|
|
sf = [os.path.basename(s) for s in all_names]
|
|
cs = BuildAPP.CHECK_SYMLINKS_PRESCRIPT % dict(dest_path=repr('/usr/bin'),
|
|
scripts=repr(sf),
|
|
sp='%s', pp='%')
|
|
launcher_path = os.path.join(resource_dir, '__boot__.py')
|
|
f = open(launcher_path, 'r')
|
|
src = f.read()
|
|
f.close()
|
|
src = re.sub('(_run\s*\(.*?.py.*?\))', cs+'%s'%(
|
|
'''
|
|
sys.frameworks_dir = os.path.join(os.path.dirname(os.environ['RESOURCEPATH']), 'Frameworks')
|
|
''') + r'\n\1', src)
|
|
f = open(launcher_path, 'w')
|
|
print >>f, 'import sys, os'
|
|
f.write(src)
|
|
f.close()
|
|
print
|
|
print 'Building disk image'
|
|
BuildAPP.makedmg(os.path.join(self.dist_dir, APPNAME+'.app'), APPNAME+'-'+VERSION)
|
|
|
|
|
|
setup(
|
|
name = APPNAME,
|
|
app = [scripts['gui'][0]],
|
|
cmdclass = { 'py2app' : BuildAPP },
|
|
options = { 'py2app' :
|
|
{
|
|
'optimize' : 2,
|
|
'dist_dir' : 'build/py2app',
|
|
'argv_emulation' : True,
|
|
'iconfile' : 'icons/library.icns',
|
|
'frameworks': ['libusb.dylib', 'libunrar.dylib'],
|
|
'includes' : ['sip', 'pkg_resources', 'PyQt4.QtSvg'],
|
|
'packages' : ['PIL', 'Authorization',],
|
|
'excludes' : ['pydoc'],
|
|
'plist' : { 'CFBundleGetInfoString' : '''libprs500, an E-book management application.'''
|
|
''' Visit http://libprs500.kovidgoyal.net for details.''',
|
|
'CFBundleIdentifier':'net.kovidgoyal.librs500',
|
|
'CFBundleShortVersionString':VERSION,
|
|
'CFBundleVersion':APPNAME + ' ' + VERSION,
|
|
'LSMinimumSystemVersion':'10.4.3',
|
|
'LSMultipleInstancesProhibited':'true',
|
|
'NSHumanReadableCopyright':'Copyright 2006, Kovid Goyal',
|
|
},
|
|
},
|
|
},
|
|
setup_requires = ['py2app'],
|
|
)
|