Commits

illume committed f14da30

Imported from svn by Bitbucket

Comments (0)

Files changed (12)

bdist_mpkg/__init__.py

+__version__ = '0.4.3'

bdist_mpkg/cmd_bdist_mpkg.py

+"""bdist_mpkg.cmd_bdist_mpkg
+
+Implements the Distutils 'bdist_mpkg' command (create an OS X "mpkg"
+binary distribution)."""
+
+import os
+import sys
+import zipfile
+
+from setuptools import Command
+
+from distutils.util import get_platform, byte_compile
+from distutils.dir_util import remove_tree, mkpath
+from distutils.errors import *
+from distutils import log
+
+from bdist_mpkg import pkg, tools, plists
+from bdist_mpkg.util import copy_tree
+
+INSTALL_SCHEME_DESCRIPTIONS = dict(
+    purelib = u'(Required) Pure Python modules and packages',
+    platlib = u'(Required) Python modules, extensions, and packages',
+    headers = u'(Optional) Header files for development',
+    scripts = u'(Optional) Scripts to use from the Unix shell',
+    data    = u'(Optional) Additional data files (sometimes documentation)',
+)
+
+class bdist_mpkg (Command):
+
+    description = "create a Mac OS X mpkg distribution for Installer.app"
+
+    user_options = [
+        ('pkg-base=', None,
+         "base directory for creating pkg (defaults to \"mpkg\" "
+         "under --bdist-base"),
+        ('bdist-base=', None,
+         "bdist base directory"),
+        ('build-base=', None,
+         "build base directory"),
+        ('optimize=', 'O',
+         "also compile with optimization: -O1 for \"python -O\", "
+         "-O2 for \"python -OO\", and -O0 to disable [default: -O2]"),
+        ('component-directory=', None,
+         "component directory for packages relative to the mpkg "
+         "(defaults to ./Contents/Packages)"),
+        ('keep-temp', 'k',
+         "keep the pseudo-installation tree around after creating "
+         "the distribution archive"),
+        ('open', None,
+         'Open with Installer.app after building'),
+        ('readme=', None,
+         'readme text file to be used in pkg (defaults to ReadMe)'),
+        ('license=', None,
+         'license text file to be used in pkg (defaults to License or COPYING)'),
+        ('welcome=', None,
+         'welcome text file to be used in pkg (defaults to Welcome)'),
+        ('background=', None,
+         'background image to be used in pkg (defaults to background)'),
+        ('dist-dir=', 'd',
+         "directory to put final built pkg distributions in"),
+        ('zipdist', 'z',
+         "build a zip containing the package in dist"),
+        ('skip-build', None,
+         "skip rebuilding everything (for testing/debugging)"),
+    ]
+
+    boolean_options = ['skip-build', 'keep-temp', 'open', 'zipdist']
+
+    def initialize_options (self):
+        self.skip_build = False
+        self.keep_temp = False
+        self.open = False
+        self.template = None
+        self.readme = None
+        self.license = None
+        self.welcome = None
+        self.background = None
+        self.bdist_base = None
+        self.build_base = None
+        self.pkg_base = None
+        self.dist_dir = None
+        self.plat_name = None
+        self.zipdist = False
+        self.optimize = 2
+        self.component_directory = './Contents/Packages'
+        self.scheme_command = {}
+        self.scheme_map = {}
+        self.packages = []
+        self.scheme_descriptions = {}
+        self.scheme_root = {}
+        self.scheme_copy = {}
+        self.scheme_subprojects = {}
+        self.command_schemes = None
+        self.custom_schemes = None
+        self.packagesdir = None
+        self.metapackagename = None
+        self.macosx_version = tools.sw_vers()
+
+    def get_command_obj(self, name):
+        return self.distribution.get_command_obj(name)
+
+    def get_name(self):
+        return self.distribution.get_name()
+
+    def get_version(self):
+        return self.distribution.get_version()
+
+    def make_fullplatcomponents(self, *args):
+        lst = [s.replace('-', '_') for s in args]
+        lst.extend([
+            'py' + sys.version[:3],
+            'macosx' + '.'.join(map(str, self.macosx_version.version[:2])),
+        ])
+        return lst
+
+    def get_fullplatname(self):
+        return '-'.join(
+            self.make_fullplatcomponents(self.get_name(), self.get_version()))
+
+    def get_command_schemes(self):
+        rval = self.command_schemes
+        if rval is None:
+            rval = {}
+            for scheme, cmd in self.scheme_command.iteritems():
+                rval.setdefault(cmd, []).append(scheme)
+            self.command_schemes = rval
+        return rval
+
+    def finalize_options (self):
+        if not isinstance(self.optimize, int):
+            self.optimize = int(self.optimize)
+        self.set_undefined_options('build', ('build_base', 'build_base'))
+        self.reinitialize_command('build').build_base =  self.build_base
+        self.set_undefined_options('bdist',
+            ('bdist_base', 'bdist_base'),
+            ('dist_dir', 'dist_dir'),
+            ('plat_name', 'plat_name'),
+        )
+        if self.pkg_base is None:
+            self.pkg_base = os.path.join(self.bdist_base, 'mpkg')
+
+        if self.custom_schemes is None:
+            self.custom_schemes = {}
+        for scheme, desc in self.custom_schemes.iteritems():
+            if 'description' in desc:
+                self.scheme_descriptions[scheme] = desc['description']
+            if 'prefix' in desc:
+                self.scheme_map[scheme] = desc['prefix']
+            if 'source' in desc:
+                self.scheme_copy[scheme] = desc['source']
+
+        install = self.get_finalized_command('install')
+        for scheme, description in INSTALL_SCHEME_DESCRIPTIONS.iteritems():
+            self.scheme_command.setdefault(scheme, 'install')
+            self.scheme_descriptions.setdefault(scheme, description)
+            self.scheme_map.setdefault(scheme,
+                os.path.realpath(getattr(install, 'install_' + scheme)))
+
+        if tools.is_framework_python():
+            if self.get_scheme_prefix('scripts').startswith(sys.prefix):
+                self.scheme_map['scripts'] = '/usr/local/bin'
+            if self.get_scheme_prefix('data').startswith(sys.prefix):
+                self.scheme_map['data'] = '/usr/local/share'
+
+        self.finalize_package_data()
+
+    def finalize_package_data(self):
+        if self.license is None:
+            for attempt in ('License', 'COPYING'):
+                self.license = pkg.try_exts(attempt, exts=pkg.TEXT_EXTS)
+                if self.license is not None:
+                    break
+
+        if self.readme is None:
+            self.readme = pkg.try_exts('ReadMe', exts=pkg.TEXT_EXTS)
+
+        if self.welcome is None:
+            self.welcome = pkg.try_exts('Welcome', exts=pkg.TEXT_EXTS)
+
+        if self.background is None:
+            self.background = pkg.try_exts('background', exts=pkg.IMAGE_EXTS)
+
+        if self.template is None:
+            if self.macosx_version < '10.3':
+                self.template = 'prepanther'
+            else:
+                self.template = 'postjaguar'
+
+        self.metapackagename = self.get_fullplatname() + '.mpkg'
+        self.pseudoinstall_root = self.get_pseudoinstall_root()
+        self.packagesdir = os.path.join(
+            self.get_metapackage(),
+            self.component_directory
+        )
+
+
+    def run_extra(self):
+        """
+        Subclass and add stuff here to add entries to scheme_map
+        """
+        pass
+
+    def scheme_hook(self, scheme, pkgname, version, files, common,
+            prefix, pkgdir):
+        """
+        Subclass and do stuff with the scheme post-packaging
+        """
+        pass
+
+    def run_subprojects(self):
+        for scheme,setupfile in self.scheme_subprojects.iteritems():
+            self.run_subproject(scheme, setupfile)
+
+    def run_subproject(self, scheme, setupfile):
+        if os.path.isdir(setupfile):
+            setupfile = os.path.join(setupfile, 'setup.py')
+        build_base = os.path.abspath(os.path.join(self.build_base, scheme))
+        dist_dir = os.path.abspath(self.packagesdir)
+        args = [
+            'bdist_mpkg', '--build-base=' + build_base,
+            '--dist-dir=' + dist_dir
+        ]
+        if self.keep_temp:
+            args.append('--keep-temp')
+        pkg = self.sub_setup(setupfile, args).get_command_obj(
+            'bdist_mpkg').metapackagename
+        if pkg is not None:
+            self.packages.append((pkg, self.get_scheme_status(scheme)))
+
+    def sub_setup(self, setupfile, args):
+        setupfile = os.path.abspath(setupfile)
+        srcdir = os.path.dirname(setupfile)
+        old_path = list(sys.path)
+        sys.path.insert(0, srcdir)
+        cwd = os.getcwd()
+        os.chdir(srcdir)
+        try:
+            return tools.run_setup(setupfile, args)
+        finally:
+            sys.path[:] = old_path
+            os.chdir(cwd)
+
+    def get_finalized_install_command(self):
+        install = self.reinitialize_command('install', reinit_subcommands=1)
+        self.get_command_obj('build').build_base = self.build_base
+        for scheme in self.get_command_schemes()['install']:
+            prefix = self.get_scheme_install_prefix(scheme)
+            setattr(install, 'install_' + scheme, prefix)
+        install.single_version_externally_managed = 1
+        install.root = self.pkg_base
+        install.skip_build = self.skip_build
+        install.warn_dir = 0
+        install.compile = 0
+        install.ensure_finalized()
+        return install
+
+    def run_install(self):
+        install = self.get_finalized_install_command()
+        data = self.get_finalized_command('install_data')
+        data.root = os.path.join(data.root, 'data')
+        install.run()
+        self.byte_compile()
+
+    def byte_compile(self):
+        for scheme in ('platlib', 'purelib'):
+            scheme_dir = self.get_scheme_dir(scheme)
+            if os.path.exists(scheme_dir):
+                self.byte_compile_scheme(scheme)
+
+    def byte_compile_scheme(self, scheme):
+        files, common, prefix = self.get_scheme_root(scheme)
+        install_root = self.get_scheme_dir(scheme)
+
+        byte_compile(
+            files,
+            optimize=0,
+            force=self.force,
+            prefix=install_root,
+            dry_run=self.dry_run,
+        )
+        if self.optimize > 0:
+            byte_compile(
+                files,
+                optimize=self.optimize,
+                force=self.force,
+                prefix=install_root,
+                verbose=self.verbose,
+                dry_run=self.dry_run,
+            )
+
+    def run_adminperms(self):
+        tools.adminperms(self.pkg_base,
+            verbose=self.verbose, dry_run=self.dry_run)
+
+    def mkpath(self, fn):
+        mkpath(fn, dry_run=self.dry_run, verbose=self.verbose)
+
+    def get_scheme_info(self, scheme):
+        return ()
+
+    def get_scheme_pkgname(self, scheme):
+        return '-'.join(self.make_fullplatcomponents(
+            self.get_name(), scheme,
+        ))
+
+    def get_scheme_pkgfile(self, scheme):
+        return '-'.join(self.make_fullplatcomponents(
+            self.get_name(),
+            scheme,
+            self.get_version(),
+        )) + '.pkg'
+
+    def get_pseudoinstall_root(self):
+        return os.path.join(self.dist_dir, self.metapackagename)
+
+    def get_schemes(self):
+        return self.scheme_map.keys()
+
+    def get_scheme_prefix(self, scheme):
+        return self.scheme_map.get(scheme)
+
+    def get_scheme_install_prefix(self, scheme):
+        prefix = self.get_scheme_prefix(scheme)
+        if prefix.startswith(os.sep):
+            prefix = prefix[len(os.sep):]
+        return os.path.join(scheme, prefix)
+
+    def get_scheme_install_target(self, scheme):
+        return os.path.join(
+            self.pkg_base,
+            self.get_scheme_install_prefix(scheme),
+        )
+
+    def get_scheme_dir(self, scheme):
+        return os.path.join(self.pkg_base, scheme)
+
+    def get_scheme_root(self, scheme):
+        rval = self.scheme_root.get(scheme)
+        if rval is None:
+            rval = self.scheme_root[scheme] = tools.find_root(
+                self.get_scheme_dir(scheme),
+            )
+        return rval
+
+    def get_scheme_description(self, scheme):
+        description = self.scheme_descriptions.get(scheme)
+        if description is None:
+            return None
+        files, common, prefix = self.get_scheme_root(scheme)
+        if prefix is not None:
+            description += u'\nInstalled to: ' + tools.unicode_path(prefix)
+        return description
+
+    def get_metapackage(self):
+        return self.get_pseudoinstall_root()
+
+    def get_metapackage_info(self):
+        return dict(
+            IFRequirementDicts=[plists.python_requirement(self.get_name())],
+            IFPkgFlagComponentDirectory=tools.unicode_path(
+                self.component_directory
+            ),
+        )
+
+    def get_scheme_version(self, scheme):
+        return self.get_version()
+
+    def get_scheme_status(self, scheme):
+        return 'selected'
+
+    def make_scheme_package(self, scheme):
+        files, common, prefix = self.get_scheme_root(scheme)
+        pkgname = self.get_scheme_pkgname(scheme)
+        pkgfile = self.get_scheme_pkgfile(scheme)
+        self.packages.append((pkgfile, self.get_scheme_status(scheme)))
+        pkgdir = os.path.join(self.packagesdir, pkgfile)
+        self.mkpath(pkgdir)
+        version = self.get_scheme_version(scheme)
+
+        pkg.make_package(self,
+            pkgname, version,
+            files, common, prefix,
+            pkgdir,
+            self.get_scheme_info(scheme),
+            self.get_scheme_description(scheme),
+        )
+
+        self.scheme_hook(scheme, pkgname, version, files, common, prefix,
+            pkgdir)
+
+    def make_metapackage(self):
+        pkg.make_metapackage(self,
+            self.get_name(),
+            self.get_version(),
+            self.packages,
+            self.get_metapackage(),
+            self.get_metapackage_info(),
+        )
+
+    def remove_temp(self):
+        remove_tree(self.pkg_base, dry_run=self.dry_run)
+
+    def run_open(self):
+        TOOL = '/usr/bin/open'
+        os.spawnv(os.P_NOWAIT, TOOL, [TOOL, self.get_metapackage()])
+
+    def run_commands(self):
+        for command in self.get_command_schemes():
+            if command != 'install':
+                self.run_command(command)
+
+    def run_copy(self):
+        for scheme, source in self.scheme_copy.iteritems():
+            log.info("copying files for scheme %s" % (scheme,))
+            target = self.get_scheme_install_target(scheme)
+            self.copy_tree(source, target)
+
+    def run(self):
+        log.info("installing to %s" % (self.pkg_base,))
+        self.run_install()
+        self.run_commands()
+        self.run_subprojects()
+        self.run_copy()
+        self.run_extra()
+        self.run_adminperms()
+
+        # And make an archive relative to the root of the
+        # pseudo-installation tree.
+        self.mkpath(self.packagesdir)
+
+        name = self.get_name()
+        version = self.get_version()
+
+        for scheme in self.get_schemes():
+            schemedir = self.get_scheme_dir(scheme)
+            if not os.path.exists(schemedir):
+                # if the scheme dir doesn't already exist,
+                # nothing was installed there
+                # installation should be done in run_extra or by the
+                # install command
+                log.info("nothing to be installed for scheme %s" % (scheme,))
+                continue
+            self.make_scheme_package(scheme)
+
+        self.make_metapackage()
+
+        if not self.keep_temp:
+            self.remove_temp()
+        if self.zipdist:
+            self.run_zipdist()
+        if self.open:
+            self.run_open()
+
+    def run_zipdist(self):
+        zipname = self.get_fullplatname()
+        outzip = os.path.join(self.dist_dir, zipname + '.zip')
+        z = zipfile.ZipFile(outzip, mode='w', compression=zipfile.ZIP_DEFLATED)
+        log.info('Creating %s', outzip)
+        mpkg = self.get_metapackage()
+        zipbase = os.path.join(zipname, os.path.basename(mpkg))
+        mpkgroot = os.path.join(mpkg, '')
+        for root, dirs, files in os.walk(mpkgroot):
+            for fn in files:
+                fn = os.path.join(root, fn)
+                arcfn = os.path.join(zipbase, fn[len(mpkgroot):])
+                compression = zipfile.ZIP_DEFLATED
+                if os.path.splitext(fn)[1] == '.gz':
+                    compression= zipfile.ZIP_STORED
+                z.write(fn, arcfn, compression)
+
+        # ZipFile always marks the files' attributes to be interpreted as if
+        # they came from a Windows host. This interferes with some software
+        # (namely unzip(1) from Info-Zip) from extracting executables with the
+        # proper file attributes. So manually fix the appropriate attributes on
+        # each of the ZipInfo's to specify the host system as a UNIX.
+        for zinfo in z.filelist:
+            zinfo.create_system = 3 # UNIX
+
+        z.close()
+
+    def copy_tree(self, infile, outfile, preserve_mode=1,
+            preserve_times=1, preserve_symlinks=1, condition=None):
+        """
+        Copy an entire directory tree respecting verbose, dry-run,
+        and force flags.
+
+        This version doesn't bork on existing symlinks
+        """
+        update = not self.force
+        dry_run = self.dry_run
+        verbose = self.verbose
+        return copy_tree(
+            infile, outfile,
+            preserve_mode, preserve_times, preserve_symlinks,
+            update, verbose, dry_run, condition
+        )

