176 lines
6.4 KiB
Python
176 lines
6.4 KiB
Python
|
""" 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')
|