xref: /aosp_15_r20/build/make/tools/releasetools/target_files_diff.py (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
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