bdist_mpkg/pkg.py

+import os
+import sys
+from cStringIO import StringIO
+from distutils.dir_util import mkpath
+from distutils.file_util import copy_file
+
+from bdist_mpkg import tools, plists
+from bdist_mpkg.util import copy_tree
+from bdist_mpkg.templates import InstallationCheck
+
+def write_template((script, strings), dest, mkpath=mkpath):
+    spath = os.path.join(dest, 'InstallationCheck')
+    f = open(spath, 'w')
+    f.write(script.encode('utf8'))
+    f.close()
+    os.chmod(spath, os.stat(spath)[0] | 0111)
+    lproj = os.path.join(dest, 'English.lproj')
+    mkpath(lproj)
+    spath = os.path.join(lproj, 'InstallationCheck.strings')
+    f = open(spath, 'w')
+    f.write(strings.encode('utf16'))
+    f.close()
+
+def write_sizes(count, size, compressed, pkgdir):
+    f = open(
+        os.path.join(pkgdir, 'Contents', 'Resources', 'Archive.sizes'),
+        'w'
+    )
+    f.write('NumFiles %d\nInstalledSize %d\nCompressedSize %d'
+        % (count, size, compressed))
+    f.close()
+
+TEXT_EXTS = '.rtfd', '.rtf', '.html', '.txt'
+IMAGE_EXTS = '.tiff', '.png', '.jpg'
+
+def write_pkginfo(pkgdir):
+    f = open(os.path.join(pkgdir, 'Contents', 'PkgInfo'), 'w')
+    f.write('pmkrpkg1')
+    f.close()
+
+def try_exts(path, exts=TEXT_EXTS):
+    path = os.path.splitext(path)[0]
+    for ext in exts:
+        npath = path + ext
+        if os.path.exists(npath):
+            return npath
+    return None
+
+def copy_doc(path, name, pkgdir, exts=TEXT_EXTS, language=None, dry_run=0,
+        copy_tree=copy_tree, copy_file=copy_file, mkpath=mkpath):
+    if path is None:
+        return
+    is_string = hasattr(path, 'getvalue')
+    if is_string:
+        ext = '.txt'
+    else:
+        ext = os.path.splitext(path)[1].lower()
+        if ext == '':
+            ext = '.txt'
+    if ext not in exts:
+        raise ValueError('Invalid extension for %s' % (path,))
+    destdir = os.path.join(pkgdir, 'Contents', 'Resources')
+    if language is not None:
+        destdir = os.path.join(destdir, language + '.lproj')
+    mkpath(destdir)
+    dest = os.path.join(destdir, name + ext)
+    if is_string:
+        if not dry_run:
+            f = file(dest, 'wb')
+            f.write(path.getvalue())
+            f.close()
+    elif ext == '.rtfd':
+        copy_tree(path, dest)
+    else:
+        copy_file(path, dest)
+
+def make_metapackage(cmd, name, version, packages, pkgdir,
+        info=(), description=None):
+    license = cmd.license
+    readme = cmd.readme
+    welcome = cmd.welcome
+    background = cmd.background
+    template = cmd.template
+    dry_run = cmd.dry_run
+    dist = cmd.distribution
+    copy_tree = cmd.copy_tree
+    copy_file = cmd.copy_file
+    mkpath = cmd.mkpath
+
+    if description is None:
+        description = dist.get_description()
+    if not description:
+        description = u'%s %s' % (name, version)
+
+    mkpath(os.path.join(pkgdir, 'Contents', 'Resources'))
+    if not dry_run:
+        write_pkginfo(pkgdir)
+
+    ninfo = plists.mpkg_info(name, version, packages)
+    ninfo.update(dict(info))
+    if not dry_run:
+        plists.write(ninfo, os.path.join(pkgdir, 'Contents', 'Info.plist'))
+
+    desc = plists.common_description(name+' '+version, version)
+    if description:
+        desc['IFPkgDescriptionDescription'] = description
+    if not dry_run:
+        plists.write(
+            desc,
+            os.path.join(pkgdir, 'Contents', 'Resources', 'Description.plist')
+        )
+
+    template_dest = os.path.join(pkgdir, 'Contents', 'Resources')
+    if not os.path.exists(template) and template in InstallationCheck:
+        write_template(InstallationCheck[template], template_dest,
+            mkpath=mkpath)
+    else:
+        copy_tree(template, template_dest)
+
+    if readme is None:
+        readme_text = dist.get_long_description()
+        if readme_text:
+            readme = StringIO(readme_text)
+
+    def doc(path, name, exts=TEXT_EXTS):
+        copy_doc(path, name, pkgdir, exts=exts, dry_run=dry_run,
+            mkpath=mkpath, copy_tree=copy_tree, copy_file=copy_file,
+        )
+
+    doc(readme, 'ReadMe')
+    doc(license, 'License')
+    doc(welcome, 'Welcome')
+    doc(background, 'Background', exts=IMAGE_EXTS)
+
+def make_package(cmd, name, version, files, common, prefix, pkgdir,
+        info=(), description=None):
+    license = cmd.license
+    readme = cmd.readme
+    welcome = cmd.welcome
+    background = cmd.background
+    template = cmd.template
+    dry_run = cmd.dry_run
+    dist = cmd.distribution
+    copy_tree = cmd.copy_tree
+    copy_file = cmd.copy_file
+    mkpath = cmd.mkpath
+
+    if description is None:
+        description = dist.get_description()
+
+    mkpath(os.path.join(pkgdir, 'Contents', 'Resources'))
+    if not dry_run:
+        write_pkginfo(pkgdir)
+
+    tools.mkbom(common, pkgdir)
+    count = len(files)
+    admin = tools.admin_writable(prefix)
+    size = tools.reduce_size(files)
+    compressed = tools.pax(common, pkgdir)
+    if not dry_run:
+        write_sizes(count, size, compressed, pkgdir)
+
+    if admin:
+        auth = u'AdminAuthorization'
+    else:
+        auth = u'RootAuthorization'
+
+    ninfo = plists.pkg_info(name, version)
+    ninfo.update(dict(
+        IFPkgFlagAuthorizationAction=auth,
+        IFPkgFlagDefaultLocation=tools.unicode_path(prefix),
+    ))
+    ninfo.update(dict(info))
+    if not dry_run:
+        plists.write(ninfo, os.path.join(pkgdir, 'Contents', 'Info.plist'))
+
+    desc = plists.common_description(name, version)
+    if description is not None:
+        desc['IFPkgDescriptionDescription'] = description
+    if not dry_run:
+        plists.write(
+            desc,
+            os.path.join(pkgdir, 'Contents', 'Resources', 'Description.plist')
+        )
+
+    template_dest = os.path.join(pkgdir, 'Contents', 'Resources')
+    if not os.path.exists(template) and template in InstallationCheck:
+        write_template(InstallationCheck[template], template_dest,
+            mkpath=mkpath)
+    else:
+        copy_tree(template, template_dest)
+
+    def doc(path, name, exts=TEXT_EXTS):
+        copy_doc(path, name, pkgdir, exts=exts, dry_run=dry_run,
+            mkpath=mkpath, copy_tree=copy_tree, copy_file=copy_file,
+        )
+
+    doc(readme, 'ReadMe')
+    doc(license, 'License')
+    doc(welcome, 'Welcome')
+    doc(background, 'Background', exts=IMAGE_EXTS)

