Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(41)

Side by Side Diff: build/android/play_services/update.py

Issue 1418833005: [gms updater] Fixes and tests to prepare activation (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: use SDK/library appropriately Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « build/android/PRESUBMIT.py ('k') | build/android/play_services/update_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2015 The Chromium Authors. All rights reserved. 2 # Copyright 2015 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 ''' 6 '''
7 Script to help uploading and downloading the Google Play services client 7 Script to help uploading and downloading the Google Play services library to
8 library to and from a Google Cloud storage. 8 and from a Google Cloud storage.
9 ''' 9 '''
10 10
11 import argparse 11 import argparse
12 import collections
13 import logging 12 import logging
14 import os 13 import os
15 import re 14 import re
16 import shutil 15 import shutil
17 import sys 16 import sys
18 import tempfile 17 import tempfile
19 import zipfile 18 import zipfile
20 19
21 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir)) 20 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
22 from devil.utils import cmd_helper 21 from devil.utils import cmd_helper
23 from play_services import utils 22 from play_services import utils
24 from pylib import constants 23 from pylib import constants
25 from pylib.utils import logging_utils 24 from pylib.utils import logging_utils
26 25
27 sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT, 'build')) 26 sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT, 'build'))
28 import find_depot_tools # pylint: disable=import-error,unused-import 27 import find_depot_tools # pylint: disable=import-error,unused-import
29 import breakpad 28 import breakpad
30 import download_from_google_storage 29 import download_from_google_storage
31 import upload_to_google_storage 30 import upload_to_google_storage
32 31
33 32
34 # Directory where the SHA1 files for the zip and the license are stored 33 # Directory where the SHA1 files for the zip and the license are stored
35 # It should be managed by git to provided information about new versions. 34 # It should be managed by git to provided information about new versions.
36 SHA1_DIRECTORY = os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'android', 35 SHA1_DIRECTORY = os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'android',
37 'play_services') 36 'play_services')
38 37
39 # Default bucket used for storing the files. 38 # Default bucket used for storing the files.
40 GMS_CLOUD_STORAGE = 'chrome-sdk-extras' 39 GMS_CLOUD_STORAGE = 'chromium-android-tools/play-services'
41 40
42 # Path to the default configuration file. It exposes the currently installed 41 # Path to the default configuration file. It exposes the currently installed
43 # version of the library in a human readable way. 42 # version of the library in a human readable way.
44 CONFIG_DEFAULT_PATH = os.path.join(constants.DIR_SOURCE_ROOT, 'build', 43 CONFIG_DEFAULT_PATH = os.path.join(constants.DIR_SOURCE_ROOT, 'build',
45 'android', 'play_services', 'config.json') 44 'android', 'play_services', 'config.json')
46 45
47 LICENSE_FILE_NAME = 'LICENSE' 46 LICENSE_FILE_NAME = 'LICENSE'
48 LIBRARY_FILE_NAME = 'google_play_services_library.zip' 47 ZIP_FILE_NAME = 'google_play_services_library.zip'
49 GMS_PACKAGE_ID = 'extra-google-google_play_services' # used by sdk manager 48 GMS_PACKAGE_ID = 'extra-google-google_play_services' # used by sdk manager
50 49
51 LICENSE_PATTERN = re.compile(r'^Pkg\.License=(?P<text>.*)$', re.MULTILINE) 50 LICENSE_PATTERN = re.compile(r'^Pkg\.License=(?P<text>.*)$', re.MULTILINE)
52 51
53 52
54 def Main(): 53 def main(raw_args):
55 parser = argparse.ArgumentParser( 54 parser = argparse.ArgumentParser(
56 description=__doc__ + 'Please see the subcommand help for more details.', 55 description=__doc__ + 'Please see the subcommand help for more details.',
57 formatter_class=utils.DefaultsRawHelpFormatter) 56 formatter_class=utils.DefaultsRawHelpFormatter)
58 subparsers = parser.add_subparsers(title='commands') 57 subparsers = parser.add_subparsers(title='commands')
59 58
60 # Download arguments 59 # Download arguments
61 parser_download = subparsers.add_parser( 60 parser_download = subparsers.add_parser(
62 'download', 61 'download',
63 help='download the library from the cloud storage', 62 help='download the library from the cloud storage',
64 description=Download.__doc__, 63 description=Download.__doc__,
65 formatter_class=utils.DefaultsRawHelpFormatter) 64 formatter_class=utils.DefaultsRawHelpFormatter)
66 parser_download.add_argument('-f', '--force',
67 action='store_true',
68 help=('run even if the local version is '
69 'already up to date'))
70 parser_download.set_defaults(func=Download) 65 parser_download.set_defaults(func=Download)
71 AddCommonArguments(parser_download) 66 AddBasicArguments(parser_download)
67 AddBucketArguments(parser_download)
72 68
73 # SDK Update arguments 69 # SDK Update arguments
74 parser_sdk = subparsers.add_parser( 70 parser_sdk = subparsers.add_parser(
75 'sdk', 71 'sdk',
76 help='update the local sdk using the Android SDK Manager', 72 help='get the latest Google Play services SDK using Android SDK Manager',
77 description=UpdateSdk.__doc__, 73 description=UpdateSdk.__doc__,
78 formatter_class=utils.DefaultsRawHelpFormatter) 74 formatter_class=utils.DefaultsRawHelpFormatter)
79 parser_sdk.add_argument('--sdk-root',
80 help=('base path to the Android SDK tools to use to '
81 'update the library'),
82 default=constants.ANDROID_SDK_ROOT)
83 parser_sdk.add_argument('-v', '--verbose',
84 action='store_true',
85 help='print debug information')
86 parser_sdk.set_defaults(func=UpdateSdk) 75 parser_sdk.set_defaults(func=UpdateSdk)
76 AddBasicArguments(parser_sdk)
87 77
88 # Upload arguments 78 # Upload arguments
89 parser_upload = subparsers.add_parser( 79 parser_upload = subparsers.add_parser(
90 'upload', 80 'upload',
91 help='upload the library to the cloud storage', 81 help='upload the library to the cloud storage',
92 description=Upload.__doc__, 82 description=Upload.__doc__,
93 formatter_class=utils.DefaultsRawHelpFormatter) 83 formatter_class=utils.DefaultsRawHelpFormatter)
94 parser_upload.add_argument('-f', '--force', 84
95 action='store_true',
96 help=('run even if the checked in version is '
97 'already up to date'))
98 parser_upload.add_argument('--sdk-root',
99 help=('base path to the Android SDK tools to use '
100 'to update the library'),
101 default=constants.ANDROID_SDK_ROOT)
102 parser_upload.add_argument('--skip-git', 85 parser_upload.add_argument('--skip-git',
103 action='store_true', 86 action='store_true',
104 help="don't commit the changes at the end") 87 help="don't commit the changes at the end")
105 parser_upload.set_defaults(func=Upload) 88 parser_upload.set_defaults(func=Upload)
106 AddCommonArguments(parser_upload) 89 AddBasicArguments(parser_upload)
90 AddBucketArguments(parser_upload)
107 91
108 args = parser.parse_args() 92 args = parser.parse_args(raw_args)
109 if args.verbose: 93 if args.verbose:
110 logging.basicConfig(level=logging.DEBUG) 94 logging.basicConfig(level=logging.DEBUG)
111 logging_utils.ColorStreamHandler.MakeDefault() 95 logging_utils.ColorStreamHandler.MakeDefault(not _IsBotEnvironment())
112 return args.func(args) 96 return args.func(args)
113 97
114 98
115 def AddCommonArguments(parser): 99 def AddBasicArguments(parser):
116 ''' 100 '''
117 Defines the common arguments on subparser rather than the main one. This 101 Defines the common arguments on subparser rather than the main one. This
118 allows to put arguments after the command: `foo.py upload --debug --force` 102 allows to put arguments after the command: `foo.py upload --debug --force`
119 instead of `foo.py --debug upload --force` 103 instead of `foo.py --debug upload --force`
120 ''' 104 '''
121 105
122 parser.add_argument('--bucket', 106 parser.add_argument('--sdk-root',
123 help='name of the bucket where the files are stored', 107 help='base path to the Android SDK tools root',
124 default=GMS_CLOUD_STORAGE) 108 default=constants.ANDROID_SDK_ROOT)
125 parser.add_argument('--config', 109
126 help='JSON Configuration file',
127 default=CONFIG_DEFAULT_PATH)
128 parser.add_argument('--dry-run',
129 action='store_true',
130 help=('run the script in dry run mode. Files will be '
131 'copied to a local directory instead of the cloud '
132 'storage. The bucket name will be as path to that '
133 'directory relative to the repository root.'))
134 parser.add_argument('-v', '--verbose', 110 parser.add_argument('-v', '--verbose',
135 action='store_true', 111 action='store_true',
136 help='print debug information') 112 help='print debug information')
137 113
138 114
115 def AddBucketArguments(parser):
116 parser.add_argument('--bucket',
117 help='name of the bucket where the files are stored',
118 default=GMS_CLOUD_STORAGE)
119
120 parser.add_argument('--config',
121 help='JSON Configuration file',
122 default=CONFIG_DEFAULT_PATH)
123
124 parser.add_argument('--dry-run',
125 action='store_true',
126 help=('run the script in dry run mode. Files will be '
127 'copied to a local directory instead of the '
128 'cloud storage. The bucket name will be as path '
129 'to that directory relative to the repository '
130 'root.'))
131
132 parser.add_argument('-f', '--force',
133 action='store_true',
134 help='run even if the library is already up to date')
135
136
139 def Download(args): 137 def Download(args):
140 ''' 138 '''
141 Downloads the Google Play services client library from a Google Cloud Storage 139 Downloads the Google Play services library from a Google Cloud Storage bucket
142 bucket and installs it to 140 and installs it to
143 //third_party/android_tools/sdk/extras/google/google_play_services. 141 //third_party/android_tools/sdk/extras/google/google_play_services.
144 142
145 A license check will be made, and the user might have to accept the license 143 A license check will be made, and the user might have to accept the license
146 if that has not been done before. 144 if that has not been done before.
147 ''' 145 '''
148 146
149 paths = _InitPaths(constants.ANDROID_SDK_ROOT) 147 if not os.path.isdir(args.sdk_root):
148 logging.debug('Did not find the Android SDK root directory at "%s".',
149 args.sdk_root)
150 if not args.force:
151 logging.info('Skipping, not on an android checkout.')
152 return 0
150 153
151 new_lib_zip_sha1 = os.path.join(SHA1_DIRECTORY, LIBRARY_FILE_NAME + '.sha1') 154 paths = PlayServicesPaths(args.sdk_root)
152 old_lib_zip_sha1 = os.path.join(paths.package, LIBRARY_FILE_NAME + '.sha1')
153 155
154 logging.debug('Comparing library hashes: %s and %s', new_lib_zip_sha1, 156 if os.path.isdir(paths.package) and not os.access(paths.package, os.W_OK):
155 old_lib_zip_sha1) 157 logging.error('Failed updating the Google Play Services library. '
156 if utils.FileEquals(new_lib_zip_sha1, old_lib_zip_sha1) and not args.force: 158 'The location is not writable. Please remove the '
157 logging.debug('The Google Play services library is up to date.') 159 'directory (%s) and try again.', paths.package)
160 return -2
161
162 new_lib_zip_sha1 = os.path.join(SHA1_DIRECTORY, ZIP_FILE_NAME + '.sha1')
163
164 logging.debug('Comparing zip hashes: %s and %s', new_lib_zip_sha1,
165 paths.lib_zip_sha1)
166 if utils.FileEquals(new_lib_zip_sha1, paths.lib_zip_sha1) and not args.force:
167 logging.info('Skipping, the Google Play services library is up to date.')
158 return 0 168 return 0
159 169
160 config = utils.ConfigParser(args.config) 170 config = utils.ConfigParser(args.config)
161 bucket_path = _VerifyBucketPathFormat(args.bucket, 171 bucket_path = _VerifyBucketPathFormat(args.bucket,
162 config.version_number, 172 config.version_number,
163 args.dry_run) 173 args.dry_run)
164 174
165 tmp_root = tempfile.mkdtemp() 175 tmp_root = tempfile.mkdtemp()
166 try: 176 try:
167 if not os.environ.get('CHROME_HEADLESS'): 177 # setup the destination directory
168 if not os.path.isdir(paths.package): 178 if not os.path.isdir(paths.package):
169 os.makedirs(paths.package) 179 os.makedirs(paths.package)
170 180
171 # download license file from bucket/{version_number}/license.sha1 181 # download license file from bucket/{version_number}/license.sha1
172 new_license = os.path.join(tmp_root, LICENSE_FILE_NAME) 182 new_license = os.path.join(tmp_root, LICENSE_FILE_NAME)
173 old_license = os.path.join(paths.package, LICENSE_FILE_NAME)
174 183
175 license_sha1 = os.path.join(SHA1_DIRECTORY, LICENSE_FILE_NAME + '.sha1') 184 license_sha1 = os.path.join(SHA1_DIRECTORY, LICENSE_FILE_NAME + '.sha1')
176 _DownloadFromBucket(bucket_path, license_sha1, new_license, 185 _DownloadFromBucket(bucket_path, license_sha1, new_license,
177 args.verbose, args.dry_run) 186 args.verbose, args.dry_run)
178 if not _CheckLicenseAgreement(new_license, old_license): 187
188 if (not _IsBotEnvironment() and
189 not _CheckLicenseAgreement(new_license, paths.license,
190 config.version_number)):
179 logging.warning('Your version of the Google Play services library is ' 191 logging.warning('Your version of the Google Play services library is '
180 'not up to date. You might run into issues building ' 192 'not up to date. You might run into issues building '
181 'or running the app. Please run `%s download` to ' 193 'or running the app. Please run `%s download` to '
182 'retry downloading it.', __file__) 194 'retry downloading it.', __file__)
183 return 0 195 return 0
184 196
185 new_lib_zip = os.path.join(tmp_root, LIBRARY_FILE_NAME) 197 new_lib_zip = os.path.join(tmp_root, ZIP_FILE_NAME)
186 _DownloadFromBucket(bucket_path, new_lib_zip_sha1, new_lib_zip, 198 _DownloadFromBucket(bucket_path, new_lib_zip_sha1, new_lib_zip,
187 args.verbose, args.dry_run) 199 args.verbose, args.dry_run)
188 200
189 # We remove only the library itself. Users having a SDK manager installed 201 try:
190 # library before will keep the documentation and samples from it. 202 # We remove the current version of the Google Play services SDK.
191 shutil.rmtree(paths.lib, ignore_errors=True) 203 if os.path.exists(paths.package):
192 os.makedirs(paths.lib) 204 shutil.rmtree(paths.package)
205 os.makedirs(paths.package)
193 206
194 logging.debug('Extracting the library to %s', paths.lib) 207 logging.debug('Extracting the library to %s', paths.lib)
195 with zipfile.ZipFile(new_lib_zip, "r") as new_lib_zip_file: 208 with zipfile.ZipFile(new_lib_zip, "r") as new_lib_zip_file:
196 new_lib_zip_file.extractall(paths.lib) 209 new_lib_zip_file.extractall(paths.lib)
197 210
198 logging.debug('Copying %s to %s', new_license, old_license) 211 logging.debug('Copying %s to %s', new_license, paths.license)
199 shutil.copy(new_license, old_license) 212 shutil.copy(new_license, paths.license)
200 213
201 logging.debug('Copying %s to %s', new_lib_zip_sha1, old_lib_zip_sha1) 214 logging.debug('Copying %s to %s', new_lib_zip_sha1, paths.lib_zip_sha1)
202 shutil.copy(new_lib_zip_sha1, old_lib_zip_sha1) 215 shutil.copy(new_lib_zip_sha1, paths.lib_zip_sha1)
203 216
217 logging.info('Update complete.')
218
219 except Exception as e: # pylint: disable=broad-except
220 logging.error('Failed updating the Google Play Services library. '
221 'An error occurred while installing the new version in '
222 'the SDK directory: %s ', e)
223 return -3
204 finally: 224 finally:
205 shutil.rmtree(tmp_root) 225 shutil.rmtree(tmp_root)
206 226
207 return 0 227 return 0
208 228
209 229
210 def UpdateSdk(args): 230 def UpdateSdk(args):
211 ''' 231 '''
212 Uses the Android SDK Manager to update or download the local Google Play 232 Uses the Android SDK Manager to download the latest Google Play services SDK
213 services library. Its usual installation path is 233 locally. Its usual installation path is
214 //third_party/android_tools/sdk/extras/google/google_play_services 234 //third_party/android_tools/sdk/extras/google/google_play_services
215 ''' 235 '''
216 236
217 # This should function should not run on bots and could fail for many user 237 # This should function should not run on bots and could fail for many user
218 # and setup related reasons. Also, exceptions here are not caught, so we 238 # and setup related reasons. Also, exceptions here are not caught, so we
219 # disable breakpad to avoid spamming the logs. 239 # disable breakpad to avoid spamming the logs.
220 breakpad.IS_ENABLED = False 240 breakpad.IS_ENABLED = False
221 241
222 sdk_manager = os.path.join(args.sdk_root, 'tools', 'android') 242 sdk_manager = os.path.join(args.sdk_root, 'tools', 'android')
223 cmd = [sdk_manager, 'update', 'sdk', '--no-ui', '--filter', GMS_PACKAGE_ID] 243 cmd = [sdk_manager, 'update', 'sdk', '--no-ui', '--filter', GMS_PACKAGE_ID]
224 cmd_helper.Call(cmd) 244 cmd_helper.Call(cmd)
225 # If no update is needed, it still returns successfully so we just do nothing 245 # If no update is needed, it still returns successfully so we just do nothing
226 246
227 return 0 247 return 0
228 248
229 249
230 def Upload(args): 250 def Upload(args):
231 ''' 251 '''
232 Uploads the local Google Play services client library to a Google Cloud 252 Uploads the library from the local Google Play services SDK to a Google Cloud
233 storage bucket. 253 storage bucket.
234 254
235 By default, a local commit will be made at the end of the operation. 255 By default, a local commit will be made at the end of the operation.
236 ''' 256 '''
237 257
238 # This should function should not run on bots and could fail for many user 258 # This should function should not run on bots and could fail for many user
239 # and setup related reasons. Also, exceptions here are not caught, so we 259 # and setup related reasons. Also, exceptions here are not caught, so we
240 # disable breakpad to avoid spamming the logs. 260 # disable breakpad to avoid spamming the logs.
241 breakpad.IS_ENABLED = False 261 breakpad.IS_ENABLED = False
242 262
243 paths = _InitPaths(args.sdk_root) 263 paths = PlayServicesPaths(args.sdk_root)
244 264
245 if not args.skip_git and utils.IsRepoDirty(constants.DIR_SOURCE_ROOT): 265 if not args.skip_git and utils.IsRepoDirty(constants.DIR_SOURCE_ROOT):
246 logging.error('The repo is dirty. Please commit or stash your changes.') 266 logging.error('The repo is dirty. Please commit or stash your changes.')
247 return -1 267 return -1
248 268
249 config = utils.ConfigParser(args.config) 269 config = utils.ConfigParser(args.config)
250 270
251 version_xml = os.path.join(paths.lib, 'res', 'values', 'version.xml') 271 new_version_number = utils.GetVersionNumberFromLibraryResources(
252 new_version_number = utils.GetVersionNumberFromLibraryResources(version_xml) 272 paths.version_xml)
253 logging.debug('comparing versions: new=%d, old=%s', 273 logging.debug('comparing versions: new=%d, old=%s',
254 new_version_number, config.version_number) 274 new_version_number, config.version_number)
255 if new_version_number <= config.version_number and not args.force: 275 if new_version_number <= config.version_number and not args.force:
256 logging.info('The checked in version of the library is already the latest ' 276 logging.info('The checked in version of the library is already the latest '
257 'one. No update needed. Please rerun with --force to skip ' 277 'one. No update is needed. Please rerun with --force to skip '
258 'this check.') 278 'this check.')
259 return 0 279 return 0
260 280
261 tmp_root = tempfile.mkdtemp() 281 tmp_root = tempfile.mkdtemp()
262 try: 282 try:
263 new_lib_zip = os.path.join(tmp_root, LIBRARY_FILE_NAME) 283 new_lib_zip = os.path.join(tmp_root, ZIP_FILE_NAME)
264 new_license = os.path.join(tmp_root, LICENSE_FILE_NAME) 284 new_license = os.path.join(tmp_root, LICENSE_FILE_NAME)
265 285
266 # need to strip '.zip' from the file name here 286 # need to strip '.zip' from the file name here
267 shutil.make_archive(new_lib_zip[:-4], 'zip', paths.lib) 287 shutil.make_archive(new_lib_zip[:-4], 'zip', paths.lib)
268 _ExtractLicenseFile(new_license, paths.package) 288 _ExtractLicenseFile(new_license, paths.source_prop)
269 289
270 bucket_path = _VerifyBucketPathFormat(args.bucket, new_version_number, 290 bucket_path = _VerifyBucketPathFormat(args.bucket, new_version_number,
271 args.dry_run) 291 args.dry_run)
272 files_to_upload = [new_lib_zip, new_license] 292 files_to_upload = [new_lib_zip, new_license]
273 logging.debug('Uploading %s to %s', files_to_upload, bucket_path) 293 logging.debug('Uploading %s to %s', files_to_upload, bucket_path)
274 _UploadToBucket(bucket_path, files_to_upload, args.dry_run) 294 _UploadToBucket(bucket_path, files_to_upload, args.dry_run)
275 295
276 new_lib_zip_sha1 = os.path.join(SHA1_DIRECTORY, 296 new_lib_zip_sha1 = os.path.join(SHA1_DIRECTORY,
277 LIBRARY_FILE_NAME + '.sha1') 297 ZIP_FILE_NAME + '.sha1')
278 new_license_sha1 = os.path.join(SHA1_DIRECTORY, 298 new_license_sha1 = os.path.join(SHA1_DIRECTORY,
279 LICENSE_FILE_NAME + '.sha1') 299 LICENSE_FILE_NAME + '.sha1')
280 shutil.copy(new_lib_zip + '.sha1', new_lib_zip_sha1) 300 shutil.copy(new_lib_zip + '.sha1', new_lib_zip_sha1)
281 shutil.copy(new_license + '.sha1', new_license_sha1) 301 shutil.copy(new_license + '.sha1', new_license_sha1)
282 finally: 302 finally:
283 shutil.rmtree(tmp_root) 303 shutil.rmtree(tmp_root)
284 304
285 config.UpdateVersionNumber(new_version_number) 305 config.UpdateVersionNumber(new_version_number)
286 306
287 if not args.skip_git: 307 if not args.skip_git:
288 commit_message = ('Update the Google Play services dependency to %s\n' 308 commit_message = ('Update the Google Play services dependency to %s\n'
289 '\n') % new_version_number 309 '\n') % new_version_number
290 utils.MakeLocalCommit(constants.DIR_SOURCE_ROOT, 310 utils.MakeLocalCommit(constants.DIR_SOURCE_ROOT,
291 [new_lib_zip_sha1, new_license_sha1, config.path], 311 [new_lib_zip_sha1, new_license_sha1, config.path],
292 commit_message) 312 commit_message)
293 313
294 return 0 314 return 0
295 315
296 316
297 def _InitPaths(sdk_root):
298 '''
299 Initializes the different paths to be used in the update process.
300 '''
301
302 PlayServicesPaths = collections.namedtuple('PlayServicesPaths', [
303 # Android SDK root path
304 'sdk_root',
305
306 # Path to the Google Play services package in the SDK manager sense,
307 # where it installs the source.properties file
308 'package',
309
310 # Path to the Google Play services library itself (jar and res)
311 'lib',
312 ])
313
314 sdk_play_services_package_dir = os.path.join('extras', 'google',
315 'google_play_services')
316 sdk_play_services_lib_dir = os.path.join(sdk_play_services_package_dir,
317 'libproject',
318 'google-play-services_lib')
319
320 return PlayServicesPaths(
321 sdk_root=sdk_root,
322 package=os.path.join(sdk_root, sdk_play_services_package_dir),
323 lib=os.path.join(sdk_root, sdk_play_services_lib_dir))
324
325
326 def _DownloadFromBucket(bucket_path, sha1_file, destination, verbose, 317 def _DownloadFromBucket(bucket_path, sha1_file, destination, verbose,
327 is_dry_run): 318 is_dry_run):
328 '''Downloads the file designated by the provided sha1 from a cloud bucket.''' 319 '''Downloads the file designated by the provided sha1 from a cloud bucket.'''
329 320
330 download_from_google_storage.download_from_google_storage( 321 download_from_google_storage.download_from_google_storage(
331 input_filename=sha1_file, 322 input_filename=sha1_file,
332 base_url=bucket_path, 323 base_url=bucket_path,
333 gsutil=_InitGsutil(is_dry_run), 324 gsutil=_InitGsutil(is_dry_run),
334 num_threads=1, 325 num_threads=1,
335 directory=None, 326 directory=None,
(...skipping 24 matching lines...) Expand all
360 def _InitGsutil(is_dry_run): 351 def _InitGsutil(is_dry_run):
361 '''Initialize the Gsutil object as regular or dummy version for dry runs. ''' 352 '''Initialize the Gsutil object as regular or dummy version for dry runs. '''
362 353
363 if is_dry_run: 354 if is_dry_run:
364 return DummyGsutil() 355 return DummyGsutil()
365 else: 356 else:
366 return download_from_google_storage.Gsutil( 357 return download_from_google_storage.Gsutil(
367 download_from_google_storage.GSUTIL_DEFAULT_PATH) 358 download_from_google_storage.GSUTIL_DEFAULT_PATH)
368 359
369 360
370 def _ExtractLicenseFile(license_path, play_services_package_dir): 361 def _ExtractLicenseFile(license_path, prop_file_path):
371 prop_file_path = os.path.join(play_services_package_dir, 'source.properties')
372 with open(prop_file_path, 'r') as prop_file: 362 with open(prop_file_path, 'r') as prop_file:
373 prop_file_content = prop_file.read() 363 prop_file_content = prop_file.read()
374 364
375 match = LICENSE_PATTERN.search(prop_file_content) 365 match = LICENSE_PATTERN.search(prop_file_content)
376 if not match: 366 if not match:
377 raise AttributeError('The license was not found in ' + 367 raise AttributeError('The license was not found in ' +
378 os.path.abspath(prop_file_path)) 368 os.path.abspath(prop_file_path))
379 369
380 with open(license_path, 'w') as license_file: 370 with open(license_path, 'w') as license_file:
381 license_file.write(match.group('text')) 371 license_file.write(match.group('text'))
382 372
383 373
384 def _CheckLicenseAgreement(expected_license_path, actual_license_path): 374 def _CheckLicenseAgreement(expected_license_path, actual_license_path,
375 version_number):
385 ''' 376 '''
386 Checks that the new license is the one already accepted by the user. If it 377 Checks that the new license is the one already accepted by the user. If it
387 isn't, it prompts the user to accept it. Returns whether the expected license 378 isn't, it prompts the user to accept it. Returns whether the expected license
388 has been accepted. 379 has been accepted.
389 ''' 380 '''
390 381
391 if utils.FileEquals(expected_license_path, actual_license_path): 382 if utils.FileEquals(expected_license_path, actual_license_path):
392 return True 383 return True
393 384
394 with open(expected_license_path) as license_file: 385 with open(expected_license_path) as license_file:
386 # Uses plain print rather than logging to make sure this is not formatted
387 # by the logger.
388 print ('Updating the Google Play services SDK to '
389 'version %d.' % version_number)
390
395 # The output is buffered when running as part of gclient hooks. We split 391 # The output is buffered when running as part of gclient hooks. We split
396 # the text here and flush is explicitly to avoid having part of it dropped 392 # the text here and flush is explicitly to avoid having part of it dropped
397 # out. 393 # out.
398 # Note: text contains *escaped* new lines, so we split by '\\n', not '\n'. 394 # Note: text contains *escaped* new lines, so we split by '\\n', not '\n'.
399 for license_part in license_file.read().split('\\n'): 395 for license_part in license_file.read().split('\\n'):
400 # Uses plain print rather than logging to make sure this is not formatted
401 # by the logger.
402 print license_part 396 print license_part
403 sys.stdout.flush() 397 sys.stdout.flush()
404 398
405 # Need to put the prompt on a separate line otherwise the gclient hook buffer 399 # Need to put the prompt on a separate line otherwise the gclient hook buffer
406 # only prints it after we received an input. 400 # only prints it after we received an input.
407 print 'Do you accept the license? [y/n]: ' 401 print 'Do you accept the license? [y/n]: '
408 sys.stdout.flush() 402 sys.stdout.flush()
409 return raw_input('> ') in ('Y', 'y') 403 return raw_input('> ') in ('Y', 'y')
410 404
411 405
406 def _IsBotEnvironment():
407 return bool(os.environ.get('CHROME_HEADLESS'))
408
409
412 def _VerifyBucketPathFormat(bucket_name, version_number, is_dry_run): 410 def _VerifyBucketPathFormat(bucket_name, version_number, is_dry_run):
413 ''' 411 '''
414 Formats and checks the download/upload path depending on whether we are 412 Formats and checks the download/upload path depending on whether we are
415 running in dry run mode or not. Returns a supposedly safe path to use with 413 running in dry run mode or not. Returns a supposedly safe path to use with
416 Gsutil. 414 Gsutil.
417 ''' 415 '''
418 416
419 if is_dry_run: 417 if is_dry_run:
420 bucket_path = os.path.abspath(os.path.join(bucket_name, 418 bucket_path = os.path.abspath(os.path.join(bucket_name,
421 str(version_number))) 419 str(version_number)))
422 if not os.path.isdir(bucket_path): 420 if not os.path.isdir(bucket_path):
423 os.makedirs(bucket_path) 421 os.makedirs(bucket_path)
424 else: 422 else:
425 if bucket_name.startswith('gs://'): 423 if bucket_name.startswith('gs://'):
426 # We enforce the syntax without gs:// for consistency with the standalone 424 # We enforce the syntax without gs:// for consistency with the standalone
427 # download/upload scripts and to make dry run transition easier. 425 # download/upload scripts and to make dry run transition easier.
428 raise AttributeError('Please provide the bucket name without the gs:// ' 426 raise AttributeError('Please provide the bucket name without the gs:// '
429 'prefix (e.g. %s)' % GMS_CLOUD_STORAGE) 427 'prefix (e.g. %s)' % GMS_CLOUD_STORAGE)
430 bucket_path = 'gs://%s/%d' % (bucket_name, version_number) 428 bucket_path = 'gs://%s/%d' % (bucket_name, version_number)
431 429
432 return bucket_path 430 return bucket_path
433 431
434 432
433 class PlayServicesPaths(object):
434 '''
435 Describes the different paths to be used in the update process.
436
437 Filesystem hierarchy | Exposed property / notes
438 ---------------------------------------------------|-------------------------
439 [sdk_root] | sdk_root / (1)
440 +- extras |
441 +- google |
442 +- google_play_services | package / (2)
443 +- source.properties | source_prop / (3)
444 +- LICENSE | license / (4)
445 +- google_play_services_library.zip.sha1 | lib_zip_sha1 / (5)
446 +- libproject |
447 +- google-play-services_lib | lib / (6)
448 +- res |
449 +- values |
450 +- version.xml | version_xml (7)
451
452 Notes:
453
454 1. sdk_root: Path provided as a parameter to the script (--sdk_root)
455 2. package: This directory contains the Google Play services SDK itself.
456 When downloaded via the Android SDK manager, it will contain,
457 documentation, samples and other files in addition to the library. When
458 the update script downloads the library from our cloud storage, it is
459 cleared.
460 3. source_prop: File created by the Android SDK manager that contains
461 the package information, such as the version info and the license.
462 4. license: File created by the update script. Contains the license accepted
463 by the user.
464 5. lib_zip_sha1: sha1 of the library zip that has been installed by the
465 update script. It is compared with the one required by the config file to
466 check if an update is necessary.
467 6. lib: Contains the library itself: jar and resources. This is what is
468 downloaded from the cloud storage.
469 7. version_xml: File that contains the exact Google Play services library
470 version, the one that we track. The version looks like 811500, is used in
471 the code and the on-device APK, as opposed to the SDK package version
472 which looks like 27.0.0 and is used only by the Android SDK manager.
473
474 '''
475
476 def __init__(self, sdk_root):
477 relative_package = os.path.join('extras', 'google', 'google_play_services')
478 relative_lib = os.path.join(relative_package, 'libproject',
479 'google-play-services_lib')
480 self.sdk_root = sdk_root
481
482 self.package = os.path.join(sdk_root, relative_package)
483 self.lib_zip_sha1 = os.path.join(self.package, ZIP_FILE_NAME + '.sha1')
484 self.license = os.path.join(self.package, LICENSE_FILE_NAME)
485 self.source_prop = os.path.join(self.package, 'source.properties')
486
487 self.lib = os.path.join(sdk_root, relative_lib)
488 self.version_xml = os.path.join(self.lib, 'res', 'values', 'version.xml')
489
490
435 class DummyGsutil(download_from_google_storage.Gsutil): 491 class DummyGsutil(download_from_google_storage.Gsutil):
436 ''' 492 '''
437 Class that replaces Gsutil to use a local directory instead of an online 493 Class that replaces Gsutil to use a local directory instead of an online
438 bucket. It relies on the fact that Gsutil commands are very similar to shell 494 bucket. It relies on the fact that Gsutil commands are very similar to shell
439 ones, so for the ones used here (ls, cp), it works to just use them with a 495 ones, so for the ones used here (ls, cp), it works to just use them with a
440 local directory. 496 local directory.
441 ''' 497 '''
442 498
443 def __init__(self): 499 def __init__(self):
444 super(DummyGsutil, self).__init__( 500 super(DummyGsutil, self).__init__(
445 download_from_google_storage.GSUTIL_DEFAULT_PATH) 501 download_from_google_storage.GSUTIL_DEFAULT_PATH)
446 502
447 def call(self, *args): 503 def call(self, *args):
448 logging.debug('Calling command "%s"', str(args)) 504 logging.debug('Calling command "%s"', str(args))
449 return cmd_helper.GetCmdStatusOutputAndError(args) 505 return cmd_helper.GetCmdStatusOutputAndError(args)
450 506
451 def check_call(self, *args): 507 def check_call(self, *args):
452 logging.debug('Calling command "%s"', str(args)) 508 logging.debug('Calling command "%s"', str(args))
453 return cmd_helper.GetCmdStatusOutputAndError(args) 509 return cmd_helper.GetCmdStatusOutputAndError(args)
454 510
455 511
456 if __name__ == '__main__': 512 if __name__ == '__main__':
457 sys.exit(Main()) 513 sys.exit(main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « build/android/PRESUBMIT.py ('k') | build/android/play_services/update_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698