1#!/usr/bin/env python3 2 3import argparse 4import csv 5import glob 6import json 7import os 8import sys 9 10HELP_MSG = ''' 11This script computes the differences between two system images (system1 - 12system2), and lists the files grouped by package. The difference is just based 13on the existence of the file, not on its contents. 14''' 15 16VENDOR_PATH_MAP = { 17 'vendor/google' : 'Google', 18 'vendor/unbundled_google': 'Google', 19 'vendor/verizon' : 'Verizon', 20 'vendor/qcom' : 'Qualcomm', 21 'vendor/tmobile' : 'TMobile', 22 'vendor/mediatek' : 'Mediatek', 23 'vendor/htc' : 'HTC', 24 'vendor/realtek' : 'Realtek' 25} 26 27def system_files(path): 28 """Returns an array of the files under /system, recursively, and ignoring 29 symbolic-links""" 30 system_files = [] 31 system_prefix = os.path.join(path, 'system') 32 # Skip trailing '/' 33 system_prefix_len = len(system_prefix) + 1 34 35 for root, dirs, files in os.walk(system_prefix, topdown=True): 36 for file in files: 37 # Ignore symbolic links. 38 if not os.path.islink(os.path.join(root, file)): 39 system_files.append(os.path.join(root[system_prefix_len:], file)) 40 41 return system_files 42 43def system_files_to_package_map(path): 44 """Returns a dictionary mapping from each file in the /system partition to its 45 package, according to modules-info.json.""" 46 system_files_to_package_map = {} 47 system_prefix = os.path.join(path, 'system') 48 # Skip trailing '/' 49 system_prefix_len = len(system_prefix) + 1 50 51 with open(os.path.join(path, 'module-info.json')) as module_info_json: 52 module_info = json.load(module_info_json) 53 for module in module_info: 54 installs = module_info[module]['installed'] 55 for install in installs: 56 if install.startswith(system_prefix): 57 system_file = install[system_prefix_len:] 58 # Not clear if collisions can ever happen in modules-info.json (e.g. 59 # the same file installed by multiple packages), but it doesn't hurt 60 # to check. 61 if system_file in system_files_to_package_map: 62 system_files_to_package_map[system_file] = "--multiple--" 63 else: 64 system_files_to_package_map[system_file] = module 65 66 return system_files_to_package_map 67 68def package_to_vendor_map(path): 69 """Returns a dictionary mapping from each package in modules-info.json to its 70 vendor. If a vendor cannot be found, it maps to "--unknown--". Those cases 71 are: 72 73 1. The package maps to multiple modules (e.g., one in APPS and one in 74 SHARED_LIBRARIES. 75 2. The path to the module is not one of the recognized vendor paths in 76 VENDOR_PATH_MAP.""" 77 package_vendor_map = {} 78 system_prefix = os.path.join(path, 'system') 79 # Skip trailing '/' 80 system_prefix_len = len(system_prefix) + 1 81 vendor_prefixes = VENDOR_PATH_MAP.keys() 82 83 with open(os.path.join(path, 'module-info.json')) as module_info_json: 84 module_info = json.load(module_info_json) 85 for module in module_info: 86 paths = module_info[module]['path'] 87 vendor = "" 88 if len(paths) == 1: 89 path = paths[0] 90 for prefix in vendor_prefixes: 91 if path.startswith(prefix): 92 vendor = VENDOR_PATH_MAP[prefix] 93 break 94 if vendor == "": 95 vendor = "--unknown--" 96 else: 97 vendor = "--multiple--" 98 package_vendor_map[module] = vendor 99 100 return package_vendor_map 101 102def main(): 103 parser = argparse.ArgumentParser(description=HELP_MSG) 104 parser.add_argument("out1", help="First $OUT directory") 105 parser.add_argument("out2", help="Second $OUT directory") 106 args = parser.parse_args() 107 108 system_files1 = system_files(args.out1) 109 system_files2 = system_files(args.out2) 110 system_files_diff = set(system_files1) - set(system_files2) 111 112 system_files_map = system_files_to_package_map(args.out1) 113 package_vendor_map = package_to_vendor_map(args.out1) 114 packages = {} 115 116 for file in system_files_diff: 117 if file in system_files_map: 118 package = system_files_map[file] 119 else: 120 package = "--unknown--" 121 122 if package in packages: 123 packages[package].append(file) 124 else: 125 packages[package] = [file] 126 127 writer = csv.writer(sys.stdout, quoting = csv.QUOTE_NONNUMERIC, 128 delimiter = ',', lineterminator = '\n') 129 for package, files in packages.items(): 130 for file in files: 131 if package in package_vendor_map: 132 vendor = package_vendor_map[package] 133 else: 134 vendor = "--unknown--" 135 # Get file size 136 full_path = os.path.join(args.out1, 'system', file) 137 size = os.stat(full_path).st_size 138 writer.writerow([vendor, package, file, size]) 139 140if __name__ == '__main__': 141 main() 142