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 29 30def copy_one_file(dest_dir, source_dir, filename): 31 source_path = os.path.join(source_dir, filename) 32 dest_path = os.path.join(dest_dir, filename) 33 print("Copying file %s ==> %s..." % (source_path, dest_path)) 34 shutil.copyfile(source_path, dest_path) 35 36 37def copy_named_files(dest_dir, source_dir, files, extensions, copy_debug_suffix_also): 38 for file, ext in itertools.product(files, extensions): 39 copy_one_file(dest_dir, source_dir, file + "." + ext) 40 if copy_debug_suffix_also: 41 copy_one_file(dest_dir, source_dir, file + "_d." + ext) 42 43 44def copy_subdirectory(dest_dir, source_dir, subdir): 45 dest_dir = os.path.join(dest_dir, subdir) 46 source_dir = os.path.join(source_dir, subdir) 47 print("Copying directory %s ==> %s..." % (source_dir, dest_dir)) 48 shutil.copytree(source_dir, dest_dir) 49 50 51def copy_distro(dest_dir, dest_subdir, source_dir, source_prefix): 52 dest_dir = os.path.join(dest_dir, dest_subdir) 53 54 print("Copying distribution %s ==> %s" % (source_dir, dest_dir)) 55 56 os.mkdir(dest_dir) 57 PCbuild_dir = os.path.join(source_dir, "PCbuild") 58 if source_prefix: 59 PCbuild_dir = os.path.join(PCbuild_dir, source_prefix) 60 # First copy the files that go into the root of the new distribution. This 61 # includes the Python executables, python27(_d).dll, and relevant PDB 62 # files. 63 print("Copying Python executables...") 64 copy_named_files(dest_dir, PCbuild_dir, ["w9xpopen"], ["exe", "pdb"], False) 65 copy_named_files(dest_dir, PCbuild_dir, ["python_d", "pythonw_d"], ["exe"], False) 66 copy_named_files( 67 dest_dir, PCbuild_dir, ["python", "pythonw"], ["exe", "pdb"], False 68 ) 69 copy_named_files(dest_dir, PCbuild_dir, ["python27"], ["dll", "pdb"], True) 70 71 # Next copy everything in the Include directory. 72 print("Copying Python include directory") 73 copy_subdirectory(dest_dir, source_dir, "Include") 74 75 # Copy Lib folder (builtin Python modules) 76 print("Copying Python Lib directory") 77 copy_subdirectory(dest_dir, source_dir, "Lib") 78 79 # Copy tools folder. These are probably not necessary, but we copy them anyway to 80 # match an official distribution as closely as possible. Note that we don't just copy 81 # the subdirectory recursively. The source distribution ships with many more tools 82 # than what you get by installing python regularly. We only copy the tools that appear 83 # in an installed distribution. 84 tools_dest_dir = os.path.join(dest_dir, "Tools") 85 tools_source_dir = os.path.join(source_dir, "Tools") 86 os.mkdir(tools_dest_dir) 87 copy_subdirectory(tools_dest_dir, tools_source_dir, "i18n") 88 copy_subdirectory(tools_dest_dir, tools_source_dir, "pynche") 89 copy_subdirectory(tools_dest_dir, tools_source_dir, "scripts") 90 copy_subdirectory(tools_dest_dir, tools_source_dir, "versioncheck") 91 copy_subdirectory(tools_dest_dir, tools_source_dir, "webchecker") 92 93 pyd_names = [ 94 "_ctypes", 95 "_ctypes_test", 96 "_elementtree", 97 "_multiprocessing", 98 "_socket", 99 "_testcapi", 100 "pyexpat", 101 "select", 102 "unicodedata", 103 "winsound", 104 ] 105 106 # Copy builtin extension modules (pyd files) 107 dlls_dir = os.path.join(dest_dir, "DLLs") 108 os.mkdir(dlls_dir) 109 print("Copying DLLs directory") 110 copy_named_files(dlls_dir, PCbuild_dir, pyd_names, ["pyd", "pdb"], True) 111 112 # Copy libs folder (implibs for the pyd files) 113 libs_dir = os.path.join(dest_dir, "libs") 114 os.mkdir(libs_dir) 115 print("Copying libs directory") 116 copy_named_files(libs_dir, PCbuild_dir, pyd_names, ["lib"], False) 117 copy_named_files(libs_dir, PCbuild_dir, ["python27"], ["lib"], True) 118 119 120parser = argparse.ArgumentParser(description="Install a custom Python distribution") 121parser.add_argument( 122 "--source", required=True, help="The root of the source tree where Python is built." 123) 124parser.add_argument( 125 "--dest", required=True, help="The location to install the Python distributions." 126) 127parser.add_argument( 128 "--overwrite", 129 default=False, 130 action="store_true", 131 help="If the destination directory already exists, destroys its contents first.", 132) 133parser.add_argument( 134 "--silent", 135 default=False, 136 action="store_true", 137 help="If --overwite was specified, suppress confirmation before deleting a directory tree.", 138) 139 140args = parser.parse_args() 141 142args.source = os.path.normpath(args.source) 143args.dest = os.path.normpath(args.dest) 144 145if not os.path.exists(args.source): 146 print("The source directory %s does not exist. Exiting...") 147 sys.exit(1) 148 149if os.path.exists(args.dest): 150 if not args.overwrite: 151 print( 152 "The destination directory '%s' already exists and --overwrite was not specified. Exiting..." 153 % args.dest 154 ) 155 sys.exit(1) 156 while not args.silent: 157 print( 158 "Ok to recursively delete '%s' and all contents (Y/N)? Choosing Y will permanently delete the contents." 159 % args.dest 160 ) 161 result = str.upper(sys.stdin.read(1)) 162 if result == "N": 163 print( 164 "Unable to copy files to the destination. The destination already exists." 165 ) 166 sys.exit(1) 167 elif result == "Y": 168 break 169 shutil.rmtree(args.dest) 170 171os.mkdir(args.dest) 172copy_distro(args.dest, "x86", args.source, None) 173copy_distro(args.dest, "x64", args.source, "amd64") 174