| """ Copies the build output of a custom python interpreter to a directory | 
 |     structure that mirrors that of an official Python distribution. | 
 |  | 
 |     -------------------------------------------------------------------------- | 
 |     File:           install_custom_python.py | 
 |  | 
 |     Overview:       Most users build LLDB by linking against the standard | 
 |                     Python distribution installed on their system.  Occasionally | 
 |                     a user may want to build their own version of Python, and on | 
 |                     platforms such as Windows this is a hard requirement.  This | 
 |                     script will take the build output of a custom interpreter and | 
 |                     install it into a canonical structure that mirrors that of an | 
 |                     official Python distribution, thus allowing PYTHONHOME to be | 
 |                     set appropriately. | 
 |  | 
 |     Gotchas:        None. | 
 |  | 
 |     Copyright:      None. | 
 |     -------------------------------------------------------------------------- | 
 |  | 
 | """ | 
 |  | 
 | import argparse | 
 | import itertools | 
 | import os | 
 | import shutil | 
 | import sys | 
 |  | 
 |  | 
 | def copy_one_file(dest_dir, source_dir, filename): | 
 |     source_path = os.path.join(source_dir, filename) | 
 |     dest_path = os.path.join(dest_dir, filename) | 
 |     print 'Copying file %s ==> %s...' % (source_path, dest_path) | 
 |     shutil.copyfile(source_path, dest_path) | 
 |  | 
 |  | 
 | def copy_named_files( | 
 |         dest_dir, | 
 |         source_dir, | 
 |         files, | 
 |         extensions, | 
 |         copy_debug_suffix_also): | 
 |     for (file, ext) in itertools.product(files, extensions): | 
 |         copy_one_file(dest_dir, source_dir, file + '.' + ext) | 
 |         if copy_debug_suffix_also: | 
 |             copy_one_file(dest_dir, source_dir, file + '_d.' + ext) | 
 |  | 
 |  | 
 | def copy_subdirectory(dest_dir, source_dir, subdir): | 
 |     dest_dir = os.path.join(dest_dir, subdir) | 
 |     source_dir = os.path.join(source_dir, subdir) | 
 |     print 'Copying directory %s ==> %s...' % (source_dir, dest_dir) | 
 |     shutil.copytree(source_dir, dest_dir) | 
 |  | 
 |  | 
 | def copy_distro(dest_dir, dest_subdir, source_dir, source_prefix): | 
 |     dest_dir = os.path.join(dest_dir, dest_subdir) | 
 |  | 
 |     print 'Copying distribution %s ==> %s' % (source_dir, dest_dir) | 
 |  | 
 |     os.mkdir(dest_dir) | 
 |     PCbuild_dir = os.path.join(source_dir, 'PCbuild') | 
 |     if source_prefix: | 
 |         PCbuild_dir = os.path.join(PCbuild_dir, source_prefix) | 
 |     # First copy the files that go into the root of the new distribution. This | 
 |     # includes the Python executables, python27(_d).dll, and relevant PDB | 
 |     # files. | 
 |     print 'Copying Python executables...' | 
 |     copy_named_files( | 
 |         dest_dir, PCbuild_dir, ['w9xpopen'], [ | 
 |             'exe', 'pdb'], False) | 
 |     copy_named_files( | 
 |         dest_dir, PCbuild_dir, [ | 
 |             'python_d', 'pythonw_d'], ['exe'], False) | 
 |     copy_named_files( | 
 |         dest_dir, PCbuild_dir, [ | 
 |             'python', 'pythonw'], [ | 
 |             'exe', 'pdb'], False) | 
 |     copy_named_files(dest_dir, PCbuild_dir, ['python27'], ['dll', 'pdb'], True) | 
 |  | 
 |     # Next copy everything in the Include directory. | 
 |     print 'Copying Python include directory' | 
 |     copy_subdirectory(dest_dir, source_dir, 'Include') | 
 |  | 
 |     # Copy Lib folder (builtin Python modules) | 
 |     print 'Copying Python Lib directory' | 
 |     copy_subdirectory(dest_dir, source_dir, 'Lib') | 
 |  | 
 |     # Copy tools folder.  These are probably not necessary, but we copy them anyway to | 
 |     # match an official distribution as closely as possible.  Note that we don't just copy | 
 |     # the subdirectory recursively.  The source distribution ships with many more tools | 
 |     # than what you get by installing python regularly.  We only copy the tools that appear | 
 |     # in an installed distribution. | 
 |     tools_dest_dir = os.path.join(dest_dir, 'Tools') | 
 |     tools_source_dir = os.path.join(source_dir, 'Tools') | 
 |     os.mkdir(tools_dest_dir) | 
 |     copy_subdirectory(tools_dest_dir, tools_source_dir, 'i18n') | 
 |     copy_subdirectory(tools_dest_dir, tools_source_dir, 'pynche') | 
 |     copy_subdirectory(tools_dest_dir, tools_source_dir, 'scripts') | 
 |     copy_subdirectory(tools_dest_dir, tools_source_dir, 'versioncheck') | 
 |     copy_subdirectory(tools_dest_dir, tools_source_dir, 'webchecker') | 
 |  | 
 |     pyd_names = [ | 
 |         '_ctypes', | 
 |         '_ctypes_test', | 
 |         '_elementtree', | 
 |         '_multiprocessing', | 
 |         '_socket', | 
 |         '_testcapi', | 
 |         'pyexpat', | 
 |         'select', | 
 |         'unicodedata', | 
 |         'winsound'] | 
 |  | 
 |     # Copy builtin extension modules (pyd files) | 
 |     dlls_dir = os.path.join(dest_dir, 'DLLs') | 
 |     os.mkdir(dlls_dir) | 
 |     print 'Copying DLLs directory' | 
 |     copy_named_files(dlls_dir, PCbuild_dir, pyd_names, ['pyd', 'pdb'], True) | 
 |  | 
 |     # Copy libs folder (implibs for the pyd files) | 
 |     libs_dir = os.path.join(dest_dir, 'libs') | 
 |     os.mkdir(libs_dir) | 
 |     print 'Copying libs directory' | 
 |     copy_named_files(libs_dir, PCbuild_dir, pyd_names, ['lib'], False) | 
 |     copy_named_files(libs_dir, PCbuild_dir, ['python27'], ['lib'], True) | 
 |  | 
 |  | 
 | parser = argparse.ArgumentParser( | 
 |     description='Install a custom Python distribution') | 
 | parser.add_argument( | 
 |     '--source', | 
 |     required=True, | 
 |     help='The root of the source tree where Python is built.') | 
 | parser.add_argument( | 
 |     '--dest', | 
 |     required=True, | 
 |     help='The location to install the Python distributions.') | 
 | parser.add_argument( | 
 |     '--overwrite', | 
 |     default=False, | 
 |     action='store_true', | 
 |     help='If the destination directory already exists, destroys its contents first.') | 
 | parser.add_argument( | 
 |     '--silent', | 
 |     default=False, | 
 |     action='store_true', | 
 |     help='If --overwite was specified, suppress confirmation before deleting a directory tree.') | 
 |  | 
 | args = parser.parse_args() | 
 |  | 
 | args.source = os.path.normpath(args.source) | 
 | args.dest = os.path.normpath(args.dest) | 
 |  | 
 | if not os.path.exists(args.source): | 
 |     print 'The source directory %s does not exist.  Exiting...' | 
 |     sys.exit(1) | 
 |  | 
 | if os.path.exists(args.dest): | 
 |     if not args.overwrite: | 
 |         print 'The destination directory \'%s\' already exists and --overwrite was not specified.  Exiting...' % args.dest | 
 |         sys.exit(1) | 
 |     while not args.silent: | 
 |         print 'Ok to recursively delete \'%s\' and all contents (Y/N)?  Choosing Y will permanently delete the contents.' % args.dest | 
 |         result = str.upper(sys.stdin.read(1)) | 
 |         if result == 'N': | 
 |             print 'Unable to copy files to the destination.  The destination already exists.' | 
 |             sys.exit(1) | 
 |         elif result == 'Y': | 
 |             break | 
 |     shutil.rmtree(args.dest) | 
 |  | 
 | os.mkdir(args.dest) | 
 | copy_distro(args.dest, 'x86', args.source, None) | 
 | copy_distro(args.dest, 'x64', args.source, 'amd64') |