blob: 5a8f48aac9491ed40d727b7057f98196037313f4 [file] [log] [blame]
Zachary Turnerdd50f742015-04-10 22:58:56 +00001""" 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
23import argparse
24import itertools
25import os
26import shutil
27import sys
28
29def copy_one_file(dest_dir, source_dir, filename):
30 source_path = os.path.join(source_dir, filename)
31 dest_path = os.path.join(dest_dir, filename)
32 print 'Copying file %s ==> %s...' % (source_path, dest_path)
33 shutil.copyfile(source_path, dest_path)
34
35def copy_named_files(dest_dir, source_dir, files, extensions, copy_debug_suffix_also):
36 for (file, ext) in itertools.product(files, extensions):
37 copy_one_file(dest_dir, source_dir, file + '.' + ext)
38 if copy_debug_suffix_also:
39 copy_one_file(dest_dir, source_dir, file + '_d.' + ext)
40
41def copy_subdirectory(dest_dir, source_dir, subdir):
42 dest_dir = os.path.join(dest_dir, subdir)
43 source_dir = os.path.join(source_dir, subdir)
44 print 'Copying directory %s ==> %s...' % (source_dir, dest_dir)
45 shutil.copytree(source_dir, dest_dir)
46
47def copy_distro(dest_dir, dest_subdir, source_dir, source_prefix):
48 dest_dir = os.path.join(dest_dir, dest_subdir)
49
50 print 'Copying distribution %s ==> %s' % (source_dir, dest_dir)
51
52 os.mkdir(dest_dir)
53 PCbuild_dir = os.path.join(source_dir, 'PCbuild')
54 if source_prefix:
55 PCbuild_dir = os.path.join(PCbuild_dir, source_prefix)
56 # First copy the files that go into the root of the new distribution. This
57 # includes the Python executables, python27(_d).dll, and relevant PDB files.
58 print 'Copying Python executables...'
59 copy_named_files(dest_dir, PCbuild_dir, ['w9xpopen'], ['exe', 'pdb'], False)
60 copy_named_files(dest_dir, PCbuild_dir, ['python_d', 'pythonw_d'], ['exe'], False)
61 copy_named_files(dest_dir, PCbuild_dir, ['python', 'pythonw'], ['exe', 'pdb'], False)
62 copy_named_files(dest_dir, PCbuild_dir, ['python27'], ['dll', 'pdb'], True)
63
64 # Next copy everything in the Include directory.
65 print 'Copying Python include directory'
66 copy_subdirectory(dest_dir, source_dir, 'Include')
67
68 # Copy Lib folder (builtin Python modules)
69 print 'Copying Python Lib directory'
70 copy_subdirectory(dest_dir, source_dir, 'Lib')
71
72 # Copy tools folder. These are probably not necessary, but we copy them anyway to
73 # match an official distribution as closely as possible. Note that we don't just copy
74 # the subdirectory recursively. The source distribution ships with many more tools
75 # than what you get by installing python regularly. We only copy the tools that appear
76 # in an installed distribution.
77 tools_dest_dir = os.path.join(dest_dir, 'Tools')
78 tools_source_dir = os.path.join(source_dir, 'Tools')
79 os.mkdir(tools_dest_dir)
80 copy_subdirectory(tools_dest_dir, tools_source_dir, 'i18n')
81 copy_subdirectory(tools_dest_dir, tools_source_dir, 'pynche')
82 copy_subdirectory(tools_dest_dir, tools_source_dir, 'scripts')
83 copy_subdirectory(tools_dest_dir, tools_source_dir, 'versioncheck')
84 copy_subdirectory(tools_dest_dir, tools_source_dir, 'webchecker')
85
86 pyd_names = ['_ctypes', '_ctypes_test', '_elementtree', '_multiprocessing', '_socket',
87 '_testcapi', 'pyexpat', 'select', 'unicodedata', 'winsound']
88
89 # Copy builtin extension modules (pyd files)
90 dlls_dir = os.path.join(dest_dir, 'DLLs')
91 os.mkdir(dlls_dir)
92 print 'Copying DLLs directory'
93 copy_named_files(dlls_dir, PCbuild_dir, pyd_names, ['pyd', 'pdb'], True)
94
95 # Copy libs folder (implibs for the pyd files)
96 libs_dir = os.path.join(dest_dir, 'libs')
97 os.mkdir(libs_dir)
98 print 'Copying libs directory'
99 copy_named_files(libs_dir, PCbuild_dir, pyd_names, ['lib'], False)
100 copy_named_files(libs_dir, PCbuild_dir, ['python27'], ['lib'], True)
101
102
103parser = argparse.ArgumentParser(description='Install a custom Python distribution')
104parser.add_argument('--source', required=True, help='The root of the source tree where Python is built.')
105parser.add_argument('--dest', required=True, help='The location to install the Python distributions.')
106parser.add_argument('--overwrite', default=False, action='store_true', help='If the destination directory already exists, destroys its contents first.')
107parser.add_argument('--silent', default=False, action='store_true', help='If --overwite was specified, suppress confirmation before deleting a directory tree.')
108
109args = parser.parse_args()
110
111args.source = os.path.normpath(args.source)
112args.dest = os.path.normpath(args.dest)
113
114if not os.path.exists(args.source):
115 print 'The source directory %s does not exist. Exiting...'
116 sys.exit(1)
117
118if os.path.exists(args.dest):
119 if not args.overwrite:
120 print 'The destination directory \'%s\' already exists and --overwrite was not specified. Exiting...' % args.dest
121 sys.exit(1)
122 while not args.silent:
123 print 'Ok to recursively delete \'%s\' and all contents (Y/N)? Choosing Y will permanently delete the contents.' % args.dest
124 result = str.upper(sys.stdin.read(1))
125 if result == 'N':
126 print 'Unable to copy files to the destination. The destination already exists.'
127 sys.exit(1)
128 elif result == 'Y':
129 break
130 shutil.rmtree(args.dest)
131
132os.mkdir(args.dest)
133copy_distro(args.dest, 'x86', args.source, None)
134copy_distro(args.dest, 'x64', args.source, 'amd64')