xref: /aosp_15_r20/external/angle/build/android/gyp/merge_manifest.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1#!/usr/bin/env python3
2
3# Copyright 2017 The Chromium Authors
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Merges dependency Android manifests into a root manifest."""
8
9import argparse
10import collections
11import contextlib
12import os
13import sys
14import tempfile
15import xml.etree.ElementTree as ElementTree
16
17from util import build_utils
18from util import manifest_utils
19import action_helpers  # build_utils adds //build to sys.path.
20
21_MANIFEST_MERGER_MAIN_CLASS = 'com.android.manifmerger.Merger'
22
23
24@contextlib.contextmanager
25def _ProcessMainManifest(manifest_path, min_sdk_version, target_sdk_version,
26                         max_sdk_version, manifest_package):
27  """Patches the main Android manifest"""
28  doc, manifest, _ = manifest_utils.ParseManifest(manifest_path)
29  manifest_utils.SetUsesSdk(manifest, target_sdk_version, min_sdk_version,
30                            max_sdk_version)
31  assert manifest_utils.GetPackage(manifest) or manifest_package, \
32            'Must set manifest package in GN or in AndroidManifest.xml'
33  if manifest_package:
34    manifest.set('package', manifest_package)
35  tmp_prefix = manifest_path.replace(os.path.sep, '-')
36  with tempfile.NamedTemporaryFile(prefix=tmp_prefix) as patched_manifest:
37    manifest_utils.SaveManifest(doc, patched_manifest.name)
38    yield patched_manifest.name, manifest_utils.GetPackage(manifest)
39
40
41@contextlib.contextmanager
42def _ProcessOtherManifest(manifest_path, min_sdk_version, target_sdk_version,
43                          seen_package_names):
44  """Patches non-main AndroidManifest.xml if necessary."""
45  # 1. Ensure targetSdkVersion is set to the expected value to avoid
46  #    spurious permissions being added (b/222331337).
47  # 2. Ensure all manifests have a unique package name so that the merger
48  #    does not fail when this happens.
49  doc, manifest, _ = manifest_utils.ParseManifest(manifest_path)
50
51  changed_api = manifest_utils.SetTargetApiIfUnset(manifest, target_sdk_version)
52
53  package_name = manifest_utils.GetPackage(manifest)
54  # Ignore minSdkVersion from androidx.pdf library. The client code will ensure
55  # not to call into the library API on older Android versions.
56  if package_name.startswith('androidx.pdf'):
57    manifest_utils.OverrideMinSdkVersionIfPresent(manifest, min_sdk_version)
58    changed_api = True
59  package_count = seen_package_names[package_name]
60  seen_package_names[package_name] += 1
61  if package_count > 0:
62    manifest.set('package', f'{package_name}_{package_count}')
63
64  if package_count > 0 or changed_api:
65    tmp_prefix = manifest_path.replace(os.path.sep, '-')
66    with tempfile.NamedTemporaryFile(prefix=tmp_prefix) as patched_manifest:
67      manifest_utils.SaveManifest(doc, patched_manifest.name)
68      yield patched_manifest.name
69  else:
70    yield manifest_path
71
72
73def main(argv):
74  argv = build_utils.ExpandFileArgs(argv)
75  parser = argparse.ArgumentParser(description=__doc__)
76  action_helpers.add_depfile_arg(parser)
77  parser.add_argument('--manifest-merger-jar',
78                      help='Path to SDK\'s manifest merger jar.',
79                      required=True)
80  parser.add_argument('--root-manifest',
81                      help='Root manifest which to merge into',
82                      required=True)
83  parser.add_argument('--output', help='Output manifest path', required=True)
84  parser.add_argument('--extras',
85                      help='GN list of additional manifest to merge')
86  parser.add_argument(
87      '--min-sdk-version',
88      required=True,
89      help='android:minSdkVersion for merging.')
90  parser.add_argument(
91      '--target-sdk-version',
92      required=True,
93      help='android:targetSdkVersion for merging.')
94  parser.add_argument(
95      '--max-sdk-version', help='android:maxSdkVersion for merging.')
96  parser.add_argument(
97      '--manifest-package',
98      help='Package name of the merged AndroidManifest.xml.')
99  parser.add_argument('--warnings-as-errors',
100                      action='store_true',
101                      help='Treat all warnings as errors.')
102  args = parser.parse_args(argv)
103
104  with action_helpers.atomic_output(args.output) as output:
105    cmd = build_utils.JavaCmd() + [
106        '-cp',
107        args.manifest_merger_jar,
108        _MANIFEST_MERGER_MAIN_CLASS,
109        '--out',
110        output.name,
111        '--property',
112        'MIN_SDK_VERSION=' + args.min_sdk_version,
113        '--property',
114        'TARGET_SDK_VERSION=' + args.target_sdk_version,
115    ]
116
117    if args.max_sdk_version:
118      cmd += [
119          '--property',
120          'MAX_SDK_VERSION=' + args.max_sdk_version,
121      ]
122
123    extras = action_helpers.parse_gn_list(args.extras)
124
125    with contextlib.ExitStack() as stack:
126      root_manifest, package = stack.enter_context(
127          _ProcessMainManifest(args.root_manifest, args.min_sdk_version,
128                               args.target_sdk_version, args.max_sdk_version,
129                               args.manifest_package))
130      if extras:
131        seen_package_names = collections.Counter()
132        extras_processed = [
133            stack.enter_context(
134                _ProcessOtherManifest(e, args.min_sdk_version,
135                                      args.target_sdk_version,
136                                      seen_package_names)) for e in extras
137        ]
138        cmd += ['--libs', ':'.join(extras_processed)]
139      cmd += [
140          '--main',
141          root_manifest,
142          '--property',
143          'PACKAGE=' + package,
144          '--remove-tools-declarations',
145      ]
146      build_utils.CheckOutput(
147          cmd,
148          # https://issuetracker.google.com/issues/63514300:
149          # The merger doesn't set a nonzero exit code for failures.
150          fail_func=lambda returncode, stderr: returncode != 0 or build_utils.
151          IsTimeStale(output.name, [root_manifest] + extras),
152          fail_on_output=args.warnings_as_errors)
153
154  if args.depfile:
155    action_helpers.write_depfile(args.depfile, args.output, inputs=extras)
156
157
158if __name__ == '__main__':
159  main(sys.argv[1:])
160