bdist_mpkg/plists.py

+import os
+import sys
+from plistlib import Plist
+
+import bdist_mpkg
+from bdist_mpkg import tools
+
+def _major_minor(v):
+    rval = [0, 0]
+    try:
+        for i, rev in enumerate(v.version):
+            rval[i] = int(rev)
+    except (TypeError, ValueError, IndexError):
+        pass
+    return rval
+
+def common_info(name, version):
+    # Keys that can appear in any package
+    name, version = unicode(name), tools.Version(version)
+    major, minor = _major_minor(version)
+    return dict(
+        CFBundleGetInfoString=u'%s %s' % (name, version),
+        CFBundleIdentifier=u'org.pythonmac.%s' % (name,),
+        CFBundleName=name,
+        CFBundleShortVersionString=unicode(version),
+        IFMajorVersion=major,
+        IFMinorRevision=minor,
+        IFPkgFormatVersion=0.10000000149011612,
+        IFRequirementDicts=[path_requirement(u'/')],
+        PythonInfoDict=dict(
+            PythonLongVersion=unicode(sys.version),
+            PythonShortVersion=unicode(sys.version[:3]),
+            PythonExecutable=unicode(sys.executable),
+            bdist_mpkg=dict(
+                version=unicode(bdist_mpkg.__version__),
+            ),
+        ),
+    )
+    return defaults
+
+def pkg_info(name, version):
+    d = common_info(name, version)
+    # Keys that can only appear in single packages
+    d.update(dict(
+        IFPkgFlagAllowBackRev=False,
+        IFPkgFlagAuthorizationAction=u'AdminAuthorization',
+        #IFPkgFlagDefaultLocation=u'/Library/Python/2.3',
+        IFPkgFlagFollowLinks=True,
+        IFPkgFlagInstallFat=False,
+        IFPkgFlagIsRequired=False,
+        IFPkgFlagOverwritePermissions=False,
+        IFPkgFlagRelocatable=False,
+        IFPkgFlagRestartAction=u'NoRestart',
+        IFPkgFlagRootVolumeOnly=True,
+        IFPkgFlagUpdateInstalledLangauges=False,
+    ))
+    return d
+
+def path_requirement(SpecArgument, Level=u'requires', **kw):
+    return dict(
+        Level=Level,
+        SpecType=u'file',
+        SpecArgument=tools.unicode_path(SpecArgument),
+        SpecProperty=u'NSFileType',
+        TestOperator=u'eq',
+        TestObject=u'NSFileTypeDirectory',
+        **kw
+    )
+
+FRIENDLY_PREFIX = {
+    os.path.expanduser(u'~/Library/Frameworks') : u'User',
+    u'/System/Library/Frameworks' : u'Apple',
+    u'/Library/Frameworks' : u'python.org',
+    u'/opt/local' : u'DarwinPorts',
+    u'/usr/local' : u'Unix',
+    u'/sw' : u'Fink',
+}
+
+def python_requirement(pkgname, prefix=None, version=None, **kw):
+    if prefix is None:
+        prefix = sys.prefix
+    if version is None:
+        version = sys.version[:3]
+    prefix = os.path.realpath(prefix)
+    fmwkprefix = os.path.dirname(os.path.dirname(prefix))
+    is_framework = fmwkprefix.endswith('.framework')
+    if is_framework:
+        dprefix = os.path.dirname(fmwkprefix)
+    else:
+        dprefix = prefix
+    dprefix = tools.unicode_path(dprefix)
+    name = u'%s Python %s' % (FRIENDLY_PREFIX.get(dprefix, dprefix), version)
+    kw.setdefault('LabelKey', name)
+    title = u'%s requires %s to install.' % (pkgname, name,)
+    kw.setdefault('TitleKey', title)
+    kw.setdefault('MessageKey', title)
+    return path_requirement(prefix, **kw)
+
+def mpkg_info(name, version, packages=[]):
+    d = common_info(name, version)
+    # Keys that can appear only in metapackages
+    npackages = []
+    for p in packages:
+        items = getattr(p, 'items', None)
+        if items is not None:
+            p = dict(items())
+        else:
+            if isinstance(p, basestring):
+                p = [p]
+            p = dict(zip(
+                (u'IFPkgFlagPackageLocation', u'IFPkgFlagPackageSelection'),
+                p
+            ))
+        npackages.append(p)
+    d.update(dict(
+        IFPkgFlagComponentDirectory=u'./Contents/Packages',
+        IFPkgFlagPackageList=npackages,
+    ))
+    return d
+
+def checkpath_plugin(path):
+    if not isinstance(path, unicode):
+        path = unicode(path, encoding)
+    return dict(
+        searchPlugin=u'CheckPath',
+        path=path,
+    )
+
+def common_description(name, version):
+    name, version = unicode(name), tools.Version(version)
+    return dict(
+        IFPkgDescriptionTitle=name,
+        IFPkgDescriptionVersion=unicode(version),
+    )
+
+def write(dct, path):
+    p = Plist()
+    p.update(dct)
+    p.write(path)

