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

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: More doc, clean up naming 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 SDK to and
jbudorick 2015/11/05 16:03:02 nit: Why the doc change from "library" to "SDK"? L
dgn 2015/11/05 16:15:05 The developer website says "Google Play services S
dgn 2015/11/05 16:30:21 Done.
8 library to and from a Google Cloud storage. 8 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 SDK 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 SDK 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='update the SDK using the 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 SDK 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 SDK 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 SDK from a Google Cloud Storage bucket and
142 bucket and installs it to 140 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 SDK. '
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 SDK 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
179 logging.warning('Your version of the Google Play services library is ' 188 if (not _IsBotEnvironment() and
189 not _CheckLicenseAgreement(new_license, paths.license,
190 config.version_number)):
191 logging.warning('Your version of the Google Play services SDK 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. Users
191 shutil.rmtree(paths.lib, ignore_errors=True) 203 # having installed it with the Android SDK manager before will keep the
192 os.makedirs(paths.lib) 204 # documentation and samples from it.
205 if os.path.exists(paths.lib):
206 shutil.rmtree(paths.lib)
207 os.makedirs(paths.lib)
193 208
194 logging.debug('Extracting the library to %s', paths.lib) 209 # We also remove source.properties so that the SDK manager doesn't fail
195 with zipfile.ZipFile(new_lib_zip, "r") as new_lib_zip_file: 210 # recognizing that it didn't install the local version.
196 new_lib_zip_file.extractall(paths.lib) 211 if os.path.isfile(paths.source_prop):
212 os.remove(paths.source_prop)
197 213
198 logging.debug('Copying %s to %s', new_license, old_license) 214 logging.debug('Extracting the SDK to %s', paths.lib)
199 shutil.copy(new_license, old_license) 215 with zipfile.ZipFile(new_lib_zip, "r") as new_lib_zip_file:
216 new_lib_zip_file.extractall(paths.lib)
200 217
201 logging.debug('Copying %s to %s', new_lib_zip_sha1, old_lib_zip_sha1) 218 logging.debug('Copying %s to %s', new_license, paths.license)
202 shutil.copy(new_lib_zip_sha1, old_lib_zip_sha1) 219 shutil.copy(new_license, paths.license)
203 220
221 logging.debug('Copying %s to %s', new_lib_zip_sha1, paths.lib_zip_sha1)
222 shutil.copy(new_lib_zip_sha1, paths.lib_zip_sha1)
223
224 logging.info('Update complete.')
225
226 except Exception as e: # pylint: disable=broad-except
227 logging.error('Failed updating the Google Play Services SDK. '
228 'An error occurred while installing the new version in '
229 'the SDK directory: %s ', e)
230 return -3
204 finally: 231 finally:
205 shutil.rmtree(tmp_root) 232 shutil.rmtree(tmp_root)
206 233
207 return 0 234 return 0
208 235
209 236
210 def UpdateSdk(args): 237 def UpdateSdk(args):
211 ''' 238 '''
212 Uses the Android SDK Manager to update or download the local Google Play 239 Uses the Android SDK Manager to update or download the local Google Play
213 services library. Its usual installation path is 240 services SDK. Its usual installation path is
214 //third_party/android_tools/sdk/extras/google/google_play_services 241 //third_party/android_tools/sdk/extras/google/google_play_services
215 ''' 242 '''
216 243
217 # This should function should not run on bots and could fail for many user 244 # 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 245 # and setup related reasons. Also, exceptions here are not caught, so we
219 # disable breakpad to avoid spamming the logs. 246 # disable breakpad to avoid spamming the logs.
220 breakpad.IS_ENABLED = False 247 breakpad.IS_ENABLED = False
221 248
222 sdk_manager = os.path.join(args.sdk_root, 'tools', 'android') 249 sdk_manager = os.path.join(args.sdk_root, 'tools', 'android')
223 cmd = [sdk_manager, 'update', 'sdk', '--no-ui', '--filter', GMS_PACKAGE_ID] 250 cmd = [sdk_manager, 'update', 'sdk', '--no-ui', '--filter', GMS_PACKAGE_ID]
224 cmd_helper.Call(cmd) 251 cmd_helper.Call(cmd)
225 # If no update is needed, it still returns successfully so we just do nothing 252 # If no update is needed, it still returns successfully so we just do nothing
226 253
227 return 0 254 return 0
228 255
229 256
230 def Upload(args): 257 def Upload(args):
231 ''' 258 '''
232 Uploads the local Google Play services client library to a Google Cloud 259 Uploads the library from the local Google Play services SDK to a Google Cloud
233 storage bucket. 260 storage bucket.
234 261
235 By default, a local commit will be made at the end of the operation. 262 By default, a local commit will be made at the end of the operation.
236 ''' 263 '''
237 264
238 # This should function should not run on bots and could fail for many user 265 # 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 266 # and setup related reasons. Also, exceptions here are not caught, so we
240 # disable breakpad to avoid spamming the logs. 267 # disable breakpad to avoid spamming the logs.
241 breakpad.IS_ENABLED = False 268 breakpad.IS_ENABLED = False
242 269
243 paths = _InitPaths(args.sdk_root) 270 paths = PlayServicesPaths(args.sdk_root)
244 271
245 if not args.skip_git and utils.IsRepoDirty(constants.DIR_SOURCE_ROOT): 272 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.') 273 logging.error('The repo is dirty. Please commit or stash your changes.')
247 return -1 274 return -1
248 275
249 config = utils.ConfigParser(args.config) 276 config = utils.ConfigParser(args.config)
250 277
251 version_xml = os.path.join(paths.lib, 'res', 'values', 'version.xml') 278 new_version_number = utils.GetVersionNumberFromLibraryResources(
252 new_version_number = utils.GetVersionNumberFromLibraryResources(version_xml) 279 paths.version_xml)
253 logging.debug('comparing versions: new=%d, old=%s', 280 logging.debug('comparing versions: new=%d, old=%s',
254 new_version_number, config.version_number) 281 new_version_number, config.version_number)
255 if new_version_number <= config.version_number and not args.force: 282 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 ' 283 logging.info('The checked in version of the SDK is already the latest '
257 'one. No update needed. Please rerun with --force to skip ' 284 'one. No update needed. Please rerun with --force to skip '
258 'this check.') 285 'this check.')
259 return 0 286 return 0
260 287
261 tmp_root = tempfile.mkdtemp() 288 tmp_root = tempfile.mkdtemp()
262 try: 289 try:
263 new_lib_zip = os.path.join(tmp_root, LIBRARY_FILE_NAME) 290 new_lib_zip = os.path.join(tmp_root, ZIP_FILE_NAME)
264 new_license = os.path.join(tmp_root, LICENSE_FILE_NAME) 291 new_license = os.path.join(tmp_root, LICENSE_FILE_NAME)
265 292
266 # need to strip '.zip' from the file name here 293 # need to strip '.zip' from the file name here
267 shutil.make_archive(new_lib_zip[:-4], 'zip', paths.lib) 294 shutil.make_archive(new_lib_zip[:-4], 'zip', paths.lib)
268 _ExtractLicenseFile(new_license, paths.package) 295 _ExtractLicenseFile(new_license, paths.source_prop)
269 296
270 bucket_path = _VerifyBucketPathFormat(args.bucket, new_version_number, 297 bucket_path = _VerifyBucketPathFormat(args.bucket, new_version_number,
271 args.dry_run) 298 args.dry_run)
272 files_to_upload = [new_lib_zip, new_license] 299 files_to_upload = [new_lib_zip, new_license]
273 logging.debug('Uploading %s to %s', files_to_upload, bucket_path) 300 logging.debug('Uploading %s to %s', files_to_upload, bucket_path)
274 _UploadToBucket(bucket_path, files_to_upload, args.dry_run) 301 _UploadToBucket(bucket_path, files_to_upload, args.dry_run)
275 302
276 new_lib_zip_sha1 = os.path.join(SHA1_DIRECTORY, 303 new_lib_zip_sha1 = os.path.join(SHA1_DIRECTORY,
277 LIBRARY_FILE_NAME + '.sha1') 304 ZIP_FILE_NAME + '.sha1')
278 new_license_sha1 = os.path.join(SHA1_DIRECTORY, 305 new_license_sha1 = os.path.join(SHA1_DIRECTORY,
279 LICENSE_FILE_NAME + '.sha1') 306 LICENSE_FILE_NAME + '.sha1')
280 shutil.copy(new_lib_zip + '.sha1', new_lib_zip_sha1) 307 shutil.copy(new_lib_zip + '.sha1', new_lib_zip_sha1)
281 shutil.copy(new_license + '.sha1', new_license_sha1) 308 shutil.copy(new_license + '.sha1', new_license_sha1)
282 finally: 309 finally:
283 shutil.rmtree(tmp_root) 310 shutil.rmtree(tmp_root)
284 311
285 config.UpdateVersionNumber(new_version_number) 312 config.UpdateVersionNumber(new_version_number)
286 313
287 if not args.skip_git: 314 if not args.skip_git:
288 commit_message = ('Update the Google Play services dependency to %s\n' 315 commit_message = ('Update the Google Play services dependency to %s\n'
289 '\n') % new_version_number 316 '\n') % new_version_number
290 utils.MakeLocalCommit(constants.DIR_SOURCE_ROOT, 317 utils.MakeLocalCommit(constants.DIR_SOURCE_ROOT,
291 [new_lib_zip_sha1, new_license_sha1, config.path], 318 [new_lib_zip_sha1, new_license_sha1, config.path],
292 commit_message) 319 commit_message)
293 320
294 return 0 321 return 0
295 322
296 323
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, 324 def _DownloadFromBucket(bucket_path, sha1_file, destination, verbose,
327 is_dry_run): 325 is_dry_run):
328 '''Downloads the file designated by the provided sha1 from a cloud bucket.''' 326 '''Downloads the file designated by the provided sha1 from a cloud bucket.'''
329 327
330 download_from_google_storage.download_from_google_storage( 328 download_from_google_storage.download_from_google_storage(
331 input_filename=sha1_file, 329 input_filename=sha1_file,
332 base_url=bucket_path, 330 base_url=bucket_path,
333 gsutil=_InitGsutil(is_dry_run), 331 gsutil=_InitGsutil(is_dry_run),
334 num_threads=1, 332 num_threads=1,
335 directory=None, 333 directory=None,
(...skipping 24 matching lines...) Expand all
360 def _InitGsutil(is_dry_run): 358 def _InitGsutil(is_dry_run):
361 '''Initialize the Gsutil object as regular or dummy version for dry runs. ''' 359 '''Initialize the Gsutil object as regular or dummy version for dry runs. '''
362 360
363 if is_dry_run: 361 if is_dry_run:
364 return DummyGsutil() 362 return DummyGsutil()
365 else: 363 else:
366 return download_from_google_storage.Gsutil( 364 return download_from_google_storage.Gsutil(
367 download_from_google_storage.GSUTIL_DEFAULT_PATH) 365 download_from_google_storage.GSUTIL_DEFAULT_PATH)
368 366
369 367
370 def _ExtractLicenseFile(license_path, play_services_package_dir): 368 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: 369 with open(prop_file_path, 'r') as prop_file:
373 prop_file_content = prop_file.read() 370 prop_file_content = prop_file.read()
374 371
375 match = LICENSE_PATTERN.search(prop_file_content) 372 match = LICENSE_PATTERN.search(prop_file_content)
376 if not match: 373 if not match:
377 raise AttributeError('The license was not found in ' + 374 raise AttributeError('The license was not found in ' +
378 os.path.abspath(prop_file_path)) 375 os.path.abspath(prop_file_path))
379 376
380 with open(license_path, 'w') as license_file: 377 with open(license_path, 'w') as license_file:
381 license_file.write(match.group('text')) 378 license_file.write(match.group('text'))
382 379
383 380
384 def _CheckLicenseAgreement(expected_license_path, actual_license_path): 381 def _CheckLicenseAgreement(expected_license_path, actual_license_path,
382 version_number):
385 ''' 383 '''
386 Checks that the new license is the one already accepted by the user. If it 384 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 385 isn't, it prompts the user to accept it. Returns whether the expected license
388 has been accepted. 386 has been accepted.
389 ''' 387 '''
390 388
391 if utils.FileEquals(expected_license_path, actual_license_path): 389 if utils.FileEquals(expected_license_path, actual_license_path):
392 return True 390 return True
393 391
394 with open(expected_license_path) as license_file: 392 with open(expected_license_path) as license_file:
393 # Uses plain print rather than logging to make sure this is not formatted
394 # by the logger.
395 print ('Updating the Google Play services SDK to '
396 'version %d.' % version_number)
397
395 # The output is buffered when running as part of gclient hooks. We split 398 # 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 399 # the text here and flush is explicitly to avoid having part of it dropped
397 # out. 400 # out.
398 # Note: text contains *escaped* new lines, so we split by '\\n', not '\n'. 401 # Note: text contains *escaped* new lines, so we split by '\\n', not '\n'.
399 for license_part in license_file.read().split('\\n'): 402 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 403 print license_part
403 sys.stdout.flush() 404 sys.stdout.flush()
404 405
405 # Need to put the prompt on a separate line otherwise the gclient hook buffer 406 # Need to put the prompt on a separate line otherwise the gclient hook buffer
406 # only prints it after we received an input. 407 # only prints it after we received an input.
407 print 'Do you accept the license? [y/n]: ' 408 print 'Do you accept the license? [y/n]: '
408 sys.stdout.flush() 409 sys.stdout.flush()
409 return raw_input('> ') in ('Y', 'y') 410 return raw_input('> ') in ('Y', 'y')
410 411
411 412
413 def _IsBotEnvironment():
414 return bool(os.environ.get('CHROME_HEADLESS'))
415
416
412 def _VerifyBucketPathFormat(bucket_name, version_number, is_dry_run): 417 def _VerifyBucketPathFormat(bucket_name, version_number, is_dry_run):
413 ''' 418 '''
414 Formats and checks the download/upload path depending on whether we are 419 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 420 running in dry run mode or not. Returns a supposedly safe path to use with
416 Gsutil. 421 Gsutil.
417 ''' 422 '''
418 423
419 if is_dry_run: 424 if is_dry_run:
420 bucket_path = os.path.abspath(os.path.join(bucket_name, 425 bucket_path = os.path.abspath(os.path.join(bucket_name,
421 str(version_number))) 426 str(version_number)))
422 if not os.path.isdir(bucket_path): 427 if not os.path.isdir(bucket_path):
423 os.makedirs(bucket_path) 428 os.makedirs(bucket_path)
424 else: 429 else:
425 if bucket_name.startswith('gs://'): 430 if bucket_name.startswith('gs://'):
426 # We enforce the syntax without gs:// for consistency with the standalone 431 # We enforce the syntax without gs:// for consistency with the standalone
427 # download/upload scripts and to make dry run transition easier. 432 # download/upload scripts and to make dry run transition easier.
428 raise AttributeError('Please provide the bucket name without the gs:// ' 433 raise AttributeError('Please provide the bucket name without the gs:// '
429 'prefix (e.g. %s)' % GMS_CLOUD_STORAGE) 434 'prefix (e.g. %s)' % GMS_CLOUD_STORAGE)
430 bucket_path = 'gs://%s/%d' % (bucket_name, version_number) 435 bucket_path = 'gs://%s/%d' % (bucket_name, version_number)
431 436
432 return bucket_path 437 return bucket_path
433 438
434 439
440 class PlayServicesPaths(object):
441 '''
442 Describes the different paths to be used in the update process.
443
444 Filesystem hierarchy | Exposed property / notes
445 ---------------------------------------------------|-------------------------
446 [sdk_root] | sdk_root / (1)
447 +- extras |
448 +- google |
449 +- google_play_services | package / (2)
450 +- source.properties | source_prop / (3)
451 +- LICENSE | license / (4)
452 +- google_play_services_library.zip.sha1 | lib_zip_sha1 / (5)
453 +- libproject |
454 +- google-play-services_lib | lib / (6)
455 +- res |
456 +- values |
457 +- version.xml | version_xml (7)
458
459 Notes:
460
461 1. sdk_root: Path provided as a parameter to the script (--sdk_root)
462 2. package: This directory contains the Google Play services SDK itself.
463 When downloaded via the Android SDK manager, it will contain,
464 documentation, samples and other files in addition to the library.
465 3. source_prop: File created by the Android SDK manager that contains
466 the package information, such as the version info and the license. When
467 the update script downloads the SDK from our cloud storage, it is
468 removed.
469 4. license: File created by the update script. Contains the license accepted
470 by the user.
471 5. lib_zip_sha1: sha1 of the SDK zip that has been installed by the updated
472 script. It is compared with the one required by the config file to check
473 if an update is necessary.
474 6. lib: Contains the library itself: jar and resources. This is what is
475 downloaded from the cloud storage.
476 7. version_xml: File that contains the exact Google Play services library
477 version, the one that we track. The version looks like 811500, is used in
478 the code and the on-device APK, as opposed to the SDK package version
479 which looks like 27.0.0 and is used only by the Android SDK manager.
480
481 '''
482
483 def __init__(self, sdk_root):
484 relative_package = os.path.join('extras', 'google', 'google_play_services')
485 relative_lib = os.path.join(relative_package, 'libproject',
486 'google-play-services_lib')
487 self.sdk_root = sdk_root
488
489 self.package = os.path.join(sdk_root, relative_package)
490 self.lib_zip_sha1 = os.path.join(self.package, ZIP_FILE_NAME + '.sha1')
491 self.license = os.path.join(self.package, LICENSE_FILE_NAME)
492 self.source_prop = os.path.join(self.package, 'source.properties')
493
494 self.lib = os.path.join(sdk_root, relative_lib)
495 self.version_xml = os.path.join(self.lib, 'res', 'values', 'version.xml')
496
497
435 class DummyGsutil(download_from_google_storage.Gsutil): 498 class DummyGsutil(download_from_google_storage.Gsutil):
436 ''' 499 '''
437 Class that replaces Gsutil to use a local directory instead of an online 500 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 501 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 502 ones, so for the ones used here (ls, cp), it works to just use them with a
440 local directory. 503 local directory.
441 ''' 504 '''
442 505
443 def __init__(self): 506 def __init__(self):
444 super(DummyGsutil, self).__init__( 507 super(DummyGsutil, self).__init__(
445 download_from_google_storage.GSUTIL_DEFAULT_PATH) 508 download_from_google_storage.GSUTIL_DEFAULT_PATH)
446 509
447 def call(self, *args): 510 def call(self, *args):
448 logging.debug('Calling command "%s"', str(args)) 511 logging.debug('Calling command "%s"', str(args))
449 return cmd_helper.GetCmdStatusOutputAndError(args) 512 return cmd_helper.GetCmdStatusOutputAndError(args)
450 513
451 def check_call(self, *args): 514 def check_call(self, *args):
452 logging.debug('Calling command "%s"', str(args)) 515 logging.debug('Calling command "%s"', str(args))
453 return cmd_helper.GetCmdStatusOutputAndError(args) 516 return cmd_helper.GetCmdStatusOutputAndError(args)
454 517
455 518
456 if __name__ == '__main__': 519 if __name__ == '__main__':
457 sys.exit(Main()) 520 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