""" 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")