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