1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/build/checksums.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,158 @@ 1.4 +#!/usr/bin/python 1.5 +# This Source Code Form is subject to the terms of the Mozilla Public 1.6 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.8 + 1.9 +from __future__ import with_statement 1.10 + 1.11 +from optparse import OptionParser 1.12 +import logging 1.13 +import os 1.14 +try: 1.15 + import hashlib 1.16 +except: 1.17 + hashlib = None 1.18 + 1.19 +def digest_file(filename, digest, chunk_size=1024): 1.20 + '''Produce a checksum for the file specified by 'filename'. 'filename' 1.21 + is a string path to a file that is opened and read in this function. The 1.22 + checksum algorithm is specified by 'digest' and is a valid OpenSSL 1.23 + algorithm. If the digest used is not valid or Python's hashlib doesn't 1.24 + work, the None object will be returned instead. The size of blocks 1.25 + that this function will read from the file object it opens based on 1.26 + 'filename' can be specified by 'chunk_size', which defaults to 1K''' 1.27 + assert not os.path.isdir(filename), 'this function only works with files' 1.28 + logger = logging.getLogger('checksums.py') 1.29 + if hashlib is not None: 1.30 + logger.debug('Creating new %s object' % digest) 1.31 + h = hashlib.new(digest) 1.32 + with open(filename, 'rb') as f: 1.33 + while True: 1.34 + data = f.read(chunk_size) 1.35 + if not data: 1.36 + logger.debug('Finished reading in file') 1.37 + break 1.38 + h.update(data) 1.39 + hash = h.hexdigest() 1.40 + logger.debug('Hash for %s is %s' % (filename, hash)) 1.41 + return hash 1.42 + else: 1.43 + # In this case we could subprocess.Popen and .communicate with 1.44 + # sha1sum or md5sum 1.45 + logger.warn('The python module for hashlib is missing!') 1.46 + return None 1.47 + 1.48 + 1.49 +def process_files(files, output_filename, digests, strip): 1.50 + '''This function takes a list of file names, 'files'. It will then 1.51 + compute the checksum for each of the files by opening the files. 1.52 + Once each file is read and its checksum is computed, this function 1.53 + will write the information to the file specified by 'output_filename'. 1.54 + The path written in the output file will have anything specified by 'strip' 1.55 + removed from the path. The output file is closed before returning nothing 1.56 + The algorithm to compute checksums with can be specified by 'digests' 1.57 + and needs to be a list of valid OpenSSL algorithms. 1.58 + 1.59 + The output file is written in the format: 1.60 + <hash> <algorithm> <filesize> <filepath> 1.61 + Example: 1.62 + d1fa09a<snip>e4220 sha1 14250744 firefox-4.0b6pre.en-US.mac64.dmg 1.63 + ''' 1.64 + 1.65 + logger = logging.getLogger('checksums.py') 1.66 + if os.path.exists(output_filename): 1.67 + logger.debug('Overwriting existing checksums file "%s"' % 1.68 + output_filename) 1.69 + else: 1.70 + logger.debug('Creating a new checksums file "%s"' % output_filename) 1.71 + with open(output_filename, 'w+') as output: 1.72 + for file in files: 1.73 + if os.path.isdir(file): 1.74 + logger.warn('%s is a directory, skipping' % file) 1.75 + else: 1.76 + for digest in digests: 1.77 + hash = digest_file(file, digest) 1.78 + if hash is None: 1.79 + logger.warn('Unable to generate a hash for %s. ' + 1.80 + 'Skipping.' % file) 1.81 + continue 1.82 + if file.startswith(strip): 1.83 + short_file = file[len(strip):] 1.84 + short_file = short_file.lstrip('/') 1.85 + else: 1.86 + short_file = file 1.87 + print >>output, '%s %s %s %s' % (hash, digest, 1.88 + os.path.getsize(file), 1.89 + short_file) 1.90 + 1.91 +def setup_logging(level=logging.DEBUG): 1.92 + '''This function sets up the logging module using a speficiable logging 1.93 + module logging level. The default log level is DEBUG. 1.94 + 1.95 + The output is in the format: 1.96 + <level> - <message> 1.97 + Example: 1.98 + DEBUG - Finished reading in file 1.99 +''' 1.100 + 1.101 + logger = logging.getLogger('checksums.py') 1.102 + logger.setLevel(logging.DEBUG) 1.103 + handler = logging.StreamHandler() 1.104 + handler.setLevel(level) 1.105 + formatter = logging.Formatter("%(levelname)s - %(message)s") 1.106 + handler.setFormatter(formatter) 1.107 + logger.addHandler(handler) 1.108 + 1.109 +def main(): 1.110 + '''This is a main function that parses arguments, sets up logging 1.111 + and generates a checksum file''' 1.112 + # Parse command line arguments 1.113 + parser = OptionParser() 1.114 + parser.add_option('-d', '--digest', help='checksum algorithm to use', 1.115 + action='append', dest='digests') 1.116 + parser.add_option('-o', '--output', help='output file to use', 1.117 + action='store', dest='outfile', default='checksums') 1.118 + parser.add_option('-v', '--verbose', 1.119 + help='Be noisy (takes precedence over quiet)', 1.120 + action='store_true', dest='verbose', default=False) 1.121 + parser.add_option('-q', '--quiet', help='Be quiet', action='store_true', 1.122 + dest='quiet', default=False) 1.123 + parser.add_option('-s', '--strip', 1.124 + help='strip this path from the filenames', 1.125 + dest='strip', default=os.getcwd()) 1.126 + options, args = parser.parse_args() 1.127 + 1.128 + #Figure out which logging level to use 1.129 + if options.verbose: 1.130 + loglevel = logging.DEBUG 1.131 + elif options.quiet: 1.132 + loglevel = logging.ERROR 1.133 + else: 1.134 + loglevel = logging.INFO 1.135 + 1.136 + #Set up logging 1.137 + setup_logging(loglevel) 1.138 + logger = logging.getLogger('checksums.py') 1.139 + 1.140 + # Validate the digest type to use 1.141 + if not options.digests: 1.142 + options.digests = ['sha1'] 1.143 + try: 1.144 + for digest in options.digests: 1.145 + hashlib.new(digest) 1.146 + except ValueError, ve: 1.147 + logger.error('Could not create a "%s" hash object (%s)' % 1.148 + (digest, ve.args[0])) 1.149 + exit(1) 1.150 + 1.151 + # Validate the files to checksum 1.152 + files = [] 1.153 + for i in args: 1.154 + if os.path.exists(i): 1.155 + files.append(i) 1.156 + else: 1.157 + logger.info('File "%s" was not found on the filesystem' % i) 1.158 + process_files(files, options.outfile, options.digests, options.strip) 1.159 + 1.160 +if __name__ == '__main__': 1.161 + main()