xref: /openbsd-src/gnu/llvm/lldb/scripts/install_custom_python.py (revision 061da546b983eb767bad15e67af1174fb0bcf31c)
1*061da546Spatrick""" Copies the build output of a custom python interpreter to a directory
2*061da546Spatrick    structure that mirrors that of an official Python distribution.
3*061da546Spatrick
4*061da546Spatrick    --------------------------------------------------------------------------
5*061da546Spatrick    File:           install_custom_python.py
6*061da546Spatrick
7*061da546Spatrick    Overview:       Most users build LLDB by linking against the standard
8*061da546Spatrick                    Python distribution installed on their system.  Occasionally
9*061da546Spatrick                    a user may want to build their own version of Python, and on
10*061da546Spatrick                    platforms such as Windows this is a hard requirement.  This
11*061da546Spatrick                    script will take the build output of a custom interpreter and
12*061da546Spatrick                    install it into a canonical structure that mirrors that of an
13*061da546Spatrick                    official Python distribution, thus allowing PYTHONHOME to be
14*061da546Spatrick                    set appropriately.
15*061da546Spatrick
16*061da546Spatrick    Gotchas:        None.
17*061da546Spatrick
18*061da546Spatrick    Copyright:      None.
19*061da546Spatrick    --------------------------------------------------------------------------
20*061da546Spatrick
21*061da546Spatrick"""
22*061da546Spatrick
23*061da546Spatrickimport argparse
24*061da546Spatrickimport itertools
25*061da546Spatrickimport os
26*061da546Spatrickimport shutil
27*061da546Spatrickimport sys
28*061da546Spatrick
29*061da546Spatrick
30*061da546Spatrickdef copy_one_file(dest_dir, source_dir, filename):
31*061da546Spatrick    source_path = os.path.join(source_dir, filename)
32*061da546Spatrick    dest_path = os.path.join(dest_dir, filename)
33*061da546Spatrick    print('Copying file %s ==> %s...' % (source_path, dest_path))
34*061da546Spatrick    shutil.copyfile(source_path, dest_path)
35*061da546Spatrick
36*061da546Spatrick
37*061da546Spatrickdef copy_named_files(
38*061da546Spatrick        dest_dir,
39*061da546Spatrick        source_dir,
40*061da546Spatrick        files,
41*061da546Spatrick        extensions,
42*061da546Spatrick        copy_debug_suffix_also):
43*061da546Spatrick    for (file, ext) in itertools.product(files, extensions):
44*061da546Spatrick        copy_one_file(dest_dir, source_dir, file + '.' + ext)
45*061da546Spatrick        if copy_debug_suffix_also:
46*061da546Spatrick            copy_one_file(dest_dir, source_dir, file + '_d.' + ext)
47*061da546Spatrick
48*061da546Spatrick
49*061da546Spatrickdef copy_subdirectory(dest_dir, source_dir, subdir):
50*061da546Spatrick    dest_dir = os.path.join(dest_dir, subdir)
51*061da546Spatrick    source_dir = os.path.join(source_dir, subdir)
52*061da546Spatrick    print('Copying directory %s ==> %s...' % (source_dir, dest_dir))
53*061da546Spatrick    shutil.copytree(source_dir, dest_dir)
54*061da546Spatrick
55*061da546Spatrick
56*061da546Spatrickdef copy_distro(dest_dir, dest_subdir, source_dir, source_prefix):
57*061da546Spatrick    dest_dir = os.path.join(dest_dir, dest_subdir)
58*061da546Spatrick
59*061da546Spatrick    print('Copying distribution %s ==> %s' % (source_dir, dest_dir))
60*061da546Spatrick
61*061da546Spatrick    os.mkdir(dest_dir)
62*061da546Spatrick    PCbuild_dir = os.path.join(source_dir, 'PCbuild')
63*061da546Spatrick    if source_prefix:
64*061da546Spatrick        PCbuild_dir = os.path.join(PCbuild_dir, source_prefix)
65*061da546Spatrick    # First copy the files that go into the root of the new distribution. This
66*061da546Spatrick    # includes the Python executables, python27(_d).dll, and relevant PDB
67*061da546Spatrick    # files.
68*061da546Spatrick    print('Copying Python executables...')
69*061da546Spatrick    copy_named_files(
70*061da546Spatrick        dest_dir, PCbuild_dir, ['w9xpopen'], [
71*061da546Spatrick            'exe', 'pdb'], False)
72*061da546Spatrick    copy_named_files(
73*061da546Spatrick        dest_dir, PCbuild_dir, [
74*061da546Spatrick            'python_d', 'pythonw_d'], ['exe'], False)
75*061da546Spatrick    copy_named_files(
76*061da546Spatrick        dest_dir, PCbuild_dir, [
77*061da546Spatrick            'python', 'pythonw'], [
78*061da546Spatrick            'exe', 'pdb'], False)
79*061da546Spatrick    copy_named_files(dest_dir, PCbuild_dir, ['python27'], ['dll', 'pdb'], True)
80*061da546Spatrick
81*061da546Spatrick    # Next copy everything in the Include directory.
82*061da546Spatrick    print('Copying Python include directory')
83*061da546Spatrick    copy_subdirectory(dest_dir, source_dir, 'Include')
84*061da546Spatrick
85*061da546Spatrick    # Copy Lib folder (builtin Python modules)
86*061da546Spatrick    print('Copying Python Lib directory')
87*061da546Spatrick    copy_subdirectory(dest_dir, source_dir, 'Lib')
88*061da546Spatrick
89*061da546Spatrick    # Copy tools folder.  These are probably not necessary, but we copy them anyway to
90*061da546Spatrick    # match an official distribution as closely as possible.  Note that we don't just copy
91*061da546Spatrick    # the subdirectory recursively.  The source distribution ships with many more tools
92*061da546Spatrick    # than what you get by installing python regularly.  We only copy the tools that appear
93*061da546Spatrick    # in an installed distribution.
94*061da546Spatrick    tools_dest_dir = os.path.join(dest_dir, 'Tools')
95*061da546Spatrick    tools_source_dir = os.path.join(source_dir, 'Tools')
96*061da546Spatrick    os.mkdir(tools_dest_dir)
97*061da546Spatrick    copy_subdirectory(tools_dest_dir, tools_source_dir, 'i18n')
98*061da546Spatrick    copy_subdirectory(tools_dest_dir, tools_source_dir, 'pynche')
99*061da546Spatrick    copy_subdirectory(tools_dest_dir, tools_source_dir, 'scripts')
100*061da546Spatrick    copy_subdirectory(tools_dest_dir, tools_source_dir, 'versioncheck')
101*061da546Spatrick    copy_subdirectory(tools_dest_dir, tools_source_dir, 'webchecker')
102*061da546Spatrick
103*061da546Spatrick    pyd_names = [
104*061da546Spatrick        '_ctypes',
105*061da546Spatrick        '_ctypes_test',
106*061da546Spatrick        '_elementtree',
107*061da546Spatrick        '_multiprocessing',
108*061da546Spatrick        '_socket',
109*061da546Spatrick        '_testcapi',
110*061da546Spatrick        'pyexpat',
111*061da546Spatrick        'select',
112*061da546Spatrick        'unicodedata',
113*061da546Spatrick        'winsound']
114*061da546Spatrick
115*061da546Spatrick    # Copy builtin extension modules (pyd files)
116*061da546Spatrick    dlls_dir = os.path.join(dest_dir, 'DLLs')
117*061da546Spatrick    os.mkdir(dlls_dir)
118*061da546Spatrick    print('Copying DLLs directory')
119*061da546Spatrick    copy_named_files(dlls_dir, PCbuild_dir, pyd_names, ['pyd', 'pdb'], True)
120*061da546Spatrick
121*061da546Spatrick    # Copy libs folder (implibs for the pyd files)
122*061da546Spatrick    libs_dir = os.path.join(dest_dir, 'libs')
123*061da546Spatrick    os.mkdir(libs_dir)
124*061da546Spatrick    print('Copying libs directory')
125*061da546Spatrick    copy_named_files(libs_dir, PCbuild_dir, pyd_names, ['lib'], False)
126*061da546Spatrick    copy_named_files(libs_dir, PCbuild_dir, ['python27'], ['lib'], True)
127*061da546Spatrick
128*061da546Spatrick
129*061da546Spatrickparser = argparse.ArgumentParser(
130*061da546Spatrick    description='Install a custom Python distribution')
131*061da546Spatrickparser.add_argument(
132*061da546Spatrick    '--source',
133*061da546Spatrick    required=True,
134*061da546Spatrick    help='The root of the source tree where Python is built.')
135*061da546Spatrickparser.add_argument(
136*061da546Spatrick    '--dest',
137*061da546Spatrick    required=True,
138*061da546Spatrick    help='The location to install the Python distributions.')
139*061da546Spatrickparser.add_argument(
140*061da546Spatrick    '--overwrite',
141*061da546Spatrick    default=False,
142*061da546Spatrick    action='store_true',
143*061da546Spatrick    help='If the destination directory already exists, destroys its contents first.')
144*061da546Spatrickparser.add_argument(
145*061da546Spatrick    '--silent',
146*061da546Spatrick    default=False,
147*061da546Spatrick    action='store_true',
148*061da546Spatrick    help='If --overwite was specified, suppress confirmation before deleting a directory tree.')
149*061da546Spatrick
150*061da546Spatrickargs = parser.parse_args()
151*061da546Spatrick
152*061da546Spatrickargs.source = os.path.normpath(args.source)
153*061da546Spatrickargs.dest = os.path.normpath(args.dest)
154*061da546Spatrick
155*061da546Spatrickif not os.path.exists(args.source):
156*061da546Spatrick    print('The source directory %s does not exist.  Exiting...')
157*061da546Spatrick    sys.exit(1)
158*061da546Spatrick
159*061da546Spatrickif os.path.exists(args.dest):
160*061da546Spatrick    if not args.overwrite:
161*061da546Spatrick        print('The destination directory \'%s\' already exists and --overwrite was not specified.  Exiting...' % args.dest)
162*061da546Spatrick        sys.exit(1)
163*061da546Spatrick    while not args.silent:
164*061da546Spatrick        print('Ok to recursively delete \'%s\' and all contents (Y/N)?  Choosing Y will permanently delete the contents.' % args.dest)
165*061da546Spatrick        result = str.upper(sys.stdin.read(1))
166*061da546Spatrick        if result == 'N':
167*061da546Spatrick            print('Unable to copy files to the destination.  The destination already exists.')
168*061da546Spatrick            sys.exit(1)
169*061da546Spatrick        elif result == 'Y':
170*061da546Spatrick            break
171*061da546Spatrick    shutil.rmtree(args.dest)
172*061da546Spatrick
173*061da546Spatrickos.mkdir(args.dest)
174*061da546Spatrickcopy_distro(args.dest, 'x86', args.source, None)
175*061da546Spatrickcopy_distro(args.dest, 'x64', args.source, 'amd64')
176