1*9e94795aSAndroid Build Coastguard Worker#!/usr/bin/env python 2*9e94795aSAndroid Build Coastguard Worker# 3*9e94795aSAndroid Build Coastguard Worker# Copyright (C) 2009 The Android Open Source Project 4*9e94795aSAndroid Build Coastguard Worker# 5*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*9e94795aSAndroid Build Coastguard Worker# 9*9e94795aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*9e94795aSAndroid Build Coastguard Worker# 11*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*9e94795aSAndroid Build Coastguard Worker# limitations under the License. 16*9e94795aSAndroid Build Coastguard Worker# 17*9e94795aSAndroid Build Coastguard Worker 18*9e94795aSAndroid Build Coastguard Worker# 19*9e94795aSAndroid Build Coastguard Worker# Finds differences between two target files packages 20*9e94795aSAndroid Build Coastguard Worker# 21*9e94795aSAndroid Build Coastguard Worker 22*9e94795aSAndroid Build Coastguard Workerfrom __future__ import print_function 23*9e94795aSAndroid Build Coastguard Worker 24*9e94795aSAndroid Build Coastguard Workerimport argparse 25*9e94795aSAndroid Build Coastguard Workerimport contextlib 26*9e94795aSAndroid Build Coastguard Workerimport os 27*9e94795aSAndroid Build Coastguard Workerimport re 28*9e94795aSAndroid Build Coastguard Workerimport subprocess 29*9e94795aSAndroid Build Coastguard Workerimport sys 30*9e94795aSAndroid Build Coastguard Workerimport tempfile 31*9e94795aSAndroid Build Coastguard Worker 32*9e94795aSAndroid Build Coastguard Workerdef ignore(name): 33*9e94795aSAndroid Build Coastguard Worker """ 34*9e94795aSAndroid Build Coastguard Worker Files to ignore when diffing 35*9e94795aSAndroid Build Coastguard Worker 36*9e94795aSAndroid Build Coastguard Worker These are packages that we're already diffing elsewhere, 37*9e94795aSAndroid Build Coastguard Worker or files that we expect to be different for every build, 38*9e94795aSAndroid Build Coastguard Worker or known problems. 39*9e94795aSAndroid Build Coastguard Worker """ 40*9e94795aSAndroid Build Coastguard Worker 41*9e94795aSAndroid Build Coastguard Worker # We're looking at the files that make the images, so no need to search them 42*9e94795aSAndroid Build Coastguard Worker if name in ['IMAGES']: 43*9e94795aSAndroid Build Coastguard Worker return True 44*9e94795aSAndroid Build Coastguard Worker # These are packages of the recovery partition, which we're already diffing 45*9e94795aSAndroid Build Coastguard Worker if name in ['SYSTEM/etc/recovery-resource.dat', 46*9e94795aSAndroid Build Coastguard Worker 'SYSTEM/recovery-from-boot.p']: 47*9e94795aSAndroid Build Coastguard Worker return True 48*9e94795aSAndroid Build Coastguard Worker 49*9e94795aSAndroid Build Coastguard Worker # These files are just the BUILD_NUMBER, and will always be different 50*9e94795aSAndroid Build Coastguard Worker if name in ['BOOT/RAMDISK/selinux_version', 51*9e94795aSAndroid Build Coastguard Worker 'RECOVERY/RAMDISK/selinux_version']: 52*9e94795aSAndroid Build Coastguard Worker return True 53*9e94795aSAndroid Build Coastguard Worker 54*9e94795aSAndroid Build Coastguard Worker return False 55*9e94795aSAndroid Build Coastguard Worker 56*9e94795aSAndroid Build Coastguard Worker 57*9e94795aSAndroid Build Coastguard Workerdef rewrite_build_property(original, new): 58*9e94795aSAndroid Build Coastguard Worker """ 59*9e94795aSAndroid Build Coastguard Worker Rewrite property files to remove values known to change for every build 60*9e94795aSAndroid Build Coastguard Worker """ 61*9e94795aSAndroid Build Coastguard Worker 62*9e94795aSAndroid Build Coastguard Worker skipped = ['ro.bootimage.build.date=', 63*9e94795aSAndroid Build Coastguard Worker 'ro.bootimage.build.date.utc=', 64*9e94795aSAndroid Build Coastguard Worker 'ro.bootimage.build.fingerprint=', 65*9e94795aSAndroid Build Coastguard Worker 'ro.build.id=', 66*9e94795aSAndroid Build Coastguard Worker 'ro.build.display.id=', 67*9e94795aSAndroid Build Coastguard Worker 'ro.build.version.incremental=', 68*9e94795aSAndroid Build Coastguard Worker 'ro.build.date=', 69*9e94795aSAndroid Build Coastguard Worker 'ro.build.date.utc=', 70*9e94795aSAndroid Build Coastguard Worker 'ro.build.host=', 71*9e94795aSAndroid Build Coastguard Worker 'ro.build.user=', 72*9e94795aSAndroid Build Coastguard Worker 'ro.build.description=', 73*9e94795aSAndroid Build Coastguard Worker 'ro.build.fingerprint=', 74*9e94795aSAndroid Build Coastguard Worker 'ro.vendor.build.date=', 75*9e94795aSAndroid Build Coastguard Worker 'ro.vendor.build.date.utc=', 76*9e94795aSAndroid Build Coastguard Worker 'ro.vendor.build.fingerprint='] 77*9e94795aSAndroid Build Coastguard Worker 78*9e94795aSAndroid Build Coastguard Worker for line in original: 79*9e94795aSAndroid Build Coastguard Worker skip = False 80*9e94795aSAndroid Build Coastguard Worker for s in skipped: 81*9e94795aSAndroid Build Coastguard Worker if line.startswith(s): 82*9e94795aSAndroid Build Coastguard Worker skip = True 83*9e94795aSAndroid Build Coastguard Worker break 84*9e94795aSAndroid Build Coastguard Worker if not skip: 85*9e94795aSAndroid Build Coastguard Worker new.write(line.encode()) 86*9e94795aSAndroid Build Coastguard Worker 87*9e94795aSAndroid Build Coastguard Worker 88*9e94795aSAndroid Build Coastguard Workerdef trim_install_recovery(original, new): 89*9e94795aSAndroid Build Coastguard Worker """ 90*9e94795aSAndroid Build Coastguard Worker Rewrite the install-recovery script to remove the hash of the recovery 91*9e94795aSAndroid Build Coastguard Worker partition. 92*9e94795aSAndroid Build Coastguard Worker """ 93*9e94795aSAndroid Build Coastguard Worker for line in original: 94*9e94795aSAndroid Build Coastguard Worker new.write(re.sub(r'[0-9a-f]{40}', '0'*40, line).encode()) 95*9e94795aSAndroid Build Coastguard Worker 96*9e94795aSAndroid Build Coastguard Workerdef sort_file(original, new): 97*9e94795aSAndroid Build Coastguard Worker """ 98*9e94795aSAndroid Build Coastguard Worker Sort the file. Some OTA metadata files are not in a deterministic order 99*9e94795aSAndroid Build Coastguard Worker currently. 100*9e94795aSAndroid Build Coastguard Worker """ 101*9e94795aSAndroid Build Coastguard Worker lines = original.readlines() 102*9e94795aSAndroid Build Coastguard Worker lines.sort() 103*9e94795aSAndroid Build Coastguard Worker for line in lines: 104*9e94795aSAndroid Build Coastguard Worker new.write(line.encode()) 105*9e94795aSAndroid Build Coastguard Worker 106*9e94795aSAndroid Build Coastguard Worker# Map files to the functions that will modify them for diffing 107*9e94795aSAndroid Build Coastguard WorkerREWRITE_RULES = { 108*9e94795aSAndroid Build Coastguard Worker 'BOOT/RAMDISK/default.prop': rewrite_build_property, 109*9e94795aSAndroid Build Coastguard Worker 'RECOVERY/RAMDISK/default.prop': rewrite_build_property, 110*9e94795aSAndroid Build Coastguard Worker 'SYSTEM/build.prop': rewrite_build_property, 111*9e94795aSAndroid Build Coastguard Worker 'VENDOR/build.prop': rewrite_build_property, 112*9e94795aSAndroid Build Coastguard Worker 113*9e94795aSAndroid Build Coastguard Worker 'SYSTEM/bin/install-recovery.sh': trim_install_recovery, 114*9e94795aSAndroid Build Coastguard Worker 115*9e94795aSAndroid Build Coastguard Worker 'META/boot_filesystem_config.txt': sort_file, 116*9e94795aSAndroid Build Coastguard Worker 'META/filesystem_config.txt': sort_file, 117*9e94795aSAndroid Build Coastguard Worker 'META/recovery_filesystem_config.txt': sort_file, 118*9e94795aSAndroid Build Coastguard Worker 'META/vendor_filesystem_config.txt': sort_file, 119*9e94795aSAndroid Build Coastguard Worker} 120*9e94795aSAndroid Build Coastguard Worker 121*9e94795aSAndroid Build Coastguard Worker@contextlib.contextmanager 122*9e94795aSAndroid Build Coastguard Workerdef preprocess(name, filename): 123*9e94795aSAndroid Build Coastguard Worker """ 124*9e94795aSAndroid Build Coastguard Worker Optionally rewrite files before diffing them, to remove known-variable 125*9e94795aSAndroid Build Coastguard Worker information. 126*9e94795aSAndroid Build Coastguard Worker """ 127*9e94795aSAndroid Build Coastguard Worker if name in REWRITE_RULES: 128*9e94795aSAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile() as newfp: 129*9e94795aSAndroid Build Coastguard Worker with open(filename, 'r') as oldfp: 130*9e94795aSAndroid Build Coastguard Worker REWRITE_RULES[name](oldfp, newfp) 131*9e94795aSAndroid Build Coastguard Worker newfp.flush() 132*9e94795aSAndroid Build Coastguard Worker yield newfp.name 133*9e94795aSAndroid Build Coastguard Worker else: 134*9e94795aSAndroid Build Coastguard Worker yield filename 135*9e94795aSAndroid Build Coastguard Worker 136*9e94795aSAndroid Build Coastguard Workerdef diff(name, file1, file2, out_file): 137*9e94795aSAndroid Build Coastguard Worker """ 138*9e94795aSAndroid Build Coastguard Worker Diff a file pair with diff, running preprocess() on the arguments first. 139*9e94795aSAndroid Build Coastguard Worker """ 140*9e94795aSAndroid Build Coastguard Worker with preprocess(name, file1) as f1: 141*9e94795aSAndroid Build Coastguard Worker with preprocess(name, file2) as f2: 142*9e94795aSAndroid Build Coastguard Worker proc = subprocess.Popen(['diff', f1, f2], stdout=subprocess.PIPE, 143*9e94795aSAndroid Build Coastguard Worker stderr=subprocess.STDOUT) 144*9e94795aSAndroid Build Coastguard Worker (stdout, _) = proc.communicate() 145*9e94795aSAndroid Build Coastguard Worker if proc.returncode == 0: 146*9e94795aSAndroid Build Coastguard Worker return 147*9e94795aSAndroid Build Coastguard Worker stdout = stdout.strip() 148*9e94795aSAndroid Build Coastguard Worker if stdout == 'Binary files %s and %s differ' % (f1, f2): 149*9e94795aSAndroid Build Coastguard Worker print("%s: Binary files differ" % name, file=out_file) 150*9e94795aSAndroid Build Coastguard Worker else: 151*9e94795aSAndroid Build Coastguard Worker for line in stdout.strip().split(b'\n'): 152*9e94795aSAndroid Build Coastguard Worker print("%s: %s" % (name, line), file=out_file) 153*9e94795aSAndroid Build Coastguard Worker 154*9e94795aSAndroid Build Coastguard Workerdef recursiveDiff(prefix, dir1, dir2, out_file): 155*9e94795aSAndroid Build Coastguard Worker """ 156*9e94795aSAndroid Build Coastguard Worker Recursively diff two directories, checking metadata then calling diff() 157*9e94795aSAndroid Build Coastguard Worker """ 158*9e94795aSAndroid Build Coastguard Worker list1 = sorted(os.listdir(dir1)) 159*9e94795aSAndroid Build Coastguard Worker list2 = sorted(os.listdir(dir2)) 160*9e94795aSAndroid Build Coastguard Worker 161*9e94795aSAndroid Build Coastguard Worker for entry in list1: 162*9e94795aSAndroid Build Coastguard Worker name = os.path.join(prefix, entry) 163*9e94795aSAndroid Build Coastguard Worker name1 = os.path.join(dir1, entry) 164*9e94795aSAndroid Build Coastguard Worker name2 = os.path.join(dir2, entry) 165*9e94795aSAndroid Build Coastguard Worker 166*9e94795aSAndroid Build Coastguard Worker if ignore(name): 167*9e94795aSAndroid Build Coastguard Worker continue 168*9e94795aSAndroid Build Coastguard Worker 169*9e94795aSAndroid Build Coastguard Worker if entry in list2: 170*9e94795aSAndroid Build Coastguard Worker if os.path.islink(name1) and os.path.islink(name2): 171*9e94795aSAndroid Build Coastguard Worker link1 = os.readlink(name1) 172*9e94795aSAndroid Build Coastguard Worker link2 = os.readlink(name2) 173*9e94795aSAndroid Build Coastguard Worker if link1 != link2: 174*9e94795aSAndroid Build Coastguard Worker print("%s: Symlinks differ: %s vs %s" % (name, link1, link2), 175*9e94795aSAndroid Build Coastguard Worker file=out_file) 176*9e94795aSAndroid Build Coastguard Worker continue 177*9e94795aSAndroid Build Coastguard Worker elif os.path.islink(name1) or os.path.islink(name2): 178*9e94795aSAndroid Build Coastguard Worker print("%s: File types differ, skipping compare" % name, file=out_file) 179*9e94795aSAndroid Build Coastguard Worker continue 180*9e94795aSAndroid Build Coastguard Worker 181*9e94795aSAndroid Build Coastguard Worker stat1 = os.stat(name1) 182*9e94795aSAndroid Build Coastguard Worker stat2 = os.stat(name2) 183*9e94795aSAndroid Build Coastguard Worker type1 = stat1.st_mode & ~0o777 184*9e94795aSAndroid Build Coastguard Worker type2 = stat2.st_mode & ~0o777 185*9e94795aSAndroid Build Coastguard Worker 186*9e94795aSAndroid Build Coastguard Worker if type1 != type2: 187*9e94795aSAndroid Build Coastguard Worker print("%s: File types differ, skipping compare" % name, file=out_file) 188*9e94795aSAndroid Build Coastguard Worker continue 189*9e94795aSAndroid Build Coastguard Worker 190*9e94795aSAndroid Build Coastguard Worker if stat1.st_mode != stat2.st_mode: 191*9e94795aSAndroid Build Coastguard Worker print("%s: Modes differ: %o vs %o" % 192*9e94795aSAndroid Build Coastguard Worker (name, stat1.st_mode, stat2.st_mode), file=out_file) 193*9e94795aSAndroid Build Coastguard Worker 194*9e94795aSAndroid Build Coastguard Worker if os.path.isdir(name1): 195*9e94795aSAndroid Build Coastguard Worker recursiveDiff(name, name1, name2, out_file) 196*9e94795aSAndroid Build Coastguard Worker elif os.path.isfile(name1): 197*9e94795aSAndroid Build Coastguard Worker diff(name, name1, name2, out_file) 198*9e94795aSAndroid Build Coastguard Worker else: 199*9e94795aSAndroid Build Coastguard Worker print("%s: Unknown file type, skipping compare" % name, file=out_file) 200*9e94795aSAndroid Build Coastguard Worker else: 201*9e94795aSAndroid Build Coastguard Worker print("%s: Only in base package" % name, file=out_file) 202*9e94795aSAndroid Build Coastguard Worker 203*9e94795aSAndroid Build Coastguard Worker for entry in list2: 204*9e94795aSAndroid Build Coastguard Worker name = os.path.join(prefix, entry) 205*9e94795aSAndroid Build Coastguard Worker name1 = os.path.join(dir1, entry) 206*9e94795aSAndroid Build Coastguard Worker name2 = os.path.join(dir2, entry) 207*9e94795aSAndroid Build Coastguard Worker 208*9e94795aSAndroid Build Coastguard Worker if ignore(name): 209*9e94795aSAndroid Build Coastguard Worker continue 210*9e94795aSAndroid Build Coastguard Worker 211*9e94795aSAndroid Build Coastguard Worker if entry not in list1: 212*9e94795aSAndroid Build Coastguard Worker print("%s: Only in new package" % name, file=out_file) 213*9e94795aSAndroid Build Coastguard Worker 214*9e94795aSAndroid Build Coastguard Workerdef main(): 215*9e94795aSAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 216*9e94795aSAndroid Build Coastguard Worker parser.add_argument('dir1', help='The base target files package (extracted)') 217*9e94795aSAndroid Build Coastguard Worker parser.add_argument('dir2', help='The new target files package (extracted)') 218*9e94795aSAndroid Build Coastguard Worker parser.add_argument('--output', 219*9e94795aSAndroid Build Coastguard Worker help='The output file, otherwise it prints to stdout') 220*9e94795aSAndroid Build Coastguard Worker args = parser.parse_args() 221*9e94795aSAndroid Build Coastguard Worker 222*9e94795aSAndroid Build Coastguard Worker if args.output: 223*9e94795aSAndroid Build Coastguard Worker out_file = open(args.output, 'w') 224*9e94795aSAndroid Build Coastguard Worker else: 225*9e94795aSAndroid Build Coastguard Worker out_file = sys.stdout 226*9e94795aSAndroid Build Coastguard Worker 227*9e94795aSAndroid Build Coastguard Worker recursiveDiff('', args.dir1, args.dir2, out_file) 228*9e94795aSAndroid Build Coastguard Worker 229*9e94795aSAndroid Build Coastguard Worker if args.output: 230*9e94795aSAndroid Build Coastguard Worker out_file.close() 231*9e94795aSAndroid Build Coastguard Worker 232*9e94795aSAndroid Build Coastguard Workerif __name__ == '__main__': 233*9e94795aSAndroid Build Coastguard Worker main() 234