bdist_mpkg/script_bdist_mpkg.py

+#!/usr/bin/env python
+
+import sys, os
+
+import setuptools
+import bdist_mpkg
+
+def main():
+    del sys.argv[0]
+    if not sys.argv:
+        sys.argv[:0] = ['setup.py', '--open']
+    elif sys.argv[0].startswith('-'):
+        sys.argv[:0] = ['setup.py']
+    elif len(sys.argv) == 1:
+        sys.argv[1:1] = ['--open']
+    sys.argv.insert(1, 'bdist_mpkg')
+    if os.path.isdir(sys.argv[0]):
+        sys.argv[0] = os.path.join(sys.argv[0], 'setup.py')
+    path, name = os.path.split(os.path.abspath(sys.argv[0]))
+    if path:
+        os.chdir(path)
+    sys.path.insert(0, path)
+    sys.argv[0] = name
+    g = dict(globals())
+    g['__file__'] = sys.argv[0]
+    g['__name__'] = '__main__'
+    execfile(sys.argv[0], g, g)
+
+if __name__ == '__main__':
+    main()

bdist_mpkg/templates.py

+InstallationCheck = dict(
+    postjaguar=(
+        u"#!/bin/sh\nexit 112\n",
+        u'"16" = "This package requires Mac OS X 10.3 or later";',
+    ),
+    prepanther=(u"""#!/bin/sh
+#
+# We use IFRequirementDicts anyway and "parse" it to find where expect
+# Python to be.  It's remotely possible that Python is not installed
+# at all, so this is a horrendously evil and ugly shell script.
+#
+
+IPATH=`grep -A 1 SpecArgument $1/Contents/Info.plist | grep string | sed 's/^.*<string>\(.*\)<\/string>.*$/\1/g'`
+if ((test -z $IPATH) || (test -e $IPATH)); then exit 0; fi
+echo "Error during InstallationCheck"
+echo "Package: $1"
+echo "Expected path not found: $IPATH"
+exit 112
+""",
+        u'"16" = "This package requires MacPython to be installed";',
+    ),
+)

