174 lines · python
1""" Copies the build output of a custom python interpreter to a directory2 structure that mirrors that of an official Python distribution.3 4 --------------------------------------------------------------------------5 File: install_custom_python.py6 7 Overview: Most users build LLDB by linking against the standard8 Python distribution installed on their system. Occasionally9 a user may want to build their own version of Python, and on10 platforms such as Windows this is a hard requirement. This11 script will take the build output of a custom interpreter and12 install it into a canonical structure that mirrors that of an13 official Python distribution, thus allowing PYTHONHOME to be14 set appropriately.15 16 Gotchas: None.17 18 Copyright: None.19 --------------------------------------------------------------------------20 21"""22 23import argparse24import itertools25import os26import shutil27import sys28 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. This61 # includes the Python executables, python27(_d).dll, and relevant PDB62 # 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"], False68 )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 to80 # match an official distribution as closely as possible. Note that we don't just copy81 # the subdirectory recursively. The source distribution ships with many more tools82 # than what you get by installing python regularly. We only copy the tools that appear83 # 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.dest154 )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.dest160 )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 break169 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