Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 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 | |
| 23 | import argparse |
| 24 | import itertools |
| 25 | import os |
| 26 | import shutil |
| 27 | import sys |
| 28 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 29 | |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 30 | def 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) |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 33 | print("Copying file %s ==> %s..." % (source_path, dest_path)) |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 34 | shutil.copyfile(source_path, dest_path) |
| 35 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 36 | |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 37 | def 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) |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 40 | if copy_debug_suffix_also: |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 41 | copy_one_file(dest_dir, source_dir, file + "_d." + ext) |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 42 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 43 | |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 44 | def 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) |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 47 | print("Copying directory %s ==> %s..." % (source_dir, dest_dir)) |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 48 | shutil.copytree(source_dir, dest_dir) |
| 49 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 50 | |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 51 | def copy_distro(dest_dir, dest_subdir, source_dir, source_prefix): |
| 52 | dest_dir = os.path.join(dest_dir, dest_subdir) |
| 53 | |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 54 | print("Copying distribution %s ==> %s" % (source_dir, dest_dir)) |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 55 | |
| 56 | os.mkdir(dest_dir) |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 57 | PCbuild_dir = os.path.join(source_dir, "PCbuild") |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 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 |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 61 | # includes the Python executables, python27(_d).dll, and relevant PDB |
| 62 | # files. |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 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) |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 66 | copy_named_files( |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 67 | dest_dir, PCbuild_dir, ["python", "pythonw"], ["exe", "pdb"], False |
| 68 | ) |
| 69 | copy_named_files(dest_dir, PCbuild_dir, ["python27"], ["dll", "pdb"], True) |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 70 | |
| 71 | # Next copy everything in the Include directory. |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 72 | print("Copying Python include directory") |
| 73 | copy_subdirectory(dest_dir, source_dir, "Include") |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 74 | |
| 75 | # Copy Lib folder (builtin Python modules) |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 76 | print("Copying Python Lib directory") |
| 77 | copy_subdirectory(dest_dir, source_dir, "Lib") |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 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. |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 84 | tools_dest_dir = os.path.join(dest_dir, "Tools") |
| 85 | tools_source_dir = os.path.join(source_dir, "Tools") |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 86 | os.mkdir(tools_dest_dir) |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 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") |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 92 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 93 | pyd_names = [ |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 94 | "_ctypes", |
| 95 | "_ctypes_test", |
| 96 | "_elementtree", |
| 97 | "_multiprocessing", |
| 98 | "_socket", |
| 99 | "_testcapi", |
| 100 | "pyexpat", |
| 101 | "select", |
| 102 | "unicodedata", |
| 103 | "winsound", |
| 104 | ] |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 105 | |
| 106 | # Copy builtin extension modules (pyd files) |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 107 | dlls_dir = os.path.join(dest_dir, "DLLs") |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 108 | os.mkdir(dlls_dir) |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 109 | print("Copying DLLs directory") |
| 110 | copy_named_files(dlls_dir, PCbuild_dir, pyd_names, ["pyd", "pdb"], True) |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 111 | |
| 112 | # Copy libs folder (implibs for the pyd files) |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 113 | libs_dir = os.path.join(dest_dir, "libs") |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 114 | os.mkdir(libs_dir) |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 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) |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 118 | |
| 119 | |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 120 | parser = argparse.ArgumentParser(description="Install a custom Python distribution") |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 121 | parser.add_argument( |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 122 | "--source", required=True, help="The root of the source tree where Python is built." |
| 123 | ) |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 124 | parser.add_argument( |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 125 | "--dest", required=True, help="The location to install the Python distributions." |
| 126 | ) |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 127 | parser.add_argument( |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 128 | "--overwrite", |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 129 | default=False, |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 130 | action="store_true", |
| 131 | help="If the destination directory already exists, destroys its contents first.", |
| 132 | ) |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 133 | parser.add_argument( |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 134 | "--silent", |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 135 | default=False, |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 136 | action="store_true", |
| 137 | help="If --overwite was specified, suppress confirmation before deleting a directory tree.", |
| 138 | ) |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 139 | |
| 140 | args = parser.parse_args() |
| 141 | |
| 142 | args.source = os.path.normpath(args.source) |
| 143 | args.dest = os.path.normpath(args.dest) |
| 144 | |
| 145 | if not os.path.exists(args.source): |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 146 | print("The source directory %s does not exist. Exiting...") |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 147 | sys.exit(1) |
| 148 | |
| 149 | if os.path.exists(args.dest): |
| 150 | if not args.overwrite: |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 151 | print( |
| 152 | "The destination directory '%s' already exists and --overwrite was not specified. Exiting..." |
| 153 | % args.dest |
| 154 | ) |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 155 | sys.exit(1) |
| 156 | while not args.silent: |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 157 | print( |
| 158 | "Ok to recursively delete '%s' and all contents (Y/N)? Choosing Y will permanently delete the contents." |
| 159 | % args.dest |
| 160 | ) |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 161 | result = str.upper(sys.stdin.read(1)) |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 162 | if result == "N": |
| 163 | print( |
| 164 | "Unable to copy files to the destination. The destination already exists." |
| 165 | ) |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 166 | sys.exit(1) |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 167 | elif result == "Y": |
Zachary Turner | dd50f74 | 2015-04-10 22:58:56 +0000 | [diff] [blame] | 168 | break |
| 169 | shutil.rmtree(args.dest) |
| 170 | |
| 171 | os.mkdir(args.dest) |
David Spickett | 602e47c | 2023-09-14 08:54:02 +0100 | [diff] [blame] | 172 | copy_distro(args.dest, "x86", args.source, None) |
| 173 | copy_distro(args.dest, "x64", args.source, "amd64") |