bdist_mpkg/tools.py

+import os
+import grp
+import sys
+from itertools import chain
+from distutils.util import spawn
+from distutils.version import StrictVersion, LooseVersion
+from distutils.dir_util import mkpath
+import distutils.core
+
+try:
+    set
+except NameError:
+    from sets import Set as set
+
+def Version(s):
+    try:
+        return StrictVersion(s)
+    except ValueError:
+        return LooseVersion(s)
+
+def run_setup(*args, **kwargs):
+    """
+    Re-entrant version of distutils.core.run_setup()
+    """
+    PRESERVE = '_setup_stop_after', '_setup_distribution'
+    d = {}
+    for k in PRESERVE:
+        try:
+            d[k] = getattr(distutils.core, k)
+        except AttributeError:
+            pass
+    try:
+        return distutils.core.run_setup(*args, **kwargs)
+    finally:
+        for k,v in d.iteritems():
+            setattr(distutils.core, k, v)
+
+def adminperms(src, verbose=0, dry_run=0):
+    try:
+        spawn(['/usr/bin/chgrp', '-R', 'admin', src])
+        spawn(['/bin/chmod', '-R', 'g+w', src])
+    except:
+        return False
+    return True
+
+def mkbom(src, pkgdir, verbose=0, dry_run=0, TOOL='/usr/bin/mkbom'):
+    """
+    Create a bill-of-materials (BOM) for the given src directory and store it
+    to the given pkg directory
+    """
+    dest = os.path.join(pkgdir, 'Contents', 'Archive.bom')
+    mkpath(os.path.dirname(dest), verbose=verbose, dry_run=dry_run)
+    spawn([TOOL, src, dest], verbose=verbose, dry_run=dry_run)
+
+def pax(src, pkgdir, verbose=0, dry_run=0, TOOL='/bin/pax'):
+    """
+    Create a pax gzipped cpio archive of the given src directory and store it
+    to the given pkg directory
+
+    returns size of archive
+    """
+    dest = os.path.realpath(os.path.join(pkgdir, 'Contents', 'Archive.pax.gz'))
+    mkpath(os.path.dirname(dest), verbose=verbose, dry_run=dry_run)
+    pwd = os.path.realpath(os.getcwd())
+    os.chdir(src)
+    try:
+        spawn([TOOL, '-w', '-f', dest, '-x', 'cpio', '-z', '.'])
+    finally:
+        os.chdir(pwd)
+    return os.stat(dest).st_size
+
+def unicode_path(path, encoding=sys.getfilesystemencoding()):
+    if isinstance(path, unicode):
+        return path
+    return unicode(path, encoding)
+
+def walk_files(path):
+    for root, dirs, files in os.walk(path):
+        for fn in files:
+            yield os.path.join(root, fn)
+
+def get_gid(name, _cache={}):
+    return grp.getgrnam(name).gr_gid 
+
+def find_root(path, base='/'):
+    """
+    Return the list of files, the archive directory, and the destination path
+    """
+    files = list(walk_files(path))
+    common = os.path.dirname(os.path.commonprefix(files))
+    prefix = os.path.join(base, common[len(os.path.join(path, '')):])
+    #while not os.path.exists(prefix):
+    #    common = os.path.dirname(common)
+    #    prefix = os.path.dirname(prefix)
+    prefix = os.path.realpath(prefix)
+    return files, common, prefix
+
+def admin_writable(path):
+    gid = get_gid('admin')
+    while not os.path.exists(path):
+        path = os.path.dirname(path)
+    s = os.stat(path)
+    mode = s.st_mode
+    return (mode & 00002) or (s.st_gid == gid and mode & 00020)
+
+def reduce_size(files):
+    return sum([os.stat(fn).st_size for fn in files])
+
+def sw_vers(_cache=[]):
+    if not _cache:
+        info = os.popen('/usr/bin/sw_vers').read().splitlines()
+        for line in info:
+            key, value = line.split(None, 1)
+            if key == 'ProductVersion:':
+                _cache.append(Version(value.strip()))
+                break
+        else:
+            raise ValueError("sw_vers not behaving correctly")
+    return _cache[0]
+
+def is_framework_python():
+    return os.path.dirname(os.path.dirname(sys.prefix)).endswith('.framework')

