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