X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/ec873c943d71f0d5f13e3398557071448cda6c23..a4027e74873007e3430af3bd77019bcab76f6c04:/wxPython/distrib/mac/buildpkg.py?ds=inline diff --git a/wxPython/distrib/mac/buildpkg.py b/wxPython/distrib/mac/buildpkg.py deleted file mode 100644 index a9c70c111c..0000000000 --- a/wxPython/distrib/mac/buildpkg.py +++ /dev/null @@ -1,484 +0,0 @@ -#!/usr/bin/env python - -"""buildpkg.py -- Build OS X packages for Apple's Installer.app. - -This is an experimental command-line tool for building packages to be -installed with the Mac OS X Installer.app application. - -It is much inspired by Apple's GUI tool called PackageMaker.app, that -seems to be part of the OS X developer tools installed in the folder -/Developer/Applications. But apparently there are other free tools to -do the same thing which are also named PackageMaker like Brian Hill's -one: - - http://personalpages.tds.net/~brian_hill/packagemaker.html - -Beware of the multi-package features of Installer.app (which are not -yet supported here) that can potentially screw-up your installation -and are discussed in these articles on Stepwise: - - http://www.stepwise.com/Articles/Technical/Packages/InstallerWoes.html - http://www.stepwise.com/Articles/Technical/Packages/InstallerOnX.html - -Beside using the PackageMaker class directly, by importing it inside -another module, say, there are additional ways of using this module: -the top-level buildPackage() function provides a shortcut to the same -feature and is also called when using this module from the command- -line. - - **************************************************************** - NOTE: For now you should be able to run this even on a non-OS X - system and get something similar to a package, but without - the real archive (needs pax) and bom files (needs mkbom) - inside! This is only for providing a chance for testing to - folks without OS X. - **************************************************************** - -TODO: - - test pre-process and post-process scripts (Python ones?) - - handle multi-volume packages (?) - - integrate into distutils (?) - -Dinu C. Gherman, -gherman@europemail.com -November 2001 - -!! USE AT YOUR OWN RISK !! -""" - -__version__ = 0.2 -__license__ = "FreeBSD" - - -import os, sys, glob, fnmatch, shutil, string, copy, getopt -from os.path import basename, dirname, join, islink, isdir, isfile - -Error = "buildpkg.Error" - -PKG_INFO_FIELDS = """\ -Title -Version -Description -DefaultLocation -DeleteWarning -NeedsAuthorization -DisableStop -UseUserMask -Application -Relocatable -Required -InstallOnly -RequiresReboot -RootVolumeOnly -LongFilenames -LibrarySubdirectory -AllowBackRev -OverwritePermissions -InstallFat\ -""" - -###################################################################### -# Helpers -###################################################################### - -# Convenience class, as suggested by /F. - -class GlobDirectoryWalker: - "A forward iterator that traverses files in a directory tree." - - def __init__(self, directory, pattern="*"): - self.stack = [directory] - self.pattern = pattern - self.files = [] - self.index = 0 - - - def __getitem__(self, index): - while 1: - try: - file = self.files[self.index] - self.index = self.index + 1 - except IndexError: - # pop next directory from stack - self.directory = self.stack.pop() - self.files = os.listdir(self.directory) - self.index = 0 - else: - # got a filename - fullname = join(self.directory, file) - if isdir(fullname) and not islink(fullname): - self.stack.append(fullname) - if fnmatch.fnmatch(file, self.pattern): - return fullname - - -###################################################################### -# The real thing -###################################################################### - -class PackageMaker: - """A class to generate packages for Mac OS X. - - This is intended to create OS X packages (with extension .pkg) - containing archives of arbitrary files that the Installer.app - will be able to handle. - - As of now, PackageMaker instances need to be created with the - title, version and description of the package to be built. - The package is built after calling the instance method - build(root, **options). It has the same name as the constructor's - title argument plus a '.pkg' extension and is located in the same - parent folder that contains the root folder. - - E.g. this will create a package folder /my/space/distutils.pkg/: - - pm = PackageMaker("distutils", "1.0.2", "Python distutils.") - pm.build("/my/space/distutils") - """ - - packageInfoDefaults = { - 'Title': None, - 'Version': None, - 'Description': '', - 'DefaultLocation': '/', - 'DeleteWarning': '', - 'NeedsAuthorization': 'NO', - 'DisableStop': 'NO', - 'UseUserMask': 'YES', - 'Application': 'NO', - 'Relocatable': 'YES', - 'Required': 'NO', - 'InstallOnly': 'NO', - 'RequiresReboot': 'NO', - 'RootVolumeOnly' : 'NO', - 'InstallFat': 'NO', - 'LongFilenames': 'YES', - 'LibrarySubdirectory': 'Standard', - 'AllowBackRev': 'YES', - 'OverwritePermissions': 'NO', - } - - - def __init__(self, title, version, desc): - "Init. with mandatory title/version/description arguments." - - info = {"Title": title, "Version": version, "Description": desc} - self.packageInfo = copy.deepcopy(self.packageInfoDefaults) - self.packageInfo.update(info) - - # variables set later - self.packageRootFolder = None - self.packageResourceFolder = None - self.sourceFolder = None - self.resourceFolder = None - - - def build(self, root, resources=None, **options): - """Create a package for some given root folder. - - With no 'resources' argument set it is assumed to be the same - as the root directory. Option items replace the default ones - in the package info. - """ - - # set folder attributes - self.sourceFolder = root - if resources == None: - self.resourceFolder = root - else: - self.resourceFolder = resources - - # replace default option settings with user ones if provided - fields = self. packageInfoDefaults.keys() - for k, v in options.items(): - if k in fields: - self.packageInfo[k] = v - elif not k in ["OutputDir"]: - raise Error, "Unknown package option: %s" % k - - # Check where we should leave the output. Default is current directory - outputdir = options.get("OutputDir", os.getcwd()) - packageName = self.packageInfo["Title"] - self.PackageRootFolder = os.path.join(outputdir, packageName + ".pkg") - - # do what needs to be done - self._makeFolders() - self._addInfo() - self._addBom() - self._addArchive() - self._addResources() - self._addSizes() - self._addLoc() - - - def _makeFolders(self): - "Create package folder structure." - - # Not sure if the package name should contain the version or not... - # packageName = "%s-%s" % (self.packageInfo["Title"], - # self.packageInfo["Version"]) # ?? - - contFolder = join(self.PackageRootFolder, "Contents") - self.packageResourceFolder = join(contFolder, "Resources") - os.mkdir(self.PackageRootFolder) - os.mkdir(contFolder) - os.mkdir(self.packageResourceFolder) - - def _addInfo(self): - "Write .info file containing installing options." - - # Not sure if options in PKG_INFO_FIELDS are complete... - - info = "" - for f in string.split(PKG_INFO_FIELDS, "\n"): - if self.packageInfo.has_key(f): - info = info + "%s %%(%s)s\n" % (f, f) - info = info % self.packageInfo - base = self.packageInfo["Title"] + ".info" - path = join(self.packageResourceFolder, base) - f = open(path, "w") - f.write(info) - - - def _addBom(self): - "Write .bom file containing 'Bill of Materials'." - - # Currently ignores if the 'mkbom' tool is not available. - - try: - base = self.packageInfo["Title"] + ".bom" - bomPath = join(self.packageResourceFolder, base) - cmd = "mkbom %s %s" % (self.sourceFolder, bomPath) - res = os.system(cmd) - except: - pass - - - def _addArchive(self): - "Write .pax.gz file, a compressed archive using pax/gzip." - - # Currently ignores if the 'pax' tool is not available. - - cwd = os.getcwd() - - # create archive - os.chdir(self.sourceFolder) - base = basename(self.packageInfo["Title"]) + ".pax" - self.archPath = join(self.packageResourceFolder, base) - cmd = "pax -w -f %s %s" % (self.archPath, ".") - res = os.system(cmd) - - # compress archive - cmd = "gzip %s" % self.archPath - res = os.system(cmd) - os.chdir(cwd) - - - def _addResources(self): - "Add Welcome/ReadMe/License files, .lproj folders and scripts." - - # Currently we just copy everything that matches the allowed - # filenames. So, it's left to Installer.app to deal with the - # same file available in multiple formats... - - if not self.resourceFolder: - return - - # find candidate resource files (txt html rtf rtfd/ or lproj/) - allFiles = [] - for pat in string.split("*.txt *.html *.rtf *.rtfd *.lproj", " "): - pattern = join(self.resourceFolder, pat) - allFiles = allFiles + glob.glob(pattern) - - # find pre-process and post-process scripts - # naming convention: packageName.{pre,post}_{upgrade,install} - # Alternatively the filenames can be {pre,post}_{upgrade,install} - # in which case we prepend the package name - packageName = self.packageInfo["Title"] - for pat in ("*upgrade", "*install", "*flight"): - pattern = join(self.resourceFolder, packageName + pat) - pattern2 = join(self.resourceFolder, pat) - allFiles = allFiles + glob.glob(pattern) - allFiles = allFiles + glob.glob(pattern2) - - # check name patterns - files = [] - for f in allFiles: - for s in ("Welcome", "License", "ReadMe"): - if string.find(basename(f), s) == 0: - files.append((f, f)) - if f[-6:] == ".lproj": - files.append((f, f)) - elif basename(f) in ["pre_upgrade", "pre_install", "post_upgrade", "post_install"]: - files.append((f, packageName+"."+basename(f))) - elif basename(f) in ["preflight", "postflight"]: - files.append((f, f)) - elif f[-8:] == "_upgrade": - files.append((f,f)) - elif f[-8:] == "_install": - files.append((f,f)) - - # copy files - for src, dst in files: - src = basename(src) - dst = basename(dst) - f = join(self.resourceFolder, src) - if isfile(f): - shutil.copy(f, os.path.join(self.packageResourceFolder, dst)) - elif isdir(f): - # special case for .rtfd and .lproj folders... - d = join(self.packageResourceFolder, dst) - os.mkdir(d) - files = GlobDirectoryWalker(f) - for file in files: - shutil.copy(file, d) - - - def _addSizes(self): - "Write .sizes file with info about number and size of files." - - # Not sure if this is correct, but 'installedSize' and - # 'zippedSize' are now in Bytes. Maybe blocks are needed? - # Well, Installer.app doesn't seem to care anyway, saying - # the installation needs 100+ MB... - - numFiles = 0 - installedSize = 0 - zippedSize = 0 - - files = GlobDirectoryWalker(self.sourceFolder) - for f in files: - numFiles = numFiles + 1 - installedSize = installedSize + os.lstat(f)[6] - - try: - zippedSize = os.stat(self.archPath+ ".gz")[6] - except OSError: # ignore error - pass - base = self.packageInfo["Title"] + ".sizes" - f = open(join(self.packageResourceFolder, base), "w") - format = "NumFiles %d\nInstalledSize %d\nCompressedSize %d\n" - f.write(format % (numFiles, installedSize, zippedSize)) - - def _addLoc(self): - "Write .loc file." - base = self.packageInfo["Title"] + ".loc" - f = open(join(self.packageResourceFolder, base), "w") - f.write('/') - -# Shortcut function interface - -def buildPackage(*args, **options): - "A Shortcut function for building a package." - - o = options - title, version, desc = o["Title"], o["Version"], o["Description"] - pm = PackageMaker(title, version, desc) - apply(pm.build, list(args), options) - - -###################################################################### -# Tests -###################################################################### - -def test0(): - "Vanilla test for the distutils distribution." - - pm = PackageMaker("distutils2", "1.0.2", "Python distutils package.") - pm.build("/Users/dinu/Desktop/distutils2") - - -def test1(): - "Test for the reportlab distribution with modified options." - - pm = PackageMaker("reportlab", "1.10", - "ReportLab's Open Source PDF toolkit.") - pm.build(root="/Users/dinu/Desktop/reportlab", - DefaultLocation="/Applications/ReportLab", - Relocatable="YES") - -def test2(): - "Shortcut test for the reportlab distribution with modified options." - - buildPackage( - "/Users/dinu/Desktop/reportlab", - Title="reportlab", - Version="1.10", - Description="ReportLab's Open Source PDF toolkit.", - DefaultLocation="/Applications/ReportLab", - Relocatable="YES") - - -###################################################################### -# Command-line interface -###################################################################### - -def printUsage(): - "Print usage message." - - format = "Usage: %s [] []" - print format % basename(sys.argv[0]) - print - print " with arguments:" - print " (mandatory) root: the package root folder" - print " (optional) resources: the package resources folder" - print - print " and options:" - print " (mandatory) opts1:" - mandatoryKeys = string.split("Title Version Description", " ") - for k in mandatoryKeys: - print " --%s" % k - print " (optional) opts2: (with default values)" - - pmDefaults = PackageMaker.packageInfoDefaults - optionalKeys = pmDefaults.keys() - for k in mandatoryKeys: - optionalKeys.remove(k) - optionalKeys.sort() - maxKeyLen = max(map(len, optionalKeys)) - for k in optionalKeys: - format = " --%%s:%s %%s" - format = format % (" " * (maxKeyLen-len(k))) - print format % (k, repr(pmDefaults[k])) - - -def main(): - "Command-line interface." - - shortOpts = "" - keys = PackageMaker.packageInfoDefaults.keys() - longOpts = map(lambda k: k+"=", keys) - - try: - opts, args = getopt.getopt(sys.argv[1:], shortOpts, longOpts) - except getopt.GetoptError, details: - print details - printUsage() - return - - optsDict = {} - for k, v in opts: - optsDict[k[2:]] = v - - ok = optsDict.keys() - if not (1 <= len(args) <= 2): - print "No argument given!" - elif not ("Title" in ok and \ - "Version" in ok and \ - "Description" in ok): - print "Missing mandatory option!" - else: - apply(buildPackage, args, optsDict) - return - - printUsage() - - # sample use: - # buildpkg.py --Title=distutils \ - # --Version=1.0.2 \ - # --Description="Python distutils package." \ - # /Users/dinu/Desktop/distutils - - -if __name__ == "__main__": - main()