bdist_mpkg/util.py

+import os, sys
+try:
+    set
+except NameError:
+    from sets import Set as set
+
+def fsencoding(s, encoding=sys.getfilesystemencoding()):
+    if isinstance(s, unicode):
+        s = s.encode(encoding)
+    return s
+
+SCMDIRS = ['CVS', '.svn']
+def skipscm(ofn):
+    ofn = fsencoding(ofn)
+    fn = os.path.basename(ofn)
+    if fn in SCMDIRS:
+        return False
+    return True
+
+def skipfunc(junk=(), junk_exts=(), chain=()):
+    junk = set(junk)
+    junk_exts = set(junk_exts)
+    chain = tuple(chain)
+    def _skipfunc(fn):
+        if os.path.basename(fn) in junk:
+            return False
+        elif os.path.splitext(fn)[1] in junk_exts:
+            return False
+        for func in chain:
+            if not func(fn):
+                return False
+        else:
+            return True
+    return _skipfunc
+
+JUNK = ['.DS_Store', '.gdb_history', 'build', 'dist'] + SCMDIRS
+JUNK_EXTS = ['.pbxuser', '.pyc', '.pyo', '.swp']
+skipjunk = skipfunc(JUNK, JUNK_EXTS)
+
+def copy_tree(src, dst,
+        preserve_mode=1,
+        preserve_times=1,
+        preserve_symlinks=0,
+        update=0,
+        verbose=0,
+        dry_run=0,
+        condition=None):
+
+    """
+    Copy an entire directory tree 'src' to a new location 'dst'.  Both
+    'src' and 'dst' must be directory names.  If 'src' is not a
+    directory, raise DistutilsFileError.  If 'dst' does not exist, it is
+    created with 'mkpath()'.  The end result of the copy is that every
+    file in 'src' is copied to 'dst', and directories under 'src' are
+    recursively copied to 'dst'.  Return the list of files that were
+    copied or might have been copied, using their output name.  The
+    return value is unaffected by 'update' or 'dry_run': it is simply
+    the list of all files under 'src', with the names changed to be
+    under 'dst'.
+
+    'preserve_mode' and 'preserve_times' are the same as for
+    'copy_file'; note that they only apply to regular files, not to
+    directories.  If 'preserve_symlinks' is true, symlinks will be
+    copied as symlinks (on platforms that support them!); otherwise
+    (the default), the destination of the symlink will be copied.
+    'update' and 'verbose' are the same as for 'copy_file'.
+    """
+
+
+    from distutils.dir_util import mkpath
+    from distutils.file_util import copy_file
+    from distutils.dep_util import newer
+    from distutils.errors import DistutilsFileError
+    from distutils import log
+
+    src = fsencoding(src)
+    dst = fsencoding(dst)
+
+    if condition is None:
+        condition = skipjunk
+
+    if not dry_run and not os.path.isdir(src):
+        raise DistutilsFileError(
+            "cannot copy tree '%s': not a directory" % src)
+    try:
+        names = os.listdir(src)
+    except os.error, (errno, errstr):
+        if dry_run:
+            names = []
+        else:
+            raise DistutilsFileError("error listing files in '%s': %s" % (
+                src, errstr))
+
+    if not dry_run:
+        mkpath(dst)
+
+    outputs = []
+
+    for n in names:
+        src_name = os.path.join(src, n)
+        dst_name = os.path.join(dst, n)
+        if (condition is not None) and (not condition(src_name)):
+            continue
+
+        if preserve_symlinks and os.path.islink(src_name):
+            link_dest = os.readlink(src_name)
+            log.info("linking %s -> %s", dst_name, link_dest)
+            if not dry_run:
+                if update and not newer(src, dst_name):
+                    pass
+                else:
+                    if os.path.islink(dst_name):
+                        os.remove(dst_name)
+                    os.symlink(link_dest, dst_name)
+            outputs.append(dst_name)
+
+        elif os.path.isdir(src_name):
+            outputs.extend(
+                copy_tree(src_name, dst_name, preserve_mode,
+                          preserve_times, preserve_symlinks, update,
+                          dry_run=dry_run, condition=condition))
+        else:
+            copy_file(src_name, dst_name, preserve_mode,
+                      preserve_times, update, dry_run=dry_run)
+            outputs.append(dst_name)
+
+    return outputs

