xref: /aosp_15_r20/development/vndk/tools/system_image_diff.py (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1*90c8c64dSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*90c8c64dSAndroid Build Coastguard Worker
3*90c8c64dSAndroid Build Coastguard Workerimport argparse
4*90c8c64dSAndroid Build Coastguard Workerimport csv
5*90c8c64dSAndroid Build Coastguard Workerimport glob
6*90c8c64dSAndroid Build Coastguard Workerimport json
7*90c8c64dSAndroid Build Coastguard Workerimport os
8*90c8c64dSAndroid Build Coastguard Workerimport sys
9*90c8c64dSAndroid Build Coastguard Worker
10*90c8c64dSAndroid Build Coastguard WorkerHELP_MSG = '''
11*90c8c64dSAndroid Build Coastguard WorkerThis script computes the differences between two system images (system1 -
12*90c8c64dSAndroid Build Coastguard Workersystem2), and lists the files grouped by package. The difference is just based
13*90c8c64dSAndroid Build Coastguard Workeron the existence of the file, not on its contents.
14*90c8c64dSAndroid Build Coastguard Worker'''
15*90c8c64dSAndroid Build Coastguard Worker
16*90c8c64dSAndroid Build Coastguard WorkerVENDOR_PATH_MAP = {
17*90c8c64dSAndroid Build Coastguard Worker    'vendor/google' : 'Google',
18*90c8c64dSAndroid Build Coastguard Worker    'vendor/unbundled_google': 'Google',
19*90c8c64dSAndroid Build Coastguard Worker    'vendor/verizon' : 'Verizon',
20*90c8c64dSAndroid Build Coastguard Worker    'vendor/qcom' : 'Qualcomm',
21*90c8c64dSAndroid Build Coastguard Worker    'vendor/tmobile' : 'TMobile',
22*90c8c64dSAndroid Build Coastguard Worker    'vendor/mediatek' : 'Mediatek',
23*90c8c64dSAndroid Build Coastguard Worker    'vendor/htc' : 'HTC',
24*90c8c64dSAndroid Build Coastguard Worker    'vendor/realtek' : 'Realtek'
25*90c8c64dSAndroid Build Coastguard Worker}
26*90c8c64dSAndroid Build Coastguard Worker
27*90c8c64dSAndroid Build Coastguard Workerdef _get_relative_out_path_from_root(out_path):
28*90c8c64dSAndroid Build Coastguard Worker  """Given a path to a target out directory, get the relative path from the
29*90c8c64dSAndroid Build Coastguard Worker  Android root.
30*90c8c64dSAndroid Build Coastguard Worker
31*90c8c64dSAndroid Build Coastguard Worker  The module-info.json file paths are relative to the root source folder
32*90c8c64dSAndroid Build Coastguard Worker  ie. one directory before out."""
33*90c8c64dSAndroid Build Coastguard Worker  system_path = os.path.normpath(os.path.join(out_path, 'system'))
34*90c8c64dSAndroid Build Coastguard Worker  system_path_dirs = system_path.split(os.sep)
35*90c8c64dSAndroid Build Coastguard Worker  out_index = system_path_dirs.index("out")
36*90c8c64dSAndroid Build Coastguard Worker  return os.path.join(*system_path_dirs[out_index:])
37*90c8c64dSAndroid Build Coastguard Worker
38*90c8c64dSAndroid Build Coastguard Workerdef system_files(path):
39*90c8c64dSAndroid Build Coastguard Worker  """Returns an array of the files under /system, recursively, and ignoring
40*90c8c64dSAndroid Build Coastguard Worker  symbolic-links"""
41*90c8c64dSAndroid Build Coastguard Worker  system_files = []
42*90c8c64dSAndroid Build Coastguard Worker  system_prefix = os.path.join(path, 'system')
43*90c8c64dSAndroid Build Coastguard Worker  # Skip trailing '/'
44*90c8c64dSAndroid Build Coastguard Worker  system_prefix_len = len(system_prefix) + 1
45*90c8c64dSAndroid Build Coastguard Worker
46*90c8c64dSAndroid Build Coastguard Worker  for root, dirs, files in os.walk(system_prefix, topdown=True):
47*90c8c64dSAndroid Build Coastguard Worker    for file in files:
48*90c8c64dSAndroid Build Coastguard Worker      # Ignore symbolic links.
49*90c8c64dSAndroid Build Coastguard Worker      if not os.path.islink(os.path.join(root, file)):
50*90c8c64dSAndroid Build Coastguard Worker        system_files.append(os.path.join(root[system_prefix_len:], file))
51*90c8c64dSAndroid Build Coastguard Worker
52*90c8c64dSAndroid Build Coastguard Worker  return system_files
53*90c8c64dSAndroid Build Coastguard Worker
54*90c8c64dSAndroid Build Coastguard Workerdef system_files_to_package_map(path):
55*90c8c64dSAndroid Build Coastguard Worker  """Returns a dictionary mapping from each file in the /system partition to its
56*90c8c64dSAndroid Build Coastguard Worker  package, according to modules-info.json."""
57*90c8c64dSAndroid Build Coastguard Worker  system_files_to_package_map = {}
58*90c8c64dSAndroid Build Coastguard Worker  system_prefix = _get_relative_out_path_from_root(path)
59*90c8c64dSAndroid Build Coastguard Worker  # Skip trailing '/'
60*90c8c64dSAndroid Build Coastguard Worker  system_prefix_len = len(system_prefix) + 1
61*90c8c64dSAndroid Build Coastguard Worker
62*90c8c64dSAndroid Build Coastguard Worker  with open(os.path.join(path, 'module-info.json')) as module_info_json:
63*90c8c64dSAndroid Build Coastguard Worker    module_info = json.load(module_info_json)
64*90c8c64dSAndroid Build Coastguard Worker    for module in module_info:
65*90c8c64dSAndroid Build Coastguard Worker      installs = module_info[module]['installed']
66*90c8c64dSAndroid Build Coastguard Worker      for install in installs:
67*90c8c64dSAndroid Build Coastguard Worker        if install.startswith(system_prefix):
68*90c8c64dSAndroid Build Coastguard Worker          system_file = install[system_prefix_len:]
69*90c8c64dSAndroid Build Coastguard Worker          # Not clear if collisions can ever happen in modules-info.json (e.g.
70*90c8c64dSAndroid Build Coastguard Worker          # the same file installed by multiple packages), but it doesn't hurt
71*90c8c64dSAndroid Build Coastguard Worker          # to check.
72*90c8c64dSAndroid Build Coastguard Worker          if system_file in system_files_to_package_map:
73*90c8c64dSAndroid Build Coastguard Worker            system_files_to_package_map[system_file] = "--multiple--"
74*90c8c64dSAndroid Build Coastguard Worker          else:
75*90c8c64dSAndroid Build Coastguard Worker            system_files_to_package_map[system_file] = module
76*90c8c64dSAndroid Build Coastguard Worker
77*90c8c64dSAndroid Build Coastguard Worker  return system_files_to_package_map
78*90c8c64dSAndroid Build Coastguard Worker
79*90c8c64dSAndroid Build Coastguard Workerdef package_to_vendor_map(path):
80*90c8c64dSAndroid Build Coastguard Worker  """Returns a dictionary mapping from each package in modules-info.json to its
81*90c8c64dSAndroid Build Coastguard Worker  vendor. If a vendor cannot be found, it maps to "--unknown--". Those cases
82*90c8c64dSAndroid Build Coastguard Worker  are:
83*90c8c64dSAndroid Build Coastguard Worker
84*90c8c64dSAndroid Build Coastguard Worker    1. The package maps to multiple modules (e.g., one in APPS and one in
85*90c8c64dSAndroid Build Coastguard Worker       SHARED_LIBRARIES.
86*90c8c64dSAndroid Build Coastguard Worker    2. The path to the module is not one of the recognized vendor paths in
87*90c8c64dSAndroid Build Coastguard Worker       VENDOR_PATH_MAP."""
88*90c8c64dSAndroid Build Coastguard Worker  package_vendor_map = {}
89*90c8c64dSAndroid Build Coastguard Worker  system_prefix = os.path.join(path, 'system')
90*90c8c64dSAndroid Build Coastguard Worker  # Skip trailing '/'
91*90c8c64dSAndroid Build Coastguard Worker  system_prefix_len = len(system_prefix) + 1
92*90c8c64dSAndroid Build Coastguard Worker  vendor_prefixes = VENDOR_PATH_MAP.keys()
93*90c8c64dSAndroid Build Coastguard Worker
94*90c8c64dSAndroid Build Coastguard Worker  with open(os.path.join(path, 'module-info.json')) as module_info_json:
95*90c8c64dSAndroid Build Coastguard Worker    module_info = json.load(module_info_json)
96*90c8c64dSAndroid Build Coastguard Worker    for module in module_info:
97*90c8c64dSAndroid Build Coastguard Worker      paths = module_info[module]['path']
98*90c8c64dSAndroid Build Coastguard Worker      vendor = ""
99*90c8c64dSAndroid Build Coastguard Worker      if len(paths) == 1:
100*90c8c64dSAndroid Build Coastguard Worker        path = paths[0]
101*90c8c64dSAndroid Build Coastguard Worker        for prefix in vendor_prefixes:
102*90c8c64dSAndroid Build Coastguard Worker          if path.startswith(prefix):
103*90c8c64dSAndroid Build Coastguard Worker            vendor = VENDOR_PATH_MAP[prefix]
104*90c8c64dSAndroid Build Coastguard Worker            break
105*90c8c64dSAndroid Build Coastguard Worker        if vendor == "":
106*90c8c64dSAndroid Build Coastguard Worker          vendor = "--unknown--"
107*90c8c64dSAndroid Build Coastguard Worker      else:
108*90c8c64dSAndroid Build Coastguard Worker        vendor = "--multiple--"
109*90c8c64dSAndroid Build Coastguard Worker      package_vendor_map[module] = vendor
110*90c8c64dSAndroid Build Coastguard Worker
111*90c8c64dSAndroid Build Coastguard Worker  return package_vendor_map
112*90c8c64dSAndroid Build Coastguard Worker
113*90c8c64dSAndroid Build Coastguard Workerdef main():
114*90c8c64dSAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(description=HELP_MSG)
115*90c8c64dSAndroid Build Coastguard Worker  parser.add_argument("out1", help="First $OUT directory")
116*90c8c64dSAndroid Build Coastguard Worker  parser.add_argument("out2", help="Second $OUT directory")
117*90c8c64dSAndroid Build Coastguard Worker  args = parser.parse_args()
118*90c8c64dSAndroid Build Coastguard Worker
119*90c8c64dSAndroid Build Coastguard Worker  system_files1 = system_files(args.out1)
120*90c8c64dSAndroid Build Coastguard Worker  system_files2 = system_files(args.out2)
121*90c8c64dSAndroid Build Coastguard Worker  system_files_diff = set(system_files1) - set(system_files2)
122*90c8c64dSAndroid Build Coastguard Worker
123*90c8c64dSAndroid Build Coastguard Worker  system_files_map = system_files_to_package_map(args.out1)
124*90c8c64dSAndroid Build Coastguard Worker  package_vendor_map = package_to_vendor_map(args.out1)
125*90c8c64dSAndroid Build Coastguard Worker  packages = {}
126*90c8c64dSAndroid Build Coastguard Worker
127*90c8c64dSAndroid Build Coastguard Worker  for file in system_files_diff:
128*90c8c64dSAndroid Build Coastguard Worker    if file in system_files_map:
129*90c8c64dSAndroid Build Coastguard Worker      package = system_files_map[file]
130*90c8c64dSAndroid Build Coastguard Worker    else:
131*90c8c64dSAndroid Build Coastguard Worker      package = "--unknown--"
132*90c8c64dSAndroid Build Coastguard Worker
133*90c8c64dSAndroid Build Coastguard Worker    if package in packages:
134*90c8c64dSAndroid Build Coastguard Worker      packages[package].append(file)
135*90c8c64dSAndroid Build Coastguard Worker    else:
136*90c8c64dSAndroid Build Coastguard Worker      packages[package] = [file]
137*90c8c64dSAndroid Build Coastguard Worker
138*90c8c64dSAndroid Build Coastguard Worker  with open(os.path.join(args.out1, 'module-info.json')) as module_info_json:
139*90c8c64dSAndroid Build Coastguard Worker    module_info = json.load(module_info_json)
140*90c8c64dSAndroid Build Coastguard Worker
141*90c8c64dSAndroid Build Coastguard Worker  writer = csv.writer(sys.stdout, quoting = csv.QUOTE_NONNUMERIC,
142*90c8c64dSAndroid Build Coastguard Worker                      delimiter = ',', lineterminator = '\n')
143*90c8c64dSAndroid Build Coastguard Worker  for package, files in packages.iteritems():
144*90c8c64dSAndroid Build Coastguard Worker    for file in files:
145*90c8c64dSAndroid Build Coastguard Worker      # Group sources of the deltas.
146*90c8c64dSAndroid Build Coastguard Worker      if package in package_vendor_map:
147*90c8c64dSAndroid Build Coastguard Worker        vendor = package_vendor_map[package]
148*90c8c64dSAndroid Build Coastguard Worker      else:
149*90c8c64dSAndroid Build Coastguard Worker        vendor = "--unknown--"
150*90c8c64dSAndroid Build Coastguard Worker      # Get file size.
151*90c8c64dSAndroid Build Coastguard Worker      full_path = os.path.join(args.out1, 'system', file)
152*90c8c64dSAndroid Build Coastguard Worker      size = os.stat(full_path).st_size
153*90c8c64dSAndroid Build Coastguard Worker      if package in module_info.keys():
154*90c8c64dSAndroid Build Coastguard Worker        module_path = module_info[package]['path']
155*90c8c64dSAndroid Build Coastguard Worker      else:
156*90c8c64dSAndroid Build Coastguard Worker        module_path = ''
157*90c8c64dSAndroid Build Coastguard Worker      writer.writerow([
158*90c8c64dSAndroid Build Coastguard Worker          # File that exists in out1 but not out2.
159*90c8c64dSAndroid Build Coastguard Worker          file,
160*90c8c64dSAndroid Build Coastguard Worker          # Module name that the file came from.
161*90c8c64dSAndroid Build Coastguard Worker          package,
162*90c8c64dSAndroid Build Coastguard Worker          # Path to the module.
163*90c8c64dSAndroid Build Coastguard Worker          module_path,
164*90c8c64dSAndroid Build Coastguard Worker          # File size.
165*90c8c64dSAndroid Build Coastguard Worker          size,
166*90c8c64dSAndroid Build Coastguard Worker          # Vendor owner.
167*90c8c64dSAndroid Build Coastguard Worker          vendor])
168*90c8c64dSAndroid Build Coastguard Worker
169*90c8c64dSAndroid Build Coastguard Workerif __name__ == '__main__':
170*90c8c64dSAndroid Build Coastguard Worker  main()
171