1# This script handles the creation of the PEP 376 .dist-info directory for a 2# package. 3# 4# Copyright (c) 2018 Riverbank Computing Limited <info@riverbankcomputing.com> 5# 6# This script is distributed under the terms of the GNU General Public License 7# v3 as published by the Free Software Foundation. 8# 9# This script is supplied WITHOUT ANY WARRANTY; without even the implied 10# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 11 12 13import base64 14import hashlib 15import os 16import shutil 17import sys 18 19 20def error(message): 21 """ Display an error message and terminate. """ 22 23 sys.stderr.write(message + '\n') 24 sys.exit(1) 25 26 27# Parse the command line. 28if len(sys.argv) != 4: 29 error("usage: {0} prefix dist-info installed".format(sys.argv[0])) 30 31prefix_dir = sys.argv[1] 32distinfo_dir = sys.argv[2] 33installed_fn = sys.argv[3] 34 35# Read the list of installed files. 36installed_f = open(installed_fn) 37installed = installed_f.read().strip().split('\n') 38installed_f.close() 39 40# The prefix directory corresponds to DESTDIR or INSTALL_ROOT. 41real_distinfo_dir = prefix_dir + distinfo_dir 42 43# Remove any existing dist-info directory and create an empty one. 44if os.path.exists(real_distinfo_dir): 45 try: 46 shutil.rmtree(real_distinfo_dir) 47 except: 48 error("unable to delete existing {0}".format(real_distinfo_dir)) 49 50try: 51 os.mkdir(real_distinfo_dir) 52except: 53 error("unable to create {0}".format(real_distinfo_dir)) 54 55# Create the INSTALLER file. We pretend that pip was the installer. 56installer_fn = os.path.join(distinfo_dir, 'INSTALLER') 57installer_f = open(prefix_dir + installer_fn, 'w') 58installer_f.write('pip\n') 59installer_f.close() 60 61installed.append(installer_fn) 62 63# Create the METADATA file. 64METADATA = '''Metadata-Version: 1.1 65Name: {0} 66Version: {1} 67''' 68 69distinfo_path, distinfo_base = os.path.split(distinfo_dir) 70pkg_name, version = os.path.splitext(distinfo_base)[0].split('-') 71 72metadata_fn = os.path.join(distinfo_dir, 'METADATA') 73metadata_f = open(prefix_dir + metadata_fn, 'w') 74metadata_f.write(METADATA.format(pkg_name, version)) 75metadata_f.close() 76 77installed.append(metadata_fn) 78 79# Create the RECORD file. 80record_fn = os.path.join(distinfo_dir, 'RECORD') 81record_f = open(prefix_dir + record_fn, 'w') 82 83for name in installed: 84 native_name = prefix_dir + name.replace('/', os.sep) 85 if os.path.isdir(native_name): 86 all_fns = [] 87 88 for root, dirs, files in os.walk(native_name): 89 # Reproducable builds. 90 dirs.sort() 91 files.sort() 92 93 for f in files: 94 all_fns.append(os.path.join(root, f).replace(os.sep, '/')) 95 96 if '__pycache__' in dirs: 97 dirs.remove('__pycache__') 98 else: 99 all_fns = [prefix_dir + name] 100 101 for fn in all_fns: 102 real_distinfo_path = prefix_dir + distinfo_path 103 104 if fn.startswith(real_distinfo_path): 105 fn_name = fn[len(real_distinfo_path) + 1:].replace('\\', '/') 106 elif fn.startswith(prefix_dir + sys.prefix): 107 fn_name = os.path.relpath( 108 fn, real_distinfo_path).replace('\\', '/') 109 else: 110 fn_name = fn[len(prefix_dir):] 111 112 fn_f = open(fn, 'rb') 113 data = fn_f.read() 114 fn_f.close() 115 116 digest = base64.urlsafe_b64encode( 117 hashlib.sha256(data).digest()).rstrip(b'=').decode('ascii') 118 119 record_f.write( 120 '{0},sha256={1},{2}\n'.format(fn_name, digest, len(data))) 121 122record_f.write('{0}/RECORD,,\n'.format(distinfo_base)) 123 124record_f.close() 125