xref: /aosp_15_r20/build/soong/scripts/manifest.py (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker#!/usr/bin/env python
2*333d2b36SAndroid Build Coastguard Worker#
3*333d2b36SAndroid Build Coastguard Worker# Copyright (C) 2018 The Android Open Source Project
4*333d2b36SAndroid Build Coastguard Worker#
5*333d2b36SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*333d2b36SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*333d2b36SAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*333d2b36SAndroid Build Coastguard Worker#
9*333d2b36SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*333d2b36SAndroid Build Coastguard Worker#
11*333d2b36SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*333d2b36SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*333d2b36SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*333d2b36SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*333d2b36SAndroid Build Coastguard Worker# limitations under the License.
16*333d2b36SAndroid Build Coastguard Worker#
17*333d2b36SAndroid Build Coastguard Worker"""A tool for inserting values from the build system into a manifest or a test config."""
18*333d2b36SAndroid Build Coastguard Worker
19*333d2b36SAndroid Build Coastguard Workerfrom xml.dom import minidom
20*333d2b36SAndroid Build Coastguard Worker
21*333d2b36SAndroid Build Coastguard Worker
22*333d2b36SAndroid Build Coastguard Workerandroid_ns = 'http://schemas.android.com/apk/res/android'
23*333d2b36SAndroid Build Coastguard Worker
24*333d2b36SAndroid Build Coastguard Worker
25*333d2b36SAndroid Build Coastguard Workerdef get_or_create_applications(doc, manifest):
26*333d2b36SAndroid Build Coastguard Worker  """Get all <application> tags from the manifest, or create one if none exist.
27*333d2b36SAndroid Build Coastguard Worker  Multiple <application> tags may exist when manifest feature flagging is used.
28*333d2b36SAndroid Build Coastguard Worker  """
29*333d2b36SAndroid Build Coastguard Worker  applications = get_children_with_tag(manifest, 'application')
30*333d2b36SAndroid Build Coastguard Worker  if len(applications) == 0:
31*333d2b36SAndroid Build Coastguard Worker    application = doc.createElement('application')
32*333d2b36SAndroid Build Coastguard Worker    indent = get_indent(manifest.firstChild, 1)
33*333d2b36SAndroid Build Coastguard Worker    first = manifest.firstChild
34*333d2b36SAndroid Build Coastguard Worker    manifest.insertBefore(doc.createTextNode(indent), first)
35*333d2b36SAndroid Build Coastguard Worker    manifest.insertBefore(application, first)
36*333d2b36SAndroid Build Coastguard Worker    applications.append(application)
37*333d2b36SAndroid Build Coastguard Worker  return applications
38*333d2b36SAndroid Build Coastguard Worker
39*333d2b36SAndroid Build Coastguard Worker
40*333d2b36SAndroid Build Coastguard Workerdef get_or_create_uses_sdks(doc, manifest):
41*333d2b36SAndroid Build Coastguard Worker  """Get all <uses-sdk> tags from the manifest, or create one if none exist.
42*333d2b36SAndroid Build Coastguard Worker  Multiple <uses-sdk> tags may exist when manifest feature flagging is used.
43*333d2b36SAndroid Build Coastguard Worker  """
44*333d2b36SAndroid Build Coastguard Worker  uses_sdks = get_children_with_tag(manifest, 'uses-sdk')
45*333d2b36SAndroid Build Coastguard Worker  if len(uses_sdks) == 0:
46*333d2b36SAndroid Build Coastguard Worker    uses_sdk = doc.createElement('uses-sdk')
47*333d2b36SAndroid Build Coastguard Worker    indent = get_indent(manifest.firstChild, 1)
48*333d2b36SAndroid Build Coastguard Worker    manifest.insertBefore(uses_sdk, manifest.firstChild)
49*333d2b36SAndroid Build Coastguard Worker
50*333d2b36SAndroid Build Coastguard Worker    # Insert an indent before uses-sdk to line it up with the indentation of the
51*333d2b36SAndroid Build Coastguard Worker    # other children of the <manifest> tag.
52*333d2b36SAndroid Build Coastguard Worker    manifest.insertBefore(doc.createTextNode(indent), manifest.firstChild)
53*333d2b36SAndroid Build Coastguard Worker    uses_sdks.append(uses_sdk)
54*333d2b36SAndroid Build Coastguard Worker  return uses_sdks
55*333d2b36SAndroid Build Coastguard Worker
56*333d2b36SAndroid Build Coastguard Workerdef get_children_with_tag(parent, tag_name):
57*333d2b36SAndroid Build Coastguard Worker  children = []
58*333d2b36SAndroid Build Coastguard Worker  for child in parent.childNodes:
59*333d2b36SAndroid Build Coastguard Worker    if child.nodeType == minidom.Node.ELEMENT_NODE and \
60*333d2b36SAndroid Build Coastguard Worker       child.tagName == tag_name:
61*333d2b36SAndroid Build Coastguard Worker      children.append(child)
62*333d2b36SAndroid Build Coastguard Worker  return children
63*333d2b36SAndroid Build Coastguard Worker
64*333d2b36SAndroid Build Coastguard Worker
65*333d2b36SAndroid Build Coastguard Workerdef find_child_with_attribute(element, tag_name, namespace_uri,
66*333d2b36SAndroid Build Coastguard Worker                              attr_name, value):
67*333d2b36SAndroid Build Coastguard Worker  for child in get_children_with_tag(element, tag_name):
68*333d2b36SAndroid Build Coastguard Worker    attr = child.getAttributeNodeNS(namespace_uri, attr_name)
69*333d2b36SAndroid Build Coastguard Worker    if attr is not None and attr.value == value:
70*333d2b36SAndroid Build Coastguard Worker      return child
71*333d2b36SAndroid Build Coastguard Worker  return None
72*333d2b36SAndroid Build Coastguard Worker
73*333d2b36SAndroid Build Coastguard Worker
74*333d2b36SAndroid Build Coastguard Workerdef parse_manifest(doc):
75*333d2b36SAndroid Build Coastguard Worker  """Get the manifest element."""
76*333d2b36SAndroid Build Coastguard Worker
77*333d2b36SAndroid Build Coastguard Worker  manifest = doc.documentElement
78*333d2b36SAndroid Build Coastguard Worker  if manifest.tagName != 'manifest':
79*333d2b36SAndroid Build Coastguard Worker    raise RuntimeError('expected manifest tag at root')
80*333d2b36SAndroid Build Coastguard Worker  return manifest
81*333d2b36SAndroid Build Coastguard Worker
82*333d2b36SAndroid Build Coastguard Worker
83*333d2b36SAndroid Build Coastguard Workerdef ensure_manifest_android_ns(doc):
84*333d2b36SAndroid Build Coastguard Worker  """Make sure the manifest tag defines the android namespace."""
85*333d2b36SAndroid Build Coastguard Worker
86*333d2b36SAndroid Build Coastguard Worker  manifest = parse_manifest(doc)
87*333d2b36SAndroid Build Coastguard Worker
88*333d2b36SAndroid Build Coastguard Worker  ns = manifest.getAttributeNodeNS(minidom.XMLNS_NAMESPACE, 'android')
89*333d2b36SAndroid Build Coastguard Worker  if ns is None:
90*333d2b36SAndroid Build Coastguard Worker    attr = doc.createAttributeNS(minidom.XMLNS_NAMESPACE, 'xmlns:android')
91*333d2b36SAndroid Build Coastguard Worker    attr.value = android_ns
92*333d2b36SAndroid Build Coastguard Worker    manifest.setAttributeNode(attr)
93*333d2b36SAndroid Build Coastguard Worker  elif ns.value != android_ns:
94*333d2b36SAndroid Build Coastguard Worker    raise RuntimeError('manifest tag has incorrect android namespace ' +
95*333d2b36SAndroid Build Coastguard Worker                       ns.value)
96*333d2b36SAndroid Build Coastguard Worker
97*333d2b36SAndroid Build Coastguard Worker
98*333d2b36SAndroid Build Coastguard Workerdef parse_test_config(doc):
99*333d2b36SAndroid Build Coastguard Worker  """ Get the configuration element. """
100*333d2b36SAndroid Build Coastguard Worker
101*333d2b36SAndroid Build Coastguard Worker  test_config = doc.documentElement
102*333d2b36SAndroid Build Coastguard Worker  if test_config.tagName != 'configuration':
103*333d2b36SAndroid Build Coastguard Worker    raise RuntimeError('expected configuration tag at root')
104*333d2b36SAndroid Build Coastguard Worker  return test_config
105*333d2b36SAndroid Build Coastguard Worker
106*333d2b36SAndroid Build Coastguard Worker
107*333d2b36SAndroid Build Coastguard Workerdef as_int(s):
108*333d2b36SAndroid Build Coastguard Worker  try:
109*333d2b36SAndroid Build Coastguard Worker    i = int(s)
110*333d2b36SAndroid Build Coastguard Worker  except ValueError:
111*333d2b36SAndroid Build Coastguard Worker    return s, False
112*333d2b36SAndroid Build Coastguard Worker  return i, True
113*333d2b36SAndroid Build Coastguard Worker
114*333d2b36SAndroid Build Coastguard Worker
115*333d2b36SAndroid Build Coastguard Workerdef compare_version_gt(a, b):
116*333d2b36SAndroid Build Coastguard Worker  """Compare two SDK versions.
117*333d2b36SAndroid Build Coastguard Worker
118*333d2b36SAndroid Build Coastguard Worker  Compares a and b, treating codenames like 'Q' as higher
119*333d2b36SAndroid Build Coastguard Worker  than numerical versions like '28'.
120*333d2b36SAndroid Build Coastguard Worker
121*333d2b36SAndroid Build Coastguard Worker  Returns True if a > b
122*333d2b36SAndroid Build Coastguard Worker
123*333d2b36SAndroid Build Coastguard Worker  Args:
124*333d2b36SAndroid Build Coastguard Worker    a: value to compare
125*333d2b36SAndroid Build Coastguard Worker    b: value to compare
126*333d2b36SAndroid Build Coastguard Worker  Returns:
127*333d2b36SAndroid Build Coastguard Worker    True if a is a higher version than b
128*333d2b36SAndroid Build Coastguard Worker  """
129*333d2b36SAndroid Build Coastguard Worker
130*333d2b36SAndroid Build Coastguard Worker  a, a_is_int = as_int(a.upper())
131*333d2b36SAndroid Build Coastguard Worker  b, b_is_int = as_int(b.upper())
132*333d2b36SAndroid Build Coastguard Worker
133*333d2b36SAndroid Build Coastguard Worker  if a_is_int == b_is_int:
134*333d2b36SAndroid Build Coastguard Worker    # Both are codenames or both are versions, compare directly
135*333d2b36SAndroid Build Coastguard Worker    return a > b
136*333d2b36SAndroid Build Coastguard Worker  else:
137*333d2b36SAndroid Build Coastguard Worker    # One is a codename, the other is not.  Return true if
138*333d2b36SAndroid Build Coastguard Worker    # b is an integer version
139*333d2b36SAndroid Build Coastguard Worker    return b_is_int
140*333d2b36SAndroid Build Coastguard Worker
141*333d2b36SAndroid Build Coastguard Worker
142*333d2b36SAndroid Build Coastguard Workerdef get_indent(element, default_level):
143*333d2b36SAndroid Build Coastguard Worker  indent = ''
144*333d2b36SAndroid Build Coastguard Worker  if element is not None and element.nodeType == minidom.Node.TEXT_NODE:
145*333d2b36SAndroid Build Coastguard Worker    text = element.nodeValue
146*333d2b36SAndroid Build Coastguard Worker    indent = text[:len(text)-len(text.lstrip())]
147*333d2b36SAndroid Build Coastguard Worker  if not indent or indent == '\n':
148*333d2b36SAndroid Build Coastguard Worker    # 1 indent = 4 space
149*333d2b36SAndroid Build Coastguard Worker    indent = '\n' + (' ' * default_level * 4)
150*333d2b36SAndroid Build Coastguard Worker  return indent
151*333d2b36SAndroid Build Coastguard Worker
152*333d2b36SAndroid Build Coastguard Worker
153*333d2b36SAndroid Build Coastguard Workerdef write_xml(f, doc):
154*333d2b36SAndroid Build Coastguard Worker  f.write('<?xml version="1.0" encoding="utf-8"?>\n')
155*333d2b36SAndroid Build Coastguard Worker  for node in doc.childNodes:
156*333d2b36SAndroid Build Coastguard Worker    f.write(node.toxml() + '\n')
157