1""" Copies the build output of a custom python interpreter to a directory 2 structure that mirrors that of an official Python distribution. 3 4 -------------------------------------------------------------------------- 5 File: install_custom_python.py 6 7 Overview: Most users build LLDB by linking against the standard 8 Python distribution installed on their system. Occasionally 9 a user may want to build their own version of Python, and on 10 platforms such as Windows this is a hard requirement. This 11 script will take the build output of a custom interpreter and 12 install it into a canonical structure that mirrors that of an 13 official Python distribution, thus allowing PYTHONHOME to be 14 set appropriately. 15 16 Gotchas: None. 17 18 Copyright: None. 19 -------------------------------------------------------------------------- 20 21""" 22 23import argparse 24import itertools 25import os 26import shutil 27import sys 28 29def copy_one_file(dest_dir, source_dir, filename): 30 source_path = os.path.join(source_dir, filename) 31 dest_path = os.path.join(dest_dir, filename) 32 print 'Copying file %s ==> %s...' % (source_path, dest_path) 33 shutil.copyfile(source_path, dest_path) 34 35def copy_named_files(dest_dir, source_dir, files, extensions, copy_debug_suffix_also): 36 for (file, ext) in itertools.product(files, extensions): 37 copy_one_file(dest_dir, source_dir, file + '.' + ext) 38 if copy_debug_suffix_also: 39 copy_one_file(dest_dir, source_dir, file + '_d.' + ext) 40 41def copy_subdirectory(dest_dir, source_dir, subdir): 42 dest_dir = os.path.join(dest_dir, subdir) 43 source_dir = os.path.join(source_dir, subdir) 44 print 'Copying directory %s ==> %s...' % (source_dir, dest_dir) 45 shutil.copytree(source_dir, dest_dir) 46 47def copy_distro(dest_dir, dest_subdir, source_dir, source_prefix): 48 dest_dir = os.path.join(dest_dir, dest_subdir) 49 50 print 'Copying distribution %s ==> %s' % (source_dir, dest_dir) 51 52 os.mkdir(dest_dir) 53 PCbuild_dir = os.path.join(source_dir, 'PCbuild') 54 if source_prefix: 55 PCbuild_dir = os.path.join(PCbuild_dir, source_prefix) 56 # First copy the files that go into the root of the new distribution. This 57 # includes the Python executables, python27(_d).dll, and relevant PDB files. 58 print 'Copying Python executables...' 59 copy_named_files(dest_dir, PCbuild_dir, ['w9xpopen'], ['exe', 'pdb'], False) 60 copy_named_files(dest_dir, PCbuild_dir, ['python_d', 'pythonw_d'], ['exe'], False) 61 copy_named_files(dest_dir, PCbuild_dir, ['python', 'pythonw'], ['exe', 'pdb'], False) 62 copy_named_files(dest_dir, PCbuild_dir, ['python27'], ['dll', 'pdb'], True) 63 64 # Next copy everything in the Include directory. 65 print 'Copying Python include directory' 66 copy_subdirectory(dest_dir, source_dir, 'Include') 67 68 # Copy Lib folder (builtin Python modules) 69 print 'Copying Python Lib directory' 70 copy_subdirectory(dest_dir, source_dir, 'Lib') 71 72 # Copy tools folder. These are probably not necessary, but we copy them anyway to 73 # match an official distribution as closely as possible. Note that we don't just copy 74 # the subdirectory recursively. The source distribution ships with many more tools 75 # than what you get by installing python regularly. We only copy the tools that appear 76 # in an installed distribution. 77 tools_dest_dir = os.path.join(dest_dir, 'Tools') 78 tools_source_dir = os.path.join(source_dir, 'Tools') 79 os.mkdir(tools_dest_dir) 80 copy_subdirectory(tools_dest_dir, tools_source_dir, 'i18n') 81 copy_subdirectory(tools_dest_dir, tools_source_dir, 'pynche') 82 copy_subdirectory(tools_dest_dir, tools_source_dir, 'scripts') 83 copy_subdirectory(tools_dest_dir, tools_source_dir, 'versioncheck') 84 copy_subdirectory(tools_dest_dir, tools_source_dir, 'webchecker') 85 86 pyd_names = ['_ctypes', '_ctypes_test', '_elementtree', '_multiprocessing', '_socket', 87 '_testcapi', 'pyexpat', 'select', 'unicodedata', 'winsound'] 88 89 # Copy builtin extension modules (pyd files) 90 dlls_dir = os.path.join(dest_dir, 'DLLs') 91 os.mkdir(dlls_dir) 92 print 'Copying DLLs directory' 93 copy_named_files(dlls_dir, PCbuild_dir, pyd_names, ['pyd', 'pdb'], True) 94 95 # Copy libs folder (implibs for the pyd files) 96 libs_dir = os.path.join(dest_dir, 'libs') 97 os.mkdir(libs_dir) 98 print 'Copying libs directory' 99 copy_named_files(libs_dir, PCbuild_dir, pyd_names, ['lib'], False) 100 copy_named_files(libs_dir, PCbuild_dir, ['python27'], ['lib'], True) 101 102 103parser = argparse.ArgumentParser(description='Install a custom Python distribution') 104parser.add_argument('--source', required=True, help='The root of the source tree where Python is built.') 105parser.add_argument('--dest', required=True, help='The location to install the Python distributions.') 106parser.add_argument('--overwrite', default=False, action='store_true', help='If the destination directory already exists, destroys its contents first.') 107parser.add_argument('--silent', default=False, action='store_true', help='If --overwite was specified, suppress confirmation before deleting a directory tree.') 108 109args = parser.parse_args() 110 111args.source = os.path.normpath(args.source) 112args.dest = os.path.normpath(args.dest) 113 114if not os.path.exists(args.source): 115 print 'The source directory %s does not exist. Exiting...' 116 sys.exit(1) 117 118if os.path.exists(args.dest): 119 if not args.overwrite: 120 print 'The destination directory \'%s\' already exists and --overwrite was not specified. Exiting...' % args.dest 121 sys.exit(1) 122 while not args.silent: 123 print 'Ok to recursively delete \'%s\' and all contents (Y/N)? Choosing Y will permanently delete the contents.' % args.dest 124 result = str.upper(sys.stdin.read(1)) 125 if result == 'N': 126 print 'Unable to copy files to the destination. The destination already exists.' 127 sys.exit(1) 128 elif result == 'Y': 129 break 130 shutil.rmtree(args.dest) 131 132os.mkdir(args.dest) 133copy_distro(args.dest, 'x86', args.source, None) 134copy_distro(args.dest, 'x64', args.source, 'amd64') 135