content: """
  Mac OS X .app build command for distutils
  
  Originally (loosely) based on code from py2exe's build_exe.py by Thomas Heller.
  """
  from __future__ import print_function
  
  import imp
  import sys
  import os
  import zipfile
  import plistlib
  import shlex
  import shutil
  import textwrap
  import pkg_resources
  import collections
  from modulegraph import modulegraph
  
  from py2app.apptemplate.setup import main as script_executable
  from py2app.util import mergecopy, make_exec
  
  try:
      from cStringIO import StringIO
  except ImportError:
      from io import StringIO
  
  from itertools import chain
  
  
  from setuptools import Command
  from distutils.util import convert_path
  from distutils import log
  from distutils.errors import *
  
  
  from modulegraph.find_modules import find_modules, parse_mf_results, find_needed_modules
  from modulegraph.modulegraph import SourceModule, Package, Script
  from modulegraph import zipio
  
  import macholib.dyld
  import macholib.MachOStandalone
  import macholib.MachO
  from macholib.util import flipwritable
  
  from py2app.create_appbundle import create_appbundle
  from py2app.create_pluginbundle import create_pluginbundle
  from py2app.util import \
      fancy_split, byte_compile, make_loader, imp_find_module, \
      copy_tree, fsencoding, strip_files, in_system_path, makedirs, \
      iter_platform_files, find_version, skipscm, momc, copy_file, \
      copy_resource
  from py2app.filters import \
      not_stdlib_filter, not_system_filter, has_filename_filter
  from py2app import recipes
  
  from distutils.sysconfig import get_config_var, get_config_h_filename
  PYTHONFRAMEWORK=get_config_var('PYTHONFRAMEWORK')
  
  
  PLUGIN_SUFFIXES = {
          '.qlgenerator':    'QuickLook',
          '.mdimporter':     'Spotlight',
          '.xpc':            'XPCServices',
          '.service':        'Services',
          '.prefPane':       'PreferencePanes',
          '.iaplugin':       'InternetAccounts',
          '.action':         'Automator',
  }
  
  try:
      basestring
  except NameError:
      basestring = str
  
  def rewrite_tkinter_load_commands(tkinter_path):
      print("rewrite_tk", tkinter_path)
      m = macholib.MachO.MachO(tkinter_path)
      tcl_path = None
      tk_path = None
  
      rewrite_map = {}
  
      for header in m.headers:
          for idx, name, other in header.walkRelocatables():
              if other.endswith('/Tk'):
                  if tk_path is not None and other != tk_path:
                      raise DistutilsPlatformError('_tkinter is linked to different Tk paths')
                  tk_path = other
              elif other.endswith('/Tcl'):
                  if tcl_path is not None and other != tcl_path:
                      raise DistutilsPlatformError('_tkinter is linked to different Tcl paths')
                  tcl_path = other
  
      if tcl_path is None or 'Tcl.framework' not in tcl_path:
          raise DistutilsPlatformError('_tkinter is not linked a Tcl.framework')
  
      if tk_path is None or 'Tk.framework' not in tk_path:
          raise DistutilsPlatformError('_tkinter is not linked a Tk.framework')
  
      system_tcl_versions = [nm for nm in os.listdir('/System/Library/Frameworks/Tcl.framework/Versions') if nm != 'Current']
      system_tk_versions = [nm for nm in os.listdir('/System/Library/Frameworks/Tk.framework/Versions') if nm != 'Current']
  
      if not tcl_path.startswith('/System/Library/Frameworks'):
          # ../Versions/8.5/Tcl
          ver = os.path.basename(os.path.dirname(tcl_path))
          if ver not in system_tcl_versions:
              raise DistutilsPlatformError('_tkinter is linked to a version of Tcl not in /System')
  
          rewrite_map[tcl_path] = '/System/Library/Frameworks/Tcl.framework/Versions/%s/Tcl'%(ver,)
  
      if not tk_path.startswith('/System/Library/Frameworks'):
          # ../Versions/8.5/Tk
          ver = os.path.basename(os.path.dirname(tk_path))
          if ver not in system_tk_versions:
              raise DistutilsPlatformError('_tkinter is linked to a version of Tk not in /System')
  
          rewrite_map[tk_path] = '/System/Library/Frameworks/Tk.framework/Versions/%s/Tk'%(ver,)
  
      if rewrite_map:
          print("Relinking _tkinter.so to system Tcl/Tk")
          rewroteAny = False
          for header in m.headers:
              for idx, name, other in header.walkRelocatables():
                  data = rewrite_map.get(other)
                  if data:
                      if header.rewriteDataForCommand(idx, data.encode(sys.getfilesystemencoding())):
                          rewroteAny = True
  
          if rewroteAny:
              old_mode = flipwritable(m.filename)
              try:
                  with open(m.filename, 'rb+') as f:
                      for header in m.headers:
                          f.seek(0)
                          header.write(f)
                          f.seek(0, 2)
                          f.flush()
  
              finally:
                  flipwritable(m.filename, old_mode)
  
      else:
          print("_tkinter already linked against system Tcl/Tk")
  
  
  def get_zipfile(dist, semi_standalone=False):
      if sys.version_info[0] == 3:
          if semi_standalone:
              return "python%d.%d/site-packages.zip"%(sys.version_info[:2])
          else:
              return "python%d%d.zip"%(sys.version_info[:2])
      return getattr(dist, "zipfile", None) or "site-packages.zip"
  
  def framework_copy_condition(src):
      # Skip Headers, .svn, and CVS dirs
      return skipscm(src) and os.path.basename(src) != 'Headers'
  
  class PythonStandalone(macholib.MachOStandalone.MachOStandalone):
      def __init__(self, appbuilder, *args, **kwargs):
          super(PythonStandalone, self).__init__(*args, **kwargs)
          self.appbuilder = appbuilder
  
      def copy_dylib(self, src):
          dest = os.path.join(self.dest, os.path.basename(src))
          if os.path.islink(src):
              dest = os.path.join(self.dest, os.path.basename(os.path.realpath(src)))
  
              # Ensure that the orginal name also exists, avoids problems when
              # the filename is used from Python (see issue #65)
              #
              # NOTE: The if statement checks that the target link won't
              #       point to itself, needed for systems like homebrew that
              #       store symlinks in "public" locations that point to
              #       files of the same name in a per-package install location.
              link_dest = os.path.join(self.dest, os.path.basename(src))
              if os.path.basename(link_dest) != os.path.basename(dest):
                  os.symlink(os.path.basename(dest), link_dest)
  
          else:
              dest = os.path.join(self.dest, os.path.basename(src))
          return self.appbuilder.copy_dylib(src, dest)
  
      def copy_framework(self, info):
          destfn = self.appbuilder.copy_framework(info, self.dest)
          dest = os.path.join(self.dest, info['shortname'] + '.framework')
          self.pending.append((destfn, iter_platform_files(dest)))
          return destfn
  
  def iterRecipes(module=recipes):
      for name in dir(module):
          if name.startswith('_'):
              continue
          check = getattr(getattr(module, name), 'check', None)
          if check is not None:
              yield (name, check)
  
  # A very loosely defined "target".  We assume either a "script" or "modules"
  # attribute.  Some attributes will be target specific.
  class Target(object):
      def __init__(self, **kw):
          self.__dict__.update(kw)
          # If modules is a simple string, assume they meant list
          m = self.__dict__.get("modules")
          if m and isinstance(m, basestring):
              self.modules = [m]
  
      def get_dest_base(self):
          dest_base = getattr(self, "dest_base", None)
          if dest_base: return dest_base
          script = getattr(self, "script", None)
          if script:
              return os.path.basename(os.path.splitext(script)[0])
          modules = getattr(self, "modules", None)
          assert modules, "no script, modules or dest_base specified"
          return modules[0].split(".")[-1]
  
      def validate(self):
          resources = getattr(self, "resources", [])
          for r_filename in resources:
              if not os.path.isfile(r_filename):
                  raise DistutilsOptionError(
                      "Resource filename '%s' does not exist" % (r_filename,))
  
  
  def validate_target(dist, attr, value):
      res = FixupTargets(value, "script")
      other = {"app": "plugin", "plugin": "app"}
      if res and getattr(dist, other[attr]):
          # XXX - support apps and plugins?
          raise DistutilsOptionError(
              "You must specify either app or plugin, not both")
  
  def FixupTargets(targets, default_attribute):
      if not targets:
          return targets
      try:
          targets = eval(targets)
      except:
          pass
      ret = []
      for target_def in targets:
          if isinstance(target_def, basestring):
              # Create a default target object, with the string as the attribute
              target = Target(**{default_attribute: target_def})
          else:
              d = getattr(target_def, "__dict__", target_def)
              if default_attribute not in d:
                  raise DistutilsOptionError(
                      "This target class requires an attribute '%s'"
                      % (default_attribute,))
              target = Target(**d)
          target.validate()
          ret.append(target)
      return ret
  
  def normalize_data_file(fn):
      if isinstance(fn, basestring):
          fn = convert_path(fn)
          return ('', [fn])
      return fn
  
  def is_system():
      prefix = sys.prefix
      if os.path.exists(os.path.join(prefix, ".Python")):
          fn = os.path.join(prefix, "lib", "python%d.%d"%(sys.version_info[:2]), "orig-prefix.txt")
          if os.path.exists(fn):
              with open(fn, 'rU') as fp:
                  prefix = fp.read().strip()
  
      return in_system_path(prefix)
  
  def installation_info(version=None):
      if version is None:
          version = sys.version
  
      if is_system():
          return version[:3] + " (FORCED: Using vendor Python)"
      else:
          return version[:3]
  
  class py2app(Command):
      description = "create a Mac OS X application or plugin from Python scripts"
      # List of option tuples: long name, short name (None if no short
      # name), and help string.
  
      user_options = [
          ("app=", None,
           "application bundle to be built"),
          ("plugin=", None,
           "plugin bundle to be built"),
          ('optimize=', 'O',
           "optimization level: -O1 for \"python -O\", "
           "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
          ("includes=", 'i',
           "comma-separated list of modules to include"),
          ("packages=", 'p',
           "comma-separated list of packages to include"),
          ("iconfile=", None,
           "Icon file to use"),
          ("excludes=", 'e',
           "comma-separated list of modules to exclude"),
          ("dylib-excludes=", 'E',
           "comma-separated list of frameworks or dylibs to exclude"),
          ("datamodels=", None,
           "xcdatamodels to be compiled and copied into Resources"),
          ("mappingmodels=", None,
            "xcmappingmodels to be compiled and copied into Resources"),
          ("resources=", 'r',
           "comma-separated list of additional data files and folders to include (not for code!)"),
          ("frameworks=", 'f',
           "comma-separated list of additional frameworks and dylibs to include"),
          ("plist=", 'P',
           "Info.plist template file, dict, or plistlib.Plist"),
          ("extension=", None,
           "Bundle extension [default:.app for app, .plugin for plugin]"),
          ("graph", 'g',
           "output module dependency graph"),
          ("xref", 'x',
           "output module cross-reference as html"),
          ("no-strip", None,
           "do not strip debug and local symbols from output"),
          #("compressed", 'c',
          # "create a compressed zipfile"),
          ("no-chdir", 'C',
           "do not change to the data directory (Contents/Resources) [forced for plugins]"),
          #("no-zip", 'Z',
          # "do not use a zip file (XXX)"),
          ("semi-standalone", 's',
           "depend on an existing installation of Python " + installation_info()),
          ("alias", 'A',
           "Use an alias to current source file (for development only!)"),
          ("argv-emulation", 'a',
           "Use argv emulation [disabled for plugins]."),
          ("argv-inject=", None,
           "Inject some commands into the argv"),
          ("emulate-shell-environment", None,
           "Emulate the shell environment you get in a Terminal window"),
          ("use-pythonpath", None,
           "Allow PYTHONPATH to effect the interpreter's environment"),
          ("use-faulthandler", None,
           "Enable the faulthandler in the generated bundle (Python 3.3 or later)"),
          ("verbose-interpreter", None,
           "Start python in verbose mode"),
          ('bdist-base=', 'b',
           'base directory for build library (default is build)'),
          ('dist-dir=', 'd',
           "directory to put final built distributions in (default is dist)"),
          ('site-packages', None,
           "include the system and user site-packages into sys.path"),
          ("strip", 'S',
           "strip debug and local symbols from output (on by default, for compatibility)"),
          ("prefer-ppc", None,
           "Force application to run translated on i386 (LSPrefersPPC=True)"),
          ('debug-modulegraph', None,
           'Drop to pdb console after the module finding phase is complete'),
          ("debug-skip-macholib", None,
           "skip macholib phase (app will not be standalone!)"),
          ("arch=", None, "set of architectures to use (fat, fat3, universal, intel, i386, ppc, x86_64; default is the set for the current python binary)"),
          ("qt-plugins=", None, "set of Qt plugins to include in the application bundle (default None)"),
          ("matplotlib-backends=", None, "set of matplotlib backends to include (default: include entire package)"),
          ("extra-scripts=", None, "set of scripts to include in the application bundle, next to the main application script"),
          ("include-plugins=", None, "List of plugins to include"),
          ("force-system-tk", None, "Ensure that Tkinter is linked against Apple's build of Tcl/Tk"),
          ("report-missing-from-imports", None, "Report the list of missing names for 'from module import name'"),
          ("no-report-missing-conditional-import", None, "Don't report missing modules when they appear to be conditional imports"),
      ]
  
      boolean_options = [
          #"compressed",
          "xref",
          "strip",
          "no-strip",
          "site-packages",
          "semi-standalone",
          "alias",
          "argv-emulation",
          #"no-zip",
          "use-pythonpath",
          "use-faulthandler",
          "verbose-interpreter",
          "no-chdir",
          "debug-modulegraph",
          "debug-skip-macholib",
          "graph",
          "prefer-ppc",
          "emulate-shell-environment",
          "force-system-tk",
          "report-missing-from-imports",
          "no-report-missing-conditional-import",
      ]
  
      def initialize_options (self):
          self.app = None
          self.plugin = None
          self.bdist_base = None
          self.xref = False
          self.graph = False
          self.no_zip = 0
          self.optimize = 0
          if hasattr(sys, 'flags'):
              self.optimize = sys.flags.optimize
          self.arch = None
          self.strip = True
          self.no_strip = False
          self.iconfile = None
          self.extension = None
          self.alias = 0
          self.argv_emulation = 0
          self.emulate_shell_environment = 0
          self.argv_inject = None
          self.no_chdir = 0
          self.site_packages = False
          self.use_pythonpath = False
          self.use_faulthandler = False
          self.verbose_interpreter = False
          self.includes = None
          self.packages = None
          self.excludes = None
          self.dylib_excludes = None
          self.frameworks = None
          self.resources = None
          self.datamodels = None
          self.mappingmodels = None
          self.plist = None
          self.compressed = True
          self.semi_standalone = is_system()
          self.dist_dir = None
          self.debug_skip_macholib = False
          self.debug_modulegraph = False
          self.prefer_ppc = False
          self.filters = []
          self.eggs = []
          self.qt_plugins = None
          self.matplotlib_backends = None
          self.extra_scripts = None
          self.include_plugins = None
          self.force_system_tk = False
          self.report_missing_from_imports = False
          self.no_report_missing_conditional_import = False
  
      def finalize_options (self):
          if not self.strip:
              self.no_strip = True
          elif self.no_strip:
              self.strip = False
          self.optimize = int(self.optimize)
          if self.argv_inject and isinstance(self.argv_inject, basestring):
              self.argv_inject = shlex.split(self.argv_inject)
          self.includes = set(fancy_split(self.includes))
          self.includes.add('encodings.*')
  
          if self.use_faulthandler:
              self.includes.add('faulthandler')
          #if sys.version_info[:2] >= (3, 2):
          #    self.includes.add('pkgutil')
          #    self.includes.add('imp')
          self.packages = set(fancy_split(self.packages))
  
          self.excludes = set(fancy_split(self.excludes))
          self.excludes.add('readline')
          # included by apptemplate
          self.excludes.add('site')
          if getattr(self.distribution, 'install_requires', None):
              self.includes.add('pkg_resources')
              self.eggs = pkg_resources.require(self.distribution.install_requires)
  
          # Setuptools/distribute style namespace packages uses
          # __import__('pkg_resources'), and that import isn't detected at the
          # moment. Forcefully include pkg_resources.
          self.includes.add('pkg_resources')
  
          dylib_excludes = fancy_split(self.dylib_excludes)
          self.dylib_excludes = []
          for fn in dylib_excludes:
              try:
                  res = macholib.dyld.framework_find(fn)
              except ValueError:
                  try:
                      res = macholib.dyld.dyld_find(fn)
                  except ValueError:
                      res = fn
              self.dylib_excludes.append(res)
          self.resources = fancy_split(self.resources)
          frameworks = fancy_split(self.frameworks)
          self.frameworks = []
          for fn in frameworks:
              try:
                  res = macholib.dyld.framework_find(fn)
              except ValueError:
                  res = macholib.dyld.dyld_find(fn)
              while res in self.dylib_excludes:
                  self.dylib_excludes.remove(res)
              self.frameworks.append(res)
          if not self.plist:
              self.plist = {}
          if isinstance(self.plist, basestring):
              self.plist = plistlib.Plist.fromFile(self.plist)
          if isinstance(self.plist, plistlib.Dict):
              self.plist = dict(self.plist.__dict__)
          else:
              self.plist = dict(self.plist)
  
          self.set_undefined_options('bdist',
                                     ('dist_dir', 'dist_dir'),
                                     ('bdist_base', 'bdist_base'))
  
          if self.semi_standalone:
              self.filters.append(not_stdlib_filter)
  
          if self.iconfile is None and 'CFBundleIconFile' not in self.plist:
              # Default is the generic applet icon in the framework
              iconfile = os.path.join(sys.prefix, 'Resources', 'Python.app',
                  'Contents', 'Resources', 'PythonApplet.icns')
              if os.path.exists(iconfile):
                  self.iconfile = iconfile
  
  
          self.runtime_preferences = list(self.get_runtime_preferences())
  
          self.qt_plugins = fancy_split(self.qt_plugins)
          self.matplotlib_backends = fancy_split(self.matplotlib_backends)
          self.extra_scripts = fancy_split(self.extra_scripts)
          self.include_plugins = fancy_split(self.include_plugins)
  
  
          if self.datamodels:
              print("WARNING: the datamodels option is deprecated, add model files to the list of resources")
  
          if self.mappingmodels:
              print("WARNING: the mappingmodels option is deprecated, add model files to the list of resources")
  
  
      def get_default_plist(self):
          # XXX - this is all single target stuff
          plist = {}
          target = self.targets[0]
  
          version = self.distribution.get_version()
          if version == '0.0.0':
              try:
                  version = find_version(target.script)
              except ValueError:
                  pass
  
          if not isinstance(version, basestring):
              raise DistutilsOptionError("Version must be a string")
  
          if sys.version_info[0] > 2 and isinstance(version, type('a'.encode('ascii'))):
              raise DistutilsOptionError("Version must be a string")
  
          plist['CFBundleVersion'] = version
  
          name = self.distribution.get_name()
          if name == 'UNKNOWN':
              base = target.get_dest_base()
              name = os.path.basename(base)
          plist['CFBundleName'] = name
  
          return plist
  
      def get_runtime(self, prefix=None, version=None):
          # XXX - this is a bit of a hack!
          #       ideally we'd use dylib functions to figure this out
          if prefix is None:
              prefix = sys.prefix
          if version is None:
              version = sys.version
          version = version[:3]
          info = None
          if os.path.exists(os.path.join(prefix, ".Python")):
              # We're in a virtualenv environment, locate the real prefix
              fn = os.path.join(prefix, "lib", "python%d.%d"%(sys.version_info[:2]), "orig-prefix.txt")
              if os.path.exists(fn):
                  with open(fn, 'rU') as fp:
                      prefix = fp.read().strip()
  
          try:
              fmwk = macholib.dyld.framework_find(prefix)
          except ValueError:
              info = None
          else:
              info = macholib.dyld.framework_info(fmwk)
  
          if info is not None:
              dylib = info['name']
              runtime = os.path.join(info['location'], info['name'])
          else:
              dylib = 'libpython%s.dylib' % (sys.version[:3],)
              runtime = os.path.join(prefix, 'lib', dylib)
  
          return dylib, runtime
  
      def symlink(self, src, dst):
          try:
              os.remove(dst)
          except OSError:
              pass
          os.symlink(src, dst)
  
      def get_runtime_preferences(self, prefix=None, version=None):
          dylib, runtime = self.get_runtime(prefix=prefix, version=version)
          yield os.path.join('@executable_path', '..', 'Frameworks', dylib)
          if self.semi_standalone or self.alias:
              yield runtime
  
      def run(self):
          if get_config_var('PYTHONFRAMEWORK') is None:
              if not get_config_var('Py_ENABLE_SHARED'):
                  raise DistutilsPlatformError("This python does not have a shared library or framework")
  
              else:
                  # Issue .. in py2app's tracker, and issue .. in python's tracker: a unix-style shared
                  # library build did not read the application environment correctly. The collection of
                  # if statements below gives a clean error message when py2app is started, instead of
                  # building a bundle that will give a confusing error message when started.
                  msg = "py2app is not supported for a shared library build with this version of python"
                  if sys.version_info[:2] < (2,7):
                      raise DistutilsPlatformError(msg)
                  elif sys.version_info[:2] == (2,7) and sys.version[3] < 4:
                      raise DistutilsPlatformError(msg)
                  elif sys.version_info[0] == 3 and sys.version_info[1] < 2:
                      raise DistutilsPlatformError(msg)
                  elif sys.version_info[0] == 3 and sys.version_info[1] == 2 and sys.version_info[3] < 3:
                      raise DistutilsPlatformError(msg)
                  elif sys.version_info[0] == 3 and sys.version_info[1] == 3 and sys.version_info[3] < 1:
                      raise DistutilsPlatformError(msg)
  
          if hasattr(self.distribution, "install_requires") \
                  and self.distribution.install_requires:
  
              self.distribution.fetch_build_eggs(self.distribution.install_requires)
  
  
          build = self.reinitialize_command('build')
          build.build_base = self.bdist_base
          build.run()
          self.create_directories()
          self.fixup_distribution()
          self.initialize_plist()
  
          sys_old_path = sys.path[:]
          extra_paths = [
              os.path.dirname(target.script)
              for target in self.targets
          ]
          extra_paths.extend([build.build_platlib, build.build_lib])
          self.additional_paths = [
              os.path.abspath(p)
              for p in extra_paths
              if p is not None
          ]
          sys.path[:0] = self.additional_paths
  
          # this needs additional_paths
          self.initialize_prescripts()
  
          try:
              self._run()
          finally:
              sys.path = sys_old_path
  
  
      def iter_datamodels(self, resdir):
          for (path, files) in (normalize_data_file(fn) for fn in (self.datamodels or ())):
              path = fsencoding(path)
              for fn in files:
                  fn = fsencoding(fn)
                  basefn, ext = os.path.splitext(fn)
                  if ext != '.xcdatamodel':
                      basefn = fn
                      fn += '.xcdatamodel'
                  destfn = os.path.basename(basefn) + '.mom'
                  yield fn, os.path.join(resdir, path, destfn)
  
      def compile_datamodels(self, resdir):
          for src, dest in self.iter_datamodels(resdir):
              print("compile datamodel", src, "->", dest)
              self.mkpath(os.path.dirname(dest))
              momc(src, dest)
  
      def iter_mappingmodels(self, resdir):
          for (path, files) in (normalize_data_file(fn) for fn in (self.mappingmodels or ())):
              path = fsencoding(path)
              for fn in files:
                  fn = fsencoding(fn)
                  basefn, ext = os.path.splitext(fn)
                  if ext != '.xcmappingmodel':
                      basefn = fn
                      fn += '.xcmappingmodel'
                  destfn = os.path.basename(basefn) + '.cdm'
                  yield fn, os.path.join(resdir, path, destfn)
  
      def compile_mappingmodels(self, resdir):
          for src, dest in self.iter_mappingmodels(resdir):
              self.mkpath(os.path.dirname(dest))
              mapc(src, dest)
  
      def iter_extra_plugins(self):
          for item in self.include_plugins:
              if isinstance(item, (list, tuple)):
                  subdir, path = item
  
              else:
                  ext = os.path.splitext(item)[1]
                  try:
                      subdir = PLUGIN_SUFFIXES[ext]
                      path = item
                  except KeyError:
                      raise DistutilsOptionError("Cannot determine subdirectory for plugin %s"%(item,))
  
              yield path, os.path.join(subdir, os.path.basename(path))
  
      def iter_data_files(self):
          dist = self.distribution
          allres = chain(getattr(dist, 'data_files', ()) or (), self.resources)
          for (path, files) in (normalize_data_file(fn) for fn in allres):
              path = fsencoding(path)
              for fn in files:
                  fn = fsencoding(fn)
                  yield fn, os.path.join(path, os.path.basename(fn))
  
      def collect_scripts(self):
          # these contains file names
          scripts = set()
  
          for target in self.targets:
              scripts.add(target.script)
              scripts.update([
                  k for k in target.prescripts if isinstance(k, basestring)
              ])
              if hasattr(target, 'extra_scripts'):
                  scripts.update(target.extra_scripts)
  
          scripts.update(self.extra_scripts)
          return scripts
  
      def get_plist_options(self):
          result = dict(
              PyOptions=dict(
                  use_pythonpath=bool(self.use_pythonpath),
                  site_packages=bool(self.site_packages),
                  alias=bool(self.alias),
                  argv_emulation=bool(self.argv_emulation),
                  emulate_shell_environment=bool(self.emulate_shell_environment),
                  no_chdir=bool(self.no_chdir),
                  prefer_ppc=self.prefer_ppc,
                  verbose=self.verbose_interpreter,
                  use_faulthandler=self.use_faulthandler,
              ),
          )
          if self.optimize:
              result['PyOptions']['optimize'] = self.optimize
          return result
  
  
      def initialize_plist(self):
          plist = self.get_default_plist()
          for target in self.targets:
              plist.update(getattr(target, 'plist', {}))
          plist.update(self.plist)
          plist.update(self.get_plist_options())
  
          if self.iconfile:
              iconfile = self.iconfile
              if not os.path.exists(iconfile):
                  iconfile = iconfile + '.icns'
              if not os.path.exists(iconfile):
                  raise DistutilsOptionError("icon file must exist: %r"
                      % (self.iconfile,))
              self.resources.append(iconfile)
              plist['CFBundleIconFile'] = os.path.basename(iconfile)
          if self.prefer_ppc:
              plist['LSPrefersPPC'] = True
  
          self.plist = plist
          return plist
  
      def run_alias(self):
          self.app_files = []
          for target in self.targets:
  
              extra_scripts = list(self.extra_scripts)
              if hasattr(target, 'extra_scripts'):
                  extra_scripts.update(extra_scripts)
  
              dst = self.build_alias_executable(target, target.script, extra_scripts)
              self.app_files.append(dst)
  
              for fn in extra_scripts:
                  if fn.endswith('.py'):
                      fn = fn[:-3]
                  elif fn.endswith('.pyw'):
                      fn = fn[:-4]
  
                  src_fn = script_executable(arch=self.arch, secondary=True)
                  tgt_fn = os.path.join(target.appdir, 'Contents', 'MacOS', os.path.basename(fn))
                  mergecopy(src_fn, tgt_fn)
                  make_exec(tgt_fn)
  
      def collect_recipedict(self):
          return dict(iterRecipes())
  
      def get_modulefinder(self):
          if self.debug_modulegraph:
              debug = 4
          else:
              debug = 0
          return find_modules(
              scripts=self.collect_scripts(),
              includes=self.includes,
              packages=self.packages,
              excludes=self.excludes,
              debug=debug,
          )
  
      def collect_filters(self):
          return [has_filename_filter] + list(self.filters)
  
      def process_recipes(self, mf, filters, flatpackages, loader_files):
          rdict = self.collect_recipedict()
          while True:
              for name, check in rdict.items():
                  rval = check(self, mf)
                  if rval is None:
                      continue
                  # we can pull this off so long as we stop the iter
                  del rdict[name]
                  print('*** using recipe: %s ***' % (name,))
  
                  if rval.get('packages'):
                      self.packages.update(rval['packages'])
                      find_needed_modules(mf, packages=rval['packages'])
  
                  for pkg in rval.get('flatpackages', ()):
                      if isinstance(pkg, basestring):
                          pkg = (os.path.basename(pkg), pkg)
                      flatpackages[pkg[0]] = pkg[1]
                  filters.extend(rval.get('filters', ()))
                  loader_files.extend(rval.get('loader_files', ()))
                  newbootstraps = list(map(self.get_bootstrap,
                      rval.get('prescripts', ())))
  
                  if rval.get('includes'):
                      find_needed_modules(mf, includes=rval['includes'])
  
                  if rval.get('resources'):
                      self.resources.extend(rval['resources'])
  
                  for fn in newbootstraps:
                      if isinstance(fn, basestring):
                          mf.run_script(fn)
  
                  for target in self.targets:
                      target.prescripts.extend(newbootstraps)
                  break
              else:
                  break
  
      def _run(self):
          try:
              if self.alias:
                  self.run_alias()
              else:
                  self.run_normal()
          except:
              raise
              # XXX - remove when not debugging
              #       distutils sucks
              import pdb, sys, traceback
              traceback.print_exc()
              pdb.post_mortem(sys.exc_info()[2])
  
          print("Done!")
  
      def filter_dependencies(self, mf, filters):
          print("*** filtering dependencies ***")
          nodes_seen, nodes_removed, nodes_orphaned = mf.filterStack(filters)
          print('%d total' % (nodes_seen,))
          print('%d filtered' % (nodes_removed,))
          print('%d orphaned' % (nodes_orphaned,))
          print('%d remaining' % (nodes_seen - nodes_removed,))
  
      def get_appname(self):
          return self.plist['CFBundleName']
  
      def build_xref(self, mf, flatpackages):
          for target in self.targets:
              base = target.get_dest_base()
              appdir = os.path.join(self.dist_dir, os.path.dirname(base))
              appname = self.get_appname()
              dgraph = os.path.join(appdir, appname + '.html')
              print("*** creating dependency html: %s ***"
                  % (os.path.basename(dgraph),))
              with open(dgraph, 'w') as fp:
                  mf.create_xref(fp)
  
      def build_graph(self, mf, flatpackages):
          for target in self.targets:
              base = target.get_dest_base()
              appdir = os.path.join(self.dist_dir, os.path.dirname(base))
              appname = self.get_appname()
              dgraph = os.path.join(appdir, appname + '.dot')
              print("*** creating dependency graph: %s ***"
                  % (os.path.basename(dgraph),))
              with open(dgraph, 'w') as fp:
                  mf.graphreport(fp, flatpackages=flatpackages)
  
      def finalize_modulefinder(self, mf):
          for item in mf.flatten():
              if isinstance(item, Package) and item.filename == '-':
                  if sys.version_info[:2] <= (3,3):
                      fn = os.path.join(self.temp_dir, 'empty_package', '__init__.py')
                      if not os.path.exists(fn):
                          dn = os.path.dirname(fn)
                          if not os.path.exists(dn):
                              os.makedirs(dn)
                          with open(fn, 'w') as fp:
                              pass
  
                      item.filename = fn
  
          py_files, extensions = parse_mf_results(mf)
  
          # Remove all top-level scripts from the list of python files,
          # those get treated differently.
          py_files = [ item for item in py_files if not isinstance(item, Script) ]
  
          extensions = list(extensions)
          return py_files, extensions
  
      def collect_packagedirs(self):
          return list(filter(os.path.exists, [
              os.path.join(os.path.realpath(self.get_bootstrap(pkg)), '')
              for pkg in self.packages
          ]))
  
      def run_normal(self):
          mf = self.get_modulefinder()
          filters = self.collect_filters()
          flatpackages = {}
          loader_files = []
          self.process_recipes(mf, filters, flatpackages, loader_files)
  
          if self.debug_modulegraph:
              import pdb
              pdb.Pdb().set_trace()
  
          self.filter_dependencies(mf, filters)
  
          if self.graph:
              self.build_graph(mf, flatpackages)
          if self.xref:
              self.build_xref(mf, flatpackages)
  
  
          py_files, extensions = self.finalize_modulefinder(mf)
          pkgdirs = self.collect_packagedirs()
          self.create_binaries(py_files, pkgdirs, extensions, loader_files)
  
  
          missing = []
          syntax_error = []
          invalid_bytecode = []
          for module in mf.nodes():
              if isinstance(module, modulegraph.MissingModule):
                  if module.identifier != '__main__':
                      missing.append(module)
              elif isinstance(module, modulegraph.InvalidSourceModule):
                  syntax_error.append(module)
              elif hasattr(modulegraph, 'InvalidCompiledModule') and isinstance(module, modulegraph.InvalidCompiledModule):
                  invalid_bytecode.append(module)
  
          if missing:
              missing_unconditional = collections.defaultdict(set)
              missing_fromimport = collections.defaultdict(set)
              missing_fromimport_conditional = collections.defaultdict(set)
              missing_conditional = collections.defaultdict(set)
  
  
              for module in sorted(missing):
                  for m in mf.getReferers(module):
                      if m is None: continue # XXX
  
                      try:
                          ed = mf.edgeData(m, module)
                      except KeyError:
                          ed = None
  
                      if hasattr(modulegraph, 'DependencyInfo') and isinstance(ed, modulegraph.DependencyInfo):
                          c = missing_unconditional
                          if ed.conditional or ed.function:
                              if ed.fromlist:
                                  c = missing_fromimport_conditional
                              else:
                                  c = missing_conditional
  
                          elif ed.fromlist:
                              c = missing_fromimport
  
                          c[module.identifier].add(m.identifier)
  
                      else:
                          missing_unconditional[module.identifier].add(m.identifier)
  
              if missing_unconditional:
                  log.warn("Modules not found (unconditional imports):")
                  for m in sorted(missing_unconditional):
                      log.warn(" * %s (%s)" % (m, ", ".join(sorted(missing_unconditional[m]))))
                  log.warn("")
  
  
              if missing_conditional and not self.no_report_missing_conditional_import:
                  log.warn("Modules not found (conditional imports):")
                  for m in sorted(missing_conditional):
                      log.warn(" * %s (%s)" % (m, ", ".join(sorted(missing_conditional[m]))))
                  log.warn("")
  
              if self.report_missing_from_imports and (
                      missing_fromimport or (
                          not self.no_report_missing_conditional_import and missing_fromimport_conditional)):
                  log.warn("Modules not found ('from ... import y'):")
                  for m in sorted(missing_fromimport):
                      log.warn(" * %s (%s)" % (m, ", ".join(sorted(missing_fromimport[m]))))
  
                  if not self.no_report_missing_conditional_import and missing_fromimport_conditional:
                      log.warn("")
                      log.warn("Conditional:")
                      for m in sorted(missing_fromimport_conditional):
                          log.warn(" * %s (%s)" % (m, ", ".join(sorted(missing_fromimport_conditional[m]))))
  
                  log.warn("")
  
          if syntax_error:
              log.warn("Modules with syntax errors:")
              for module in sorted(syntax_error):
                  log.warn(" * %s"%(module.identifier))
  
              log.warn("")
  
          if invalid_bytecode:
              log.warn("Modules with invalid bytecode:")
              for module in sorted(invalid_bytecode):
                  log.warn(" * %s"%(module.identifier))
  
              log.warn("")
  
  
      def create_directories(self):
          bdist_base = self.bdist_base
          if self.semi_standalone:
              self.bdist_dir = os.path.join(bdist_base,
                  'python%s-semi_standalone' % (sys.version[:3],), 'app')
          else:
              self.bdist_dir = os.path.join(bdist_base,
                  'python%s-standalone' % (sys.version[:3],), 'app')
  
          if os.path.exists(self.bdist_dir):
              shutil.rmtree(self.bdist_dir)
  
          self.collect_dir = os.path.abspath(
              os.path.join(self.bdist_dir, "collect"))
          self.mkpath(self.collect_dir)
  
          self.temp_dir = os.path.abspath(os.path.join(self.bdist_dir, "temp"))
          self.mkpath(self.temp_dir)
  
          self.dist_dir = os.path.abspath(self.dist_dir)
          self.mkpath(self.dist_dir)
  
          self.lib_dir = os.path.join(self.bdist_dir,
              os.path.dirname(get_zipfile(self.distribution, self.semi_standalone)))
          self.mkpath(self.lib_dir)
  
          self.ext_dir = os.path.join(self.lib_dir, 'lib-dynload')
          self.mkpath(self.ext_dir)
  
          self.framework_dir = os.path.join(self.bdist_dir, 'Frameworks')
          self.mkpath(self.framework_dir)
  
      def create_binaries(self, py_files, pkgdirs, extensions, loader_files):
          print("*** create binaries ***")
          dist = self.distribution
          pkgexts = []
          copyexts = []
          extmap = {}
          def packagefilter(mod, pkgdirs=pkgdirs):
              fn = os.path.realpath(getattr(mod, 'filename', None))
              if fn is None:
                  return None
              for pkgdir in pkgdirs:
                  if fn.startswith(pkgdir):
                      return None
              return fn
          if pkgdirs:
              py_files = list(filter(packagefilter, py_files))
          for ext in extensions:
              fn = packagefilter(ext)
              if fn is None:
                  fn = os.path.realpath(getattr(ext, 'filename', None))
                  pkgexts.append(ext)
              else:
                  if '.' in ext.identifier:
                      py_files.append(self.create_loader(ext))
                  copyexts.append(ext)
              extmap[fn] = ext
  
          # byte compile the python modules into the target directory
          print("*** byte compile python files ***")
          byte_compile(py_files,
                       target_dir=self.collect_dir,
                       optimize=self.optimize,
                       force=self.force,
                       verbose=self.verbose,
                       dry_run=self.dry_run)
  
          for item in py_files:
              if not isinstance(item, Package): continue
              self.copy_package_data(item, self.collect_dir)
  
          self.lib_files = []
          self.app_files = []
  
          # create the shared zipfile containing all Python modules
          archive_name = os.path.join(self.lib_dir,
                                      get_zipfile(dist, self.semi_standalone))
  
          for path, files in loader_files:
              dest = os.path.join(self.collect_dir, path)
              self.mkpath(dest)
              for fn in files:
                  destfn = os.path.join(dest, os.path.basename(fn))
                  if os.path.isdir(fn):
                      self.copy_tree(fn, destfn, preserve_symlinks=False)
                  else:
                      self.copy_file(fn, destfn)
  
          arcname = self.make_lib_archive(archive_name,
              base_dir=self.collect_dir, verbose=self.verbose,
              dry_run=self.dry_run)
  
          # XXX: this doesn't work with python3
          #self.lib_files.append(arcname)
  
          # build the executables
          for target in self.targets:
              extra_scripts = list(self.extra_scripts)
              if hasattr(target, 'extra_scripts'):
                  extra_scripts.extend(target.extra_scripts)
              dst = self.build_executable(
                  target, arcname, pkgexts, copyexts, target.script, extra_scripts)
              exp = os.path.join(dst, 'Contents', 'MacOS')
              execdst = os.path.join(exp, 'python')
              if self.semi_standalone:
                  self.symlink(sys.executable, execdst)
              else:
                  if os.path.exists(os.path.join(sys.prefix, ".Python")):
                      fn = os.path.join(sys.prefix, "lib", "python%d.%d"%(sys.version_info[:2]), "orig-prefix.txt")
                      if os.path.exists(fn):
                          with open(fn, 'rU') as fp:
                              prefix = fp.read().strip()
  
                      rest_path = os.path.normpath(sys.executable)[len(os.path.normpath(sys.prefix))+1:]
                      if rest_path.startswith('.'):
                          rest_path = rest_path[1:]
  
                      if PYTHONFRAMEWORK:
                          # When we're using a python framework bin/python refers to a stub executable
                          # that we don't want use, we need the executable in Resources/Python.app
                          dpath = os.path.join(prefix, 'Resources', 'Python.app', 'Contents', 'MacOS')
                          self.copy_file(os.path.join(dpath, PYTHONFRAMEWORK), execdst)
  
  
                      else:
                          self.copy_file(os.path.join(prefix, rest_path), execdst)
  
                  else:
                      if PYTHONFRAMEWORK:
                          # When we're using a python framework bin/python refers to a stub executable
                          # that we don't want use, we need the executable in Resources/Python.app
                          dpath = os.path.join(sys.prefix, 'Resources', 'Python.app', 'Contents', 'MacOS')
                          self.copy_file(os.path.join(dpath, PYTHONFRAMEWORK), execdst)
  
                      else:
                          self.copy_file(sys.executable, execdst)
  
              if not self.debug_skip_macholib:
                  if self.force_system_tk:
                      print("force system tk")
                      resdir = os.path.join(dst, 'Contents', 'Resources')
                      pydir = os.path.join(resdir, 'lib', 'python%s.%s'%(sys.version_info[:2]))
                      ext_dir = os.path.join(pydir, os.path.basename(self.ext_dir))
                      tkinter_path = os.path.join(ext_dir, '_tkinter.so')
                      if os.path.exists(tkinter_path):
                          rewrite_tkinter_load_commands(tkinter_path)
                      else:
                          print("tkinter not found at", tkinter_path)
  
  
                  mm = PythonStandalone(self, dst, executable_path=exp)
                  dylib, runtime = self.get_runtime()
                  if self.semi_standalone:
                      mm.excludes.append(runtime)
                  else:
                      mm.mm.run_file(runtime)
                  for exclude in self.dylib_excludes:
                      info = macholib.dyld.framework_info(exclude)
                      if info is not None:
                          exclude = os.path.join(
                              info['location'], info['shortname'] + '.framework')
                      mm.excludes.append(exclude)
                  for fmwk in self.frameworks:
                      mm.mm.run_file(fmwk)
                  platfiles = mm.run()
                  if self.strip:
                      platfiles = self.strip_dsym(platfiles)
                      self.strip_files(platfiles)
              self.app_files.append(dst)
  
      def copy_package_data(self, package, target_dir):
          """
          Copy any package data in a python package into the target_dir.
  
          This is a bit of a hack, it would be better to identify python eggs
          and copy those in whole.
          """
          exts = [ i[0] for i in imp.get_suffixes() ]
          exts.append('.py')
          exts.append('.pyc')
          exts.append('.pyo')
          def datafilter(item):
              for e in exts:
                  if item.endswith(e):
                      return False
              return True
  
          target_dir = os.path.join(target_dir, *(package.identifier.split('.')))
          for dname in package.packagepath:
              filenames = list(filter(datafilter, zipio.listdir(dname)))
              for fname in filenames:
                  if fname in ('.svn', 'CVS', '.hg', '.git'):
                      # Scrub revision manager junk
                      continue
                  if fname in ('__pycache__',):
                      # Ignore PEP 3147 bytecode cache
                      continue
                  if fname.startswith('.') and fname.endswith('.swp'):
                      # Ignore vim(1) temporary files
                      continue
                  if fname.endswith('~') or fname.endswith('.orig'):
                      # Ignore backup files for common tools (hg, emacs, ...)
                      continue
                  pth = os.path.join(dname, fname)
  
                  # Check if we have found a package, exclude those
                  if zipio.isdir(pth):
                      # XXX: the 'and not' part is wrong, need to fix zipio.isdir
                      for p in zipio.listdir(pth):
                          if p.startswith('__init__.') and p[8:] in exts:
                              break
  
                      else:
                          if os.path.isfile(pth):
                              # Avoid extracting a resource file that happens
                              # to be zipfile.
                              # XXX: Need API in zipio for nicer code.
                              copy_file(pth, os.path.join(target_dir, fname))
                          else:
                              copy_tree(pth, os.path.join(target_dir, fname))
                      continue
  
                  elif zipio.isdir(pth) and (
                          zipio.isfile(os.path.join(pth, '__init__.py'))
                       or zipio.isfile(os.path.join(pth, '__init__.pyc'))
                       or zipio.isfile(os.path.join(pth, '__init__.pyo'))):
                      # Subdirectory is a python package, these will get included later on
                      # when the subpackage itself is included, ignore for now.
                      pass
  
                  else:
                      copy_file(pth, os.path.join(target_dir, fname))
  
  
      def strip_dsym(self, platfiles):
          """ Remove .dSYM directories in the bundled application """
  
          #
          # .dSYM directories are contain detached debugging information and
          # should be completely removed when the "strip" option is specified.
          #
          if self.dry_run:
              return platfiles
          for dirpath, dnames, fnames in os.walk(self.appdir):
              for nm in list(dnames):
                  if nm.endswith('.dSYM'):
                      print("removing debug info: %s/%s"%(dirpath, nm))
                      shutil.rmtree(os.path.join(dirpath, nm))
                      dnames.remove(nm)
          return [file for file in platfiles if '.dSYM' not in file]
  
      def strip_files(self, files):
          unstripped = 0
          stripfiles = []
          for fn in files:
              unstripped += os.stat(fn).st_size
              stripfiles.append(fn)
              log.info('stripping %s', os.path.basename(fn))
          strip_files(stripfiles, dry_run=self.dry_run, verbose=self.verbose)
          stripped = 0
          for fn in stripfiles:
              stripped += os.stat(fn).st_size
          log.info('stripping saved %d bytes (%d / %d)',
              unstripped - stripped, stripped, unstripped)
  
      def copy_dylib(self, src, dst):
          # will be copied from the framework?
          if src != sys.executable:
              force, self.force = self.force, True
              self.copy_file(src, dst)
              self.force = force
          return dst
  
      def copy_versioned_framework(self, info, dst):
          # XXX - Boy is this ugly, but it makes sense because the developer
          #       could have both Python 2.3 and 2.4, or Tk 8.4 and 8.5, etc.
          #       Saves a good deal of space, and I'm pretty sure this ugly
          #       hack is correct in the general case.
          version = info['version']
          if version is None:
              return self.raw_copy_framework(info, dst)
          short = info['shortname'] + '.framework'
          infile = os.path.join(info['location'], short)
          outfile = os.path.join(dst, short)
          vsplit = os.path.join(infile, 'Versions').split(os.sep)
          def condition(src, vsplit=vsplit, version=version):
              srcsplit = src.split(os.sep)
              if (
                      len(srcsplit) > len(vsplit) and
                      srcsplit[:len(vsplit)] == vsplit and
                      srcsplit[len(vsplit)] != version and
                      not os.path.islink(src)
                  ):
                  return False
              # Skip Headers, .svn, and CVS dirs
              return framework_copy_condition(src)
  
          return self.copy_tree(infile, outfile,
              preserve_symlinks=True, condition=condition)
  
      def copy_framework(self, info, dst):
          force, self.force = self.force, True
          if info['shortname'] == PYTHONFRAMEWORK:
              self.copy_python_framework(info, dst)
          else:
              self.copy_versioned_framework(info, dst)
          self.force = force
          return os.path.join(dst, info['name'])
  
      def raw_copy_framework(self, info, dst):
          short = info['shortname'] + '.framework'
          infile = os.path.join(info['location'], short)
          outfile = os.path.join(dst, short)
          return self.copy_tree(infile, outfile,
              preserve_symlinks=True, condition=framework_copy_condition)
  
      def copy_python_framework(self, info, dst):
          # XXX - In this particular case we know exactly what we can
          #       get away with.. should this be extended to the general
          #       case?  Per-framework recipes?
          includedir = get_config_var('CONFINCLUDEPY')
          configdir = get_config_var('LIBPL')
  
  
          if includedir is None:
              includedir = 'python%d.%d'%(sys.version_info[:2])
          else:
              includedir = os.path.basename(includedir)
  
          if configdir is None:
              configdir = 'config'
          else:
              configdir = os.path.basename(configdir)
  
  
          indir = os.path.dirname(os.path.join(info['location'], info['name']))
          outdir = os.path.dirname(os.path.join(dst, info['name']))
          self.mkpath(os.path.join(outdir, 'Resources'))
          pydir = 'python%s.%s'%(sys.version_info[:2])
  
          # Create a symlink "for Python.frameworks/Versions/Current". This
          # is required for the Mac App-store.
          os.symlink(
                  os.path.basename(outdir),
                  os.path.join(os.path.dirname(outdir), "Current"))
  
          # Likewise for two links in the root of the framework:
          os.symlink(
              'Versions/Current/Resources',
              os.path.join(os.path.dirname(os.path.dirname(outdir)), 'Resources'))
          os.symlink(
              os.path.join('Versions/Current', PYTHONFRAMEWORK),
              os.path.join(os.path.dirname(os.path.dirname(outdir)), PYTHONFRAMEWORK))
  
  
          # Experiment for issue 57
          if not os.path.exists(os.path.join(indir, 'include')):
              alt = os.path.join(indir, 'Versions/Current')
              if os.path.exists(os.path.join(alt, 'include')):
                  indir = alt
  
          # distutils looks for some files relative to sys.executable, which
          # means they have to be in the framework...
          self.mkpath(os.path.join(outdir, 'include'))
          self.mkpath(os.path.join(outdir, 'include', includedir))
          self.mkpath(os.path.join(outdir, 'lib'))
          self.mkpath(os.path.join(outdir, 'lib', pydir))
          self.mkpath(os.path.join(outdir, 'lib', pydir, configdir))
  
  
          fmwkfiles = [
              os.path.basename(info['name']),
              'Resources/Info.plist',
              'include/%s/pyconfig.h'%(includedir),
          ]
          if '_sysconfigdata' not in sys.modules:
              fmwkfiles.append(
                 'lib/%s/%s/Makefile'%(pydir, configdir)
              )
  
          for fn in fmwkfiles:
              self.copy_file(
                  os.path.join(indir, fn),
                  os.path.join(outdir, fn))
  
  
  
      def fixup_distribution(self):
          dist = self.distribution
  
          # Trying to obtain app and plugin from dist for backward compatibility
          # reasons.
          app = dist.app
          plugin = dist.plugin
          # If we can get suitable values from self.app and self.plugin, we prefer
          # them.
          if self.app is not None or self.plugin is not None:
              app = self.app
              plugin = self.plugin
  
          # Convert our args into target objects.
          dist.app = FixupTargets(app, "script")
          dist.plugin = FixupTargets(plugin, "script")
          if dist.app and dist.plugin:
              # XXX - support apps and plugins?
              raise DistutilsOptionError(
                  "You must specify either app or plugin, not both")
          elif dist.app:
              self.style = 'app'
              self.targets = dist.app
          elif dist.plugin:
              self.style = 'plugin'
              self.targets = dist.plugin
          else:
              raise DistutilsOptionError(
                  "You must specify either app or plugin")
          if len(self.targets) != 1:
              # XXX - support multiple targets?
              raise DistutilsOptionError(
                  "Multiple targets not currently supported")
          if not self.extension:
              self.extension = '.' + self.style
  
          # make sure all targets use the same directory, this is
          # also the directory where the pythonXX.dylib must reside
          paths = set()
          for target in self.targets:
              paths.add(os.path.dirname(target.get_dest_base()))
  
          if len(paths) > 1:
              raise DistutilsOptionError(
                    "all targets must use the same directory: %s" %
                    ([p for p in paths],))
          if paths:
              app_dir = paths.pop() # the only element
              if os.path.isabs(app_dir):
                  raise DistutilsOptionError(
                        "app directory must be relative: %s" % (app_dir,))
              self.app_dir = os.path.join(self.dist_dir, app_dir)
              self.mkpath(self.app_dir)
          else:
              # Do we allow to specify no targets?
              # We can at least build a zipfile...
              self.app_dir = self.lib_dir
  
      def initialize_prescripts(self):
          prescripts = []
          prescripts.append('reset_sys_path')
          if self.semi_standalone:
              prescripts.append('semi_standalone_path')
  
          if 0 and sys.version_info[:2] >= (3, 2) and not self.alias:
              # Python 3.2 or later requires a more complicated
              # bootstrap
              prescripts.append('import_encodings')
  
          if os.path.exists(os.path.join(sys.prefix, ".Python")):
              # We're in a virtualenv, which means sys.path
              # will be broken in alias builds unless we fix
              # it.
              if self.alias or self.semi_standalone:
                  prescripts.append("virtualenv")
                  prescripts.append(StringIO('_fixup_virtualenv(%r)' % (sys.real_prefix,)))
  
              if self.site_packages or self.alias:
                  import site
                  global_site_packages = not os.path.exists(
                          os.path.join(os.path.dirname(site.__file__), 'no-global-site-packages.txt'))
                  prescripts.append('virtualenv_site_packages')
                  prescripts.append(StringIO('_site_packages(%r, %r, %d)' % (
                      sys.prefix, sys.real_prefix, global_site_packages)))
  
          elif self.site_packages or self.alias:
              prescripts.append('site_packages')
  
          if is_system():
              prescripts.append('system_path_extras')
  
          #if self.style == 'app':
          #    prescripts.append('setup_pkgresource')
  
          included_subpkg = [pkg for pkg in self.packages if '.' in pkg]
          if included_subpkg:
              prescripts.append('setup_included_subpackages')
              prescripts.append(StringIO('_path_hooks = %r'%(
                  included_subpkg)))
  
          if self.emulate_shell_environment:
              prescripts.append('emulate_shell_environment')
  
          if self.argv_emulation and self.style == 'app':
              prescripts.append('argv_emulation')
              if 'CFBundleDocumentTypes' not in self.plist:
                  self.plist['CFBundleDocumentTypes'] = [
                      {
                          'CFBundleTypeOSTypes' : [
                              '****',
                              'fold',
                              'disk',
                          ],
                          'CFBundleTypeRole': 'Viewer'
                      },
                  ]
  
          if self.argv_inject is not None:
              prescripts.append('argv_inject')
              prescripts.append(
                  StringIO('_argv_inject(%r)\n' % (self.argv_inject,)))
  
          if self.style == 'app' and not self.no_chdir:
              prescripts.append('chdir_resource')
          if not self.alias:
              prescripts.append('disable_linecache')
              prescripts.append('boot_' + self.style)
          else:
  
              # Add ctypes prescript because it is needed to
              # find libraries in the bundle, but we don't run
              # recipes and hence the ctypes recipe is not used
              # for alias builds.
              prescripts.append('ctypes_setup')
  
              if self.additional_paths:
                  prescripts.append('path_inject')
                  prescripts.append(
                      StringIO('_path_inject(%r)\n' % (self.additional_paths,)))
              prescripts.append('boot_alias' + self.style)
          newprescripts = []
          for s in prescripts:
              if isinstance(s, basestring):
                  newprescripts.append(
                      self.get_bootstrap('py2app.bootstrap.' + s))
              else:
                  newprescripts.append(s)
  
          for target in self.targets:
              prescripts = getattr(target, 'prescripts', [])
              target.prescripts = newprescripts + prescripts
  
  
      def get_bootstrap(self, bootstrap):
          if isinstance(bootstrap, basestring):
              if not os.path.exists(bootstrap):
                  bootstrap = imp_find_module(bootstrap)[1]
          return bootstrap
  
      def get_bootstrap_data(self, bootstrap):
          bootstrap = self.get_bootstrap(bootstrap)
          if not isinstance(bootstrap, basestring):
              return bootstrap.getvalue()
          else:
              with open(bootstrap, 'rU') as fp:
                  return fp.read()
  
      def create_pluginbundle(self, target, script, use_runtime_preference=True):
          base = target.get_dest_base()
          appdir = os.path.join(self.dist_dir, os.path.dirname(base))
          appname = self.get_appname()
          print("*** creating plugin bundle: %s ***" % (appname,))
          if self.runtime_preferences and use_runtime_preference:
              self.plist.setdefault(
                  'PyRuntimeLocations', self.runtime_preferences)
          appdir, plist = create_pluginbundle(
              appdir,
              appname,
              plist=self.plist,
              extension=self.extension,
              arch=self.arch,
          )
          appdir = fsencoding(appdir)
          resdir = os.path.join(appdir, 'Contents', 'Resources')
          return appdir, resdir, plist
  
      def create_appbundle(self, target, script, use_runtime_preference=True):
          base = target.get_dest_base()
          appdir = os.path.join(self.dist_dir, os.path.dirname(base))
          appname = self.get_appname()
          print("*** creating application bundle: %s ***" % (appname,))
          if self.runtime_preferences and use_runtime_preference:
              self.plist.setdefault(
                  'PyRuntimeLocations', self.runtime_preferences)
          pythonInfo = self.plist.setdefault('PythonInfoDict', {})
          py2appInfo = pythonInfo.setdefault('py2app', {}).update(dict(
              alias=bool(self.alias),
          ))
          appdir, plist = create_appbundle(
              appdir,
              appname,
              plist=self.plist,
              extension=self.extension,
              arch=self.arch,
          )
          appdir = fsencoding(appdir)
          resdir = os.path.join(appdir, 'Contents', 'Resources')
          return appdir, resdir, plist
  
      def create_bundle(self, target, script, use_runtime_preference=True):
          fn = getattr(self, 'create_%sbundle' % (self.style,))
          return fn(
              target,
              script,
              use_runtime_preference=use_runtime_preference
          )
  
      def iter_frameworks(self):
          for fn in self.frameworks:
              fmwk = macholib.dyld.framework_info(fn)
              if fmwk is None:
                  yield fn
              else:
                  basename = fmwk['shortname'] + '.framework'
                  yield os.path.join(fmwk['location'], basename)
  
      def build_alias_executable(self, target, script, extra_scripts):
          # Build an alias executable for the target
          appdir, resdir, plist = self.create_bundle(target, script)
  
          # symlink python executable
          execdst = os.path.join(appdir, 'Contents', 'MacOS', 'python')
          prefixPathExecutable = os.path.join(sys.prefix, 'bin', 'python')
          if os.path.exists(prefixPathExecutable):
              pyExecutable = prefixPathExecutable
          else:
              pyExecutable = sys.executable
          self.symlink(pyExecutable, execdst)
  
          # make PYTHONHOME
          pyhome = os.path.join(resdir, 'lib', 'python' + sys.version[:3])
          realhome = os.path.join(sys.prefix, 'lib', 'python' + sys.version[:3])
          makedirs(pyhome)
          if self.optimize:
              self.symlink('../../site.pyo', os.path.join(pyhome, 'site.pyo'))
          else:
              self.symlink('../../site.pyc', os.path.join(pyhome, 'site.pyc'))
          self.symlink(
              os.path.join(realhome, 'config'),
              os.path.join(pyhome, 'config'))
  
  
          # symlink data files
          # XXX: fixme: need to integrate automatic data conversion
          for src, dest in self.iter_data_files():
              dest = os.path.join(resdir, dest)
              if src == dest:
                  continue
              makedirs(os.path.dirname(dest))
              try:
                  copy_resource(src, dest, dry_run=self.dry_run, symlink=1)
              except:
                  import traceback
                  traceback.print_exc()
                  raise
  
          plugindir = os.path.join(appdir, 'Contents', 'Library')
          for src, dest in self.iter_extra_plugins():
              dest = os.path.join(plugindir, dest)
              if src == dest:
                  continue
  
              makedirs(os.path.dirname(dest))
              try:
                  copy_resource(src, dest, dry_run=self.dry_run)
              except:
                  import traceback
                  traceback.print_exc()
                  raise
  
          # symlink frameworks
          for src in self.iter_frameworks():
              dest = os.path.join(
                  appdir, 'Contents', 'Frameworks', os.path.basename(src))
              if src == dest:
                  continue
              makedirs(os.path.dirname(dest))
              self.symlink(os.path.abspath(src), dest)
  
          self.compile_datamodels(resdir)
          self.compile_mappingmodels(resdir)
  
          bootfn = '__boot__'
          bootfile = open(os.path.join(resdir, bootfn + '.py'), 'w')
          for fn in target.prescripts:
              bootfile.write(self.get_bootstrap_data(fn))
              bootfile.write('\n\n')
          bootfile.write("DEFAULT_SCRIPT=%r\n"%(os.path.realpath(script),))
          script_map = {}
          for fn in extra_scripts:
              tgt = os.path.realpath(fn)
              fn = os.path.basename(fn)
              if fn.endswith('.py'):
                  script_map[fn[:-3]] = tgt
              elif fn.endswith('.py'):
                  script_map[fn[:-4]] = tgt
              else:
                  script_map[fn] = tgt
  
          bootfile.write("SCRIPT_MAP=%r\n"%(script_map,))
          bootfile.write('try:\n')
          bootfile.write('    _run()\n')
          bootfile.write('except KeyboardInterrupt:\n')
          bootfile.write('    pass\n')
          bootfile.close()
  
          target.appdir = appdir
          return appdir
  
      def build_executable(self, target, arcname, pkgexts, copyexts, script, extra_scripts):
          # Build an executable for the target
          appdir, resdir, plist = self.create_bundle(target, script)
          self.appdir = appdir
          self.resdir = resdir
          self.plist = plist
  
          for fn in extra_scripts:
              if fn.endswith('.py'):
                  fn = fn[:-3]
              elif fn.endswith('.pyw'):
                  fn = fn[:-4]
  
              src_fn = script_executable(arch=self.arch, secondary=True)
              tgt_fn = os.path.join(self.appdir, 'Contents', 'MacOS', os.path.basename(fn))
              mergecopy(src_fn, tgt_fn)
              make_exec(tgt_fn)
  
  
          site_path = os.path.join(resdir, 'site.py')
          byte_compile([
              SourceModule('site', site_path),
              ],
              target_dir=resdir,
              optimize=self.optimize,
              force=self.force,
              verbose=self.verbose,
              dry_run=self.dry_run)
          if not self.dry_run:
              os.unlink(site_path)
  
  
          includedir = get_config_var('CONFINCLUDEPY')
          configdir = get_config_var('LIBPL')
  
  
          if includedir is None:
              includedir = 'python%d.%d'%(sys.version_info[:2])
          else:
              includedir = os.path.basename(includedir)
  
          if configdir is None:
              configdir = 'config'
          else:
              configdir = os.path.basename(configdir)
  
          self.compile_datamodels(resdir)
          self.compile_mappingmodels(resdir)
  
          bootfn = '__boot__'
          bootfile = open(os.path.join(resdir, bootfn + '.py'), 'w')
          for fn in target.prescripts:
              bootfile.write(self.get_bootstrap_data(fn))
              bootfile.write('\n\n')
  
          bootfile.write("DEFAULT_SCRIPT=%r\n"%(os.path.basename(script),))
  
          script_map = {}
          for fn in extra_scripts:
              fn = os.path.basename(fn)
              if fn.endswith('.py'):
                  script_map[fn[:-3]] = fn
              elif fn.endswith('.py'):
                  script_map[fn[:-4]] = fn
              else:
                  script_map[fn] = fn
  
          bootfile.write("SCRIPT_MAP=%r\n"%(script_map,))
          bootfile.write('_run()\n')
          bootfile.close()
  
          self.copy_file(script, resdir)
          for fn in extra_scripts:
              self.copy_file(fn, resdir)
  
          pydir = os.path.join(resdir, 'lib', 'python%s.%s'%(sys.version_info[:2]))
          if sys.version_info[0] == 2 or self.semi_standalone:
              arcdir = os.path.join(resdir, 'lib', 'python' + sys.version[:3])
          else:
              arcdir = os.path.join(resdir, 'lib')
          realhome = os.path.join(sys.prefix, 'lib', 'python' + sys.version[:3])
          self.mkpath(pydir)
  
          # The site.py file needs to be a two locations
          # 1) in lib/pythonX.Y, to be found during normal startup and
          #    by the 'python' executable
          # 2) in the resources directory next to the script for
          #    semistandalone builds (the lib/pythonX.Y directory is too
          #    late on sys.path to be found in that case).
          #
          if self.optimize:
              self.symlink('../../site.pyo', os.path.join(pydir, 'site.pyo'))
          else:
              self.symlink('../../site.pyc', os.path.join(pydir, 'site.pyc'))
          cfgdir = os.path.join(pydir, configdir)
          realcfg = os.path.join(realhome, configdir)
          real_include = os.path.join(sys.prefix, 'include')
          if self.semi_standalone:
              self.symlink(realcfg, cfgdir)
              self.symlink(real_include, os.path.join(resdir, 'include'))
          else:
              self.mkpath(cfgdir)
              if '_sysconfigdata' not in sys.modules:
                  # Recent enough versions of Python 2.7 and 3.x have
                  # an _sysconfigdata module and don't need the Makefile
                  # to provide the sysconfig data interface. Don't copy
                  # them.
                  for fn in 'Makefile', 'Setup', 'Setup.local', 'Setup.config':
                      rfn = os.path.join(realcfg, fn)
                      if os.path.exists(rfn):
                         self.copy_file(rfn, os.path.join(cfgdir, fn))
  
              inc_dir = os.path.join(resdir, 'include', includedir)
              self.mkpath(inc_dir)
              self.copy_file(get_config_h_filename(),
                             os.path.join(inc_dir, 'pyconfig.h'))
  
  
          self.copy_file(arcname, arcdir)
          if sys.version_info[0] != 2:
              import zlib
              self.copy_file(zlib.__file__, os.path.dirname(arcdir))
  
          ext_dir = os.path.join(pydir, os.path.basename(self.ext_dir))
          self.copy_tree(self.ext_dir, ext_dir, preserve_symlinks=True)
          self.copy_tree(self.framework_dir,
              os.path.join(appdir, 'Contents', 'Frameworks'),
              preserve_symlinks=True)
          for pkg_name in self.packages:
              pkg = self.get_bootstrap(pkg_name)
              print('XXXX', pkg_name, pkg)
  
              if self.semi_standalone:
                  # For semi-standalone builds don't copy packages
                  # from the stdlib into the app bundle, even when
                  # they are mentioned in self.packages.
                  p = Package(pkg_name, pkg)
                  if not not_stdlib_filter(p):
                      continue
  
              dst = os.path.join(pydir, pkg_name)
              self.mkpath(dst)
              self.copy_tree(pkg, dst)
  
              # FIXME: The python files should be bytecompiled
              #        here (see issue 101)
  
          for copyext in copyexts:
              fn = os.path.join(ext_dir,
                  (copyext.identifier.replace('.', os.sep) +
                  os.path.splitext(copyext.filename)[1])
              )
              self.mkpath(os.path.dirname(fn))
              copy_file(copyext.filename, fn, dry_run=self.dry_run)
  
          for src, dest in self.iter_data_files():
              dest = os.path.join(resdir, dest)
              if src == dest:
                  continue
              makedirs(os.path.dirname(dest))
              copy_resource(src, dest, dry_run=self.dry_run)
  
          plugindir = os.path.join(appdir, 'Contents', 'Library')
          for src, dest in self.iter_extra_plugins():
              dest = os.path.join(plugindir, dest)
              if src == dest:
                  continue
  
              makedirs(os.path.dirname(dest))
              copy_resource(src, dest, dry_run=self.dry_run)
  
  
          target.appdir = appdir
          return appdir
  
      def create_loader(self, item):
          # Hm, how to avoid needless recreation of this file?
          slashname = item.identifier.replace('.', os.sep)
          pathname = os.path.join(self.temp_dir, "%s.py" % slashname)
          if os.path.exists(pathname):
              if self.verbose:
                  print("skipping python loader for extension %r"
                      % (item.identifier,))
          else:
              self.mkpath(os.path.dirname(pathname))
              # and what about dry_run?
              if self.verbose:
                  print("creating python loader for extension %r"
                      % (item.identifier,))
  
              fname = slashname + os.path.splitext(item.filename)[1]
              source = make_loader(fname)
              if not self.dry_run:
                  with open(pathname, "w") as fp:
                      fp.write(source)
              else:
                  return
          return SourceModule(item.identifier, pathname)
  
      def make_lib_archive(self, zip_filename, base_dir, verbose=0,
                           dry_run=0):
          # Like distutils "make_archive", except we can specify the
          # compression to use - default is ZIP_STORED to keep the
          # runtime performance up.
          # Also, we don't append '.zip' to the filename.
          from distutils.dir_util import mkpath
          mkpath(os.path.dirname(zip_filename), dry_run=dry_run)
  
          if self.compressed:
              compression = zipfile.ZIP_DEFLATED
          else:
              compression = zipfile.ZIP_STORED
          if not dry_run:
              z = zipfile.ZipFile(zip_filename, "w",
                                  compression=compression)
              save_cwd = os.getcwd()
              os.chdir(base_dir)
              for dirpath, dirnames, filenames in os.walk('.'):
                  if filenames:
                      # Ensure that there are directory entries for
                      # all directories in the zipfile. This is a
                      # workaround for <http://bugs.python.org/issue14905>:
                      # zipimport won't consider 'pkg/foo.py' to be in
                      # namespace package 'pkg' unless there is an
                      # entry for the directory (or there is a
                      # pkg/__init__.py file as well)
                      z.write(dirpath, dirpath)
  
                  for fn in filenames:
                      path = os.path.normpath(os.path.join(dirpath, fn))
                      if os.path.isfile(path):
                          z.write(path, path)
  
              os.chdir(save_cwd)
              z.close()
  
          return zip_filename
  
      def copy_tree(self, infile, outfile,
                     preserve_mode=1, preserve_times=1, preserve_symlinks=0,
                     level=1, condition=None):
          """Copy an entire directory tree respecting verbose, dry-run,
          and force flags.
  
          This version doesn't bork on existing symlinks
          """
          return copy_tree(
              infile, outfile,
              preserve_mode,preserve_times,preserve_symlinks,
              not self.force,
              dry_run=self.dry_run,
              condition=condition)
  