1effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch# Copyright 2014 The Chromium Authors. All rights reserved. 2effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch# Use of this source code is governed by a BSD-style license that can be 3effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch# found in the LICENSE file. 4effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 5effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochimport logging 6effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochimport subprocess 7effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochimport sys 8effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 9effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochfrom hashlib import sha256 10effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochfrom os.path import basename, realpath 11effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 12effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch_logging = logging.getLogger() 13effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 14effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch# Based on/taken from 15effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch# http://code.activestate.com/recipes/578231-probably-the-fastest-memoization-decorator-in-the-/ 16effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch# (with cosmetic changes). 17effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochdef _memoize(f): 18effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """Memoization decorator for a function taking a single argument.""" 19effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch class Memoize(dict): 20effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def __missing__(self, key): 21effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch rv = self[key] = f(key) 22effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return rv 23effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return Memoize().__getitem__ 24effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 25effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch@_memoize 26effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochdef _file_hash(filename): 27effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """Returns a string representing the hash of the given file.""" 28effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch _logging.debug("Hashing %s ...", filename) 29effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch rv = subprocess.check_output(['sha256sum', '-b', filename]).split(None, 1)[0] 30effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch _logging.debug(" => %s", rv) 31effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return rv 32effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 33effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch@_memoize 34effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochdef _get_dependencies(filename): 35effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """Returns a list of filenames for files that the given file depends on.""" 36effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch _logging.debug("Getting dependencies for %s ...", filename) 37effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch lines = subprocess.check_output(['ldd', filename]).splitlines() 38effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch rv = [] 39effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch for line in lines: 40effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch i = line.find('/') 41effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if i < 0: 42effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch _logging.debug(" => no file found in line: %s", line) 43effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch continue 44effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch rv.append(line[i:].split(None, 1)[0]) 45effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch _logging.debug(" => %s", rv) 46effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return rv 47effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 48effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochdef transitive_hash(filename): 49effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """Returns a string that represents the "transitive" hash of the given 50effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch file. The transitive hash is a hash of the file and all the shared libraries 51effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch on which it depends (done in an order-independent way).""" 52effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch hashes = set() 53effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch to_hash = [filename] 54effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch while to_hash: 55effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch current_filename = realpath(to_hash.pop()) 56effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch current_hash = _file_hash(current_filename) 57effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if current_hash in hashes: 58effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch _logging.debug("Already seen %s (%s) ...", current_filename, current_hash) 59effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch continue 60effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch _logging.debug("Haven't seen %s (%s) ...", current_filename, current_hash) 61effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch hashes.add(current_hash) 62effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch to_hash.extend(_get_dependencies(current_filename)) 63effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return sha256('|'.join(sorted(hashes))).hexdigest() 64effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 65effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochdef main(argv): 66effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch logging.basicConfig() 67effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # Uncomment to debug: 68effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # _logging.setLevel(logging.DEBUG) 69effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 70effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if len(argv) < 2: 71effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch print """\ 72effb81e5f8246d0db0270817048dc992db66e9fbBen MurdochUsage: %s [file] ... 73effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 74effb81e5f8246d0db0270817048dc992db66e9fbBen MurdochPrints the \"transitive\" hash of each (executable) file. The transitive 75effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochhash is a hash of the file and all the shared libraries on which it 76effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochdepends (done in an order-independent way).""" % basename(argv[0]) 77effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return 0 78effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 79effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch rv = 0 80effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch for filename in argv[1:]: 81effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch try: 82effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch print transitive_hash(filename), filename 83effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch except: 84effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch print "ERROR", filename 85effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch rv = 1 86effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return rv 87effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 88effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochif __name__ == '__main__': 89effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch sys.exit(main(sys.argv)) 90