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