1#!/usr/bin/env python 2# coding=utf-8 3 4""" 5A setup file to build Frescobaldi.app with py2app. 6 7Use the '-h' flag to see the usage notes. 8""" 9 10 11import argparse 12import os 13import sys 14from setuptools import setup 15import shutil 16from subprocess import Popen 17 18# Python 2 text strings: basestring = str (ASCII) + unicode (Unicode) 19# Python 3 text strings: str (Unicode) 20# See https://docs.python.org/3/howto/pyporting.html for details. 21try: 22 string_types = basestring 23except NameError: 24 string_types = str 25 26macosx = os.path.realpath(os.path.dirname(__file__)) 27root = os.path.dirname(macosx) 28 29sys.path.insert(0, root) 30 31from frescobaldi_app import toplevel 32toplevel.install() 33import appinfo 34try: 35 from portmidi import pm_ctypes 36 dylib_name = pm_ctypes.dll_name 37except ImportError: 38 dylib_name = None 39 40icon = '{0}/icons/{1}.icns'.format(macosx, appinfo.name) 41ipstrings = '{0}/app_resources/InfoPlist.strings'.format(macosx) 42 43parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) 44parser.add_argument('-f', '--force', action = 'store_true', \ 45 help = 'force execution even if SCRIPT does not exist') 46parser.add_argument('-v', '--version', \ 47 help = 'version string for the application bundle, \ 48 visible e.g. in \'Get Info\' and in \'Open with...\'', default = appinfo.version) 49parser.add_argument('-s', '--script', \ 50 help = 'path of {0}\'s main script; you should use an absolute path, \ 51 so that the application bundle can be moved to another \ 52 directory'.format(appinfo.appname), default = '{0}/{1}'.format(root, appinfo.name)) 53parser.add_argument('-a', '--standalone', action = 'store_true', \ 54 help = 'build a standalone application bundle \ 55 (WARNING: some manual steps are required after the execution of this script)') 56parser.add_argument('-p', '--portmidi', \ 57 help = 'full path of PortMIDI library (used only with \'-a\')', \ 58 default = dylib_name) 59parser.add_argument('-r', '--arch', \ 60 help = 'architecture set to include, e.g. i386, x86_64, intel; \ 61 if the value is None, the architecture of the current Python binary is used \ 62 (used only with \'-a\')') 63args = parser.parse_args() 64 65if not (os.path.isfile(args.script) or args.force): 66 sys.exit('Error: \'{0}\' does not exist or is not a file.\n\ 67If you really want to point the application bundle to \'{0}\',\n\ 68use the \'-f\' or \'--force\' flag.'.format(args.script)) 69 70if args.standalone and not (isinstance(args.portmidi, string_types) and os.path.isfile(args.portmidi)): 71 sys.exit('Error: \'{0}\' does not exist or is not a file.'.format(args.portmidi)) 72 73plist = dict( 74 CFBundleName = appinfo.appname, 75 CFBundleDisplayName = appinfo.appname, 76 CFBundleShortVersionString = args.version, 77 CFBundleVersion = args.version, 78 CFBundleExecutable = appinfo.appname, 79 CFBundleIdentifier = 'org.{0}.{0}'.format(appinfo.name), 80 CFBundleIconFile = '{0}.icns'.format(appinfo.name), 81 NSHumanReadableCopyright = u'Copyright © 2008-2014 Wilbert Berendsen.', 82 CFBundleDocumentTypes = [ 83 { 84 'CFBundleTypeExtensions': ['ly', 'lyi', 'ily'], 85 'CFBundleTypeName': 'LilyPond file', 86 'CFBundleTypeRole': 'Editor', 87 }, 88 { 89 'CFBundleTypeExtensions': ['tex', 'lytex', 'latex'], 90 'CFBundleTypeName': 'LaTeX file', 91 'CFBundleTypeRole': 'Editor', 92 }, 93 { 94 'CFBundleTypeExtensions': ['docbook', 'lyxml'], 95 'CFBundleTypeName': 'DocBook file', 96 'CFBundleTypeRole': 'Editor', 97 }, 98 { 99 'CFBundleTypeExtensions': ['html'], 100 'CFBundleTypeName': 'HTML file', 101 'CFBundleTypeRole': 'Editor', 102 'LSItemContentTypes': ['public.html'] 103 }, 104 { 105 'CFBundleTypeExtensions': ['xml'], 106 'CFBundleTypeName': 'XML file', 107 'CFBundleTypeRole': 'Editor', 108 'LSItemContentTypes': ['public.xml'] 109 }, 110 { 111 'CFBundleTypeExtensions': ['itely', 'tely', 'texi', 'texinfo'], 112 'CFBundleTypeName': 'Texinfo file', 113 'CFBundleTypeRole': 'Editor', 114 }, 115 { 116 'CFBundleTypeExtensions': ['scm'], 117 'CFBundleTypeName': 'Scheme file', 118 'CFBundleTypeRole': 'Editor', 119 }, 120 { 121 'CFBundleTypeExtensions': ['*'], 122 'CFBundleTypeName': 'Text file', 123 'CFBundleTypeRole': 'Editor', 124 'LSItemContentTypes': ['public.text'] 125 } 126 ] 127) 128 129options = { 130 'argv_emulation': True, 131 'plist': plist 132} 133 134if args.standalone: 135 options.update({ 136 'packages': ['frescobaldi_app'], 137 'frameworks': [args.portmidi], 138 }) 139 if args.arch: 140 options.update({ 141 'arch': args.arch 142 }) 143 try: 144 patchlist = [f for f in os.listdir('patch') if f.endswith(".diff")] 145 except OSError: 146 patchlist = [] 147 for patchfile in patchlist: 148 with open('patch/{0}'.format(patchfile), 'r') as input: 149 Popen(["patch", "-d..", "-p0"], stdin=input) 150else: 151 options.update({ 152 'semi_standalone': True, 153 'alias': True 154 }) 155 156setup( 157 app = [args.script], 158 name = appinfo.appname, 159 options = {'py2app': options}, 160 setup_requires = ['py2app'], 161 script_args = ['py2app'] 162) 163 164app_resources = 'dist/{0}.app/Contents/Resources'.format(appinfo.appname) 165icon_dest = '{0}/{1}.icns'.format(app_resources, appinfo.name) 166print('copying file {0} -> {1}'.format(icon, icon_dest)) 167shutil.copyfile(icon, icon_dest) 168os.chmod(icon_dest, 0o0644) 169locales = ['cs', 'de', 'en', 'es', 'fr', 'gl', 'it', 'nl', 'pl', 'pt', 'ru', 'tr', 'uk', 'zh_CN', 'zh_HK', 'zh_TW'] 170for l in locales: 171 app_lproj = '{0}/{1}.lproj'.format(app_resources, l) 172 os.mkdir(app_lproj, 0o0755) 173 ipstrings_dest = '{0}/InfoPlist.strings'.format(app_lproj) 174 print('copying file {0} -> {1}'.format(ipstrings, ipstrings_dest)) 175 shutil.copyfile(ipstrings, ipstrings_dest) 176 os.chmod(ipstrings_dest, 0o0644) 177 178if args.standalone: 179 if patchlist: 180 print('reversing patches:') 181 for patchfile in patchlist: 182 with open('patch/{0}'.format(patchfile), 'r') as input: 183 Popen(["patch", "-R", "-d..", "-p0"], stdin=input) 184 print('removing file {0}/qt.conf'.format(app_resources)) 185 os.remove('{0}/qt.conf'.format(app_resources)) 186 imageformats_dest = 'dist/{0}.app/Contents/PlugIns/imageformats'.format(appinfo.appname) 187 print('creating directory {0}'.format(imageformats_dest)) 188 os.makedirs(imageformats_dest, 0o0755) 189 print(""" 190WARNING: To complete the creation of the standalone application bundle \ 191you need to perform the following steps manually: 192 193- copy libqsvg.dylib from Qt's 'plugins/imageformats' directory to '{1}', 194- execute Qt's macdeployqt tool on dist/{0}.app \ 195(you can safely ignore the error about the failed copy of libqsvg.dylib). 196""".format(appinfo.appname, imageformats_dest)) 197