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