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