1# Copyright 2015 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5''' 6Utility functions for all things related to manipulating google play services 7related files. 8''' 9 10import argparse 11import filecmp 12import json 13import logging 14import os 15import re 16import sys 17 18sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir)) 19from devil.utils import cmd_helper 20 21 22_XML_VERSION_NUMBER_PATTERN = re.compile( 23 r'<integer name="google_play_services_version">(\d+)<\/integer>') 24 25 26class DefaultsRawHelpFormatter(argparse.ArgumentDefaultsHelpFormatter, 27 argparse.RawDescriptionHelpFormatter): 28 ''' 29 Combines the features of RawDescriptionHelpFormatter and 30 ArgumentDefaultsHelpFormatter, providing defaults for the arguments and raw 31 text for the description. 32 ''' 33 pass 34 35 36class ConfigParser(object): 37 '''Reads and writes the configuration files for play services related scripts 38 39 The configuration files are JSON files. Here is the data they are expected 40 to contain: 41 42 - version_number 43 Number. Mirrors @integer/google_play_services_version from the library. 44 Example: 815000 45 46 - sdk_version 47 Version of the Play Services SDK to retrieve, when preprocessing the 48 library from a maven/gradle repository. 49 Example: "8.1.0" 50 51 - clients 52 List of strings. Name of the clients (or play services modules) to 53 include when preprocessing the library. 54 Example: ["play-services-base", "play-services-cast"] 55 56 - version_xml_path 57 String. Path to the version.xml string describing the current version. 58 Should be relative to the library base directory 59 Example: "res/values/version.xml" 60 61 - locale_whitelist 62 List of strings. List of locales to keep from the resources. Can be 63 obtained by generating an android build and looking at the content of 64 `out/Debug/gen/chrome/java/res`; or looking at the android section in 65 `//chrome/app/generated_resources.grd` 66 Example: ["am", "ar", "bg", "ca", "cs"] 67 68 - resource_whitelist 69 List of strings. List of resource files to explicitely keep in the final 70 output. Use it to keep drawables for example, as we currently remove them 71 all. 72 Example: ["play-services-base/res/drawables/foobar.xml"] 73 ''' 74 _VERSION_NUMBER_KEY = 'version_number' 75 76 def __init__(self, path): 77 self.path = path 78 self._data = {} 79 80 with open(path, 'r') as stream: 81 self._data = json.load(stream) 82 83 @property 84 def version_number(self): 85 return self._data.get(self._VERSION_NUMBER_KEY) 86 87 @property 88 def sdk_version(self): 89 return self._data.get('sdk_version') 90 91 @property 92 def clients(self): 93 return self._data.get('clients') or [] 94 95 @property 96 def version_xml_path(self): 97 return self._data.get('version_xml_path') 98 99 @property 100 def locale_whitelist(self): 101 return self._data.get('locale_whitelist') or [] 102 103 @property 104 def resource_whitelist(self): 105 return self._data.get('resource_whitelist') or [] 106 107 def UpdateVersionNumber(self, new_version_number): 108 '''Updates the version number and saves it in the configuration file. ''' 109 110 with open(self.path, 'w') as stream: 111 self._data[self._VERSION_NUMBER_KEY] = new_version_number 112 stream.write(DumpTrimmedJson(self._data)) 113 114 115def DumpTrimmedJson(json_data): 116 ''' 117 Default formatting when dumping json to string has trailing spaces and lacks 118 a new line at the end. This function fixes that. 119 ''' 120 121 out = json.dumps(json_data, sort_keys=True, indent=2) 122 out = out.replace(' ' + os.linesep, os.linesep) 123 return out + os.linesep 124 125 126def FileEquals(expected_file, actual_file): 127 ''' 128 Returns whether the two files are equal. Returns False if any of the files 129 doesn't exist. 130 ''' 131 132 if not os.path.isfile(actual_file) or not os.path.isfile(expected_file): 133 return False 134 return filecmp.cmp(expected_file, actual_file) 135 136 137def IsRepoDirty(repo_root): 138 '''Returns True if there are no staged or modified files, False otherwise.''' 139 140 # diff-index returns 1 if there are staged changes or modified files, 141 # 0 otherwise 142 cmd = ['git', 'diff-index', '--quiet', 'HEAD'] 143 return cmd_helper.Call(cmd, cwd=repo_root) == 1 144 145 146def GetVersionNumberFromLibraryResources(version_xml): 147 ''' 148 Extracts a Google Play services version number from its version.xml file. 149 ''' 150 151 with open(version_xml, 'r') as version_file: 152 version_file_content = version_file.read() 153 154 match = _XML_VERSION_NUMBER_PATTERN.search(version_file_content) 155 if not match: 156 raise AttributeError('A value for google_play_services_version was not ' 157 'found in ' + version_xml) 158 return int(match.group(1)) 159 160 161def MakeLocalCommit(repo_root, files_to_commit, message): 162 '''Makes a local git commit.''' 163 164 logging.debug('Staging files (%s) for commit.', files_to_commit) 165 if cmd_helper.Call(['git', 'add'] + files_to_commit, cwd=repo_root) != 0: 166 raise Exception('The local commit failed.') 167 168 logging.debug('Committing.') 169 if cmd_helper.Call(['git', 'commit', '-m', message], cwd=repo_root) != 0: 170 raise Exception('The local commit failed.') 171