xref: /aosp_15_r20/system/apex/tools/apexd_host.py (revision 33f3758387333dbd2962d7edbd98681940d895da)
1*33f37583SAndroid Build Coastguard Worker#!/usr/bin/env python
2*33f37583SAndroid Build Coastguard Worker#
3*33f37583SAndroid Build Coastguard Worker# Copyright (C) 2023 The Android Open Source Project
4*33f37583SAndroid Build Coastguard Worker#
5*33f37583SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*33f37583SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*33f37583SAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*33f37583SAndroid Build Coastguard Worker#
9*33f37583SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*33f37583SAndroid Build Coastguard Worker#
11*33f37583SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*33f37583SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*33f37583SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*33f37583SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*33f37583SAndroid Build Coastguard Worker# limitations under the License.
16*33f37583SAndroid Build Coastguard Worker"""apexd_host simulates apexd on host.
17*33f37583SAndroid Build Coastguard Worker
18*33f37583SAndroid Build Coastguard WorkerBasically the tool scans .apex/.capex files from given directories
19*33f37583SAndroid Build Coastguard Worker(e.g --system_path) and extracts them under the output directory (--apex_path)
20*33f37583SAndroid Build Coastguard Workerusing the deapexer tool. It also generates apex-info-list.xml file because
21*33f37583SAndroid Build Coastguard Workersome tools need that file as well to know the partitions of APEX files.
22*33f37583SAndroid Build Coastguard Worker
23*33f37583SAndroid Build Coastguard WorkerThis can be used when handling APEX files on host at buildtime or with
24*33f37583SAndroid Build Coastguard Workertarget-files. For example, check_target_files_vintf tool invokes checkvintf with
25*33f37583SAndroid Build Coastguard Workertarget-files, which, in turn, needs to read VINTF fragment files in APEX files.
26*33f37583SAndroid Build Coastguard WorkerHence, check_target_files_vintf can use apexd_host before checkvintf.
27*33f37583SAndroid Build Coastguard Worker
28*33f37583SAndroid Build Coastguard WorkerExample:
29*33f37583SAndroid Build Coastguard Worker    $ apexd_host --apex_path /path/to/apex --system_path /path/to/system
30*33f37583SAndroid Build Coastguard Worker"""
31*33f37583SAndroid Build Coastguard Workerfrom __future__ import print_function
32*33f37583SAndroid Build Coastguard Worker
33*33f37583SAndroid Build Coastguard Workerimport argparse
34*33f37583SAndroid Build Coastguard Workerimport glob
35*33f37583SAndroid Build Coastguard Workerimport os
36*33f37583SAndroid Build Coastguard Workerimport subprocess
37*33f37583SAndroid Build Coastguard Workerimport sys
38*33f37583SAndroid Build Coastguard Workerfrom xml.dom import minidom
39*33f37583SAndroid Build Coastguard Worker
40*33f37583SAndroid Build Coastguard Workerimport apex_manifest
41*33f37583SAndroid Build Coastguard Worker
42*33f37583SAndroid Build Coastguard Worker
43*33f37583SAndroid Build Coastguard Worker# This should be in sync with kApexPackageBuiltinDirs in
44*33f37583SAndroid Build Coastguard Worker# system/apex/apexd/apex_constants.h
45*33f37583SAndroid Build Coastguard WorkerPARTITIONS = ['system', 'system_ext', 'product', 'vendor', 'odm']
46*33f37583SAndroid Build Coastguard Worker
47*33f37583SAndroid Build Coastguard Worker
48*33f37583SAndroid Build Coastguard Workerdef DirectoryType(path):
49*33f37583SAndroid Build Coastguard Worker  if not os.path.exists(path):
50*33f37583SAndroid Build Coastguard Worker    return None
51*33f37583SAndroid Build Coastguard Worker  if not os.path.isdir(path):
52*33f37583SAndroid Build Coastguard Worker    raise argparse.ArgumentTypeError(f'{path} is not a directory')
53*33f37583SAndroid Build Coastguard Worker  return os.path.realpath(path)
54*33f37583SAndroid Build Coastguard Worker
55*33f37583SAndroid Build Coastguard Worker
56*33f37583SAndroid Build Coastguard Workerdef ExistentDirectoryType(path):
57*33f37583SAndroid Build Coastguard Worker  if not os.path.exists(path):
58*33f37583SAndroid Build Coastguard Worker    raise argparse.ArgumentTypeError(f'{path} is not found')
59*33f37583SAndroid Build Coastguard Worker  return DirectoryType(path)
60*33f37583SAndroid Build Coastguard Worker
61*33f37583SAndroid Build Coastguard Worker
62*33f37583SAndroid Build Coastguard Workerdef ParseArgs():
63*33f37583SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser()
64*33f37583SAndroid Build Coastguard Worker  parser.add_argument('--tool_path', help='Tools are searched in TOOL_PATH/bin')
65*33f37583SAndroid Build Coastguard Worker  parser.add_argument(
66*33f37583SAndroid Build Coastguard Worker      '--apex_path',
67*33f37583SAndroid Build Coastguard Worker      required=True,
68*33f37583SAndroid Build Coastguard Worker      type=ExistentDirectoryType,
69*33f37583SAndroid Build Coastguard Worker      help='Path to the directory where to activate APEXes',
70*33f37583SAndroid Build Coastguard Worker  )
71*33f37583SAndroid Build Coastguard Worker  for part in PARTITIONS:
72*33f37583SAndroid Build Coastguard Worker    parser.add_argument(
73*33f37583SAndroid Build Coastguard Worker        f'--{part}_path',
74*33f37583SAndroid Build Coastguard Worker        help=f'Path to the directory corresponding /{part} on device',
75*33f37583SAndroid Build Coastguard Worker        type=DirectoryType,
76*33f37583SAndroid Build Coastguard Worker    )
77*33f37583SAndroid Build Coastguard Worker  return parser.parse_args()
78*33f37583SAndroid Build Coastguard Worker
79*33f37583SAndroid Build Coastguard Worker
80*33f37583SAndroid Build Coastguard Workerclass ApexFile(object):
81*33f37583SAndroid Build Coastguard Worker  """Represents an APEX file."""
82*33f37583SAndroid Build Coastguard Worker
83*33f37583SAndroid Build Coastguard Worker  def __init__(self, path_on_host, path_on_device):
84*33f37583SAndroid Build Coastguard Worker    self._path_on_host = path_on_host
85*33f37583SAndroid Build Coastguard Worker    self._path_on_device = path_on_device
86*33f37583SAndroid Build Coastguard Worker    self._manifest = apex_manifest.fromApex(path_on_host)
87*33f37583SAndroid Build Coastguard Worker
88*33f37583SAndroid Build Coastguard Worker  @property
89*33f37583SAndroid Build Coastguard Worker  def name(self):
90*33f37583SAndroid Build Coastguard Worker    return self._manifest.name
91*33f37583SAndroid Build Coastguard Worker
92*33f37583SAndroid Build Coastguard Worker  @property
93*33f37583SAndroid Build Coastguard Worker  def path_on_host(self):
94*33f37583SAndroid Build Coastguard Worker    return self._path_on_host
95*33f37583SAndroid Build Coastguard Worker
96*33f37583SAndroid Build Coastguard Worker  @property
97*33f37583SAndroid Build Coastguard Worker  def path_on_device(self):
98*33f37583SAndroid Build Coastguard Worker    return self._path_on_device
99*33f37583SAndroid Build Coastguard Worker
100*33f37583SAndroid Build Coastguard Worker  # Helper to create apex-info element
101*33f37583SAndroid Build Coastguard Worker  @property
102*33f37583SAndroid Build Coastguard Worker  def attrs(self):
103*33f37583SAndroid Build Coastguard Worker    return {
104*33f37583SAndroid Build Coastguard Worker        'moduleName': self.name,
105*33f37583SAndroid Build Coastguard Worker        'modulePath': self.path_on_device,
106*33f37583SAndroid Build Coastguard Worker        'preinstalledModulePath': self.path_on_device,
107*33f37583SAndroid Build Coastguard Worker        'versionCode': str(self._manifest.version),
108*33f37583SAndroid Build Coastguard Worker        'versionName': self._manifest.versionName,
109*33f37583SAndroid Build Coastguard Worker        'isFactory': 'true',
110*33f37583SAndroid Build Coastguard Worker        'isActive': 'true',
111*33f37583SAndroid Build Coastguard Worker        'provideSharedApexLibs': (
112*33f37583SAndroid Build Coastguard Worker            'true' if self._manifest.provideSharedApexLibs else 'false'
113*33f37583SAndroid Build Coastguard Worker        ),
114*33f37583SAndroid Build Coastguard Worker    }
115*33f37583SAndroid Build Coastguard Worker
116*33f37583SAndroid Build Coastguard Worker
117*33f37583SAndroid Build Coastguard Workerdef InitTools(tool_path):
118*33f37583SAndroid Build Coastguard Worker  if tool_path is None:
119*33f37583SAndroid Build Coastguard Worker    exec_path = os.path.realpath(sys.argv[0])
120*33f37583SAndroid Build Coastguard Worker    if exec_path.endswith('.py'):
121*33f37583SAndroid Build Coastguard Worker      script_name = os.path.basename(exec_path)[:-3]
122*33f37583SAndroid Build Coastguard Worker      sys.exit(
123*33f37583SAndroid Build Coastguard Worker          f'Do not invoke {exec_path} directly. Instead, use {script_name}'
124*33f37583SAndroid Build Coastguard Worker      )
125*33f37583SAndroid Build Coastguard Worker    tool_path = os.path.dirname(os.path.dirname(exec_path))
126*33f37583SAndroid Build Coastguard Worker
127*33f37583SAndroid Build Coastguard Worker  def ToolPath(name):
128*33f37583SAndroid Build Coastguard Worker    path = os.path.join(tool_path, 'bin', name)
129*33f37583SAndroid Build Coastguard Worker    if not os.path.exists(path):
130*33f37583SAndroid Build Coastguard Worker      sys.exit(f'Required tool({name}) not found in {tool_path}')
131*33f37583SAndroid Build Coastguard Worker    return path
132*33f37583SAndroid Build Coastguard Worker
133*33f37583SAndroid Build Coastguard Worker  return {
134*33f37583SAndroid Build Coastguard Worker      tool: ToolPath(tool)
135*33f37583SAndroid Build Coastguard Worker      for tool in [
136*33f37583SAndroid Build Coastguard Worker          'deapexer',
137*33f37583SAndroid Build Coastguard Worker          'debugfs_static',
138*33f37583SAndroid Build Coastguard Worker          'fsck.erofs',
139*33f37583SAndroid Build Coastguard Worker      ]
140*33f37583SAndroid Build Coastguard Worker  }
141*33f37583SAndroid Build Coastguard Worker
142*33f37583SAndroid Build Coastguard Worker
143*33f37583SAndroid Build Coastguard Workerdef ScanApexes(partition, real_path) -> list[ApexFile]:
144*33f37583SAndroid Build Coastguard Worker  apexes = []
145*33f37583SAndroid Build Coastguard Worker  for path_on_host in glob.glob(
146*33f37583SAndroid Build Coastguard Worker      os.path.join(real_path, 'apex/*.apex')
147*33f37583SAndroid Build Coastguard Worker  ) + glob.glob(os.path.join(real_path, 'apex/*.capex')):
148*33f37583SAndroid Build Coastguard Worker    path_on_device = f'/{partition}/apex/' + os.path.basename(path_on_host)
149*33f37583SAndroid Build Coastguard Worker    apexes.append(ApexFile(path_on_host, path_on_device))
150*33f37583SAndroid Build Coastguard Worker  # sort list for stability
151*33f37583SAndroid Build Coastguard Worker  return sorted(apexes, key=lambda apex: apex.path_on_device)
152*33f37583SAndroid Build Coastguard Worker
153*33f37583SAndroid Build Coastguard Worker
154*33f37583SAndroid Build Coastguard Workerdef ActivateApexes(partitions, apex_dir, tools):
155*33f37583SAndroid Build Coastguard Worker  # Emit apex-info-list.xml
156*33f37583SAndroid Build Coastguard Worker  impl = minidom.getDOMImplementation()
157*33f37583SAndroid Build Coastguard Worker  doc = impl.createDocument(None, 'apex-info-list', None)
158*33f37583SAndroid Build Coastguard Worker  apex_info_list = doc.documentElement
159*33f37583SAndroid Build Coastguard Worker
160*33f37583SAndroid Build Coastguard Worker  # Scan each partition for apexes and activate them
161*33f37583SAndroid Build Coastguard Worker  for partition, real_path in partitions.items():
162*33f37583SAndroid Build Coastguard Worker    apexes = ScanApexes(partition, real_path)
163*33f37583SAndroid Build Coastguard Worker
164*33f37583SAndroid Build Coastguard Worker    # Activate each apex with deapexer
165*33f37583SAndroid Build Coastguard Worker    for apex_file in apexes:
166*33f37583SAndroid Build Coastguard Worker      # Multi-apex is ignored
167*33f37583SAndroid Build Coastguard Worker      if os.path.exists(os.path.join(apex_dir, apex_file.name)):
168*33f37583SAndroid Build Coastguard Worker        continue
169*33f37583SAndroid Build Coastguard Worker
170*33f37583SAndroid Build Coastguard Worker      cmd = [tools['deapexer']]
171*33f37583SAndroid Build Coastguard Worker      cmd += ['--debugfs_path', tools['debugfs_static']]
172*33f37583SAndroid Build Coastguard Worker      cmd += ['--fsckerofs_path', tools['fsck.erofs']]
173*33f37583SAndroid Build Coastguard Worker      cmd += [
174*33f37583SAndroid Build Coastguard Worker          'extract',
175*33f37583SAndroid Build Coastguard Worker          apex_file.path_on_host,
176*33f37583SAndroid Build Coastguard Worker          os.path.join(apex_dir, apex_file.name),
177*33f37583SAndroid Build Coastguard Worker      ]
178*33f37583SAndroid Build Coastguard Worker      subprocess.check_output(cmd, text=True, stderr=subprocess.STDOUT)
179*33f37583SAndroid Build Coastguard Worker
180*33f37583SAndroid Build Coastguard Worker      # Add activated apex_info to apex_info_list
181*33f37583SAndroid Build Coastguard Worker      apex_info = doc.createElement('apex-info')
182*33f37583SAndroid Build Coastguard Worker      for name, value in apex_file.attrs.items():
183*33f37583SAndroid Build Coastguard Worker        apex_info.setAttribute(name, value)
184*33f37583SAndroid Build Coastguard Worker      apex_info_list.appendChild(apex_info)
185*33f37583SAndroid Build Coastguard Worker
186*33f37583SAndroid Build Coastguard Worker  apex_info_list_file = os.path.join(apex_dir, 'apex-info-list.xml')
187*33f37583SAndroid Build Coastguard Worker  with open(apex_info_list_file, 'wt', encoding='utf-8') as f:
188*33f37583SAndroid Build Coastguard Worker    doc.writexml(f, encoding='utf-8', addindent='  ', newl='\n')
189*33f37583SAndroid Build Coastguard Worker
190*33f37583SAndroid Build Coastguard Worker
191*33f37583SAndroid Build Coastguard Workerdef main():
192*33f37583SAndroid Build Coastguard Worker  args = ParseArgs()
193*33f37583SAndroid Build Coastguard Worker
194*33f37583SAndroid Build Coastguard Worker  partitions = {}
195*33f37583SAndroid Build Coastguard Worker  for part in PARTITIONS:
196*33f37583SAndroid Build Coastguard Worker    if vars(args).get(f'{part}_path'):
197*33f37583SAndroid Build Coastguard Worker      partitions[part] = vars(args).get(f'{part}_path')
198*33f37583SAndroid Build Coastguard Worker
199*33f37583SAndroid Build Coastguard Worker  tools = InitTools(args.tool_path)
200*33f37583SAndroid Build Coastguard Worker  ActivateApexes(partitions, args.apex_path, tools)
201*33f37583SAndroid Build Coastguard Worker
202*33f37583SAndroid Build Coastguard Worker
203*33f37583SAndroid Build Coastguard Workerif __name__ == '__main__':
204*33f37583SAndroid Build Coastguard Worker  main()
205