symbian-qemu-0.9.1-12/python-2.6.1/Mac/scripts/buildpkg.py
changeset 1 2fb8b9db1c86
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/symbian-qemu-0.9.1-12/python-2.6.1/Mac/scripts/buildpkg.py	Fri Jul 31 15:01:17 2009 +0100
@@ -0,0 +1,484 @@
+#!/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 is 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 <opts1> [<opts2>] <root> [<resources>]"
+    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()