def makeMpkgPlist(path): vers = getFullVersion() major, minor = map(int, getVersion().split('.', 2)) pl = Plist( CFBundleGetInfoString="Python %s" % (vers, ), CFBundleIdentifier='org.python.Python', CFBundleName='Python', CFBundleShortVersionString=vers, IFMajorVersion=major, IFMinorVersion=minor, IFPkgFlagComponentDirectory="Contents/Packages", IFPkgFlagPackageList=[ dict(IFPkgFlagPackageLocation='%s-%s.pkg' % (item['name'], getVersion()), IFPkgFlagPackageSelection='selected') for item in pkg_recipes() ], IFPkgFormatVersion=0.10000000149011612, IFPkgFlagBackgroundScaling="proportional", IFPkgFlagBackgroundAlignment="left", IFPkgFlagAuthorizationAction="RootAuthorization", ) writePlist(pl, path)
def _writePlistInfo(self): """ Writes the Info.plist file in the Contests directory. """ pl = Plist( CFBundleExecutable=self.executable, CFBundleGetInfoString='%s-1.0.0' % self.name, CFBundleIconFile=basename(self.icns), CFBundleIdentifier='com.%s' % self.name, CFBundlePackageType='APPL', CFBundleVersion='1.0.0', CFBundleShortVersionString='1.0.0', ) writePlist(pl, join(self.contents_dir, 'Info.plist'))
def buildInstaller(): # Zap all compiled files for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')): for fn in filenames: if fn.endswith('.pyc') or fn.endswith('.pyo'): os.unlink(os.path.join(dirpath, fn)) outdir = os.path.join(WORKDIR, 'installer') if os.path.exists(outdir): shutil.rmtree(outdir) os.mkdir(outdir) pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents') pkgcontents = os.path.join(pkgroot, 'Packages') os.makedirs(pkgcontents) for recipe in pkg_recipes(): packageFromRecipe(pkgcontents, recipe) rsrcDir = os.path.join(pkgroot, 'Resources') fn = os.path.join(pkgroot, 'PkgInfo') fp = open(fn, 'w') fp.write('pmkrpkg1') fp.close() os.mkdir(rsrcDir) makeMpkgPlist(os.path.join(pkgroot, 'Info.plist')) pl = Plist( IFPkgDescriptionTitle="Python", IFPkgDescriptionVersion=getVersion(), ) writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist')) for fn in os.listdir('resources'): if fn == '.svn': continue if fn.endswith('.jpg'): shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn)) else: patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn)) shutil.copy("../../LICENSE", os.path.join(rsrcDir, 'License.txt'))
class BundleBuilder(Defaults): """BundleBuilder is a barebones class for assembling bundles. It knows nothing about executables or icons, it only copies files and creates the PkgInfo and Info.plist files. """ # (Note that Defaults.__init__ (deep)copies these values to # instance variables. Mutable defaults are therefore safe.) # Name of the bundle, with or without extension. name = None # The property list ("plist") plist = Plist(CFBundleDevelopmentRegion="English", CFBundleInfoDictionaryVersion="6.0") # The type of the bundle. type = "BNDL" # The creator code of the bundle. creator = None # the CFBundleIdentifier (this is used for the preferences file name) bundle_id = None # List of files that have to be copied to <bundle>/Contents/Resources. resources = [] # List of (src, dest) tuples; dest should be a path relative to the bundle # (eg. "Contents/Resources/MyStuff/SomeFile.ext). files = [] # List of shared libraries (dylibs, Frameworks) to bundle with the app # will be placed in Contents/Frameworks libs = [] # Directory where the bundle will be assembled. builddir = "build" # Make symlinks instead copying files. This is handy during debugging, but # makes the bundle non-distributable. symlink = 0 # Verbosity level. verbosity = 1 # Destination root directory destroot = "" def setup(self): # XXX rethink self.name munging, this is brittle. self.name, ext = os.path.splitext(self.name) if not ext: ext = ".bundle" bundleextension = ext # misc (derived) attributes self.bundlepath = pathjoin(self.builddir, self.name + bundleextension) plist = self.plist plist.CFBundleName = self.name plist.CFBundlePackageType = self.type if self.creator is None: if hasattr(plist, "CFBundleSignature"): self.creator = plist.CFBundleSignature else: self.creator = "????" plist.CFBundleSignature = self.creator if self.bundle_id: plist.CFBundleIdentifier = self.bundle_id elif not hasattr(plist, "CFBundleIdentifier"): plist.CFBundleIdentifier = self.name def build(self): """Build the bundle.""" builddir = self.builddir if builddir and not os.path.exists(builddir): os.mkdir(builddir) self.message("Building %s" % repr(self.bundlepath), 1) if os.path.exists(self.bundlepath): shutil.rmtree(self.bundlepath) if os.path.exists(self.bundlepath + '~'): shutil.rmtree(self.bundlepath + '~') bp = self.bundlepath # Create the app bundle in a temporary location and then # rename the completed bundle. This way the Finder will # never see an incomplete bundle (where it might pick up # and cache the wrong meta data) self.bundlepath = bp + '~' try: os.mkdir(self.bundlepath) self.preProcess() self._copyFiles() self._addMetaFiles() self.postProcess() os.rename(self.bundlepath, bp) finally: self.bundlepath = bp self.message("Done.", 1) def preProcess(self): """Hook for subclasses.""" pass def postProcess(self): """Hook for subclasses.""" pass def _addMetaFiles(self): contents = pathjoin(self.bundlepath, "Contents") makedirs(contents) # # Write Contents/PkgInfo assert len(self.type) == len(self.creator) == 4, \ "type and creator must be 4-byte strings." pkginfo = pathjoin(contents, "PkgInfo") f = open(pkginfo, "wb") f.write(self.type + self.creator) f.close() # # Write Contents/Info.plist infoplist = pathjoin(contents, "Info.plist") self.plist.write(infoplist) def _copyFiles(self): files = self.files[:] for path in self.resources: files.append( (path, pathjoin("Contents", "Resources", os.path.basename(path)))) for path in self.libs: files.append((path, pathjoin("Contents", "Frameworks", os.path.basename(path)))) if self.symlink: self.message("Making symbolic links", 1) msg = "Making symlink from" else: self.message("Copying files", 1) msg = "Copying" files.sort() for src, dst in files: if os.path.isdir(src): self.message("%s %s/ to %s/" % (msg, src, dst), 2) else: self.message("%s %s to %s" % (msg, src, dst), 2) dst = pathjoin(self.bundlepath, dst) if self.symlink: symlink(src, dst, mkdirs=1) else: copy(src, dst, mkdirs=1) def message(self, msg, level=0): if level <= self.verbosity: indent = "" if level > 1: indent = (level - 1) * " " sys.stderr.write(indent + msg + "\n") def report(self): # XXX something decent pass
from distutils.core import setup import py2app from plistlib import Plist import os name = 'ASDictionary' version = '0.11.0' os.system(''' sdp -fa %s.sdef; Rez %sScripting.r -o %s.rsrc -useDF ''' % ((name, ) * 3)) setup(app=[name + ".py"], data_files=["MainMenu.nib"], options=dict(py2app=dict(plist=Plist( NSAppleScriptEnabled=True, CFBundleVersion=version, CFBundleShortVersionString=version, NSHumanReadableCopyright="(C) 2005-2008 HAS", CFBundleIdentifier="net.sourceforge.appscript.asdictionary", CFBundleDocumentTypes=[ dict( CFBundleTypeExtensions=["*"], CFBundleTypeName="public.item", CFBundleTypeRole="Viewer", ), ]), resources=[name + '.icns', name + '.rsrc'], iconfile=name + '.icns')))
-- To build, cd to this directory and run: python setup.py py2app """ from distutils.core import setup, Extension import py2app from plistlib import Plist version = '0.4.0' setup(app=["ASTranslate.py"], data_files=["MainMenu.nib", "ASTranslateDocument.nib"], options=dict(py2app=dict(plist=Plist( NSAppleScriptEnabled=True, CFBundleIdentifier="net.sourceforge.appscript.astranslate", CFBundleVersion=version, CFBundleShortVersionString=version, NSHumanReadableCopyright="(C) 2007-2008 HAS", CFBundleDocumentTypes=[ dict(CFBundleTypeExtensions=[], CFBundleTypeName="Text File", CFBundleTypeRole="Editor", NSDocumentClass="ASTranslateDocument") ]), resources=['ASTranslate.icns'], iconfile='ASTranslate.icns')))
fullVersStr = __version__ shortVersStr = fullVersStr inclModules = ( # "FileDialog", ) # packages to include recursively inclPackages = ( # "RO", ) plist = Plist( CFBundleName=appName, CFBundleShortVersionString=shortVersStr, CFBundleGetInfoString="%s %s" % (appName, fullVersStr), CFBundleExecutable=appName, LSMinimumSystemVersion="10.6.0", LSArchitecturePriority=("i386", ) # force 32-bit mode; # this is needed for Tcl/TK 8.5.11 to run on MacOS X 10.9; # I'm stuck with 8.5.11 due to a crashing bug in Tcl/Tk 8.5.12 - 8.5.15.1 ) setup( app=[mainProg], setup_requires=["py2app"], options=dict(py2app=dict( plist=plist, # iconfile = iconFile, # includes = inclModules, # packages = inclPackages, )), )
import os from bundlebuilder import buildapp from plistlib import Plist, Dict plist = Plist( CFBundleDocumentTypes = [ Dict( CFBundleTypeExtensions = [ "py", "pyw" ], CFBundleTypeName = "Python File", CFBundleTypeRole = "Editor", NSDocumentClass = "PyDEPythonDocument", ), ], ) buildapp( name = "PyDE", mainprogram = "main.py", resources = [ os.path.join("Resources", x) for x in os.listdir("Resources") if x != 'CVS' ] + [ 'pythonfile.py', 'PyDETextView.py', 'PyFontify.py'], nibname = "MainMenu.nib", plist = plist, )
class BundleBuilder(Defaults): """BundleBuilder is a barebones class for assembling bundles. It knows nothing about executables or icons, it only copies files and creates the PkgInfo and Info.plist files. """ name = None plist = Plist(CFBundleDevelopmentRegion='English', CFBundleInfoDictionaryVersion='6.0') type = 'BNDL' creator = None bundle_id = None resources = [] files = [] libs = [] builddir = 'build' symlink = 0 verbosity = 1 destroot = '' def setup(self): self.name, ext = os.path.splitext(self.name) if not ext: ext = '.bundle' bundleextension = ext self.bundlepath = pathjoin(self.builddir, self.name + bundleextension) plist = self.plist plist.CFBundleName = self.name plist.CFBundlePackageType = self.type if self.creator is None: if hasattr(plist, 'CFBundleSignature'): self.creator = plist.CFBundleSignature else: self.creator = '????' plist.CFBundleSignature = self.creator if self.bundle_id: plist.CFBundleIdentifier = self.bundle_id elif not hasattr(plist, 'CFBundleIdentifier'): plist.CFBundleIdentifier = self.name return def build(self): """Build the bundle.""" builddir = self.builddir if builddir and not os.path.exists(builddir): os.mkdir(builddir) self.message('Building %s' % repr(self.bundlepath), 1) if os.path.exists(self.bundlepath): shutil.rmtree(self.bundlepath) if os.path.exists(self.bundlepath + '~'): shutil.rmtree(self.bundlepath + '~') bp = self.bundlepath self.bundlepath = bp + '~' try: os.mkdir(self.bundlepath) self.preProcess() self._copyFiles() self._addMetaFiles() self.postProcess() os.rename(self.bundlepath, bp) finally: self.bundlepath = bp self.message('Done.', 1) def preProcess(self): """Hook for subclasses.""" pass def postProcess(self): """Hook for subclasses.""" pass def _addMetaFiles(self): contents = pathjoin(self.bundlepath, 'Contents') makedirs(contents) raise len(self.type) == len(self.creator) == 4 or AssertionError('type and creator must be 4-byte strings.') pkginfo = pathjoin(contents, 'PkgInfo') f = open(pkginfo, 'wb') f.write(self.type + self.creator) f.close() infoplist = pathjoin(contents, 'Info.plist') self.plist.write(infoplist) def _copyFiles(self): files = self.files[:] for path in self.resources: files.append((path, pathjoin('Contents', 'Resources', os.path.basename(path)))) for path in self.libs: files.append((path, pathjoin('Contents', 'Frameworks', os.path.basename(path)))) if self.symlink: self.message('Making symbolic links', 1) msg = 'Making symlink from' else: self.message('Copying files', 1) msg = 'Copying' files.sort() for src, dst in files: if os.path.isdir(src): self.message('%s %s/ to %s/' % (msg, src, dst), 2) else: self.message('%s %s to %s' % (msg, src, dst), 2) dst = pathjoin(self.bundlepath, dst) if self.symlink: symlink(src, dst, mkdirs=1) else: copy(src, dst, mkdirs=1) def message(self, msg, level = 0): if level <= self.verbosity: indent = '' if level > 1: indent = (level - 1) * ' ' sys.stderr.write(indent + msg + '\n') def report(self): pass
from bundlebuilder import buildapp from plistlib import Plist, Dict plist = Plist(CFBundleDocumentTypes=[ Dict( CFBundleTypeExtensions=["xml", "xml.gz", "*"], CFBundleTypeName="XML File", CFBundleTypeRole="Editor", NSDocumentClass="ImageDocument", ), Dict( CFBundleTypeExtensions=["dwg"], CFBundleTypeName="DWG File", CFBundleTypeRole="Viewer", NSDocumentClass="ImageDocument", ), ]) buildapp( mainprogram="PythonCad.py", resources=[ "PythonCAD/Interface/Cocoa/MainMenu.nib", "PythonCAD/Interface/Cocoa/ImageDocument.nib", "PythonCAD", "prefs.py" ], nibname="MainMenu", plist=plist, )
shortVersStr = fullVersStr.split(None, 1)[0] inclModules = ("FileDialog", ) # packages to include recursively inclPackages = ( "TUI", "RO", "matplotlib", # py2app already does this, but it doesn't hurt to insist ) plist = Plist( CFBundleName=appName, CFBundleShortVersionString=shortVersStr, CFBundleGetInfoString="%s %s" % (appName, fullVersStr), CFBundleExecutable=appName, LSMinimumSystemVersion="10.6.0", # LSArchitecturePriority = ("i386",) # force 32-bit mode; # this is needed for Tcl/TK 8.5.11 to run on MacOS X 10.9; # I'm stuck with 8.5.11 due to a crashing bug in Tcl/Tk 8.5.12 - 8.5.15.1 # 8.5.16 has a nasty regression in http that prevents downloading images # 8.5.17 is a possibility; I'm trying a release candidate as I write this ) setup( app=[mainProg], setup_requires=["py2app"], options=dict(py2app=dict( plist=plist, iconfile=iconFile, includes=inclModules, packages=inclPackages, )),
def write(dct, path): p = Plist() p.update(dct) p.write(path)
from setuptools import setup from plistlib import Plist setup( name='BasicApp', app=['main.py'], options = dict(py2app=dict( plist = Plist( CFBundleName = "SimpleApp", CFBundleShortVersionString = "1.0", CFBudleGetInfoString = "SimpleApp 1.0", ), iconfile = "main.icns", resources = "data3/source.c", )), data_files = [ ( 'sub1', [ 'data1/file1.txt', 'data1/file2.txt', 'data1/file3.sh' ]), 'data2' ] )
Extension('astranslate', sources=['astranslate.c'], extra_compile_args=['-DMAC_OS_X_VERSION_MIN_REQUIRED=MAC_OS_X_VERSION_10_4'], extra_link_args=['-framework', 'Carbon'], ), ], options=dict( py2app=dict( plist=Plist( CFBundleIdentifier="net.sourceforge.appscript.astranslate", CFBundleVersion=version, CFBundleShortVersionString=version, NSHumanReadableCopyright="", CFBundleDocumentTypes = [ dict( CFBundleTypeExtensions=[], CFBundleTypeName="Text File", CFBundleTypeRole="Editor", NSDocumentClass="ASTranslateDocument" ) ] ), resources=['ASTranslate.icns'], iconfile='ASTranslate.icns' ) ) )
sdpPath = 'sdp' # Tiger RezPath = '/Developer/Tools/Rez' ####### os.system(''' %r -fa %s.sdef; %r %sScripting.r -o %s.rsrc -useDF ''' % (sdpPath, name, RezPath, name, name)) setup( name= name, version=version, app=[name + '.py'], options={ 'py2app': { 'plist': Plist( LSUIElement=int(hide), NSAppleScriptEnabled=True, #CFBundleIdentifier=url, CFBundleVersion=version, CFBundleShortVersionString=version, CFBundleIconFile=name + '.icns' ), 'resources': [name + '.rsrc', name + '.icns'] } } )
def packageFromRecipe(targetDir, recipe): curdir = os.getcwd() try: # The major version (such as 2.5) is included in the package name # because having two version of python installed at the same time is # common. pkgname = '%s-%s' % (recipe['name'], getVersion()) srcdir = recipe.get('source') pkgroot = recipe.get('topdir', srcdir) postflight = recipe.get('postflight') readme = textwrap.dedent(recipe['readme']) isRequired = recipe.get('required', True) print "- building package %s" % (pkgname, ) # Substitute some variables textvars = dict( VER=getVersion(), FULLVER=getFullVersion(), ) readme = readme % textvars if pkgroot is not None: pkgroot = pkgroot % textvars else: pkgroot = '/' if srcdir is not None: srcdir = os.path.join(WORKDIR, '_root', srcdir[1:]) srcdir = srcdir % textvars if postflight is not None: postflight = os.path.abspath(postflight) packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents') os.makedirs(packageContents) if srcdir is not None: os.chdir(srcdir) runCommand( "pax -wf %s . 2>&1" % (shellQuote(os.path.join(packageContents, 'Archive.pax')), )) runCommand( "gzip -9 %s 2>&1" % (shellQuote(os.path.join(packageContents, 'Archive.pax')), )) runCommand( "mkbom . %s 2>&1" % (shellQuote(os.path.join(packageContents, 'Archive.bom')), )) fn = os.path.join(packageContents, 'PkgInfo') fp = open(fn, 'w') fp.write('pmkrpkg1') fp.close() rsrcDir = os.path.join(packageContents, "Resources") os.mkdir(rsrcDir) fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w') fp.write(readme) fp.close() if postflight is not None: patchScript(postflight, os.path.join(rsrcDir, 'postflight')) vers = getFullVersion() major, minor = map(int, getVersion().split('.', 2)) pl = Plist( CFBundleGetInfoString="Python.%s %s" % ( pkgname, vers, ), CFBundleIdentifier='org.python.Python.%s' % (pkgname, ), CFBundleName='Python.%s' % (pkgname, ), CFBundleShortVersionString=vers, IFMajorVersion=major, IFMinorVersion=minor, IFPkgFormatVersion=0.10000000149011612, IFPkgFlagAllowBackRev=False, IFPkgFlagAuthorizationAction="RootAuthorization", IFPkgFlagDefaultLocation=pkgroot, IFPkgFlagFollowLinks=True, IFPkgFlagInstallFat=True, IFPkgFlagIsRequired=isRequired, IFPkgFlagOverwritePermissions=False, IFPkgFlagRelocatable=False, IFPkgFlagRestartAction="NoRestart", IFPkgFlagRootVolumeOnly=True, IFPkgFlagUpdateInstalledLangauges=False, ) writePlist(pl, os.path.join(packageContents, 'Info.plist')) pl = Plist( IFPkgDescriptionDescription=readme, IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s" % (pkgname, )), IFPkgDescriptionVersion=vers, ) writePlist( pl, os.path.join(packageContents, 'Resources', 'Description.plist')) finally: os.chdir(curdir)
s = "%s %sScripting.r -o %s.rsrc -useDF;" % (RezPath, name, name) print s os.system(s) ####### setup(name=name, version=version, app=[name + '.py'], options={ 'py2app': { 'plist': Plist(LSUIElement=int(hide), NSAppleScriptEnabled=True, CFBundleIdentifier=bundle, CFBundleName=name, CFBundleDisplayName=name, CFBundleVersion=version, CFBundleShortVersionString=version, CFBundleSignature=creator, CFBundlePackageType="APPL", CFBundleIconFile=name + '.icns', NSHumanReadableCopyright=shortCopyright, CFBundleGetInfoString=longCopyright), 'iconfile': './+icons/appicon.icns', 'resources': [name + '.rsrc', './+icons/appicon.icns'] } })
from distutils.core import setup import py2app from plistlib import Plist setup( app=['ASDictionary.py'], options={ 'py2app': { 'argv_emulation': True, 'plist': Plist(CFBundleVersion='0.6.2', CFBundleShortVersionString='0.6.2'), 'resources': ['ASDictionary.icns'], 'iconfile': 'ASDictionary.icns', } }, ) # kludge to copy dialogs.rsrc into application bundle as py2app doesn't do it import EasyDialogs from shutil import copy from os.path import split, join rsrc = join(split(EasyDialogs.__file__)[0], 'dialogs.rsrc') rdst = join(split(__file__)[0], 'dist/ASDictionary.app/Contents/Resources/') copy(rsrc, rdst)