home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / bin / dh_python2 < prev    next >
Encoding:
Text File  |  2012-05-04  |  22.4 KB  |  552 lines

  1. #! /usr/bin/python
  2. # -*- coding: UTF-8 -*- vim: et ts=4 sw=4
  3.  
  4. # Copyright ┬⌐ 2010 Piotr O┼╝arowski <piotr@debian.org>
  5. #
  6. # Permission is hereby granted, free of charge, to any person obtaining a copy
  7. # of this software and associated documentation files (the "Software"), to deal
  8. # in the Software without restriction, including without limitation the rights
  9. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. # copies of the Software, and to permit persons to whom the Software is
  11. # furnished to do so, subject to the following conditions:
  12. #
  13. # The above copyright notice and this permission notice shall be included in
  14. # all copies or substantial portions of the Software.
  15. #
  16. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. # THE SOFTWARE.
  23.  
  24. from __future__ import with_statement
  25. import logging
  26. import os
  27. import re
  28. import sys
  29. from filecmp import dircmp, cmpfiles
  30. from optparse import OptionParser, SUPPRESS_HELP
  31. from os.path import isdir, islink, exists, join
  32. from shutil import rmtree, copy as fcopy
  33. from stat import ST_MODE, S_IXUSR, S_IXGRP, S_IXOTH
  34. sys.path.insert(1, '/usr/share/python/')
  35. from debpython.debhelper import DebHelper
  36. from debpython.depends import Dependencies
  37. from debpython.version import SUPPORTED, DEFAULT, \
  38.     debsorted, getver, vrepr, parse_pycentral_vrange, \
  39.     get_requested_versions, parse_vrange, vrange_str
  40. from debpython.pydist import validate as validate_pydist, \
  41.                              PUBLIC_DIR_RE
  42. from debpython.tools import sitedir, relative_symlink, \
  43.                             shebang2pyver, clean_egg_name
  44. from debpython.option import Option
  45.  
  46. # initialize script
  47. logging.basicConfig(format='%(levelname).1s: %(module)s:%(lineno)d: '
  48.                            '%(message)s')
  49. log = logging.getLogger(__name__)
  50. os.umask(022)
  51. EGGnPTH_RE = re.compile(r'(.*?)(-py\d\.\d+)?(.*?)(\.egg-info|\.pth)$')
  52.  
  53. """TODO: move it to manpage
  54. Examples:
  55.     dh_python2
  56.     dh_python2 -V 2.4- # public files only, Python >= 2.4
  57.     dh_python2 -p python-foo -X 'bar.*' /usr/lib/baz/ # private files in
  58.                                                        python-foo package
  59. """
  60.  
  61. # naming conventions used in the file:
  62. # * version - tuple of integers
  63. # * ver - string representation of version
  64. # * vrange - version range, pair of max and min versions
  65. # * fn - file name (without path)
  66. # * fpath - file path
  67.  
  68.  
  69. ### FILES ######################################################
  70. def fix_locations(package):
  71.     """Move files to the right location."""
  72.     found_versions = {}
  73.     for version in SUPPORTED:
  74.         ver = vrepr(version)
  75.         to_check = [i % ver for i in (\
  76.                     'usr/local/lib/python%s/site-packages',
  77.                     'usr/local/lib/python%s/dist-packages',
  78.                     'var/lib/python-support/python%s',
  79.                     'usr/lib/pymodules/python%s')]
  80.         if version >= (2, 6):
  81.             to_check.append("usr/lib/python%s/site-packages" % ver)
  82.         dstdir = sitedir(version, package)
  83.  
  84.         for location in to_check:
  85.             srcdir = "debian/%s/%s" % (package, location)
  86.             if isdir(srcdir):
  87.                 if ver in found_versions:
  88.                     log.error('files for version %s '
  89.                               'found in two locations:\n %s\n %s',
  90.                               ver, location, found_versions[ver])
  91.                     exit(2)
  92.                 log.warn('Python %s should install files in %s. '
  93.                          'Did you forget "--install-layout=deb"?',
  94.                          ver, sitedir(version))
  95.                 if not isdir(dstdir):
  96.                     os.makedirs(dstdir)
  97.                 # TODO: what about relative symlinks?
  98.                 log.debug('moving files from %s to %s', srcdir, dstdir)
  99.                 os.renames(srcdir, dstdir)
  100.                 found_versions[ver] = location
  101.  
  102.         # do the same with debug locations
  103.         dbg_to_check = ['usr/lib/debug/%s' % i for i in to_check]
  104.         dbg_to_check.append("usr/lib/debug/usr/lib/pyshared/python%s" % ver)
  105.         dstdir = sitedir(version, package, gdb=True)
  106.  
  107.         for location in dbg_to_check:
  108.             srcdir = "debian/%s/%s" % (package, location)
  109.             if isdir(srcdir):
  110.                 if not isdir(dstdir):
  111.                     os.makedirs(dstdir)
  112.                 log.debug('moving files from %s to %s', srcdir, dstdir)
  113.                 os.renames(srcdir, dstdir)
  114.  
  115.  
  116. ### SHARING FILES ##############################################
  117. def share(package, stats, options):
  118.     """Move files to /usr/share/pyshared/ if possible."""
  119.     if package.endswith('-dbg'):
  120.         # nothing to share in debug packages
  121.         return
  122.     pubvers = debsorted(i for i in stats['public_vers'] if i[0] == 2)
  123.     if len(pubvers) > 1:
  124.         for pos, version1 in enumerate(pubvers):
  125.             dir1 = sitedir(version1, package)
  126.             for version2 in pubvers[pos + 1:]:
  127.                 dir2 = sitedir(version2, package)
  128.                 dc = dircmp(dir1, dir2)
  129.                 share_2x(dir1, dir2, dc)
  130.     elif len(pubvers) == 1:
  131.         # TODO: remove this once file conflicts will not be needed anymore
  132.         move_to_pyshared(sitedir(pubvers[0], package))
  133.  
  134.     for version in stats['public_ext']:
  135.         create_ext_links(sitedir(version, package))
  136.  
  137.     if options.guess_versions and pubvers:
  138.         versions = get_requested_versions(options.vrange)
  139.         for version in (i for i in versions if i[0] == 2):
  140.             if version not in pubvers:
  141.                 log.debug('guessing files for Python %s', vrepr(version))
  142.                 versions_without_ext = debsorted(set(pubvers) -\
  143.                                                  stats['public_ext'])
  144.                 if not versions_without_ext:
  145.                     log.error('you most probably have to build extension '
  146.                               'for python%s.', vrepr(version))
  147.                     exit(12)
  148.                 srcver = versions_without_ext[0]
  149.                 if srcver in stats['public_vers']:
  150.                     stats['public_vers'].add(version)
  151.                 share_2x(sitedir(srcver, package), sitedir(version, package))
  152.  
  153.  
  154. def move_to_pyshared(dir1):
  155.     # dir1 starts with debian/packagename/usr/lib/pythonX.Y/*-packages/
  156.     debian, package, path = dir1.split('/', 2)
  157.     dstdir = join(debian, package, 'usr/share/pyshared/', \
  158.                   '/'.join(dir1.split('/')[6:]))
  159.  
  160.     fext = lambda fname: fname.rsplit('.', 1)[-1]
  161.  
  162.     for i in os.listdir(dir1):
  163.         fpath1 = join(dir1, i)
  164.         if isdir(fpath1):
  165.             if any(fn for fn in os.listdir(fpath1) if fext(fn) != 'so'):
  166.                 # at least one file that is not an extension
  167.                 move_to_pyshared(join(dir1, i))
  168.         else:
  169.             if fext(i) == 'so':
  170.                 continue
  171.             fpath2 = join(dstdir, i)
  172.             if not exists(fpath2):
  173.                 if not exists(dstdir):
  174.                     os.makedirs(dstdir)
  175.                 os.rename(fpath1, fpath2)
  176.                 relative_symlink(fpath2, fpath1)
  177.  
  178.  
  179. def create_ext_links(dir1):
  180.     """Create extension symlinks in /usr/lib/pyshared/pythonX.Y.
  181.  
  182.     These symlinks are used to let dpkg detect file conflicts with
  183.     python-support and python-central packages.
  184.     """
  185.  
  186.     debian, package, path = dir1.split('/', 2)
  187.     python, _, module_subpath = path[8:].split('/', 2)
  188.     dstdir = join(debian, package, 'usr/lib/pyshared/', python, module_subpath)
  189.  
  190.     for i in os.listdir(dir1):
  191.         fpath1 = join(dir1, i)
  192.         if isdir(fpath1):
  193.             create_ext_links(fpath1)
  194.         elif i.rsplit('.', 1)[-1] == 'so':
  195.             fpath2 = join(dstdir, i)
  196.             if exists(fpath2):
  197.                 continue
  198.             if not exists(dstdir):
  199.                 os.makedirs(dstdir)
  200.             relative_symlink(fpath1, join(dstdir, i))
  201.  
  202.  
  203. def share_2x(dir1, dir2, dc=None):
  204.     """Move common files to pyshared and create symlinks in original
  205.     locations."""
  206.     debian, package, path = dir2.split('/', 2)
  207.     # dir1 starts with debian/packagename/usr/lib/pythonX.Y/*-packages/
  208.     dstdir = join(debian, package, 'usr/share/pyshared/', \
  209.                   '/'.join(dir1.split('/')[6:]))
  210.     if not exists(dstdir):
  211.         os.makedirs(dstdir)
  212.     if dc is None:  # guess/copy mode
  213.         if not exists(dir2):
  214.             os.makedirs(dir2)
  215.         common_dirs = []
  216.         common_files = []
  217.         for i in os.listdir(dir1):
  218.             if isdir(join(dir1, i)):
  219.                 common_dirs.append([i, None])
  220.             else:
  221.                 # directories with .so files will be blocked earlier
  222.                 common_files.append(i)
  223.     else:
  224.         common_dirs = dc.subdirs.iteritems()
  225.         common_files = dc.common_files
  226.         # dircmp returns common names only, lets check files more carefully...
  227.         common_files = cmpfiles(dir1, dir2, common_files, shallow=False)[0]
  228.  
  229.     for fn in common_files:
  230.         if fn.endswith('.so'):
  231.             # in unlikely case where extensions are exactly the same
  232.             continue
  233.         fpath1 = join(dir1, fn)
  234.         fpath2 = join(dir2, fn)
  235.         fpath3 = join(dstdir, fn)
  236.         # do not touch symlinks created by previous loop or other tools
  237.         if dc and not islink(fpath1):
  238.             # replace with a link to pyshared
  239.             os.rename(fpath1, fpath3)
  240.             relative_symlink(fpath3, fpath1)
  241.         if dc is None:  # guess/copy mode
  242.             if islink(fpath1):
  243.                 # ralative links will work as well, it's always the same level
  244.                 os.symlink(os.readlink(fpath1), fpath2)
  245.             else:
  246.                 if exists(fpath3):
  247.                     # cannot share it, pyshared contains another copy
  248.                     fcopy(fpath1, fpath2)
  249.                 else:
  250.                     # replace with a link to pyshared
  251.                     os.rename(fpath1, fpath3)
  252.                     relative_symlink(fpath3, fpath1)
  253.                     relative_symlink(fpath3, fpath2)
  254.         else:
  255.             os.remove(fpath2)
  256.             relative_symlink(fpath3, fpath2)
  257.     for dn, dc in common_dirs:
  258.         share_2x(join(dir1, dn), join(dir2, dn), dc)
  259.  
  260.  
  261. ### PACKAGE DETAILS ############################################
  262. def scan(package, dname=None):
  263.     """Gather statistics about Python files in given package."""
  264.     r = {'requires.txt': set(),
  265.          'shebangs': set(),
  266.          'public_vers': set(),
  267.          'private_dirs': {},
  268.          'compile': False,
  269.          'public_ext': set()}
  270.  
  271.     dbg_package = package.endswith('-dbg')
  272.  
  273.     if not dname:
  274.         proot = "debian/%s" % package
  275.         if dname is False:
  276.             private_to_check = []
  277.         else:
  278.             private_to_check = [i % package for i in
  279.                                 ('usr/lib/%s', 'usr/lib/games/%s',
  280.                                 'usr/share/%s', 'usr/share/games/%s')]
  281.     else:
  282.         proot = join('debian', package, dname.strip('/'))
  283.         private_to_check = [dname[1:]]
  284.  
  285.     for root, dirs, file_names in os.walk(proot):
  286.         # ignore Python 3.X locations
  287.         if '/usr/lib/python3' in root or\
  288.            '/usr/local/lib/python3' in root:
  289.             # warn only once
  290.             warn = root[root.find('/lib/python'):].count('/') == 2
  291.             if warn:
  292.                 log.warning('Python 3.x location detected, '
  293.                             'please use dh_python3: %s', root)
  294.             continue
  295.  
  296.         bin_dir = private_dir = None
  297.         public_dir = PUBLIC_DIR_RE.match(root)
  298.         if public_dir:
  299.             version = getver(public_dir.group(1))
  300.             if root.endswith('-packages'):
  301.                 r['public_vers'].add(version)
  302.         else:
  303.             version = False
  304.             for i in private_to_check:
  305.                 if root.startswith(join('debian', package, i)):
  306.                     private_dir = '/' + i
  307.                     break
  308.             else:  # i.e. not public_dir and not private_dir
  309.                 if len(root.split('/', 6)) < 6 and (\
  310.                    root.endswith('/sbin') or root.endswith('/bin') or\
  311.                    root.endswith('/usr/games')):
  312.                    # /(s)bin or /usr/(s)bin or /usr/games
  313.                     bin_dir = root
  314.  
  315.         # handle some EGG related data (.egg-info dirs)
  316.         for name in dirs:
  317.             if name.endswith('.egg-info'):
  318.                 if dbg_package:
  319.                     rmtree(join(root, name))
  320.                     dirs.pop(dirs.index(name))
  321.                     continue
  322.                 clean_name = clean_egg_name(name)
  323.                 if clean_name != name:
  324.                     log.info('renaming %s to %s', name, clean_name)
  325.                     os.rename(join(root, name), join(root, clean_name))
  326.         if root.endswith('.egg-info') and 'requires.txt' in file_names:
  327.             r['requires.txt'].add(join(root, 'requires.txt'))
  328.             continue
  329.  
  330.         # check files
  331.         for fn in file_names:
  332.             fext = fn.rsplit('.', 1)[-1]
  333.             if fext in ('pyc', 'pyo'):
  334.                 os.remove(join(root, fn))
  335.                 continue
  336.             if public_dir:
  337.                 if dbg_package and fext not in ('so', 'h'):
  338.                     os.remove(join(root, fn))
  339.                     continue
  340.  
  341.             elif private_dir:
  342.                 mode = os.stat(join(root, fn))[ST_MODE]
  343.                 if mode is S_IXUSR or mode is S_IXGRP or mode is S_IXOTH:
  344.                     res = shebang2pyver(join(root, fn))
  345.                     if res:
  346.                         r['private_dirs'].setdefault(private_dir, {})\
  347.                             .setdefault('shebangs', set()).add(res)
  348.  
  349.             if public_dir or private_dir:
  350.                 if fext == 'so':
  351.                     (r if public_dir else
  352.                      r['private_dirs'].setdefault(private_dir, {}))\
  353.                     .setdefault('public_ext', set()).add(version)
  354.                     continue
  355.                 elif fext == 'py':
  356.                     (r if public_dir else
  357.                      r['private_dirs'].setdefault(private_dir, {}))\
  358.                     ['compile'] = True
  359.                     continue
  360.  
  361.             # .egg-info files
  362.             if fn.endswith('.egg-info'):
  363.                 clean_name = clean_egg_name(fn)
  364.                 if clean_name != fn:
  365.                     log.info('renaming %s to %s', fn, clean_name)
  366.                     os.rename(join(root, fn), join(root, clean_name))
  367.                 continue
  368.             # search for scripts in bin dirs
  369.             if bin_dir:
  370.                 fpath = join(root, fn)
  371.                 res = shebang2pyver(fpath)
  372.                 if res:
  373.                     r['shebangs'].add(res)
  374.  
  375.     if dbg_package:
  376.         # remove empty directories in -dbg packages
  377.         proot = proot + '/usr/lib'
  378.         for root, dirs, file_names in os.walk(proot, topdown=False):
  379.             if '-packages/' in root and not file_names:
  380.                 try:
  381.                     os.rmdir(root)
  382.                 except:
  383.                     pass
  384.  
  385.     log.debug("package %s details = %s", package, r)
  386.     return r
  387.  
  388.  
  389. ################################################################
  390. def main():
  391.     usage = '%prog -p PACKAGE [-V [X.Y][-][A.B]] DIR_OR_FILE [-X REGEXPR]\n'
  392.     parser = OptionParser(usage, version='%prog 2.0~beta1',
  393.                                    option_class=Option)
  394.     parser.add_option('--no-guessing-versions', action='store_false',
  395.         dest='guess_versions', default=True,
  396.         help='disable guessing other supported Python versions')
  397.     parser.add_option('--no-guessing-deps', action='store_false',
  398.         dest='guess_deps', default=True, help='disable guessing dependencies')
  399.     parser.add_option('--skip-private', action='store_true',
  400.         dest='skip_private', default=False,
  401.         help='don\'t check private directories')
  402.     parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
  403.         default=False, help='turn verbose mode on')
  404.     # arch=False->arch:all only, arch=True->arch:any only, None->all of them
  405.     parser.add_option('-i', '--indep', action='store_false',
  406.         dest='arch', default=None,
  407.         help='act on architecture independent packages')
  408.     parser.add_option('-a', '--arch', action='store_true',
  409.         dest='arch', help='act on architecture dependent packages')
  410.     parser.add_option('-q', '--quiet', action='store_false', dest='verbose',
  411.         help='be quiet')
  412.     parser.add_option('-p', '--package', action='append', dest='package',
  413.         help='act on the package named PACKAGE')
  414.     parser.add_option('-N', '--no-package', action='append', dest='no_package',
  415.         help='do not act on the specified package')
  416.     parser.add_option('-V', type='version_range', dest='vrange',
  417.         help='specify list of supported Python versions. ' +\
  418.              'See pycompile(1) for examples')
  419.     parser.add_option('-X', '--exclude', action='append', dest='regexpr',
  420.         help='exclude items that match given REGEXPR. You may use this option '
  421.              'multiple times to build up a list of things to exclude.')
  422.     parser.add_option('--depends', action='append', dest='depends',
  423.         help='translate given requirements into Debian dependencies '
  424.              'and add them to ${python:Depends}. '
  425.              'Use it for missing items in requires.txt.')
  426.     parser.add_option('--recommends', action='append', dest='recommends',
  427.         help='translate given requirements into Debian '
  428.              'dependencies and add them to ${python:Recommends}')
  429.     parser.add_option('--suggests', action='append', dest='suggests',
  430.         help='translate given requirements into Debian '
  431.              'dependencies and add them to ${python:Suggests}')
  432.     # ignore some debhelper options:
  433.     parser.add_option('-O', help=SUPPRESS_HELP)
  434.  
  435.     (options, args) = parser.parse_args()
  436.     # regexpr option type is not used so lets check patterns here
  437.     for pattern in options.regexpr or []:
  438.         # fail now rather than at runtime
  439.         try:
  440.             pattern = re.compile(pattern)
  441.         except:
  442.             log.error('regular expression is not valid: %s', pattern)
  443.             exit(1)
  444.  
  445.     if not options.vrange and exists('debian/pyversions'):
  446.         log.debug('parsing version range from debian/pyversions')
  447.         with open('debian/pyversions') as fp:
  448.             for line in fp:
  449.                 line = line.strip()
  450.                 if line and not line.startswith('#'):
  451.                     options.vrange = parse_vrange(line)
  452.                     break
  453.  
  454.     # disable PyDist if dh_pydeb is used
  455.     if options.guess_deps:
  456.         try:
  457.             fp = open('debian/rules', 'r')
  458.         except IOError:
  459.             log.warning('cannot open debian/rules file')
  460.         else:
  461.             if re.compile('\n\s*dh_pydeb').search(fp.read()):
  462.                 log.warning('dh_pydeb detected, PyDist feature disabled')
  463.                 options.guess_deps = False
  464.  
  465.     private_dir = None if not args else args[0]
  466.     # TODO: support more than one private dir at the same time (see :meth:scan)
  467.     if options.skip_private:
  468.         private_dir = False
  469.  
  470.     if options.verbose or os.environ.get('DH_VERBOSE') == '1':
  471.         log.setLevel(logging.DEBUG)
  472.         log.debug('argv: %s', sys.argv)
  473.         log.debug('options: %s', options)
  474.         log.debug('args: %s', args)
  475.  
  476.     dh = DebHelper(options.package, options.no_package)
  477.     if not options.vrange and dh.python_version:
  478.         options.vrange = parse_pycentral_vrange(dh.python_version)
  479.  
  480.     for package, pdetails in dh.packages.iteritems():
  481.         if options.arch is False and pdetails['arch'] != 'all' or \
  482.            options.arch is True and pdetails['arch'] == 'all':
  483.             continue
  484.         log.debug('processing package %s...', package)
  485.         fix_locations(package)
  486.         stats = scan(package, private_dir)
  487.         share(package, stats, options)
  488.  
  489.         dependencies = Dependencies(package,
  490.                                     dh.packages[package]['uses_breaks'])
  491.         dependencies.parse(stats, options)
  492.         dependencies.export_to(dh)
  493.  
  494.         if stats['public_vers']:
  495.             dh.addsubstvar(package, 'python:Versions', \
  496.                            ', '.join(sorted(vrepr(stats['public_vers']))))
  497.             ps = package.split('-', 1)
  498.             if len(ps) > 1 and ps[0] == 'python':
  499.                 dh.addsubstvar(package, 'python:Provides', \
  500.                            ', '.join("python%s-%s" % (i, ps[1])\
  501.                            for i in sorted(vrepr(stats['public_vers']))))
  502.  
  503.         pyclean_added = False  # invoke pyclean only once in maintainer script
  504.         if stats['compile']:
  505.             dh.autoscript(package, 'preinst', 'preinst-pycentral-clean', '')
  506.             dh.autoscript(package, 'postinst', 'postinst-pycompile', '')
  507.             dh.autoscript(package, 'prerm', 'prerm-pyclean', '')
  508.             pyclean_added = True
  509.  
  510.         for pdir, details in stats['private_dirs'].iteritems():
  511.             if not details.get('compile'):
  512.                 continue
  513.             if not pyclean_added:
  514.                 dh.autoscript(package, 'prerm', 'prerm-pyclean', '')
  515.                 pyclean_added = True
  516.  
  517.             args = pdir
  518.  
  519.             ext_for = details.get('public_ext')
  520.             if ext_for is None:  # no extension
  521.                 if options.vrange:
  522.                     args += " -V %s" % vrange_str(options.vrange)
  523.             elif False in ext_for:  # extension's version not detected
  524.                 if options.vrange and '-' not in vrange_str(options.vrange):
  525.                     ver = vrange_str(options.vrange)
  526.                 else:  # try shebang or default Python version
  527.                     ver = (list(v for i, v in details.get('shebangs', [])
  528.                            if v) or [None])[0] or DEFAULT
  529.                 args += " -V %s" % vrepr(ver)
  530.             else:
  531.                 args += " -V %s" % vrepr(ext_for.pop())
  532.  
  533.             for pattern in options.regexpr or []:
  534.                 args += " -X '%s'" % pattern.replace("'", r"\'")
  535.  
  536.             dh.autoscript(package, 'postinst', 'postinst-pycompile', args)
  537.  
  538.         pydist_file = join('debian', "%s.pydist" % package)
  539.         if exists(pydist_file):
  540.             if not validate_pydist(pydist_file, True):
  541.                 log.warning("%s.pydist file is invalid", package)
  542.             else:
  543.                 dstdir = join('debian', package, 'usr/share/python/dist/')
  544.                 if not exists(dstdir):
  545.                     os.makedirs(dstdir)
  546.                 fcopy(pydist_file, join(dstdir, package))
  547.  
  548.     dh.save()
  549.  
  550. if __name__ == '__main__':
  551.     main()
  552.