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