ez_setup/README.txt

+This directory exists so that Subversion-based projects can share a single
+copy of the ``ez_setup`` bootstrap module for ``setuptools``, and have it
+automatically updated in their projects when ``setuptools`` is updated.
+
+For your convenience, you may use the following svn:externals definition::
+
+    ez_setup svn://svn.eby-sarna.com/svnroot/ez_setup
+
+You can set this by executing this command in your project directory::
+
+    svn propedit svn:externals .
+
+And then adding the line shown above to the file that comes up for editing.
+Then, whenever you update your project, ``ez_setup`` will be updated as well.
+

ez_setup/__init__.py

+#!python
+"""Bootstrap setuptools installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+    from ez_setup import use_setuptools
+    use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import sys
+DEFAULT_VERSION = "0.6c11"
+DEFAULT_URL     = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
+
+md5_data = {
+    'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
+    'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
+    'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
+    'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
+    'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
+    'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
+    'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
+    'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
+    'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
+    'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
+    'setuptools-0.6c10-py2.3.egg': 'ce1e2ab5d3a0256456d9fc13800a7090',
+    'setuptools-0.6c10-py2.4.egg': '57d6d9d6e9b80772c59a53a8433a5dd4',
+    'setuptools-0.6c10-py2.5.egg': 'de46ac8b1c97c895572e5e8596aeb8c7',
+    'setuptools-0.6c10-py2.6.egg': '58ea40aef06da02ce641495523a0b7f5',
+    'setuptools-0.6c11-py2.3.egg': '2baeac6e13d414a9d28e7ba5b5a596de',
+    'setuptools-0.6c11-py2.4.egg': 'bd639f9b0eac4c42497034dec2ec0c2b',
+    'setuptools-0.6c11-py2.5.egg': '64c94f3bf7a72a13ec83e0b24f2749b2',
+    'setuptools-0.6c11-py2.6.egg': 'bfa92100bd772d5a213eedd356d64086',
+    'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
+    'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
+    'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
+    'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
+    'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
+    'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
+    'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
+    'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
+    'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
+    'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
+    'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
+    'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
+    'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
+    'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
+    'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
+    'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
+    'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
+    'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
+    'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
+    'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
+    'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
+    'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
+    'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
+    'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
+}
+
+import sys, os
+try: from hashlib import md5
+except ImportError: from md5 import md5
+
+def _validate_md5(egg_name, data):
+    if egg_name in md5_data:
+        digest = md5(data).hexdigest()
+        if digest != md5_data[egg_name]:
+            print >>sys.stderr, (
+                "md5 validation of %s failed!  (Possible download problem?)"
+                % egg_name
+            )
+            sys.exit(2)
+    return data
+
+def use_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    download_delay=15
+):
+    """Automatically find/download setuptools and make it available on sys.path
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end with
+    a '/').  `to_dir` is the directory where setuptools will be downloaded, if
+    it is not already available.  If `download_delay` is specified, it should
+    be the number of seconds that will be paused before initiating a download,
+    should one be required.  If an older version of setuptools is installed,
+    this routine will print a message to ``sys.stderr`` and raise SystemExit in
+    an attempt to abort the calling script.
+    """
+    was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
+    def do_download():
+        egg = download_setuptools(version, download_base, to_dir, download_delay)
+        sys.path.insert(0, egg)
+        import setuptools; setuptools.bootstrap_install_from = egg
+    try:
+        import pkg_resources
+    except ImportError:
+        return do_download()       
+    try:
+        pkg_resources.require("setuptools>="+version); return
+    except pkg_resources.VersionConflict, e:
+        if was_imported:
+            print >>sys.stderr, (
+            "The required version of setuptools (>=%s) is not available, and\n"
+            "can't be installed while this script is running. Please install\n"
+            " a more recent version first, using 'easy_install -U setuptools'."
+            "\n\n(Currently using %r)"
+            ) % (version, e.args[0])
+            sys.exit(2)
+    except pkg_resources.DistributionNotFound:
+        pass
+
+    del pkg_resources, sys.modules['pkg_resources']    # reload ok
+    return do_download()
+
+def download_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    delay = 15
+):
+    """Download setuptools from a specified location and return its filename
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end
+    with a '/'). `to_dir` is the directory where the egg will be downloaded.
+    `delay` is the number of seconds to pause before an actual download attempt.
+    """
+    import urllib2, shutil
+    egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
+    url = download_base + egg_name
+    saveto = os.path.join(to_dir, egg_name)
+    src = dst = None
+    if not os.path.exists(saveto):  # Avoid repeated downloads
+        try:
+            from distutils import log
+            if delay:
+                log.warn("""
+---------------------------------------------------------------------------
+This script requires setuptools version %s to run (even to display
+help).  I will attempt to download it for you (from
+%s), but
+you may need to enable firewall access for this script first.
+I will start the download in %d seconds.
+
+(Note: if this machine does not have network access, please obtain the file
+
+   %s
+
+and place it in this directory before rerunning this script.)
+---------------------------------------------------------------------------""",
+                    version, download_base, delay, url
+                ); from time import sleep; sleep(delay)
+            log.warn("Downloading %s", url)
+            src = urllib2.urlopen(url)
+            # Read/write all in one block, so we don't create a corrupt file
+            # if the download is interrupted.
+            data = _validate_md5(egg_name, src.read())
+            dst = open(saveto,"wb"); dst.write(data)
+        finally:
+            if src: src.close()
+            if dst: dst.close()
+    return os.path.realpath(saveto)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+def main(argv, version=DEFAULT_VERSION):
+    """Install or upgrade setuptools and EasyInstall"""
+    try:
+        import setuptools
+    except ImportError:
+        egg = None
+        try:
+            egg = download_setuptools(version, delay=0)
+            sys.path.insert(0,egg)
+            from setuptools.command.easy_install import main
+            return main(list(argv)+[egg])   # we're done here
+        finally:
+            if egg and os.path.exists(egg):
+                os.unlink(egg)
+    else:
+        if setuptools.__version__ == '0.0.1':
+            print >>sys.stderr, (
+            "You have an obsolete version of setuptools installed.  Please\n"
+            "remove it from your system entirely before rerunning this script."
+            )
+            sys.exit(2)
+
+    req = "setuptools>="+version
+    import pkg_resources
+    try:
+        pkg_resources.require(req)
+    except pkg_resources.VersionConflict:
+        try:
+            from setuptools.command.easy_install import main
+        except ImportError:
+            from easy_install import main
+        main(list(argv)+[download_setuptools(delay=0)])
+        sys.exit(0) # try to force an exit
+    else:
+        if argv:
+            from setuptools.command.easy_install import main
+            main(argv)
+        else:
+            print "Setuptools version",version,"or greater has been installed."
+            print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
+
+def update_md5(filenames):
+    """Update our built-in md5 registry"""
+
+    import re
+
+    for name in filenames:
+        base = os.path.basename(name)
+        f = open(name,'rb')
+        md5_data[base] = md5(f.read()).hexdigest()
+        f.close()
+
+    data = ["    %r: %r,\n" % it for it in md5_data.items()]
+    data.sort()
+    repl = "".join(data)
+
+    import inspect
+    srcfile = inspect.getsourcefile(sys.modules[__name__])
+    f = open(srcfile, 'rb'); src = f.read(); f.close()
+
+    match = re.search("\nmd5_data = {\n([^}]+)}", src)
+    if not match:
+        print >>sys.stderr, "Internal error!"
+        sys.exit(2)
+
+    src = src[:match.start(1)] + repl + src[match.end(1):]
+    f = open(srcfile,'w')
+    f.write(src)
+    f.close()
+
+
+if __name__=='__main__':
+    if len(sys.argv)>2 and sys.argv[1]=='--md5update':
+        update_md5(sys.argv[2:])
+    else:
+        main(sys.argv[1:])
+
+
+
+
+
+
+[egg_info]
+tag_build = .dev
+tag_svn_revision = true
+#!/usr/bin/env python
+
+import ez_setup
+ez_setup.use_setuptools()
+
+from setuptools import setup, Extension
+
+VERSION = '0.4.4'
+DESCRIPTION = "Builds Mac OS X installer packages from distutils"
+LONG_DESCRIPTION = """
+bdist_mpkg is a distutils plugin that implements the bdist_mpkg command,
+which builds a Mac OS X metapackage for use by Installer.app for easy GUI
+installation of Python modules, much like bdist_wininst.
+
+It also comes with a bdist_mpkg script, which is a setup.py front-end that
+will allow you to easy build an installer metapackage from nearly any existing
+package that uses distutils.
+"""
+
+CLASSIFIERS = filter(None, map(str.strip,
+"""                 
+Intended Audience :: Developers
+License :: OSI Approved :: MIT License
+Programming Language :: Python
+Operating System :: MacOS :: MacOS X
+Topic :: Software Development :: Libraries :: Python Modules
+Topic :: Software Development :: Build Tools
+""".splitlines()))
+
+setup(
+    name="bdist_mpkg",
+    version=VERSION,
+    description=DESCRIPTION,
+    long_description=LONG_DESCRIPTION,
+    classifiers=CLASSIFIERS,
+    author="Bob Ippolito",
+    author_email="pythonmac-sig@python.org",
+    url="http://undefined.org/python/#bdist_mpkg",
+    license="MIT License",
+    packages=['bdist_mpkg'],
+    platforms=['any'],
+    zip_safe=True,
+    entry_points={
+        'distutils.commands': [
+            'bdist_mpkg = bdist_mpkg.cmd_bdist_mpkg:bdist_mpkg',
+        ],
+        'console_scripts': [
+            'bdist_mpkg = bdist_mpkg.script_bdist_mpkg:main',
+        ],
+    },
+)