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